From 1d054ebc5bb098f9a847b54330e83d7be3b64c1e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 14 Nov 2023 07:56:16 -0800 Subject: [PATCH 01/87] Basic working terminal command highlighting on mousemove Part of #196634 --- .../terminal/browser/media/terminal.css | 12 +++ .../contrib/terminal/browser/terminal.ts | 3 +- .../browser/xterm/markNavigationAddon.ts | 80 ++++++++++++++++--- .../contrib/terminal/terminal.all.ts | 1 + .../terminal.highlight.contribution.ts | 57 +++++++++++++ 5 files changed, 143 insertions(+), 10 deletions(-) create mode 100644 src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 3d462bd7524..d0b971a2022 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -479,6 +479,18 @@ pointer-events: none; } +.terminal-scroll-highlight-output { + left: 0; + border-left: 5px solid #ffffff; + border-color: var(--vscode-terminalCommandDecoration-defaultBackground); +} +.terminal-scroll-highlight-output.success { + border-color: var(--vscode-terminalCommandDecoration-successBackground); +} +.terminal-scroll-highlight-output.error { + border-color: var(--vscode-terminalCommandDecoration-errorBackground); +} + .terminal-scroll-highlight-outline { border-left: 1px solid #ffffff; border-right: 1px solid #ffffff; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 39f195272ee..168dd11b49f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -111,7 +111,8 @@ export interface IMarkTracker { scrollToLine(line: number, position: ScrollPosition): void; revealCommand(command: ITerminalCommand, position?: ScrollPosition): void; - registerTemporaryDecoration(marker: IMarker, endMarker?: IMarker): void; + registerTemporaryDecoration(marker: IMarker, endMarker: IMarker | undefined, showOutline: boolean): void; + highlight(command: ITerminalCommand | undefined): void; } export interface ITerminalGroup { diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts index a1a5e962f90..4d88b747b01 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { coalesce } from 'vs/base/common/arrays'; -import { Disposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, MutableDisposable, dispose } from 'vs/base/common/lifecycle'; import { IMarkTracker } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ITerminalCapabilityStore, ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import type { Terminal, IMarker, ITerminalAddon, IDecoration } from '@xterm/xterm'; @@ -212,7 +212,7 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe this._terminal.scrollToLine(line); } if (!hideDecoration) { - this.registerTemporaryDecoration(start, end); + this.registerTemporaryDecoration(start, end, true); } } @@ -243,7 +243,63 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe ); } - registerTemporaryDecoration(marker: IMarker | number, endMarker?: IMarker | number): void { + private _highlightedCommand?: ITerminalCommand; + private _outputHighlights = this._register(new MutableDisposable()); + + highlight(command: ITerminalCommand | undefined): void { + if (!this._terminal) { + return; + } + if (!command) { + this._resetNavigationDecorations(); + return; + } + if (this._highlightedCommand === command) { + return; + } + const promptRowCount = command.getPromptRowCount(); + const commandRowCount = command.getCommandRowCount(); + if (command.marker) { + this._highlightedCommand = command; + this.registerTemporaryDecoration(command.marker.line - (promptRowCount - 1), command.marker.line + (commandRowCount - 1), false); + + // Highlight output + const store = this._outputHighlights.value = new DisposableStore(); + if (!command.executedMarker || !command.endMarker) { + return; + } + const startLine = toLineIndex(command.executedMarker); + const decorationCount = toLineIndex(command.endMarker) - startLine; + console.trace('highlight', startLine, decorationCount, command); + for (let i = 0; i < decorationCount; i++) { + const decoration = this._terminal.registerDecoration({ + marker: this._createMarkerForOffset(command.executedMarker, i), + width: this._terminal.cols + }); + if (decoration) { + store.add(decoration); + let renderedElement: HTMLElement | undefined; + + store.add(decoration.onRender(element => { + if (!renderedElement) { + renderedElement = element; + element.classList.add('terminal-scroll-highlight-output'); + if (command.exitCode === 0) { + element.classList.add('success'); + } else if (command.exitCode !== undefined) { + element.classList.add('error'); + } + } + if (this._terminal?.element) { + element.style.marginLeft = `-${getWindow(this._terminal.element).getComputedStyle(this._terminal.element).paddingLeft}`; + } + })); + } + } + } + } + + registerTemporaryDecoration(marker: IMarker | number, endMarker: IMarker | number | undefined, showOutline: boolean): void { if (!this._terminal) { return; } @@ -267,7 +323,10 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe decoration.onRender(element => { if (!renderedElement) { renderedElement = element; - element.classList.add('terminal-scroll-highlight', 'terminal-scroll-highlight-outline'); + element.classList.add('terminal-scroll-highlight'); + if (showOutline) { + element.classList.add('terminal-scroll-highlight-outline'); + } if (i === 0) { element.classList.add('top'); } @@ -281,13 +340,16 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe element.style.marginLeft = `-${getWindow(this._terminal.element).getComputedStyle(this._terminal.element).paddingLeft}`; } }); + // TODO: This is not efficient for a large decorationCount decoration.onDispose(() => { this._navigationDecorations = this._navigationDecorations?.filter(d => d !== decoration); }); // Number picked to align with symbol highlight in the editor - timeout(350).then(() => { - if (renderedElement) { - renderedElement.classList.remove('terminal-scroll-highlight-outline'); - } - }); + if (showOutline) { + timeout(350).then(() => { + if (renderedElement) { + renderedElement.classList.remove('terminal-scroll-highlight-outline'); + } + }); + } } } } diff --git a/src/vs/workbench/contrib/terminal/terminal.all.ts b/src/vs/workbench/contrib/terminal/terminal.all.ts index 563bf065317..e8ecb40a47c 100644 --- a/src/vs/workbench/contrib/terminal/terminal.all.ts +++ b/src/vs/workbench/contrib/terminal/terminal.all.ts @@ -17,6 +17,7 @@ import 'vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.acce import 'vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution'; import 'vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution'; import 'vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution'; +import 'vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution'; import 'vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution'; import 'vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminal.stickyScroll.contribution'; import 'vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution'; diff --git a/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts b/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts new file mode 100644 index 00000000000..5f9431c67d8 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; +import { addDisposableGenericMouseMoveListener, addDisposableListener } from 'vs/base/browser/dom'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; +import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; +import { ITerminalProcessInfo, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; + + +class TerminalHighlightContribution extends Disposable implements ITerminalContribution { + static readonly ID = 'terminal.highlight'; + + static get(instance: ITerminalInstance | IDetachedTerminalInstance): TerminalHighlightContribution | null { + return instance.getContribution(TerminalHighlightContribution.ID); + } + + constructor( + private readonly _instance: ITerminalInstance | IDetachedTerminalInstance, + processManager: ITerminalProcessManager | ITerminalProcessInfo, + widgetManager: TerminalWidgetManager, + @IInstantiationService instantiationService: IInstantiationService, + @ITerminalService terminalService: ITerminalService + ) { + super(); + } + + xtermOpen(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { + const screenElement = xterm.raw.element!.querySelector('.xterm-screen')!; + this._register(addDisposableListener(screenElement, 'mousemove', (e: MouseEvent) => { + console.log(e.target); + if ((e.target as any).tagName !== 'CANVAS') { + return; + } + const rect = xterm.raw.element?.getBoundingClientRect(); + if (!rect) { + return; + } + const mouseCursorY = Math.floor(e.offsetY / (rect.height / xterm.raw.rows)); + console.log(`@${mouseCursorY}`, e.offsetY); + const command = this._instance.capabilities.get(TerminalCapability.CommandDetection)?.getCommandForLine(xterm.raw.buffer.active.viewportY + mouseCursorY); + if (command && 'getOutput' in command) { + xterm.markTracker.highlight(command); + } else { + xterm.markTracker.highlight(undefined); + } + })); + } +} + +registerTerminalContribution(TerminalHighlightContribution.ID, TerminalHighlightContribution, false); From c9a90f9b4ef23f12c775142b8a807a18a8f310fe Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 14 Nov 2023 07:58:05 -0800 Subject: [PATCH 02/87] Make color more subtle --- .../terminal/browser/xterm/markNavigationAddon.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts index 4d88b747b01..53126a81367 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts @@ -284,11 +284,12 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe if (!renderedElement) { renderedElement = element; element.classList.add('terminal-scroll-highlight-output'); - if (command.exitCode === 0) { - element.classList.add('success'); - } else if (command.exitCode !== undefined) { - element.classList.add('error'); - } + // if (command.exitCode === 0) { + // element.classList.add('success'); + // } else if (command.exitCode !== undefined) { + // element.classList.add('error'); + // } + element.style.opacity = '0.5'; } if (this._terminal?.element) { element.style.marginLeft = `-${getWindow(this._terminal.element).getComputedStyle(this._terminal.element).paddingLeft}`; From 9ee91de4c07c1a9597b950faf56e7d62edf0a423 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 15 Nov 2023 16:17:11 +0100 Subject: [PATCH 03/87] Also send sqmid to extension telemetry events --- src/vs/platform/telemetry/common/telemetry.ts | 1 + src/vs/workbench/api/common/extHost.api.impl.ts | 1 + src/vs/workbench/api/common/extHostTelemetry.ts | 1 + src/vs/workbench/api/test/browser/extHostTelemetry.test.ts | 1 + .../services/extensions/browser/webWorkerExtensionHost.ts | 1 + .../services/extensions/common/extensionHostProtocol.ts | 1 + .../services/extensions/common/remoteExtensionHost.ts | 1 + .../extensions/electron-sandbox/localProcessExtensionHost.ts | 1 + src/vscode-dts/vscode.proposed.telemetry.d.ts | 5 +++++ 9 files changed, 13 insertions(+) diff --git a/src/vs/platform/telemetry/common/telemetry.ts b/src/vs/platform/telemetry/common/telemetry.ts index 3c48ecfaf0d..73db745352a 100644 --- a/src/vs/platform/telemetry/common/telemetry.ts +++ b/src/vs/platform/telemetry/common/telemetry.ts @@ -22,6 +22,7 @@ export interface ITelemetryService { readonly sessionId: string; readonly machineId: string; + readonly sqmId: string; readonly firstSessionDate: string; readonly msftInternal?: boolean; diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 6ea907a0f07..afd2b784c62 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -335,6 +335,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const env: typeof vscode.env = { get machineId() { return initData.telemetryInfo.machineId; }, get sessionId() { return initData.telemetryInfo.sessionId; }, + get sqmId() { return initData.telemetryInfo.sqmId; }, get language() { return initData.environment.appLanguage; }, get appName() { return initData.environment.appName; }, get appRoot() { return initData.environment.appRoot?.fsPath ?? ''; }, diff --git a/src/vs/workbench/api/common/extHostTelemetry.ts b/src/vs/workbench/api/common/extHostTelemetry.ts index ea9d652f143..549c604ad0b 100644 --- a/src/vs/workbench/api/common/extHostTelemetry.ts +++ b/src/vs/workbench/api/common/extHostTelemetry.ts @@ -103,6 +103,7 @@ export class ExtHostTelemetry extends Disposable implements ExtHostTelemetryShap commonProperties['common.extversion'] = extension.version; commonProperties['common.vscodemachineid'] = this.initData.telemetryInfo.machineId; commonProperties['common.vscodesessionid'] = this.initData.telemetryInfo.sessionId; + commonProperties['common.sqmid'] = this.initData.telemetryInfo.sqmId; commonProperties['common.vscodeversion'] = this.initData.version; commonProperties['common.isnewappinstall'] = isNewAppInstall(this.initData.telemetryInfo.firstSessionDate); commonProperties['common.product'] = this.initData.environment.appHost; diff --git a/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts b/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts index 262ba48a3ef..40e8224f45f 100644 --- a/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts @@ -42,6 +42,7 @@ suite('ExtHostTelemetry', function () { firstSessionDate: '2020-01-01T00:00:00.000Z', sessionId: 'test', machineId: 'test', + sqmId: 'test' }; const mockRemote = { diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index ff8c53636fd..b6d4f0f3831 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -309,6 +309,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost telemetryInfo: { sessionId: this._telemetryService.sessionId, machineId: this._telemetryService.machineId, + sqmId: this._telemetryService.sqmId, firstSessionDate: this._telemetryService.firstSessionDate, msftInternal: this._telemetryService.msftInternal }, diff --git a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts index 87b031a1c56..b145bc80f6c 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts @@ -40,6 +40,7 @@ export interface IExtensionHostInitData { telemetryInfo: { readonly sessionId: string; readonly machineId: string; + readonly sqmId: string; readonly firstSessionDate: string; readonly msftInternal?: boolean; }; diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts index 9a60251454b..69184c22b2e 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts @@ -243,6 +243,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost { telemetryInfo: { sessionId: this._telemetryService.sessionId, machineId: this._telemetryService.machineId, + sqmId: this._telemetryService.sqmId, firstSessionDate: this._telemetryService.firstSessionDate, msftInternal: this._telemetryService.msftInternal }, diff --git a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts index b4ae92453ae..2360b276ddc 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts @@ -496,6 +496,7 @@ export class NativeLocalProcessExtensionHost implements IExtensionHost { telemetryInfo: { sessionId: this._telemetryService.sessionId, machineId: this._telemetryService.machineId, + sqmId: this._telemetryService.sqmId, firstSessionDate: this._telemetryService.firstSessionDate, msftInternal: this._telemetryService.msftInternal }, diff --git a/src/vscode-dts/vscode.proposed.telemetry.d.ts b/src/vscode-dts/vscode.proposed.telemetry.d.ts index cc05bc10005..29ddc055227 100644 --- a/src/vscode-dts/vscode.proposed.telemetry.d.ts +++ b/src/vscode-dts/vscode.proposed.telemetry.d.ts @@ -27,6 +27,11 @@ declare module 'vscode' { */ export const telemetryConfiguration: TelemetryConfiguration; + /** + * sqm (Software Quality Metric) is an id used on Windows devices. + */ + export const sqmId: string; + /** * An {@link Event} which fires when the collectable state of telemetry changes * Returns a {@link TelemetryConfiguration} object From 6ebe1ba3ab9b06944e7161b746efcbfcd2f9daa1 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 15 Nov 2023 16:22:16 +0100 Subject: [PATCH 04/87] fix monaco editor build errors --- src/vs/editor/standalone/browser/standaloneServices.ts | 1 + src/vs/platform/telemetry/common/telemetryUtils.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 353447e0f34..d72b83da15e 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -759,6 +759,7 @@ class StandaloneTelemetryService implements ITelemetryService { readonly telemetryLevel = TelemetryLevel.NONE; readonly sessionId = 'someValue.sessionId'; readonly machineId = 'someValue.machineId'; + readonly sqmId = 'someValue.sqmId'; readonly firstSessionDate = 'someValue.firstSessionDate'; readonly sendErrorTelemetry = false; setEnabled(): void { } diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index 85c0fbee4dd..7971bdaadbf 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -31,6 +31,7 @@ export class NullTelemetryServiceShape implements ITelemetryService { readonly telemetryLevel = TelemetryLevel.NONE; readonly sessionId = 'someValue.sessionId'; readonly machineId = 'someValue.machineId'; + readonly sqmId = 'someValue.sqmId'; readonly firstSessionDate = 'someValue.firstSessionDate'; readonly sendErrorTelemetry = false; publicLog() { } From 2192c51b1ca7064c06f99ec93d366f66c7bc99fe Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 15 Nov 2023 16:49:26 +0100 Subject: [PATCH 05/87] fix compile errors --- src/vs/platform/telemetry/common/telemetryService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index e10d846b083..a9a9cc48109 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -33,6 +33,7 @@ export class TelemetryService implements ITelemetryService { readonly sessionId: string; readonly machineId: string; + readonly sqmId: string; readonly firstSessionDate: string; readonly msftInternal: boolean | undefined; @@ -56,6 +57,7 @@ export class TelemetryService implements ITelemetryService { this.sessionId = this._commonProperties['sessionID'] as string; this.machineId = this._commonProperties['common.machineId'] as string; + this.sqmId = this._commonProperties['common.sqmId'] as string; this.firstSessionDate = this._commonProperties['common.firstSessionDate'] as string; this.msftInternal = this._commonProperties['common.msftInternal'] as boolean | undefined; From c1f758042a9df4da966182d9a2d4ab953a5b764f Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 15 Nov 2023 16:52:51 +0100 Subject: [PATCH 06/87] add sqmId also to electron-sandbox/telemetryService --- src/vs/workbench/services/telemetry/browser/telemetryService.ts | 1 + .../services/telemetry/electron-sandbox/telemetryService.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index ac45112d126..3b7937e3afc 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -28,6 +28,7 @@ export class TelemetryService extends Disposable implements ITelemetryService { get sessionId(): string { return this.impl.sessionId; } get machineId(): string { return this.impl.machineId; } + get sqmId(): string { return this.impl.sqmId; } get firstSessionDate(): string { return this.impl.firstSessionDate; } get msftInternal(): boolean | undefined { return this.impl.msftInternal; } diff --git a/src/vs/workbench/services/telemetry/electron-sandbox/telemetryService.ts b/src/vs/workbench/services/telemetry/electron-sandbox/telemetryService.ts index 84207e13089..f87e5a9f950 100644 --- a/src/vs/workbench/services/telemetry/electron-sandbox/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/electron-sandbox/telemetryService.ts @@ -27,6 +27,7 @@ export class TelemetryService extends Disposable implements ITelemetryService { get sessionId(): string { return this.impl.sessionId; } get machineId(): string { return this.impl.machineId; } + get sqmId(): string { return this.impl.sqmId; } get firstSessionDate(): string { return this.impl.firstSessionDate; } get msftInternal(): boolean | undefined { return this.impl.msftInternal; } From 8bb522bf6ee37728659c4241d48073a9960bce7a Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 17 Nov 2023 14:42:26 +0100 Subject: [PATCH 07/87] adding one more context key in order to record whether the sparkle toolbar icon is enabled --- .../contrib/inlineChat/browser/inlineChatActions.ts | 4 ++-- .../inlineChat/browser/inlineChatDecorations.ts | 12 ++++++++++-- .../contrib/inlineChat/common/inlineChat.ts | 6 ++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 4d5563f66f2..f981c60c757 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, 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, CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING, InlineChatResponseFeedbackKind } 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, CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING, InlineChatResponseFeedbackKind, CTX_INLINE_CHAT_TOOLBAR_ICON_ENABLED } 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'; @@ -52,7 +52,7 @@ export class StartSessionAction extends EditorAction2 { icon: START_INLINE_CHAT, menu: [{ id: MenuId.EditorTitle, - when: ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_PROVIDER, CTX_INLINE_CHAT_VISIBLE.toNegated(), EditorContextKeys.focus), + when: ContextKeyExpr.and(CTX_INLINE_CHAT_TOOLBAR_ICON_ENABLED, CTX_INLINE_CHAT_HAS_PROVIDER, CTX_INLINE_CHAT_VISIBLE.toNegated(), EditorContextKeys.focus), group: 'navigation', order: -1000000, // at the very front }], diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts index b69a5c233bd..a5a57c8e108 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts @@ -16,7 +16,7 @@ import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/co import { DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { GutterActionsRegistry } from 'vs/workbench/contrib/codeEditor/browser/editorLineNumberMenu'; import { Action } from 'vs/base/common/actions'; -import { IInlineChatService, ShowGutterIcon } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { CTX_INLINE_CHAT_TOOLBAR_ICON_ENABLED, IInlineChatService, ShowGutterIcon } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Iterable } from 'vs/base/common/iterator'; import { Range } from 'vs/editor/common/core/range'; @@ -27,6 +27,7 @@ import { LOCALIZED_START_INLINE_CHAT_STRING } from 'vs/workbench/contrib/inlineC import { IBreakpoint, IDebugService } from 'vs/workbench/contrib/debug/common/debug'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { URI } from 'vs/base/common/uri'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; const GUTTER_INLINE_CHAT_OPAQUE_ICON = registerIcon('inline-chat-opaque', Codicon.sparkle, localize('startInlineChatOpaqueIcon', 'Icon which spawns the inline chat from the gutter. It is half opaque by default and becomes completely opaque on hover.')); const GUTTER_INLINE_CHAT_TRANSPARENT_ICON = registerIcon('inline-chat-transparent', Codicon.sparkle, localize('startInlineChatTransparentIcon', 'Icon which spawns the inline chat from the gutter. It is transparent by default and becomes opaque on hover.')); @@ -42,6 +43,7 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd private readonly _gutterDecorationTransparent: IModelDecorationOptions; public static readonly GUTTER_SETTING_ID = 'inlineChat.showGutterIcon'; + public static readonly TOOLBAR_SETTING_ID = 'inlineChat.showToolbarIcon'; private static readonly GUTTER_ICON_OPAQUE_CLASSNAME = 'codicon-inline-chat-opaque'; private static readonly GUTTER_ICON_TRANSPARENT_CLASSNAME = 'codicon-inline-chat-transparent'; @@ -51,12 +53,18 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd @IInlineChatSessionService private readonly _inlineChatSessionService: IInlineChatSessionService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IDebugService private readonly _debugService: IDebugService + @IDebugService private readonly _debugService: IDebugService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, ) { super(); this._gutterDecorationTransparent = this._registerGutterDecoration(true); this._gutterDecorationOpaque = this._registerGutterDecoration(false); + const ctxToolbarIconEnabled = CTX_INLINE_CHAT_TOOLBAR_ICON_ENABLED.bindTo(this._contextKeyService); this._register(this._configurationService.onDidChangeConfiguration((e: IConfigurationChangeEvent) => { + if (e.affectsConfiguration(InlineChatDecorationsContribution.TOOLBAR_SETTING_ID)) { + ctxToolbarIconEnabled.set(this._configurationService.getValue(InlineChatDecorationsContribution.TOOLBAR_SETTING_ID)); + return; + } if (!e.affectsConfiguration(InlineChatDecorationsContribution.GUTTER_SETTING_ID)) { return; } diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index 72a8ba05785..65fd54cec1f 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -153,6 +153,7 @@ export const CTX_INLINE_CHAT_LAST_FEEDBACK = new RawContextKey<'unhelpful' | 'he export const CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING = new RawContextKey('inlineChatSupportIssueReporting', false, localize('inlineChatSupportIssueReporting', "Whether the interactive editor supports issue reporting")); export const CTX_INLINE_CHAT_DOCUMENT_CHANGED = new RawContextKey('inlineChatDocumentChanged', false, localize('inlineChatDocumentChanged', "Whether the document has changed concurrently")); export const CTX_INLINE_CHAT_EDIT_MODE = new RawContextKey('config.inlineChat.editMode', EditMode.Live); +export const CTX_INLINE_CHAT_TOOLBAR_ICON_ENABLED = new RawContextKey('inlineChatToolbarIconEnabled', false, localize('inlineChatToolbarIconEnabled', "Whether the toolbar icon spawning inline chat is enabled.")); // --- (select) action identifier @@ -235,6 +236,11 @@ Registry.as(Extensions.Configuration).registerConfigurat localize('showGutterIcon.mouseover', "Show the gutter icon when the mouse is over the icon."), localize('showGutterIcon.never', "Never show the gutter icon."), ] + }, + 'inlineChat.showToolbarIcon': { + description: localize('showToolbarIcon', "Controls whether the toolbar icon spawning the inline chat is enabled."), + default: true, + type: 'boolean' } } }); From dab5650d3a12112a225c33691283f896057f38cb Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 17 Nov 2023 14:49:18 +0100 Subject: [PATCH 08/87] setting the toolbar icon enablement setting --- .../inlineChat/browser/inlineChatDecorations.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts index a5a57c8e108..01e53e18875 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts @@ -27,13 +27,14 @@ import { LOCALIZED_START_INLINE_CHAT_STRING } from 'vs/workbench/contrib/inlineC import { IBreakpoint, IDebugService } from 'vs/workbench/contrib/debug/common/debug'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { URI } from 'vs/base/common/uri'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; const GUTTER_INLINE_CHAT_OPAQUE_ICON = registerIcon('inline-chat-opaque', Codicon.sparkle, localize('startInlineChatOpaqueIcon', 'Icon which spawns the inline chat from the gutter. It is half opaque by default and becomes completely opaque on hover.')); const GUTTER_INLINE_CHAT_TRANSPARENT_ICON = registerIcon('inline-chat-transparent', Codicon.sparkle, localize('startInlineChatTransparentIcon', 'Icon which spawns the inline chat from the gutter. It is transparent by default and becomes opaque on hover.')); export class InlineChatDecorationsContribution extends Disposable implements IEditorContribution { + private _ctxToolbarIconEnabled: IContextKey; private _currentBreakpoints: readonly IBreakpoint[] = []; private _gutterDecorationID: string | undefined; private _inlineChatKeybinding: string | undefined; @@ -59,10 +60,11 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd super(); this._gutterDecorationTransparent = this._registerGutterDecoration(true); this._gutterDecorationOpaque = this._registerGutterDecoration(false); - const ctxToolbarIconEnabled = CTX_INLINE_CHAT_TOOLBAR_ICON_ENABLED.bindTo(this._contextKeyService); + this._ctxToolbarIconEnabled = CTX_INLINE_CHAT_TOOLBAR_ICON_ENABLED.bindTo(this._contextKeyService); + this._setToolbarIconEnablementToSetting(); this._register(this._configurationService.onDidChangeConfiguration((e: IConfigurationChangeEvent) => { if (e.affectsConfiguration(InlineChatDecorationsContribution.TOOLBAR_SETTING_ID)) { - ctxToolbarIconEnabled.set(this._configurationService.getValue(InlineChatDecorationsContribution.TOOLBAR_SETTING_ID)); + this._setToolbarIconEnablementToSetting(); return; } if (!e.affectsConfiguration(InlineChatDecorationsContribution.GUTTER_SETTING_ID)) { @@ -92,6 +94,10 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd this._onEnablementOrModelChanged(); } + private _setToolbarIconEnablementToSetting(): void { + this._ctxToolbarIconEnabled.set(this._configurationService.getValue(InlineChatDecorationsContribution.TOOLBAR_SETTING_ID)); + } + private _registerGutterDecoration(isTransparent: boolean): ModelDecorationOptions { return ModelDecorationOptions.register({ description: 'inline-chat-decoration', From a8c521042bb8af957f8e6272f160ad2e70934265 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 17 Nov 2023 14:51:14 +0100 Subject: [PATCH 09/87] polishing the code --- .../contrib/inlineChat/browser/inlineChatDecorations.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts index 01e53e18875..cb1bc320f5c 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts @@ -43,24 +43,24 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd private readonly _gutterDecorationOpaque: IModelDecorationOptions; private readonly _gutterDecorationTransparent: IModelDecorationOptions; - public static readonly GUTTER_SETTING_ID = 'inlineChat.showGutterIcon'; public static readonly TOOLBAR_SETTING_ID = 'inlineChat.showToolbarIcon'; + public static readonly GUTTER_SETTING_ID = 'inlineChat.showGutterIcon'; private static readonly GUTTER_ICON_OPAQUE_CLASSNAME = 'codicon-inline-chat-opaque'; private static readonly GUTTER_ICON_TRANSPARENT_CLASSNAME = 'codicon-inline-chat-transparent'; constructor( private readonly _editor: ICodeEditor, + @IContextKeyService _contextKeyService: IContextKeyService, @IInlineChatService private readonly _inlineChatService: IInlineChatService, @IInlineChatSessionService private readonly _inlineChatSessionService: IInlineChatSessionService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IDebugService private readonly _debugService: IDebugService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IDebugService private readonly _debugService: IDebugService ) { super(); this._gutterDecorationTransparent = this._registerGutterDecoration(true); this._gutterDecorationOpaque = this._registerGutterDecoration(false); - this._ctxToolbarIconEnabled = CTX_INLINE_CHAT_TOOLBAR_ICON_ENABLED.bindTo(this._contextKeyService); + this._ctxToolbarIconEnabled = CTX_INLINE_CHAT_TOOLBAR_ICON_ENABLED.bindTo(_contextKeyService); this._setToolbarIconEnablementToSetting(); this._register(this._configurationService.onDidChangeConfiguration((e: IConfigurationChangeEvent) => { if (e.affectsConfiguration(InlineChatDecorationsContribution.TOOLBAR_SETTING_ID)) { From 639b40d738d5c05adf07cf20fe8f9c8a770111ae Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 17 Nov 2023 15:01:54 +0100 Subject: [PATCH 10/87] try to not expose sqmId in proposed.telemetry api --- src/vs/workbench/api/common/extHost.api.impl.ts | 1 - src/vscode-dts/vscode.proposed.telemetry.d.ts | 5 ----- 2 files changed, 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index afd2b784c62..6ea907a0f07 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -335,7 +335,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const env: typeof vscode.env = { get machineId() { return initData.telemetryInfo.machineId; }, get sessionId() { return initData.telemetryInfo.sessionId; }, - get sqmId() { return initData.telemetryInfo.sqmId; }, get language() { return initData.environment.appLanguage; }, get appName() { return initData.environment.appName; }, get appRoot() { return initData.environment.appRoot?.fsPath ?? ''; }, diff --git a/src/vscode-dts/vscode.proposed.telemetry.d.ts b/src/vscode-dts/vscode.proposed.telemetry.d.ts index 29ddc055227..cc05bc10005 100644 --- a/src/vscode-dts/vscode.proposed.telemetry.d.ts +++ b/src/vscode-dts/vscode.proposed.telemetry.d.ts @@ -27,11 +27,6 @@ declare module 'vscode' { */ export const telemetryConfiguration: TelemetryConfiguration; - /** - * sqm (Software Quality Metric) is an id used on Windows devices. - */ - export const sqmId: string; - /** * An {@link Event} which fires when the collectable state of telemetry changes * Returns a {@link TelemetryConfiguration} object From 33f83664a277b86624db8399e2da4d39cf2fd9fb Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 17 Nov 2023 16:24:04 +0100 Subject: [PATCH 11/87] not displaying the sparkle in the gutter on an active debug session --- .../browser/inlineChatDecorations.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts index b69a5c233bd..be6cd31a9ab 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts @@ -24,7 +24,7 @@ import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/brows import { MarkdownString } from 'vs/base/common/htmlContent'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { LOCALIZED_START_INLINE_CHAT_STRING } from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions'; -import { IBreakpoint, IDebugService } from 'vs/workbench/contrib/debug/common/debug'; +import { IBreakpoint, IDebugService, IDebugSession } from 'vs/workbench/contrib/debug/common/debug'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { URI } from 'vs/base/common/uri'; @@ -37,6 +37,8 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd private _gutterDecorationID: string | undefined; private _inlineChatKeybinding: string | undefined; private _hasInlineChatSession: boolean = false; + private _hasActiveDebugSession: boolean = false; + private _debugSessions: Set = new Set(); private readonly _localToDispose = new DisposableStore(); private readonly _gutterDecorationOpaque: IModelDecorationOptions; private readonly _gutterDecorationTransparent: IModelDecorationOptions; @@ -74,6 +76,20 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd this._onEnablementOrModelChanged(); } })); + this._register(this._debugService.onWillNewSession((session) => { + this._debugSessions.add(session); + if (!this._hasActiveDebugSession) { + this._hasActiveDebugSession = true; + this._onEnablementOrModelChanged(); + } + })); + this._register(this._debugService.onDidEndSession((session) => { + this._debugSessions.delete(session); + if (this._debugSessions.size === 0) { + this._hasActiveDebugSession = false; + this._onEnablementOrModelChanged(); + } + })); this._register(this._inlineChatService.onDidChangeProviders(() => this._onEnablementOrModelChanged())); this._register(this._editor.onDidChangeModel(() => this._onEnablementOrModelChanged())); this._register(this._keybindingService.onDidUpdateKeybindings(() => { @@ -111,7 +127,7 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd private _onEnablementOrModelChanged(): void { // cancels the scheduler, removes editor listeners / removes decoration this._localToDispose.clear(); - if (!this._editor.hasModel() || this._hasInlineChatSession || this._showGutterIconMode() === ShowGutterIcon.Never || !this._hasProvider()) { + if (!this._editor.hasModel() || this._hasActiveDebugSession || this._hasInlineChatSession || this._showGutterIconMode() === ShowGutterIcon.Never || !this._hasProvider()) { return; } const editor = this._editor; From 5bcc2ad8ebeae6f3d9123644a3722ae53ed81cb7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 17 Nov 2023 07:30:22 -0800 Subject: [PATCH 12/87] Remove now unused css --- .../browser/media/stickyScroll.css | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/media/stickyScroll.css b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/media/stickyScroll.css index 8c65bf940a6..21f47917b60 100644 --- a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/media/stickyScroll.css +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/media/stickyScroll.css @@ -39,23 +39,3 @@ .terminal-sticky-scroll .hover-overlay:hover { background-color: var(--vscode-terminalStickyScrollHover-background, transparent); } - -.terminal-sticky-scroll .hover-overlay .keybinding-hint { - display: none; - position: absolute; - right: 10px; - top: 50%; - transform: translateY(-50%); - z-index: 100; - opacity: 0.5; - pointer-events: none; - display: flex; -} -.terminal-sticky-scroll .hover-overlay .keybinding-hint > div { - /* Preventing wrapping of keybinding and its localized string */ - display: inline-block; -} -.terminal-sticky-scroll .hover-overlay:hover .keybinding-hint { - display: flex; - align-items: center; -} From 12f6838c4fdb630abe7bf408af21a1a2374c0e94 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 17 Nov 2023 07:46:08 -0800 Subject: [PATCH 13/87] Correct sticky scroll bg color in terminal editors Fixes #198358 --- src/vs/workbench/contrib/terminal/browser/terminal.ts | 1 + .../workbench/contrib/terminal/browser/terminalInstance.ts | 7 ++++++- .../stickyScroll/browser/media/stickyScroll.css | 3 +++ .../stickyScroll/browser/terminalStickyScrollOverlay.ts | 3 +++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index aaa3ee98c5d..236cc4eab7e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -653,6 +653,7 @@ export interface ITerminalInstance extends IBaseTerminalInstance { onDidInputData: Event; onDidChangeSelection: Event; onDidRunText: Event; + onDidChangeTarget: 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/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 73a905dcd57..51229ea570d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -201,7 +201,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } get target(): TerminalLocation | undefined { return this._target; } - set target(value: TerminalLocation | undefined) { this._target = value; } + set target(value: TerminalLocation | undefined) { + this._target = value; + this._onDidChangeTarget.fire(value); + } get instanceId(): number { return this._instanceId; } get resource(): URI { return this._resource; } @@ -322,6 +325,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { readonly onDidChangeHasChildProcesses = this._onDidChangeHasChildProcesses.event; private readonly _onDidRunText = this._register(new Emitter()); readonly onDidRunText = this._onDidRunText.event; + private readonly _onDidChangeTarget = this._register(new Emitter()); + readonly onDidChangeTarget = this._onDidChangeTarget.event; constructor( private readonly _terminalShellTypeContextKey: IContextKey, diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/media/stickyScroll.css b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/media/stickyScroll.css index 8c65bf940a6..a874b52f5dd 100644 --- a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/media/stickyScroll.css +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/media/stickyScroll.css @@ -12,6 +12,9 @@ background: var(--vscode-terminalStickyScroll-background, var(--vscode-terminal-background, var(--vscode-panel-background))); box-shadow: var(--vscode-scrollbar-shadow) 0 3px 2px -2px; } +.editor-instance .terminal-sticky-scroll { + background: var(--vscode-terminalStickyScroll-background, var(--vscode-terminal-background, var(--vscode-editor-background))); +} .terminal-sticky-scroll.visible { display:block; diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts index a54e52081e0..7a9fa1aafda 100644 --- a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts @@ -85,6 +85,9 @@ export class TerminalStickyScrollOverlay extends Disposable { } })); + // React to terminal location changes + this._register(this._instance.onDidChangeTarget(() => this._syncOptions())); + // Eagerly create the overlay xtermCtor.then(ctor => { this._stickyScrollOverlay = this._register(new ctor({ From 96df831457688a31200a9f661a2195fec89b913f Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 17 Nov 2023 08:06:47 -0800 Subject: [PATCH 14/87] Fix #198492. Avoid action name duplicate (#198527) --- .../notebook/browser/view/cellParts/chat/cellChatActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions.ts index 683141c5275..ff4136e8465 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions.ts @@ -148,7 +148,7 @@ registerAction2(class extends NotebookCellAction { constructor() { super( { - id: 'OFF.inlineChat.discard', + id: 'notebook.cell.chat.discard', title: localize('discard', 'Discard'), icon: Codicon.discard, menu: { From c2e58d5ca564c131d4ec16054f5131a8ee02a9a7 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 17 Nov 2023 16:55:06 +0100 Subject: [PATCH 15/87] Fixes #198516 --- .../browser/widget/diffEditor/diffEditorViewModel.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts index 3b54ff415f4..4e014f4741a 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts @@ -104,12 +104,10 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo const lastUnchangedRegions = this._unchangedRegions.get(); const lastUnchangedRegionsOrigRanges = lastUnchangedRegions.originalDecorationIds .map(id => model.original.getDecorationRange(id)) - .filter(r => !!r) - .map(r => LineRange.fromRange(r!)); + .map(r => r ? LineRange.fromRange(r) : undefined); const lastUnchangedRegionsModRanges = lastUnchangedRegions.modifiedDecorationIds .map(id => model.modified.getDecorationRange(id)) - .filter(r => !!r) - .map(r => LineRange.fromRange(r!)); + .map(r => r ? LineRange.fromRange(r) : undefined); const originalDecorationIds = model.original.deltaDecorations( lastUnchangedRegions.originalDecorationIds, @@ -123,8 +121,8 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo for (const r of newUnchangedRegions) { for (let i = 0; i < lastUnchangedRegions.regions.length; i++) { - if (r.originalUnchangedRange.intersectsStrict(lastUnchangedRegionsOrigRanges[i]) - && r.modifiedUnchangedRange.intersectsStrict(lastUnchangedRegionsModRanges[i])) { + if (lastUnchangedRegionsOrigRanges[i] && r.originalUnchangedRange.intersectsStrict(lastUnchangedRegionsOrigRanges[i]!) + && lastUnchangedRegionsModRanges[i] && r.modifiedUnchangedRange.intersectsStrict(lastUnchangedRegionsModRanges[i]!)) { r.setHiddenModifiedRange(lastUnchangedRegions.regions[i].getHiddenModifiedRange(undefined), tx); break; } From 21d2297f8cbd713cac21ecb5d56f2c7f3069e572 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 17 Nov 2023 10:54:53 -0600 Subject: [PATCH 16/87] Make filtered responses less readable and not copyable (#198539) --- .../contrib/chat/browser/actions/chatCopyActions.ts | 5 ++++- src/vs/workbench/contrib/chat/browser/chatWidget.ts | 10 +++++++--- src/vs/workbench/contrib/chat/browser/media/chat.css | 6 ++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts index 972b483c675..816379d8646 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts @@ -9,6 +9,7 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions'; import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; +import { CONTEXT_RESPONSE_FILTERED } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IChatRequestViewModel, IChatResponseViewModel, isRequestVM, isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; export function registerChatCopyActions() { @@ -24,6 +25,7 @@ export function registerChatCopyActions() { category: CHAT_CATEGORY, menu: { id: MenuId.ChatContext, + when: CONTEXT_RESPONSE_FILTERED.toNegated(), group: 'copy', } }); @@ -36,7 +38,7 @@ export function registerChatCopyActions() { if (widget) { const viewModel = widget.viewModel; const sessionAsText = viewModel?.getItems() - .filter((item): item is (IChatRequestViewModel | IChatResponseViewModel) => isRequestVM(item) || isResponseVM(item)) + .filter((item): item is (IChatRequestViewModel | IChatResponseViewModel) => isRequestVM(item) || (isResponseVM(item) && !item.errorDetails?.responseIsFiltered)) .map(stringifyItem) .join('\n\n'); if (sessionAsText) { @@ -58,6 +60,7 @@ export function registerChatCopyActions() { category: CHAT_CATEGORY, menu: { id: MenuId.ChatContext, + when: CONTEXT_RESPONSE_FILTERED.toNegated(), group: 'copy', } }); diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 89e4e1bd940..0bf13a5db1a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -27,7 +27,7 @@ import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart'; import { ChatAccessibilityProvider, ChatListDelegate, ChatListItemRenderer, IChatListItemRendererOptions, IChatRendererDelegate } from 'vs/workbench/contrib/chat/browser/chatListRenderer'; import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions'; import { ChatViewPane } from 'vs/workbench/contrib/chat/browser/chatViewPane'; -import { CONTEXT_CHAT_REQUEST_IN_PROGRESS, CONTEXT_IN_CHAT_SESSION } from 'vs/workbench/contrib/chat/common/chatContextKeys'; +import { CONTEXT_CHAT_REQUEST_IN_PROGRESS, CONTEXT_IN_CHAT_SESSION, CONTEXT_RESPONSE_FILTERED } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService'; import { ChatModelInitState, IChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; import { IChatReplyFollowup, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; @@ -354,12 +354,16 @@ export class ChatWidget extends Disposable implements IChatWidget { e.browserEvent.preventDefault(); e.browserEvent.stopPropagation(); + const selected = e.element; + const scopedContextKeyService = this.contextKeyService.createOverlay([ + [CONTEXT_RESPONSE_FILTERED.key, isResponseVM(selected) && !!selected.errorDetails?.responseIsFiltered] + ]); this.contextMenuService.showContextMenu({ menuId: MenuId.ChatContext, menuActionOptions: { shouldForwardArgs: true }, - contextKeyService: this.contextKeyService, + contextKeyService: scopedContextKeyService, getAnchor: () => e.anchor, - getActionsContext: () => e.element, + getActionsContext: () => selected, }); } diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 7ba3e573bd9..307a49652c2 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -460,8 +460,10 @@ } .interactive-item-container.filtered-response .value > .rendered-markdown { - -webkit-mask-image: linear-gradient(rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0.05)); - mask-image: linear-gradient(rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0.05)); + pointer-events: none; + filter: blur(1.8px); + -webkit-mask-image: linear-gradient(rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.02)); + mask-image: linear-gradient(rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.02)); } /* #region Quick Chat */ From e740a5ccb06c7097c58c69781cf83e6e2d98ee16 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 17 Nov 2023 09:30:56 -0800 Subject: [PATCH 17/87] Adjust command finished marker in started adjustment Part of #196634 --- .../commandDetectionCapability.ts | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index d4d2892df16..da16580b4b4 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -32,7 +32,6 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe private __isCommandStorageDisabled: boolean = false; private _handleCommandStartOptions?: IHandleCommandOptions; - private _commitCommandFinished?: RunOnceScheduler; private _ptyHeuristicsHooks: ICommandDetectionHeuristicsHooks; @@ -99,6 +98,10 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe get commandMarkers() { return that._commandMarkers; } set commandMarkers(value) { that._commandMarkers = value; } get clearCommandsInViewport() { return that._clearCommandsInViewport.bind(that); } + commitCommandFinished() { + that._commitCommandFinished?.flush(); + that._commitCommandFinished = undefined; + } }; this._ptyHeuristics = this._register(new MandatoryMutableDisposable(new UnixPtyHeuristics(this._terminal, this, this._ptyHeuristicsHooks, this._logService))); @@ -172,6 +175,10 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe get commandMarkers() { return that._commandMarkers; } set commandMarkers(value) { that._commandMarkers = value; } get clearCommandsInViewport() { return that._clearCommandsInViewport.bind(that); } + commitCommandFinished() { + that._commitCommandFinished?.flush(); + that._commitCommandFinished = undefined; + } }, this._logService ); @@ -235,8 +242,6 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe this._logService.debug('CommandDetectionCapability#handlePromptStart adjusted commandFinished', `${lastCommand.endMarker.line} -> ${lastCommand.executedMarker.line + 1}`); lastCommand.endMarker = cloneMarker(this._terminal, lastCommand.executedMarker, 1); } - this._commitCommandFinished?.flush(); - this._commitCommandFinished = undefined; this._currentCommand.promptStartMarker = options?.marker || (lastCommand?.endMarker ? cloneMarker(this._terminal, lastCommand.endMarker) : this._terminal.registerMarker(0)); @@ -408,6 +413,7 @@ interface ICommandDetectionHeuristicsHooks { commandMarkers: IMarker[]; clearCommandsInViewport(): void; + commitCommandFinished(): void; } type IPtyHeuristics = ( @@ -439,6 +445,8 @@ class UnixPtyHeuristics extends Disposable { } async handleCommandStart(options?: IHandleCommandOptions) { + this._hooks.commitCommandFinished(); + const currentCommand = this._capability.currentCommand; currentCommand.commandStartX = this._terminal.buffer.active.cursorX; currentCommand.commandStartMarker = options?.marker || this._terminal.registerMarker(0); @@ -507,6 +515,10 @@ class WindowsPtyHeuristics extends Disposable { private _recentlyPerformedCsiJ = false; + private _tryAdjustCommandStartMarkerScheduler?: RunOnceScheduler; + private _tryAdjustCommandStartMarkerScannedLineCount: number = 0; + private _tryAdjustCommandStartMarkerPollCount: number = 0; + constructor( private readonly _terminal: Terminal, private readonly _capability: CommandDetectionCapability, @@ -592,10 +604,6 @@ class WindowsPtyHeuristics extends Disposable { } } - private _tryAdjustCommandStartMarkerScheduler?: RunOnceScheduler; - private _tryAdjustCommandStartMarkerScannedLineCount: number = 0; - private _tryAdjustCommandStartMarkerPollCount: number = 0; - async handleCommandStart() { this._capability.currentCommand.commandStartX = this._terminal.buffer.active.cursorX; @@ -661,6 +669,13 @@ class WindowsPtyHeuristics extends Disposable { this._logService.debug('CommandDetectionCapability#_tryAdjustCommandStartMarker adjusted promptStart', `${this._capability.currentCommand.promptStartMarker?.line} -> ${this._capability.currentCommand.commandStartMarker.line}`); this._capability.currentCommand.promptStartMarker?.dispose(); this._capability.currentCommand.promptStartMarker = cloneMarker(this._terminal, this._capability.currentCommand.commandStartMarker); + // Adjust the last command if it's not in the same position as the following + // prompt start marker + const lastCommand = this._capability.commands.at(-1); + if (lastCommand && this._capability.currentCommand.commandStartMarker.line !== lastCommand.endMarker?.line) { + lastCommand.endMarker?.dispose(); + lastCommand.endMarker = cloneMarker(this._terminal, this._capability.currentCommand.commandStartMarker); + } } // use the regex to set the position as it's possible input has occurred this._capability.currentCommand.commandStartX = adjustedPrompt.length; @@ -692,6 +707,8 @@ class WindowsPtyHeuristics extends Disposable { this._tryAdjustCommandStartMarkerScheduler = undefined; } + this._hooks.commitCommandFinished(); + if (!this._capability.currentCommand.commandExecutedMarker) { this._onCursorMoveListener.value = this._terminal.onCursorMove(() => { if (this._hooks.commandMarkers.length === 0 || this._hooks.commandMarkers[this._hooks.commandMarkers.length - 1].line !== this._terminal.buffer.active.cursorY) { From 36d4b03b9f5f24cb023dc84f7c383e26a4427b1a Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 6 Nov 2023 15:08:54 -0800 Subject: [PATCH 18/87] new classes --- .../contrib/kernelVariables/variablesPanel.ts | 40 +++++++++++++++++++ .../contrib/kernelVariables/variablesView.ts | 17 ++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts create mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts new file mode 100644 index 00000000000..b3923452546 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.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 { ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; + + +export class VariablesPanel extends ViewPane { + + constructor( + options: IViewPaneOptions, + @IKeybindingService keybindingService: IKeybindingService, + @IContextMenuService contextMenuService: IContextMenuService, + @IContextKeyService contextKeyService: IContextKeyService, + @IConfigurationService configurationService: IConfigurationService, + @IInstantiationService instantiationService: IInstantiationService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @IOpenerService openerService: IOpenerService, + @IQuickInputService protected quickInputService: IQuickInputService, + @ICommandService protected commandService: ICommandService, + @IThemeService themeService: IThemeService, + @ITelemetryService telemetryService: ITelemetryService, + ) { + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + + + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts new file mode 100644 index 00000000000..22cdd3f981b --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; + +const VARIABLES_VIEW_CONTAINER_ID = 'variablesViewContainer'; + +export class VariablesView extends Disposable implements IWorkbenchContribution { + + constructor() { + super(); + } + +} From 58c2ebe5f836f5ac49dc665310e8a80642285bcf Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 6 Nov 2023 15:30:40 -0800 Subject: [PATCH 19/87] ..... --- .../contrib/kernelVariables/variablesPanel.ts | 23 ++++++++++++ .../contrib/kernelVariables/variablesView.ts | 35 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts index b3923452546..4e65671450c 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts @@ -19,6 +19,9 @@ import { IViewDescriptorService } from 'vs/workbench/common/views'; export class VariablesPanel extends ViewPane { + static readonly ID = TUNNEL_VIEW_ID; + static readonly TITLE: ILocalizedString = nls.localize2('remote.tunnel', "Ports"); + constructor( options: IViewPaneOptions, @IKeybindingService keybindingService: IKeybindingService, @@ -38,3 +41,23 @@ export class VariablesPanel extends ViewPane { } } + +export class TunnelPanelDescriptor implements IViewDescriptor { + readonly id = TunnelPanel.ID; + readonly name: ILocalizedString = TunnelPanel.TITLE; + readonly ctorDescriptor: SyncDescriptor; + readonly canToggleVisibility = true; + readonly hideByDefault = false; + // group is not actually used for views that are not extension contributed. Use order instead. + readonly group = 'details@0'; + // -500 comes from the remote explorer viewOrderDelegate + readonly order = -500; + readonly remoteAuthority?: string | string[]; + readonly canMoveView = true; + readonly containerIcon = portsViewIcon; + + constructor(viewModel: ITunnelViewModel, environmentService: IWorkbenchEnvironmentService) { + this.ctorDescriptor = new SyncDescriptor(TunnelPanel, [viewModel]); + this.remoteAuthority = environmentService.remoteAuthority ? environmentService.remoteAuthority.split('+')[0] : undefined; + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts index 22cdd3f981b..c78b9772d98 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts @@ -3,8 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions, IViewContainersRegistry, IViewsRegistry } from 'vs/workbench/common/views'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; const VARIABLES_VIEW_CONTAINER_ID = 'variablesViewContainer'; @@ -12,6 +16,37 @@ export class VariablesView extends Disposable implements IWorkbenchContribution constructor() { super(); + this.enableVariablesView(); + } + + private async enableVariablesView() { + const viewEnabled = true; + + if (viewEnabled) { + const viewContainer = await this.getViewContainer(); + const variablesPanelDescriptor = new VariablesPanelDescriptor(); + const viewsRegistry = Registry.as(Extensions.ViewsRegistry); + if (viewContainer) { + viewsRegistry.registerViews([variablesPanelDescriptor!], viewContainer); + } + } else { + this.contextKeyListener = this.contextKeyService.onDidChangeContext(e => { + if (e.affectsSome(new Set(forwardedPortsViewEnabled.keys()))) { + this.enableForwardedPortsView(); + } + }); + } + } + + getViewContainer() { + return Registry.as(Extensions.ViewContainersRegistry).registerViewContainer({ + id: VARIABLES_VIEW_CONTAINER_ID, + title: { value: nls.localize('variables', "Kernel Variables"), original: 'Kernel Variables' }, + ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [VARIABLES_VIEW_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true }]), + storageId: VARIABLES_VIEW_CONTAINER_ID, + hideIfEmpty: true, + order: 5 + }, ViewContainerLocation.Panel); } } From 449c95f7bec80c5748c3b350643e784dcd48ec18 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 6 Nov 2023 15:35:50 -0800 Subject: [PATCH 20/87] more boilerplate --- .../contrib/kernelVariables/variablesPanel.ts | 21 ++++++++++--------- .../contrib/kernelVariables/variablesView.ts | 14 +++++++------ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts index 4e65671450c..a0b77ed5d67 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts @@ -3,10 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; +import { ILocalizedString } from 'vs/platform/action/common/action'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -14,12 +17,12 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; -import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { IViewDescriptor, IViewDescriptorService } from 'vs/workbench/common/views'; export class VariablesPanel extends ViewPane { - static readonly ID = TUNNEL_VIEW_ID; + static readonly ID = 'variablesView'; static readonly TITLE: ILocalizedString = nls.localize2('remote.tunnel', "Ports"); constructor( @@ -42,10 +45,10 @@ export class VariablesPanel extends ViewPane { } } -export class TunnelPanelDescriptor implements IViewDescriptor { - readonly id = TunnelPanel.ID; - readonly name: ILocalizedString = TunnelPanel.TITLE; - readonly ctorDescriptor: SyncDescriptor; +export class VariablesPanelDescriptor implements IViewDescriptor { + readonly id = VariablesPanel.ID; + readonly name: ILocalizedString = VariablesPanel.TITLE; + readonly ctorDescriptor: SyncDescriptor; readonly canToggleVisibility = true; readonly hideByDefault = false; // group is not actually used for views that are not extension contributed. Use order instead. @@ -54,10 +57,8 @@ export class TunnelPanelDescriptor implements IViewDescriptor { readonly order = -500; readonly remoteAuthority?: string | string[]; readonly canMoveView = true; - readonly containerIcon = portsViewIcon; - constructor(viewModel: ITunnelViewModel, environmentService: IWorkbenchEnvironmentService) { - this.ctorDescriptor = new SyncDescriptor(TunnelPanel, [viewModel]); - this.remoteAuthority = environmentService.remoteAuthority ? environmentService.remoteAuthority.split('+')[0] : undefined; + constructor() { + this.ctorDescriptor = new SyncDescriptor(VariablesPanel); } } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts index c78b9772d98..806eb39ec3b 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts @@ -7,8 +7,10 @@ import * as nls from 'vs/nls'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions, IViewContainersRegistry, IViewsRegistry } from 'vs/workbench/common/views'; +import { Extensions, IViewContainersRegistry, IViewsRegistry, ViewContainerLocation } from 'vs/workbench/common/views'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { VariablesPanelDescriptor } from 'vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; const VARIABLES_VIEW_CONTAINER_ID = 'variablesViewContainer'; @@ -30,11 +32,11 @@ export class VariablesView extends Disposable implements IWorkbenchContribution viewsRegistry.registerViews([variablesPanelDescriptor!], viewContainer); } } else { - this.contextKeyListener = this.contextKeyService.onDidChangeContext(e => { - if (e.affectsSome(new Set(forwardedPortsViewEnabled.keys()))) { - this.enableForwardedPortsView(); - } - }); + // this.contextKeyListener = this.contextKeyService.onDidChangeContext(e => { + // if (e.affectsSome()) { + // this.enableVariablesView(); + // } + // }); } } From 9b313c37f878ae1574e7e9866b8a1d18ae0bf75a Mon Sep 17 00:00:00 2001 From: aamunger Date: Thu, 9 Nov 2023 09:19:54 -0800 Subject: [PATCH 21/87] move view into debug container --- .../debug/browser/debug.contribution.ts | 3 ++ .../contrib/kernelVariables/variablesView.ts | 54 ------------------- .../notebookVariablesView.ts} | 40 +++++++++----- .../notebookVariables/variablesView.ts | 42 +++++++++++++++ .../notebook/browser/notebook.contribution.ts | 3 ++ .../contrib/notebook/browser/notebookIcons.ts | 1 + 6 files changed, 77 insertions(+), 66 deletions(-) delete mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts rename src/vs/workbench/contrib/notebook/browser/contrib/{kernelVariables/variablesPanel.ts => notebookVariables/notebookVariablesView.ts} (62%) create mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/variablesView.ts diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 4ff1a98bd73..e956a5de039 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -52,6 +52,8 @@ import { BREAKPOINTS_VIEW_ID, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CALLSTACK_VIEW_ import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; import { DebugLifecycle } from 'vs/workbench/contrib/debug/common/debugLifecycle'; import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput'; +import { NotebookVariablesView } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView'; +import { NOTEBOOK_KERNEL } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -413,6 +415,7 @@ viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize2('callS viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize2('breakpoints', "Breakpoints"), containerIcon: icons.breakpointsViewIcon, ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default'), CONTEXT_HAS_DEBUGGED) }], viewContainer); viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: icons.runViewIcon, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer); viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize2('loadedScripts', "Loaded Scripts"), containerIcon: icons.loadedScriptsViewIcon, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); +viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize2('notebookVariables', "Notebook Variables"), containerIcon: icons.variablesViewIcon, ctorDescriptor: new SyncDescriptor(NotebookVariablesView), order: 50, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: NOTEBOOK_KERNEL }], viewContainer); // Register disassembly view diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.ts deleted file mode 100644 index 806eb39ec3b..00000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesView.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 nls from 'vs/nls'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions, IViewContainersRegistry, IViewsRegistry, ViewContainerLocation } from 'vs/workbench/common/views'; -import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { VariablesPanelDescriptor } from 'vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel'; -import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; - -const VARIABLES_VIEW_CONTAINER_ID = 'variablesViewContainer'; - -export class VariablesView extends Disposable implements IWorkbenchContribution { - - constructor() { - super(); - this.enableVariablesView(); - } - - private async enableVariablesView() { - const viewEnabled = true; - - if (viewEnabled) { - const viewContainer = await this.getViewContainer(); - const variablesPanelDescriptor = new VariablesPanelDescriptor(); - const viewsRegistry = Registry.as(Extensions.ViewsRegistry); - if (viewContainer) { - viewsRegistry.registerViews([variablesPanelDescriptor!], viewContainer); - } - } else { - // this.contextKeyListener = this.contextKeyService.onDidChangeContext(e => { - // if (e.affectsSome()) { - // this.enableVariablesView(); - // } - // }); - } - } - - getViewContainer() { - return Registry.as(Extensions.ViewContainersRegistry).registerViewContainer({ - id: VARIABLES_VIEW_CONTAINER_ID, - title: { value: nls.localize('variables', "Kernel Variables"), original: 'Kernel Variables' }, - ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [VARIABLES_VIEW_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true }]), - storageId: VARIABLES_VIEW_CONTAINER_ID, - hideIfEmpty: true, - order: 5 - }, ViewContainerLocation.Panel); - } - -} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts similarity index 62% rename from src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts rename to src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index a0b77ed5d67..ef977aa48b3 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/kernelVariables/variablesPanel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -18,15 +18,20 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewDescriptor, IViewDescriptorService } from 'vs/workbench/common/views'; +import { getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { variablesViewIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; +import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +export class NotebookVariablesView extends ViewPane { -export class VariablesPanel extends ViewPane { - - static readonly ID = 'variablesView'; - static readonly TITLE: ILocalizedString = nls.localize2('remote.tunnel', "Ports"); + static readonly ID = 'notebookVariablesView'; + static readonly TITLE: ILocalizedString = nls.localize2('notebook.notebookVariables', "Notebook Variables"); constructor( options: IViewPaneOptions, + @IEditorService private readonly editorService: IEditorService, + @INotebookKernelService private readonly notebookKernelService: INotebookKernelService, @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, @IContextKeyService contextKeyService: IContextKeyService, @@ -41,24 +46,35 @@ export class VariablesPanel extends ViewPane { ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + this._register(this.editorService.onDidActiveEditorChange(this.handleActiveEditorChange)); + } + private handleActiveEditorChange() { + const activeEditorPane = this.editorService.activeEditorPane; + if (activeEditorPane && activeEditorPane.getId() === 'workbench.editor.notebook') { + const notebookModel = getNotebookEditorFromEditorPane(activeEditorPane)?.textModel; + if (notebookModel) { + const selectedKernel = this.notebookKernelService.getMatchingKernel(notebookModel).selected; + console.log(selectedKernel?.id); + } + } } } export class VariablesPanelDescriptor implements IViewDescriptor { - readonly id = VariablesPanel.ID; - readonly name: ILocalizedString = VariablesPanel.TITLE; - readonly ctorDescriptor: SyncDescriptor; + readonly id = NotebookVariablesView.ID; + readonly name: ILocalizedString = NotebookVariablesView.TITLE; + readonly ctorDescriptor: SyncDescriptor; readonly canToggleVisibility = true; readonly hideByDefault = false; - // group is not actually used for views that are not extension contributed. Use order instead. - readonly group = 'details@0'; - // -500 comes from the remote explorer viewOrderDelegate - readonly order = -500; + readonly order = 500; readonly remoteAuthority?: string | string[]; readonly canMoveView = true; + readonly containerIcon = variablesViewIcon; constructor() { - this.ctorDescriptor = new SyncDescriptor(VariablesPanel); + this.ctorDescriptor = new SyncDescriptor(NotebookVariablesView); } } + + diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/variablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/variablesView.ts new file mode 100644 index 00000000000..aee3e1e6f4c --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/variablesView.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IViewContainersRegistry } from 'vs/workbench/common/views'; +import { VIEWLET_ID as debugContainerId } from 'vs/workbench/contrib/debug/common/debug'; + + +export class NotebookVariables extends Disposable implements IWorkbenchContribution { + + constructor() { + super(); + this.enableVariablesView(); + } + + private async enableVariablesView() { + const viewEnabled = true; + + if (viewEnabled) { + const debugViewContainer = Registry.as('workbench.registry.view.containers').get(debugContainerId); + + if (debugViewContainer) { + // const variablesPanelDescriptor = new VariablesPanelDescriptor(); + // const viewsRegistry = Registry.as(Extensions.ViewsRegistry); + + // viewsRegistry.registerViews([variablesPanelDescriptor], debugViewContainer); + } + + } else { + // this.contextKeyListener = this.contextKeyService.onDidChangeContext(e => { + // if (e.affectsSome()) { + // this.enableVariablesView(); + // } + // }); + } + } + +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 9689de4040b..fc47fa84be9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -119,6 +119,8 @@ import { runAccessibilityHelpAction, showAccessibleOutput } from 'vs/workbench/c import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { AccessibilityHelpAction, AccessibleViewAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; +import { NotebookVariables } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/variablesView'; + /*--------------------------------------------------------------------------------------------- */ Registry.as(EditorExtensions.EditorPane).registerEditorPane( @@ -718,6 +720,7 @@ workbenchContributionsRegistry.registerWorkbenchContribution(NotebookLanguageSel workbenchContributionsRegistry.registerWorkbenchContribution(SimpleNotebookWorkingCopyEditorHandler, LifecyclePhase.Ready); workbenchContributionsRegistry.registerWorkbenchContribution(NotebookAccessibilityHelpContribution, LifecyclePhase.Eventually); workbenchContributionsRegistry.registerWorkbenchContribution(NotebookAccessibleViewContribution, LifecyclePhase.Eventually); +workbenchContributionsRegistry.registerWorkbenchContribution(NotebookVariables, LifecyclePhase.Eventually); registerSingleton(INotebookService, NotebookService, InstantiationType.Delayed); registerSingleton(INotebookEditorWorkerService, NotebookEditorWorkerServiceImpl, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts b/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts index 69c932935eb..20ffe3867e8 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts @@ -36,3 +36,4 @@ export const copyIcon = registerIcon('notebook-copy', Codicon.copy, localize('co export const previousChangeIcon = registerIcon('notebook-diff-editor-previous-change', Codicon.arrowUp, localize('previousChangeIcon', 'Icon for the previous change action in the diff editor.')); export const nextChangeIcon = registerIcon('notebook-diff-editor-next-change', Codicon.arrowDown, localize('nextChangeIcon', 'Icon for the next change action in the diff editor.')); +export const variablesViewIcon = registerIcon('variables-view-icon', Codicon.debugAlt, localize('variablesViewIcon', 'View icon of the variables view.')); From f76f831fd11a25bfa5f65b2039904a1990ddb92d Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Thu, 9 Nov 2023 15:47:10 -0800 Subject: [PATCH 22/87] more instantiation fixes --- .../debug/browser/debug.contribution.ts | 3 - .../notebookVariables/notebookVariables.ts | 53 ++++++++ .../notebookVariablesTree.ts | 120 ++++++++++++++++++ .../notebookVariablesView.ts | 35 ++--- .../notebookVariables/variablesView.ts | 42 ------ .../notebook/browser/notebook.contribution.ts | 2 +- 6 files changed, 188 insertions(+), 67 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts create mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts delete mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/variablesView.ts diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index e956a5de039..4ff1a98bd73 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -52,8 +52,6 @@ import { BREAKPOINTS_VIEW_ID, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CALLSTACK_VIEW_ import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; import { DebugLifecycle } from 'vs/workbench/contrib/debug/common/debugLifecycle'; import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput'; -import { NotebookVariablesView } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView'; -import { NOTEBOOK_KERNEL } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -415,7 +413,6 @@ viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize2('callS viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize2('breakpoints', "Breakpoints"), containerIcon: icons.breakpointsViewIcon, ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default'), CONTEXT_HAS_DEBUGGED) }], viewContainer); viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, containerIcon: icons.runViewIcon, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 1, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer); viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize2('loadedScripts', "Loaded Scripts"), containerIcon: icons.loadedScriptsViewIcon, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); -viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize2('notebookVariables', "Notebook Variables"), containerIcon: icons.variablesViewIcon, ctorDescriptor: new SyncDescriptor(NotebookVariablesView), order: 50, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: NOTEBOOK_KERNEL }], viewContainer); // Register disassembly view diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts new file mode 100644 index 00000000000..e1d6227a76d --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions, IViewContainersRegistry, IViewsRegistry } from 'vs/workbench/common/views'; +import { VIEWLET_ID as debugContainerId } from 'vs/workbench/contrib/debug/common/debug'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { NotebookVariablesView } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView'; +import { NOTEBOOK_KERNEL } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { variablesViewIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + + +export class NotebookVariables extends Disposable implements IWorkbenchContribution { + private listener: IDisposable; + + constructor( + @IEditorService private readonly editorService: IEditorService + ) { + super(); + this.initializeView(); + + this.listener = this.editorService.onDidEditorsChange(() => { + if (this.editorService.activeEditorPane?.getId() === 'workbench.editor.notebook') { + this.listener.dispose(); + this.initializeView(); + } + }); + } + + private async initializeView() { + + const debugViewContainer = Registry.as('workbench.registry.view.containers').get(debugContainerId); + + if (debugViewContainer) { + const viewsRegistry = Registry.as(Extensions.ViewsRegistry); + const viewDescriptor = { + id: 'NOTEBOOK_VARIABLES', name: nls.localize2('notebookVariables', "Notebook Variables"), containerIcon: variablesViewIcon, ctorDescriptor: new SyncDescriptor(NotebookVariablesView), + order: 50, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.notEquals(NOTEBOOK_KERNEL.key, ''), + }; + + viewsRegistry.registerViews([viewDescriptor], debugViewContainer); + } + + } + +} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts new file mode 100644 index 00000000000..c643cda1c65 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts @@ -0,0 +1,120 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { IAsyncDataSource, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { FuzzyScore } from 'vs/base/common/filters'; +import { localize } from 'vs/nls'; +import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; + +export abstract class VariablesSession { + abstract getVariables(): Promise; +} + +export interface IVariableTreeElement { + readonly id: string; + readonly label: string; + readonly value: string; + readonly hasChildren: boolean; + getChildren(): Promise; +} + +export class NotebookVariablesTree extends WorkbenchAsyncDataTree { } + +export class NotebookVariablesDelegate implements IListVirtualDelegate { + + getHeight(element: IVariableTreeElement): number { + return 22; + } + + getTemplateId(element: IVariableTreeElement): string { + return 'variableElement'; + } +} + +export class NotebookVariableRenderer implements ITreeRenderer { + + get templateId(): string { + return 'variableElement'; + } + + renderTemplate(container: HTMLElement) { + return { container }; + } + + renderElement(element: ITreeNode, index: number, templateData: { container: HTMLElement }, height: number | undefined): void { + templateData.container.innerText = element.element.label; + } + + disposeTemplate(): void { + // noop + } +} + +export class NotebookVariablesDataSource implements IAsyncDataSource { + + hasChildren(element: VariablesSession | IVariableTreeElement): boolean { + if (element instanceof VariablesSession) { + return true; + } + return element.hasChildren; + } + + getChildren(element: VariablesSession | IVariableTreeElement): Promise { + if (element instanceof VariablesSession) { + return element.getVariables(); + } + + return element.getChildren(); + } +} + +export class NotebookVariableAccessibilityProvider implements IListAccessibilityProvider { + + getWidgetAriaLabel(): string { + return localize('debugConsole', "Notebook Variables"); + } + + getAriaLabel(element: IVariableTreeElement): string { + return localize('notebookVariableAriaLabel', "Variable {0}, value {1}", element.label, element.value); + } +} + +export class MockVariables extends VariablesSession { + override getVariables(): Promise { + return Promise.resolve([ + { + id: '1', + label: 'Variable 1', + value: 'Value 1', + hasChildren: true, + getChildren: async () => [ + { + id: '1.1', + label: 'Child Variable 1', + value: 'Child Value 1', + hasChildren: false, + getChildren: async () => [] + }, + { + id: '1.2', + label: 'Child Variable 2', + value: 'Child Value 2', + hasChildren: false, + getChildren: async () => [] + } + ] + }, + { + id: '2', + label: 'Variable 2', + value: 'Value 2', + hasChildren: false, + getChildren: async () => [] + } + ]); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index ef977aa48b3..14dd4f69c23 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -9,7 +9,6 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -17,9 +16,9 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; -import { IViewDescriptor, IViewDescriptorService } from 'vs/workbench/common/views'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { MockVariables, NotebookVariableAccessibilityProvider, NotebookVariableRenderer, NotebookVariablesDataSource, NotebookVariablesDelegate, NotebookVariablesTree } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree'; import { getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { variablesViewIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -28,6 +27,8 @@ export class NotebookVariablesView extends ViewPane { static readonly ID = 'notebookVariablesView'; static readonly TITLE: ILocalizedString = nls.localize2('notebook.notebookVariables', "Notebook Variables"); + private tree: NotebookVariablesTree | undefined; + constructor( options: IViewPaneOptions, @IEditorService private readonly editorService: IEditorService, @@ -49,6 +50,16 @@ export class NotebookVariablesView extends ViewPane { this._register(this.editorService.onDidActiveEditorChange(this.handleActiveEditorChange)); } + public override renderBody(container: HTMLElement): void { + this.tree = this.instantiationService.createInstance(NotebookVariablesTree, 'notebookVariablesView', container, + new NotebookVariablesDelegate(), + [new NotebookVariableRenderer()], + new NotebookVariablesDataSource(), + { accessibilityProvider: new NotebookVariableAccessibilityProvider() }); + + this.tree.setInput(new MockVariables()); + } + private handleActiveEditorChange() { const activeEditorPane = this.editorService.activeEditorPane; if (activeEditorPane && activeEditorPane.getId() === 'workbench.editor.notebook') { @@ -60,21 +71,3 @@ export class NotebookVariablesView extends ViewPane { } } } - -export class VariablesPanelDescriptor implements IViewDescriptor { - readonly id = NotebookVariablesView.ID; - readonly name: ILocalizedString = NotebookVariablesView.TITLE; - readonly ctorDescriptor: SyncDescriptor; - readonly canToggleVisibility = true; - readonly hideByDefault = false; - readonly order = 500; - readonly remoteAuthority?: string | string[]; - readonly canMoveView = true; - readonly containerIcon = variablesViewIcon; - - constructor() { - this.ctorDescriptor = new SyncDescriptor(NotebookVariablesView); - } -} - - diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/variablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/variablesView.ts deleted file mode 100644 index aee3e1e6f4c..00000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/variablesView.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. - *--------------------------------------------------------------------------------------------*/ - -import { Disposable } from 'vs/base/common/lifecycle'; -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IViewContainersRegistry } from 'vs/workbench/common/views'; -import { VIEWLET_ID as debugContainerId } from 'vs/workbench/contrib/debug/common/debug'; - - -export class NotebookVariables extends Disposable implements IWorkbenchContribution { - - constructor() { - super(); - this.enableVariablesView(); - } - - private async enableVariablesView() { - const viewEnabled = true; - - if (viewEnabled) { - const debugViewContainer = Registry.as('workbench.registry.view.containers').get(debugContainerId); - - if (debugViewContainer) { - // const variablesPanelDescriptor = new VariablesPanelDescriptor(); - // const viewsRegistry = Registry.as(Extensions.ViewsRegistry); - - // viewsRegistry.registerViews([variablesPanelDescriptor], debugViewContainer); - } - - } else { - // this.contextKeyListener = this.contextKeyService.onDidChangeContext(e => { - // if (e.affectsSome()) { - // this.enableVariablesView(); - // } - // }); - } - } - -} diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index fc47fa84be9..4936a25da52 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -119,7 +119,7 @@ import { runAccessibilityHelpAction, showAccessibleOutput } from 'vs/workbench/c import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { AccessibilityHelpAction, AccessibleViewAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; -import { NotebookVariables } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/variablesView'; +import { NotebookVariables } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables'; /*--------------------------------------------------------------------------------------------- */ From d39e29e6171880cfa07ac77612946e1c5b5b42be Mon Sep 17 00:00:00 2001 From: aamunger Date: Mon, 13 Nov 2023 10:40:08 -0800 Subject: [PATCH 23/87] Ive created a monster --- .../contrib/notebookVariables/notebookVariablesTree.ts | 6 ++++-- .../contrib/notebookVariables/notebookVariablesView.ts | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts index c643cda1c65..8427a77a9f1 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts @@ -31,14 +31,16 @@ export class NotebookVariablesDelegate implements IListVirtualDelegate { + static readonly ID = 'variableElement'; + get templateId(): string { - return 'variableElement'; + return NotebookVariableRenderer.ID; } renderTemplate(container: HTMLElement) { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index 14dd4f69c23..e439fc88239 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -51,6 +51,8 @@ export class NotebookVariablesView extends ViewPane { } public override renderBody(container: HTMLElement): void { + super.renderBody(container); + this.tree = this.instantiationService.createInstance(NotebookVariablesTree, 'notebookVariablesView', container, new NotebookVariablesDelegate(), [new NotebookVariableRenderer()], From 85a73e7fcc34379463b6ffa91367cce9be1d8134 Mon Sep 17 00:00:00 2001 From: aamunger Date: Mon, 13 Nov 2023 16:31:44 -0800 Subject: [PATCH 24/87] fix tree layout, add proposed API --- .../notebookVariablesTree.ts | 183 ++++++++++++------ .../notebookVariablesView.ts | 58 +++++- .../common/extensionsApiProposals.ts | 1 + ...ode.proposed.notebookVariableProvider.d.ts | 43 ++++ 4 files changed, 214 insertions(+), 71 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts index 8427a77a9f1..7ccdbca6479 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts @@ -3,39 +3,34 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as dom from 'vs/base/browser/dom'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { IAsyncDataSource, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { IObjectTreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { FuzzyScore } from 'vs/base/common/filters'; import { localize } from 'vs/nls'; -import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; +import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; -export abstract class VariablesSession { - abstract getVariables(): Promise; -} - -export interface IVariableTreeElement { +export interface INotebookVariableElement { readonly id: string; readonly label: string; readonly value: string; - readonly hasChildren: boolean; - getChildren(): Promise; } -export class NotebookVariablesTree extends WorkbenchAsyncDataTree { } +export class NotebookVariablesTree extends WorkbenchObjectTree { } -export class NotebookVariablesDelegate implements IListVirtualDelegate { +export class NotebookVariablesDelegate implements IListVirtualDelegate { - getHeight(element: IVariableTreeElement): number { + getHeight(element: INotebookVariableElement): number { return 22; } - getTemplateId(element: IVariableTreeElement): string { + getTemplateId(element: INotebookVariableElement): string { return NotebookVariableRenderer.ID; } } -export class NotebookVariableRenderer implements ITreeRenderer { +export class NotebookVariableRenderer implements ITreeRenderer { static readonly ID = 'variableElement'; @@ -44,11 +39,12 @@ export class NotebookVariableRenderer implements ITreeRenderer, index: number, templateData: { container: HTMLElement }, height: number | undefined): void { - templateData.container.innerText = element.element.label; + renderElement(element: ITreeNode, index: number, templateData: { wrapper: HTMLElement }, height: number | undefined): void { + templateData.wrapper.innerText = `${element.element.label} - ${element.element.value}`; } disposeTemplate(): void { @@ -56,67 +52,132 @@ export class NotebookVariableRenderer implements ITreeRenderer { - - hasChildren(element: VariablesSession | IVariableTreeElement): boolean { - if (element instanceof VariablesSession) { - return true; - } - return element.hasChildren; - } - - getChildren(element: VariablesSession | IVariableTreeElement): Promise { - if (element instanceof VariablesSession) { - return element.getVariables(); - } - - return element.getChildren(); - } -} - -export class NotebookVariableAccessibilityProvider implements IListAccessibilityProvider { +export class NotebookVariableAccessibilityProvider implements IListAccessibilityProvider { getWidgetAriaLabel(): string { return localize('debugConsole', "Notebook Variables"); } - getAriaLabel(element: IVariableTreeElement): string { + getAriaLabel(element: INotebookVariableElement): string { return localize('notebookVariableAriaLabel', "Variable {0}, value {1}", element.label, element.value); } } -export class MockVariables extends VariablesSession { - override getVariables(): Promise { - return Promise.resolve([ - { +export function mockVariables(notebook: string, kernel: string): IObjectTreeElement[] { + return [ + { + element: { id: '1', - label: 'Variable 1', - value: 'Value 1', - hasChildren: true, - getChildren: async () => [ - { + label: 'Notebook', + value: notebook, + }, + children: [ + { + element: { id: '1.1', - label: 'Child Variable 1', - value: 'Child Value 1', - hasChildren: false, - getChildren: async () => [] - }, - { + label: 'Kernel', + value: kernel, + } + }, + { + element: { id: '1.2', label: 'Child Variable 2', value: 'Child Value 2', - hasChildren: false, - getChildren: async () => [] } - ] - }, - { + } + ], + collapsed: true + }, + { + element: { id: '2', label: 'Variable 2', value: 'Value 2', - hasChildren: false, - getChildren: async () => [] + }, + children: [ + { + element: { + id: '2.1', + label: 'Child Variable 1', + value: 'Child Value 1', + } + }, + { + element: { + id: '2.2', + label: 'Child Variable 2', + value: 'Child Value 2', + } + } + ], + collapsed: true + }, + { + element: { + id: '3', + label: 'Variable 3', + value: 'Value 3', } - ]); - } + }, + { + element: { + id: '4', + label: 'Variable 4', + value: 'Value 4', + }, + children: [ + { + element: { + id: '4.1', + label: 'Child Variable 1', + value: 'Child Value 1', + } + }, + { + element: { + id: '4.2', + label: 'Child Variable 2', + value: 'Child Value 2', + } + } + ], + collapsed: true + }, + { + element: { + id: '5', + label: 'Variable 5', + value: 'Value 5', + } + }, + { + element: { + id: '6', + label: 'Variable 6', + value: 'Value 6', + } + }, + { + element: { + id: '7', + label: 'Variable 7', + value: 'Value 7', + } + }, + { + element: { + id: '8', + label: 'Variable 8', + value: 'Value 8', + } + }, + { + element: { + id: '9', + label: 'Variable 9', + value: 'Value 9', + } + } + ]; } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index e439fc88239..7d11cf31d74 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -11,14 +11,17 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewDescriptorService } from 'vs/workbench/common/views'; -import { MockVariables, NotebookVariableAccessibilityProvider, NotebookVariableRenderer, NotebookVariablesDataSource, NotebookVariablesDelegate, NotebookVariablesTree } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree'; +import { mockVariables, NotebookVariableAccessibilityProvider, NotebookVariableRenderer, INotebookVariableElement, NotebookVariablesDelegate, NotebookVariablesTree } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree'; import { getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; +import { ICellExecutionStateChangedEvent, IExecutionStateChangedEvent, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -28,11 +31,13 @@ export class NotebookVariablesView extends ViewPane { static readonly TITLE: ILocalizedString = nls.localize2('notebook.notebookVariables', "Notebook Variables"); private tree: NotebookVariablesTree | undefined; + private activeNotebook: NotebookTextModel | undefined; constructor( options: IViewPaneOptions, @IEditorService private readonly editorService: IEditorService, @INotebookKernelService private readonly notebookKernelService: INotebookKernelService, + @INotebookExecutionStateService private readonly notebookExecutionStateService: INotebookExecutionStateService, @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, @IContextKeyService contextKeyService: IContextKeyService, @@ -47,29 +52,62 @@ export class NotebookVariablesView extends ViewPane { ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); - this._register(this.editorService.onDidActiveEditorChange(this.handleActiveEditorChange)); + this._register(this.editorService.onDidActiveEditorChange(this.handleActiveEditorChange.bind(this))); + this._register(this.notebookExecutionStateService.onDidChangeExecution(this.handleExecutionStateChange.bind(this))); } public override renderBody(container: HTMLElement): void { super.renderBody(container); - this.tree = this.instantiationService.createInstance(NotebookVariablesTree, 'notebookVariablesView', container, + this.tree = this.instantiationService.createInstance( + WorkbenchObjectTree, + 'notebookVariablesTree', + container, new NotebookVariablesDelegate(), [new NotebookVariableRenderer()], - new NotebookVariablesDataSource(), - { accessibilityProvider: new NotebookVariableAccessibilityProvider() }); + { + identityProvider: { getId: (e: INotebookVariableElement) => e.id }, + horizontalScrolling: false, + supportDynamicHeights: true, + hideTwistiesOfChildlessElements: true, + accessibilityProvider: new NotebookVariableAccessibilityProvider(), + setRowLineHeight: false, + }); - this.tree.setInput(new MockVariables()); + this.tree.layout(); + this.tree?.setChildren(null, []); + } + + protected override layoutBody(height: number, width: number): void { + super.layoutBody(height, width); + this.tree?.layout(height, width); } private handleActiveEditorChange() { const activeEditorPane = this.editorService.activeEditorPane; if (activeEditorPane && activeEditorPane.getId() === 'workbench.editor.notebook') { - const notebookModel = getNotebookEditorFromEditorPane(activeEditorPane)?.textModel; - if (notebookModel) { - const selectedKernel = this.notebookKernelService.getMatchingKernel(notebookModel).selected; - console.log(selectedKernel?.id); + const notebookDocument = getNotebookEditorFromEditorPane(activeEditorPane)?.getViewModel()?.notebookDocument; + if (notebookDocument && notebookDocument !== this.activeNotebook) { + this.activeNotebook = notebookDocument; + this.updateVariables(this.activeNotebook); } } } + + private handleExecutionStateChange(event: ICellExecutionStateChangedEvent | IExecutionStateChangedEvent) { + if (this.activeNotebook) { + // changed === undefined -> excecution ended + if (event.changed === undefined && event.affectsNotebook(this.activeNotebook?.uri)) { + this.updateVariables(this.activeNotebook); + } + } + } + + private updateVariables(notebook: NotebookTextModel) { + const selectedKernel = this.notebookKernelService.getMatchingKernel(notebook).selected; + if (selectedKernel) { + + this.tree?.setChildren(null, mockVariables(notebook.uri.toString(), selectedKernel.label)); + } + } } diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 770411e3e93..e29607525e3 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -69,6 +69,7 @@ export const allApiProposals = Object.freeze({ notebookLiveShare: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookLiveShare.d.ts', notebookMessaging: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts', notebookMime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMime.d.ts', + notebookVariableProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts', portsAttributes: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.portsAttributes.d.ts', profileContentHandlers: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.profileContentHandlers.d.ts', quickDiffProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickDiffProvider.d.ts', diff --git a/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts new file mode 100644 index 00000000000..7f9a76d138d --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.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. + *--------------------------------------------------------------------------------------------*/ +declare module 'vscode' { + + export interface NotebookController { + /** Set this to attach a variable provider to this controller. */ + variableProvider?: NotebookVariableProvider; + } + + export enum VariablesRequestKind { + Named, + Indexed + } + + interface VariablesResult { + variable: Variable; + namedVariableCount: number; + indexedVariableCount: number; + } + + interface NotebookVariableProvider { + onDidChangeVariables: Event; + + /** When variablesReference is undefined, this is requesting global Variables. When a variable is passed, it's requesting child props of that Variable. */ + provideChildren(notebook: NotebookDocument, variable: Variable | undefined, kind: VariablesRequestKind, start: number, token: CancellationToken): AsyncIterable; + } + + interface Variable { + /** The variable's name. */ + name: string; + + /** The variable's value. + This can be a multi-line text, e.g. for a function the body of a function. + For structured variables (which do not have a simple value), it is recommended to provide a one-line representation of the structured object. + This helps to identify the structured object in the collapsed state when its children are not yet visible. + An empty string can be used if no value should be shown in the UI. + */ + value: string; + } + +} From c282c5a98d404a6b8eac431d5c12d94b47e3c67a Mon Sep 17 00:00:00 2001 From: aamunger Date: Tue, 14 Nov 2023 13:13:59 -0800 Subject: [PATCH 25/87] implement extension host proxy --- .../api/browser/mainThreadNotebookKernels.ts | 37 ++++++++++++++++++- .../workbench/api/common/extHost.protocol.ts | 4 ++ .../api/common/extHostNotebookKernels.ts | 29 +++++++++++++++ src/vs/workbench/api/common/extHostTypes.ts | 2 + .../notebook/common/notebookKernelService.ts | 15 ++++++++ .../browser/notebookExecutionService.test.ts | 7 +++- .../notebookExecutionStateService.test.ts | 8 +++- .../browser/notebookKernelHistory.test.ts | 7 +++- .../browser/notebookKernelService.test.ts | 7 +++- ...ode.proposed.notebookVariableProvider.d.ts | 11 ++---- 10 files changed, 114 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index f85d1d111b8..34e8baaf676 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -16,10 +16,11 @@ import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/ext import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { INotebookCellExecution, INotebookExecution, INotebookExecutionStateService, NotebookExecutionType } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { IKernelSourceActionProvider, INotebookKernel, INotebookKernelChangeEvent, INotebookKernelDetectionTask, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { IKernelSourceActionProvider, INotebookKernel, INotebookKernelChangeEvent, INotebookKernelDetectionTask, INotebookKernelService, VariablesResult } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from '../common/extHost.protocol'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { AsyncIterableSource } from 'vs/base/common/async'; abstract class MainThreadKernel implements INotebookKernel { private readonly _onDidChange = new Emitter(); @@ -36,6 +37,7 @@ abstract class MainThreadKernel implements INotebookKernel { detail?: string; supportedLanguages: string[]; implementsExecutionOrder: boolean; + providesVariables: boolean; localResourceRoot: URI; public get preloadUris() { @@ -57,6 +59,7 @@ abstract class MainThreadKernel implements INotebookKernel { this.detail = data.detail; this.supportedLanguages = isNonEmptyArray(data.supportedLanguages) ? data.supportedLanguages : _languageService.getRegisteredLanguageIds(); this.implementsExecutionOrder = data.supportsExecutionOrder ?? false; + this.providesVariables = data.providesVariables ?? false; this.localResourceRoot = URI.revive(data.extensionLocation); this.preloads = data.preloads?.map(u => ({ uri: URI.revive(u.uri), provides: u.provides })) ?? []; } @@ -89,11 +92,16 @@ abstract class MainThreadKernel implements INotebookKernel { this.implementsInterrupt = data.supportsInterrupt; event.hasInterruptHandler = true; } + if (data.providesVariables !== undefined) { + this.providesVariables = data.providesVariables; + event.hasVariableProvider = true; + } this._onDidChange.fire(event); } abstract executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise; abstract cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; + abstract provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable; } class MainThreadKernelDetectionTask implements INotebookKernelDetectionTask { @@ -214,6 +222,15 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape return didSend; } + private variableRequestIndex = 0; + private variableRequestMap = new Map>(); + $receiveVariable(requestId: string, variable: VariablesResult) { + const source = this.variableRequestMap.get(requestId); + if (source) { + source.emitOne(variable); + } + } + // --- kernel adding/updating/removal async $addKernel(handle: number, data: INotebookKernelDto2): Promise { @@ -225,6 +242,24 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape async cancelNotebookCellExecution(uri: URI, handles: number[]): Promise { await that._proxy.$cancelCells(handle, uri, handles); } + provideVariables(notebookUri: URI, parentName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable { + const requestId = `${handle}variables${that.variableRequestIndex++}`; + if (that.variableRequestMap.has(requestId)) { + return that.variableRequestMap.get(requestId)!.asyncIterable; + } + + const source = new AsyncIterableSource(); + that.variableRequestMap.set(requestId, source); + that._proxy.$provideVariables(handle, requestId, notebookUri, parentName, kind, start, token).then(() => { + source.resolve(); + that.variableRequestMap.delete(requestId); + }).catch((err) => { + source.reject(err); + that.variableRequestMap.delete(requestId); + }); + + return source.asyncIterable; + } }(data, this._languageService); const listener = this._notebookKernelService.onDidChangeSelectedNotebooks(e => { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index ba6939ca741..c359b7f34ce 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -80,6 +80,7 @@ import { CandidatePort } from 'vs/workbench/services/remote/common/tunnelModel'; import { ITextQueryBuilderOptions } from 'vs/workbench/services/search/common/queryBuilder'; import * as search from 'vs/workbench/services/search/common/search'; import { ISaveProfileResult } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { VariablesResult } from 'vscode'; export interface IWorkspaceData extends IStaticWorkspaceData { folders: { uri: UriComponents; name: string; index: number }[]; @@ -1075,6 +1076,7 @@ export interface INotebookKernelDto2 { supportsInterrupt?: boolean; supportsExecutionOrder?: boolean; preloads?: { uri: UriComponents; provides: readonly string[] }[]; + providesVariables?: boolean; } export interface INotebookProxyKernelDto { @@ -1131,6 +1133,7 @@ export interface MainThreadNotebookKernelsShape extends IDisposable { $addKernelSourceActionProvider(handle: number, eventHandle: number, notebookType: string): Promise; $removeKernelSourceActionProvider(handle: number, eventHandle: number): void; $emitNotebookKernelSourceActionsChangeEvent(eventHandle: number): void; + $receiveVariable(requestId: string, variable: VariablesResult): void; } export interface MainThreadNotebookRenderersShape extends IDisposable { @@ -2538,6 +2541,7 @@ export interface ExtHostNotebookKernelsShape { $acceptKernelMessageFromRenderer(handle: number, editorId: string, message: any): void; $cellExecutionChanged(uri: UriComponents, cellHandle: number, state: notebookCommon.NotebookCellExecutionState | undefined): void; $provideKernelSourceActions(handle: number, token: CancellationToken): Promise; + $provideVariables(handle: number, requestId: string, notebookUri: UriComponents, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): Promise; } export interface ExtHostInteractiveShape { diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index a3ac51d0446..9c3f7d51006 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -125,6 +125,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { // let _executeHandler = handler ?? _defaultExecutHandler; let _interruptHandler: ((this: vscode.NotebookController, notebook: vscode.NotebookDocument) => void | Thenable) | undefined; + let _variableProvider: vscode.NotebookVariableProvider | undefined; this._proxy.$addKernel(handle, data).catch(err => { // this can happen when a kernel with that ID is already registered @@ -207,6 +208,14 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { data.supportsInterrupt = Boolean(value); _update(); }, + set variableProvider(value) { + _variableProvider = value; + data.providesVariables = !!value; + _update(); + }, + get variableProvider() { + return _variableProvider; + }, createNotebookCellExecution(cell) { if (isDisposed) { throw new Error('notebook controller is DISPOSED'); @@ -404,6 +413,26 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { } } + async $provideVariables(handle: number, requestId: string, notebookUri: UriComponents, parentName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): Promise { + const obj = this._kernelData.get(handle); + if (!obj) { + return; + } + + const document = this._extHostNotebook.getNotebookDocument(URI.revive(notebookUri)); + const variableProvider = obj.controller.variableProvider; + if (!variableProvider) { + return; + } + + + const parent = parentName ? { name: parentName, value: '' } : undefined; + const variables = variableProvider.provideVariables(document.apiNotebook, parent, kind, start, token); + for await (const variable of variables) { + this._proxy.$receiveVariable(requestId, variable); + } + } + $acceptKernelMessageFromRenderer(handle: number, editorId: string, message: any): void { const obj = this._kernelData.get(handle); if (!obj) { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index df40c53d3be..0d76391cb38 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3798,6 +3798,8 @@ export class NotebookKernelSourceAction { ) { } } + + //#endregion //#region Timeline diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index f2dacb01c0e..1c84142dc24 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IAction } from 'vs/base/common/actions'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; @@ -33,6 +34,18 @@ export interface INotebookKernelChangeEvent { supportedLanguages?: true; hasExecutionOrder?: true; hasInterruptHandler?: true; + hasVariableProvider?: true; +} + +export interface Variable { + name: string; + value: string; +} + +export interface VariablesResult { + variable: Variable; + namedChildrenCount: number; + indexedChildrenCount: number; } export interface INotebookKernel { @@ -54,6 +67,8 @@ export interface INotebookKernel { executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise; cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; + + provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable; } export const enum ProxyKernelState { diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts index 76853078119..ab789e1909d 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts @@ -5,6 +5,8 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; +import { AsyncIterableObject } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; @@ -23,7 +25,7 @@ import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CellKind, IOutputDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernel, INotebookKernelHistoryService, INotebookKernelService, INotebookTextModelLike } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelHistoryService, INotebookKernelService, INotebookTextModelLike, VariablesResult } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; @@ -172,6 +174,9 @@ class TestNotebookKernel implements INotebookKernel { preloadUris: URI[] = []; preloadProvides: string[] = []; supportedLanguages: string[] = []; + provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable { + return AsyncIterableObject.EMPTY; + } executeNotebookCellsRequest(): Promise { throw new Error('Method not implemented.'); } diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts index 8874152bdb9..8ab64b129aa 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { DeferredPromise } from 'vs/base/common/async'; +import { AsyncIterableObject, DeferredPromise } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; @@ -22,7 +23,7 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { CellEditType, CellKind, CellUri, IOutputDto, NotebookCellMetadata, NotebookExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CellExecutionUpdateType, INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { INotebookExecutionStateService, NotebookExecutionType } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService, VariablesResult } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; @@ -361,6 +362,9 @@ class TestNotebookKernel implements INotebookKernel { supportedLanguages: string[] = []; async executeNotebookCellsRequest(): Promise { } async cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise { } + provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable { + return AsyncIterableObject.EMPTY; + } constructor(opts?: { languages?: string[]; id?: string }) { this.supportedLanguages = opts?.languages ?? [PLAINTEXT_LANGUAGE_ID]; 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 24d459f9eef..4629593b604 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelHistory.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelHistory.test.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; import { Emitter, Event } from 'vs/base/common/event'; -import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService, VariablesResult } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookKernelService } from 'vs/workbench/contrib/notebook/browser/services/notebookKernelServiceImpl'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { mock } from 'vs/base/test/common/mock'; @@ -21,6 +21,8 @@ import { NotebookKernelHistoryService } from 'vs/workbench/contrib/notebook/brow import { IApplicationStorageValueChangeEvent, IProfileStorageValueChangeEvent, IStorageService, IStorageValueChangeEvent, IWillSaveStateEvent, IWorkspaceStorageValueChangeEvent, StorageScope } from 'vs/platform/storage/common/storage'; import { INotebookLoggingService } from 'vs/workbench/contrib/notebook/common/notebookLoggingService'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { AsyncIterableObject } from 'vs/base/common/async'; suite('NotebookKernelHistoryService', () => { @@ -184,6 +186,9 @@ class TestNotebookKernel implements INotebookKernel { cancelNotebookCellExecution(): Promise { throw new Error('Method not implemented.'); } + provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable { + return AsyncIterableObject.EMPTY; + } constructor(opts?: { languages?: string[]; label?: string; viewType?: string }) { this.supportedLanguages = opts?.languages ?? [PLAINTEXT_LANGUAGE_ID]; 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 61664a2bfa1..4825829ebf1 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; import { Emitter, Event } from 'vs/base/common/event'; -import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService, VariablesResult } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookKernelService } from 'vs/workbench/contrib/notebook/browser/services/notebookKernelServiceImpl'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { mock } from 'vs/base/test/common/mock'; @@ -19,6 +19,8 @@ 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'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { AsyncIterableObject } from 'vs/base/common/async'; suite('NotebookKernelService', () => { @@ -197,6 +199,9 @@ class TestNotebookKernel implements INotebookKernel { cancelNotebookCellExecution(): Promise { throw new Error('Method not implemented.'); } + provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable { + return AsyncIterableObject.EMPTY; + } constructor(opts?: { languages?: string[]; label?: string; viewType?: string }) { this.supportedLanguages = opts?.languages ?? [PLAINTEXT_LANGUAGE_ID]; diff --git a/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts index 7f9a76d138d..ce457f4617f 100644 --- a/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts @@ -9,22 +9,19 @@ declare module 'vscode' { variableProvider?: NotebookVariableProvider; } - export enum VariablesRequestKind { - Named, - Indexed - } + export type VariablesRequestKind = 'named' | 'indexed'; interface VariablesResult { variable: Variable; - namedVariableCount: number; - indexedVariableCount: number; + namedChildrenCount: number; + indexedChildrenCount: number; } interface NotebookVariableProvider { onDidChangeVariables: Event; /** When variablesReference is undefined, this is requesting global Variables. When a variable is passed, it's requesting child props of that Variable. */ - provideChildren(notebook: NotebookDocument, variable: Variable | undefined, kind: VariablesRequestKind, start: number, token: CancellationToken): AsyncIterable; + provideVariables(notebook: NotebookDocument, parent: Variable | undefined, kind: VariablesRequestKind, start: number, token: CancellationToken): AsyncIterable; } interface Variable { From 6f6d0a7e18a4a6b368b28fd5eba37c9142cc7da1 Mon Sep 17 00:00:00 2001 From: aamunger Date: Wed, 15 Nov 2023 10:00:12 -0800 Subject: [PATCH 26/87] display variables provided by extension --- .../api/browser/mainThreadNotebookKernels.ts | 6 +-- .../notebookVariablesView.ts | 50 +++++++++++++++++-- .../notebook/common/notebookKernelService.ts | 4 +- .../browser/notebookExecutionService.test.ts | 2 +- .../notebookExecutionStateService.test.ts | 2 +- .../browser/notebookKernelHistory.test.ts | 2 +- .../browser/notebookKernelService.test.ts | 2 +- ...ode.proposed.notebookVariableProvider.d.ts | 2 +- 8 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index 34e8baaf676..9b13bc0bfd7 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -20,7 +20,7 @@ import { IKernelSourceActionProvider, INotebookKernel, INotebookKernelChangeEven import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from '../common/extHost.protocol'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; -import { AsyncIterableSource } from 'vs/base/common/async'; +import { AsyncIterableObject, AsyncIterableSource } from 'vs/base/common/async'; abstract class MainThreadKernel implements INotebookKernel { private readonly _onDidChange = new Emitter(); @@ -101,7 +101,7 @@ abstract class MainThreadKernel implements INotebookKernel { abstract executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise; abstract cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; - abstract provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable; + abstract provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterableObject; } class MainThreadKernelDetectionTask implements INotebookKernelDetectionTask { @@ -242,7 +242,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape async cancelNotebookCellExecution(uri: URI, handles: number[]): Promise { await that._proxy.$cancelCells(handle, uri, handles); } - provideVariables(notebookUri: URI, parentName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable { + provideVariables(notebookUri: URI, parentName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterableObject { const requestId = `${handle}variables${that.variableRequestIndex++}`; if (that.variableRequestMap.has(requestId)) { return that.variableRequestMap.get(requestId)!.asyncIterable; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index 7d11cf31d74..86778116866 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IObjectTreeElement } from 'vs/base/browser/ui/tree/tree'; +import { CancellationToken } from 'vs/base/common/cancellation'; import * as nls from 'vs/nls'; import { ILocalizedString } from 'vs/platform/action/common/action'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -18,11 +20,11 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewDescriptorService } from 'vs/workbench/common/views'; -import { mockVariables, NotebookVariableAccessibilityProvider, NotebookVariableRenderer, INotebookVariableElement, NotebookVariablesDelegate, NotebookVariablesTree } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree'; +import { NotebookVariableAccessibilityProvider, NotebookVariableRenderer, INotebookVariableElement, NotebookVariablesDelegate, NotebookVariablesTree } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree'; import { getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { ICellExecutionStateChangedEvent, IExecutionStateChangedEvent, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelService, VariablesResult } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export class NotebookVariablesView extends ViewPane { @@ -103,11 +105,49 @@ export class NotebookVariablesView extends ViewPane { } } - private updateVariables(notebook: NotebookTextModel) { + private async updateVariables(notebook: NotebookTextModel) { const selectedKernel = this.notebookKernelService.getMatchingKernel(notebook).selected; - if (selectedKernel) { + if (selectedKernel && selectedKernel.providesVariables) { - this.tree?.setChildren(null, mockVariables(notebook.uri.toString(), selectedKernel.label)); + const variables = selectedKernel.provideVariables(notebook.uri, undefined, 'named', 0, CancellationToken.None); + const treeData = await variables + .map(variable => { return this.createTreeItem(variable); }) + .toPromise(); + + this.tree?.setChildren(null, treeData); } } + + private index = 0; + + private createTreeItem(variable: VariablesResult): IObjectTreeElement { + let collapsed: boolean | undefined = undefined; + let children: IObjectTreeElement[] | undefined = undefined; + if (variable.namedChildrenCount > 0 || variable.indexedChildrenCount > 0) { + collapsed = true; + children = [ + { + element: { + id: `${this.index + 1}-placeholder`, + label: ' ', + value: 'loading...', + } + } + ]; + } + + const element = { + element: { + id: `${this.index++}`, + label: variable.variable.name, + value: variable.variable.value, + }, + children, + collapsed + }; + + + + return element; + } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index 1c84142dc24..1658d7f8dea 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IAction } from 'vs/base/common/actions'; +import { AsyncIterableObject } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -64,11 +65,12 @@ export interface INotebookKernel { supportedLanguages: string[]; implementsInterrupt?: boolean; implementsExecutionOrder?: boolean; + providesVariables?: boolean; executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise; cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; - provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable; + provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterableObject; } export const enum ProxyKernelState { diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts index ab789e1909d..a471c3c1ed7 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts @@ -174,7 +174,7 @@ class TestNotebookKernel implements INotebookKernel { preloadUris: URI[] = []; preloadProvides: string[] = []; supportedLanguages: string[] = []; - provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable { + provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterableObject { return AsyncIterableObject.EMPTY; } executeNotebookCellsRequest(): Promise { diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts index 8ab64b129aa..6e608d7e03a 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts @@ -362,7 +362,7 @@ class TestNotebookKernel implements INotebookKernel { supportedLanguages: string[] = []; async executeNotebookCellsRequest(): Promise { } async cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise { } - provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable { + provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterableObject { return AsyncIterableObject.EMPTY; } 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 4629593b604..084f7e5f0b0 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelHistory.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelHistory.test.ts @@ -186,7 +186,7 @@ class TestNotebookKernel implements INotebookKernel { cancelNotebookCellExecution(): Promise { throw new Error('Method not implemented.'); } - provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable { + provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterableObject { return AsyncIterableObject.EMPTY; } 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 4825829ebf1..f82e97ae7fa 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts @@ -199,7 +199,7 @@ class TestNotebookKernel implements INotebookKernel { cancelNotebookCellExecution(): Promise { throw new Error('Method not implemented.'); } - provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterable { + provideVariables(notebookUri: URI, variableName: string | undefined, kind: 'named' | 'indexed', start: number, token: CancellationToken): AsyncIterableObject { return AsyncIterableObject.EMPTY; } diff --git a/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts index ce457f4617f..2e3711220bf 100644 --- a/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts @@ -20,7 +20,7 @@ declare module 'vscode' { interface NotebookVariableProvider { onDidChangeVariables: Event; - /** When variablesReference is undefined, this is requesting global Variables. When a variable is passed, it's requesting child props of that Variable. */ + /** When parent is undefined, this is requesting global Variables. When a variable is passed, it's requesting child props of that Variable. */ provideVariables(notebook: NotebookDocument, parent: Variable | undefined, kind: VariablesRequestKind, start: number, token: CancellationToken): AsyncIterable; } From ac59af460c662df843add92e32c9d203abc11668 Mon Sep 17 00:00:00 2001 From: aamunger Date: Thu, 16 Nov 2023 08:53:20 -0800 Subject: [PATCH 27/87] remove mock variables --- .../notebookVariablesTree.ts | 121 +----------------- 1 file changed, 1 insertion(+), 120 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts index 7ccdbca6479..1fbda38b993 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts @@ -6,7 +6,7 @@ import * as dom from 'vs/base/browser/dom'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { IObjectTreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { FuzzyScore } from 'vs/base/common/filters'; import { localize } from 'vs/nls'; import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; @@ -62,122 +62,3 @@ export class NotebookVariableAccessibilityProvider implements IListAccessibility return localize('notebookVariableAriaLabel', "Variable {0}, value {1}", element.label, element.value); } } - -export function mockVariables(notebook: string, kernel: string): IObjectTreeElement[] { - return [ - { - element: { - id: '1', - label: 'Notebook', - value: notebook, - }, - children: [ - { - element: { - id: '1.1', - label: 'Kernel', - value: kernel, - } - }, - { - element: { - id: '1.2', - label: 'Child Variable 2', - value: 'Child Value 2', - } - } - ], - collapsed: true - }, - { - element: { - id: '2', - label: 'Variable 2', - value: 'Value 2', - }, - children: [ - { - element: { - id: '2.1', - label: 'Child Variable 1', - value: 'Child Value 1', - } - }, - { - element: { - id: '2.2', - label: 'Child Variable 2', - value: 'Child Value 2', - } - } - ], - collapsed: true - }, - { - element: { - id: '3', - label: 'Variable 3', - value: 'Value 3', - } - }, - { - element: { - id: '4', - label: 'Variable 4', - value: 'Value 4', - }, - children: [ - { - element: { - id: '4.1', - label: 'Child Variable 1', - value: 'Child Value 1', - } - }, - { - element: { - id: '4.2', - label: 'Child Variable 2', - value: 'Child Value 2', - } - } - ], - collapsed: true - }, - { - element: { - id: '5', - label: 'Variable 5', - value: 'Value 5', - } - }, - { - element: { - id: '6', - label: 'Variable 6', - value: 'Value 6', - } - }, - { - element: { - id: '7', - label: 'Variable 7', - value: 'Value 7', - } - }, - { - element: { - id: '8', - label: 'Variable 8', - value: 'Value 8', - } - }, - { - element: { - id: '9', - label: 'Variable 9', - value: 'Value 9', - } - } - ]; -} From 74d6858c83fcd0ff47841f55fda9503f9f0c6998 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Thu, 16 Nov 2023 14:23:48 -0800 Subject: [PATCH 28/87] naming, formatting --- src/vs/workbench/api/browser/mainThreadNotebookKernels.ts | 8 ++++---- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostNotebookKernels.ts | 6 ++++-- src/vs/workbench/api/common/extHostTypes.ts | 2 -- .../contrib/notebookVariables/notebookVariablesView.ts | 2 +- .../contrib/notebook/common/notebookKernelService.ts | 2 +- .../vscode.proposed.notebookVariableProvider.d.ts | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index 9b13bc0bfd7..196ecaea1ea 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -37,7 +37,7 @@ abstract class MainThreadKernel implements INotebookKernel { detail?: string; supportedLanguages: string[]; implementsExecutionOrder: boolean; - providesVariables: boolean; + hasVariableProvider: boolean; localResourceRoot: URI; public get preloadUris() { @@ -59,7 +59,7 @@ abstract class MainThreadKernel implements INotebookKernel { this.detail = data.detail; this.supportedLanguages = isNonEmptyArray(data.supportedLanguages) ? data.supportedLanguages : _languageService.getRegisteredLanguageIds(); this.implementsExecutionOrder = data.supportsExecutionOrder ?? false; - this.providesVariables = data.providesVariables ?? false; + this.hasVariableProvider = data.hasVariableProvider ?? false; this.localResourceRoot = URI.revive(data.extensionLocation); this.preloads = data.preloads?.map(u => ({ uri: URI.revive(u.uri), provides: u.provides })) ?? []; } @@ -92,8 +92,8 @@ abstract class MainThreadKernel implements INotebookKernel { this.implementsInterrupt = data.supportsInterrupt; event.hasInterruptHandler = true; } - if (data.providesVariables !== undefined) { - this.providesVariables = data.providesVariables; + if (data.hasVariableProvider !== undefined) { + this.hasVariableProvider = data.hasVariableProvider; event.hasVariableProvider = true; } this._onDidChange.fire(event); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index c359b7f34ce..7bb1c7cc4b4 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1076,7 +1076,7 @@ export interface INotebookKernelDto2 { supportsInterrupt?: boolean; supportsExecutionOrder?: boolean; preloads?: { uri: UriComponents; provides: readonly string[] }[]; - providesVariables?: boolean; + hasVariableProvider?: boolean; } export interface INotebookProxyKernelDto { diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 9c3f7d51006..78d7e39d90f 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -210,7 +210,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { }, set variableProvider(value) { _variableProvider = value; - data.providesVariables = !!value; + data.hasVariableProvider = !!value; _update(); }, get variableProvider() { @@ -425,10 +425,12 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { return; } - const parent = parentName ? { name: parentName, value: '' } : undefined; const variables = variableProvider.provideVariables(document.apiNotebook, parent, kind, start, token); for await (const variable of variables) { + if (token.isCancellationRequested) { + return; + } this._proxy.$receiveVariable(requestId, variable); } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 0d76391cb38..df40c53d3be 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3798,8 +3798,6 @@ export class NotebookKernelSourceAction { ) { } } - - //#endregion //#region Timeline diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index 86778116866..19728ad4a38 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -107,7 +107,7 @@ export class NotebookVariablesView extends ViewPane { private async updateVariables(notebook: NotebookTextModel) { const selectedKernel = this.notebookKernelService.getMatchingKernel(notebook).selected; - if (selectedKernel && selectedKernel.providesVariables) { + if (selectedKernel && selectedKernel.hasVariableProvider) { const variables = selectedKernel.provideVariables(notebook.uri, undefined, 'named', 0, CancellationToken.None); const treeData = await variables diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index 1658d7f8dea..30c9bc59f57 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -65,7 +65,7 @@ export interface INotebookKernel { supportedLanguages: string[]; implementsInterrupt?: boolean; implementsExecutionOrder?: boolean; - providesVariables?: boolean; + hasVariableProvider?: boolean; executeNotebookCellsRequest(uri: URI, cellHandles: number[]): Promise; cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; diff --git a/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts index 2e3711220bf..7f7bcd26c92 100644 --- a/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts @@ -9,7 +9,7 @@ declare module 'vscode' { variableProvider?: NotebookVariableProvider; } - export type VariablesRequestKind = 'named' | 'indexed'; + type VariablesRequestKind = 'named' | 'indexed'; interface VariablesResult { variable: Variable; From fc6b8579564afaf984d501abf7922f6aa6494779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 17 Nov 2023 18:54:55 +0100 Subject: [PATCH 29/87] Workaround for AZDO artifact throttling (#198545) * download and extract in main thread, spin off other threads for rest of IO * remove limiters: everything runs on workers now * add docs --- build/azure-pipelines/common/publish.js | 156 +++++++++------------ build/azure-pipelines/common/publish.ts | 178 +++++++++--------------- 2 files changed, 131 insertions(+), 203 deletions(-) diff --git a/build/azure-pipelines/common/publish.js b/build/azure-pipelines/common/publish.js index f34a446c916..80792caeb0a 100644 --- a/build/azure-pipelines/common/publish.js +++ b/build/azure-pipelines/common/publish.js @@ -4,7 +4,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); -exports.Limiter = void 0; const fs = require("fs"); const path = require("path"); const stream_1 = require("stream"); @@ -18,6 +17,7 @@ const cosmos_1 = require("@azure/cosmos"); const identity_1 = require("@azure/identity"); const cp = require("child_process"); const os = require("os"); +const node_worker_threads_1 = require("node:worker_threads"); function e(name) { const result = process.env[name]; if (typeof result !== 'string') { @@ -43,41 +43,6 @@ class Temp { } } } -class Limiter { - _size = 0; - runningPromises; - maxDegreeOfParalellism; - outstandingPromises; - constructor(maxDegreeOfParalellism) { - this.maxDegreeOfParalellism = maxDegreeOfParalellism; - this.outstandingPromises = []; - this.runningPromises = 0; - } - queue(factory) { - this._size++; - return new Promise((c, e) => { - this.outstandingPromises.push({ factory, c, e }); - this.consume(); - }); - } - consume() { - while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) { - const iLimitedTask = this.outstandingPromises.shift(); - this.runningPromises++; - const promise = iLimitedTask.factory(); - promise.then(iLimitedTask.c, iLimitedTask.e); - promise.then(() => this.consumed(), () => this.consumed()); - } - } - consumed() { - this._size--; - this.runningPromises--; - if (this.outstandingPromises.length > 0) { - this.consume(); - } - } -} -exports.Limiter = Limiter; class ProvisionService { log; accessToken; @@ -133,7 +98,6 @@ function hashStream(hashName, stream) { class ESRPClient { log; tmp; - static Limiter = new Limiter(1); authPath; constructor(log, tmp, tenantId, clientId, authCertSubjectName, requestSigningCertSubjectName) { this.log = log; @@ -158,10 +122,8 @@ class ESRPClient { })); } async release(version, filePath) { - const submitReleaseResult = await ESRPClient.Limiter.queue(async () => { - this.log(`Submitting release for ${version}: ${filePath}`); - return await this.SubmitRelease(version, filePath); - }); + this.log(`Submitting release for ${version}: ${filePath}`); + const submitReleaseResult = await this.SubmitRelease(version, filePath); if (submitReleaseResult.submissionResponse.statusCode !== 'pass') { throw new Error(`Unexpected status code: ${submitReleaseResult.submissionResponse.statusCode}`); } @@ -505,8 +467,6 @@ function getRealType(type) { return type; } } -const azureLimiter = new Limiter(1); -const mooncakeLimiter = new Limiter(1); async function uploadAssetLegacy(log, quality, commit, filePath) { const fileName = path.basename(filePath); const blobName = commit + '/' + fileName; @@ -529,11 +489,11 @@ async function uploadAssetLegacy(log, quality, commit, filePath) { throw new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`); } else { - await (0, retry_1.retry)(attempt => azureLimiter.queue(async () => { + await (0, retry_1.retry)(async (attempt) => { log(`Uploading blobs to Azure storage (attempt ${attempt})...`); await blobClient.uploadFile(filePath, blobOptions); log('Blob successfully uploaded to Azure storage.'); - })); + }); } })()); const shouldUploadToMooncake = /true/i.test(e('VSCODE_PUBLISH_TO_MOONCAKE')); @@ -548,11 +508,11 @@ async function uploadAssetLegacy(log, quality, commit, filePath) { throw new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); } else { - await (0, retry_1.retry)(attempt => mooncakeLimiter.queue(async () => { + await (0, retry_1.retry)(async (attempt) => { log(`Uploading blobs to Mooncake Azure storage (attempt ${attempt})...`); await mooncakeBlobClient.uploadFile(filePath, blobOptions); log('Blob successfully uploaded to Mooncake Azure storage.'); - })); + }); } })()); } @@ -569,71 +529,53 @@ async function uploadAssetLegacy(log, quality, commit, filePath) { // eslint-disable-next-line no-throw-literal throw rejectedPromiseResults[0]?.reason; } - const assetUrl = `${e('AZURE_CDN_URL')}/${quality}/${blobName}`; + const assetUrl = `${e('AZURE_CDN_URL')} / ${quality} / ${blobName}`; const blobPath = new URL(assetUrl).pathname; const mooncakeUrl = `${e('MOONCAKE_CDN_URL')}${blobPath}`; return { assetUrl, mooncakeUrl }; } -const downloadLimiter = new Limiter(5); -const cosmosLimiter = new Limiter(1); -async function processArtifact(artifact) { +async function processArtifact(artifact, artifactFilePath) { + const log = (...args) => console.log(`[${artifact.name}]`, ...args); const match = /^vscode_(?[^_]+)_(?[^_]+)_(?[^_]+)_(?[^_]+)$/.exec(artifact.name); if (!match) { throw new Error(`Invalid artifact name: ${artifact.name}`); } - const { product, os, arch, unprocessedType } = match.groups; - const log = (...args) => console.log(`[${product} ${os} ${arch} ${unprocessedType}]`, ...args); - const start = Date.now(); - const filePath = await (0, retry_1.retry)(async (attempt) => { - const artifactZipPath = path.join(e('AGENT_TEMPDIRECTORY'), `${artifact.name}.zip`); - const start = Date.now(); - log(`Downloading ${artifact.resource.downloadUrl} (attempt ${attempt})...`); - try { - await downloadLimiter.queue(() => downloadArtifact(artifact, artifactZipPath)); - } - catch (err) { - log(`Download failed: ${err.message}`); - throw err; - } - const archiveSize = fs.statSync(artifactZipPath).size; - const downloadDurationS = (Date.now() - start) / 1000; - const downloadSpeedKBS = Math.round((archiveSize / 1024) / downloadDurationS); - log(`Successfully downloaded ${artifact.resource.downloadUrl} after ${Math.floor(downloadDurationS)} seconds (${downloadSpeedKBS} KB/s).`); - const filePath = await unzip(artifactZipPath, e('AGENT_TEMPDIRECTORY')); - const artifactSize = fs.statSync(filePath).size; - if (artifactSize !== Number(artifact.resource.properties.artifactsize)) { - log(`Artifact size mismatch. Expected ${artifact.resource.properties.artifactsize}. Actual ${artifactSize}`); - throw new Error(`Artifact size mismatch.`); - } - return filePath; - }); - log(`Successfully downloaded and extracted after ${(Date.now() - start) / 1000} seconds.`); // getPlatform needs the unprocessedType const quality = e('VSCODE_QUALITY'); const commit = e('BUILD_SOURCEVERSION'); + const { product, os, arch, unprocessedType } = match.groups; const platform = getPlatform(product, os, arch, unprocessedType); const type = getRealType(unprocessedType); - const size = fs.statSync(filePath).size; - const stream = fs.createReadStream(filePath); + const size = fs.statSync(artifactFilePath).size; + const stream = fs.createReadStream(artifactFilePath); const [sha1hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); const [{ assetUrl, mooncakeUrl }, prssUrl] = await Promise.all([ - uploadAssetLegacy(log, quality, commit, filePath), - releaseAndProvision(log, e('RELEASE_TENANT_ID'), e('RELEASE_CLIENT_ID'), e('RELEASE_AUTH_CERT_SUBJECT_NAME'), e('RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME'), e('PROVISION_TENANT_ID'), e('PROVISION_AAD_USERNAME'), e('PROVISION_AAD_PASSWORD'), commit, quality, filePath) + uploadAssetLegacy(log, quality, commit, artifactFilePath), + releaseAndProvision(log, e('RELEASE_TENANT_ID'), e('RELEASE_CLIENT_ID'), e('RELEASE_AUTH_CERT_SUBJECT_NAME'), e('RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME'), e('PROVISION_TENANT_ID'), e('PROVISION_AAD_USERNAME'), e('PROVISION_AAD_PASSWORD'), commit, quality, artifactFilePath) ]); const asset = { platform, type, url: assetUrl, hash: sha1hash, mooncakeUrl, prssUrl, sha256hash, size, supportsFastUpdate: true }; log('Creating asset...', JSON.stringify(asset)); await (0, retry_1.retry)(async (attempt) => { - await cosmosLimiter.queue(async () => { - log(`Creating asset in Cosmos DB (attempt ${attempt})...`); - const aadCredentials = new identity_1.ClientSecretCredential(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_CLIENT_SECRET')); - const client = new cosmos_1.CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT'), aadCredentials }); - const scripts = client.database('builds').container(quality).scripts; - await scripts.storedProcedure('createAsset').execute('', [commit, asset, true]); - }); + log(`Creating asset in Cosmos DB(attempt ${attempt})...`); + const aadCredentials = new identity_1.ClientSecretCredential(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_CLIENT_SECRET')); + const client = new cosmos_1.CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT'), aadCredentials }); + const scripts = client.database('builds').container(quality).scripts; + await scripts.storedProcedure('createAsset').execute('', [commit, asset, true]); }); log('Asset successfully created'); } +// It is VERY important that we don't download artifacts too much too fast from AZDO. +// AZDO throttles us SEVERELY if we do. Not just that, but they also close open +// sockets, so the whole things turns to a grinding halt. So, downloading and extracting +// happens serially in the main thread, making the downloads are spaced out +// properly. For each extracted artifact, we spawn a worker thread to upload it to +// the CDN and finally update the build in Cosmos DB. async function main() { + if (!node_worker_threads_1.isMainThread) { + const { artifact, artifactFilePath } = node_worker_threads_1.workerData; + await processArtifact(artifact, artifactFilePath); + return; + } const done = new State(); const processing = new Set(); for (const name of done) { @@ -677,12 +619,40 @@ async function main() { if (done.has(artifact.name) || processing.has(artifact.name)) { continue; } - console.log(`Found new artifact: ${artifact.name}`); + console.log(`${artifact.name} Found new artifact`); + const artifactZipPath = path.join(e('AGENT_TEMPDIRECTORY'), `${artifact.name}.zip`); + await (0, retry_1.retry)(async (attempt) => { + const start = Date.now(); + console.log(`[${artifact.name}]Downloading(attempt ${attempt})...`); + await downloadArtifact(artifact, artifactZipPath); + const archiveSize = fs.statSync(artifactZipPath).size; + const downloadDurationS = (Date.now() - start) / 1000; + const downloadSpeedKBS = Math.round((archiveSize / 1024) / downloadDurationS); + console.log(`[${artifact.name}] Successfully downloaded after ${Math.floor(downloadDurationS)} seconds(${downloadSpeedKBS} KB / s).`); + }); + const artifactFilePath = await unzip(artifactZipPath, e('AGENT_TEMPDIRECTORY')); + const artifactSize = fs.statSync(artifactFilePath).size; + if (artifactSize !== Number(artifact.resource.properties.artifactsize)) { + console.log(`[${artifact.name}] Artifact size mismatch.Expected ${artifact.resource.properties.artifactsize}. Actual ${artifactSize} `); + throw new Error(`Artifact size mismatch.`); + } processing.add(artifact.name); - const operation = processArtifact(artifact).then(() => { + const promise = new Promise((resolve, reject) => { + const worker = new node_worker_threads_1.Worker(__filename, { workerData: { artifact, artifactFilePath } }); + worker.on('error', reject); + worker.on('exit', code => { + if (code === 0) { + resolve(); + } + else { + reject(new Error('Worker stopped with exit code ${code}')); + } + }); + }); + const operation = promise.then(() => { processing.delete(artifact.name); done.add(artifact.name); - console.log(`\u2705 ${artifact.name}`); + console.log(`\u2705 ${artifact.name} `); }); operations.push({ name: artifact.name, operation }); } @@ -713,4 +683,4 @@ if (require.main === module) { process.exit(1); }); } -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGlzaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInB1Ymxpc2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOzs7QUFFaEcseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3QixtQ0FBa0M7QUFFbEMsbURBQWdEO0FBQ2hELCtCQUErQjtBQUMvQixpQ0FBaUM7QUFDakMsbUNBQWdDO0FBQ2hDLHNEQUF3STtBQUN4SSw2QkFBNkI7QUFDN0IsMENBQTZDO0FBQzdDLDhDQUF5RDtBQUN6RCxvQ0FBb0M7QUFDcEMseUJBQXlCO0FBRXpCLFNBQVMsQ0FBQyxDQUFDLElBQVk7SUFDdEIsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUVqQyxJQUFJLE9BQU8sTUFBTSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ2hDLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0JBQWdCLElBQUksRUFBRSxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUVELE9BQU8sTUFBTSxDQUFDO0FBQ2YsQ0FBQztBQUVELE1BQU0sSUFBSTtJQUNELE1BQU0sR0FBYSxFQUFFLENBQUM7SUFFOUIsV0FBVztRQUNWLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDNUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdkIsT0FBTyxJQUFJLENBQUM7SUFDYixDQUFDO0lBRUQsT0FBTztRQUNOLEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2hDLElBQUksQ0FBQztnQkFDSixFQUFFLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3JCLENBQUM7WUFBQyxPQUFP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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGlzaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInB1Ymxpc2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyx5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLG1DQUFrQztBQUVsQyxtREFBZ0Q7QUFDaEQsK0JBQStCO0FBQy9CLGlDQUFpQztBQUNqQyxtQ0FBZ0M7QUFDaEMsc0RBQXdJO0FBQ3hJLDZCQUE2QjtBQUM3QiwwQ0FBNkM7QUFDN0MsOENBQXlEO0FBQ3pELG9DQUFvQztBQUNwQyx5QkFBeUI7QUFDekIsNkRBQXVFO0FBRXZFLFNBQVMsQ0FBQyxDQUFDLElBQVk7SUFDdEIsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUVqQyxJQUFJLE9BQU8sTUFBTSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ2hDLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0JBQWdCLElBQUksRUFBRSxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUVELE9BQU8sTUFBTSxDQUFDO0FBQ2YsQ0FBQztBQUVELE1BQU0sSUFBSTtJQUNELE1BQU0sR0FBYSxFQUFFLENBQUM7SUFFOUIsV0FBVztRQUNWLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDNUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdkIsT0FBTyxJQUFJLENBQUM7SUFDYixDQUFDO0lBRUQsT0FBTztRQUNOLEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2hDLElBQUksQ0FBQztnQkFDSixFQUFFLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3JCLENB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 \ No newline at end of file diff --git a/build/azure-pipelines/common/publish.ts b/build/azure-pipelines/common/publish.ts index a338b2e4384..57fb617d710 100644 --- a/build/azure-pipelines/common/publish.ts +++ b/build/azure-pipelines/common/publish.ts @@ -17,6 +17,7 @@ import { CosmosClient } from '@azure/cosmos'; import { ClientSecretCredential } from '@azure/identity'; import * as cp from 'child_process'; import * as os from 'os'; +import { Worker, isMainThread, workerData } from 'node:worker_threads'; function e(name: string): string { const result = process.env[name]; @@ -48,49 +49,6 @@ class Temp { } } -export class Limiter { - - private _size = 0; - private runningPromises: number; - private readonly maxDegreeOfParalellism: number; - private readonly outstandingPromises: { factory: () => Promise; c: (v: any) => void; e: (err: Error) => void }[]; - - constructor(maxDegreeOfParalellism: number) { - this.maxDegreeOfParalellism = maxDegreeOfParalellism; - this.outstandingPromises = []; - this.runningPromises = 0; - } - - queue(factory: () => Promise): Promise { - this._size++; - - return new Promise((c, e) => { - this.outstandingPromises.push({ factory, c, e }); - this.consume(); - }); - } - - private consume(): void { - while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) { - const iLimitedTask = this.outstandingPromises.shift()!; - this.runningPromises++; - - const promise = iLimitedTask.factory(); - promise.then(iLimitedTask.c, iLimitedTask.e); - promise.then(() => this.consumed(), () => this.consumed()); - } - } - - private consumed(): void { - this._size--; - this.runningPromises--; - - if (this.outstandingPromises.length > 0) { - this.consume(); - } - } -} - interface RequestOptions { readonly body?: string; } @@ -196,8 +154,6 @@ interface ReleaseDetailsResult { class ESRPClient { - private static Limiter = new Limiter(1); - private readonly authPath: string; constructor( @@ -232,10 +188,8 @@ class ESRPClient { version: string, filePath: string ): Promise { - const submitReleaseResult = await ESRPClient.Limiter.queue(async () => { - this.log(`Submitting release for ${version}: ${filePath}`); - return await this.SubmitRelease(version, filePath); - }); + this.log(`Submitting release for ${version}: ${filePath}`); + const submitReleaseResult = await this.SubmitRelease(version, filePath); if (submitReleaseResult.submissionResponse.statusCode !== 'pass') { throw new Error(`Unexpected status code: ${submitReleaseResult.submissionResponse.statusCode}`); @@ -674,9 +628,6 @@ function getRealType(type: string) { } } -const azureLimiter = new Limiter(1); -const mooncakeLimiter = new Limiter(1); - async function uploadAssetLegacy(log: (...args: any[]) => void, quality: string, commit: string, filePath: string): Promise<{ assetUrl: string; mooncakeUrl: string }> { const fileName = path.basename(filePath); const blobName = commit + '/' + fileName; @@ -704,11 +655,11 @@ async function uploadAssetLegacy(log: (...args: any[]) => void, quality: string, if (await retry(() => blobClient.exists())) { throw new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`); } else { - await retry(attempt => azureLimiter.queue(async () => { + await retry(async attempt => { log(`Uploading blobs to Azure storage (attempt ${attempt})...`); await blobClient.uploadFile(filePath, blobOptions); log('Blob successfully uploaded to Azure storage.'); - })); + }); } })()); @@ -726,11 +677,11 @@ async function uploadAssetLegacy(log: (...args: any[]) => void, quality: string, if (await retry(() => mooncakeBlobClient.exists())) { throw new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); } else { - await retry(attempt => mooncakeLimiter.queue(async () => { + await retry(async attempt => { log(`Uploading blobs to Mooncake Azure storage (attempt ${attempt})...`); await mooncakeBlobClient.uploadFile(filePath, blobOptions); log('Blob successfully uploaded to Mooncake Azure storage.'); - })); + }); } })()); } @@ -748,69 +699,33 @@ async function uploadAssetLegacy(log: (...args: any[]) => void, quality: string, throw rejectedPromiseResults[0]?.reason; } - const assetUrl = `${e('AZURE_CDN_URL')}/${quality}/${blobName}`; + const assetUrl = `${e('AZURE_CDN_URL')} / ${quality} / ${blobName}`; const blobPath = new URL(assetUrl).pathname; const mooncakeUrl = `${e('MOONCAKE_CDN_URL')}${blobPath}`; return { assetUrl, mooncakeUrl }; } -const downloadLimiter = new Limiter(5); -const cosmosLimiter = new Limiter(1); - -async function processArtifact(artifact: Artifact): Promise { +async function processArtifact(artifact: Artifact, artifactFilePath: string): Promise { + const log = (...args: any[]) => console.log(`[${artifact.name}]`, ...args); const match = /^vscode_(?[^_]+)_(?[^_]+)_(?[^_]+)_(?[^_]+)$/.exec(artifact.name); if (!match) { throw new Error(`Invalid artifact name: ${artifact.name}`); } - const { product, os, arch, unprocessedType } = match.groups!; - const log = (...args: any[]) => console.log(`[${product} ${os} ${arch} ${unprocessedType}]`, ...args); - const start = Date.now(); - - const filePath = await retry(async attempt => { - const artifactZipPath = path.join(e('AGENT_TEMPDIRECTORY'), `${artifact.name}.zip`); - - const start = Date.now(); - log(`Downloading ${artifact.resource.downloadUrl} (attempt ${attempt})...`); - - try { - await downloadLimiter.queue(() => downloadArtifact(artifact, artifactZipPath)); - } catch (err) { - log(`Download failed: ${err.message}`); - throw err; - } - - const archiveSize = fs.statSync(artifactZipPath).size; - const downloadDurationS = (Date.now() - start) / 1000; - const downloadSpeedKBS = Math.round((archiveSize / 1024) / downloadDurationS); - log(`Successfully downloaded ${artifact.resource.downloadUrl} after ${Math.floor(downloadDurationS)} seconds (${downloadSpeedKBS} KB/s).`); - - const filePath = await unzip(artifactZipPath, e('AGENT_TEMPDIRECTORY')); - const artifactSize = fs.statSync(filePath).size; - - if (artifactSize !== Number(artifact.resource.properties.artifactsize)) { - log(`Artifact size mismatch. Expected ${artifact.resource.properties.artifactsize}. Actual ${artifactSize}`); - throw new Error(`Artifact size mismatch.`); - } - - return filePath; - }); - - log(`Successfully downloaded and extracted after ${(Date.now() - start) / 1000} seconds.`); - // getPlatform needs the unprocessedType const quality = e('VSCODE_QUALITY'); const commit = e('BUILD_SOURCEVERSION'); + const { product, os, arch, unprocessedType } = match.groups!; const platform = getPlatform(product, os, arch, unprocessedType); const type = getRealType(unprocessedType); - const size = fs.statSync(filePath).size; - const stream = fs.createReadStream(filePath); + const size = fs.statSync(artifactFilePath).size; + const stream = fs.createReadStream(artifactFilePath); const [sha1hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); const [{ assetUrl, mooncakeUrl }, prssUrl] = await Promise.all([ - uploadAssetLegacy(log, quality, commit, filePath), + uploadAssetLegacy(log, quality, commit, artifactFilePath), releaseAndProvision( log, e('RELEASE_TENANT_ID'), @@ -822,7 +737,7 @@ async function processArtifact(artifact: Artifact): Promise { e('PROVISION_AAD_PASSWORD'), commit, quality, - filePath + artifactFilePath ) ]); @@ -830,19 +745,29 @@ async function processArtifact(artifact: Artifact): Promise { log('Creating asset...', JSON.stringify(asset)); await retry(async (attempt) => { - await cosmosLimiter.queue(async () => { - log(`Creating asset in Cosmos DB (attempt ${attempt})...`); - const aadCredentials = new ClientSecretCredential(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_CLIENT_SECRET')); - const client = new CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT'), aadCredentials }); - const scripts = client.database('builds').container(quality).scripts; - await scripts.storedProcedure('createAsset').execute('', [commit, asset, true]); - }); + log(`Creating asset in Cosmos DB(attempt ${attempt})...`); + const aadCredentials = new ClientSecretCredential(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_CLIENT_SECRET')); + const client = new CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT'), aadCredentials }); + const scripts = client.database('builds').container(quality).scripts; + await scripts.storedProcedure('createAsset').execute('', [commit, asset, true]); }); log('Asset successfully created'); } +// It is VERY important that we don't download artifacts too much too fast from AZDO. +// AZDO throttles us SEVERELY if we do. Not just that, but they also close open +// sockets, so the whole things turns to a grinding halt. So, downloading and extracting +// happens serially in the main thread, making the downloads are spaced out +// properly. For each extracted artifact, we spawn a worker thread to upload it to +// the CDN and finally update the build in Cosmos DB. async function main() { + if (!isMainThread) { + const { artifact, artifactFilePath } = workerData; + await processArtifact(artifact, artifactFilePath); + return; + } + const done = new State(); const processing = new Set(); @@ -880,12 +805,45 @@ async function main() { continue; } - console.log(`Found new artifact: ${artifact.name}`); + console.log(`${artifact.name} Found new artifact`); + + const artifactZipPath = path.join(e('AGENT_TEMPDIRECTORY'), `${artifact.name}.zip`); + + await retry(async (attempt) => { + const start = Date.now(); + console.log(`[${artifact.name}]Downloading(attempt ${attempt})...`); + await downloadArtifact(artifact, artifactZipPath); + const archiveSize = fs.statSync(artifactZipPath).size; + const downloadDurationS = (Date.now() - start) / 1000; + const downloadSpeedKBS = Math.round((archiveSize / 1024) / downloadDurationS); + console.log(`[${artifact.name}] Successfully downloaded after ${Math.floor(downloadDurationS)} seconds(${downloadSpeedKBS} KB / s).`); + }); + + const artifactFilePath = await unzip(artifactZipPath, e('AGENT_TEMPDIRECTORY')); + const artifactSize = fs.statSync(artifactFilePath).size; + + if (artifactSize !== Number(artifact.resource.properties.artifactsize)) { + console.log(`[${artifact.name}] Artifact size mismatch.Expected ${artifact.resource.properties.artifactsize}. Actual ${artifactSize} `); + throw new Error(`Artifact size mismatch.`); + } + processing.add(artifact.name); - const operation = processArtifact(artifact).then(() => { + const promise = new Promise((resolve, reject) => { + const worker = new Worker(__filename, { workerData: { artifact, artifactFilePath } }); + worker.on('error', reject); + worker.on('exit', code => { + if (code === 0) { + resolve(); + } else { + reject(new Error('Worker stopped with exit code ${code}')); + } + }); + }); + + const operation = promise.then(() => { processing.delete(artifact.name); done.add(artifact.name); - console.log(`\u2705 ${artifact.name}`); + console.log(`\u2705 ${artifact.name} `); }); operations.push({ name: artifact.name, operation }); From 773e2b4dbfda2fabba86702d4b0016af74983f66 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 17 Nov 2023 18:40:04 +0100 Subject: [PATCH 30/87] Fixes #198432 --- .../diffEditorItemTemplate.ts | 51 +++++++++++------ .../multiDiffEditorViewModel.ts | 43 +++++++++++++++ .../multiDiffEditorWidget.ts | 14 ++++- .../multiDiffEditorWidgetImpl.ts | 55 ++++++------------- .../browser/multiDiffEditor.ts | 13 ++++- .../browser/multiDiffEditorInput.ts | 14 ++++- 6 files changed, 130 insertions(+), 60 deletions(-) create mode 100644 src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts b/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts index 7021e1a0147..4587abb6d44 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts @@ -6,8 +6,8 @@ import { h } from 'vs/base/browser/dom'; import { Button } from 'vs/base/browser/ui/button/button'; import { Codicon } from 'vs/base/common/codicons'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { autorun, derived } from 'vs/base/common/observable'; -import { globalTransaction, observableValue } from 'vs/base/common/observableInternal/base'; +import { autorun, derived, observableFromEvent } from 'vs/base/common/observable'; +import { IObservable, globalTransaction, observableValue } from 'vs/base/common/observableInternal/base'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { IDocumentDiffItem } from 'vs/editor/browser/widget/multiDiffEditorWidget/model'; import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory'; @@ -16,6 +16,7 @@ import { IDiffEditorViewModel } from 'vs/editor/common/editorCommon'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IObjectData, IPooledObject } from './objectPool'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; export class TemplateData implements IObjectData { constructor( @@ -85,10 +86,14 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< ]) ]); - private readonly _editor = this._register(this._instantiationService.createInstance(DiffEditorWidget, this._elements.editor, { + public readonly editor = this._register(this._instantiationService.createInstance(DiffEditorWidget, this._elements.editor, { overflowWidgetsDomNode: this._overflowWidgetsDomNode, }, {})); + private readonly isModifedFocused = isFocused(this.editor.getModifiedEditor()); + private readonly isOriginalFocused = isFocused(this.editor.getOriginalEditor()); + public readonly isFocused = derived(this, reader => this.isModifedFocused.read(reader) || this.isOriginalFocused.read(reader)); + private readonly _resourceLabel = this._workbenchUIElementFactory.createResourceLabel ? this._register(this._workbenchUIElementFactory.createResourceLabel(this._elements.title)) : undefined; @@ -114,21 +119,21 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< this._elements.editor.style.display = this._collapsed.get() ? 'none' : 'block'; })); - this._editor.getModifiedEditor().onDidLayoutChange(e => { - const width = this._editor.getModifiedEditor().getLayoutInfo().contentWidth; + this.editor.getModifiedEditor().onDidLayoutChange(e => { + const width = this.editor.getModifiedEditor().getLayoutInfo().contentWidth; this._modifiedWidth.set(width, undefined); }); - this._editor.getOriginalEditor().onDidLayoutChange(e => { - const width = this._editor.getOriginalEditor().getLayoutInfo().contentWidth; + this.editor.getOriginalEditor().onDidLayoutChange(e => { + const width = this.editor.getOriginalEditor().getLayoutInfo().contentWidth; this._originalWidth.set(width, undefined); }); - this._register(this._editor.onDidContentSizeChange(e => { + this._register(this.editor.onDidContentSizeChange(e => { globalTransaction(tx => { this._contentHeight.set(e.contentHeight, tx); - this._modifiedContentWidth.set(this._editor.getModifiedEditor().getContentWidth(), tx); - this._originalContentWidth.set(this._editor.getOriginalEditor().getContentWidth(), tx); + this._modifiedContentWidth.set(this.editor.getModifiedEditor().getContentWidth(), tx); + this._originalContentWidth.set(this.editor.getOriginalEditor().getContentWidth(), tx); }); })); @@ -138,9 +143,9 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< public setScrollLeft(left: number): void { if (this._modifiedContentWidth.get() - this._modifiedWidth.get() > this._originalContentWidth.get() - this._originalWidth.get()) { - this._editor.getModifiedEditor().setScrollLeft(left); + this.editor.getModifiedEditor().setScrollLeft(left); } else { - this._editor.getOriginalEditor().setScrollLeft(left); + this.editor.getOriginalEditor().setScrollLeft(left); } } @@ -169,12 +174,12 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< if (data.entry.onOptionsDidChange) { this._dataStore.add(data.entry.onOptionsDidChange(() => { - this._editor.updateOptions(updateOptions(data.entry.options ?? {})); + this.editor.updateOptions(updateOptions(data.entry.options ?? {})); })); } globalTransaction(tx => { - this._editor.setModel(data.viewModel, tx); - this._editor.updateOptions(updateOptions(data.entry.options ?? {})); + this.editor.setModel(data.viewModel, tx); + this.editor.updateOptions(updateOptions(data.entry.options ?? {})); }); } @@ -189,12 +194,12 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< this._elements.header.style.transform = `translateY(${Math.max(0, Math.min(verticalRange.length - this._elements.header.clientHeight, viewPort.start - verticalRange.start))}px)`; globalTransaction(tx => { - this._editor.layout({ + this.editor.layout({ width: width, height: verticalRange.length - this._outerEditorHeight, }); }); - this._editor.getOriginalEditor().setScrollTop(editorScroll); + this.editor.getOriginalEditor().setScrollTop(editorScroll); } public hide(): void { @@ -202,3 +207,15 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< this._elements.root.style.visibility = 'hidden'; // Some editor parts are still visible } } + +function isFocused(editor: ICodeEditor): IObservable { + return observableFromEvent( + h => { + const store = new DisposableStore(); + store.add(editor.onDidFocusEditorWidget(() => h(true))); + store.add(editor.onDidBlurEditorWidget(() => h(false))); + return store; + }, + () => editor.hasWidgetFocus() + ); +} diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts new file mode 100644 index 00000000000..5bd8541e52a --- /dev/null +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.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 { Disposable } from 'vs/base/common/lifecycle'; +import { derivedWithStore, observableFromEvent, observableValue } from 'vs/base/common/observable'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; +import { IDocumentDiffItem, IMultiDiffEditorModel, LazyPromise } from 'vs/editor/browser/widget/multiDiffEditorWidget/model'; +import { IDiffEditorViewModel } from 'vs/editor/common/editorCommon'; + +export class MultiDiffEditorViewModel extends Disposable { + private readonly _documents = observableFromEvent(this._model.onDidChange, /** @description MultiDiffEditorViewModel.documents */() => this._model.documents); + + public readonly items = derivedWithStore(this, + (reader, store) => this._documents.read(reader).map(d => store.add(new DocumentDiffItemViewModel(d, this._diffEditorViewModelFactory))) + ).recomputeInitiallyAndOnChange(this._store); + + public readonly activeDiffItem = observableValue(this, undefined); + + constructor( + private readonly _model: IMultiDiffEditorModel, + private readonly _diffEditorViewModelFactory: DiffEditorWidget + ) { + super(); + } +} + +export class DocumentDiffItemViewModel extends Disposable { + public readonly diffEditorViewModel: IDiffEditorViewModel; + + constructor( + public readonly entry: LazyPromise, + diffEditorViewModelFactory: DiffEditorWidget + ) { + super(); + + this.diffEditorViewModel = this._register(diffEditorViewModelFactory.createViewModel({ + original: entry.value!.original!, + modified: entry.value!.modified!, + })); + } +} diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget.ts b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget.ts index 69b47dfb5f5..97376e36d19 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget.ts +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget.ts @@ -5,14 +5,16 @@ import { Dimension } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; -import { derivedWithStore, observableValue, recomputeInitiallyAndOnChange } from 'vs/base/common/observable'; +import { derived, derivedWithStore, observableValue, recomputeInitiallyAndOnChange } from 'vs/base/common/observable'; import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditor/utils'; import { IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/model'; -import { MultiDiffEditorViewModel, MultiDiffEditorWidgetImpl } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl'; +import { MultiDiffEditorWidgetImpl } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl'; +import { MultiDiffEditorViewModel } from './multiDiffEditorViewModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import './colors'; import { DiffEditorItemTemplate } from 'vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate'; import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory'; +import { Event } from 'vs/base/common/event'; export class MultiDiffEditorWidget extends Disposable { private readonly _dimension = observableValue(this, undefined); @@ -50,4 +52,12 @@ export class MultiDiffEditorWidget extends Disposable { public layout(dimension: Dimension): void { this._dimension.set(dimension, undefined); } + + private readonly _activeControl = derived(this, (reader) => this._widgetImpl.read(reader).activeControl.read(reader)); + + public getActiveControl(): any | undefined { + return this._activeControl.get(); + } + + public readonly onDidChangeActiveControl = Event.fromObservableLight(this._activeControl); } diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts index 2261883a556..ffe1aa9eb8c 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts @@ -6,20 +6,20 @@ import { Dimension, getWindow, h, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Disposable, IReference, toDisposable } from 'vs/base/common/lifecycle'; -import { IObservable, IReader, autorun, derived, derivedWithStore, observableFromEvent, observableValue } from 'vs/base/common/observable'; +import { IObservable, IReader, autorun, derived, derivedObservableWithCache, derivedWithStore, observableFromEvent, observableValue } from 'vs/base/common/observable'; import { Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; import 'vs/css!./style'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { ObservableElementSizeObserver } from 'vs/editor/browser/widget/diffEditor/utils'; -import { IDocumentDiffItem, IMultiDiffEditorModel, LazyPromise } from 'vs/editor/browser/widget/multiDiffEditorWidget/model'; +import { IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/model'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; -import { IDiffEditorViewModel } from 'vs/editor/common/editorCommon'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DiffEditorItemTemplate, TemplateData } from './diffEditorItemTemplate'; import { ObjectPool } from './objectPool'; import { disposableObservableValue, globalTransaction, transaction } from 'vs/base/common/observableInternal/base'; import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory'; import { findFirstMaxBy } from 'vs/base/common/arraysFind'; +import { MultiDiffEditorViewModel, DocumentDiffItemViewModel } from './multiDiffEditorViewModel'; export class MultiDiffEditorWidgetImpl extends Disposable { private readonly _elements = h('div', { @@ -85,6 +85,9 @@ export class MultiDiffEditorWidgetImpl extends Disposable { ); private readonly _totalHeight = this._viewItems.map(this, (items, reader) => items.reduce((r, i) => r + i.contentHeight.read(reader), 0)); + public readonly activeDiffItem = derived(this, reader => this._viewItems.read(reader).find(i => i.template.read(reader)?.isFocused.read(reader))); + public readonly lastActiveDiffItem = derivedObservableWithCache((reader, lastValue) => this.activeDiffItem.read(reader) ?? lastValue); + public readonly activeControl = derived(this, reader => this.lastActiveDiffItem.read(reader)?.template.read(reader)?.editor); constructor( private readonly _element: HTMLElement, @@ -95,6 +98,13 @@ export class MultiDiffEditorWidgetImpl extends Disposable { ) { super(); + this._register(autorun((reader) => { + const lastActiveDiffItem = this.lastActiveDiffItem.read(reader); + transaction(tx => { + this._viewModel.read(reader)?.activeDiffItem.set(lastActiveDiffItem?.viewModel, tx); + }); + })); + this._register(autorun((reader) => { /** @description Update widget dimension */ const dimension = this._dimension.read(reader); @@ -179,37 +189,6 @@ export class MultiDiffEditorWidgetImpl extends Disposable { } } -export class MultiDiffEditorViewModel extends Disposable { - private readonly _documents = observableFromEvent(this._model.onDidChange, /** @description MultiDiffEditorViewModel.documents */() => this._model.documents); - - public readonly items = derivedWithStore(this, - (reader, store) => this._documents.read(reader).map(d => store.add(new DocumentDiffItemViewModel(d, this._diffEditorViewModelFactory))) - ).recomputeInitiallyAndOnChange(this._store); - - constructor( - private readonly _model: IMultiDiffEditorModel, - private readonly _diffEditorViewModelFactory: DiffEditorWidget, - ) { - super(); - } -} - -class DocumentDiffItemViewModel extends Disposable { - public readonly diffEditorViewModel: IDiffEditorViewModel; - - constructor( - public readonly entry: LazyPromise, - diffEditorViewModelFactory: DiffEditorWidget, - ) { - super(); - - this.diffEditorViewModel = this._register(diffEditorViewModelFactory.createViewModel({ - original: entry.value!.original!, - modified: entry.value!.modified!, - })); - } -} - class VirtualizedViewItem extends Disposable { // TODO this should be in the view model private readonly _lastTemplateData = observableValue<{ contentHeight: number; maxScroll: { maxScroll: number; width: number } }>( @@ -224,8 +203,10 @@ class VirtualizedViewItem extends Disposable { public readonly maxScroll = derived(this, reader => this._templateRef.read(reader)?.object.maxScroll.read(reader) ?? this._lastTemplateData.read(reader).maxScroll); + public readonly template = derived(this, reader => this._templateRef.read(reader)?.object); + constructor( - private readonly _viewModel: DocumentDiffItemViewModel, + public readonly viewModel: DocumentDiffItemViewModel, private readonly _objectPool: ObjectPool, private readonly _scrollLeft: IObservable, ) { @@ -243,7 +224,7 @@ class VirtualizedViewItem extends Disposable { } public override toString(): string { - return `VirtualViewItem(${this._viewModel.entry.value!.title})`; + return `VirtualViewItem(${this.viewModel.entry.value!.title})`; } public hide(): void { @@ -263,7 +244,7 @@ class VirtualizedViewItem extends Disposable { public render(verticalSpace: OffsetRange, offset: number, width: number, viewPort: OffsetRange): void { let ref = this._templateRef.get(); if (!ref) { - ref = this._objectPool.getUnusedObj(new TemplateData(this._viewModel.diffEditorViewModel, this._viewModel.entry.value!)); + ref = this._objectPool.getUnusedObj(new TemplateData(this.viewModel.diffEditorViewModel, this.viewModel.entry.value!)); this._templateRef.set(ref, undefined); } ref.object.render(verticalSpace, width, offset, viewPort); diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts index 4c74c8e7484..4fddc9ac6b0 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts @@ -15,6 +15,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ResourceLabel } from 'vs/workbench/browser/labels'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; +import { ICompositeControl } from 'vs/workbench/common/composite'; import { IEditorOpenContext } from 'vs/workbench/common/editor'; import { MultiDiffEditorInput } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput'; @@ -38,6 +39,10 @@ export class MultiDiffEditor extends EditorPane { parent, this.instantiationService.createInstance(WorkbenchUIElementFactory), )); + + this._register(this._multiDiffEditorWidget.onDidChangeActiveControl(() => { + this._onDidChangeControl.fire(); + })); } override async setInput(input: MultiDiffEditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { @@ -51,11 +56,15 @@ export class MultiDiffEditor extends EditorPane { override async clearInput(): Promise { await super.clearInput(); - this._multiDiffEditorWidget?.setViewModel(undefined); + this._multiDiffEditorWidget!.setViewModel(undefined); } layout(dimension: DOM.Dimension): void { - this._multiDiffEditorWidget?.layout(dimension); + this._multiDiffEditorWidget!.layout(dimension); + } + + override getControl(): ICompositeControl | undefined { + return this._multiDiffEditorWidget!.getActiveControl(); } } diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts index 29e3eaebf53..d97b3424f0c 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts @@ -8,7 +8,7 @@ import { deepClone } from 'vs/base/common/objects'; import { isObject } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { ConstLazyPromise, IDocumentDiffItem, IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/model'; -import { MultiDiffEditorViewModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl'; +import { MultiDiffEditorViewModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; @@ -16,8 +16,9 @@ import { localize } from 'vs/nls'; import { IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor'; import { DEFAULT_EDITOR_ASSOCIATION, EditorInputCapabilities } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { ILanguageSupport } from 'vs/workbench/services/textfile/common/textfiles'; -export class MultiDiffEditorInput extends EditorInput { +export class MultiDiffEditorInput extends EditorInput implements ILanguageSupport { static readonly ID: string = 'workbench.input.multiDiffEditor'; get resource(): URI | undefined { @@ -54,6 +55,15 @@ export class MultiDiffEditorInput extends EditorInput { super(); } + setLanguageId(languageId: string, source?: string | undefined): void { + const activeDiffItem = this.viewModel?.activeDiffItem.get(); + const value = activeDiffItem?.entry?.value; + if (!value) { return; } + const target = value.modified ?? value.original; + if (!target) { return; } + target.setLanguage(languageId, source); + } + // TODO this should return the view model async getModel(): Promise { if (!this._model) { From a086a83a4fc13326ea4dbeb762b3126550941e60 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 17 Nov 2023 10:09:54 -0800 Subject: [PATCH 31/87] fix protected field --- .../browser/contrib/notebookVariables/notebookVariablesView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index 19728ad4a38..b7fada32c5e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -58,7 +58,7 @@ export class NotebookVariablesView extends ViewPane { this._register(this.notebookExecutionStateService.onDidChangeExecution(this.handleExecutionStateChange.bind(this))); } - public override renderBody(container: HTMLElement): void { + protected override renderBody(container: HTMLElement): void { super.renderBody(container); this.tree = this.instantiationService.createInstance( From fb2702cb66af773121ccb0285100ab889a639a5d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 17 Nov 2023 10:36:00 -0800 Subject: [PATCH 32/87] Refine command guide further --- .../terminal/browser/media/terminal.css | 19 +++++++++----- .../contrib/terminal/browser/terminal.ts | 2 +- .../browser/xterm/markNavigationAddon.ts | 26 +++++++------------ .../terminal.highlight.contribution.ts | 11 +++----- 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index d0b971a2022..a934dae26c7 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -479,16 +479,21 @@ pointer-events: none; } -.terminal-scroll-highlight-output { +.terminal-command-guide { left: 0; - border-left: 5px solid #ffffff; - border-color: var(--vscode-terminalCommandDecoration-defaultBackground); + border: 1.5px solid #ffffff; + width: 0px !important; + border-color: var(--vscode-notebook-inactiveFocusedCellBorder); + box-sizing: border-box; + transform: translateX(3px); } -.terminal-scroll-highlight-output.success { - border-color: var(--vscode-terminalCommandDecoration-successBackground); +.terminal-command-guide.top { + border-top-left-radius: 1px; + border-top-right-radius: 1px; } -.terminal-scroll-highlight-output.error { - border-color: var(--vscode-terminalCommandDecoration-errorBackground); +.terminal-command-guide.bottom { + border-bottom-left-radius: 1px; + border-bottom-right-radius: 1px; } .terminal-scroll-highlight-outline { diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 799157b4ae3..eeb835f407f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -117,7 +117,7 @@ export interface IMarkTracker { scrollToLine(line: number, position: ScrollPosition): void; revealCommand(command: ITerminalCommand, position?: ScrollPosition): void; registerTemporaryDecoration(marker: IMarker, endMarker: IMarker | undefined, showOutline: boolean): void; - highlight(command: ITerminalCommand | undefined): void; + showCommandGuide(command: ITerminalCommand | undefined): void; } export interface ITerminalGroup { diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts index aa79815bd17..3127deae36f 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts @@ -260,7 +260,7 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe private _highlightedCommand?: ITerminalCommand; private _outputHighlights = this._register(new MutableDisposable()); - highlight(command: ITerminalCommand | undefined): void { + showCommandGuide(command: ITerminalCommand | undefined): void { if (!this._terminal) { return; } @@ -271,39 +271,33 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe if (this._highlightedCommand === command) { return; } - const promptRowCount = command.getPromptRowCount(); - const commandRowCount = command.getCommandRowCount(); if (command.marker) { this._highlightedCommand = command; - this.registerTemporaryDecoration(command.marker.line - (promptRowCount - 1), command.marker.line + (commandRowCount - 1), false); // Highlight output const store = this._outputHighlights.value = new DisposableStore(); if (!command.executedMarker || !command.endMarker) { return; } - const startLine = toLineIndex(command.executedMarker); + const startLine = command.marker.line - (command.getPromptRowCount() - 1); const decorationCount = toLineIndex(command.endMarker) - startLine; - console.trace('highlight', startLine, decorationCount, command); for (let i = 0; i < decorationCount; i++) { const decoration = this._terminal.registerDecoration({ - marker: this._createMarkerForOffset(command.executedMarker, i), - width: this._terminal.cols + marker: this._createMarkerForOffset(command.executedMarker, i) }); if (decoration) { store.add(decoration); let renderedElement: HTMLElement | undefined; - store.add(decoration.onRender(element => { if (!renderedElement) { renderedElement = element; - element.classList.add('terminal-scroll-highlight-output'); - // if (command.exitCode === 0) { - // element.classList.add('success'); - // } else if (command.exitCode !== undefined) { - // element.classList.add('error'); - // } - element.style.opacity = '0.5'; + element.classList.add('terminal-command-guide'); + if (i === 0) { + element.classList.add('top'); + } + if (i === decorationCount - 1) { + element.classList.add('bottom'); + } } if (this._terminal?.element) { element.style.marginLeft = `-${getWindow(this._terminal.element).getComputedStyle(this._terminal.element).paddingLeft}`; diff --git a/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts b/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts index 5f9431c67d8..38ec3e391e9 100644 --- a/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts @@ -4,11 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; -import { addDisposableGenericMouseMoveListener, addDisposableListener } from 'vs/base/browser/dom'; +import { addDisposableListener } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; -import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { ITerminalProcessInfo, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -25,8 +24,6 @@ class TerminalHighlightContribution extends Disposable implements ITerminalContr private readonly _instance: ITerminalInstance | IDetachedTerminalInstance, processManager: ITerminalProcessManager | ITerminalProcessInfo, widgetManager: TerminalWidgetManager, - @IInstantiationService instantiationService: IInstantiationService, - @ITerminalService terminalService: ITerminalService ) { super(); } @@ -46,9 +43,9 @@ class TerminalHighlightContribution extends Disposable implements ITerminalContr console.log(`@${mouseCursorY}`, e.offsetY); const command = this._instance.capabilities.get(TerminalCapability.CommandDetection)?.getCommandForLine(xterm.raw.buffer.active.viewportY + mouseCursorY); if (command && 'getOutput' in command) { - xterm.markTracker.highlight(command); + xterm.markTracker.showCommandGuide(command); } else { - xterm.markTracker.highlight(undefined); + xterm.markTracker.showCommandGuide(undefined); } })); } From b277683dbe8c096664fe7758f3e217b8a9c99b4c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 17 Nov 2023 12:43:34 -0600 Subject: [PATCH 33/87] Make filtered responses even less visible (#198551) --- src/vs/workbench/contrib/chat/browser/media/chat.css | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 307a49652c2..2f24cb1712a 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -461,9 +461,8 @@ .interactive-item-container.filtered-response .value > .rendered-markdown { pointer-events: none; - filter: blur(1.8px); - -webkit-mask-image: linear-gradient(rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.02)); - mask-image: linear-gradient(rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.02)); + -webkit-mask-image: linear-gradient(rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.05) 60%, rgba(0, 0, 0, 0.00) 80%); + mask-image: linear-gradient(rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.05) 60%, rgba(0, 0, 0, 0.00) 80%); } /* #region Quick Chat */ From 31e5940fb0f424c89474f4c967be9564a1e793a8 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 17 Nov 2023 20:22:36 +0100 Subject: [PATCH 34/87] adopt test-extension for colorize & configuration-editing (#195691) * adopt test-extension for colorize & configuration-editing * update * fixes --- .vscode-test.js | 10 ++++++++++ scripts/test-integration.bat | 4 ++-- scripts/test-integration.sh | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.vscode-test.js b/.vscode-test.js index 6846ca522f6..f2db8d695fe 100644 --- a/.vscode-test.js +++ b/.vscode-test.js @@ -31,6 +31,16 @@ const extensions = [ workspaceFolder: path.join(os.tmpdir(), `nbout-${Math.floor(Math.random() * 100000)}`), mocha: { timeout: 60_000 } }, + { + label: 'vscode-colorize-tests', + workspaceFolder: `extensions/vscode-colorize-tests/test`, + mocha: { timeout: 60_000 } + }, + { + label: 'configuration-editing', + workspaceFolder: path.join(os.tmpdir(), `confeditout-${Math.floor(Math.random() * 100000)}`), + mocha: { timeout: 60_000 } + }, { label: 'github-authentication', workspaceFolder: path.join(os.tmpdir(), `msft-auth-${Math.floor(Math.random() * 100000)}`), diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index 4786c7f7a6d..facf82828f1 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -49,7 +49,7 @@ if %errorlevel% neq 0 exit /b %errorlevel% echo. echo ### Colorize tests -call "%INTEGRATION_TEST_ELECTRON_PATH%" %~dp0\..\extensions\vscode-colorize-tests\test --extensionDevelopmentPath=%~dp0\..\extensions\vscode-colorize-tests --extensionTestsPath=%~dp0\..\extensions\vscode-colorize-tests\out %API_TESTS_EXTRA_ARGS% +call yarn test-extension -l vscode-colorize-tests if %errorlevel% neq 0 exit /b %errorlevel% echo. @@ -89,7 +89,7 @@ echo. echo ### Configuration editing tests set CFWORKSPACE=%TEMPDIR%\cf-%RANDOM% mkdir %CFWORKSPACE% -call "%INTEGRATION_TEST_ELECTRON_PATH%" %CFWORKSPACE% --extensionDevelopmentPath=%~dp0\..\extensions\configuration-editing --extensionTestsPath=%~dp0\..\extensions\configuration-editing\out\test %API_TESTS_EXTRA_ARGS% +call yarn test-extension -l configuration-editing if %errorlevel% neq 0 exit /b %errorlevel% echo. diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index ab32efc798f..483fb5507c5 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -67,7 +67,7 @@ kill_app echo echo "### Colorize tests" echo -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out $API_TESTS_EXTRA_ARGS +yarn test-extension -l vscode-colorize-tests kill_app echo @@ -109,7 +109,7 @@ kill_app echo echo "### Configuration editing tests" echo -"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_EXTRA_ARGS $(mktemp -d 2>/dev/null) --extensionDevelopmentPath=$ROOT/extensions/configuration-editing --extensionTestsPath=$ROOT/extensions/configuration-editing/out/test $API_TESTS_EXTRA_ARGS +yarn test-extension -l configuration-editing kill_app echo From e9ab39744bbe1ba820d75e732dc199d104efcaf9 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 17 Nov 2023 20:39:20 +0100 Subject: [PATCH 35/87] Use chatListRenderer in InlineChat (#198525) * Use chatListRenderer in InlineChat * use for progress * manage ChatModel and ChatResponseModel in inlineChatWidget --- .../contrib/chat/browser/chatListRenderer.ts | 51 +++++++++----- .../contrib/chat/browser/media/chat.css | 8 +++ .../contrib/inlineChat/browser/inlineChat.css | 13 +++- .../browser/inlineChatController.ts | 35 +++++++--- .../inlineChat/browser/inlineChatSession.ts | 1 + .../inlineChat/browser/inlineChatWidget.ts | 68 ++++++++++++++++++- .../view/cellParts/chat/cellChatController.ts | 2 +- 7 files changed, 145 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 57fb9595ac1..26b1ab5ebab 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -68,7 +68,7 @@ const $ = dom.$; interface IChatListItemTemplate { readonly rowContainer: HTMLElement; - readonly titleToolbar: MenuWorkbenchToolBar; + readonly titleToolbar?: MenuWorkbenchToolBar; readonly avatarContainer: HTMLElement; readonly agentAvatarContainer: HTMLElement; readonly username: HTMLElement; @@ -94,6 +94,8 @@ export interface IChatRendererDelegate { export interface IChatListItemRendererOptions { readonly renderStyle?: 'default' | 'compact'; + readonly noHeader?: boolean; + readonly noPadding?: boolean; } export class ChatListItemRenderer extends Disposable implements ITreeRenderer { @@ -211,7 +213,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { editor.layout(this._currentLayoutWidth); }); @@ -223,6 +225,9 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { - if (action instanceof MenuItemAction && (action.item.id === 'workbench.action.chat.voteDown' || action.item.id === 'workbench.action.chat.voteUp')) { - return scopedInstantiationService.createInstance(ChatVoteButton, action, options as IMenuEntryActionViewItemOptions); + let titleToolbar: MenuWorkbenchToolBar | undefined; + if (this.rendererOptions.noHeader) { + header.classList.add('hidden'); + } else { + titleToolbar = templateDisposables.add(scopedInstantiationService.createInstance(MenuWorkbenchToolBar, header, MenuId.ChatMessageTitle, { + menuOptions: { + shouldForwardArgs: true + }, + actionViewItemProvider: (action: IAction, options: IActionViewItemOptions) => { + if (action instanceof MenuItemAction && (action.item.id === 'workbench.action.chat.voteDown' || action.item.id === 'workbench.action.chat.voteUp')) { + return scopedInstantiationService.createInstance(ChatVoteButton, action, options as IMenuEntryActionViewItemOptions); + } + + return undefined; } - - return undefined; - } - })); - - + })); + } const template: IChatListItemTemplate = { avatarContainer, agentAvatarContainer, username, detail, progressSteps, referencesListContainer, value, rowContainer, elementDisposables, titleToolbar, templateDisposables, contextKeyService }; return template; } renderElement(node: ITreeNode, index: number, templateData: IChatListItemTemplate): void { - const { element } = node; + this.renderChatTreeItem(node.element, index, templateData); + } + + renderChatTreeItem(element: ChatTreeItem, index: number, templateData: IChatListItemTemplate): void { const kind = isRequestVM(element) ? 'request' : isResponseVM(element) ? 'response' : 'welcome'; @@ -272,7 +283,9 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer(data => { this._log('received chunk', data, request); @@ -658,8 +659,16 @@ export class InlineChatController implements IEditorContribution { }); } if (data.markdownFragment) { - markdownContents.appendMarkdown(data.markdownFragment); - this._zone.value.widget.updateMarkdownMessage(markdownContents); + if (!progressiveChatResponse) { + const message = { + message: new MarkdownString(data.markdownFragment, { supportThemeIcons: true, supportHtml: true, isTrusted: false }), + providerId: this._activeSession!.provider.debugName, + requestId: request.requestId, + }; + progressiveChatResponse = this._zone.value.widget.updateChatMessage(message, true); + } else { + progressiveChatResponse.appendContent(data.markdownFragment); + } } }); @@ -673,6 +682,8 @@ export class InlineChatController implements IEditorContribution { let response: ReplyResponse | ErrorResponse | EmptyResponse; let reply: IInlineChatResponse | null | undefined; try { + this._zone.value.widget.updateChatMessage(undefined); + this._zone.value.widget.updateMarkdownMessage(undefined); this._zone.value.widget.updateProgress(true); this._zone.value.widget.updateInfo(!this._activeSession.lastExchange ? localize('thinking', "Thinking\u2026") : ''); this._ctxHasActiveRequest.set(true); @@ -682,15 +693,16 @@ export class InlineChatController implements IEditorContribution { // we must wait for all edits that came in via progress to complete await Event.toPromise(progressiveEditsQueue.onDrained); } + if (progressiveChatResponse) { + progressiveChatResponse.cancel(); + } if (!reply) { response = new EmptyResponse(); a11yResponse = localize('empty', "No results, please refine your input and try again"); } else { - if (reply.message) { - markdownContents.appendMarkdown(reply.message.value); - } - const replyResponse = response = this._instaService.createInstance(ReplyResponse, reply, markdownContents, this._activeSession.textModelN.uri, modelAltVersionIdNow, progressEdits); + const markdownContents = reply.message ?? new MarkdownString('', { supportThemeIcons: true, supportHtml: true, isTrusted: false }); + const replyResponse = response = this._instaService.createInstance(ReplyResponse, reply, markdownContents, this._activeSession.textModelN.uri, modelAltVersionIdNow, progressEdits, request.requestId); for (let i = progressEdits.length; i < replyResponse.allLocalEdits.length; i++) { await this._makeChanges(replyResponse.allLocalEdits[i], undefined); @@ -738,7 +750,7 @@ export class InlineChatController implements IEditorContribution { } } - private async [State.APPLY_RESPONSE](): Promise { + private async[State.APPLY_RESPONSE](): Promise { assertType(this._activeSession); assertType(this._strategy); @@ -820,7 +832,10 @@ export class InlineChatController implements IEditorContribution { } else if (response instanceof ReplyResponse) { // real response -> complex... this._zone.value.widget.updateStatus(''); - this._zone.value.widget.updateMarkdownMessage(response.mdContent); + const message = { message: response.mdContent, providerId: this._activeSession.provider.debugName, requestId: response.requestId }; + this._zone.value.widget.updateChatMessage(message); + + //this._zone.value.widget.updateMarkdownMessage(response.mdContent); this._activeSession.lastExpansionState = this._zone.value.widget.expansionState; this._zone.value.widget.updateToolbar(true); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index 388856b8007..8dd5e4ffca7 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -298,6 +298,7 @@ export class ReplyResponse { localUri: URI, readonly modelAltVersionId: number, progressEdits: TextEdit[][], + readonly requestId: string, @ITextFileService private readonly _textFileService: ITextFileService, @ILanguageService private readonly _languageService: ILanguageService, ) { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index a87cfef068b..145d858b521 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -54,7 +54,7 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; import { assertType } from 'vs/base/common/types'; import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; -import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; import { MarkdownRenderer } from 'vs/editor/contrib/markdownRenderer/browser/markdownRenderer'; import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions'; import { MenuId } from 'vs/platform/actions/common/actions'; @@ -62,8 +62,13 @@ import { editorForeground, inputBackground, editorBackground } from 'vs/platform import { CodeBlockPart } from 'vs/workbench/contrib/chat/browser/codeBlockPart'; import { Lazy } from 'vs/base/common/lazy'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; -import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; +import { ChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; +import { ChatModel, ChatResponseModel } from 'vs/workbench/contrib/chat/common/chatModel'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ChatListItemRenderer, IChatListItemRendererOptions, IChatRendererDelegate } from 'vs/workbench/contrib/chat/browser/chatListRenderer'; import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; const defaultAriaLabel = localize('aria-label', "Inline Chat Input"); @@ -137,6 +142,19 @@ export interface IInlineChatWidgetConstructionOptions { feedbackMenuId: MenuId; } +export interface IInlineChatMessage { + message: IMarkdownString; + followups?: IChatFollowup[]; + requestId: string; + providerId: string; +} + +export interface IInlineChatMessageAppender { + appendContent(fragment: string): void; + cancel(): void; + complete(): void; +} + export class InlineChatWidget { private static _modelPool: number = 1; @@ -157,6 +175,9 @@ export class InlineChatWidget { h('div.previewDiff.hidden@previewDiff'), h('div.previewCreateTitle.show-file-icons@previewCreateTitle'), h('div.previewCreate.hidden@previewCreate'), + h('div.chatMessage.hidden@chatMessage', [ + h('div.chatMessageContent@chatMessageContent') + ]), h('div.markdownMessage.hidden@markdownMessage', [ h('div.message@message'), h('div.messageActions@messageActions') @@ -212,6 +233,7 @@ export class InlineChatWidget { private readonly _markdownRenderer: MarkdownRenderer; private readonly _editorOptions: ChatEditorOptions; private _codeBlockDisposables = this._store.add(new DisposableStore()); + private _chatMessageDisposables = this._store.add(new DisposableStore()); constructor( private readonly parentEditor: ICodeEditor, @@ -226,7 +248,9 @@ export class InlineChatWidget { @IContextMenuService private readonly _contextMenuService: IContextMenuService, @IAccessibleViewService private readonly _accessibleViewService: IAccessibleViewService, @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, + @ILogService private readonly _logService: ILogService, @ITextModelService private readonly _textModelResolverService: ITextModelService, + @IChatAgentService private readonly _chatAgentService: IChatAgentService, ) { // input editor logic @@ -397,6 +421,8 @@ export class InlineChatWidget { this._elements.message.tabIndex = 0; this._elements.message.ariaLabel = this._accessibleViewService.getOpenAriaHint(AccessibilityVerbositySettingId.InlineChat); + this._elements.chatMessageContent.tabIndex = 0; + this._elements.chatMessageContent.ariaLabel = this._accessibleViewService.getOpenAriaHint(AccessibilityVerbositySettingId.InlineChat); this._elements.statusLabel.tabIndex = 0; const markdownMessageToolbar = this._instantiationService.createInstance(MenuWorkbenchToolBar, this._elements.messageActions, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, workbenchToolbarOptions); this._store.add(markdownMessageToolbar.onDidChangeMenuItems(() => this._onDidChangeHeight.fire())); @@ -481,10 +507,11 @@ export class InlineChatWidget { const base = getTotalHeight(this._elements.progress) + getTotalHeight(this._elements.status); const editorHeight = this._inputEditor.getContentHeight() + 12 /* padding and border */; const markdownMessageHeight = getTotalHeight(this._elements.markdownMessage); + const chatResponseHeight = getTotalHeight(this._elements.chatMessage); const previewDiffHeight = this._previewDiffEditor.hasValue && this._previewDiffEditor.value.getModel() ? 12 + Math.min(300, Math.max(0, this._previewDiffEditor.value.getContentHeight())) : 0; const previewCreateTitleHeight = getTotalHeight(this._elements.previewCreateTitle); const previewCreateHeight = this._previewCreateEditor.hasValue && this._previewCreateEditor.value.getModel() ? 18 + Math.min(300, Math.max(0, this._previewCreateEditor.value.getContentHeight())) : 0; - return base + editorHeight + markdownMessageHeight + previewDiffHeight + previewCreateTitleHeight + previewCreateHeight + 18 /* padding */ + 8 /*shadow*/; + return base + editorHeight + markdownMessageHeight + chatResponseHeight + previewDiffHeight + previewCreateTitleHeight + previewCreateHeight + 18 /* padding */ + 8 /*shadow*/; } updateProgress(show: boolean) { @@ -550,6 +577,40 @@ export class InlineChatWidget { return this._elements.markdownMessage.textContent ?? undefined; } + updateChatMessage(message: IInlineChatMessage, isIncomplete: true): IInlineChatMessageAppender; + updateChatMessage(message: IInlineChatMessage | undefined): void; + updateChatMessage(message: IInlineChatMessage | undefined, isIncomplete?: boolean): IInlineChatMessageAppender | undefined { + this._chatMessageDisposables.clear(); + this._elements.chatMessage.classList.toggle('hidden', !message); + reset(this._elements.chatMessageContent); + if (message) { + const sessionModel = this._chatMessageDisposables.add(new ChatModel(message.providerId, undefined, this._logService, this._chatAgentService)); + const responseModel = this._chatMessageDisposables.add(new ChatResponseModel(message.message, sessionModel, undefined, message.requestId, !isIncomplete, false, undefined)); + const viewModel = this._chatMessageDisposables.add(new ChatResponseViewModel(responseModel, this._logService)); + const renderOptions: IChatListItemRendererOptions = { renderStyle: 'compact', noHeader: true, noPadding: true }; + const chatRendererDelegate: IChatRendererDelegate = { getListLength() { return 1; } }; + const renderer = this._chatMessageDisposables.add(this._instantiationService.createInstance(ChatListItemRenderer, this._editorOptions, renderOptions, chatRendererDelegate)); + renderer.layout(this._elements.chatMessageContent.clientWidth - 4); // 2 for the padding used for the tab index border + this._chatMessageDisposables.add(this._onDidChangeLayout.event(() => { + renderer.layout(this._elements.chatMessageContent.clientWidth - 4); + })); + const template = renderer.renderTemplate(this._elements.chatMessageContent); + this._chatMessageDisposables.add(template.elementDisposables); + this._chatMessageDisposables.add(template.templateDisposables); + renderer.renderChatTreeItem(viewModel, 0, template); + this._chatMessageDisposables.add(renderer.onDidChangeItemHeight(() => this._onDidChangeHeight.fire())); + this._onDidChangeHeight.fire(); + return { + cancel: () => responseModel.cancel(), + complete: () => responseModel.complete(), + appendContent: (fragment: string) => responseModel.updateContent({ kind: 'markdownContent', content: new MarkdownString(fragment) }), + }; + } else { + this._onDidChangeHeight.fire(); + return undefined; + } + } + updateMarkdownMessage(message: IMarkdownString | undefined) { this._codeBlockDisposables.clear(); const hasMessage = message?.value; @@ -656,6 +717,7 @@ export class InlineChatWidget { this.value = ''; this.updateMarkdownMessage(undefined); + this.updateChatMessage(undefined); reset(this._elements.statusLabel); this._elements.statusLabel.classList.toggle('hidden', true); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts index e0426c4c663..502ec2d63d7 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts @@ -292,7 +292,7 @@ export class NotebookCellChatController extends Disposable { } const markdownContents = new MarkdownString('', { supportThemeIcons: true, supportHtml: true, isTrusted: false }); - const replyResponse = this._instantiationService.createInstance(ReplyResponse, reply, markdownContents, this._activeSession.textModelN.uri, this._activeSession.textModelN.getAlternativeVersionId(), progressEdits); + const replyResponse = this._instantiationService.createInstance(ReplyResponse, reply, markdownContents, this._activeSession.textModelN.uri, this._activeSession.textModelN.getAlternativeVersionId(), progressEdits, request.requestId); for (let i = progressEdits.length; i < replyResponse.allLocalEdits.length; i++) { await this._makeChanges(editor, replyResponse.allLocalEdits[i], undefined); } From 9ed6654cc5e2103e2b5f27cf10bd869654175324 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 17 Nov 2023 20:28:29 +0100 Subject: [PATCH 36/87] Adopts GlyphMargin for diff editor revert icons. Adds support for selection based revert. --- .../diffEditor/accessibleDiffViewer.css | 4 + .../browser/widget/diffEditor/decorations.ts | 9 - .../diffEditor/diffEditorDecorations.ts | 160 +++++++++++++++++- .../widget/diffEditor/diffEditorEditors.ts | 8 + .../widget/diffEditor/diffEditorViewModel.ts | 2 + .../widget/diffEditor/diffEditorWidget.ts | 58 +++---- src/vs/editor/common/core/lineRange.ts | 4 + 7 files changed, 192 insertions(+), 53 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditor/accessibleDiffViewer.css b/src/vs/editor/browser/widget/diffEditor/accessibleDiffViewer.css index cca9d486aa6..cd20cfb12f3 100644 --- a/src/vs/editor/browser/widget/diffEditor/accessibleDiffViewer.css +++ b/src/vs/editor/browser/widget/diffEditor/accessibleDiffViewer.css @@ -62,3 +62,7 @@ height: 16px; margin: 2px 0; } + +.monaco-diff-editor .revertButton { + cursor: pointer; +} diff --git a/src/vs/editor/browser/widget/diffEditor/decorations.ts b/src/vs/editor/browser/widget/diffEditor/decorations.ts index 350bbd58120..9b2c6c74ffc 100644 --- a/src/vs/editor/browser/widget/diffEditor/decorations.ts +++ b/src/vs/editor/browser/widget/diffEditor/decorations.ts @@ -76,12 +76,3 @@ export const diffDeleteDecorationEmpty = ModelDecorationOptions.register({ className: 'char-delete diff-range-empty', description: 'char-delete diff-range-empty', }); - - -export const arrowRevertChange = ModelDecorationOptions.register({ - description: 'diff-editor-arrow-revert-change', - glyphMarginHoverMessage: new MarkdownString(undefined, { isTrusted: true, supportThemeIcons: true }) - .appendMarkdown(localize('revertChangeHoverMessage', 'Click to revert change')), - glyphMarginClassName: 'arrow-revert-change ' + ThemeIcon.asClassName(Codicon.arrowRight), - zIndex: 10001, -}); diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorDecorations.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorDecorations.ts index 2e8fc3f0cf3..6791b2f9df2 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorDecorations.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorDecorations.ts @@ -3,26 +3,36 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -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/diffEditor/decorations'; +import { h } from 'vs/base/browser/dom'; +import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { Codicon } from 'vs/base/common/codicons'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IObservable, autorunWithStore, derived } from 'vs/base/common/observable'; +import { IGlyphMarginWidget, IGlyphMarginWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { 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 { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; 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 { LineRange, LineRangeSet } from 'vs/editor/common/core/lineRange'; import { Range } from 'vs/editor/common/core/range'; -import { IModelDeltaDecoration } from 'vs/editor/common/model'; +import { RangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { GlyphMarginLane, IModelDeltaDecoration } from 'vs/editor/common/model'; +import { localize } from 'vs/nls'; export class DiffEditorDecorations extends Disposable { constructor( private readonly _editors: DiffEditorEditors, private readonly _diffModel: IObservable, private readonly _options: DiffEditorOptions, + widget: DiffEditorWidget, ) { super(); + this._register(new RevertButtonsFeature(_editors, _diffModel, _options, widget)); + this._register(applyObservableDecorations(this._editors.original, this._decorations.map(d => d?.originalDecorations || []))); this._register(applyObservableDecorations(this._editors.modified, this._decorations.map(d => d?.modifiedDecorations || []))); } @@ -66,10 +76,6 @@ export class DiffEditorDecorations extends Disposable { } } } - - 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 }); - } } } @@ -113,3 +119,139 @@ export class DiffEditorDecorations extends Disposable { return { originalDecorations, modifiedDecorations }; }); } + +class RevertButtonsFeature extends Disposable { + constructor( + private readonly _editors: DiffEditorEditors, + private readonly _diffModel: IObservable, + private readonly _options: DiffEditorOptions, + private readonly _widget: DiffEditorWidget, + ) { + super(); + + const emptyArr: never[] = []; + const selectedDiffs = derived(this, (reader) => { + /** @description selectedDiffs */ + + const model = this._diffModel.read(reader); + const diff = model?.diff.read(reader); + if (!diff) { return emptyArr; } + + const selections = this._editors.modifiedSelections.read(reader); + + if (selections.every(s => s.isEmpty())) { + return emptyArr; + } + + const lineRanges = new LineRangeSet(selections.map(s => LineRange.fromRangeInclusive(s))); + + const mappings = diff.mappings.filter(m => m.lineRangeMapping.innerChanges && lineRanges.intersects(m.lineRangeMapping.modified)); + + const result = mappings.map(mapping => ({ + mapping, + rangeMappings: mapping.lineRangeMapping.innerChanges!.filter(c => selections.some(s => Range.areIntersecting(c.modifiedRange, s))) + })); + if (result.length === 0 || result.every(r => r.rangeMappings.length === 0)) { return emptyArr; } + return result; + }); + + this._register(autorunWithStore((reader, store) => { + const model = this._diffModel.read(reader); + const diff = model?.diff.read(reader); + if (!model || !diff) { return; } + const movedTextToCompare = this._diffModel.read(reader)!.movedTextToCompare.read(reader); + if (movedTextToCompare) { return; } + if (!this._options.shouldRenderRevertArrows.read(reader)) { return; } + + const glyphWidgetsModified: IGlyphMarginWidget[] = []; + + const selectedDiffs_ = selectedDiffs.read(reader); + const diffsSet = new Set(selectedDiffs_.map(d => d.mapping)); + + if (selectedDiffs_.length > 0) { + const selections = this._editors.modifiedSelections.read(reader); + + const btn = new RevertButton(selections[selections.length - 1].positionLineNumber, this._widget, selectedDiffs_.flatMap(d => d.rangeMappings), true); + this._editors.modified.addGlyphMarginWidget(btn); + glyphWidgetsModified.push(btn); + } + + for (const m of diff.mappings) { + if (diffsSet.has(m)) { + continue; + } + if (!m.lineRangeMapping.modified.isEmpty && m.lineRangeMapping.innerChanges) { + const btn = new RevertButton(m.lineRangeMapping.modified.startLineNumber, this._widget, m.lineRangeMapping.innerChanges, false); + this._editors.modified.addGlyphMarginWidget(btn); + glyphWidgetsModified.push(btn); + } + } + + store.add(toDisposable(() => { + for (const w of glyphWidgetsModified) { + this._editors.modified.removeGlyphMarginWidget(w); + } + })); + })); + } +} + +class RevertButton implements IGlyphMarginWidget { + public static counter = 0; + + private readonly _id: string = `revertButton${RevertButton.counter++}`; + + getId(): string { return this._id; } + + private readonly _domNode = h('div.revertButton', { + title: this._selection + ? localize('revertSelectedChanges', 'Revert Selected Changes') + : localize('revertChange', 'Revert Change') + }, + [renderIcon(Codicon.arrowRight)] + ).root; + + constructor( + private readonly _lineNumber: number, + private readonly _widget: DiffEditorWidget, + private readonly _diffs: RangeMapping[], + private readonly _selection: boolean, + ) { + this._domNode.onmousedown = e => { + e.stopPropagation(); + e.preventDefault(); + }; + this._domNode.onmouseup = e => { + e.stopPropagation(); + e.preventDefault(); + }; + this._domNode.onclick = (e) => { + this._widget.revertRangeMappings(this._diffs); + e.stopPropagation(); + e.preventDefault(); + }; + } + + /** + * Get the dom node of the glyph widget. + */ + getDomNode(): HTMLElement { + return this._domNode; + } + + /** + * Get the placement of the glyph widget. + */ + getPosition(): IGlyphMarginWidgetPosition { + return { + lane: GlyphMarginLane.Right, + range: { + startColumn: 1, + startLineNumber: this._lineNumber, + endColumn: 1, + endLineNumber: this._lineNumber, + }, + zIndex: 0, + }; + } +} diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorEditors.ts index 56f4f7b2686..aea7fc9b20b 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorEditors.ts @@ -17,6 +17,8 @@ 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/diffEditor/diffEditorWidget'; +import { Selection } from 'vs/editor/common/core/selection'; +import { Position } from 'vs/editor/common/core/position'; export class DiffEditorEditors extends Disposable { public readonly modified: CodeEditorWidget; @@ -30,6 +32,9 @@ export class DiffEditorEditors extends Disposable { public readonly modifiedModel: IObservable; + public readonly modifiedSelections: IObservable; + public readonly modifiedCursor: IObservable; + constructor( private readonly originalEditorElement: HTMLElement, private readonly modifiedEditorElement: HTMLElement, @@ -49,6 +54,9 @@ export class DiffEditorEditors extends Disposable { this.modifiedScrollTop = observableFromEvent(this.modified.onDidScrollChange, () => /** @description modified.getScrollTop */ this.modified.getScrollTop()); this.modifiedScrollHeight = observableFromEvent(this.modified.onDidScrollChange, () => /** @description modified.getScrollHeight */ this.modified.getScrollHeight()); + this.modifiedSelections = observableFromEvent(this.modified.onDidChangeCursorSelection, () => this.modified.getSelections() ?? []); + this.modifiedCursor = observableFromEvent(this.modified.onDidChangeCursorPosition, () => this.modified.getPosition() ?? new Position(1, 1)); + this._register(autorunHandleChanges({ createEmptyChangeSummary: () => ({} as IDiffEditorConstructionOptions), handleChange: (ctx, changeSummary) => { diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts index 4e014f4741a..265c885643b 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts @@ -154,6 +154,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo } } + this._isDiffUpToDate.set(false, undefined); debouncer.schedule(); })); this._register(model.original.onDidChangeContent((e) => { @@ -172,6 +173,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo } } + this._isDiffUpToDate.set(false, undefined); debouncer.schedule(); })); diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts index 1231ec322f4..0747ff3fde2 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts @@ -12,7 +12,7 @@ import { IObservable, ITransaction, autorunWithStore, derived, observableFromEve import { derivedDisposable } from 'vs/base/common/observableInternal/derived'; import 'vs/css!./style'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; -import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions, IMouseTargetViewZone } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions } 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'; @@ -31,7 +31,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 { IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; -import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { DetailedLineRangeMapping, RangeMapping } 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'; @@ -171,7 +171,7 @@ export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor { derivedDisposable(this, reader => /** @description DiffEditorDecorations */ this._instantiationService.createInstance( readHotReloadableExport(DiffEditorDecorations, reader), - this._editors, this._diffModel, this._options, + this._editors, this._diffModel, this._options, this, ) ).recomputeInitiallyAndOnChange(this._store); @@ -260,27 +260,6 @@ export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor { ), })); - // 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')) { - const lineNumber = event.target.position.lineNumber; - const viewZone = event.target as IMouseTargetViewZone | undefined; - - const model = this._diffModel.get(); - if (!model) { return; } - const diffs = model.diff.get()?.mappings; - if (!diffs) { return; } - const diff = diffs.find(d => - viewZone?.detail.afterLineNumber === d.lineRangeMapping.modified.startLineNumber - 1 || - d.lineRangeMapping.modified.startLineNumber === lineNumber - ); - if (!diff) { return; } - this.revert(diff.lineRangeMapping); - - event.event.stopPropagation(); - } - })); - 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.modified.contains(e.position.lineNumber)); @@ -486,20 +465,29 @@ export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor { } revert(diff: DetailedLineRangeMapping): void { + if (diff.innerChanges) { + this.revertRangeMappings(diff.innerChanges); + } + const model = this._diffModel.get()?.model; if (!model) { return; } - const changes: IIdentifiedSingleEditOperation[] = diff.innerChanges - ? diff.innerChanges.map(c => ({ - range: c.modifiedRange, - text: model.original.getValueInRange(c.originalRange) - })) - : [ - { - range: diff.modified.toExclusiveRange(), - text: model.original.getValueInRange(diff.original.toExclusiveRange()) - } - ]; + this._editors.modified.executeEdits('diffEditor', [ + { + range: diff.modified.toExclusiveRange(), + text: model.original.getValueInRange(diff.original.toExclusiveRange()) + } + ]); + } + + revertRangeMappings(diffs: RangeMapping[]): void { + const model = this._diffModel.get(); + if (!model || !model.isDiffUpToDate.get()) { return; } + + const changes: IIdentifiedSingleEditOperation[] = diffs.map(c => ({ + range: c.modifiedRange, + text: model.model.original.getValueInRange(c.originalRange) + })); this._editors.modified.executeEdits('diffEditor', changes); } diff --git a/src/vs/editor/common/core/lineRange.ts b/src/vs/editor/common/core/lineRange.ts index 6a99f241df0..da150f47952 100644 --- a/src/vs/editor/common/core/lineRange.ts +++ b/src/vs/editor/common/core/lineRange.ts @@ -16,6 +16,10 @@ export class LineRange { return new LineRange(range.startLineNumber, range.endLineNumber); } + public static fromRangeInclusive(range: Range): LineRange { + return new LineRange(range.startLineNumber, range.endLineNumber + 1); + } + public static subtract(a: LineRange, b: LineRange | undefined): LineRange[] { if (!b) { return [a]; From 6c398ced757f7413d99116374ad792c707d42478 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 17 Nov 2023 20:30:32 +0100 Subject: [PATCH 37/87] Fixes CI --- src/vs/editor/browser/widget/diffEditor/decorations.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/editor/browser/widget/diffEditor/decorations.ts b/src/vs/editor/browser/widget/diffEditor/decorations.ts index 9b2c6c74ffc..d8be47dce6a 100644 --- a/src/vs/editor/browser/widget/diffEditor/decorations.ts +++ b/src/vs/editor/browser/widget/diffEditor/decorations.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Codicon } from 'vs/base/common/codicons'; -import { MarkdownString } from 'vs/base/common/htmlContent'; import { ThemeIcon } from 'vs/base/common/themables'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { localize } from 'vs/nls'; From 3979f2b7db14a474e8a4f267969c45c0f72d27d8 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 17 Nov 2023 12:05:21 -0800 Subject: [PATCH 38/87] Fix cell widget layout update --- .../browser/media/notebookCellChat.css | 2 +- .../notebook/browser/notebookEditorWidget.ts | 2 +- .../browser/view/cellParts/cellExecution.ts | 2 +- .../view/cellParts/cellFocusIndicator.ts | 8 ++-- .../view/cellParts/chat/cellChatController.ts | 40 ++++++++++++++----- 5 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookCellChat.css b/src/vs/workbench/contrib/notebook/browser/media/notebookCellChat.css index e891cdce5ab..5c0081e507d 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookCellChat.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookCellChat.css @@ -6,7 +6,7 @@ .monaco-workbench .notebookOverlay .cell-chat-part { display: none; color: inherit; - padding: 6px; + padding: 0 6px; border-radius: 6px; border: 1px solid var(--vscode-inlineChat-border); background: var(--vscode-inlineChat-background); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 9ff69e99572..60d7b1a9902 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -863,7 +863,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD // chat styleSheets.push(` .monaco-workbench .notebookOverlay .cell-chat-part { - margin: 0 ${cellRightMargin}px 8px 6px; + margin: 0 ${cellRightMargin}px 6px 6px; } `); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts index 385d2cbaa39..e62b28ee4dc 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts @@ -78,7 +78,7 @@ export class CellExecutionPart extends CellContentPart { DOM.hide(this._executionOrderLabel); } else { DOM.show(this._executionOrderLabel); - const top = element.layoutInfo.editorHeight - 22 + element.layoutInfo.statusBarHeight + element.layoutInfo.chatHeight; + const top = element.layoutInfo.editorHeight - 22 + element.layoutInfo.statusBarHeight; this._executionOrderLabel.style.top = `${top}px`; } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts index 95333b95f4c..7549fdeaab8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts @@ -80,7 +80,6 @@ export class CellFocusIndicator extends CellContentPart { this.bottom.domNode.style.transform = `translateY(${indicatorPostion.bottomIndicatorTop}px)`; this.left.setHeight(indicatorPostion.verticalIndicatorHeight); this.right.setHeight(indicatorPostion.verticalIndicatorHeight); - this.codeFocusIndicator.setTop(element.layoutInfo.chatHeight); this.codeFocusIndicator.setHeight(indicatorPostion.verticalIndicatorHeight - this.getIndicatorTopMargin() * 2 - element.layoutInfo.chatHeight); } else { const cell = element as CodeCellViewModel; @@ -89,9 +88,7 @@ export class CellFocusIndicator extends CellContentPart { const indicatorHeight = cell.layoutInfo.codeIndicatorHeight + cell.layoutInfo.outputIndicatorHeight + cell.layoutInfo.commentHeight; this.left.setHeight(indicatorHeight); this.right.setHeight(indicatorHeight); - this.codeFocusIndicator.setTop(cell.layoutInfo.chatHeight); this.codeFocusIndicator.setHeight(cell.layoutInfo.codeIndicatorHeight); - this.outputFocusIndicator.setTop(cell.layoutInfo.chatHeight); this.outputFocusIndicator.setHeight(Math.max(cell.layoutInfo.outputIndicatorHeight - cell.viewContext.notebookOptions.getLayoutConfiguration().focusIndicatorGap, 0)); this.bottom.domNode.style.transform = `translateY(${cell.layoutInfo.totalHeight - bottomToolbarDimensions.bottomToolbarGap - layoutInfo.cellBottomMargin}px)`; } @@ -100,8 +97,9 @@ export class CellFocusIndicator extends CellContentPart { } private updateFocusIndicatorsForTitleMenu(): void { - this.left.domNode.style.transform = `translateY(${this.getIndicatorTopMargin()}px)`; - this.right.domNode.style.transform = `translateY(${this.getIndicatorTopMargin()}px)`; + const y = (this.currentCell?.layoutInfo.chatHeight ?? 0) + this.getIndicatorTopMargin(); + this.left.domNode.style.transform = `translateY(${y}px)`; + this.right.domNode.style.transform = `translateY(${y}px)`; } private getIndicatorTopMargin() { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts index e0426c4c663..5d4e6052d23 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts @@ -8,7 +8,7 @@ import { CancelablePromise, Queue, createCancelablePromise, raceCancellationErro import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { MarkdownString } from 'vs/base/common/htmlContent'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { MovingAverage } from 'vs/base/common/numbers'; import { StopWatch } from 'vs/base/common/stopwatch'; import { assertType } from 'vs/base/common/types'; @@ -65,6 +65,7 @@ export class NotebookCellChatController extends Disposable { private _toolbar: MenuWorkbenchToolBar | undefined; private readonly _ctxVisible: IContextKey; private readonly _ctxCellWidgetFocused: IContextKey; + private _widgetDisposableStore: DisposableStore = this._register(new DisposableStore()); constructor( private readonly _notebookEditor: INotebookEditorDelegate, private readonly _chatPart: ICellChatPart, @@ -97,6 +98,10 @@ export class NotebookCellChatController extends Disposable { feedbackMenuId: MENU_CELL_CHAT_WIDGET_FEEDBACK }); + this._widgetDisposableStore.add(this._widget.onDidChangeHeight(() => { + this._updateHeight(); + })); + this._partContainer.appendChild(this._widget.domNode); this._partContainer.appendChild(this._toolbarDOM.editorToolbar); @@ -123,11 +128,15 @@ export class NotebookCellChatController extends Disposable { this._sessionCtor = undefined; } - if (this._widget) { - this._partContainer.removeChild(this._widget.domNode); - } + try { + if (this._widget) { + this._partContainer.removeChild(this._widget.domNode); + } - this._partContainer.removeChild(this._toolbarDOM.editorToolbar); + this._partContainer.removeChild(this._toolbarDOM.editorToolbar); + } catch (_ex) { + // might not be attached + } this._inlineChatListener?.dispose(); this._toolbar?.dispose(); @@ -141,12 +150,25 @@ export class NotebookCellChatController extends Disposable { layout() { if (this._isVisible && this._widget) { const innerEditorWidth = this._cell.layoutInfo.editorWidth; - const height = 82 + 8 * 2 /* vertical margin*/; - + const height = this._widget.getHeight(); this._widget.layout(new Dimension(innerEditorWidth, height)); } } + private _updateHeight() { + const margin = 6; + const heightWithMargin = this._isVisible && this._widget + ? (this._widget.getHeight() - 8 /** shadow */ - 18 /** padding */ + margin /** bottom margin for the cell part */) + : 0; + + if (this._cell.chatHeight === heightWithMargin) { + return; + } + + this._cell.chatHeight = heightWithMargin; + this._partContainer.style.height = `${heightWithMargin - margin}px`; + } + async show() { this._isVisible = true; this._partContainer.style.display = 'flex'; @@ -154,7 +176,7 @@ export class NotebookCellChatController extends Disposable { this._widget?.updateInfo(localize('welcome.1', "AI-generated code may be incorrect")); this._ctxVisible.set(true); this._ctxCellWidgetFocused.set(true); - this._cell.chatHeight = 82 + 8 * 2 /* vertical margin*/; + this._updateHeight(); this._sessionCtor = createCancelablePromise(async token => { if (this._cell.editorAttached) { @@ -336,7 +358,7 @@ export class NotebookCellChatController extends Disposable { this._ctxCellWidgetFocused.set(false); this._ctxVisible.set(false); this._widget?.reset(); - this._cell.chatHeight = 0; + this._updateHeight(); } private async _makeChanges(editor: IActiveCodeEditor, edits: TextEdit[], opts: ProgressingEditsOptions | undefined) { From 487ea5404c61080446874993fe13d4b653cce720 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 17 Nov 2023 12:09:18 -0800 Subject: [PATCH 39/87] Escape when users focus inside the cell chat widget --- .../browser/view/cellParts/chat/cellChatActions.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions.ts index ff4136e8465..b53f0279e13 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions.ts @@ -15,7 +15,7 @@ import { INotebookCellActionContext, NotebookAction, NotebookCellAction } from ' import { insertNewCell } from 'vs/workbench/contrib/notebook/browser/controller/insertCellActions'; import { CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST, MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_STATUS, MENU_CELL_CHAT_WIDGET_TOOLBAR, NotebookCellChatController } from 'vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController'; import { CellKind, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { NOTEBOOK_EDITOR_EDITABLE } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_EDITOR_EDITABLE } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; registerAction2(class extends NotebookCellAction { @@ -151,6 +151,11 @@ registerAction2(class extends NotebookCellAction { id: 'notebook.cell.chat.discard', title: localize('discard', 'Discard'), icon: Codicon.discard, + keybinding: { + when: ContextKeyExpr.and(CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_INLINE_CHAT_FOCUSED, NOTEBOOK_CELL_LIST_FOCUSED), + weight: KeybindingWeight.EditorContrib, + primary: KeyCode.Escape + }, menu: { id: MENU_CELL_CHAT_WIDGET_STATUS, group: 'main', From cbf528f09275f4022ddeed1dfef3996cd0c71d6c Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 17 Nov 2023 12:19:14 -0800 Subject: [PATCH 40/87] fix chat height in collapse state --- .../contrib/notebook/browser/viewModel/codeCellViewModel.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index dc9d1dd3bb0..93ae763c6e9 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -290,6 +290,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod } else { const codeIndicatorHeight = notebookLayoutConfiguration.collapsedIndicatorHeight; const outputIndicatorHeight = outputTotalHeight + outputShowMoreContainerHeight; + const chatHeight = state.chatHeight ? this._chatHeight : this._layoutInfo.chatHeight; const outputContainerOffset = notebookLayoutConfiguration.cellTopMargin + notebookLayoutConfiguration.collapsedIndicatorHeight; const totalHeight = @@ -297,6 +298,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod + notebookLayoutConfiguration.collapsedIndicatorHeight + notebookLayoutConfiguration.cellBottomMargin //CELL_BOTTOM_MARGIN + bottomToolbarDimensions.bottomToolbarGap //BOTTOM_CELL_TOOLBAR_GAP + + chatHeight + commentHeight + outputTotalHeight + outputShowMoreContainerHeight; const outputShowMoreContainerOffset = totalHeight @@ -312,7 +314,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod fontInfo: state.font ?? this._layoutInfo.fontInfo ?? null, editorHeight: this._layoutInfo.editorHeight, editorWidth, - chatHeight: 0, + chatHeight: chatHeight, statusBarHeight: 0, commentHeight, outputContainerOffset, From 02f5ebe5872023611a6d2d304a4a0ef638757be1 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 17 Nov 2023 14:40:18 -0600 Subject: [PATCH 41/87] "Copy" on chat message shouldn't include username (#198561) --- .../contrib/chat/browser/actions/chatCopyActions.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts index 816379d8646..b679221edce 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts @@ -39,7 +39,7 @@ export function registerChatCopyActions() { const viewModel = widget.viewModel; const sessionAsText = viewModel?.getItems() .filter((item): item is (IChatRequestViewModel | IChatResponseViewModel) => isRequestVM(item) || (isResponseVM(item) && !item.errorDetails?.responseIsFiltered)) - .map(stringifyItem) + .map(item => stringifyItem(item)) .join('\n\n'); if (sessionAsText) { clipboardService.writeText(sessionAsText); @@ -73,13 +73,16 @@ export function registerChatCopyActions() { } const clipboardService = accessor.get(IClipboardService); - const text = stringifyItem(item); + const text = stringifyItem(item, false); clipboardService.writeText(text); } }); } -function stringifyItem(item: IChatRequestViewModel | IChatResponseViewModel): string { - return isRequestVM(item) ? - `${item.username}: ${item.messageText}` : `${item.username}: ${item.response.asString()}`; +function stringifyItem(item: IChatRequestViewModel | IChatResponseViewModel, includeName = true): string { + if (isRequestVM(item)) { + return (includeName ? `${item.username}: ` : '') + item.messageText; + } else { + return (includeName ? `${item.username}: ` : '') + item.response.asString(); + } } From 8bc4783e375107fa2f48076273507f277d9d9a1f Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 17 Nov 2023 12:41:09 -0800 Subject: [PATCH 42/87] Notebook Variable view follow-up (#198555) * PR feedback follow-up * fix API type issues --- .../workbench/api/common/extHost.api.impl.ts | 1 + .../api/common/extHostNotebookKernels.ts | 6 +++-- src/vs/workbench/api/common/extHostTypes.ts | 5 ++++ .../notebookVariables/notebookVariables.ts | 23 ++++++++++++------- .../notebookVariablesView.ts | 6 ++--- ...ode.proposed.notebookVariableProvider.d.ts | 7 ++++-- 6 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 6ea907a0f07..e2e6da5263d 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1574,6 +1574,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I NotebookControllerAffinity2: extHostTypes.NotebookControllerAffinity2, NotebookEdit: extHostTypes.NotebookEdit, NotebookKernelSourceAction: extHostTypes.NotebookKernelSourceAction, + NotebookVariablesRequestKind: extHostTypes.NotebookVariablesRequestKind, PortAttributes: extHostTypes.PortAttributes, LinkedEditingRanges: extHostTypes.LinkedEditingRanges, TestResultState: extHostTypes.TestResultState, diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 78d7e39d90f..76eadcb84c3 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -18,7 +18,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; import { ExtHostCell, ExtHostNotebookDocument } from 'vs/workbench/api/common/extHostNotebookDocument'; import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; -import { NotebookCellExecutionState as ExtHostNotebookCellExecutionState, NotebookCellOutput, NotebookControllerAffinity2 } from 'vs/workbench/api/common/extHostTypes'; +import { NotebookCellExecutionState as ExtHostNotebookCellExecutionState, NotebookCellOutput, NotebookControllerAffinity2, NotebookVariablesRequestKind } from 'vs/workbench/api/common/extHostTypes'; import { asWebviewUri } from 'vs/workbench/contrib/webview/common/webview'; import { INotebookKernelSourceAction, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CellExecutionUpdateType } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; @@ -209,6 +209,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { _update(); }, set variableProvider(value) { + checkProposedApiEnabled(extension, 'notebookVariableProvider'); _variableProvider = value; data.hasVariableProvider = !!value; _update(); @@ -426,7 +427,8 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { } const parent = parentName ? { name: parentName, value: '' } : undefined; - const variables = variableProvider.provideVariables(document.apiNotebook, parent, kind, start, token); + const requestKind = kind === 'named' ? NotebookVariablesRequestKind.Named : NotebookVariablesRequestKind.Indexed; + const variables = variableProvider.provideVariables(document.apiNotebook, parent, requestKind, start, token); for await (const variable of variables) { if (token.isCancellationRequested) { return; diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index df40c53d3be..a9d705653b1 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3798,6 +3798,11 @@ export class NotebookKernelSourceAction { ) { } } +export enum NotebookVariablesRequestKind { + Named = 1, + Indexed = 2 +} + //#endregion //#region Timeline diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts index e1d6227a76d..90b17fe7aad 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts @@ -15,27 +15,32 @@ import { NotebookVariablesView } from 'vs/workbench/contrib/notebook/browser/con import { NOTEBOOK_KERNEL } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { variablesViewIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class NotebookVariables extends Disposable implements IWorkbenchContribution { - private listener: IDisposable; + private listener: IDisposable | undefined; constructor( - @IEditorService private readonly editorService: IEditorService + @IEditorService private readonly editorService: IEditorService, + @IConfigurationService configurationService: IConfigurationService, ) { super(); - this.initializeView(); + if (this.initializeView()) { + return; + } this.listener = this.editorService.onDidEditorsChange(() => { - if (this.editorService.activeEditorPane?.getId() === 'workbench.editor.notebook') { - this.listener.dispose(); - this.initializeView(); + if (configurationService.getValue('notebook.experimental.notebookVariablesView') + && this.editorService.activeEditorPane?.getId() === 'workbench.editor.notebook') { + if (this.initializeView()) { + this.listener?.dispose(); + } } }); } - private async initializeView() { - + private initializeView() { const debugViewContainer = Registry.as('workbench.registry.view.containers').get(debugContainerId); if (debugViewContainer) { @@ -46,8 +51,10 @@ export class NotebookVariables extends Disposable implements IWorkbenchContribut }; viewsRegistry.registerViews([viewDescriptor], debugViewContainer); + return true; } + return false; } } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index b7fada32c5e..445c8e442be 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -20,7 +20,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewDescriptorService } from 'vs/workbench/common/views'; -import { NotebookVariableAccessibilityProvider, NotebookVariableRenderer, INotebookVariableElement, NotebookVariablesDelegate, NotebookVariablesTree } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree'; +import { NotebookVariableAccessibilityProvider, NotebookVariableRenderer, INotebookVariableElement, NotebookVariablesDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree'; import { getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { ICellExecutionStateChangedEvent, IExecutionStateChangedEvent, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; @@ -32,7 +32,7 @@ export class NotebookVariablesView extends ViewPane { static readonly ID = 'notebookVariablesView'; static readonly TITLE: ILocalizedString = nls.localize2('notebook.notebookVariables', "Notebook Variables"); - private tree: NotebookVariablesTree | undefined; + private tree: WorkbenchObjectTree | undefined; private activeNotebook: NotebookTextModel | undefined; constructor( @@ -61,7 +61,7 @@ export class NotebookVariablesView extends ViewPane { protected override renderBody(container: HTMLElement): void { super.renderBody(container); - this.tree = this.instantiationService.createInstance( + this.tree = >this.instantiationService.createInstance( WorkbenchObjectTree, 'notebookVariablesTree', container, diff --git a/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts index 7f7bcd26c92..02fa3f10b77 100644 --- a/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts @@ -9,7 +9,10 @@ declare module 'vscode' { variableProvider?: NotebookVariableProvider; } - type VariablesRequestKind = 'named' | 'indexed'; + export enum NotebookVariablesRequestKind { + Named = 1, + Indexed = 2 + } interface VariablesResult { variable: Variable; @@ -21,7 +24,7 @@ declare module 'vscode' { onDidChangeVariables: Event; /** When parent is undefined, this is requesting global Variables. When a variable is passed, it's requesting child props of that Variable. */ - provideVariables(notebook: NotebookDocument, parent: Variable | undefined, kind: VariablesRequestKind, start: number, token: CancellationToken): AsyncIterable; + provideVariables(notebook: NotebookDocument, parent: Variable | undefined, kind: NotebookVariablesRequestKind, start: number, token: CancellationToken): AsyncIterable; } interface Variable { From 3a11d41082a774895a098aaaf50f44d5787cc2b8 Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 17 Nov 2023 12:46:45 -0800 Subject: [PATCH 43/87] create snapshot when cell executes succesfully --- .../view/cellParts/chat/cellChatController.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts index 5d4e6052d23..9c058cbcb53 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts @@ -34,6 +34,7 @@ import { ProgressingEditsOptions, asProgressiveEdit, performAsyncTextEdit } from import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_VISIBLE, EditMode, IInlineChatProgressItem, IInlineChatRequest } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookExecutionStateService, NotebookExecutionType } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; export const CTX_NOTEBOOK_CELL_CHAT_FOCUSED = new RawContextKey('notebookCellChatFocused', false, localize('notebookCellChatFocused', "Whether the cell chat editor is focused")); export const CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST = new RawContextKey('notebookChatHasActiveRequest', false, localize('notebookChatHasActiveRequest', "Whether the cell chat editor has an active request")); @@ -75,6 +76,7 @@ export class NotebookCellChatController extends Disposable { @IInlineChatSessionService private readonly _inlineChatSessionService: IInlineChatSessionService, @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, @IInstantiationService private readonly _instantiationService: IInstantiationService, + @INotebookExecutionStateService _notebookExecutionStateService: INotebookExecutionStateService, ) { super(); @@ -102,6 +104,21 @@ export class NotebookCellChatController extends Disposable { this._updateHeight(); })); + this._widgetDisposableStore.add(_notebookExecutionStateService.onDidChangeExecution(e => { + if (e.notebook.toString() !== this._notebookEditor.textModel?.uri.toString()) { + return; + } + + if (e.type === NotebookExecutionType.cell && e.affectsCell(this._cell.uri) && e.changed === undefined /** complete */) { + // check if execution is successfull + const { lastRunSuccess } = this._cell.internalMetadata; + if (lastRunSuccess) { + this._strategy?.createSnapshot(); + } + } + })); + + this._partContainer.appendChild(this._widget.domNode); this._partContainer.appendChild(this._toolbarDOM.editorToolbar); @@ -456,4 +473,10 @@ class EditStrategy { modelN.undo(); } } + + createSnapshot(): void { + if (this._session && !this._session.textModel0.equalsTextBuffer(this._session.textModelN.getTextBuffer())) { + this._session.createSnapshot(); + } + } } From 01072fbf2b4005b8418611a9ddea9c0b17e194ee Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 17 Nov 2023 13:01:09 -0800 Subject: [PATCH 44/87] Avoid eager creation of controller --- .../browser/view/cellParts/chat/cellChatController.ts | 1 + .../notebook/browser/view/cellParts/chat/cellChatPart.ts | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts index 9c058cbcb53..808201cae98 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts @@ -155,6 +155,7 @@ export class NotebookCellChatController extends Disposable { // might not be attached } + this._widget?.dispose(); this._inlineChatListener?.dispose(); this._toolbar?.dispose(); this._inlineChatListener = undefined; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatPart.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatPart.ts index 7bd5d43e88a..bbcdd98fe08 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatPart.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatPart.ts @@ -9,6 +9,7 @@ import { CellContentPart } from 'vs/workbench/contrib/notebook/browser/view/cell import { NotebookCellChatController } from 'vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController'; import 'vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class CellChatPart extends CellContentPart { private _controller: NotebookCellChatController | undefined; @@ -21,13 +22,17 @@ export class CellChatPart extends CellContentPart { private readonly _notebookEditor: INotebookEditorDelegate, private readonly _partContainer: HTMLElement, @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IConfigurationService private readonly _configurationService: IConfigurationService, ) { super(); } override didRenderCell(element: ICellViewModel): void { this._controller?.dispose(); - this._controller = this._instantiationService.createInstance(NotebookCellChatController, this._notebookEditor, this, element, this._partContainer); + const enabled = this._configurationService.getValue('notebook.experimental.cellChat'); + if (enabled) { + this._controller = this._instantiationService.createInstance(NotebookCellChatController, this._notebookEditor, this, element, this._partContainer); + } super.didRenderCell(element); } From 907744a8cb654bf74c40ca538f0c78e5fc57af4f Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 17 Nov 2023 13:04:12 -0800 Subject: [PATCH 45/87] lazy chat widget ctor --- .../view/cellParts/chat/cellChatController.ts | 79 +++++++++++-------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts index 808201cae98..24de01fcfd2 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts @@ -76,7 +76,7 @@ export class NotebookCellChatController extends Disposable { @IInlineChatSessionService private readonly _inlineChatSessionService: IInlineChatSessionService, @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @INotebookExecutionStateService _notebookExecutionStateService: INotebookExecutionStateService, + @INotebookExecutionStateService private readonly _notebookExecutionStateService: INotebookExecutionStateService, ) { super(); @@ -93,39 +93,8 @@ export class NotebookCellChatController extends Disposable { return; } - if (!this._widget) { - this._widget = this._instantiationService.createInstance(InlineChatWidget, editor, { - menuId: MENU_CELL_CHAT_WIDGET, - statusMenuId: MENU_CELL_CHAT_WIDGET_STATUS, - feedbackMenuId: MENU_CELL_CHAT_WIDGET_FEEDBACK - }); - - this._widgetDisposableStore.add(this._widget.onDidChangeHeight(() => { - this._updateHeight(); - })); - - this._widgetDisposableStore.add(_notebookExecutionStateService.onDidChangeExecution(e => { - if (e.notebook.toString() !== this._notebookEditor.textModel?.uri.toString()) { - return; - } - - if (e.type === NotebookExecutionType.cell && e.affectsCell(this._cell.uri) && e.changed === undefined /** complete */) { - // check if execution is successfull - const { lastRunSuccess } = this._cell.internalMetadata; - if (lastRunSuccess) { - this._strategy?.createSnapshot(); - } - } - })); - - - this._partContainer.appendChild(this._widget.domNode); - this._partContainer.appendChild(this._toolbarDOM.editorToolbar); - - this._toolbar = this._register(this._instantiationService.createInstance(MenuWorkbenchToolBar, this._toolbarDOM.editorToolbar, MENU_CELL_CHAT_WIDGET_TOOLBAR, { - telemetrySource: 'interactiveEditorWidget-toolbar', - toolbarOptions: { primaryGroup: 'main' } - })); + if (!this._widget && this._isVisible) { + this._initialize(editor); } const inlineChatController = InlineChatController.get(editor); @@ -137,6 +106,41 @@ export class NotebookCellChatController extends Disposable { })); } + private _initialize(editor: IActiveCodeEditor) { + this._widget = this._instantiationService.createInstance(InlineChatWidget, editor, { + menuId: MENU_CELL_CHAT_WIDGET, + statusMenuId: MENU_CELL_CHAT_WIDGET_STATUS, + feedbackMenuId: MENU_CELL_CHAT_WIDGET_FEEDBACK + }); + + this._widgetDisposableStore.add(this._widget.onDidChangeHeight(() => { + this._updateHeight(); + })); + + this._widgetDisposableStore.add(this._notebookExecutionStateService.onDidChangeExecution(e => { + if (e.notebook.toString() !== this._notebookEditor.textModel?.uri.toString()) { + return; + } + + if (e.type === NotebookExecutionType.cell && e.affectsCell(this._cell.uri) && e.changed === undefined /** complete */) { + // check if execution is successfull + const { lastRunSuccess } = this._cell.internalMetadata; + if (lastRunSuccess) { + this._strategy?.createSnapshot(); + } + } + })); + + + this._partContainer.appendChild(this._widget.domNode); + this._partContainer.appendChild(this._toolbarDOM.editorToolbar); + + this._toolbar = this._register(this._instantiationService.createInstance(MenuWorkbenchToolBar, this._toolbarDOM.editorToolbar, MENU_CELL_CHAT_WIDGET_TOOLBAR, { + telemetrySource: 'interactiveEditorWidget-toolbar', + toolbarOptions: { primaryGroup: 'main' } + })); + } + public override dispose(): void { if (this._isVisible) { // detach the chat widget @@ -189,6 +193,13 @@ export class NotebookCellChatController extends Disposable { async show() { this._isVisible = true; + if (!this._widget) { + const editor = this._getCellEditor(); + if (editor) { + this._initialize(editor); + } + } + this._partContainer.style.display = 'flex'; this._widget?.focus(); this._widget?.updateInfo(localize('welcome.1', "AI-generated code may be incorrect")); From 521466d52ff2f0ae0dbcc141e3dcdd8f892cdda2 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 17 Nov 2023 22:15:48 +0100 Subject: [PATCH 46/87] inlinechat: add UI for followups (#198563) inlinechat: add followups --- .../contrib/inlineChat/browser/inlineChat.css | 14 +++++++++ .../browser/inlineChatController.ts | 24 ++++++++++++++- .../inlineChat/browser/inlineChatWidget.ts | 30 +++++++++++++++---- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css index d26cc1cd917..d48e61ff247 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css @@ -193,6 +193,20 @@ -webkit-line-clamp: var(--vscode-inline-chat-expanded, 10); } +.monaco-editor .inline-chat .followUps { + padding: 5px 5px; +} + +.monaco-editor .inline-chat .followUps .interactive-session-followups .monaco-button { + display: block; + color: var(--vscode-textLink-foreground); + font-size: 12px; +} + +.monaco-editor .inline-chat .followUps.hidden { + display: none; +} + .monaco-editor .inline-chat .chatMessage { padding: 8px 3px; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 77664f39d96..7f2f70def69 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as aria from 'vs/base/browser/ui/aria/aria'; -import { Barrier, Queue, raceCancellationError } from 'vs/base/common/async'; +import { Barrier, Queue, raceCancellation, raceCancellationError } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter, Event } from 'vs/base/common/event'; @@ -684,6 +684,7 @@ export class InlineChatController implements IEditorContribution { try { this._zone.value.widget.updateChatMessage(undefined); this._zone.value.widget.updateMarkdownMessage(undefined); + this._zone.value.widget.updateFollowUps(undefined); this._zone.value.widget.updateProgress(true); this._zone.value.widget.updateInfo(!this._activeSession.lastExchange ? localize('thinking', "Thinking\u2026") : ''); this._ctxHasActiveRequest.set(true); @@ -840,6 +841,27 @@ export class InlineChatController implements IEditorContribution { this._zone.value.widget.updateToolbar(true); await this._strategy.renderChanges(response); + + if (this._activeSession.provider.provideFollowups) { + const followupCts = new CancellationTokenSource(); + const msgListener = Event.once(this._messages.event)(() => { + followupCts.cancel(); + }); + const followupTask = this._activeSession.provider.provideFollowups(this._activeSession.session, response.raw, followupCts.token); + this._log('followup request started', this._activeSession.provider.debugName, this._activeSession.session, response.raw); + raceCancellation(Promise.resolve(followupTask), followupCts.token).then(followupReply => { + if (followupReply && this._activeSession) { + this._log('followup request received', this._activeSession.provider.debugName, this._activeSession.session, followupReply); + this._zone.value.widget.updateFollowUps(followupReply, followup => { + this.updateInput(followup.message); + this.acceptInput(); + }); + } + }).finally(() => { + msgListener.dispose(); + followupCts.dispose(); + }); + } } this._showWidget(false); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 145d858b521..986178c283a 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -67,8 +67,9 @@ import { ChatModel, ChatResponseModel } from 'vs/workbench/contrib/chat/common/c import { ILogService } from 'vs/platform/log/common/log'; import { ChatListItemRenderer, IChatListItemRendererOptions, IChatRendererDelegate } from 'vs/workbench/contrib/chat/browser/chatListRenderer'; import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatReplyFollowup } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { ChatFollowups } from 'vs/workbench/contrib/chat/browser/chatFollowups'; const defaultAriaLabel = localize('aria-label', "Inline Chat Input"); @@ -144,7 +145,6 @@ export interface IInlineChatWidgetConstructionOptions { export interface IInlineChatMessage { message: IMarkdownString; - followups?: IChatFollowup[]; requestId: string; providerId: string; } @@ -182,6 +182,7 @@ export class InlineChatWidget { h('div.message@message'), h('div.messageActions@messageActions') ]), + h('div.followUps.hidden@followUps'), h('div.status@status', [ h('div.label.info.hidden@infoLabel'), h('div.actions.hidden@statusToolbar'), @@ -234,6 +235,7 @@ export class InlineChatWidget { private readonly _editorOptions: ChatEditorOptions; private _codeBlockDisposables = this._store.add(new DisposableStore()); private _chatMessageDisposables = this._store.add(new DisposableStore()); + private _followUpDisposables = this._store.add(new DisposableStore()); constructor( private readonly parentEditor: ICodeEditor, @@ -419,10 +421,14 @@ export class InlineChatWidget { this._previewCreateTitle = this._store.add(_instantiationService.createInstance(ResourceLabel, this._elements.previewCreateTitle, { supportIcons: true })); this._previewCreateEditor = new Lazy(() => 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); this._elements.chatMessageContent.tabIndex = 0; this._elements.chatMessageContent.ariaLabel = this._accessibleViewService.getOpenAriaHint(AccessibilityVerbositySettingId.InlineChat); + this._elements.message.tabIndex = 0; + this._elements.message.ariaLabel = this._accessibleViewService.getOpenAriaHint(AccessibilityVerbositySettingId.InlineChat); + this._elements.followUps.tabIndex = 0; + this._elements.followUps.ariaLabel = this._accessibleViewService.getOpenAriaHint(AccessibilityVerbositySettingId.InlineChat); + + this._elements.statusLabel.tabIndex = 0; const markdownMessageToolbar = this._instantiationService.createInstance(MenuWorkbenchToolBar, this._elements.messageActions, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, workbenchToolbarOptions); this._store.add(markdownMessageToolbar.onDidChangeMenuItems(() => this._onDidChangeHeight.fire())); @@ -507,11 +513,12 @@ export class InlineChatWidget { const base = getTotalHeight(this._elements.progress) + getTotalHeight(this._elements.status); const editorHeight = this._inputEditor.getContentHeight() + 12 /* padding and border */; const markdownMessageHeight = getTotalHeight(this._elements.markdownMessage); + const followUpsHeight = getTotalHeight(this._elements.followUps); const chatResponseHeight = getTotalHeight(this._elements.chatMessage); const previewDiffHeight = this._previewDiffEditor.hasValue && this._previewDiffEditor.value.getModel() ? 12 + Math.min(300, Math.max(0, this._previewDiffEditor.value.getContentHeight())) : 0; const previewCreateTitleHeight = getTotalHeight(this._elements.previewCreateTitle); const previewCreateHeight = this._previewCreateEditor.hasValue && this._previewCreateEditor.value.getModel() ? 18 + Math.min(300, Math.max(0, this._previewCreateEditor.value.getContentHeight())) : 0; - return base + editorHeight + markdownMessageHeight + chatResponseHeight + previewDiffHeight + previewCreateTitleHeight + previewCreateHeight + 18 /* padding */ + 8 /*shadow*/; + return base + editorHeight + markdownMessageHeight + followUpsHeight + chatResponseHeight + previewDiffHeight + previewCreateTitleHeight + previewCreateHeight + 18 /* padding */ + 8 /*shadow*/; } updateProgress(show: boolean) { @@ -611,6 +618,18 @@ export class InlineChatWidget { } } + updateFollowUps(items: IChatReplyFollowup[], onFollowup: (followup: IChatReplyFollowup) => void): void; + updateFollowUps(items: undefined): void; + updateFollowUps(items: IChatReplyFollowup[] | undefined, onFollowup?: ((followup: IChatReplyFollowup) => void)) { + this._followUpDisposables.clear(); + this._elements.followUps.classList.toggle('hidden', !items || items.length === 0); + reset(this._elements.followUps); + if (items && items.length > 0 && onFollowup) { + this._followUpDisposables.add(new ChatFollowups(this._elements.followUps, items, undefined, onFollowup, this._contextKeyService)); + } + this._onDidChangeHeight.fire(); + } + updateMarkdownMessage(message: IMarkdownString | undefined) { this._codeBlockDisposables.clear(); const hasMessage = message?.value; @@ -718,6 +737,7 @@ export class InlineChatWidget { this.value = ''; this.updateMarkdownMessage(undefined); this.updateChatMessage(undefined); + this.updateFollowUps(undefined); reset(this._elements.statusLabel); this._elements.statusLabel.classList.toggle('hidden', true); From 69c36258933fd28d96b34fc86c1ad0098243b932 Mon Sep 17 00:00:00 2001 From: John Murray Date: Fri, 17 Nov 2023 22:08:54 +0000 Subject: [PATCH 47/87] Remove obsolete migration code for Timeline setting (#198542) --- .../workbench/contrib/timeline/browser/timelinePane.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 88f98b07a07..28036f14af7 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -279,15 +279,6 @@ export class TimelinePane extends ViewPane { this.followActiveEditorContext = TimelineFollowActiveEditorContext.bindTo(this.contextKeyService); this.timelineExcludeSourcesContext = TimelineExcludeSources.bindTo(this.contextKeyService); - // TOOD @lramos15 remove after a few iterations of deprecated setting - const oldExcludedSourcesSetting: string[] = configurationService.getValue('timeline.excludeSources'); - if (oldExcludedSourcesSetting) { - configurationService.updateValue('timeline.excludeSources', undefined); - const oldSettingString = JSON.stringify(oldExcludedSourcesSetting); - this.timelineExcludeSourcesContext.set(oldSettingString); - // Update the storage service with the setting - storageService.store('timeline.excludeSources', oldSettingString, StorageScope.PROFILE, StorageTarget.USER); - } const excludedSourcesString = storageService.get('timeline.excludeSources', StorageScope.PROFILE, '[]'); this.timelineExcludeSourcesContext.set(excludedSourcesString); this.excludedSources = new Set(JSON.parse(excludedSourcesString)); From 0fb4e6f5d94ff71934c4f38ce79ac19e1a967b5c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 17 Nov 2023 14:27:02 -0800 Subject: [PATCH 48/87] Hide command guides when not needed Fixes #196634 --- .../contrib/terminal/browser/xterm/markNavigationAddon.ts | 4 ++-- .../highlight/browser/terminal.highlight.contribution.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts index 3127deae36f..5b7908e25aa 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts @@ -265,7 +265,8 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe return; } if (!command) { - this._resetNavigationDecorations(); + this._outputHighlights.clear(); + this._highlightedCommand = undefined; return; } if (this._highlightedCommand === command) { @@ -316,7 +317,6 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe const color = this._themeService.getColorTheme().getColor(TERMINAL_OVERVIEW_RULER_CURSOR_FOREGROUND_COLOR); const startLine = toLineIndex(marker); const decorationCount = endMarker ? toLineIndex(endMarker) - startLine + 1 : 1; - for (let i = 0; i < decorationCount; i++) { const decoration = this._terminal.registerDecoration({ marker: this._createMarkerForOffset(marker, i), diff --git a/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts b/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts index 38ec3e391e9..a0708b038a0 100644 --- a/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts @@ -31,7 +31,6 @@ class TerminalHighlightContribution extends Disposable implements ITerminalContr xtermOpen(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { const screenElement = xterm.raw.element!.querySelector('.xterm-screen')!; this._register(addDisposableListener(screenElement, 'mousemove', (e: MouseEvent) => { - console.log(e.target); if ((e.target as any).tagName !== 'CANVAS') { return; } @@ -40,7 +39,6 @@ class TerminalHighlightContribution extends Disposable implements ITerminalContr return; } const mouseCursorY = Math.floor(e.offsetY / (rect.height / xterm.raw.rows)); - console.log(`@${mouseCursorY}`, e.offsetY); const command = this._instance.capabilities.get(TerminalCapability.CommandDetection)?.getCommandForLine(xterm.raw.buffer.active.viewportY + mouseCursorY); if (command && 'getOutput' in command) { xterm.markTracker.showCommandGuide(command); @@ -48,6 +46,8 @@ class TerminalHighlightContribution extends Disposable implements ITerminalContr xterm.markTracker.showCommandGuide(undefined); } })); + this._register(addDisposableListener(screenElement, 'mouseout', () => xterm.markTracker.showCommandGuide(undefined))); + this._register(xterm.raw.onData(() => xterm.markTracker.showCommandGuide(undefined))); } } From 952679449b7707bef6c3f37d7373c1ece2d3ef6d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 17 Nov 2023 14:28:26 -0800 Subject: [PATCH 49/87] Improve property names --- .../browser/xterm/markNavigationAddon.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts index 5b7908e25aa..7cedc2249dc 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts @@ -31,6 +31,9 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe protected _terminal: Terminal | undefined; private _navigationDecorations: IDecoration[] | undefined; + private _activeCommandGuide?: ITerminalCommand; + private _commandGuideDecorations = this._register(new MutableDisposable()); + activate(terminal: Terminal): void { this._terminal = terminal; this._register(this._terminal.onData(() => { @@ -257,26 +260,23 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe ); } - private _highlightedCommand?: ITerminalCommand; - private _outputHighlights = this._register(new MutableDisposable()); - showCommandGuide(command: ITerminalCommand | undefined): void { if (!this._terminal) { return; } if (!command) { - this._outputHighlights.clear(); - this._highlightedCommand = undefined; + this._commandGuideDecorations.clear(); + this._activeCommandGuide = undefined; return; } - if (this._highlightedCommand === command) { + if (this._activeCommandGuide === command) { return; } if (command.marker) { - this._highlightedCommand = command; + this._activeCommandGuide = command; // Highlight output - const store = this._outputHighlights.value = new DisposableStore(); + const store = this._commandGuideDecorations.value = new DisposableStore(); if (!command.executedMarker || !command.endMarker) { return; } From dbd4b8a6697fc0b7d41b0c7bd690adea9cdf19f4 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 17 Nov 2023 14:32:07 -0800 Subject: [PATCH 50/87] only trigger view creation when notebook is opened (#198568) --- .../browser/contrib/notebookVariables/notebookVariables.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts index 90b17fe7aad..be6c2da14e1 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts @@ -26,9 +26,6 @@ export class NotebookVariables extends Disposable implements IWorkbenchContribut @IConfigurationService configurationService: IConfigurationService, ) { super(); - if (this.initializeView()) { - return; - } this.listener = this.editorService.onDidEditorsChange(() => { if (configurationService.getValue('notebook.experimental.notebookVariablesView') From 736d3a809d36a31274b5f42c25598f481cf91cb9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 17 Nov 2023 14:38:52 -0800 Subject: [PATCH 51/87] Include command start sequence in test --- .../test/browser/capabilities/commandDetectionCapability.test.ts | 1 + 1 file changed, 1 insertion(+) 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 aa656c1a431..a9765ea9bd3 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 @@ -63,6 +63,7 @@ suite('CommandDetectionCapability', () => { async function printCommandStart(prompt: string) { capability.handlePromptStart(); await writeP(xterm, `\r${prompt}`); + capability.handleCommandStart(); } From 6f7e102f08acbb75fbdbe4c052b425149f10518a Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 17 Nov 2023 15:30:35 -0800 Subject: [PATCH 52/87] pipe variable change event through from extention API (#198572) pipe variable change event through --- src/vs/workbench/api/browser/mainThreadNotebookKernels.ts | 4 ++++ src/vs/workbench/api/common/extHost.protocol.ts | 1 + src/vs/workbench/api/common/extHostNotebookKernels.ts | 1 + .../contrib/notebookVariables/notebookVariablesView.ts | 8 ++++++++ .../browser/services/notebookKernelServiceImpl.ts | 6 ++++++ .../contrib/notebook/common/notebookKernelService.ts | 3 +++ .../vscode.proposed.notebookVariableProvider.d.ts | 2 +- 7 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index 196ecaea1ea..e1190a65660 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -440,4 +440,8 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape emitter.fire(undefined); } } + + $variablesUpdated(notebookUri: UriComponents): void { + this._notebookKernelService.notifyVariablesChange(URI.revive(notebookUri)); + } } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 7bb1c7cc4b4..a5675e79f1e 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1134,6 +1134,7 @@ export interface MainThreadNotebookKernelsShape extends IDisposable { $removeKernelSourceActionProvider(handle: number, eventHandle: number): void; $emitNotebookKernelSourceActionsChangeEvent(eventHandle: number): void; $receiveVariable(requestId: string, variable: VariablesResult): void; + $variablesUpdated(notebookUri: UriComponents): void; } export interface MainThreadNotebookRenderersShape extends IDisposable { diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 76eadcb84c3..164369d1212 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -212,6 +212,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { checkProposedApiEnabled(extension, 'notebookVariableProvider'); _variableProvider = value; data.hasVariableProvider = !!value; + value?.onDidChangeVariables(e => that._proxy.$variablesUpdated(e.uri)); _update(); }, get variableProvider() { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index 445c8e442be..eba684c4c9f 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -5,6 +5,7 @@ import { IObjectTreeElement } from 'vs/base/browser/ui/tree/tree'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { ILocalizedString } from 'vs/platform/action/common/action'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -56,6 +57,7 @@ export class NotebookVariablesView extends ViewPane { this._register(this.editorService.onDidActiveEditorChange(this.handleActiveEditorChange.bind(this))); this._register(this.notebookExecutionStateService.onDidChangeExecution(this.handleExecutionStateChange.bind(this))); + this._register(this.notebookKernelService.onDidNotebookVariablesUpdate(this.handleVariablesChanged.bind(this))); } protected override renderBody(container: HTMLElement): void { @@ -105,6 +107,12 @@ export class NotebookVariablesView extends ViewPane { } } + private handleVariablesChanged(notebookUri: URI) { + if (this.activeNotebook && notebookUri.toString() === this.activeNotebook.uri.toString()) { + this.updateVariables(this.activeNotebook); + } + } + private async updateVariables(notebook: NotebookTextModel) { const selectedKernel = this.notebookKernelService.getMatchingKernel(notebook).selected; if (selectedKernel && selectedKernel.hasVariableProvider) { diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookKernelServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookKernelServiceImpl.ts index adc3249aab0..d86b1b7f928 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookKernelServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookKernelServiceImpl.ts @@ -106,6 +106,7 @@ export class NotebookKernelService extends Disposable implements INotebookKernel private readonly _onDidRemoveKernel = this._register(new Emitter()); private readonly _onDidChangeNotebookAffinity = this._register(new Emitter()); private readonly _onDidChangeSourceActions = this._register(new Emitter()); + private readonly _onDidNotebookVariablesChange = this._register(new Emitter()); private readonly _kernelSources = new Map(); private readonly _kernelSourceActionsUpdates = new Map(); private readonly _kernelDetectionTasks = new Map(); @@ -118,6 +119,7 @@ export class NotebookKernelService extends Disposable implements INotebookKernel readonly onDidChangeNotebookAffinity: Event = this._onDidChangeNotebookAffinity.event; readonly onDidChangeSourceActions: Event = this._onDidChangeSourceActions.event; readonly onDidChangeKernelDetectionTasks: Event = this._onDidChangeKernelDetectionTasks.event; + readonly onDidNotebookVariablesUpdate: Event = this._onDidNotebookVariablesChange.event; private static _storageNotebookBinding = 'notebook.controller2NotebookBindings'; @@ -201,6 +203,10 @@ export class NotebookKernelService extends Disposable implements INotebookKernel } } + notifyVariablesChange(notebookUri: URI): void { + this._onDidNotebookVariablesChange.fire(notebookUri); + } + registerKernel(kernel: INotebookKernel): IDisposable { if (this._kernels.has(kernel.id)) { throw new Error(`NOTEBOOK CONTROLLER with id '${kernel.id}' already exists`); diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index 30c9bc59f57..6b1e5ca8c9b 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -117,6 +117,7 @@ export interface INotebookKernelService { readonly onDidRemoveKernel: Event; readonly onDidChangeSelectedNotebooks: Event; readonly onDidChangeNotebookAffinity: Event; + readonly onDidNotebookVariablesUpdate: Event; registerKernel(kernel: INotebookKernel): IDisposable; getMatchingKernel(notebook: INotebookTextModelLike): INotebookKernelMatchResult; @@ -155,6 +156,8 @@ export interface INotebookKernelService { registerKernelSourceActionProvider(viewType: string, provider: IKernelSourceActionProvider): IDisposable; getKernelSourceActions2(notebook: INotebookTextModelLike): Promise; //#endregion + + notifyVariablesChange(notebookUri: URI): void; } export const INotebookKernelHistoryService = createDecorator('INotebookKernelHistoryService'); diff --git a/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts index 02fa3f10b77..0071af59ff4 100644 --- a/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts @@ -21,7 +21,7 @@ declare module 'vscode' { } interface NotebookVariableProvider { - onDidChangeVariables: Event; + onDidChangeVariables: Event; /** When parent is undefined, this is requesting global Variables. When a variable is passed, it's requesting child props of that Variable. */ provideVariables(notebook: NotebookDocument, parent: Variable | undefined, kind: NotebookVariablesRequestKind, start: number, token: CancellationToken): AsyncIterable; From b1cddfafe0c054fbeb346a3d3dc9a58f504eb55b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Sat, 18 Nov 2023 09:41:37 +0100 Subject: [PATCH 53/87] tighten up blob uploading (#198553) * tighten up blob uploading * bad save * bad save again --- build/azure-pipelines/common/publish.js | 36 +++++++++++------------- build/azure-pipelines/common/publish.ts | 37 +++++++++++-------------- 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/build/azure-pipelines/common/publish.js b/build/azure-pipelines/common/publish.js index 80792caeb0a..a8e45f6d22b 100644 --- a/build/azure-pipelines/common/publish.js +++ b/build/azure-pipelines/common/publish.js @@ -470,9 +470,8 @@ function getRealType(type) { async function uploadAssetLegacy(log, quality, commit, filePath) { const fileName = path.basename(filePath); const blobName = commit + '/' + fileName; - const storagePipelineOptions = { retryOptions: { retryPolicyType: storage_blob_1.StorageRetryPolicyType.EXPONENTIAL, maxTries: 6, tryTimeoutInMs: 10 * 60 * 1000 } }; const credential = new identity_1.ClientSecretCredential(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_CLIENT_SECRET')); - const blobServiceClient = new storage_blob_1.BlobServiceClient(`https://vscode.blob.core.windows.net`, credential, storagePipelineOptions); + const blobServiceClient = new storage_blob_1.BlobServiceClient(`https://vscode.blob.core.windows.net`, credential, { retryOptions: { retryPolicyType: storage_blob_1.StorageRetryPolicyType.FIXED, tryTimeoutInMs: 2 * 60 * 1000 } }); const containerClient = blobServiceClient.getContainerClient(quality); const blobClient = containerClient.getBlockBlobClient(blobName); const blobOptions = { @@ -485,34 +484,32 @@ async function uploadAssetLegacy(log, quality, commit, filePath) { const uploadPromises = []; uploadPromises.push((async () => { log(`Checking for blob in Azure...`); - if (await (0, retry_1.retry)(() => blobClient.exists())) { + if (await blobClient.exists()) { + log(`Blob ${quality}, ${blobName} already exists, not publishing again.`); throw new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`); } else { - await (0, retry_1.retry)(async (attempt) => { - log(`Uploading blobs to Azure storage (attempt ${attempt})...`); - await blobClient.uploadFile(filePath, blobOptions); - log('Blob successfully uploaded to Azure storage.'); - }); + log(`Uploading blobs to Azure storage...`); + await blobClient.uploadFile(filePath, blobOptions); + log('Blob successfully uploaded to Azure storage.'); } })()); const shouldUploadToMooncake = /true/i.test(e('VSCODE_PUBLISH_TO_MOONCAKE')); if (shouldUploadToMooncake) { const mooncakeCredential = new identity_1.ClientSecretCredential(e('AZURE_MOONCAKE_TENANT_ID'), e('AZURE_MOONCAKE_CLIENT_ID'), e('AZURE_MOONCAKE_CLIENT_SECRET')); - const mooncakeBlobServiceClient = new storage_blob_1.BlobServiceClient(`https://vscode.blob.core.chinacloudapi.cn`, mooncakeCredential, storagePipelineOptions); + const mooncakeBlobServiceClient = new storage_blob_1.BlobServiceClient(`https://vscode.blob.core.chinacloudapi.cn`, mooncakeCredential, { retryOptions: { retryPolicyType: storage_blob_1.StorageRetryPolicyType.FIXED, tryTimeoutInMs: 5 * 60 * 1000 } }); const mooncakeContainerClient = mooncakeBlobServiceClient.getContainerClient(quality); const mooncakeBlobClient = mooncakeContainerClient.getBlockBlobClient(blobName); uploadPromises.push((async () => { log(`Checking for blob in Mooncake Azure...`); - if (await (0, retry_1.retry)(() => mooncakeBlobClient.exists())) { + if (await mooncakeBlobClient.exists()) { + log(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); throw new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); } else { - await (0, retry_1.retry)(async (attempt) => { - log(`Uploading blobs to Mooncake Azure storage (attempt ${attempt})...`); - await mooncakeBlobClient.uploadFile(filePath, blobOptions); - log('Blob successfully uploaded to Mooncake Azure storage.'); - }); + log(`Uploading blobs to Mooncake Azure storage...`); + await mooncakeBlobClient.uploadFile(filePath, blobOptions); + log('Blob successfully uploaded to Mooncake Azure storage.'); } })()); } @@ -522,14 +519,13 @@ async function uploadAssetLegacy(log, quality, commit, filePath) { log('All blobs successfully uploaded.'); } else if (rejectedPromiseResults[0]?.reason?.message?.includes('already exists')) { - log(rejectedPromiseResults[0].reason.message); log('Some blobs successfully uploaded.'); } else { // eslint-disable-next-line no-throw-literal throw rejectedPromiseResults[0]?.reason; } - const assetUrl = `${e('AZURE_CDN_URL')} / ${quality} / ${blobName}`; + const assetUrl = `${e('AZURE_CDN_URL')}/${quality}/${blobName}`; const blobPath = new URL(assetUrl).pathname; const mooncakeUrl = `${e('MOONCAKE_CDN_URL')}${blobPath}`; return { assetUrl, mooncakeUrl }; @@ -619,7 +615,7 @@ async function main() { if (done.has(artifact.name) || processing.has(artifact.name)) { continue; } - console.log(`${artifact.name} Found new artifact`); + console.log(`[${artifact.name}] Found new artifact`); const artifactZipPath = path.join(e('AGENT_TEMPDIRECTORY'), `${artifact.name}.zip`); await (0, retry_1.retry)(async (attempt) => { const start = Date.now(); @@ -628,7 +624,7 @@ async function main() { const archiveSize = fs.statSync(artifactZipPath).size; const downloadDurationS = (Date.now() - start) / 1000; const downloadSpeedKBS = Math.round((archiveSize / 1024) / downloadDurationS); - console.log(`[${artifact.name}] Successfully downloaded after ${Math.floor(downloadDurationS)} seconds(${downloadSpeedKBS} KB / s).`); + console.log(`[${artifact.name}] Successfully downloaded after ${Math.floor(downloadDurationS)} seconds(${downloadSpeedKBS} KB/s).`); }); const artifactFilePath = await unzip(artifactZipPath, e('AGENT_TEMPDIRECTORY')); const artifactSize = fs.statSync(artifactFilePath).size; @@ -683,4 +679,4 @@ if (require.main === module) { process.exit(1); }); } -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGlzaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInB1Ymxpc2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyx5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLG1DQUFrQztBQUVsQyxtREFBZ0Q7QUFDaEQsK0JBQStCO0FBQy9CLGlDQUFpQztBQUNqQyxtQ0FBZ0M7QUFDaEMsc0RBQXdJO0FBQ3hJLDZCQUE2QjtBQUM3QiwwQ0FBNkM7QUFDN0MsOENBQXlEO0FBQ3pELG9DQUFvQztBQUNwQyx5QkFBeUI7QUFDekIsNkRBQXVFO0FBRXZFLFNBQVMsQ0FBQyxDQUFDLElBQVk7SUFDdEIsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUVqQyxJQUFJLE9BQU8sTUFBTSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ2hDLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0JBQWdCLElBQUksRUFBRSxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUVELE9BQU8sTUFBTSxDQUFDO0FBQ2YsQ0FBQztBQUVELE1BQU0sSUFBSTtJQUNELE1BQU0sR0FBYSxFQUFFLENBQUM7SUFFOUIsV0FBVztRQUNWLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDNUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdkIsT0FBTyxJQUFJLENBQUM7SUFDYixDQUFDO0lBRUQsT0FBTztRQUNOLEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2hDLElBQUksQ0FBQztnQkFDSixFQUFFLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3JCLENB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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGlzaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInB1Ymxpc2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyx5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLG1DQUFrQztBQUVsQyxtREFBZ0Q7QUFDaEQsK0JBQStCO0FBQy9CLGlDQUFpQztBQUNqQyxtQ0FBZ0M7QUFDaEMsc0RBQWdIO0FBQ2hILDZCQUE2QjtBQUM3QiwwQ0FBNkM7QUFDN0MsOENBQXlEO0FBQ3pELG9DQUFvQztBQUNwQyx5QkFBeUI7QUFDekIsNkRBQXVFO0FBRXZFLFNBQVMsQ0FBQyxDQUFDLElBQVk7SUFDdEIsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUVqQyxJQUFJLE9BQU8sTUFBTSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ2hDLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0JBQWdCLElBQUksRUFBRSxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUVELE9BQU8sTUFBTSxDQUFDO0FBQ2YsQ0FBQztBQUVELE1BQU0sSUFBSTtJQUNELE1BQU0sR0FBYSxFQUFFLENBQUM7SUFFOUIsV0FBVztRQUNWLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDNUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdkIsT0FBTyxJQUFJLENBQUM7SUFDYixDQUFDO0lBRUQsT0FBTztRQUNOLEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2hDLElBQUksQ0FBQztnQkFDSixFQUFFLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3JCLENBQUM7WUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO2dCQUNkLE9BQU87WUFDUixDQUFDO1FBQ0YsQ0FBQztJQUNGLENBQUM7Q0FDRDtBQXdCRCxNQUFNLGdCQUFnQjtJQUdIO0lBQ0E7SUFGbEIsWUFDa0IsR0FBNkIsRUFDN0IsV0FBbUI7UUFEbkIsUUFBRyxHQUFILEdBQUcsQ0FBMEI7UUFDN0IsZ0JBQVcsR0FBWCxXQUFXLENBQVE7SUFDakMsQ0FBQztJQUVMLEtBQUssQ0FBQyxTQUFTLENBQUMsU0FBaUIsRUFBRSxNQUFjLEVBQUUsUUFBZ0I7UUFDbEUsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztZQUMzQixTQUFTLEVBQUUsU0FBUztZQUNwQixVQUFVLEVBQUUsUUFBUTtZQUNwQixhQUFhLEVBQUUsUUFBUTtZQUN2QiwwQkFBMEIsRUFBRSxDQUFDO29CQUM1QixZQUFZLEVBQUUsTUFBTTtvQkFDcEIsd0JBQXdCLEVBQUUsSUFBSTtvQkFDOUIsZ0JBQWdCLEVBQUUsUUFBUTtvQkFDMUIsTUFBTSxFQUFFLE1BQU07b0JBQ2QsV0FBVyxFQUFFLENBQUMsS0FBSyxDQUFDO2lCQUNwQixDQUFDO1NBQ0YsQ0FBQyxDQUFDO1FBRUgsSUFBSSxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsUUFBUSxnQkFBZ0IsU0FBUyxhQUFhLE1BQU0sTUFBTSxDQUFDLENBQUM7UUFDckYsTUFBTSxHQUFHLEdBQUcsTUFBTSxJQUFBLGFBQUssRUFBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFpQyxNQUFNLEVBQUUsaURBQWlELEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFFakosSUFBSSxDQUFDLEdBQUcsQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNwQixNQUFNLElBQUksS0FBSyxDQUFDLDBDQUEwQyxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDL0YsQ0FBQztRQUVELElBQUksQ0FBQyxHQUFHLENBQUMsNEJBQTRCLFFBQVEsRUFBRSxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUVPLEtBQUssQ0FBQyxPQUFPLENBQUksTUFBYyxFQUFFLEdBQVcsRUFBRSxPQUF3QjtRQUM3RSxNQUFNLElBQUksR0FBZ0I7WUFDekIsTUFBTTtZQUNOLElBQUksRUFBRSxPQUFPLEVBQUUsSUFBSTtZQUNuQixPQUFPLEVBQUU7Z0JBQ1IsYUFBYSxFQUFFLFVBQVUsSUFBSSxDQUFDLFdBQVcsRUFBRTtnQkFDM0MsY0FBYyxFQUFFLGtCQUFrQjthQUNsQztTQUNELENBQUM7UUFFRixNQUFNLEdBQUcsR0FBRyxNQUFNLEtBQUssQ0FBQyx1Q0FBdUMsR0FBRyxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFFNUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLElBQUksR0FBRyxDQUFDLE1BQU0sR0FBRyxHQUFHLElBQUksR0FBRyxDQUFDLE1BQU0sSUFBSSxHQUFHLEVBQUUsQ0FBQztZQUN0RCxNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixHQUFHLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUMxRCxDQUFDO1FBRUQsT0FBTyxNQUFNLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN6QixDQUFDO0NBQ0Q7QUFFRCxTQUFTLFVBQVUsQ0FBQyxRQUFnQixFQUFFLE1BQWdCO0lBQ3JELE9BQU8sSUFBSSxPQUFPLENBQVMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUU7UUFDbkMsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUUzQyxNQUFNO2FBQ0osRUFBRSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQzthQUN0QyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQzthQUNkLEVBQUUsQ0FBQyxPQUFPLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzlDLENBQUMsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQXFCRCxNQUFNLFVBQVU7SUFLRztJQUNBO0lBSkQsUUFBUSxDQUFTO0lBRWxDLFlBQ2tCLEdBQTZCLEVBQzdCLEdBQVMsRUFDMUIsUUFBZ0IsRUFDaEIsUUFBZ0IsRUFDaEIsbUJBQTJCLEVBQzNCLDZCQUFxQztRQUxwQixRQUFHLEdBQUgsR0FBRyxDQUEwQjtRQUM3QixRQUFHLEdBQUgsR0FBRyxDQUFNO1FBTTFCLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUN2QyxFQUFFLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQztZQUM5QyxPQUFPLEVBQUUsT0FBTztZQUNoQixrQkFBa0IsRUFBRSxVQUFVO1lBQzlCLFFBQVEsRUFBRSxRQUFRO1lBQ2xCLFFBQVEsRUFBRSxRQUFRO1lBQ2xCLFFBQVEsRUFBRTtnQkFDVCxXQUFXLEVBQUUsbUJBQW1CO2dCQUNoQyxhQUFhLEVBQUUsY0FBYztnQkFDN0IsU0FBUyxFQUFFLElBQUk7Z0JBQ2YsT0FBTyxFQUFFLE1BQU07YUFDZjtZQUNELGtCQUFrQixFQUFFO2dCQUNuQixXQUFXLEVBQUUsNkJBQTZCO2dCQUMxQyxhQUFhLEVBQUUsY0FBYztnQkFDN0IsU0FBUyxFQUFFLElBQUk7YUFDZjtTQUNELENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELEtBQUssQ0FBQyxPQUFPLENBQ1osT0FBZSxFQUNmLFFBQWdCO1FBRWhCLElBQUksQ0FBQyxHQUFHLENBQUMsMEJBQTBCLE9BQU8sS0FBSyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1FBQzNELE1BQU0sbUJBQW1CLEdBQUcsTUFBTSxJQUFJLENBQUMsYUFBYSxDQUFDLE9BQU8sRUFBRSxRQUFRLENBQUMsQ0FBQztRQUV4RSxJQUFJLG1CQUFtQixDQUFDLGtCQUFrQixDQUFDLFVBQVUsS0FBSyxNQUFNLEVBQUUsQ0FBQztZQUNsRSxNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixtQkFBbUIsQ0FBQyxrQkFBa0IsQ0FBQyxVQUFVLEVBQUUsQ0FBQyxDQUFDO1FBQ2pHLENBQUM7UUFFRCxNQUFNLFNBQVMsR0FBRyxtQkFBbUIsQ0FBQyxrQkFBa0IsQ0FBQyxXQUFXLENBQUM7UUFDckUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxrQ0FBa0MsU0FBUyw2QkFBNkIsQ0FBQyxDQUFDO1FBRW5GLElBQUksT0FBOEIsQ0FBQztRQUVuQyxzRUFBc0U7UUFDdEUsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEdBQUcsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQzlCLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsU0FBUyxDQUFDLENBQUM7WUFFL0MsSUFBSSxPQUFPLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDLFVBQVUsS0FBSyxNQUFNLEVBQUUsQ0FBQztnQkFDckQsTUFBTTtZQUNQLENBQUM7aUJBQU0sSUFBSSxPQUFPLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDLFVBQVUsS0FBSyxZQUFZLEVBQUUsQ0FBQztnQkFDbEUsTUFBTSxJQUFJLEtBQUssQ0FBQyw2QkFBNkIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDekUsQ0FBQztZQUVELE1BQU0sSUFBSSxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDN0MsQ0FBQztRQUVELElBQUksT0FBTyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxVQUFVLEtBQUssTUFBTSxFQUFFLENBQUM7WUFDckQsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQ0FBaUMsU0FBUyxLQUFLLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzNGLENBQUM7UUFFRCxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUM7UUFDckUsSUFBSSxDQUFDLEdBQUcsQ0FBQyw4Q0FBOEMsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUVqRSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxDQUFDO0lBQzlCLENBQUM7SUFFTyxLQUFLLENBQUMsYUFBYSxDQUMxQixPQUFlLEVBQ2YsUUFBZ0I7UUFFaEIsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUMxQyxFQUFFLENBQUMsYUFBYSxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDO1lBQzNDLE9BQU8sRUFBRSxPQUFPO1lBQ2hCLFFBQVEsRUFBRSxpQkFBaUI7WUFDM0IsTUFBTSxFQUFFLGNBQWM7WUFDdEIsV0FBVyxFQUFFLGdCQUFnQjtTQUM3QixDQUFDLENBQUMsQ0FBQztRQUVKLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDekMsTUFBTSxJQUFJLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDeEMsTUFBTSxPQUFPLEdBQUcsRUFBRSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQzlDLE1BQU0sTUFBTSxHQUFHLE1BQU0sVUFBVSxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUNuRCxFQUFFLENBQUMsYUFBYSxDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDO1lBQzFDLE9BQU8sRUFBRSxPQUFPO1lBQ2hCLFdBQVcsRUFBRTtnQkFDWixlQUFlLEVBQUU7b0JBQ2hCLEtBQUssRUFBRSxTQUFTO29CQUNoQixVQUFVLEVBQUU7d0JBQ1gsa0JBQWtCLEVBQUUsZ0JBQWdCO3FCQUNwQztvQkFDRCx3QkFBd0IsRUFBRSxDQUFDO2lCQUMzQjtnQkFDRCxXQUFXLEVBQUU7b0JBQ1osSUFBSSxFQUFFLFNBQVM7b0JBQ2YsT0FBTyxFQUFFLE9BQU87b0JBQ2hCLFdBQVcsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO2lCQUM1RDtnQkFDRCxNQUFNLEVBQUU7b0JBQ1A7d0JBQ0MsS0FBSyxFQUFFOzRCQUNOLGlCQUFpQixFQUFFLG9CQUFvQjt5QkFDdkM7cUJBQ0Q7aUJBQ0Q7Z0JBQ0QsU0FBUyxFQUFFO29CQUNWO3dCQUNDLFFBQVEsRUFBRTs0QkFDVCxpQkFBaUIsRUFBRSxvQkFBb0I7eUJBQ3ZDO3dCQUNELGNBQWMsRUFBRSxJQUFJO3dCQUNwQixXQUFXLEVBQUUsS0FBSztxQkFDbEI7aUJBQ0Q7Z0JBQ0QsaUJBQWlCLEVBQUU7b0JBQ2xCLGFBQWEsRUFBRSxRQUFRO29CQUN2Qiw0QkFBNEIsRUFBRTt3QkFDN0IsUUFBUSxFQUFFLENBQUMsUUFBUSxDQUFDO3FCQUNwQjtpQkFDRDtnQkFDRCxTQUFTLEVBQUU7b0JBQ1YsaUJBQWlCLEVBQUUsb0JBQW9CO2lCQUN2QzthQUNEO1lBQ0QsY0FBYyxFQUFFO2dCQUNmO29CQUNDLG1CQUFtQixFQUFFO3dCQUNwQjs0QkFDQyxXQUFXLEVBQUUsSUFBSTs0QkFDakIsVUFBVSxFQUFFLE1BQU07NEJBQ2xCLFFBQVEsRUFBRSxRQUFROzRCQUNsQixjQUFjLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUM7eUJBQ3ZDO3FCQUNEO29CQUNELGtCQUFrQixFQUFFLEtBQUs7b0JBQ3pCLG1CQUFtQixFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDO29CQUMzQyx1QkFBdUIsRUFBRSxXQUFXO2lCQUNwQzthQUNEO1NBQ0QsQ0FBQyxDQUFDLENBQUM7UUFFSixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQzFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsK0JBQStCLElBQUksQ0FBQyxRQUFRLE9BQU8sVUFBVSxPQUFPLFNBQVMsT0FBTyxVQUFVLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDO1FBRXBJLE1BQU0sTUFBTSxHQUFHLEVBQUUsQ0FBQyxZQUFZLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ25ELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQXdCLENBQUM7SUFDbEQsQ0FBQztJQUVPLEtBQUssQ0FBQyxjQUFjLENBQzNCLFNBQWlCO1FBRWpCLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDekMsRUFBRSxDQUFDLGFBQWEsQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQztZQUMxQyxPQUFPLEVBQUUsT0FBTztZQUNoQixZQUFZLEVBQUUsQ0FBQyxTQUFTLENBQUM7U0FDekIsQ0FBQyxDQUFDLENBQUM7UUFFSixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQzFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsZ0NBQWdDLElBQUksQ0FBQyxRQUFRLE9BQU8sU0FBUyxPQUFPLFVBQVUsRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUM7UUFFcEgsTUFBTSxNQUFNLEdBQUcsRUFBRSxDQUFDLFlBQVksQ0FBQyxVQUFVLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDbkQsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBeUIsQ0FBQztJQUNuRCxDQUFDO0NBQ0Q7QUFFRCxLQUFLLFVBQVUsbUJBQW1CLENBQ2pDLEdBQTZCLEVBQzdCLGVBQXVCLEVBQ3ZCLGVBQXVCLEVBQ3ZCLDBCQUFrQyxFQUNsQyxvQ0FBNEMsRUFDNUMsaUJBQXlCLEVBQ3pCLG9CQUE0QixFQUM1QixvQkFBNEIsRUFDNUIsT0FBZSxFQUNmLE9BQWUsRUFDZixRQUFnQjtJQUVoQixNQUFNLFFBQVEsR0FBRyxHQUFHLE9BQU8sSUFBSSxPQUFPLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO0lBQ3BFLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxJQUFJLFFBQVEsRUFBRSxDQUFDO0lBRWxELE1BQU0sR0FBRyxHQUFHLE1BQU0sSUFBQSxhQUFLLEVBQUMsR0FBRyxFQUFFLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFFN0MsSUFBSSxHQUFHLENBQUMsTUFBTSxLQUFLLEdBQUcsRUFBRSxDQUFDO1FBQ3hCLEdBQUcsQ0FBQyxxQ0FBcUMsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUNuRCxPQUFPLE1BQU0sQ0FBQztJQUNmLENBQUM7SUFFRCxNQUFNLEdBQUcsR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDO0lBQ3ZCLE9BQU8sQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO0lBRXhDLE1BQU0sVUFBVSxHQUFHLElBQUksVUFBVSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsZUFBZSxFQUFFLGVBQWUsRUFBRSwwQkFBMEIsRUFBRSxvQ0FBb0MsQ0FBQyxDQUFDO0lBQ2hKLE1BQU0sT0FBTyxHQUFHLE1BQU0sVUFBVSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFFNUQsTUFBTSxVQUFVLEdBQUcsSUFBSSxpQ0FBc0IsQ0FBQyxpQkFBaUIsRUFBRSxvQkFBb0IsRUFBRSxvQkFBb0IsQ0FBQyxDQUFDO0lBQzdHLE1BQU0sV0FBVyxHQUFHLE1BQU0sVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDLG1FQUFtRSxDQUFDLENBQUMsQ0FBQztJQUNySCxNQUFNLE9BQU8sR0FBRyxJQUFJLGdCQUFnQixDQUFDLEdBQUcsRUFBRSxXQUFXLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFN0QsTUFBTSxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsT0FBTyxDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUMsQ0FBQztJQUVyRSxPQUFPLE1BQU0sQ0FBQztBQUNmLENBQUM7QUFFRCxNQUFNLEtBQUs7SUFFRixTQUFTLENBQVM7SUFDbEIsR0FBRyxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFFaEM7UUFDQyxNQUFNLHFCQUFxQixHQUFHLENBQUMsQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1FBQ3RELE1BQU0sYUFBYSxHQUFHLEVBQUUsQ0FBQyxXQUFXLENBQUMscUJBQXFCLENBQUM7YUFDekQsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsNkJBQTZCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO2FBQ3JELE1BQU0sQ0FBQyxDQUFDLEtBQUssRUFBNEIsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7YUFDcEQsR0FBRyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxLQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLE1BQU0sQ0FBQyxLQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7YUFDL0QsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFFM0MsSUFBSSxhQUFhLEVBQUUsQ0FBQztZQUNuQixNQUFNLGlCQUFpQixHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMscUJBQXFCLEVBQUUsYUFBYSxDQUFDLElBQUksRUFBRSxhQUFhLENBQUMsSUFBSSxHQUFHLE1BQU0sQ0FBQyxDQUFDO1lBQzVHLEVBQUUsQ0FBQyxZQUFZLENBQUMsaUJBQWlCLEVBQUUsTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ25ILENBQUM7UUFFRCxNQUFNLFlBQVksR0FBRyxDQUFDLENBQUMscUJBQXFCLENBQUMsQ0FBQztRQUM5QyxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMscUJBQXFCLEVBQUUsdUJBQXVCLFlBQVksRUFBRSxFQUFFLHVCQUF1QixZQUFZLE1BQU0sQ0FBQyxDQUFDO1FBQ3BJLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUNoRSxFQUFFLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDNUYsQ0FBQztJQUVELElBQUksSUFBSTtRQUNQLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUM7SUFDdEIsQ0FBQztJQUVELEdBQUcsQ0FBQyxJQUFZO1FBQ2YsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUMzQixDQUFDO0lBRUQsR0FBRyxDQUFDLElBQVk7UUFDZixJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNuQixFQUFFLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsR0FBRyxJQUFJLElBQUksQ0FBQyxDQUFDO0lBQ2hELENBQUM7SUFFRCxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUM7UUFDaEIsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO0lBQ3BDLENBQUM7Q0FDRDtBQUVELE1BQU0sZ0JBQWdCLEdBQUc7SUFDeEIsT0FBTyxFQUFFO1FBQ1IsNERBQTREO1FBQzVELFlBQVksRUFBRSxxSUFBcUk7UUFDbkosUUFBUSxFQUFFLDhIQUE4SDtRQUN4SSxpQkFBaUIsRUFBRSxtQkFBbUI7UUFDdEMsaUJBQWlCLEVBQUUsZ0JBQWdCO1FBQ25DLFNBQVMsRUFBRSx1QkFBdUI7UUFDbEMsYUFBYSxFQUFFLFVBQVUsQ0FBQyxDQUFDLG9CQUFvQixDQUFDLEVBQUU7S0FDbEQ7Q0FDRCxDQUFDO0FBRUYsS0FBSyxVQUFVLGNBQWMsQ0FBSSxJQUFZO0lBQzVDLE1BQU0sZUFBZSxHQUFHLElBQUksZUFBZSxFQUFFLENBQUM7SUFDOUMsTUFBTSxPQUFPLEdBQUcsVUFBVSxDQUFDLEdBQUcsRUFBRSxDQUFDLGVBQWUsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDO0lBRXpFLElBQUksQ0FBQztRQUNKLE1BQU0sR0FBRyxHQUFHLE1BQU0sS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLEdBQUcsSUFBSSxrQkFBa0IsRUFBRSxFQUFFLEdBQUcsZ0JBQWdCLEVBQUUsTUFBTSxFQUFFLGVBQWUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBRWxJLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDYixNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixHQUFHLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUMxRCxDQUFDO1FBRUQsT0FBTyxNQUFNLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN6QixDQUFDO1lBQVMsQ0FBQztRQUNWLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN2QixDQUFDO0FBQ0YsQ0FBQztBQVlELEtBQUssVUFBVSxvQkFBb0I7SUFDbEMsTUFBTSxNQUFNLEdBQUcsTUFBTSxjQUFjLENBQWlDLFdBQVcsQ0FBQyxDQUFDO0lBQ2pGLE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7QUFDbkYsQ0FBQztBQVVELEtBQUssVUFBVSxtQkFBbUI7SUFDakMsT0FBTyxNQUFNLGNBQWMsQ0FBVyxVQUFVLENBQUMsQ0FBQztBQUNuRCxDQUFDO0FBRUQsS0FBSyxVQUFVLGdCQUFnQixDQUFDLFFBQWtCLEVBQUUsWUFBb0I7SUFDdkUsTUFBTSxlQUFlLEdBQUcsSUFBSSxlQUFlLEVBQUUsQ0FBQztJQUM5QyxNQUFNLE9BQU8sR0FBRyxVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsZUFBZSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLENBQUM7SUFFekUsSUFBSSxDQUFDO1FBQ0osTUFBTSxHQUFHLEdBQUcsTUFBTSxLQUFLLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxXQUFXLEVBQUUsRUFBRSxHQUFHLGdCQUFnQixFQUFFLE1BQU0sRUFBRSxlQUFlLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUVoSCxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ2IsTUFBTSxJQUFJLEtBQUssQ0FBQywyQkFBMkIsR0FBRyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDMUQsQ0FBQztRQUVELE1BQU0sSUFBQSxtQkFBUSxFQUFDLGlCQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFzQixDQUFDLEVBQUUsRUFBRSxDQUFDLGlCQUFpQixDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUM7SUFDbEcsQ0FBQztZQUFTLENBQUM7UUFDVixZQUFZLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdkIsQ0FBQztBQUNGLENBQUM7QUFFRCxLQUFLLFVBQVUsS0FBSyxDQUFDLFdBQW1CLEVBQUUsVUFBa0I7SUFDM0QsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtRQUN0QyxLQUFLLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsRUFBRSxDQUFDLEdBQUcsRUFBRSxPQUFPLEVBQUUsRUFBRTtZQUMvRCxJQUFJLEdBQUcsRUFBRSxDQUFDO2dCQUNULE9BQU8sTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3BCLENBQUM7WUFFRCxPQUFRLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsRUFBRTtnQkFDNUIsSUFBSSxLQUFLLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO29CQUNoQyxPQUFRLENBQUMsU0FBUyxFQUFFLENBQUM7Z0JBQ3RCLENBQUM7cUJBQU0sQ0FBQztvQkFDUCxPQUFRLENBQUMsY0FBYyxDQUFDLEtBQUssRUFBRSxDQUFDLEdBQUcsRUFBRSxPQUFPLEVBQUUsRUFBRTt3QkFDL0MsSUFBSSxHQUFHLEVBQUUsQ0FBQzs0QkFDVCxPQUFPLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQzt3QkFDcEIsQ0FBQzt3QkFFRCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUM7d0JBQ3ZELEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO3dCQUUxRCxNQUFNLE9BQU8sR0FBRyxFQUFFLENBQUMsaUJBQWlCLENBQUMsUUFBUSxDQUFDLENBQUM7d0JBQy9DLE9BQU8sQ0FBQyxFQUFFLENBQUMsUUFBUSxFQUFFLEdBQUcsRUFBRTs0QkFDekIsT0FBUSxDQUFDLEtBQUssRUFBRSxDQUFDOzRCQUNqQixPQUFPLENBQUMsUUFBUSxDQUFDLENBQUM7d0JBQ25CLENBQUMsQ0FBQyxDQUFDO3dCQUNILE9BQU8sRUFBRSxFQUFFLENBQUMsT0FBTyxFQUFFLEdBQUcsQ0FBQyxFQUFFLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7d0JBQ3pDLE9BQVEsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7b0JBQ3hCLENBQUMsQ0FBQyxDQUFDO2dCQUNKLENBQUM7WUFDRixDQUFDLENBQUMsQ0FBQztZQUVILE9BQVEsQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUN0QixDQUFDLENBQUMsQ0FBQztJQUNKLENBQUMsQ0FBQyxDQUFDO0FBQ0osQ0FBQztBQWNELHdGQUF3RjtBQUN4RixTQUFTLFdBQVcsQ0FBQyxPQUFlLEVBQUUsRUFBVSxFQUFFLElBQVksRUFBRSxJQUFZO0lBQzNFLFFBQVEsRUFBRSxFQUFFLENBQUM7UUFDWixLQUFLLE9BQU87WUFDWCxRQUFRLE9BQU8sRUFBRSxDQUFDO2dCQUNqQixLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUM7b0JBQ2YsUUFBUSxJQUFJLEVBQUUsQ0FBQzt3QkFDZCxLQUFLLFNBQVM7NEJBQ2IsT0FBTyxTQUFTLElBQUksVUFBVSxDQUFDO3dCQUNoQyxLQUFLLE9BQU87NEJBQ1gsT0FBTyxTQUFTLElBQUksRUFBRSxDQUFDO3dCQUN4QixLQUFLLFlBQVk7NEJBQ2hCLE9BQU8sU0FBUyxJQUFJLE9BQU8sQ0FBQzt3QkFDN0I7NEJBQ0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztvQkFDcEUsQ0FBQztnQkFDRixDQUFDO2dCQUNELEtBQUssUUFBUTtvQkFDWixJQUFJLElBQUksS0FBSyxPQUFPLEVBQUUsQ0FBQzt3QkFDdEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztvQkFDbkUsQ0FBQztvQkFDRCxPQUFPLGdCQUFnQixJQUFJLEVBQUUsQ0FBQztnQkFDL0IsS0FBSyxLQUFLO29CQUNULElBQUksSUFBSSxLQUFLLE9BQU8sRUFBRSxDQUFDO3dCQUN0QixNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLElBQUksRUFBRSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO29CQUNuRSxDQUFDO29CQUNELE9BQU8sZ0JBQWdCLElBQUksTUFBTSxDQUFDO2dCQUNuQyxLQUFLLEtBQUs7b0JBQ1QsT0FBTyxhQUFhLElBQUksRUFBRSxDQUFDO2dCQUM1QjtvQkFDQyxNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLElBQUksRUFBRSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQ3BFLENBQUM7UUFDRixLQUFLLFFBQVE7WUFDWixRQUFRLE9BQU8sRUFBRSxDQUFDO2dCQUNqQixLQUFLLFFBQVE7b0JBQ1osT0FBTyxpQkFBaUIsSUFBSSxFQUFFLENBQUM7Z0JBQ2hDLEtBQUssS0FBSztvQkFDVCxPQUFPLGlCQUFpQixJQUFJLE1BQU0sQ0FBQztnQkFDcEMsS0FBSyxLQUFLO29CQUNULE9BQU8sY0FBYyxJQUFJLEVBQUUsQ0FBQztnQkFDN0I7b0JBQ0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztZQUNwRSxDQUFDO1FBQ0YsS0FBSyxPQUFPO1lBQ1gsUUFBUSxJQUFJLEVBQUUsQ0FBQztnQkFDZCxLQUFLLE1BQU07b0JBQ1YsT0FBTyxjQUFjLElBQUksRUFBRSxDQUFDO2dCQUM3QixLQUFLLGtCQUFrQjtvQkFDdEIsUUFBUSxPQUFPLEVBQUUsQ0FBQzt3QkFDakIsS0FBSyxRQUFROzRCQUNaLE9BQU8sU0FBUyxJQUFJLEVBQUUsQ0FBQzt3QkFDeEIsS0FBSyxRQUFROzRCQUNaLE9BQU8sZ0JBQWdCLElBQUksRUFBRSxDQUFDO3dCQUMvQixLQUFLLEtBQUs7NEJBQ1QsT0FBTyxJQUFJLEtBQUssWUFBWSxDQUFDLENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsZ0JBQWdCLElBQUksTUFBTSxDQUFDO3dCQUM5RTs0QkFDQyxNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLElBQUksRUFBRSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO29CQUNwRSxDQUFDO2dCQUNGLEtBQUssYUFBYTtvQkFDakIsT0FBTyxhQUFhLElBQUksRUFBRSxDQUFDO2dCQUM1QixLQUFLLGFBQWE7b0JBQ2pCLE9BQU8sYUFBYSxJQUFJLEVBQUUsQ0FBQztnQkFDNUIsS0FBSyxLQUFLO29CQUNULE9BQU8sYUFBYSxJQUFJLEVBQUUsQ0FBQztnQkFDNUI7b0JBQ0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztZQUNwRSxDQUFDO1FBQ0YsS0FBSyxRQUFRO1lBQ1osUUFBUSxPQUFPLEVBQUUsQ0FBQztnQkFDakIsS0FBSyxRQUFRO29CQUNaLElBQUksSUFBSSxLQUFLLEtBQUssRUFBRSxDQUFDO3dCQUNwQixPQUFPLFFBQVEsQ0FBQztvQkFDakIsQ0FBQztvQkFDRCxPQUFPLFVBQVUsSUFBSSxFQUFFLENBQUM7Z0JBQ3pCLEtBQUssUUFBUTtvQkFDWixJQUFJLElBQUksS0FBSyxLQUFLLEVBQUUsQ0FBQzt3QkFDcEIsT0FBTyxlQUFlLENBQUM7b0JBQ3hCLENBQUM7b0JBQ0QsT0FBTyxpQkFBaUIsSUFBSSxFQUFFLENBQUM7Z0JBQ2hDLEtBQUssS0FBSztvQkFDVCxJQUFJLElBQUksS0FBSyxLQUFLLEVBQUUsQ0FBQzt3QkFDcEIsT0FBTyxtQkFBbUIsQ0FBQztvQkFDNUIsQ0FBQztvQkFDRCxPQUFPLGlCQUFpQixJQUFJLE1BQU0sQ0FBQztnQkFDcEMsS0FBSyxLQUFLO29CQUNULE9BQU8sY0FBYyxJQUFJLEVBQUUsQ0FBQztnQkFDN0I7b0JBQ0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztZQUNwRSxDQUFDO1FBQ0Y7WUFDQyxNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLElBQUksRUFBRSxJQUFJLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3BFLENBQUM7QUFDRixDQUFDO0FBRUQsOEVBQThFO0FBQzlFLFNBQVMsV0FBVyxDQUFDLElBQVk7SUFDaEMsUUFBUSxJQUFJLEVBQUUsQ0FBQztRQUNkLEtBQUssWUFBWTtZQUNoQixPQUFPLE9BQU8sQ0FBQztRQUNoQixLQUFLLGFBQWEsQ0FBQztRQUNuQixLQUFLLGFBQWE7WUFDakIsT0FBTyxTQUFTLENBQUM7UUFDbEI7WUFDQyxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7QUFDRixDQUFDO0FBRUQsS0FBSyxVQUFVLGlCQUFpQixDQUFDLEdBQTZCLEVBQUUsT0FBZSxFQUFFLE1BQWMsRUFBRSxRQUFnQjtJQUNoSCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3pDLE1BQU0sUUFBUSxHQUFHLE1BQU0sR0FBRyxHQUFHLEdBQUcsUUFBUSxDQUFDO0lBRXpDLE1BQU0sVUFBVSxHQUFHLElBQUksaUNBQXNCLENBQUMsQ0FBQyxDQUFDLGlCQUFpQixDQUFDLEVBQUUsQ0FBQyxDQUFDLGlCQUFpQixDQUFDLEVBQUUsQ0FBQyxDQUFDLHFCQUFxQixDQUFDLENBQUMsQ0FBQztJQUNwSCxNQUFNLGlCQUFpQixHQUFHLElBQUksZ0NBQWlCLENBQUMsc0NBQXNDLEVBQUUsVUFBVSxFQUFFLEVBQUUsWUFBWSxFQUFFLEVBQUUsZUFBZSxFQUFFLHFDQUFzQixDQUFDLEtBQUssRUFBRSxjQUFjLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDeE0sTUFBTSxlQUFlLEdBQUcsaUJBQWlCLENBQUMsa0JBQWtCLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdEUsTUFBTSxVQUFVLEdBQUcsZUFBZSxDQUFDLGtCQUFrQixDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBRWhFLE1BQU0sV0FBVyxHQUFtQztRQUNuRCxlQUFlLEVBQUU7WUFDaEIsZUFBZSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDO1lBQ3RDLHNCQUFzQixFQUFFLHlCQUF5QixRQUFRLEdBQUc7WUFDNUQsZ0JBQWdCLEVBQUUsMEJBQTBCO1NBQzVDO0tBQ0QsQ0FBQztJQUVGLE1BQU0sY0FBYyxHQUFvQixFQUFFLENBQUM7SUFFM0MsY0FBYyxDQUFDLElBQUksQ0FBQyxDQUFDLEtBQUssSUFBbUIsRUFBRTtRQUM5QyxHQUFHLENBQUMsK0JBQStCLENBQUMsQ0FBQztRQUVyQyxJQUFJLE1BQU0sVUFBVSxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUM7WUFDL0IsR0FBRyxDQUFDLFFBQVEsT0FBTyxLQUFLLFFBQVEsd0NBQXdDLENBQUMsQ0FBQztZQUMxRSxNQUFNLElBQUksS0FBSyxDQUFDLFFBQVEsT0FBTyxLQUFLLFFBQVEsd0NBQXdDLENBQUMsQ0FBQztRQUN2RixDQUFDO2FBQU0sQ0FBQztZQUNQLEdBQUcsQ0FBQyxxQ0FBcUMsQ0FBQyxDQUFDO1lBQzNDLE1BQU0sVUFBVSxDQUFDLFVBQVUsQ0FBQyxRQUFRLEVBQUUsV0FBVyxDQUFDLENBQUM7WUFDbkQsR0FBRyxDQUFDLDhDQUE4QyxDQUFDLENBQUM7UUFDckQsQ0FBQztJQUNGLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUVOLE1BQU0sc0JBQXNCLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsNEJBQTRCLENBQUMsQ0FBQyxDQUFDO0lBRTdFLElBQUksc0JBQXNCLEVBQUUsQ0FBQztRQUM1QixNQUFNLGtCQUFrQixHQUFHLElBQUksaUNBQXNCLENBQUMsQ0FBQyxDQUFDLDBCQUEwQixDQUFDLEVBQUUsQ0FBQyxDQUFDLDBCQUEwQixDQUFDLEVBQUUsQ0FBQyxDQUFDLDhCQUE4QixDQUFDLENBQUMsQ0FBQztRQUN2SixNQUFNLHlCQUF5QixHQUFHLElBQUksZ0NBQWlCLENBQUMsMkNBQTJDLEVBQUUsa0JBQWtCLEVBQUUsRUFBRSxZQUFZLEVBQUUsRUFBRSxlQUFlLEVBQUUscUNBQXNCLENBQUMsS0FBSyxFQUFFLGNBQWMsRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQztRQUM3TixNQUFNLHVCQUF1QixHQUFHLHlCQUF5QixDQUFDLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ3RGLE1BQU0sa0JBQWtCLEdBQUcsdUJBQXVCLENBQUMsa0JBQWtCLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFaEYsY0FBYyxDQUFDLElBQUksQ0FBQyxDQUFDLEtBQUssSUFBbUIsRUFBRTtZQUM5QyxHQUFHLENBQUMsd0NBQXdDLENBQUMsQ0FBQztZQUU5QyxJQUFJLE1BQU0sa0JBQWtCLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQztnQkFDdkMsR0FBRyxDQUFDLGlCQUFpQixPQUFPLEtBQUssUUFBUSx3Q0FBd0MsQ0FBQyxDQUFDO2dCQUNuRixNQUFNLElBQUksS0FBSyxDQUFDLGlCQUFpQixPQUFPLEtBQUssUUFBUSx3Q0FBd0MsQ0FBQyxDQUFDO1lBQ2hHLENBQUM7aUJBQU0sQ0FBQztnQkFDUCxHQUFHLENBQUMsOENBQThDLENBQUMsQ0FBQztnQkFDcEQsTUFBTSxrQkFBa0IsQ0FBQyxVQUFVLENBQUMsUUFBUSxFQUFFLFdBQVcsQ0FBQyxDQUFDO2dCQUMzRCxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztZQUM5RCxDQUFDO1FBQ0YsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVELE1BQU0sY0FBYyxHQUFHLE1BQU0sT0FBTyxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUMsQ0FBQztJQUNoRSxNQUFNLHNCQUFzQixHQUFHLGNBQWMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxNQUFNLENBQUMsTUFBTSxLQUFLLFVBQVUsQ0FBNEIsQ0FBQztJQUV4SCxJQUFJLHNCQUFzQixDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN6QyxHQUFHLENBQUMsa0NBQWtDLENBQUMsQ0FBQztJQUN6QyxDQUFDO1NBQU0sSUFBSSxzQkFBc0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFFLENBQUM7UUFDbkYsR0FBRyxDQUFDLG1DQUFtQyxDQUFDLENBQUM7SUFDMUMsQ0FBQztTQUFNLENBQUM7UUFDUCw0Q0FBNEM7UUFDNUMsTUFBTSxzQkFBc0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUM7SUFDekMsQ0FBQztJQUVELE1BQU0sUUFBUSxHQUFHLEdBQUcsQ0FBQyxDQUFDLGVBQWUsQ0FBQyxJQUFJLE9BQU8sSUFBSSxRQUFRLEVBQUUsQ0FBQztJQUNoRSxNQUFNLFFBQVEsR0FBRyxJQUFJLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQyxRQUFRLENBQUM7SUFDNUMsTUFBTSxXQUFXLEdBQUcsR0FBRyxDQUFDLENBQUMsa0JBQWtCLENBQUMsR0FBRyxRQUFRLEVBQUUsQ0FBQztJQUUxRCxPQUFPLEVBQUUsUUFBUSxFQUFFLFdBQVcsRUFBRSxDQUFDO0FBQ2xDLENBQUM7QUFFRCxLQUFLLFVBQVUsZUFBZSxDQUFDLFFBQWtCLEVBQUUsZ0JBQXdCO0lBQzFFLE1BQU0sR0FBRyxHQUFHLENBQUMsR0FBRyxJQUFXLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxRQUFRLENBQUMsSUFBSSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQztJQUMzRSxNQUFNLEtBQUssR0FBRyxrRkFBa0YsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBRXJILElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNaLE1BQU0sSUFBSSxLQUFLLENBQUMsMEJBQTBCLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQzVELENBQUM7SUFFRCx3Q0FBd0M7SUFDeEMsTUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLENBQUM7SUFDcEMsTUFBTSxNQUFNLEdBQUcsQ0FBQyxDQUFDLHFCQUFxQixDQUFDLENBQUM7SUFDeEMsTUFBTSxFQUFFLE9BQU8sRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLGVBQWUsRUFBRSxHQUFHLEtBQUssQ0FBQyxNQUFPLENBQUM7SUFDN0QsTUFBTSxRQUFRLEdBQUcsV0FBVyxDQUFDLE9BQU8sRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLGVBQWUsQ0FBQyxDQUFDO0lBQ2pFLE1BQU0sSUFBSSxHQUFHLFdBQVcsQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUMxQyxNQUFNLElBQUksR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLGdCQUFnQixDQUFDLENBQUMsSUFBSSxDQUFDO0lBQ2hELE1BQU0sTUFBTSxHQUFHLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3JELE1BQU0sQ0FBQyxRQUFRLEVBQUUsVUFBVSxDQUFDLEdBQUcsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsRUFBRSxVQUFVLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUU3RyxNQUFNLENBQUMsRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLEVBQUUsT0FBTyxDQUFDLEdBQUcsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDO1FBQzlELGlCQUFpQixDQUFDLEdBQUcsRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLGdCQUFnQixDQUFDO1FBQ3pELG1CQUFtQixDQUNsQixHQUFHLEVBQ0gsQ0FBQyxDQUFDLG1CQUFtQixDQUFDLEVBQ3RCLENBQUMsQ0FBQyxtQkFBbUIsQ0FBQyxFQUN0QixDQUFDLENBQUMsZ0NBQWdDLENBQUMsRUFDbkMsQ0FBQyxDQUFDLDJDQUEyQyxDQUFDLEVBQzlDLENBQUMsQ0FBQyxxQkFBcUIsQ0FBQyxFQUN4QixDQUFDLENBQUMsd0JBQXdCLENBQUMsRUFDM0IsQ0FBQyxDQUFDLHdCQUF3QixDQUFDLEVBQzNCLE1BQU0sRUFDTixPQUFPLEVBQ1AsZ0JBQWdCLENBQ2hCO0tBQ0QsQ0FBQyxDQUFDO0lBRUgsTUFBTSxLQUFLLEdBQVUsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxJQUFJLEVBQUUsa0JBQWtCLEVBQUUsSUFBSSxFQUFFLENBQUM7SUFDekksR0FBRyxDQUFDLG1CQUFtQixFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUVoRCxNQUFNLElBQUEsYUFBSyxFQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRTtRQUM3QixHQUFHLENBQUMsdUNBQXVDLE9BQU8sTUFBTSxDQUFDLENBQUM7UUFDMUQsTUFBTSxjQUFjLEdBQUcsSUFBSSxpQ0FBc0IsQ0FBQyxDQUFDLENBQUMsaUJBQWlCLENBQUMsRUFBRSxDQUFDLENBQUMsaUJBQWlCLENBQUMsRUFBRSxDQUFDLENBQUMscUJBQXFCLENBQUMsQ0FBQyxDQUFDO1FBQ3hILE1BQU0sTUFBTSxHQUFHLElBQUkscUJBQVksQ0FBQyxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUMsMkJBQTJCLENBQUMsRUFBRSxjQUFjLEVBQUUsQ0FBQyxDQUFDO1FBQzlGLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUNyRSxNQUFNLE9BQU8sQ0FBQyxlQUFlLENBQUMsYUFBYSxDQUFDLENBQUMsT0FBTyxDQUFDLEVBQUUsRUFBRSxDQUFDLE1BQU0sRUFBRSxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUNqRixDQUFDLENBQUMsQ0FBQztJQUVILEdBQUcsQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO0FBQ25DLENBQUM7QUFFRCxxRkFBcUY7QUFDckYsK0VBQStFO0FBQy9FLHdGQUF3RjtBQUN4RiwyRUFBMkU7QUFDM0Usa0ZBQWtGO0FBQ2xGLHFEQUFxRDtBQUNyRCxLQUFLLFVBQVUsSUFBSTtJQUNsQixJQUFJLENBQUMsa0NBQVksRUFBRSxDQUFDO1FBQ25CLE1BQU0sRUFBRSxRQUFRLEVBQUUsZ0JBQWdCLEVBQUUsR0FBRyxnQ0FBVSxDQUFDO1FBQ2xELE1BQU0sZUFBZSxDQUFDLFFBQVEsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO1FBQ2xELE9BQU87SUFDUixDQUFDO0lBRUQsTUFBTSxJQUFJLEdBQUcsSUFBSSxLQUFLLEVBQUUsQ0FBQztJQUN6QixNQUFNLFVBQVUsR0FBRyxJQUFJLEdBQUcsRUFBVSxDQUFDO0lBRXJDLEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUM7UUFDekIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLElBQUksRUFBRSxDQUFDLENBQUM7SUFDL0IsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLElBQUksR0FBRyxDQUFTLENBQUMsU0FBUyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUM7SUFDMUQsSUFBSSxDQUFDLENBQUMsNEJBQTRCLENBQUMsS0FBSyxNQUFNLEVBQUUsQ0FBQztRQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUM7SUFBQyxDQUFDO0lBQzFFLElBQUksQ0FBQyxDQUFDLDBCQUEwQixDQUFDLEtBQUssTUFBTSxFQUFFLENBQUM7UUFBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQUMsQ0FBQztJQUN0RSxJQUFJLENBQUMsQ0FBQywyQkFBMkIsQ0FBQyxLQUFLLE1BQU0sRUFBRSxDQUFDO1FBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUFDLENBQUM7SUFDeEUsSUFBSSxDQUFDLENBQUMsMEJBQTBCLENBQUMsS0FBSyxNQUFNLEVBQUUsQ0FBQztRQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7SUFBQyxDQUFDO0lBQ3RFLElBQUksQ0FBQyxDQUFDLHdCQUF3QixDQUFDLEtBQUssTUFBTSxFQUFFLENBQUM7UUFBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQUMsQ0FBQztJQUVsRSxNQUFNLFVBQVUsR0FBaUQsRUFBRSxDQUFDO0lBRXBFLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFDYixNQUFNLENBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQyxHQUFHLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUEsYUFBSyxFQUFDLEdBQUcsRUFBRSxDQUFDLG1CQUFtQixFQUFFLENBQUMsRUFBRSxJQUFBLGFBQUssRUFBQyxHQUFHLEVBQUUsQ0FBQyxvQkFBb0IsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzNILE1BQU0sZUFBZSxHQUFHLElBQUksR0FBRyxDQUFTLFFBQVEsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksS0FBSyxPQUFPLElBQUksQ0FBQyxDQUFDLEtBQUssS0FBSyxXQUFXLElBQUksTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUM1SixNQUFNLGdCQUFnQixHQUFHLENBQUMsR0FBRyxNQUFNLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMxRSxNQUFNLG1CQUFtQixHQUFHLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBRTFFLElBQUksZ0JBQWdCLENBQUMsTUFBTSxLQUFLLENBQUMsSUFBSSxTQUFTLENBQUMsTUFBTSxLQUFLLElBQUksQ0FBQyxJQUFJLEdBQUcsVUFBVSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ3ZGLE1BQU07UUFDUCxDQUFDO2FBQU0sSUFBSSxnQkFBZ0IsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDeEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxxQkFBcUIsRUFBRSxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUNqRSxDQUFDO2FBQU0sSUFBSSxtQkFBbUIsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDM0MsT0FBTyxDQUFDLEdBQUcsQ0FBQyx3QkFBd0IsRUFBRSxtQkFBbUIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDeEYsQ0FBQzthQUFNLENBQUM7WUFDUCxPQUFPLENBQUMsR0FBRyxDQUFDLDBCQUEwQixTQUFTLENBQUMsTUFBTSxLQUFLLElBQUksQ0FBQyxJQUFJLFVBQVUsVUFBVSxDQUFDLElBQUksaUJBQWlCLENBQUMsQ0FBQztRQUNqSCxDQUFDO1FBRUQsS0FBSyxNQUFNLFFBQVEsSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNsQyxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLFVBQVUsQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7Z0JBQzlELFNBQVM7WUFDVixDQUFDO1lBRUQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLFFBQVEsQ0FBQyxJQUFJLHNCQUFzQixDQUFDLENBQUM7WUFFckQsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMscUJBQXFCLENBQUMsRUFBRSxHQUFHLFFBQVEsQ0FBQyxJQUFJLE1BQU0sQ0FBQyxDQUFDO1lBRXBGLE1BQU0sSUFBQSxhQUFLLEVBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSxFQUFFO2dCQUM3QixNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7Z0JBQ3pCLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxRQUFRLENBQUMsSUFBSSx3QkFBd0IsT0FBTyxNQUFNLENBQUMsQ0FBQztnQkFDcEUsTUFBTSxnQkFBZ0IsQ0FBQyxRQUFRLEVBQUUsZUFBZSxDQUFDLENBQUM7Z0JBQ2xELE1BQU0sV0FBVyxHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUMsZUFBZSxDQUFDLENBQUMsSUFBSSxDQUFDO2dCQUN0RCxNQUFNLGlCQUFpQixHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQUssQ0FBQyxHQUFHLElBQUksQ0FBQztnQkFDdEQsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQyxHQUFHLGlCQUFpQixDQUFDLENBQUM7Z0JBQzlFLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxRQUFRLENBQUMsSUFBSSxtQ0FBbUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxZQUFZLGdCQUFnQixTQUFTLENBQUMsQ0FBQztZQUNySSxDQUFDLENBQUMsQ0FBQztZQUVILE1BQU0sZ0JBQWdCLEdBQUcsTUFBTSxLQUFLLENBQUMsZUFBZSxFQUFFLENBQUMsQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDLENBQUM7WUFDaEYsTUFBTSxZQUFZLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLElBQUksQ0FBQztZQUV4RCxJQUFJLFlBQVksS0FBSyxNQUFNLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQztnQkFDeEUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLFFBQVEsQ0FBQyxJQUFJLHFDQUFxQyxRQUFRLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxZQUFZLFlBQVksWUFBWSxHQUFHLENBQUMsQ0FBQztnQkFDeEksTUFBTSxJQUFJLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO1lBQzVDLENBQUM7WUFFRCxVQUFVLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUM5QixNQUFNLE9BQU8sR0FBRyxJQUFJLE9BQU8sQ0FBTyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtnQkFDckQsTUFBTSxNQUFNLEdBQUcsSUFBSSw0QkFBTSxDQUFDLFVBQVUsRUFBRSxFQUFFLFVBQVUsRUFBRSxFQUFFLFFBQVEsRUFBRSxnQkFBZ0IsRUFBRSxFQUFFLENBQUMsQ0FBQztnQkFDdEYsTUFBTSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUM7Z0JBQzNCLE1BQU0sQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxFQUFFO29CQUN4QixJQUFJLElBQUksS0FBSyxDQUFDLEVBQUUsQ0FBQzt3QkFDaEIsT0FBTyxFQUFFLENBQUM7b0JBQ1gsQ0FBQzt5QkFBTSxDQUFDO3dCQUNQLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyx1Q0FBdUMsQ0FBQyxDQUFDLENBQUM7b0JBQzVELENBQUM7Z0JBQ0YsQ0FBQyxDQUFDLENBQUM7WUFDSixDQUFDLENBQUMsQ0FBQztZQUVILE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFO2dCQUNuQyxVQUFVLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDakMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ3hCLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxRQUFRLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQztZQUN6QyxDQUFDLENBQUMsQ0FBQztZQUVILFVBQVUsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLEVBQUUsUUFBUSxDQUFDLElBQUksRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDO1FBQ3JELENBQUM7UUFFRCxNQUFNLElBQUksT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLENBQUMsRUFBRSxLQUFNLENBQUMsQ0FBQyxDQUFDO0lBQy9DLENBQUM7SUFFRCxPQUFPLENBQUMsR0FBRyxDQUFDLGFBQWEsSUFBSSxDQUFDLElBQUksR0FBRyxVQUFVLENBQUMsSUFBSSwyQkFBMkIsVUFBVSxDQUFDLElBQUksb0NBQW9DLENBQUMsQ0FBQztJQUVwSSxNQUFNLG1CQUFtQixHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBRTNFLElBQUksbUJBQW1CLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3BDLE9BQU8sQ0FBQyxHQUFHLENBQUMsd0JBQXdCLEVBQUUsbUJBQW1CLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQ3hGLENBQUM7SUFFRCxNQUFNLE9BQU8sR0FBRyxNQUFNLE9BQU8sQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO0lBRTNFLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxVQUFVLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDNUMsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBRTFCLElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxVQUFVLEVBQUUsQ0FBQztZQUNsQyxPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksR0FBRyxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUN6RCxDQUFDO0lBQ0YsQ0FBQztJQUVELElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLEtBQUssVUFBVSxDQUFDLEVBQUUsQ0FBQztRQUNoRCxNQUFNLElBQUksS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUM7SUFDckQsQ0FBQztJQUVELE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxJQUFJLENBQUMsSUFBSSx1QkFBdUIsQ0FBQyxDQUFDO0FBQ3RELENBQUM7QUFFRCxJQUFJLE9BQU8sQ0FBQyxJQUFJLEtBQUssTUFBTSxFQUFFLENBQUM7SUFDN0IsSUFBSSxFQUFFLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRTtRQUNoQixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2pCLENBQUMsRUFBRSxHQUFHLENBQUMsRUFBRTtRQUNSLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDbkIsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNqQixDQUFDLENBQUMsQ0FBQztBQUNKLENBQUMifQ== \ No newline at end of file diff --git a/build/azure-pipelines/common/publish.ts b/build/azure-pipelines/common/publish.ts index 57fb617d710..55f8302ec5b 100644 --- a/build/azure-pipelines/common/publish.ts +++ b/build/azure-pipelines/common/publish.ts @@ -11,7 +11,7 @@ import { pipeline } from 'node:stream/promises'; import * as yauzl from 'yauzl'; import * as crypto from 'crypto'; import { retry } from './retry'; -import { BlobServiceClient, BlockBlobParallelUploadOptions, StoragePipelineOptions, StorageRetryPolicyType } from '@azure/storage-blob'; +import { BlobServiceClient, BlockBlobParallelUploadOptions, StorageRetryPolicyType } from '@azure/storage-blob'; import * as mime from 'mime'; import { CosmosClient } from '@azure/cosmos'; import { ClientSecretCredential } from '@azure/identity'; @@ -632,10 +632,8 @@ async function uploadAssetLegacy(log: (...args: any[]) => void, quality: string, const fileName = path.basename(filePath); const blobName = commit + '/' + fileName; - const storagePipelineOptions: StoragePipelineOptions = { retryOptions: { retryPolicyType: StorageRetryPolicyType.EXPONENTIAL, maxTries: 6, tryTimeoutInMs: 10 * 60 * 1000 } }; - const credential = new ClientSecretCredential(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_CLIENT_SECRET')); - const blobServiceClient = new BlobServiceClient(`https://vscode.blob.core.windows.net`, credential, storagePipelineOptions); + const blobServiceClient = new BlobServiceClient(`https://vscode.blob.core.windows.net`, credential, { retryOptions: { retryPolicyType: StorageRetryPolicyType.FIXED, tryTimeoutInMs: 2 * 60 * 1000 } }); const containerClient = blobServiceClient.getContainerClient(quality); const blobClient = containerClient.getBlockBlobClient(blobName); @@ -652,14 +650,13 @@ async function uploadAssetLegacy(log: (...args: any[]) => void, quality: string, uploadPromises.push((async (): Promise => { log(`Checking for blob in Azure...`); - if (await retry(() => blobClient.exists())) { + if (await blobClient.exists()) { + log(`Blob ${quality}, ${blobName} already exists, not publishing again.`); throw new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`); } else { - await retry(async attempt => { - log(`Uploading blobs to Azure storage (attempt ${attempt})...`); - await blobClient.uploadFile(filePath, blobOptions); - log('Blob successfully uploaded to Azure storage.'); - }); + log(`Uploading blobs to Azure storage...`); + await blobClient.uploadFile(filePath, blobOptions); + log('Blob successfully uploaded to Azure storage.'); } })()); @@ -667,21 +664,20 @@ async function uploadAssetLegacy(log: (...args: any[]) => void, quality: string, if (shouldUploadToMooncake) { const mooncakeCredential = new ClientSecretCredential(e('AZURE_MOONCAKE_TENANT_ID'), e('AZURE_MOONCAKE_CLIENT_ID'), e('AZURE_MOONCAKE_CLIENT_SECRET')); - const mooncakeBlobServiceClient = new BlobServiceClient(`https://vscode.blob.core.chinacloudapi.cn`, mooncakeCredential, storagePipelineOptions); + const mooncakeBlobServiceClient = new BlobServiceClient(`https://vscode.blob.core.chinacloudapi.cn`, mooncakeCredential, { retryOptions: { retryPolicyType: StorageRetryPolicyType.FIXED, tryTimeoutInMs: 5 * 60 * 1000 } }); const mooncakeContainerClient = mooncakeBlobServiceClient.getContainerClient(quality); const mooncakeBlobClient = mooncakeContainerClient.getBlockBlobClient(blobName); uploadPromises.push((async (): Promise => { log(`Checking for blob in Mooncake Azure...`); - if (await retry(() => mooncakeBlobClient.exists())) { + if (await mooncakeBlobClient.exists()) { + log(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); throw new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); } else { - await retry(async attempt => { - log(`Uploading blobs to Mooncake Azure storage (attempt ${attempt})...`); - await mooncakeBlobClient.uploadFile(filePath, blobOptions); - log('Blob successfully uploaded to Mooncake Azure storage.'); - }); + log(`Uploading blobs to Mooncake Azure storage...`); + await mooncakeBlobClient.uploadFile(filePath, blobOptions); + log('Blob successfully uploaded to Mooncake Azure storage.'); } })()); } @@ -692,14 +688,13 @@ async function uploadAssetLegacy(log: (...args: any[]) => void, quality: string, if (rejectedPromiseResults.length === 0) { log('All blobs successfully uploaded.'); } else if (rejectedPromiseResults[0]?.reason?.message?.includes('already exists')) { - log(rejectedPromiseResults[0].reason.message); log('Some blobs successfully uploaded.'); } else { // eslint-disable-next-line no-throw-literal throw rejectedPromiseResults[0]?.reason; } - const assetUrl = `${e('AZURE_CDN_URL')} / ${quality} / ${blobName}`; + const assetUrl = `${e('AZURE_CDN_URL')}/${quality}/${blobName}`; const blobPath = new URL(assetUrl).pathname; const mooncakeUrl = `${e('MOONCAKE_CDN_URL')}${blobPath}`; @@ -805,7 +800,7 @@ async function main() { continue; } - console.log(`${artifact.name} Found new artifact`); + console.log(`[${artifact.name}] Found new artifact`); const artifactZipPath = path.join(e('AGENT_TEMPDIRECTORY'), `${artifact.name}.zip`); @@ -816,7 +811,7 @@ async function main() { const archiveSize = fs.statSync(artifactZipPath).size; const downloadDurationS = (Date.now() - start) / 1000; const downloadSpeedKBS = Math.round((archiveSize / 1024) / downloadDurationS); - console.log(`[${artifact.name}] Successfully downloaded after ${Math.floor(downloadDurationS)} seconds(${downloadSpeedKBS} KB / s).`); + console.log(`[${artifact.name}] Successfully downloaded after ${Math.floor(downloadDurationS)} seconds(${downloadSpeedKBS} KB/s).`); }); const artifactFilePath = await unzip(artifactZipPath, e('AGENT_TEMPDIRECTORY')); From c1d0aafbc3c176666102c697d4d64a6b119610b6 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Sun, 19 Nov 2023 13:42:32 +0100 Subject: [PATCH 54/87] SCM - remove the sync view (#198475) --- .../contrib/scm/browser/scm.contribution.ts | 19 +- .../contrib/scm/browser/scmSyncViewPane.ts | 885 ------------------ src/vs/workbench/contrib/scm/common/scm.ts | 1 - 3 files changed, 1 insertion(+), 904 deletions(-) delete mode 100644 src/vs/workbench/contrib/scm/browser/scmSyncViewPane.ts diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index e53baa92f8f..410c4e7d80a 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -7,7 +7,7 @@ import { localize, localize2 } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { DirtyDiffWorkbenchController } from './dirtydiffDecorator'; -import { VIEWLET_ID, ISCMService, VIEW_PANE_ID, ISCMProvider, ISCMViewService, REPOSITORIES_VIEW_PANE_ID, SYNC_VIEW_PANE_ID } from 'vs/workbench/contrib/scm/common/scm'; +import { VIEWLET_ID, ISCMService, VIEW_PANE_ID, ISCMProvider, ISCMViewService, REPOSITORIES_VIEW_PANE_ID } from 'vs/workbench/contrib/scm/common/scm'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { SCMActiveResourceContextKeyController, SCMStatusController } from './activity'; @@ -33,7 +33,6 @@ import { MANAGE_TRUST_COMMAND_ID, WorkspaceTrustContext } from 'vs/workbench/con import { IQuickDiffService } from 'vs/workbench/contrib/scm/common/quickDiff'; import { QuickDiffService } from 'vs/workbench/contrib/scm/common/quickDiffService'; import { getActiveElement } from 'vs/base/browser/dom'; -import { SCMSyncViewPane } from 'vs/workbench/contrib/scm/browser/scmSyncViewPane'; ModesRegistry.registerLanguage({ id: 'scminput', @@ -111,17 +110,6 @@ viewsRegistry.registerViews([{ containerIcon: sourceControlViewIcon }], viewContainer); -viewsRegistry.registerViews([{ - id: SYNC_VIEW_PANE_ID, - name: localize2('source control sync', "Source Control Sync"), - ctorDescriptor: new SyncDescriptor(SCMSyncViewPane), - canToggleVisibility: true, - canMoveView: true, - weight: 20, - order: -998, - when: ContextKeyExpr.equals('config.scm.experimental.showSyncView', true), -}], viewContainer); - Registry.as(WorkbenchExtensions.Workbench) .registerWorkbenchContribution(SCMActiveResourceContextKeyController, LifecyclePhase.Restored); @@ -319,11 +307,6 @@ Registry.as(ConfigurationExtensions.Configuration).regis ], description: localize('scm.showOutgoingChanges', "Controls whether outgoing changes are shown in the Source Control view."), default: 'never' - }, - 'scm.experimental.showSyncView': { - type: 'boolean', - description: localize('showSyncView', "Controls whether the Source Control Sync view is shown."), - default: false } } }); diff --git a/src/vs/workbench/contrib/scm/browser/scmSyncViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmSyncViewPane.ts deleted file mode 100644 index 4805711e193..00000000000 --- a/src/vs/workbench/contrib/scm/browser/scmSyncViewPane.ts +++ /dev/null @@ -1,885 +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 path from 'vs/base/common/path'; -import { append, $, prepend } from 'vs/base/browser/dom'; -import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; -import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; -import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { IAsyncDataSource, ITreeNode, ITreeRenderer, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; -import { DisposableStore, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import { ThemeIcon } from 'vs/base/common/themables'; -import { URI } from 'vs/base/common/uri'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IOpenEvent, WorkbenchCompressibleAsyncDataTree } from 'vs/platform/list/browser/listService'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { defaultCountBadgeStyles } from 'vs/platform/theme/browser/defaultStyles'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; -import { API_OPEN_DIFF_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; -import { IViewPaneOptions, ViewAction, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; -import { IViewDescriptorService } from 'vs/workbench/common/views'; -import { RepositoryRenderer } from 'vs/workbench/contrib/scm/browser/scmRepositoryRenderer'; -import { ActionButtonRenderer } from 'vs/workbench/contrib/scm/browser/scmViewPane'; -import { getActionViewItemProvider, isSCMActionButton, isSCMRepository, isSCMRepositoryArray } from 'vs/workbench/contrib/scm/browser/util'; -import { ISCMActionButton, ISCMRepository, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent, SYNC_VIEW_PANE_ID } from 'vs/workbench/contrib/scm/common/scm'; -import { comparePaths } from 'vs/base/common/comparers'; -import { localize } from 'vs/nls'; -import { Iterable } from 'vs/base/common/iterator'; -import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { basename, dirname } from 'vs/base/common/resources'; -import { ILabelService } from 'vs/platform/label/common/label'; -import { stripIcons } from 'vs/base/common/iconLabels'; -import { FileKind } from 'vs/platform/files/common/files'; -import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; -import { Codicon } from 'vs/base/common/codicons'; -import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { Event, Emitter } from 'vs/base/common/event'; -import { ITreeCompressionDelegate } from 'vs/base/browser/ui/tree/asyncDataTree'; -import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; -import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; -import { IResourceNode, ResourceTree } from 'vs/base/common/resourceTree'; -import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { SCMHistoryItemChangeTreeElement, SCMHistoryItemGroupTreeElement, SCMHistoryItemTreeElement } from 'vs/workbench/contrib/scm/common/history'; - -type SCMHistoryItemChangeResourceTreeNode = IResourceNode; -type TreeElement = ISCMRepository[] | ISCMRepository | ISCMActionButton | SCMHistoryItemGroupTreeElement | SCMHistoryItemTreeElement | SCMHistoryItemChangeTreeElement | SCMHistoryItemChangeResourceTreeNode; - -function isSCMHistoryItemGroupTreeElement(obj: any): obj is SCMHistoryItemGroupTreeElement { - return (obj as SCMHistoryItemGroupTreeElement).type === 'historyItemGroup'; -} - -function isSCMHistoryItemTreeElement(obj: any): obj is SCMHistoryItemTreeElement { - return (obj as SCMHistoryItemTreeElement).type === 'historyItem'; -} - -function isSCMHistoryItemChangeTreeElement(obj: any): obj is SCMHistoryItemChangeTreeElement { - return (obj as SCMHistoryItemChangeTreeElement).type === 'historyItemChange'; -} - -function toDiffEditorArguments(uri: URI, originalUri: URI, modifiedUri: URI): unknown[] { - const basename = path.basename(uri.fsPath); - const originalQuery = JSON.parse(originalUri.query) as { path: string; ref: string }; - const modifiedQuery = JSON.parse(modifiedUri.query) as { path: string; ref: string }; - - const originalShortRef = originalQuery.ref.substring(0, 8).concat(originalQuery.ref.endsWith('^') ? '^' : ''); - const modifiedShortRef = modifiedQuery.ref.substring(0, 8).concat(modifiedQuery.ref.endsWith('^') ? '^' : ''); - - return [originalUri, modifiedUri, `${basename} (${originalShortRef}) ↔ ${basename} (${modifiedShortRef})`, null]; -} - -function getSCMResourceId(element: TreeElement): string { - if (isSCMRepository(element)) { - const provider = element.provider; - return `repo:${provider.id}`; - } else if (isSCMActionButton(element)) { - const provider = element.repository.provider; - return `actionButton:${provider.id}`; - } else if (isSCMHistoryItemGroupTreeElement(element)) { - const provider = element.repository.provider; - return `historyItemGroup:${provider.id}/${element.id}`; - } else if (isSCMHistoryItemTreeElement(element)) { - const historyItemGroup = element.historyItemGroup; - const provider = historyItemGroup.repository.provider; - return `historyItem:${provider.id}/${historyItemGroup.id}/${element.id}`; - } else if (isSCMHistoryItemChangeTreeElement(element)) { - const historyItem = element.historyItem; - const historyItemGroup = historyItem.historyItemGroup; - const provider = historyItemGroup.repository.provider; - return `historyItemChange:${provider.id}/${historyItemGroup.id}/${historyItem.id}/${element.uri.toString()}`; - } else if (ResourceTree.isResourceNode(element)) { - const historyItem = element.context; - const historyItemGroup = historyItem.historyItemGroup; - const provider = historyItemGroup.repository.provider; - return element.childrenCount === 0 && element.element ? - `historyItemChange:${provider.id}/${historyItemGroup.id}/${historyItem.id}/${element.element.uri.toString()}` : - `folder:${provider.id}/${historyItemGroup.id}/${historyItem.id}/$FOLDER/${element.uri.toString()}`; - } else { - throw new Error('Invalid tree element'); - } -} - -const enum ViewMode { - List = 'list', - Tree = 'tree' -} - -const ContextKeys = { - ViewMode: new RawContextKey('scmSyncViewMode', ViewMode.List), -}; - -class ListDelegate implements IListVirtualDelegate { - - getHeight(element: any): number { - if (isSCMActionButton(element)) { - return ActionButtonRenderer.DEFAULT_HEIGHT + 10; - } else { - return 22; - } - } - - getTemplateId(element: any): string { - if (isSCMRepository(element)) { - return RepositoryRenderer.TEMPLATE_ID; - } else if (isSCMActionButton(element)) { - return ActionButtonRenderer.TEMPLATE_ID; - } else if (isSCMHistoryItemGroupTreeElement(element)) { - return HistoryItemGroupRenderer.TEMPLATE_ID; - } else if (isSCMHistoryItemTreeElement(element)) { - return HistoryItemRenderer.TEMPLATE_ID; - } else if (isSCMHistoryItemChangeTreeElement(element)) { - return HistoryItemChangeRenderer.TEMPLATE_ID; - } else if (ResourceTree.isResourceNode(element)) { - return HistoryItemChangeRenderer.TEMPLATE_ID; - } else { - throw new Error('Invalid tree element'); - } - } -} - -class CompressionDelegate implements ITreeCompressionDelegate { - - isIncompressible(element: TreeElement): boolean { - if (ResourceTree.isResourceNode(element)) { - return element.childrenCount === 0 || !element.parent || !element.parent.parent; - } - - return true; - } - -} - -interface HistoryItemGroupTemplate { - readonly label: IconLabel; - readonly count: CountBadge; - readonly disposables: IDisposable; -} - -class HistoryItemGroupRenderer implements ITreeRenderer { - - static readonly TEMPLATE_ID = 'history-item-group'; - get templateId(): string { return HistoryItemGroupRenderer.TEMPLATE_ID; } - - renderTemplate(container: HTMLElement) { - // hack - (container.parentElement!.parentElement!.querySelector('.monaco-tl-twistie')! as HTMLElement).classList.add('force-twistie'); - - const element = append(container, $('.history-item-group')); - const label = new IconLabel(element, { supportIcons: true }); - const countContainer = append(element, $('.count')); - const count = new CountBadge(countContainer, {}, defaultCountBadgeStyles); - - return { label, count, disposables: new DisposableStore() }; - } - - renderElement(node: ITreeNode, index: number, templateData: HistoryItemGroupTemplate, height: number | undefined): void { - const historyItemGroup = node.element; - templateData.label.setLabel(historyItemGroup.label, historyItemGroup.description); - templateData.count.setCount(historyItemGroup.count ?? 0); - } - - disposeTemplate(templateData: HistoryItemGroupTemplate): void { - templateData.disposables.dispose(); - } -} - -interface HistoryItemTemplate { - readonly iconContainer: HTMLElement; - // readonly avatarImg: HTMLImageElement; - readonly iconLabel: IconLabel; - // readonly timestampContainer: HTMLElement; - // readonly timestamp: HTMLSpanElement; - readonly disposables: IDisposable; -} - -class HistoryItemRenderer implements ITreeRenderer { - - static readonly TEMPLATE_ID = 'history-item'; - get templateId(): string { return HistoryItemRenderer.TEMPLATE_ID; } - - renderTemplate(container: HTMLElement): HistoryItemTemplate { - // hack - (container.parentElement!.parentElement!.querySelector('.monaco-tl-twistie')! as HTMLElement).classList.add('force-twistie'); - - const element = append(container, $('.history-item')); - const iconLabel = new IconLabel(element, { supportIcons: true }); - - const iconContainer = prepend(iconLabel.element, $('.icon-container')); - // const avatarImg = append(iconContainer, $('img.avatar')) as HTMLImageElement; - - // const timestampContainer = append(iconLabel.element, $('.timestamp-container')); - // const timestamp = append(timestampContainer, $('span.timestamp')); - - return { iconContainer, iconLabel, disposables: new DisposableStore() }; - } - - renderElement(node: ITreeNode, index: number, templateData: HistoryItemTemplate, height: number | undefined): void { - const historyItem = node.element; - - templateData.iconContainer.className = 'icon-container'; - if (historyItem.icon && ThemeIcon.isThemeIcon(historyItem.icon)) { - templateData.iconContainer.classList.add(...ThemeIcon.asClassNameArray(historyItem.icon)); - } - - // if (commit.authorAvatar) { - // templateData.avatarImg.src = commit.authorAvatar; - // templateData.avatarImg.style.display = 'block'; - // templateData.iconContainer.classList.remove(...ThemeIcon.asClassNameArray(Codicon.account)); - // } else { - // templateData.avatarImg.style.display = 'none'; - // templateData.iconContainer.classList.add(...ThemeIcon.asClassNameArray(Codicon.account)); - // } - - templateData.iconLabel.setLabel(historyItem.label, historyItem.description); - - // templateData.timestampContainer.classList.toggle('timestamp-duplicate', commit.hideTimestamp === true); - // templateData.timestamp.textContent = fromNow(commit.timestamp); - } - - disposeTemplate(templateData: HistoryItemTemplate): void { - templateData.disposables.dispose(); - } -} - -interface HistoryItemChangeTemplate { - readonly element: HTMLElement; - readonly name: HTMLElement; - readonly fileLabel: IResourceLabel; - readonly decorationIcon: HTMLElement; - readonly disposables: IDisposable; -} - -class HistoryItemChangeRenderer implements ICompressibleTreeRenderer { - - static readonly TEMPLATE_ID = 'historyItemChange'; - get templateId(): string { return HistoryItemChangeRenderer.TEMPLATE_ID; } - - constructor( - private readonly viewMode: () => ViewMode, - private readonly labels: ResourceLabels, - @ILabelService private labelService: ILabelService) { } - - renderTemplate(container: HTMLElement): HistoryItemChangeTemplate { - const element = append(container, $('.change')); - const name = append(element, $('.name')); - const fileLabel = this.labels.create(name, { supportDescriptionHighlights: true, supportHighlights: true }); - const decorationIcon = append(element, $('.decoration-icon')); - - return { element, name, fileLabel, decorationIcon, disposables: new DisposableStore() }; - } - - renderElement(node: ITreeNode, index: number, templateData: HistoryItemChangeTemplate, height: number | undefined): void { - const historyItemChangeOrFolder = node.element; - const uri = ResourceTree.isResourceNode(historyItemChangeOrFolder) ? historyItemChangeOrFolder.element?.uri ?? historyItemChangeOrFolder.uri : historyItemChangeOrFolder.uri; - const fileKind = ResourceTree.isResourceNode(historyItemChangeOrFolder) && historyItemChangeOrFolder.childrenCount > 0 ? FileKind.FOLDER : FileKind.FILE; - - templateData.fileLabel.setFile(uri, { - fileDecorations: { colors: false, badges: true }, - fileKind, - hidePath: this.viewMode() === ViewMode.Tree, - }); - } - - renderCompressedElements(node: ITreeNode, void>, index: number, templateData: HistoryItemChangeTemplate, height: number | undefined): void { - const compressed = node.element as ICompressedTreeNode; - - const folder = compressed.elements[compressed.elements.length - 1]; - const label = compressed.elements.map(e => e.name); - - templateData.fileLabel.setResource({ resource: folder.uri, name: label }, { - fileDecorations: { colors: false, badges: true }, - fileKind: FileKind.FOLDER, - separator: this.labelService.getSeparator(folder.uri.scheme) - }); - } - - disposeTemplate(templateData: HistoryItemChangeTemplate): void { - templateData.disposables.dispose(); - } -} - -class SCMSyncViewPaneAccessibilityProvider implements IListAccessibilityProvider { - - constructor( - @ILabelService private readonly labelService: ILabelService, - @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService - ) { } - - getAriaLabel(element: TreeElement): string { - if (isSCMRepository(element)) { - let folderName = ''; - if (element.provider.rootUri) { - const folder = this.workspaceContextService.getWorkspaceFolder(element.provider.rootUri); - - if (folder?.uri.toString() === element.provider.rootUri.toString()) { - folderName = folder.name; - } else { - folderName = basename(element.provider.rootUri); - } - } - return `${folderName} ${element.provider.label}`; - } else if (isSCMHistoryItemGroupTreeElement(element)) { - return `${stripIcons(element.label).trim()}${element.description ? `, ${element.description}` : ''}`; - } else if (isSCMActionButton(element)) { - return element.button?.command.title ?? ''; - } else if (isSCMHistoryItemTreeElement(element)) { - return `${stripIcons(element.label).trim()}${element.description ? `, ${element.description}` : ''}`; - } else if (isSCMHistoryItemChangeTreeElement(element)) { - const result = [basename(element.uri)]; - const path = this.labelService.getUriLabel(dirname(element.uri), { relative: true, noPrefix: true }); - - if (path) { - result.push(path); - } - - return result.join(', '); - } - - return ''; - } - getWidgetAriaLabel(): string { - return localize('scmSync', 'Source Control Sync'); - } - -} - -class SCMSyncViewPaneTreeIdentityProvider implements IIdentityProvider { - - getId(element: TreeElement): string { - return getSCMResourceId(element); - } - -} - -class SCMSyncViewPaneTreeSorter implements ITreeSorter { - - compare(element: TreeElement, otherElement: TreeElement): number { - // Repository - if (isSCMRepository(element)) { - if (!isSCMRepository(otherElement)) { - throw new Error('Invalid comparison'); - } - - return 0; - } - - // Action button - if (isSCMActionButton(element)) { - return -1; - } else if (isSCMActionButton(otherElement)) { - return 1; - } - - // History item group - if (isSCMHistoryItemGroupTreeElement(element)) { - if (!isSCMHistoryItemGroupTreeElement(otherElement)) { - throw new Error('Invalid comparison'); - } - - return 0; - } - - // History item - if (isSCMHistoryItemTreeElement(element)) { - if (!isSCMHistoryItemTreeElement(otherElement)) { - throw new Error('Invalid comparison'); - } - - return 0; - } - - // History item change - const elementPath = (element as SCMHistoryItemChangeTreeElement).uri.fsPath; - const otherElementPath = (otherElement as SCMHistoryItemChangeTreeElement).uri.fsPath; - - return comparePaths(elementPath, otherElementPath); - } -} - -export class SCMSyncViewPane extends ViewPane { - - private listLabels!: ResourceLabels; - private treeContainer!: HTMLElement; - private _tree!: WorkbenchCompressibleAsyncDataTree; - - private _viewModel!: SCMSyncPaneViewModel; - get viewModel(): SCMSyncPaneViewModel { return this._viewModel; } - - private readonly disposables = new DisposableStore(); - - constructor( - options: IViewPaneOptions, - @ICommandService private commandService: ICommandService, - @IKeybindingService keybindingService: IKeybindingService, - @IContextMenuService contextMenuService: IContextMenuService, - @IInstantiationService instantiationService: IInstantiationService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IContextKeyService contextKeyService: IContextKeyService, - @IConfigurationService configurationService: IConfigurationService, - @IOpenerService openerService: IOpenerService, - @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService - ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); - } - - protected override renderBody(container: HTMLElement): void { - super.renderBody(container); - - this.treeContainer = append(container, $('.scm-view.scm-sync-view')); - - this.listLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility }); - this._register(this.listLabels); - - const updateProviderCountVisibility = () => { - const value = this.configurationService.getValue<'hidden' | 'auto' | 'visible'>('scm.providerCountBadge'); - this.treeContainer.classList.toggle('hide-provider-counts', value === 'hidden'); - this.treeContainer.classList.toggle('auto-provider-counts', value === 'auto'); - }; - this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.providerCountBadge'), this.disposables)(updateProviderCountVisibility)); - updateProviderCountVisibility(); - - this._tree = this.instantiationService.createInstance( - WorkbenchCompressibleAsyncDataTree, - 'SCM Sync View', - this.treeContainer, - new ListDelegate(), - new CompressionDelegate(), - [ - this.instantiationService.createInstance(RepositoryRenderer, getActionViewItemProvider(this.instantiationService)), - this.instantiationService.createInstance(ActionButtonRenderer), - this.instantiationService.createInstance(HistoryItemGroupRenderer), - this.instantiationService.createInstance(HistoryItemRenderer), - this.instantiationService.createInstance(HistoryItemChangeRenderer, () => this.viewModel.mode, this.listLabels), - ], - this.instantiationService.createInstance(SCMSyncDataSource, () => this.viewModel.mode), - { - compressionEnabled: true, - horizontalScrolling: false, - autoExpandSingleChildren: true, - multipleSelectionSupport: false, - collapseByDefault: (e) => !ResourceTree.isResourceNode(e), - accessibilityProvider: this.instantiationService.createInstance(SCMSyncViewPaneAccessibilityProvider), - identityProvider: this.instantiationService.createInstance(SCMSyncViewPaneTreeIdentityProvider), - sorter: this.instantiationService.createInstance(SCMSyncViewPaneTreeSorter), - }) as WorkbenchCompressibleAsyncDataTree; - - this._register(this._tree); - this._register(this._tree.onDidOpen(this.onDidOpen, this)); - - this._viewModel = this.instantiationService.createInstance(SCMSyncPaneViewModel, this._tree); - - this.treeContainer.classList.add('file-icon-themable-tree'); - this.treeContainer.classList.add('show-file-icons'); - - this.updateIndentStyles(this.themeService.getFileIconTheme()); - this._register(this.themeService.onDidFileIconThemeChange(this.updateIndentStyles, this)); - this._register(this._viewModel.onDidChangeMode(this.onDidChangeMode, this)); - } - - protected override layoutBody(height: number, width: number): void { - super.layoutBody(height, width); - this._tree.layout(height, width); - } - - private onDidChangeMode(): void { - this.updateIndentStyles(this.themeService.getFileIconTheme()); - } - - private async onDidOpen(e: IOpenEvent): Promise { - if (!e.element) { - return; - } - - if (isSCMHistoryItemChangeTreeElement(e.element)) { - if (e.element.originalUri && e.element.modifiedUri) { - await this.commandService.executeCommand(API_OPEN_DIFF_EDITOR_COMMAND_ID, ...toDiffEditorArguments(e.element.uri, e.element.originalUri, e.element.modifiedUri), e); - } - } else if (ResourceTree.isResourceNode(e.element) && e.element.childrenCount === 0) { - if (e.element.element?.originalUri && e.element.element?.modifiedUri) { - await this.commandService.executeCommand(API_OPEN_DIFF_EDITOR_COMMAND_ID, ...toDiffEditorArguments(e.element.element.uri, e.element.element.originalUri, e.element.element.modifiedUri), e); - } - } - } - - private updateIndentStyles(theme: any): void { - this.treeContainer.classList.toggle('list-view-mode', this._viewModel.mode === ViewMode.List); - this.treeContainer.classList.toggle('tree-view-mode', this._viewModel.mode === ViewMode.Tree); - this.treeContainer.classList.toggle('align-icons-and-twisties', (this._viewModel.mode === ViewMode.List && theme.hasFileIcons) || (theme.hasFileIcons && !theme.hasFolderIcons)); - } - - override dispose(): void { - this.disposables.dispose(); - super.dispose(); - } -} - -class SCMSyncPaneViewModel { - - private readonly _onDidChangeMode = new Emitter(); - readonly onDidChangeMode = this._onDidChangeMode.event; - - private _mode: ViewMode; - get mode(): ViewMode { return this._mode; } - set mode(mode: ViewMode) { - if (this._mode === mode) { - return; - } - - this._mode = mode; - - this.refresh(); - this.modeContextKey.set(mode); - this._onDidChangeMode.fire(mode); - - this.storageService.store(`scm.syncViewMode`, mode, StorageScope.WORKSPACE, StorageTarget.USER); - } - - private modeContextKey: IContextKey; - private repositories = new Map(); - private historyProviders = new Map(); - - private alwaysShowRepositories = false; - - private readonly disposables = new DisposableStore(); - - constructor( - private readonly tree: WorkbenchCompressibleAsyncDataTree, - @IContextKeyService contextKeyService: IContextKeyService, - @ISCMViewService scmViewService: ISCMViewService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IStorageService private readonly storageService: IStorageService, - ) { - configurationService.onDidChangeConfiguration(this._onDidChangeConfiguration, this, this.disposables); - this._onDidChangeConfiguration(); - - scmViewService.onDidChangeVisibleRepositories(this._onDidChangeVisibleRepositories, this, this.disposables); - this._onDidChangeVisibleRepositories({ added: scmViewService.visibleRepositories, removed: [] }); - - this._mode = this.getViewMode(); - - this.modeContextKey = ContextKeys.ViewMode.bindTo(contextKeyService); - this.modeContextKey.set(this._mode); - } - - private _onDidChangeConfiguration(e?: IConfigurationChangeEvent): void { - if (!e || e.affectsConfiguration('scm.alwaysShowRepositories')) { - this.alwaysShowRepositories = this.configurationService.getValue('scm.alwaysShowRepositories'); - this.refresh(); - } - } - - private _onDidChangeVisibleRepositories({ added, removed }: ISCMViewVisibleRepositoryChangeEvent): void { - for (const repository of added) { - const repositoryDisposable = repository.provider.onDidChangeHistoryProvider(() => this._onDidChangeHistoryProvider(repository)); - this._onDidChangeHistoryProvider(repository); - - this.repositories.set(repository, repositoryDisposable); - } - - for (const repository of removed) { - this.historyProviders.get(repository)?.dispose(); - this.historyProviders.delete(repository); - - this.repositories.get(repository)?.dispose(); - this.repositories.delete(repository); - } - - this.refresh(); - } - - private _onDidChangeHistoryProvider(repository: ISCMRepository): void { - if (repository.provider.historyProvider) { - const historyProviderDisposable = combinedDisposable( - repository.provider.historyProvider.onDidChangeActionButton(() => this.refresh(repository)), - repository.provider.historyProvider.onDidChangeCurrentHistoryItemGroup(() => this.refresh(repository))); - - this.historyProviders.set(repository, historyProviderDisposable); - } else { - this.historyProviders.get(repository)?.dispose(); - this.historyProviders.delete(repository); - } - } - - private getViewMode(): ViewMode { - let mode = this.configurationService.getValue<'tree' | 'list'>('scm.defaultViewMode') === 'list' ? ViewMode.List : ViewMode.Tree; - const storageMode = this.storageService.get(`scm.syncViewMode`, StorageScope.WORKSPACE) as ViewMode; - if (typeof storageMode === 'string') { - mode = storageMode; - } - - return mode; - } - - async refresh(repository?: ISCMRepository): Promise { - if (this.repositories.size === 0) { - return; - } - - if (repository) { - // Particular repository - await this.tree.updateChildren(repository); - } else if (this.repositories.size === 1 && !this.alwaysShowRepositories) { - // Single repository and not always show repositories - await this.tree.setInput(Iterable.first(this.repositories.keys())!); - } else { - // Expand repository nodes - const expanded = Array.from(this.repositories.keys()) - .map(repository => `repo:${repository.provider.id}`); - - // Multiple repositories or always show repositories - await this.tree.setInput([...this.repositories.keys()], { expanded }); - } - } -} - -class SCMSyncDataSource implements IAsyncDataSource { - - constructor( - private readonly viewMode: () => ViewMode, - @IUriIdentityService private uriIdentityService: IUriIdentityService) { } - - hasChildren(element: TreeElement): boolean { - if (isSCMRepositoryArray(element)) { - return true; - } else if (isSCMRepository(element)) { - return true; - } else if (isSCMActionButton(element)) { - return false; - } else if (isSCMHistoryItemGroupTreeElement(element)) { - return true; - } else if (isSCMHistoryItemTreeElement(element)) { - return true; - } else if (isSCMHistoryItemChangeTreeElement(element)) { - return false; - } else if (ResourceTree.isResourceNode(element)) { - return element.childrenCount > 0; - } else { - throw new Error('hasChildren not implemented.'); - } - } - - async getChildren(element: TreeElement): Promise { - const children: TreeElement[] = []; - - if (isSCMRepositoryArray(element)) { - children.push(...element); - } else if (isSCMRepository(element)) { - const scmProvider = element.provider; - const historyProvider = scmProvider.historyProvider; - const historyItemGroup = historyProvider?.currentHistoryItemGroup; - - if (!historyProvider || !historyItemGroup) { - return []; - } - - // Action Button - const actionButton = historyProvider.actionButton; - if (actionButton) { - children.push({ - type: 'actionButton', - repository: element, - button: actionButton - } as ISCMActionButton); - } - - // History item groups - children.push(...await this.getHistoryItemGroups(element)); - } else if (isSCMHistoryItemGroupTreeElement(element)) { - children.push(...await this.getHistoryItems(element)); - } else if (isSCMHistoryItemTreeElement(element)) { - children.push(...await this.getHistoryItemChanges(element)); - } else if (ResourceTree.isResourceNode(element)) { - children.push(...element.children); - } else { - throw new Error('getChildren Method not implemented.'); - } - - return children; - } - - private async getHistoryItemGroups(element: ISCMRepository): Promise { - const scmProvider = element.provider; - const historyProvider = scmProvider.historyProvider; - const currentHistoryItemGroup = historyProvider?.currentHistoryItemGroup; - - if (!historyProvider || !currentHistoryItemGroup) { - return []; - } - - // History item group base - const historyItemGroupBase = await historyProvider.resolveHistoryItemGroupBase(currentHistoryItemGroup.id); - if (!historyItemGroupBase) { - return []; - } - - // Common ancestor, ahead, behind - const ancestor = await historyProvider.resolveHistoryItemGroupCommonAncestor(currentHistoryItemGroup.id, historyItemGroupBase.id); - - const children: SCMHistoryItemGroupTreeElement[] = []; - // Incoming - if (historyItemGroupBase) { - children.push({ - id: historyItemGroupBase.id, - label: `$(cloud-download) ${historyItemGroupBase.label}`, - description: localize('incoming', "Incoming Changes"), - ancestor: ancestor?.id, - count: ancestor?.behind ?? 0, - repository: element, - type: 'historyItemGroup' - } as SCMHistoryItemGroupTreeElement); - } - - // Outgoing - children.push({ - id: currentHistoryItemGroup.id, - label: `$(cloud-upload) ${currentHistoryItemGroup.label}`, - description: localize('outgoing', "Outgoing Changes"), - ancestor: ancestor?.id, - count: ancestor?.ahead ?? 0, - repository: element, - type: 'historyItemGroup' - } as SCMHistoryItemGroupTreeElement); - - return children; - } - - private async getHistoryItems(element: SCMHistoryItemGroupTreeElement): Promise { - const scmProvider = element.repository.provider; - const historyProvider = scmProvider.historyProvider; - - if (!historyProvider) { - return []; - } - - const historyItems = await historyProvider.provideHistoryItems(element.id, { limit: { id: element.ancestor } }) ?? []; - return historyItems.map(historyItem => ({ - id: historyItem.id, - label: historyItem.label, - description: historyItem.description, - icon: historyItem.icon, - historyItemGroup: element, - type: 'historyItem' - } as SCMHistoryItemTreeElement)); - } - - private async getHistoryItemChanges(element: SCMHistoryItemTreeElement): Promise<(SCMHistoryItemChangeTreeElement | SCMHistoryItemChangeResourceTreeNode)[]> { - const repository = element.historyItemGroup.repository; - const historyProvider = repository.provider.historyProvider; - - if (!historyProvider) { - return []; - } - - // History Item Changes - const changes = await historyProvider.provideHistoryItemChanges(element.id) ?? []; - - if (this.viewMode() === ViewMode.List) { - // List - return changes.map(change => ({ - uri: change.uri, - originalUri: change.originalUri, - modifiedUri: change.modifiedUri, - renameUri: change.renameUri, - historyItem: element, - type: 'historyItemChange' - } as SCMHistoryItemChangeTreeElement)); - } - - // Tree - const tree = new ResourceTree(element, repository.provider.rootUri ?? URI.file('/'), this.uriIdentityService.extUri); - for (const change of changes) { - tree.add(change.uri, { - uri: change.uri, - originalUri: change.originalUri, - modifiedUri: change.modifiedUri, - renameUri: change.renameUri, - historyItem: element, - type: 'historyItemChange' - } as SCMHistoryItemChangeTreeElement); - } - - return [...tree.root.children]; - } - -} - -class RefreshAction extends ViewAction { - constructor() { - super({ - id: 'workbench.scm.sync.action.refresh', - title: localize('refresh', "Refresh"), - viewId: SYNC_VIEW_PANE_ID, - f1: false, - icon: Codicon.refresh, - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', SYNC_VIEW_PANE_ID), - group: 'navigation' - } - }); - } - - async runInView(_: ServicesAccessor, view: SCMSyncViewPane): Promise { - view.viewModel.refresh(); - } -} - -class SetListViewModeAction extends ViewAction { - - constructor() { - super({ - id: 'workbench.scm.sync.action.setListViewMode', - title: localize('setListViewMode', "View as List"), - viewId: SYNC_VIEW_PANE_ID, - f1: false, - icon: Codicon.listTree, - toggled: ContextKeys.ViewMode.isEqualTo(ViewMode.List), - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', SYNC_VIEW_PANE_ID), - group: '1_viewmode' - } - }); - } - - async runInView(_: ServicesAccessor, view: SCMSyncViewPane): Promise { - view.viewModel.mode = ViewMode.List; - } -} - -class SetTreeViewModeAction extends ViewAction { - - constructor() { - super({ - id: 'workbench.scm.sync.action.setTreeViewMode', - title: localize('setTreeViewMode', "View as Tree"), - viewId: SYNC_VIEW_PANE_ID, - f1: false, - icon: Codicon.listFlat, - toggled: ContextKeys.ViewMode.isEqualTo(ViewMode.Tree), - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', SYNC_VIEW_PANE_ID), - group: '1_viewmode' - } - }); - } - - async runInView(_: ServicesAccessor, view: SCMSyncViewPane): Promise { - view.viewModel.mode = ViewMode.Tree; - } -} - -registerAction2(RefreshAction); -registerAction2(SetListViewModeAction); -registerAction2(SetTreeViewModeAction); diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index 3378d796300..3c222ca9393 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -18,7 +18,6 @@ import { ISCMHistoryProvider, ISCMHistoryProviderMenus } from 'vs/workbench/cont export const VIEWLET_ID = 'workbench.view.scm'; export const VIEW_PANE_ID = 'workbench.scm'; export const REPOSITORIES_VIEW_PANE_ID = 'workbench.scm.repositories'; -export const SYNC_VIEW_PANE_ID = 'workbench.scm.sync'; export interface IBaselineResourceProvider { getBaselineResource(resource: URI): Promise; From 55181913b909cb176018a58db84078ceb4f756e8 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Sun, 19 Nov 2023 17:23:01 +0100 Subject: [PATCH 55/87] SCM - update incoming/outgoing setting default (#198476) --- src/vs/workbench/contrib/scm/browser/scm.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 410c4e7d80a..ca8d16a7276 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -295,7 +295,7 @@ Registry.as(ConfigurationExtensions.Configuration).regis localize('scm.showIncomingChanges.auto', "Only show incoming changes in the Source Control view when any exist."), ], description: localize('scm.showIncomingChanges', "Controls whether incoming changes are shown in the Source Control view."), - default: 'never' + default: 'auto' }, 'scm.showOutgoingChanges': { type: 'string', @@ -306,7 +306,7 @@ Registry.as(ConfigurationExtensions.Configuration).regis localize('scm.showOutgoingChanges.auto', "Only show outgoing changes in the Source Control view when any exist."), ], description: localize('scm.showOutgoingChanges', "Controls whether outgoing changes are shown in the Source Control view."), - default: 'never' + default: 'auto' } } }); From 244c48768dbde775d32009da90be9abd3ea43f73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Sun, 19 Nov 2023 20:15:53 +0100 Subject: [PATCH 56/87] Make PRSS CDN optional (#198631) * empty commit * mooncake creds expired * more logs * prevent exit * log * async is hard * :lipstick: * temporarily ignore PRSS errors * whitespace --- build/azure-pipelines/common/publish.js | 76 +++++++-------------- build/azure-pipelines/common/publish.ts | 83 +++++++---------------- build/azure-pipelines/product-build.yml | 8 --- build/azure-pipelines/product-publish.yml | 15 ---- 4 files changed, 49 insertions(+), 133 deletions(-) diff --git a/build/azure-pipelines/common/publish.js b/build/azure-pipelines/common/publish.js index a8e45f6d22b..4f46a5ee563 100644 --- a/build/azure-pipelines/common/publish.js +++ b/build/azure-pipelines/common/publish.js @@ -481,54 +481,16 @@ async function uploadAssetLegacy(log, quality, commit, filePath) { blobCacheControl: 'max-age=31536000, public' } }; - const uploadPromises = []; - uploadPromises.push((async () => { - log(`Checking for blob in Azure...`); - if (await blobClient.exists()) { - log(`Blob ${quality}, ${blobName} already exists, not publishing again.`); - throw new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`); - } - else { - log(`Uploading blobs to Azure storage...`); - await blobClient.uploadFile(filePath, blobOptions); - log('Blob successfully uploaded to Azure storage.'); - } - })()); - const shouldUploadToMooncake = /true/i.test(e('VSCODE_PUBLISH_TO_MOONCAKE')); - if (shouldUploadToMooncake) { - const mooncakeCredential = new identity_1.ClientSecretCredential(e('AZURE_MOONCAKE_TENANT_ID'), e('AZURE_MOONCAKE_CLIENT_ID'), e('AZURE_MOONCAKE_CLIENT_SECRET')); - const mooncakeBlobServiceClient = new storage_blob_1.BlobServiceClient(`https://vscode.blob.core.chinacloudapi.cn`, mooncakeCredential, { retryOptions: { retryPolicyType: storage_blob_1.StorageRetryPolicyType.FIXED, tryTimeoutInMs: 5 * 60 * 1000 } }); - const mooncakeContainerClient = mooncakeBlobServiceClient.getContainerClient(quality); - const mooncakeBlobClient = mooncakeContainerClient.getBlockBlobClient(blobName); - uploadPromises.push((async () => { - log(`Checking for blob in Mooncake Azure...`); - if (await mooncakeBlobClient.exists()) { - log(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); - throw new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); - } - else { - log(`Uploading blobs to Mooncake Azure storage...`); - await mooncakeBlobClient.uploadFile(filePath, blobOptions); - log('Blob successfully uploaded to Mooncake Azure storage.'); - } - })()); - } - const promiseResults = await Promise.allSettled(uploadPromises); - const rejectedPromiseResults = promiseResults.filter(result => result.status === 'rejected'); - if (rejectedPromiseResults.length === 0) { - log('All blobs successfully uploaded.'); - } - else if (rejectedPromiseResults[0]?.reason?.message?.includes('already exists')) { - log('Some blobs successfully uploaded.'); + log(`Checking for blob in Azure...`); + if (await blobClient.exists()) { + log(`Blob ${quality}, ${blobName} already exists, not publishing again.`); } else { - // eslint-disable-next-line no-throw-literal - throw rejectedPromiseResults[0]?.reason; + log(`Uploading blobs to Azure storage...`); + await blobClient.uploadFile(filePath, blobOptions); + log('Blob successfully uploaded to Azure storage.'); } - const assetUrl = `${e('AZURE_CDN_URL')}/${quality}/${blobName}`; - const blobPath = new URL(assetUrl).pathname; - const mooncakeUrl = `${e('MOONCAKE_CDN_URL')}${blobPath}`; - return { assetUrl, mooncakeUrl }; + return `${e('AZURE_CDN_URL')}/${quality}/${blobName}`; } async function processArtifact(artifact, artifactFilePath) { const log = (...args) => console.log(`[${artifact.name}]`, ...args); @@ -545,14 +507,22 @@ async function processArtifact(artifact, artifactFilePath) { const size = fs.statSync(artifactFilePath).size; const stream = fs.createReadStream(artifactFilePath); const [sha1hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); - const [{ assetUrl, mooncakeUrl }, prssUrl] = await Promise.all([ + const [cdnSettledResult, prssSettledResult] = await Promise.allSettled([ uploadAssetLegacy(log, quality, commit, artifactFilePath), releaseAndProvision(log, e('RELEASE_TENANT_ID'), e('RELEASE_CLIENT_ID'), e('RELEASE_AUTH_CERT_SUBJECT_NAME'), e('RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME'), e('PROVISION_TENANT_ID'), e('PROVISION_AAD_USERNAME'), e('PROVISION_AAD_PASSWORD'), commit, quality, artifactFilePath) ]); - const asset = { platform, type, url: assetUrl, hash: sha1hash, mooncakeUrl, prssUrl, sha256hash, size, supportsFastUpdate: true }; + if (cdnSettledResult.status === 'rejected') { + throw cdnSettledResult.reason; + } + else if (prssSettledResult.status === 'rejected') { // TODO@joaomoreno, let's temporarily ignore these errors + console.error(prssSettledResult.reason); + } + const assetUrl = cdnSettledResult.value; + const prssUrl = prssSettledResult.status === 'fulfilled' ? prssSettledResult.value : undefined; + const asset = { platform, type, url: assetUrl, hash: sha1hash, mooncakeUrl: prssUrl, prssUrl, sha256hash, size, supportsFastUpdate: true }; log('Creating asset...', JSON.stringify(asset)); await (0, retry_1.retry)(async (attempt) => { - log(`Creating asset in Cosmos DB(attempt ${attempt})...`); + log(`Creating asset in Cosmos DB (attempt ${attempt})...`); const aadCredentials = new identity_1.ClientSecretCredential(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_CLIENT_SECRET')); const client = new cosmos_1.CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT'), aadCredentials }); const scripts = client.database('builds').container(quality).scripts; @@ -593,6 +563,7 @@ async function main() { if (e('VSCODE_BUILD_STAGE_WEB') === 'True') { stages.add('Web'); } + let resultPromise = Promise.resolve([]); const operations = []; while (true) { const [timeline, artifacts] = await Promise.all([(0, retry_1.retry)(() => getPipelineTimeline()), (0, retry_1.retry)(() => getPipelineArtifacts())]); @@ -619,7 +590,7 @@ async function main() { const artifactZipPath = path.join(e('AGENT_TEMPDIRECTORY'), `${artifact.name}.zip`); await (0, retry_1.retry)(async (attempt) => { const start = Date.now(); - console.log(`[${artifact.name}]Downloading(attempt ${attempt})...`); + console.log(`[${artifact.name}] Downloading (attempt ${attempt})...`); await downloadArtifact(artifact, artifactZipPath); const archiveSize = fs.statSync(artifactZipPath).size; const downloadDurationS = (Date.now() - start) / 1000; @@ -641,7 +612,7 @@ async function main() { resolve(); } else { - reject(new Error('Worker stopped with exit code ${code}')); + reject(new Error(`[${artifact.name}] Worker stopped with exit code ${code}`)); } }); }); @@ -651,6 +622,7 @@ async function main() { console.log(`\u2705 ${artifact.name} `); }); operations.push({ name: artifact.name, operation }); + resultPromise = Promise.allSettled(operations.map(o => o.operation)); } await new Promise(c => setTimeout(c, 10000)); } @@ -659,7 +631,7 @@ async function main() { if (artifactsInProgress.length > 0) { console.log('Artifacts in progress:', artifactsInProgress.map(a => a.name).join(', ')); } - const results = await Promise.allSettled(operations.map(o => o.operation)); + const results = await resultPromise; for (let i = 0; i < operations.length; i++) { const result = results[i]; if (result.status === 'rejected') { @@ -679,4 +651,4 @@ if (require.main === module) { process.exit(1); }); } -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGlzaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInB1Ymxpc2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyx5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLG1DQUFrQztBQUVsQyxtREFBZ0Q7QUFDaEQsK0JBQStCO0FBQy9CLGlDQUFpQztBQUNqQyxtQ0FBZ0M7QUFDaEMsc0RBQWdIO0FBQ2hILDZCQUE2QjtBQUM3QiwwQ0FBNkM7QUFDN0MsOENBQXlEO0FBQ3pELG9DQUFvQztBQUNwQyx5QkFBeUI7QUFDekIsNkRBQXVFO0FBRXZFLFNBQVMsQ0FBQyxDQUFDLElBQVk7SUFDdEIsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUVqQyxJQUFJLE9BQU8sTUFBTSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ2hDLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0JBQWdCLElBQUksRUFBRSxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUVELE9BQU8sTUFBTSxDQUFDO0FBQ2YsQ0FBQztBQUVELE1BQU0sSUFBSTtJQUNELE1BQU0sR0FBYSxFQUFFLENBQUM7SUFFOUIsV0FBVztRQUNWLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDNUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdkIsT0FBTyxJQUFJLENBQUM7SUFDYixDQUFDO0lBRUQsT0FBTztRQUNOLEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2hDLElBQUksQ0FBQztnQkFDSixFQUFFLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3JCLENB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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGlzaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInB1Ymxpc2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyx5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLG1DQUFrQztBQUVsQyxtREFBZ0Q7QUFDaEQsK0JBQStCO0FBQy9CLGlDQUFpQztBQUNqQyxtQ0FBZ0M7QUFDaEMsc0RBQWdIO0FBQ2hILDZCQUE2QjtBQUM3QiwwQ0FBNkM7QUFDN0MsOENBQXlEO0FBQ3pELG9DQUFvQztBQUNwQyx5QkFBeUI7QUFDekIsNkRBQXVFO0FBRXZFLFNBQVMsQ0FBQyxDQUFDLElBQVk7SUFDdEIsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUVqQyxJQUFJLE9BQU8sTUFBTSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ2hDLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0JBQWdCLElBQUksRUFBRSxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUVELE9BQU8sTUFBTSxDQUFDO0FBQ2YsQ0FBQztBQUVELE1BQU0sSUFBSTtJQUNELE1BQU0sR0FBYSxFQUFFLENBQUM7SUFFOUIsV0FBVztRQUNWLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDNUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdkIsT0FBTyxJQUFJLENBQUM7SUFDYixDQUFDO0lBRUQsT0FBTztRQUNOLEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2hDLElBQUksQ0FBQztnQkFDSixFQUFFLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3JCLENB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 \ No newline at end of file diff --git a/build/azure-pipelines/common/publish.ts b/build/azure-pipelines/common/publish.ts index 55f8302ec5b..a67c29e98b4 100644 --- a/build/azure-pipelines/common/publish.ts +++ b/build/azure-pipelines/common/publish.ts @@ -353,7 +353,6 @@ async function releaseAndProvision( const credential = new ClientSecretCredential(provisionTenantId, provisionAADUsername, provisionAADPassword); const accessToken = await credential.getToken(['https://microsoft.onmicrosoft.com/DS.Provisioning.WebApi/.default']); const service = new ProvisionService(log, accessToken.token); - await service.provision(release.releaseId, release.fileId, fileName); return result; @@ -628,7 +627,7 @@ function getRealType(type: string) { } } -async function uploadAssetLegacy(log: (...args: any[]) => void, quality: string, commit: string, filePath: string): Promise<{ assetUrl: string; mooncakeUrl: string }> { +async function uploadAssetLegacy(log: (...args: any[]) => void, quality: string, commit: string, filePath: string): Promise { const fileName = path.basename(filePath); const blobName = commit + '/' + fileName; @@ -645,60 +644,17 @@ async function uploadAssetLegacy(log: (...args: any[]) => void, quality: string, } }; - const uploadPromises: Promise[] = []; + log(`Checking for blob in Azure...`); - uploadPromises.push((async (): Promise => { - log(`Checking for blob in Azure...`); - - if (await blobClient.exists()) { - log(`Blob ${quality}, ${blobName} already exists, not publishing again.`); - throw new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`); - } else { - log(`Uploading blobs to Azure storage...`); - await blobClient.uploadFile(filePath, blobOptions); - log('Blob successfully uploaded to Azure storage.'); - } - })()); - - const shouldUploadToMooncake = /true/i.test(e('VSCODE_PUBLISH_TO_MOONCAKE')); - - if (shouldUploadToMooncake) { - const mooncakeCredential = new ClientSecretCredential(e('AZURE_MOONCAKE_TENANT_ID'), e('AZURE_MOONCAKE_CLIENT_ID'), e('AZURE_MOONCAKE_CLIENT_SECRET')); - const mooncakeBlobServiceClient = new BlobServiceClient(`https://vscode.blob.core.chinacloudapi.cn`, mooncakeCredential, { retryOptions: { retryPolicyType: StorageRetryPolicyType.FIXED, tryTimeoutInMs: 5 * 60 * 1000 } }); - const mooncakeContainerClient = mooncakeBlobServiceClient.getContainerClient(quality); - const mooncakeBlobClient = mooncakeContainerClient.getBlockBlobClient(blobName); - - uploadPromises.push((async (): Promise => { - log(`Checking for blob in Mooncake Azure...`); - - if (await mooncakeBlobClient.exists()) { - log(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); - throw new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); - } else { - log(`Uploading blobs to Mooncake Azure storage...`); - await mooncakeBlobClient.uploadFile(filePath, blobOptions); - log('Blob successfully uploaded to Mooncake Azure storage.'); - } - })()); - } - - const promiseResults = await Promise.allSettled(uploadPromises); - const rejectedPromiseResults = promiseResults.filter(result => result.status === 'rejected') as PromiseRejectedResult[]; - - if (rejectedPromiseResults.length === 0) { - log('All blobs successfully uploaded.'); - } else if (rejectedPromiseResults[0]?.reason?.message?.includes('already exists')) { - log('Some blobs successfully uploaded.'); + if (await blobClient.exists()) { + log(`Blob ${quality}, ${blobName} already exists, not publishing again.`); } else { - // eslint-disable-next-line no-throw-literal - throw rejectedPromiseResults[0]?.reason; + log(`Uploading blobs to Azure storage...`); + await blobClient.uploadFile(filePath, blobOptions); + log('Blob successfully uploaded to Azure storage.'); } - const assetUrl = `${e('AZURE_CDN_URL')}/${quality}/${blobName}`; - const blobPath = new URL(assetUrl).pathname; - const mooncakeUrl = `${e('MOONCAKE_CDN_URL')}${blobPath}`; - - return { assetUrl, mooncakeUrl }; + return `${e('AZURE_CDN_URL')}/${quality}/${blobName}`; } async function processArtifact(artifact: Artifact, artifactFilePath: string): Promise { @@ -719,7 +675,7 @@ async function processArtifact(artifact: Artifact, artifactFilePath: string): Pr const stream = fs.createReadStream(artifactFilePath); const [sha1hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); - const [{ assetUrl, mooncakeUrl }, prssUrl] = await Promise.all([ + const [cdnSettledResult, prssSettledResult] = await Promise.allSettled([ uploadAssetLegacy(log, quality, commit, artifactFilePath), releaseAndProvision( log, @@ -736,11 +692,20 @@ async function processArtifact(artifact: Artifact, artifactFilePath: string): Pr ) ]); - const asset: Asset = { platform, type, url: assetUrl, hash: sha1hash, mooncakeUrl, prssUrl, sha256hash, size, supportsFastUpdate: true }; + if (cdnSettledResult.status === 'rejected') { + throw cdnSettledResult.reason; + } else if (prssSettledResult.status === 'rejected') { // TODO@joaomoreno, let's temporarily ignore these errors + console.error(prssSettledResult.reason); + } + + const assetUrl = cdnSettledResult.value; + const prssUrl = prssSettledResult.status === 'fulfilled' ? prssSettledResult.value : undefined; + + const asset: Asset = { platform, type, url: assetUrl, hash: sha1hash, mooncakeUrl: prssUrl, prssUrl, sha256hash, size, supportsFastUpdate: true }; log('Creating asset...', JSON.stringify(asset)); await retry(async (attempt) => { - log(`Creating asset in Cosmos DB(attempt ${attempt})...`); + log(`Creating asset in Cosmos DB (attempt ${attempt})...`); const aadCredentials = new ClientSecretCredential(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_CLIENT_SECRET')); const client = new CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT'), aadCredentials }); const scripts = client.database('builds').container(quality).scripts; @@ -777,6 +742,7 @@ async function main() { if (e('VSCODE_BUILD_STAGE_MACOS') === 'True') { stages.add('macOS'); } if (e('VSCODE_BUILD_STAGE_WEB') === 'True') { stages.add('Web'); } + let resultPromise = Promise.resolve[]>([]); const operations: { name: string; operation: Promise }[] = []; while (true) { @@ -806,7 +772,7 @@ async function main() { await retry(async (attempt) => { const start = Date.now(); - console.log(`[${artifact.name}]Downloading(attempt ${attempt})...`); + console.log(`[${artifact.name}] Downloading (attempt ${attempt})...`); await downloadArtifact(artifact, artifactZipPath); const archiveSize = fs.statSync(artifactZipPath).size; const downloadDurationS = (Date.now() - start) / 1000; @@ -830,7 +796,7 @@ async function main() { if (code === 0) { resolve(); } else { - reject(new Error('Worker stopped with exit code ${code}')); + reject(new Error(`[${artifact.name}] Worker stopped with exit code ${code}`)); } }); }); @@ -842,6 +808,7 @@ async function main() { }); operations.push({ name: artifact.name, operation }); + resultPromise = Promise.allSettled(operations.map(o => o.operation)); } await new Promise(c => setTimeout(c, 10_000)); @@ -855,7 +822,7 @@ async function main() { console.log('Artifacts in progress:', artifactsInProgress.map(a => a.name).join(', ')); } - const results = await Promise.allSettled(operations.map(o => o.operation)); + const results = await resultPromise; for (let i = 0; i < operations.length; i++) { const result = results[i]; diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 972ddb3411f..a46a61be86e 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -76,10 +76,6 @@ parameters: displayName: "Publish to builds.code.visualstudio.com" type: boolean default: true - - name: VSCODE_PUBLISH_TO_MOONCAKE - displayName: "Publish to Azure China" - type: boolean - default: true - name: VSCODE_RELEASE displayName: "Release build if successful" type: boolean @@ -116,8 +112,6 @@ variables: value: ${{ in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }} - name: VSCODE_PUBLISH value: ${{ and(eq(parameters.VSCODE_PUBLISH, true), eq(variables.VSCODE_CIBUILD, false), eq(parameters.VSCODE_COMPILE_ONLY, false)) }} - - name: VSCODE_PUBLISH_TO_MOONCAKE - value: ${{ eq(parameters.VSCODE_PUBLISH_TO_MOONCAKE, true) }} - name: VSCODE_SCHEDULEDBUILD value: ${{ eq(variables['Build.Reason'], 'Schedule') }} - name: VSCODE_7PM_BUILD @@ -138,8 +132,6 @@ variables: value: https://az764295.vo.msecnd.net - name: AZURE_DOCUMENTDB_ENDPOINT value: https://vscode.documents.azure.com:443/ - - name: MOONCAKE_CDN_URL - value: https://vscode.cdn.azure.cn - name: VSCODE_MIXIN_REPO value: microsoft/vscode-distro - name: skipComponentGovernanceDetection diff --git a/build/azure-pipelines/product-publish.yml b/build/azure-pipelines/product-publish.yml index 16730948182..1cf0209aa63 100644 --- a/build/azure-pipelines/product-publish.yml +++ b/build/azure-pipelines/product-publish.yml @@ -49,18 +49,6 @@ steps: Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" - - task: AzureCLI@2 - displayName: Fetch Mooncake secrets - inputs: - azureSubscription: "vscode-builds-mooncake-subscription" - scriptType: pscore - scriptLocation: inlineScript - addSpnToEnvironment: true - inlineScript: | - Write-Host "##vso[task.setvariable variable=AZURE_MOONCAKE_TENANT_ID]$env:tenantId" - Write-Host "##vso[task.setvariable variable=AZURE_MOONCAKE_CLIENT_ID]$env:servicePrincipalId" - Write-Host "##vso[task.setvariable variable=AZURE_MOONCAKE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" - - pwsh: | . build/azure-pipelines/win32/exec.ps1 @@ -102,9 +90,6 @@ steps: AZURE_TENANT_ID: "$(AZURE_TENANT_ID)" AZURE_CLIENT_ID: "$(AZURE_CLIENT_ID)" AZURE_CLIENT_SECRET: "$(AZURE_CLIENT_SECRET)" - AZURE_MOONCAKE_TENANT_ID: "$(AZURE_MOONCAKE_TENANT_ID)" - AZURE_MOONCAKE_CLIENT_ID: "$(AZURE_MOONCAKE_CLIENT_ID)" - AZURE_MOONCAKE_CLIENT_SECRET: "$(AZURE_MOONCAKE_CLIENT_SECRET)" SYSTEM_ACCESSTOKEN: $(System.AccessToken) RELEASE_TENANT_ID: "$(PRSS_RELEASE_TENANT_ID)" RELEASE_CLIENT_ID: "$(PRSS_RELEASE_CLIENT_ID)" From e0b9ba5710060c8a8014f8a12980f011d81fb844 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Sun, 19 Nov 2023 13:47:24 -0800 Subject: [PATCH 57/87] notebook cell chat setting (#198633) --- .../contrib/notebook/browser/notebook.contribution.ts | 5 +++++ .../browser/view/cellParts/chat/cellChatController.ts | 2 +- .../notebook/browser/view/cellParts/chat/cellChatPart.ts | 3 ++- 3 files changed, 8 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 4936a25da52..701abbd1384 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -1050,6 +1050,11 @@ configurationRegistry.registerConfiguration({ nls.localize('notebook.scrolling.anchorToFocusedCell.off.description', "The focused cell may shift around as cells resize.") ], default: 'auto' + }, + [NotebookSetting.cellChat]: { + markdownDescription: nls.localize('notebook.cellChat', "Enable experimental cell chat for notebooks."), + type: 'boolean', + default: false } } }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts index c8dcbba2064..20a55a63bcf 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts @@ -152,9 +152,9 @@ export class NotebookCellChatController extends Disposable { try { if (this._widget) { this._partContainer.removeChild(this._widget.domNode); + this._partContainer.removeChild(this._toolbarDOM.editorToolbar); } - this._partContainer.removeChild(this._toolbarDOM.editorToolbar); } catch (_ex) { // might not be attached } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatPart.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatPart.ts index bbcdd98fe08..461dc85c664 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatPart.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatPart.ts @@ -10,6 +10,7 @@ import { NotebookCellChatController } from 'vs/workbench/contrib/notebook/browse import 'vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export class CellChatPart extends CellContentPart { private _controller: NotebookCellChatController | undefined; @@ -29,7 +30,7 @@ export class CellChatPart extends CellContentPart { override didRenderCell(element: ICellViewModel): void { this._controller?.dispose(); - const enabled = this._configurationService.getValue('notebook.experimental.cellChat'); + const enabled = this._configurationService.getValue(NotebookSetting.cellChat); if (enabled) { this._controller = this._instantiationService.createInstance(NotebookCellChatController, this._notebookEditor, this, element, this._partContainer); } From 46cd720f834ee31fedeb2f17e039ddf00a96edcb Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 20 Nov 2023 16:12:55 +1100 Subject: [PATCH 58/87] Drop uuid pkg used to to generate notebook cell id (#198647) --- extensions/ipynb/package.json | 28 +++++++------- extensions/ipynb/src/cellIdService.ts | 56 ++++++++++++++++++++++++++- extensions/ipynb/yarn.lock | 10 ----- 3 files changed, 67 insertions(+), 27 deletions(-) diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index f7afc12a448..aee7e85a30a 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -5,7 +5,7 @@ "publisher": "vscode", "version": "1.0.0", "license": "MIT", - "icon": "media/icon.png", + "icon": "media/icon.png", "engines": { "vscode": "^1.57.0" }, @@ -58,10 +58,10 @@ "command": "ipynb.cleanInvalidImageAttachment", "title": "%cleanInvalidImageAttachment.title%" }, - { - "command": "notebook.cellOutput.copy", - "title": "%copyCellOutput.title%" - } + { + "command": "notebook.cellOutput.copy", + "title": "%copyCellOutput.title%" + } ], "notebooks": [ { @@ -105,12 +105,12 @@ "when": "false" } ], - "webview/context": [ - { - "command": "notebook.cellOutput.copy", - "when": "webviewId == 'notebook.output' && webviewSection == 'image'" - } - ] + "webview/context": [ + { + "command": "notebook.cellOutput.copy", + "when": "webviewId == 'notebook.output' && webviewSection == 'image'" + } + ] } }, "scripts": { @@ -120,13 +120,11 @@ }, "dependencies": { "@enonic/fnv-plus": "^1.3.0", - "detect-indent": "^6.0.0", - "uuid": "^8.3.2" + "detect-indent": "^6.0.0" }, "devDependencies": { "@jupyterlab/nbformat": "^3.2.9", - "@types/markdown-it": "12.2.3", - "@types/uuid": "^8.3.1" + "@types/markdown-it": "12.2.3" }, "repository": { "type": "git", diff --git a/extensions/ipynb/src/cellIdService.ts b/extensions/ipynb/src/cellIdService.ts index 2eccb586ef4..43c769bca51 100644 --- a/extensions/ipynb/src/cellIdService.ts +++ b/extensions/ipynb/src/cellIdService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { ExtensionContext, NotebookDocument, NotebookDocumentChangeEvent, NotebookEdit, workspace, WorkspaceEdit } from 'vscode'; -import { v4 as uuid } from 'uuid'; import { getCellMetadata } from './serializers'; import { CellMetadata } from './common'; import { getNotebookMetadata } from './notebookSerializer'; @@ -57,7 +56,7 @@ function generateCellId(notebook: NotebookDocument) { while (true) { // Details of the id can be found here https://jupyter.org/enhancement-proposals/62-cell-id/cell-id.html#adding-an-id-field, // & here https://jupyter.org/enhancement-proposals/62-cell-id/cell-id.html#updating-older-formats - const id = uuid().replace(/-/g, '').substring(0, 8); + const id = generateUuid().replace(/-/g, '').substring(0, 8); let duplicate = false; for (let index = 0; index < notebook.cellCount; index++) { const cell = notebook.cellAt(index); @@ -75,3 +74,56 @@ function generateCellId(notebook: NotebookDocument) { } } } + + +/** + * Copied from src/vs/base/common/uuid.ts + */ +function generateUuid() { + // use `randomValues` if possible + function getRandomValues(bucket: Uint8Array): Uint8Array { + for (let i = 0; i < bucket.length; i++) { + bucket[i] = Math.floor(Math.random() * 256); + } + return bucket; + } + + // prep-work + const _data = new Uint8Array(16); + const _hex: string[] = []; + for (let i = 0; i < 256; i++) { + _hex.push(i.toString(16).padStart(2, '0')); + } + + // get data + getRandomValues(_data); + + // set version bits + _data[6] = (_data[6] & 0x0f) | 0x40; + _data[8] = (_data[8] & 0x3f) | 0x80; + + // print as string + let i = 0; + let result = ''; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + return result; +} diff --git a/extensions/ipynb/yarn.lock b/extensions/ipynb/yarn.lock index 7b5488e7109..fa950f66b64 100644 --- a/extensions/ipynb/yarn.lock +++ b/extensions/ipynb/yarn.lock @@ -37,17 +37,7 @@ resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA== -"@types/uuid@^8.3.1": - version "8.3.4" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" - integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== - detect-indent@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== - -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== From dbc482a1f697a9b10ee4eaa88536d3c1b4442b76 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 20 Nov 2023 07:48:41 +0100 Subject: [PATCH 59/87] Engineering - remove incoming/outgoing workspace setting (#198650) --- .vscode/settings.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 46ec2e83d68..95888a8f542 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -166,8 +166,5 @@ "src/vs/workbench/api/common/extHostTypes.ts" ], // Temporarily enabled for self-hosting - "terminal.integrated.stickyScroll.enabled": true, - // Temporarily enabled for self-hosting - "scm.showIncomingChanges": "always", - "scm.showOutgoingChanges": "always", + "terminal.integrated.stickyScroll.enabled": true } From 356784e05f0e8a7267218c49fdb0da64c817f871 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 20 Nov 2023 07:51:19 +0100 Subject: [PATCH 60/87] Aux window: Unable to switch to first editor group from new window (fix #198607) (#198652) --- src/vs/workbench/browser/parts/editor/editorParts.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editorParts.ts b/src/vs/workbench/browser/parts/editor/editorParts.ts index 475889c9d15..234ed040043 100644 --- a/src/vs/workbench/browser/parts/editor/editorParts.ts +++ b/src/vs/workbench/browser/parts/editor/editorParts.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { EditorGroupLayout, GroupDirection, GroupOrientation, GroupsArrangement, GroupsOrder, IAuxiliaryEditorPart, IEditorDropTargetDelegate, IEditorGroupsService, IEditorSideGroup, IFindGroupScope, IMergeGroupOptions } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { EditorGroupLayout, GroupDirection, GroupLocation, GroupOrientation, GroupsArrangement, GroupsOrder, IAuxiliaryEditorPart, IEditorDropTargetDelegate, IEditorGroupsService, IEditorSideGroup, IFindGroupScope, IMergeGroupOptions } from 'vs/workbench/services/editor/common/editorGroupsService'; import { Event, Emitter } from 'vs/base/common/event'; import { getActiveDocument } from 'vs/base/browser/dom'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -306,6 +306,12 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd } findGroup(scope: IFindGroupScope, source?: IEditorGroupView | GroupIdentifier, wrap?: boolean): IEditorGroupView | undefined { + if (scope.location === GroupLocation.FIRST || scope.location === GroupLocation.LAST) { + // TODO implement support for all scopes with multiple editor parts + // https://github.com/microsoft/vscode/issues/198651 + return scope.location === GroupLocation.FIRST ? this.groups[0] : this.groups[this.groups.length - 1]; + } + if (source) { return this.getPart(source).findGroup(scope, source, wrap); } From 3671d157445a4efffef2805963cd472fbb346ebc Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 20 Nov 2023 11:21:30 +0100 Subject: [PATCH 61/87] SCM - fix text overflow for incoming/outgoing labels (#198663) --- src/vs/workbench/api/browser/mainThreadSCM.ts | 26 +++++-------------- .../contrib/scm/browser/media/scm.css | 14 +--------- .../contrib/scm/browser/scmViewPane.ts | 12 ++++++++- .../workbench/contrib/scm/common/history.ts | 6 ++--- 4 files changed, 21 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 1e29e53e8bb..b5d02c40e37 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -7,7 +7,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, DisposableStore, combinedDisposable, dispose, DisposableMap } from 'vs/base/common/lifecycle'; import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation, ISCMViewService, InputValidationType, ISCMActionButtonDescriptor, ISCMInputValueProvider, ISCMInputValueProviderContext } from 'vs/workbench/contrib/scm/common/scm'; -import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, SCMHistoryItemDto, SCMActionButtonDto, SCMHistoryItemGroupDto, SCMInputActionButtonDto } from '../common/extHost.protocol'; +import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, SCMActionButtonDto, SCMHistoryItemGroupDto, SCMInputActionButtonDto } from '../common/extHost.protocol'; import { Command } from 'vs/editor/common/languages'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -18,6 +18,7 @@ import { IQuickDiffService, QuickDiffProvider } from 'vs/workbench/contrib/scm/c import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryItemGroup, ISCMHistoryItemGroupDetails, ISCMHistoryItemGroupEntry, ISCMHistoryOptions, ISCMHistoryProvider } from 'vs/workbench/contrib/scm/common/history'; import { ResourceTree } from 'vs/base/common/resourceTree'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { Codicon } from 'vs/base/common/codicons'; function getSCMInputBoxActionButtonIcon(actionButton: SCMInputActionButtonDto): URI | { light: URI; dark: URI } | ThemeIcon | undefined { if (!actionButton.icon) { @@ -32,19 +33,6 @@ function getSCMInputBoxActionButtonIcon(actionButton: SCMInputActionButtonDto): } } -function getSCMHistoryItemIcon(historyItem: SCMHistoryItemDto): URI | { light: URI; dark: URI } | ThemeIcon | undefined { - if (!historyItem.icon) { - return undefined; - } else if (URI.isUri(historyItem.icon)) { - return URI.revive(historyItem.icon); - } else if (ThemeIcon.isThemeIcon(historyItem.icon)) { - return historyItem.icon; - } else { - const icon = historyItem.icon as { light: UriComponents; dark: UriComponents }; - return { light: URI.revive(icon.light), dark: URI.revive(icon.dark) }; - } -} - function getIconFromIconDto(iconDto?: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon): URI | { light: URI; dark: URI } | ThemeIcon | undefined { if (iconDto === undefined) { return undefined; @@ -184,8 +172,8 @@ class MainThreadSCMHistoryProvider implements ISCMHistoryProvider { if (historyItemGroupBase) { incoming = { id: historyItemGroupBase.id, - label: `$(cloud-download) ${historyItemGroupBase.label}`, - // description: localize('incoming', "Incoming Changes"), + label: historyItemGroupBase.label, + icon: Codicon.cloudDownload, ancestor: ancestor?.id, count: ancestor?.behind ?? 0, }; @@ -194,8 +182,8 @@ class MainThreadSCMHistoryProvider implements ISCMHistoryProvider { // Outgoing const outgoing: ISCMHistoryItemGroupEntry = { id: historyItemGroup.id, - label: `$(cloud-upload) ${historyItemGroup.label}`, - // description: localize('outgoing', "Outgoing Changes"), + label: historyItemGroup.label, + icon: Codicon.cloudUpload, ancestor: ancestor?.id, count: ancestor?.ahead ?? 0, }; @@ -213,7 +201,7 @@ class MainThreadSCMHistoryProvider implements ISCMHistoryProvider { async provideHistoryItems(historyItemGroupId: string, options: ISCMHistoryOptions): Promise { const historyItems = await this.proxy.$provideHistoryItems(this.handle, historyItemGroupId, options, CancellationToken.None); - return historyItems?.map(historyItem => ({ ...historyItem, icon: getSCMHistoryItemIcon(historyItem), })); + return historyItems?.map(historyItem => ({ ...historyItem, icon: getIconFromIconDto(historyItem.icon) })); } async provideHistoryItemChanges(historyItemId: string): Promise { diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index 38ff045e4d1..38f4e87b259 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -118,25 +118,12 @@ align-items: center; } -.scm-view .monaco-list-row .history-item-group .monaco-highlighted-label { - display: flex; - align-items: center; -} - .scm-view .monaco-list-row .history-item-group .monaco-icon-label, .scm-view .monaco-list-row .history-item .monaco-icon-label { flex-grow: 1; align-items: center; } -.scm-view .monaco-list-row .history-item-group .monaco-icon-label > .monaco-icon-label-container { - display: flex; -} -.scm-view .monaco-list-row .history-item-group .monaco-icon-label > .monaco-icon-label-container .monaco-icon-description-container { - overflow: hidden; - text-overflow: ellipsis; -} - .scm-sync-view .monaco-list-row .monaco-icon-label .icon-container .scm-sync-view .monaco-list-row .monaco-icon-label .icon-container { display: flex; @@ -185,6 +172,7 @@ color: var(--vscode-scm-historyItemDeletionsForeground); } +.scm-view .monaco-list-row .history-item-group .monaco-icon-label .icon-container, .scm-view .monaco-list-row .history-item .monaco-icon-label .icon-container { display: flex; font-size: 14px; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 95706a2e8dd..3e825d51f05 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -726,6 +726,7 @@ class ResourceRenderer implements ICompressibleTreeRenderer, index: number, templateData: HistoryItemGroupTemplate, height: number | undefined): void { const historyItemGroup = node.element; + + templateData.iconContainer.className = 'icon-container'; + if (historyItemGroup.icon && ThemeIcon.isThemeIcon(historyItemGroup.icon)) { + templateData.iconContainer.classList.add(...ThemeIcon.asClassNameArray(historyItemGroup.icon)); + } + templateData.label.setLabel(historyItemGroup.label, historyItemGroup.description); templateData.count.setCount(historyItemGroup.count ?? 0); } diff --git a/src/vs/workbench/contrib/scm/common/history.ts b/src/vs/workbench/contrib/scm/common/history.ts index 9e05de0285c..eb350b43286 100644 --- a/src/vs/workbench/contrib/scm/common/history.ts +++ b/src/vs/workbench/contrib/scm/common/history.ts @@ -61,15 +61,13 @@ export interface ISCMHistoryItemGroupDetails { export interface ISCMHistoryItemGroupEntry { readonly id: string; readonly label: string; + readonly icon?: URI | { light: URI; dark: URI } | ThemeIcon; readonly description?: string; readonly ancestor?: string; readonly count?: number; } -export interface SCMHistoryItemGroupTreeElement extends ISCMHistoryItemGroup { - readonly description?: string; - readonly ancestor?: string; - readonly count?: number; +export interface SCMHistoryItemGroupTreeElement extends ISCMHistoryItemGroupEntry { readonly repository: ISCMRepository; readonly type: 'historyItemGroup'; } From be038181caf85152692ac2327402e87a88886669 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 20 Nov 2023 12:06:05 +0100 Subject: [PATCH 62/87] Lazy-load @vscode/proxy-agent (#198662) --- src/vs/platform/native/electron-main/nativeHostMainService.ts | 4 ++-- src/vs/platform/request/node/requestService.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 88ec38a2787..646d67a9c3e 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -42,7 +42,6 @@ import { WindowProfiler } from 'vs/platform/profiling/electron-main/windowProfil import { IV8Profile } from 'vs/platform/profiling/common/profiling'; import { IAuxiliaryWindowsMainService, isAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows'; import { IAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow'; -import { loadSystemCertificates } from '@vscode/proxy-agent'; export interface INativeHostMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } @@ -766,7 +765,8 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async loadCertificates(_windowId: number | undefined): Promise { - return loadSystemCertificates({ log: this.logService }); + const proxyAgent = await import('@vscode/proxy-agent'); + return proxyAgent.loadSystemCertificates({ log: this.logService }); } findFreePort(windowId: number | undefined, startPort: number, giveUpAfter: number, timeout: number, stride = 1): Promise { diff --git a/src/vs/platform/request/node/requestService.ts b/src/vs/platform/request/node/requestService.ts index 07318efce27..23f8f0d44c8 100644 --- a/src/vs/platform/request/node/requestService.ts +++ b/src/vs/platform/request/node/requestService.ts @@ -20,7 +20,6 @@ import { ILogService, ILoggerService } from 'vs/platform/log/common/log'; import { AbstractRequestService, IRequestService } from 'vs/platform/request/common/request'; import { Agent, getProxyAgent } from 'vs/platform/request/node/proxy'; import { createGunzip } from 'zlib'; -import { loadSystemCertificates } from '@vscode/proxy-agent'; interface IHTTPConfiguration { proxy?: string; @@ -112,7 +111,8 @@ export class RequestService extends AbstractRequestService implements IRequestSe } async loadCertificates(): Promise { - return loadSystemCertificates({ log: this.logService }); + const proxyAgent = await import('@vscode/proxy-agent'); + return proxyAgent.loadSystemCertificates({ log: this.logService }); } } From 047cf79f5d10df18fcf03dbdffe84c107d5c8cbd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 20 Nov 2023 12:16:11 +0100 Subject: [PATCH 63/87] debt - prefer lazy imports (#198668) --- package.json | 4 ++-- src/vs/base/node/zip.ts | 15 +++++++++------ .../electron-main/keyboardLayoutMainService.ts | 2 +- src/vs/platform/log/node/spdlogLog.ts | 2 +- .../platform/policy/node/nativePolicyService.ts | 4 +++- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index be35df296c3..50ccdc8eac7 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,6 @@ "native-watchdog": "^1.4.1", "node-pty": "1.1.0-beta5", "tas-client-umd": "0.1.8", - "util": "^0.12.4", "v8-inspect-profiler": "^0.1.0", "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", @@ -211,6 +210,7 @@ "tsec": "0.2.7", "typescript": "^5.4.0-dev.20231116", "typescript-formatter": "7.1.0", + "util": "^0.12.4", "vscode-nls-dev": "^3.3.1", "webpack": "^5.77.0", "webpack-cli": "^5.0.1", @@ -228,4 +228,4 @@ "optionalDependencies": { "windows-foreground-love": "0.5.0" } -} \ No newline at end of file +} diff --git a/src/vs/base/node/zip.ts b/src/vs/base/node/zip.ts index cc4c65a625f..3cb7bc9795b 100644 --- a/src/vs/base/node/zip.ts +++ b/src/vs/base/node/zip.ts @@ -11,8 +11,7 @@ import * as path from 'vs/base/common/path'; import { assertIsDefined } from 'vs/base/common/types'; import { Promises } from 'vs/base/node/pfs'; import * as nls from 'vs/nls'; -import { Entry, open as _openZip, ZipFile } from 'yauzl'; -import * as yazl from 'yazl'; +import type { Entry, ZipFile } from 'yauzl'; export const CorruptZipMessage: string = 'end of central directory record signature not found'; const CORRUPT_ZIP_PATTERN = new RegExp(CorruptZipMessage); @@ -161,9 +160,11 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, tok }).finally(() => listener.dispose()); } -function openZip(zipFile: string, lazy: boolean = false): Promise { +async function openZip(zipFile: string, lazy: boolean = false): Promise { + const { open } = await import('yauzl'); + return new Promise((resolve, reject) => { - _openZip(zipFile, lazy ? { lazyEntries: true } : undefined!, (error?: Error, zipfile?: ZipFile) => { + open(zipFile, lazy ? { lazyEntries: true } : undefined!, (error?: Error, zipfile?: ZipFile) => { if (error) { reject(toExtractError(error)); } else { @@ -191,9 +192,11 @@ export interface IFile { localPath?: string; } -export function zip(zipPath: string, files: IFile[]): Promise { +export async function zip(zipPath: string, files: IFile[]): Promise { + const { ZipFile } = await import('yazl'); + return new Promise((c, e) => { - const zip = new yazl.ZipFile(); + const zip = new ZipFile(); files.forEach(f => { if (f.contents) { zip.addBuffer(typeof f.contents === 'string' ? Buffer.from(f.contents, 'utf8') : f.contents, f.path); diff --git a/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts b/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts index 1acbb89651c..1d17e4c709e 100644 --- a/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts +++ b/src/vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nativeKeymap from 'native-keymap'; +import type * as nativeKeymap from 'native-keymap'; import * as platform from 'vs/base/common/platform'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/platform/log/node/spdlogLog.ts b/src/vs/platform/log/node/spdlogLog.ts index 4f5f1bf8a8c..11f078f941e 100644 --- a/src/vs/platform/log/node/spdlogLog.ts +++ b/src/vs/platform/log/node/spdlogLog.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as spdlog from '@vscode/spdlog'; +import type * as spdlog from '@vscode/spdlog'; import { ByteSize } from 'vs/platform/files/common/files'; import { AbstractMessageLogger, ILogger, LogLevel } from 'vs/platform/log/common/log'; diff --git a/src/vs/platform/policy/node/nativePolicyService.ts b/src/vs/platform/policy/node/nativePolicyService.ts index eae3bf8dfac..d6e3f60cdd1 100644 --- a/src/vs/platform/policy/node/nativePolicyService.ts +++ b/src/vs/platform/policy/node/nativePolicyService.ts @@ -6,7 +6,7 @@ import { AbstractPolicyService, IPolicyService, PolicyDefinition } from 'vs/platform/policy/common/policy'; import { IStringDictionary } from 'vs/base/common/collections'; import { Throttler } from 'vs/base/common/async'; -import { createWatcher, PolicyUpdate, Watcher } from '@vscode/policy-watcher'; +import type { PolicyUpdate, Watcher } from '@vscode/policy-watcher'; import { MutableDisposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; @@ -25,6 +25,8 @@ export class NativePolicyService extends AbstractPolicyService implements IPolic protected async _updatePolicyDefinitions(policyDefinitions: IStringDictionary): Promise { this.logService.trace(`NativePolicyService#_updatePolicyDefinitions - Found ${Object.keys(policyDefinitions).length} policy definitions`); + const { createWatcher } = await import('@vscode/policy-watcher'); + await this.throttler.queue(() => new Promise((c, e) => { try { this.watcher.value = createWatcher(this.productName, policyDefinitions, update => { From 93724538798b99d7f24acc4e3db266c59fce81f1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 20 Nov 2023 14:35:53 +0100 Subject: [PATCH 64/87] persist inline chat history (#198680) --- .../browser/inlineChatController.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 7f2f70def69..f9d0c371bcd 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -45,6 +45,7 @@ import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { renderMarkdownAsPlaintext } from 'vs/base/browser/markdownRenderer'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; export const enum State { CREATE_SESSION = 'CREATE_SESSION', @@ -105,8 +106,10 @@ export class InlineChatController implements IEditorContribution { className: 'inline-chat-block-selection', }); + private static _storageKey = 'inline-chat-history'; private static _promptHistory: string[] = []; private _historyOffset: number = -1; + private _historyUpdate: (prompt: string) => void; private readonly _store = new DisposableStore(); private readonly _zone: Lazy; @@ -146,6 +149,7 @@ export class InlineChatController implements IEditorContribution { @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService, @IChatAgentService private readonly _chatAgentService: IChatAgentService, @IBulkEditService private readonly _bulkEditService: IBulkEditService, + @IStorageService private readonly _storageService: IStorageService, ) { this._ctxHasActiveRequest = CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST.bindTo(contextKeyService); this._ctxDidEdit = CTX_INLINE_CHAT_DID_EDIT.bindTo(contextKeyService); @@ -171,6 +175,17 @@ export class InlineChatController implements IEditorContribution { this._log('session done or paused'); })); this._log('NEW controller'); + + InlineChatController._promptHistory = JSON.parse(_storageService.get(InlineChatController._storageKey, StorageScope.PROFILE, '[]')); + this._historyUpdate = (prompt: string) => { + const idx = InlineChatController._promptHistory.indexOf(prompt); + if (idx >= 0) { + InlineChatController._promptHistory.splice(idx, 1); + } + InlineChatController._promptHistory.unshift(prompt); + this._historyOffset = -1; + this._storageService.store(InlineChatController._storageKey, JSON.stringify(InlineChatController._promptHistory), StorageScope.PROFILE, StorageTarget.USER); + }; } dispose(): void { @@ -542,9 +557,8 @@ export class InlineChatController implements IEditorContribution { const input = this.getInput(); - if (!InlineChatController._promptHistory.includes(input)) { - InlineChatController._promptHistory.unshift(input); - } + + this._historyUpdate(input); const refer = this._activeSession.session.slashCommands?.some(value => value.refer && input!.startsWith(`/${value.command}`)); if (refer) { From e2bf0c1d0121c9848b80f5edb1dbddea1738cf9a Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 20 Nov 2023 15:45:55 +0100 Subject: [PATCH 65/87] Style improvements --- .../diffEditorItemTemplate.ts | 67 ++++++++++--------- .../multiDiffEditorViewModel.ts | 4 +- .../multiDiffEditorWidgetImpl.ts | 2 +- .../widget/multiDiffEditorWidget/style.css | 29 +++++++- 4 files changed, 67 insertions(+), 35 deletions(-) diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts b/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts index 4587abb6d44..ea2d446132d 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts @@ -8,29 +8,31 @@ import { Codicon } from 'vs/base/common/codicons'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { autorun, derived, observableFromEvent } from 'vs/base/common/observable'; import { IObservable, globalTransaction, observableValue } from 'vs/base/common/observableInternal/base'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; -import { IDocumentDiffItem } from 'vs/editor/browser/widget/multiDiffEditorWidget/model'; +import { DocumentDiffItemViewModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel'; import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory'; +import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; -import { IDiffEditorViewModel } from 'vs/editor/common/editorCommon'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IObjectData, IPooledObject } from './objectPool'; -import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; export class TemplateData implements IObjectData { constructor( - public readonly viewModel: IDiffEditorViewModel, - public readonly entry: IDocumentDiffItem, + public readonly viewModel: DocumentDiffItemViewModel, ) { } getId(): unknown { - return this.entry; + return this.viewModel; } } export class DiffEditorItemTemplate extends Disposable implements IPooledObject { + private readonly _viewModel = observableValue(this, undefined); + + private readonly _collapsed = derived(this, reader => this._viewModel.read(reader)?.collapsed.read(reader)); + private readonly _contentHeight = observableValue(this, 500); public readonly height = derived(this, reader => { const h = this._collapsed.read(reader) ? 0 : this._contentHeight.read(reader); @@ -52,29 +54,21 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< } }); - private readonly _collapsed = observableValue(this, false); - - private readonly _elements = h('div', { - style: { - display: 'flex', - flexDirection: 'column', - } - }, [ - h('div', { + private readonly _elements = h('div.multiDiffEntry', [ + h('div.content', { style: { display: 'flex', flexDirection: 'column', - flex: '1', overflow: 'hidden', } }, [ - h('div@header', { style: { display: 'flex', alignItems: 'center', padding: '8px 5px', color: 'var(--vscode-foreground)', background: 'var(--vscode-editor-background)', zIndex: '10000' } }, [ - h('div.expand-button@collapseButton', { style: { margin: '0 5px' } }), - h('div.show-file-icons@title', { style: { fontSize: '14px', lineHeight: '22px' } }, [] as any), + h('div.header@header', [ + h('div.collapse-button@collapseButton'), + h('div.title.show-file-icons@title', [] as any), ]), - h('div', { + h('div.editorParent', { style: { flex: '1', display: 'flex', @@ -115,8 +109,11 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< btn.icon = this._collapsed.read(reader) ? Codicon.chevronRight : Codicon.chevronDown; })); this._register(btn.onDidClick(() => { - this._collapsed.set(!this._collapsed.get(), undefined); - this._elements.editor.style.display = this._collapsed.get() ? 'none' : 'block'; + this._viewModel.get()?.collapsed.set(!this._collapsed.get(), undefined); + })); + + this._register(autorun(reader => { + this._elements.editor.style.display = this._collapsed.read(reader) ? 'none' : 'block'; })); this.editor.getModifiedEditor().onDidLayoutChange(e => { @@ -152,9 +149,6 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< private readonly _dataStore = new DisposableStore(); public setData(data: TemplateData): void { - this._resourceLabel?.setUri(data.viewModel.model.modified.uri); - this._dataStore.clear(); - function updateOptions(options: IDiffEditorOptions): IDiffEditorOptions { return { ...options, @@ -166,20 +160,26 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< vertical: 'hidden', horizontal: 'hidden', handleMouseWheel: false, + useShadows: false, }, renderOverviewRuler: false, fixedOverflowWidgets: true, }; } - if (data.entry.onOptionsDidChange) { - this._dataStore.add(data.entry.onOptionsDidChange(() => { - this.editor.updateOptions(updateOptions(data.entry.options ?? {})); + const value = data.viewModel.entry.value!; // TODO + + if (value.onOptionsDidChange) { + this._dataStore.add(value.onOptionsDidChange(() => { + this.editor.updateOptions(updateOptions(value.options ?? {})); })); } globalTransaction(tx => { - this.editor.setModel(data.viewModel, tx); - this.editor.updateOptions(updateOptions(data.entry.options ?? {})); + this._resourceLabel?.setUri(data.viewModel.diffEditorViewModel.model.modified.uri); + this._dataStore.clear(); + this._viewModel.set(data.viewModel, tx); + this.editor.setModel(data.viewModel.diffEditorViewModel, tx); + this.editor.updateOptions(updateOptions(value.options ?? {})); }); } @@ -191,7 +191,8 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< this._elements.root.style.position = 'absolute'; // For sticky scroll - this._elements.header.style.transform = `translateY(${Math.max(0, Math.min(verticalRange.length - this._elements.header.clientHeight, viewPort.start - verticalRange.start))}px)`; + const delta = Math.max(0, Math.min(verticalRange.length - this._elements.header.clientHeight, viewPort.start - verticalRange.start)); + this._elements.header.style.transform = `translateY(${delta}px)`; globalTransaction(tx => { this.editor.layout({ @@ -200,6 +201,8 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< }); }); this.editor.getOriginalEditor().setScrollTop(editorScroll); + + this._elements.header.classList.toggle('shadow', delta > 0 || editorScroll > 0); } public hide(): void { diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts index 5bd8541e52a..35d669db764 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts @@ -20,7 +20,7 @@ export class MultiDiffEditorViewModel extends Disposable { constructor( private readonly _model: IMultiDiffEditorModel, - private readonly _diffEditorViewModelFactory: DiffEditorWidget + private readonly _diffEditorViewModelFactory: DiffEditorWidget, ) { super(); } @@ -29,6 +29,8 @@ export class MultiDiffEditorViewModel extends Disposable { export class DocumentDiffItemViewModel extends Disposable { public readonly diffEditorViewModel: IDiffEditorViewModel; + public readonly collapsed = observableValue(this, false); + constructor( public readonly entry: LazyPromise, diffEditorViewModelFactory: DiffEditorWidget diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts index ffe1aa9eb8c..e35b1101265 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts @@ -244,7 +244,7 @@ class VirtualizedViewItem extends Disposable { public render(verticalSpace: OffsetRange, offset: number, width: number, viewPort: OffsetRange): void { let ref = this._templateRef.get(); if (!ref) { - ref = this._objectPool.getUnusedObj(new TemplateData(this.viewModel.diffEditorViewModel, this.viewModel.entry.value!)); + ref = this._objectPool.getUnusedObj(new TemplateData(this.viewModel)); this._templateRef.set(ref, undefined); } ref.object.render(verticalSpace, width, offset, viewPort); diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/style.css b/src/vs/editor/browser/widget/multiDiffEditorWidget/style.css index 7206dda8cf6..246eeae0bec 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/style.css +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/style.css @@ -3,6 +3,33 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-component .expand-button a { +.monaco-component .multiDiffEntry { + display: flex; + flex-direction: column; +} + +.monaco-component .multiDiffEntry .collapse-button { + margin: 0 5px; +} + +.monaco-component .multiDiffEntry .collapse-button a { display: block; } + +.monaco-component .multiDiffEntry .header { + display: flex; + align-items: center; + padding: 8px 5px; + color: var(--vscode-foreground); + background: var(--vscode-editor-background); + z-index: 10000; +} + +.monaco-component .multiDiffEntry .header.shadow { + box-shadow: var(--vscode-scrollbar-shadow) 0px 6px 6px -6px; +} + +.monaco-component .multiDiffEntry .header .title { + font-size: 14px; + line-height: 22px; +} From 0ce333b482362ec70c47abc55cbc3a5901c0e804 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 20 Nov 2023 15:49:13 +0100 Subject: [PATCH 66/87] Fixes context menu issue --- src/vs/editor/browser/widget/multiDiffEditorWidget/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/style.css b/src/vs/editor/browser/widget/multiDiffEditorWidget/style.css index 246eeae0bec..75c8db7a956 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/style.css +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/style.css @@ -22,7 +22,7 @@ padding: 8px 5px; color: var(--vscode-foreground); background: var(--vscode-editor-background); - z-index: 10000; + z-index: 1000; } .monaco-component .multiDiffEntry .header.shadow { From 9cbc6ef29a36a7731f49bb44130472a9e7107d81 Mon Sep 17 00:00:00 2001 From: Anthony Stewart <150152+a-stewart@users.noreply.github.com> Date: Mon, 20 Nov 2023 18:21:52 +0100 Subject: [PATCH 67/87] Simplify `'solid' || ''` in viewPaneContainer.ts (#198515) Remove odd or in viewPaneContainer.ts --- src/vs/workbench/browser/parts/views/viewPaneContainer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index a185956cd94..a36b7a4de29 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -140,7 +140,7 @@ class ViewPaneDropOverlay extends Themable { this.overlay.style.outlineWidth = activeContrastBorderColor ? '2px' : ''; this.overlay.style.borderColor = activeContrastBorderColor || ''; - this.overlay.style.borderStyle = 'solid' || ''; + this.overlay.style.borderStyle = 'solid'; this.overlay.style.borderWidth = '0px'; } From 69ddb3f3c6bfeb0cd9736d3e74161e25a778c7e1 Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Mon, 20 Nov 2023 12:22:21 -0500 Subject: [PATCH 68/87] Write out `Cannot` (#198377) spelling: cannot Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- src/vs/workbench/contrib/chat/common/chatServiceImpl.ts | 2 +- test/smoke/src/main.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 9d447d5bbfc..93946df9488 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -558,7 +558,7 @@ export class ChatService extends Disposable implements IChatService { rawResponse = { session: model.session! }; } else { - throw new Error(`Can't handle request`); + throw new Error(`Cannot handle request`); } if (token.isCancellationRequested) { diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index ef9e04f39bf..cd964d010ca 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -193,7 +193,7 @@ if (!opts.web) { } if (!fs.existsSync(electronPath || '')) { - fail(`Can't find VSCode at ${electronPath}. Please run VSCode once first (scripts/code.sh, scripts\\code.bat) and try again.`); + fail(`Cannot find VSCode at ${electronPath}. Please run VSCode once first (scripts/code.sh, scripts\\code.bat) and try again.`); } quality = parseQuality(); @@ -213,7 +213,7 @@ else { if (typeof testCodeServerPath === 'string') { if (!fs.existsSync(testCodeServerPath)) { - fail(`Can't find Code server at ${testCodeServerPath}.`); + fail(`Cannot find Code server at ${testCodeServerPath}.`); } else { logger.log(`Running web smoke tests against ${testCodeServerPath}`); } @@ -326,7 +326,7 @@ async function ensureStableCode(): Promise { } if (!fs.existsSync(stableCodePath)) { - throw new Error(`Can't find Stable VSCode at ${stableCodePath}.`); + throw new Error(`Cannot find Stable VSCode at ${stableCodePath}.`); } logger.log(`Using stable build ${stableCodePath} for migration tests`); From 25ac184fe7ef01affae72d2b5d9ad47dcf56e446 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 20 Nov 2023 09:23:14 -0800 Subject: [PATCH 69/87] Fix notebook markdown list rendering (#198696) Fixes #197097 --- extensions/markdown-language-features/notebook/index.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/extensions/markdown-language-features/notebook/index.ts b/extensions/markdown-language-features/notebook/index.ts index d52d6b6b12a..22fe3119ad1 100644 --- a/extensions/markdown-language-features/notebook/index.ts +++ b/extensions/markdown-language-features/notebook/index.ts @@ -282,6 +282,15 @@ export const activate: ActivationFunction = (ctx) => { line-height: 1.357em; white-space: pre-wrap; } + + li p { + margin-bottom: 0.7em; + } + + ul, + ol { + margin-bottom: 0.7em; + } `; const template = document.createElement('template'); template.classList.add('markdown-style'); From 8a6f44ef3c6838cd1ddb00a506c4e58abdd2f57a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 20 Nov 2023 09:26:45 -0800 Subject: [PATCH 70/87] Move `matchesScheme` out of opener service (#198366) Move matchesScheme out of opener service This was creating extra deps on opener service --- src/vs/base/common/network.ts | 13 ++ .../editor/browser/services/openerService.ts | 4 +- .../services/languageFeatureDebounce.ts | 2 +- .../browser/services/openerService.test.ts | 2 +- src/vs/platform/opener/common/opener.ts | 13 -- .../workbench/api/common/extHost.api.impl.ts | 3 +- .../api/common/extHostApiCommands.ts | 3 +- .../browser/parts/editor/breadcrumbsModel.ts | 11 +- .../browser/parts/editor/editorCommands.ts | 74 ++++----- src/vs/workbench/browser/window.ts | 4 +- .../extensions/browser/extensionEditor.ts | 150 ++++++++++-------- .../view/renderers/backLayerWebView.ts | 22 +-- .../links/browser/terminalWordLinkDetector.ts | 2 +- .../url/browser/trustedDomainsValidator.ts | 24 +-- .../browser/gettingStarted.ts | 128 +++++++-------- .../services/url/browser/urlService.ts | 3 +- .../url/electron-sandbox/urlService.ts | 3 +- 17 files changed, 240 insertions(+), 221 deletions(-) diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index ad4808472c6..00ad27c3d55 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -5,6 +5,7 @@ import * as errors from 'vs/base/common/errors'; import * as platform from 'vs/base/common/platform'; +import { equalsIgnoreCase, startsWithIgnoreCase } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; export namespace Schemas { @@ -109,6 +110,18 @@ export namespace Schemas { export const vscodeSourceControl = 'vscode-scm'; } +export function matchesScheme(target: URI | string, scheme: string): boolean { + if (URI.isUri(target)) { + return equalsIgnoreCase(target.scheme, scheme); + } else { + return startsWithIgnoreCase(target, scheme + ':'); + } +} + +export function matchesSomeScheme(target: URI | string, ...schemes: string[]): boolean { + return schemes.some(scheme => matchesScheme(target, scheme)); +} + export const connectionTokenCookieName = 'vscode-tkn'; export const connectionTokenQueryName = 'tkn'; diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 5e63ede1f3f..23f8d13ce46 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -10,13 +10,13 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; import { ResourceMap } from 'vs/base/common/map'; import { parse } from 'vs/base/common/marshalling'; -import { Schemas } from 'vs/base/common/network'; +import { matchesScheme, matchesSomeScheme, Schemas } from 'vs/base/common/network'; import { normalizePath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { EditorOpenSource } from 'vs/platform/editor/common/editor'; -import { extractSelection, IExternalOpener, IExternalUriResolver, IOpener, IOpenerService, IResolvedExternalUri, IValidator, matchesScheme, matchesSomeScheme, OpenOptions, ResolveExternalUriOptions } from 'vs/platform/opener/common/opener'; +import { extractSelection, IExternalOpener, IExternalUriResolver, IOpener, IOpenerService, IResolvedExternalUri, IValidator, OpenOptions, ResolveExternalUriOptions } from 'vs/platform/opener/common/opener'; class CommandOpener implements IOpener { diff --git a/src/vs/editor/common/services/languageFeatureDebounce.ts b/src/vs/editor/common/services/languageFeatureDebounce.ts index e0d2cb832f3..e537b05bc82 100644 --- a/src/vs/editor/common/services/languageFeatureDebounce.ts +++ b/src/vs/editor/common/services/languageFeatureDebounce.ts @@ -12,7 +12,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; -import { matchesScheme } from 'vs/platform/opener/common/opener'; +import { matchesScheme } from 'vs/base/common/network'; export const ILanguageFeatureDebounceService = createDecorator('ILanguageFeatureDebounceService'); diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index 4f1b579d5e7..26dea91e681 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -11,7 +11,7 @@ import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { NullCommandService } from 'vs/platform/commands/test/common/nullCommandService'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; -import { matchesScheme, matchesSomeScheme } from 'vs/platform/opener/common/opener'; +import { matchesScheme, matchesSomeScheme } from 'vs/base/common/network'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; suite('OpenerService', function () { diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 1996a2c8b49..1f4d1d96b13 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -5,7 +5,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { equalsIgnoreCase, startsWithIgnoreCase } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IEditorOptions, ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -117,18 +116,6 @@ export interface IOpenerService { resolveExternalUri(resource: URI, options?: ResolveExternalUriOptions): Promise; } -export function matchesScheme(target: URI | string, scheme: string): boolean { - if (URI.isUri(target)) { - return equalsIgnoreCase(target.scheme, scheme); - } else { - return startsWithIgnoreCase(target, scheme + ':'); - } -} - -export function matchesSomeScheme(target: URI | string, ...schemes: string[]): boolean { - return schemes.some(scheme => matchesScheme(target, scheme)); -} - /** * Encodes selection into the `URI`. * diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index e2e6da5263d..ee5ec60e54b 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -7,7 +7,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { combinedDisposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; +import { Schemas, matchesScheme } from 'vs/base/common/network'; import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; @@ -18,7 +18,6 @@ import { ExtensionIdentifierSet, IExtensionDescription } from 'vs/platform/exten import * as files from 'vs/platform/files/common/files'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILogService, ILoggerService, LogLevel } from 'vs/platform/log/common/log'; -import { matchesScheme } from 'vs/platform/opener/common/opener'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; import { TelemetryTrustedValue } from 'vs/platform/telemetry/common/telemetryUtils'; import { EditSessionIdentityMatch } from 'vs/platform/workspace/common/editSessions'; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 7412cb9168a..62f9ebbf690 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -5,7 +5,7 @@ import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { VSBuffer } from 'vs/base/common/buffer'; -import { Schemas } from 'vs/base/common/network'; +import { Schemas, matchesSomeScheme } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange } from 'vs/editor/common/core/range'; @@ -13,7 +13,6 @@ import * as languages from 'vs/editor/common/languages'; import { decodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto'; import { validateWhenClauses } from 'vs/platform/contextkey/common/contextkey'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; -import { matchesSomeScheme } from 'vs/platform/opener/common/opener'; import { ICallHierarchyItemDto, IIncomingCallDto, IInlineValueContextDto, IOutgoingCallDto, IRawColorInfo, ITypeHierarchyItemDto, IWorkspaceEditDto } from 'vs/workbench/api/common/extHost.protocol'; import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { CustomCodeAction } from 'vs/workbench/api/common/extHostLanguageFeatures'; diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index c056eb29fb3..22f155f85c7 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -7,16 +7,15 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { isEqual, dirname } from 'vs/base/common/resources'; +import { Schemas, matchesSomeScheme } from 'vs/base/common/network'; +import { dirname, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { Schemas } from 'vs/base/common/network'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { FileKind } from 'vs/platform/files/common/files'; -import { IOutline, IOutlineService, OutlineTarget } from 'vs/workbench/services/outline/browser/outline'; +import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { IEditorPane } from 'vs/workbench/common/editor'; -import { matchesSomeScheme } from 'vs/platform/opener/common/opener'; +import { IOutline, IOutlineService, OutlineTarget } from 'vs/workbench/services/outline/browser/outline'; export class FileElement { constructor( diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index c7cee50d8c3..761e48edc6b 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -3,44 +3,44 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; -import { isObject, isString, isUndefined, isNumber } from 'vs/base/common/types'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IEditorIdentifier, IEditorCommandsContext, CloseDirection, IVisibleEditorPane, EditorsOrder, EditorInputCapabilities, isEditorIdentifier, isEditorInputWithOptionsAndGroup, IUntitledTextResourceEditorInput, IResourceDiffEditorInput } from 'vs/workbench/common/editor'; -import { TextCompareEditorVisibleContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, ActiveEditorStickyContext, ActiveEditorGroupLockedContext, ActiveEditorCanSplitInGroupContext, TextCompareEditorActiveContext, SideBySideEditorActiveContext } from 'vs/workbench/common/contextkeys'; -import { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { EditorGroupColumn, columnToEditorGroup } from 'vs/workbench/services/editor/common/editorGroupColumn'; -import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor'; -import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; -import { URI, UriComponents } from 'vs/base/common/uri'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { IListService, IOpenEvent } from 'vs/platform/list/browser/listService'; -import { List } from 'vs/base/browser/ui/list/listWidget'; -import { distinct, coalesce } from 'vs/base/common/arrays'; -import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout, isEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { CommandsRegistry, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands'; -import { MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; -import { Categories } from 'vs/platform/action/common/actionCommonCategories'; -import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; -import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; -import { EditorResolution, IEditorOptions, IResourceEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; -import { Schemas } from 'vs/base/common/network'; -import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; -import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; -import { IPathService } from 'vs/workbench/services/path/common/pathService'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { extname } from 'vs/base/common/resources'; -import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { isDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { getActiveElement } from 'vs/base/browser/dom'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import { coalesce, distinct } from 'vs/base/common/arrays'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Schemas, matchesScheme } from 'vs/base/common/network'; +import { extname } from 'vs/base/common/resources'; +import { isNumber, isObject, isString, isUndefined } from 'vs/base/common/types'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { isDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { localize } from 'vs/nls'; +import { Categories } from 'vs/platform/action/common/actionCommonCategories'; +import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { EditorResolution, IEditorOptions, IResourceEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IListService, IOpenEvent } from 'vs/platform/list/browser/listService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; +import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor'; +import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor'; +import { ActiveEditorCanSplitInGroupContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupLockedContext, ActiveEditorStickyContext, MultipleEditorGroupsContext, SideBySideEditorActiveContext, TextCompareEditorActiveContext, TextCompareEditorVisibleContext } from 'vs/workbench/common/contextkeys'; +import { CloseDirection, EditorInputCapabilities, EditorsOrder, IEditorCommandsContext, IEditorIdentifier, IResourceDiffEditorInput, IUntitledTextResourceEditorInput, IVisibleEditorPane, isEditorIdentifier, isEditorInputWithOptionsAndGroup } from 'vs/workbench/common/editor'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; +import { EditorGroupColumn, columnToEditorGroup } from 'vs/workbench/services/editor/common/editorGroupColumn'; +import { EditorGroupLayout, GroupDirection, GroupLocation, GroupsOrder, IEditorGroup, IEditorGroupsService, isEditorGroup, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; +import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; +import { IPathService } from 'vs/workbench/services/path/common/pathService'; +import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors'; export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup'; diff --git a/src/vs/workbench/browser/window.ts b/src/vs/workbench/browser/window.ts index 8c983240ded..c1ea5ce7b4a 100644 --- a/src/vs/workbench/browser/window.ts +++ b/src/vs/workbench/browser/window.ts @@ -10,7 +10,7 @@ import { HidDeviceData, requestHidDevice, requestSerialPort, requestUsbDevice, S import { timeout } from 'vs/base/common/async'; import { Event } from 'vs/base/common/event'; import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; +import { matchesScheme, Schemas } from 'vs/base/common/network'; import { isIOS, isMacintosh } from 'vs/base/common/platform'; import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; @@ -19,7 +19,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IDialogService, IPromptButton } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; -import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 8293c365d25..5b5b09c6966 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -3,74 +3,94 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/extensionEditor'; -import { localize } from 'vs/nls'; -import * as arrays from 'vs/base/common/arrays'; -import { OS, language } from 'vs/base/common/platform'; -import { Event, Emitter } from 'vs/base/common/event'; -import { Cache, CacheResult } from 'vs/base/common/cache'; -import { Action, IAction } from 'vs/base/common/actions'; -import { getErrorMessage, isCancellationError, onUnexpectedError } from 'vs/base/common/errors'; -import { dispose, toDisposable, Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; -import { append, $, join, addDisposableListener, setParentFlowTo, reset, Dimension } from 'vs/base/browser/dom'; -import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; -import { ExtensionIdentifier, IExtensionManifest, IKeyBinding, IView, IViewContainer } from 'vs/platform/extensions/common/extensions'; -import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { ResolvedKeybinding } from 'vs/base/common/keybindings'; -import { ExtensionsInput, IExtensionEditorOptions } from 'vs/workbench/contrib/extensions/common/extensionsInput'; -import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, IExtension, ExtensionContainers, ExtensionEditorTab, ExtensionState, IExtensionContainer } from 'vs/workbench/contrib/extensions/common/extensions'; -import { RatingsWidget, InstallCountWidget, RemoteBadgeWidget, ExtensionWidget, ExtensionStatusWidget, ExtensionRecommendationWidget, SponsorWidget, onClick, VerifiedPublisherWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; -import { IEditorOpenContext } from 'vs/workbench/common/editor'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { - UpdateAction, ReloadAction, EnableDropDownAction, DisableDropDownAction, ExtensionStatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, - RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ToggleSyncExtensionAction, SetProductIconThemeAction, - ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, UninstallAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, - InstallAnotherVersionAction, ExtensionEditorManageExtensionAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction, SkipUpdateAction -} from 'vs/workbench/contrib/extensions/browser/extensionsActions'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; -import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { ThemeIcon } from 'vs/base/common/themables'; -import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; -import { ContextKeyExpr, IContextKey, IContextKeyService, IScopedContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { Color } from 'vs/base/common/color'; -import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { ExtensionsTree, ExtensionData, ExtensionsGridView, getExtensions } from 'vs/workbench/contrib/extensions/browser/extensionsViewer'; -import { ShowCurrentReleaseNotesActionId } from 'vs/workbench/contrib/update/common/update'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry'; -import { isUndefined } from 'vs/base/common/types'; -import { IWebviewService, IWebview, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview'; -import { generateUuid } from 'vs/base/common/uuid'; -import { platform } from 'vs/base/common/process'; -import { URI } from 'vs/base/common/uri'; -import { Schemas } from 'vs/base/common/network'; -import { DEFAULT_MARKDOWN_STYLES, renderMarkdownDocument } from 'vs/workbench/contrib/markdown/browser/markdownDocumentRenderer'; -import { ILanguageService } from 'vs/editor/common/languages/language'; -import { TokenizationRegistry } from 'vs/editor/common/languages'; -import { generateTokensCSSForColorMap } from 'vs/editor/common/languages/supports/tokenization'; -import { buttonForeground, buttonHoverBackground, editorBackground, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; -import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { Delegate } from 'vs/workbench/contrib/extensions/browser/extensionsList'; +import { $, Dimension, addDisposableListener, append, join, reset, setParentFlowTo } from 'vs/base/browser/dom'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; -import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { errorIcon, infoIcon, preReleaseIcon, warningIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; -import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; -import { ViewContainerLocation } from 'vs/workbench/common/views'; -import { IExtensionGalleryService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { Action, IAction } from 'vs/base/common/actions'; +import * as arrays from 'vs/base/common/arrays'; +import { Cache, CacheResult } from 'vs/base/common/cache'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Color } from 'vs/base/common/color'; +import { getErrorMessage, isCancellationError, onUnexpectedError } from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ResolvedKeybinding } from 'vs/base/common/keybindings'; +import { Disposable, DisposableStore, MutableDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { Schemas, matchesScheme } from 'vs/base/common/network'; +import { OS, language } from 'vs/base/common/platform'; +import { platform } from 'vs/base/common/process'; import * as semver from 'vs/base/common/semver/semver'; +import { ThemeIcon } from 'vs/base/common/themables'; +import { isUndefined } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; +import 'vs/css!./media/extensionEditor'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { TokenizationRegistry } from 'vs/editor/common/languages'; +import { ILanguageService } from 'vs/editor/common/languages/language'; +import { generateTokensCSSForColorMap } from 'vs/editor/common/languages/supports/tokenization'; +import { localize } from 'vs/nls'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry'; +import { ContextKeyExpr, IContextKey, IContextKeyService, IScopedContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IExtensionGalleryService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { ExtensionIdentifier, IExtensionManifest, IKeyBinding, IView, IViewContainer } from 'vs/platform/extensions/common/extensions'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { defaultKeybindingLabelStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { buttonForeground, buttonHoverBackground, editorBackground, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; +import { IEditorOpenContext } from 'vs/workbench/common/editor'; +import { ViewContainerLocation } from 'vs/workbench/common/views'; +import { + ActionWithDropDownAction, + ClearLanguageAction, + DisableDropDownAction, + EnableDropDownAction, + ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, + ExtensionEditorManageExtensionAction, + ExtensionStatusAction, + ExtensionStatusLabelAction, + InstallAnotherVersionAction, + InstallDropdownAction, InstallingLabelAction, + LocalInstallAction, + MigrateDeprecatedExtensionAction, + ReloadAction, + RemoteInstallAction, + SetColorThemeAction, + SetFileIconThemeAction, + SetLanguageAction, + SetProductIconThemeAction, + SkipUpdateAction, + SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, + ToggleSyncExtensionAction, + UninstallAction, + UpdateAction, + WebInstallAction +} from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { errorIcon, infoIcon, preReleaseIcon, warningIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; +import { Delegate } from 'vs/workbench/contrib/extensions/browser/extensionsList'; +import { ExtensionData, ExtensionsGridView, ExtensionsTree, getExtensions } from 'vs/workbench/contrib/extensions/browser/extensionsViewer'; +import { ExtensionRecommendationWidget, ExtensionStatusWidget, ExtensionWidget, InstallCountWidget, RatingsWidget, RemoteBadgeWidget, SponsorWidget, VerifiedPublisherWidget, onClick } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; +import { ExtensionContainers, ExtensionEditorTab, ExtensionState, IExtension, IExtensionContainer, IExtensionsViewPaneContainer, IExtensionsWorkbenchService, VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; +import { ExtensionsInput, IExtensionEditorOptions } from 'vs/workbench/contrib/extensions/common/extensionsInput'; +import { DEFAULT_MARKDOWN_STYLES, renderMarkdownDocument } from 'vs/workbench/contrib/markdown/browser/markdownDocumentRenderer'; +import { ShowCurrentReleaseNotesActionId } from 'vs/workbench/contrib/update/common/update'; +import { IWebview, IWebviewService, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; class NavBar extends Disposable { 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 c2151962a4d..4c559a6d359 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -3,15 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as osPath from 'vs/base/common/path'; +import { getWindow } from 'vs/base/browser/dom'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; import { coalesce } from 'vs/base/common/arrays'; import { DeferredPromise, runWhenGlobalIdle } from 'vs/base/common/async'; import { decodeBase64 } from 'vs/base/common/buffer'; import { Emitter, Event } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { getExtensionForMimeType } from 'vs/base/common/mime'; -import { FileAccess, Schemas } from 'vs/base/common/network'; +import { FileAccess, Schemas, matchesScheme, matchesSomeScheme } from 'vs/base/common/network'; import { equals } from 'vs/base/common/objects'; +import * as osPath from 'vs/base/common/path'; import { isMacintosh, isWeb } from 'vs/base/common/platform'; import { dirname, extname, isEqual, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; @@ -26,13 +29,16 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IFileService } from 'vs/platform/files/common/files'; -import { IOpenerService, matchesScheme, matchesSomeScheme } from 'vs/platform/opener/common/opener'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { editorFindMatch, editorFindMatchHighlight } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { CellEditState, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IDisplayOutputViewModel, IFocusNotebookCellOptions, IGenericCellViewModel, IInsetRenderOutput, INotebookEditorCreationOptions, INotebookWebviewMessage, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NOTEBOOK_WEBVIEW_BOUNDARY } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; import { preloadsScriptStr } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads'; @@ -40,6 +46,7 @@ import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { CellUri, ICellOutput, INotebookRendererInfo, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookLoggingService } from 'vs/workbench/contrib/notebook/common/notebookLoggingService'; import { IScopedRendererMessaging } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IWebviewElement, IWebviewService, WebviewContentPurpose, WebviewOriginStore } from 'vs/workbench/contrib/webview/browser/webview'; @@ -47,15 +54,8 @@ import { WebviewWindowDragMonitor } from 'vs/workbench/contrib/webview/browser/w import { asWebviewUri, webviewGenericCspSource } from 'vs/workbench/contrib/webview/common/webview'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { FromWebviewMessage, IAckOutputHeight, IClickedDataUrlMessage, ICodeBlockHighlightRequest, IContentWidgetTopRequest, IControllerPreload, ICreationContent, ICreationRequestMessage, IFindMatch, IMarkupCellInitialization, RendererMetadata, StaticPreloadMetadata, ToWebviewMessage } from './webviewMessages'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; -import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; -import { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { INotebookLoggingService } from 'vs/workbench/contrib/notebook/common/notebookLoggingService'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; -import { getWindow } from 'vs/base/browser/dom'; +import { FromWebviewMessage, IAckOutputHeight, IClickedDataUrlMessage, ICodeBlockHighlightRequest, IContentWidgetTopRequest, IControllerPreload, ICreationContent, ICreationRequestMessage, IFindMatch, IMarkupCellInitialization, RendererMetadata, StaticPreloadMetadata, ToWebviewMessage } from './webviewMessages'; const LINE_COLUMN_REGEX = /:([\d]+)(?::([\d]+))?$/; const LineQueryRegex = /line=(\d+)$/; diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalWordLinkDetector.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalWordLinkDetector.ts index 3285042999a..1675d0fac73 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalWordLinkDetector.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalWordLinkDetector.ts @@ -7,7 +7,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { matchesScheme } from 'vs/platform/opener/common/opener'; +import { matchesScheme } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { ITerminalSimpleLink, ITerminalLinkDetector, TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; diff --git a/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts b/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts index 20cb4e67a50..57fc3951b07 100644 --- a/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts +++ b/src/vs/workbench/contrib/url/browser/trustedDomainsValidator.ts @@ -3,28 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Schemas } from 'vs/base/common/network'; +import { WindowIdleValue } from 'vs/base/browser/dom'; +import { mainWindow } from 'vs/base/browser/window'; +import { Schemas, matchesScheme } from 'vs/base/common/network'; import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IOpenerService, matchesScheme, OpenOptions } from 'vs/platform/opener/common/opener'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IOpenerService, OpenOptions } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { configureOpenerTrustedDomainsHandler, readAuthenticationTrustedDomains, readStaticTrustedDomains, readWorkspaceTrustedDomains } from 'vs/workbench/contrib/url/browser/trustedDomains'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { testUrlMatchesGlob } from 'vs/workbench/contrib/url/common/urlGlob'; -import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { mainWindow } from 'vs/base/browser/window'; -import { WindowIdleValue } from 'vs/base/browser/dom'; +import { IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export class OpenerValidatorContributions implements IWorkbenchContribution { diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts index 5fd3578b39f..04ddd988865 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts @@ -3,77 +3,77 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors'; -import 'vs/css!./media/gettingStarted'; -import { localize } from 'vs/nls'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IEditorSerializer, IEditorOpenContext } from 'vs/workbench/common/editor'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { assertIsDefined } from 'vs/base/common/types'; -import { $, addDisposableListener, append, clearNode, Dimension, getWindow, reset } from 'vs/base/browser/dom'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IProductService } from 'vs/platform/product/common/productService'; -import { hiddenEntriesConfigurationKey, IResolvedWalkthrough, IResolvedWalkthroughStep, IWalkthroughsService } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ThemeIcon } from 'vs/base/common/themables'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { firstSessionDateStorageKey, ITelemetryService, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; +import { $, Dimension, addDisposableListener, append, clearNode, getWindow, reset } from 'vs/base/browser/dom'; +import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { gettingStartedCheckedCodicon, gettingStartedUncheckedCodicon } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedIcons'; -import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; -import { URI } from 'vs/base/common/uri'; -import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; -import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; +import { coalesce, equals, flatten } from 'vs/base/common/arrays'; +import { Delayer, Throttler } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { Codicon } from 'vs/base/common/codicons'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { splitRecentLabel } from 'vs/base/common/labels'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { ILink, LinkedText } from 'vs/base/common/linkedText'; +import { parse } from 'vs/base/common/marshalling'; +import { Schemas, matchesScheme } from 'vs/base/common/network'; +import { isMacintosh } from 'vs/base/common/platform'; +import { IFeaturedExtension } from 'vs/base/common/product'; +import { ThemeIcon } from 'vs/base/common/themables'; +import { assertIsDefined } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; +import 'vs/css!./media/gettingStarted'; +import { ILanguageService } from 'vs/editor/common/languages/language'; +import { MarkdownRenderer } from 'vs/editor/contrib/markdownRenderer/browser/markdownRenderer'; +import { localize } from 'vs/nls'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr, ContextKeyExpression, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IRecentFolder, IRecentlyOpened, IRecentWorkspace, isRecentFolder, isRecentWorkspace, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; -import { IWorkspaceContextService, UNKNOWN_EMPTY_WINDOW_WORKSPACE } from 'vs/platform/workspace/common/workspace'; -import { ILabelService, Verbosity } from 'vs/platform/label/common/label'; -import { IWindowOpenable } from 'vs/platform/window/common/window'; -import { splitRecentLabel } from 'vs/base/common/labels'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { isMacintosh } from 'vs/base/common/platform'; -import { Delayer, Throttler } from 'vs/base/common/async'; -import { GettingStartedInput } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedInput'; -import { GroupDirection, GroupsOrder, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { ILink, LinkedText } from 'vs/base/common/linkedText'; -import { Button } from 'vs/base/browser/ui/button/button'; -import { Link } from 'vs/platform/opener/browser/link'; -import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; -import { IWebviewElement, IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; -import { ILanguageService } from 'vs/editor/common/languages/language'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { generateUuid } from 'vs/base/common/uuid'; -import { IFileService } from 'vs/platform/files/common/files'; -import { parse } from 'vs/base/common/marshalling'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { Schemas } from 'vs/base/common/network'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; -import { coalesce, equals, flatten } from 'vs/base/common/arrays'; -import { ThemeSettings } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { startEntries } from 'vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent'; -import { MarkdownRenderer } from 'vs/editor/contrib/markdownRenderer/browser/markdownRenderer'; -import { GettingStartedIndexList } from './gettingStartedList'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { getTelemetryLevel } from 'vs/platform/telemetry/common/telemetryUtils'; -import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; -import { OpenFolderAction, OpenFileFolderAction, OpenFolderViaWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; -import { OpenRecentAction } from 'vs/workbench/browser/actions/windowActions'; -import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; -import { Codicon } from 'vs/base/common/codicons'; -import { restoreWalkthroughsConfigurationKey, RestoreWalkthroughsConfigurationValue } from 'vs/workbench/contrib/welcomeGettingStarted/browser/startupPage'; -import { GettingStartedDetailsRenderer } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedDetailsRenderer'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; -import { defaultButtonStyles, defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; -import { IFeaturedExtensionsService } from 'vs/workbench/contrib/welcomeGettingStarted/browser/featuredExtensionService'; -import { IFeaturedExtension } from 'vs/base/common/product'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { onUnexpectedError } from 'vs/base/common/errors'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ILabelService, Verbosity } from 'vs/platform/label/common/label'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { Link } from 'vs/platform/opener/browser/link'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { ITelemetryService, TelemetryLevel, firstSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { getTelemetryLevel } from 'vs/platform/telemetry/common/telemetryUtils'; +import { defaultButtonStyles, defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWindowOpenable } from 'vs/platform/window/common/window'; +import { IWorkspaceContextService, UNKNOWN_EMPTY_WINDOW_WORKSPACE } from 'vs/platform/workspace/common/workspace'; +import { IRecentFolder, IRecentWorkspace, IRecentlyOpened, IWorkspacesService, isRecentFolder, isRecentWorkspace } from 'vs/platform/workspaces/common/workspaces'; +import { OpenRecentAction } from 'vs/workbench/browser/actions/windowActions'; +import { OpenFileFolderAction, OpenFolderAction, OpenFolderViaWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; +import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; +import { IEditorOpenContext, IEditorSerializer } from 'vs/workbench/common/editor'; +import { IWebviewElement, IWebviewService } from 'vs/workbench/contrib/webview/browser/webview'; +import { IFeaturedExtensionsService } from 'vs/workbench/contrib/welcomeGettingStarted/browser/featuredExtensionService'; +import 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors'; +import { GettingStartedDetailsRenderer } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedDetailsRenderer'; +import { gettingStartedCheckedCodicon, gettingStartedUncheckedCodicon } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedIcons'; +import { GettingStartedInput } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedInput'; +import { IResolvedWalkthrough, IResolvedWalkthroughStep, IWalkthroughsService, hiddenEntriesConfigurationKey } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService'; +import { RestoreWalkthroughsConfigurationValue, restoreWalkthroughsConfigurationKey } from 'vs/workbench/contrib/welcomeGettingStarted/browser/startupPage'; +import { startEntries } from 'vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent'; +import { GroupDirection, GroupsOrder, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { ThemeSettings } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { GettingStartedIndexList } from './gettingStartedList'; const SLIDE_TRANSITION_TIME_MS = 250; const configurationKey = 'workbench.startupEditor'; diff --git a/src/vs/workbench/services/url/browser/urlService.ts b/src/vs/workbench/services/url/browser/urlService.ts index 602d6d32c6b..5c1b1872096 100644 --- a/src/vs/workbench/services/url/browser/urlService.ts +++ b/src/vs/workbench/services/url/browser/urlService.ts @@ -9,7 +9,8 @@ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/ import { AbstractURLService } from 'vs/platform/url/common/urlService'; import { Event } from 'vs/base/common/event'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; -import { IOpenerService, IOpener, OpenExternalOptions, OpenInternalOptions, matchesScheme } from 'vs/platform/opener/common/opener'; +import { IOpenerService, IOpener, OpenExternalOptions, OpenInternalOptions } from 'vs/platform/opener/common/opener'; +import { matchesScheme } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; export interface IURLCallbackProvider { diff --git a/src/vs/workbench/services/url/electron-sandbox/urlService.ts b/src/vs/workbench/services/url/electron-sandbox/urlService.ts index 6e3662033e6..f8ec96c4998 100644 --- a/src/vs/workbench/services/url/electron-sandbox/urlService.ts +++ b/src/vs/workbench/services/url/electron-sandbox/urlService.ts @@ -7,7 +7,8 @@ import { IURLService, IURLHandler, IOpenURLOptions } from 'vs/platform/url/commo import { URI, UriComponents } from 'vs/base/common/uri'; import { IMainProcessService } from 'vs/platform/ipc/common/mainProcessService'; import { URLHandlerChannel } from 'vs/platform/url/common/urlIpc'; -import { IOpenerService, IOpener, matchesScheme } from 'vs/platform/opener/common/opener'; +import { IOpenerService, IOpener } from 'vs/platform/opener/common/opener'; +import { matchesScheme } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; From dd2a645bcfacc4310e569d7f6e8a548a31de1fec Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 20 Nov 2023 11:50:15 -0600 Subject: [PATCH 71/87] Show detected vulnerabilities in chat codeblocks (#198595) * Add vulnerability annotations for chat * Make vulnerabilities label a Button * Update test snapshots --- .../api/common/extHostTypeConverters.ts | 5 ++ .../contrib/chat/browser/chatListRenderer.ts | 17 +++-- .../chatMarkdownDecorationsRenderer.ts | 45 +++++++++++-- .../contrib/chat/browser/codeBlockPart.css | 58 ++++++++++++++++- .../contrib/chat/browser/codeBlockPart.ts | 65 +++++++++++++++++-- .../contrib/chat/common/chatModel.ts | 18 +++-- .../contrib/chat/common/chatService.ts | 17 +++++ .../contrib/chat/common/chatViewModel.ts | 11 +++- ...ctVulnerabilitiesFromText_multiline.0.snap | 11 ++++ ...ctVulnerabilitiesFromText_multiline.1.snap | 15 +++++ ...nerabilitiesFromText_multiple_vulns.0.snap | 11 ++++ ...nerabilitiesFromText_multiple_vulns.1.snap | 25 +++++++ ...VulnerabilitiesFromText_single_line.0.snap | 11 ++++ ...VulnerabilitiesFromText_single_line.1.snap | 15 +++++ .../chatMarkdownDecorationsRenderer.test.ts | 61 +++++++++++++++++ .../vscode.proposed.chatAgents2Additions.d.ts | 10 +++ 16 files changed, 371 insertions(+), 24 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiline.0.snap create mode 100644 src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiline.1.snap create mode 100644 src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiple_vulns.0.snap create mode 100644 src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiple_vulns.1.snap create mode 100644 src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_single_line.0.snap create mode 100644 src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_single_line.1.snap create mode 100644 src/vs/workbench/contrib/chat/test/browser/chatMarkdownDecorationsRenderer.test.ts diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 3079d146530..e66f47c9010 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2302,6 +2302,11 @@ export namespace ChatResponseProgress { checkProposedApiEnabled(extension, 'chatAgents2Additions'); return { content: MarkdownString.from(progress.markdownContent), kind: 'markdownContent' }; } else if ('content' in progress) { + if ('vulnerability' in progress && progress.vulnerability) { + checkProposedApiEnabled(extension, 'chatAgents2Additions'); + return { content: progress.content, title: progress.vulnerability.title, description: progress.vulnerability!.description, kind: 'vulnerability' }; + } + if (typeof progress.content === 'string') { return { content: progress.content, kind: 'content' }; } diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 26b1ab5ebab..dac8c2a0423 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -50,14 +50,14 @@ import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibil import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { ChatTreeItem, IChatCodeBlockInfo, IChatFileTreeInfo } from 'vs/workbench/contrib/chat/browser/chat'; import { ChatFollowups } from 'vs/workbench/contrib/chat/browser/chatFollowups'; -import { convertParsedRequestToMarkdown, reduceInlineContentReferences, walkTreeAndAnnotateReferenceLinks } from 'vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer'; +import { annotateSpecialMarkdownContent, convertParsedRequestToMarkdown, extractVulnerabilitiesFromText, walkTreeAndAnnotateReferenceLinks } from 'vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer'; import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions'; import { CodeBlockPart, ICodeBlockData, ICodeBlockPart } from 'vs/workbench/contrib/chat/browser/codeBlockPart'; import { IChatAgentMetadata } from 'vs/workbench/contrib/chat/common/chatAgents'; import { CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING, CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys'; -import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel'; +import { IChatProgressRenderableResponseContent } from 'vs/workbench/contrib/chat/common/chatModel'; import { chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChatContentInlineReference, IChatContentReference, IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatContentReference, IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, 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'; import { createFileIconThemableTreeContainerScope } from 'vs/workbench/contrib/files/browser/views/explorerView'; @@ -331,7 +331,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer>, element: ChatTreeItem, index: number, templateData: IChatListItemTemplate) { + private basicRenderElement(value: ReadonlyArray, element: ChatTreeItem, index: number, templateData: IChatListItemTemplate) { const fillInIncompleteTokens = isResponseVM(element) && (!element.isComplete || element.isCanceled || element.errorDetails?.responseIsFiltered || element.errorDetails?.responseIsIncomplete); dom.clearNode(templateData.value); @@ -535,7 +535,8 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { + const vulns = extractVulnerabilitiesFromText(text); + const hideToolbar = isResponseVM(element) && element.errorDetails?.responseIsFiltered; - const data = { languageId, text, codeBlockIndex: codeBlockIndex++, element, hideToolbar, parentContextKeyService: templateData.contextKeyService }; + const data = { languageId, text: vulns.newText, codeBlockIndex: codeBlockIndex++, element, hideToolbar, parentContextKeyService: templateData.contextKeyService, vulns: vulns.vulnerabilities }; const ref = this.renderCodeBlock(data, disposables); // Attach this after updating text/layout of the editor, so it should only be fired when the size updates later (horizontal scrollbar, wrapping) diff --git a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts index 0015e1cf339..b630cba32d2 100644 --- a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts @@ -8,10 +8,11 @@ import { MarkdownString } from 'vs/base/common/htmlContent'; import { revive } from 'vs/base/common/marshalling'; import { basename } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; +import { IRange } from 'vs/editor/common/core/range'; import { Location } from 'vs/editor/common/languages'; -import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel'; +import { IChatProgressRenderableResponseContent, IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel'; import { ChatRequestTextPart, IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChatContentInlineReference } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatAgentMarkdownContentWithVulnerability, IChatContentInlineReference } from 'vs/workbench/contrib/chat/common/chatService'; const variableRefUrl = 'http://_vscodedecoration_'; @@ -58,10 +59,39 @@ function renderFileWidget(href: string, a: HTMLAnchorElement): void { a.setAttribute('data-href', location.uri.with({ fragment }).toString()); } +export interface IMarkdownVulnerability { + title: string; + description: string; + range: IRange; +} + +export function extractVulnerabilitiesFromText(text: string): { newText: string; vulnerabilities: IMarkdownVulnerability[] } { + const vulnerabilities: IMarkdownVulnerability[] = []; + let newText = text; + let match: RegExpExecArray | null; + while ((match = /(.*?)<\/vscode_annotation>/ms.exec(newText)) !== null) { + const [full, title, description, content] = match; + const start = match.index; + const textBefore = newText.substring(0, start); + const linesBefore = textBefore.split('\n').length - 1; + const linesInside = content.split('\n').length - 1; + + const previousNewlineIdx = textBefore.lastIndexOf('\n'); + const startColumn = start - (previousNewlineIdx + 1) + 1; + const endPreviousNewlineIdx = (textBefore + content).lastIndexOf('\n'); + const endColumn = start + content.length - (endPreviousNewlineIdx + 1) + 1; + + vulnerabilities.push({ title: decodeURIComponent(title), description: decodeURIComponent(description), range: { startLineNumber: linesBefore + 1, startColumn, endLineNumber: linesBefore + linesInside + 1, endColumn } }); + newText = newText.substring(0, start) + content + newText.substring(start + full.length); + } + + return { newText, vulnerabilities }; +} + const contentRefUrl = 'http://_vscodecontentref_'; // must be lowercase for URI -export function reduceInlineContentReferences(response: ReadonlyArray): ReadonlyArray> { - const result: Exclude[] = []; +export function annotateSpecialMarkdownContent(response: ReadonlyArray): ReadonlyArray { + const result: Exclude[] = []; for (const item of response) { const previousItem = result[result.length - 1]; if (item.kind === 'inlineReference') { @@ -75,6 +105,13 @@ export function reduceInlineContentReferences(response: ReadonlyArray${item.content.value}`; + if (previousItem?.kind === 'markdownContent') { + result[result.length - 1] = { content: new MarkdownString(previousItem.content.value + markdownText, { isTrusted: previousItem.content.isTrusted }), kind: 'markdownContent' }; + } else { + result.push({ content: new MarkdownString(markdownText), kind: 'markdownContent' }); + } } else { result.push(item); } diff --git a/src/vs/workbench/contrib/chat/browser/codeBlockPart.css b/src/vs/workbench/contrib/chat/browser/codeBlockPart.css index cfda6c03a44..5c778eb2601 100644 --- a/src/vs/workbench/contrib/chat/browser/codeBlockPart.css +++ b/src/vs/workbench/contrib/chat/browser/codeBlockPart.css @@ -44,11 +44,12 @@ margin: 16px 0; } -.interactive-result-code-block .interactive-result-editor .monaco-editor { +.interactive-result-code-block { border: 1px solid var(--vscode-input-border, transparent); + background-color: var(--vscode-interactive-result-editor-background-color); } -.interactive-result-code-block .interactive-result-editor .monaco-editor.focused { +.interactive-result-code-block:has(.monaco-editor.focused) { border-color: var(--vscode-focusBorder, transparent); } @@ -57,3 +58,56 @@ .interactive-result-code-block .monaco-editor .overflow-guard { border-radius: 4px; } + +.interactive-result-code-block .interactive-result-vulns { + font-size: 0.9em; + padding: 0px 8px 2px 8px; +} + +.interactive-result-code-block .interactive-result-vulns-header { + display: flex; + height: 22px; +} + +.interactive-result-code-block .interactive-result-vulns-header, +.interactive-result-code-block .interactive-result-vulns-list { + opacity: 0.8; +} + +.interactive-result-code-block .interactive-result-vulns-list { + margin: 0px; + padding-bottom: 3px; + padding-left: 16px !important; /* Override markdown styles */ +} + +.interactive-result-code-block.chat-vulnerabilities-collapsed .interactive-result-vulns-list { + display: none; +} + +.interactive-result-code-block .interactive-result-vulns-list .chat-vuln-title { + font-weight: bold; +} + +.interactive-result-code-block.no-vulns .interactive-result-vulns { + display: none; +} + +.interactive-result-code-block .interactive-result-vulns-header .monaco-button { + /* unset Button styles */ + display: inline-flex; + width: 100%; + border: none; + padding: 0; + text-align: initial; + justify-content: initial; + color: var(--vscode-foreground) !important; /* This is inside .rendered-markdown */ + user-select: none; +} + +.interactive-result-code-block .interactive-result-vulns-header .monaco-text-button:focus { + outline: none; +} + +.interactive-result-code-block .interactive-result-vulns-header .monaco-text-button:focus-visible { + outline: 1px solid var(--vscode-focusBorder); +} diff --git a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts index fd5350bf367..7f55edcb93b 100644 --- a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts +++ b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts @@ -9,6 +9,8 @@ import * as dom from 'vs/base/browser/dom'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { Codicon } from 'vs/base/common/codicons'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; @@ -31,14 +33,15 @@ 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 { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { IMarkdownVulnerability } from 'vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer'; import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions'; +import { IChatResponseViewModel, isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreventer'; import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; const $ = dom.$; - export interface ICodeBlockData { text: string; languageId: string; @@ -46,6 +49,7 @@ export interface ICodeBlockData { element: unknown; parentContextKeyService?: IContextKeyService; hideToolbar?: boolean; + vulns?: IMarkdownVulnerability[]; } export interface ICodeBlockActionContext { @@ -57,7 +61,7 @@ export interface ICodeBlockActionContext { export interface ICodeBlockPart { - readonly onDidChangeContentHeight: Event; + readonly onDidChangeContentHeight: Event; readonly element: HTMLElement; readonly textModel: ITextModel; layout(width: number): void; @@ -69,16 +73,20 @@ export interface ICodeBlockPart { const defaultCodeblockPadding = 10; export class CodeBlockPart extends Disposable implements ICodeBlockPart { - private readonly _onDidChangeContentHeight = this._register(new Emitter()); + private readonly _onDidChangeContentHeight = this._register(new Emitter()); public readonly onDidChangeContentHeight = this._onDidChangeContentHeight.event; private readonly editor: CodeEditorWidget; private readonly toolbar: MenuWorkbenchToolBar; private readonly contextKeyService: IContextKeyService; + private readonly vulnsButton: Button; + private readonly vulnsListElement: HTMLElement; + public readonly textModel: ITextModel; public readonly element: HTMLElement; + private currentCodeBlockData: ICodeBlockData | undefined; private currentScrollWidth = 0; constructor( @@ -134,6 +142,32 @@ export class CodeBlockPart extends Disposable implements ICodeBlockPart { shouldForwardArgs: true } })); + + const vulnsContainer = dom.append(this.element, $('.interactive-result-vulns')); + const vulnsHeaderElement = dom.append(vulnsContainer, $('.interactive-result-vulns-header', undefined)); + this.vulnsButton = new Button(vulnsHeaderElement, { + buttonBackground: undefined, + buttonBorder: undefined, + buttonForeground: undefined, + buttonHoverBackground: undefined, + buttonSecondaryBackground: undefined, + buttonSecondaryForeground: undefined, + buttonSecondaryHoverBackground: undefined, + buttonSeparator: undefined, + supportIcons: true + }); + + this.vulnsListElement = dom.append(vulnsContainer, $('ul.interactive-result-vulns-list')); + + this.vulnsButton.onDidClick(() => { + const element = this.currentCodeBlockData!.element as IChatResponseViewModel; + element.vulnerabilitiesListExpanded = !element.vulnerabilitiesListExpanded; + this.vulnsButton.label = this.getVulnerabilitiesLabel(); + this.element.classList.toggle('chat-vulnerabilities-collapsed', !element.vulnerabilitiesListExpanded); + this._onDidChangeContentHeight.fire(); + // this.updateAriaLabel(collapseButton.element, referencesLabel, element.usedReferencesExpanded); + }); + this._register(this.toolbar.onDidChangeDropdownVisibility(e => { toolbarElement.classList.toggle('force-visibility', e); })); @@ -155,7 +189,7 @@ export class CodeBlockPart extends Disposable implements ICodeBlockPart { })); this._register(this.editor.onDidContentSizeChange(e => { if (e.contentHeightChanged) { - this._onDidChangeContentHeight.fire(e.contentHeight); + this._onDidChangeContentHeight.fire(); } })); this._register(this.editor.onDidBlurEditorWidget(() => { @@ -220,6 +254,7 @@ export class CodeBlockPart extends Disposable implements ICodeBlockPart { render(data: ICodeBlockData, width: number): void { + this.currentCodeBlockData = data; if (data.parentContextKeyService) { this.contextKeyService.updateParent(data.parentContextKeyService); } @@ -250,6 +285,28 @@ export class CodeBlockPart extends Disposable implements ICodeBlockPart { } else { dom.show(this.toolbar.getElement()); } + + if (data.vulns?.length && isResponseVM(data.element)) { + dom.clearNode(this.vulnsListElement); + this.element.classList.remove('no-vulns'); + this.element.classList.toggle('chat-vulnerabilities-collapsed', !data.element.vulnerabilitiesListExpanded); + dom.append(this.vulnsListElement, ...data.vulns.map(v => $('li', undefined, $('span.chat-vuln-title', undefined, v.title), ' ' + v.description))); + this.vulnsButton.label = this.getVulnerabilitiesLabel(); + } else { + this.element.classList.add('no-vulns'); + } + } + + private getVulnerabilitiesLabel(): string { + if (!this.currentCodeBlockData || !this.currentCodeBlockData.vulns) { + return ''; + } + + const referencesLabel = this.currentCodeBlockData.vulns.length > 1 ? + localize('vulnerabilitiesPlural', "{0} vulnerabilities", this.currentCodeBlockData.vulns.length) : + localize('vulnerabilitiesSingular', "{0} vulnerability", 1); + const icon = (element: IChatResponseViewModel) => element.vulnerabilitiesListExpanded ? Codicon.chevronDown : Codicon.chevronRight; + return `${referencesLabel} $(${icon(this.currentCodeBlockData.element as IChatResponseViewModel).id})`; } private fixCodeText(text: string, languageId: string): string { diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index d4617660efe..217fa004ea4 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -16,7 +16,7 @@ import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { ILogService } from 'vs/platform/log/common/log'; import { IChatAgentCommand, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChat, IChatAsyncContent, IChatContent, IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatProgress, IChatProgressMessage, IChatReplyFollowup, IChatResponse, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatTreeData, IChatUsedContext, InteractiveSessionVoteDirection, isIUsedContext } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChat, IChatAgentMarkdownContentWithVulnerability, IChatAsyncContent, IChatContent, IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatProgress, IChatProgressMessage, IChatReplyFollowup, IChatResponse, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatTreeData, IChatUsedContext, InteractiveSessionVoteDirection, isIUsedContext } from 'vs/workbench/contrib/chat/common/chatService'; export interface IChatRequestModel { readonly id: string; @@ -29,10 +29,13 @@ export interface IChatRequestModel { export type IChatProgressResponseContent = | IChatMarkdownContent + | IChatAgentMarkdownContentWithVulnerability | IChatTreeData | IChatAsyncContent | IChatContentInlineReference; +export type IChatProgressRenderableResponseContent = Exclude; + export interface IResponse { readonly value: ReadonlyArray; asString(): string; @@ -100,7 +103,7 @@ export class Response implements IResponse { return this._responseParts; } - constructor(value: IMarkdownString | ReadonlyArray) { + constructor(value: IMarkdownString | ReadonlyArray) { this._responseParts = asArray(value).map((v) => (isMarkdownString(v) ? { content: v, kind: 'markdownContent' } satisfies IChatMarkdownContent : 'kind' in v ? v : { kind: 'treeData', treeData: v })); @@ -155,7 +158,7 @@ export class Response implements IResponse { } this._updateRepr(quiet); }); - } else if (progress.kind === 'treeData' || progress.kind === 'inlineReference') { + } else if (progress.kind === 'treeData' || progress.kind === 'inlineReference' || progress.kind === 'markdownVuln') { this._responseParts.push(progress); this._updateRepr(quiet); } @@ -256,7 +259,7 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel } constructor( - _response: IMarkdownString | ReadonlyArray, + _response: IMarkdownString | ReadonlyArray, public readonly session: ChatModel, agent: IChatAgentData | undefined, public readonly requestId: string, @@ -352,7 +355,7 @@ export type ISerializableChatAgentData = UriDto; export interface ISerializableChatRequestData { message: string | IParsedChatRequest; - response: ReadonlyArray | undefined; + response: ReadonlyArray | undefined; agent?: ISerializableChatAgentData; slashCommand?: IChatAgentCommand; responseErrorDetails: IChatResponseErrorDetails | undefined; @@ -640,7 +643,10 @@ export class ChatModel extends Disposable implements IChatModel { throw new Error('acceptResponseProgress: Adding progress to a completed response'); } - if (progress.kind === 'content' || progress.kind === 'markdownContent' || progress.kind === 'asyncContent' || progress.kind === 'treeData' || progress.kind === 'inlineReference') { + if (progress.kind === 'vulnerability') { + // TODO@roblourens ChatModel should just work with strings + request.response.updateContent({ kind: 'markdownVuln', content: { value: progress.content }, title: progress.title, description: progress.description }, quiet); + } else if (progress.kind === 'content' || progress.kind === 'markdownContent' || progress.kind === 'asyncContent' || progress.kind === 'treeData' || progress.kind === 'inlineReference' || progress.kind === 'markdownVuln') { request.response.updateContent(progress, quiet); } else if (progress.kind === 'usedContext' || progress.kind === 'reference' || progress.kind === 'progressMessage') { request.response.applyProgress(progress); diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index fcd5a652619..c69e9da1b5e 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -130,9 +130,26 @@ export interface IChatProgressMessage { kind: 'progressMessage'; } +export interface IChatAgentContentWithVulnerability { + content: string; + title: string; + description: string; + kind: 'vulnerability'; +} + +// TODO@roblourens Temp until I get MarkdownString out of ChatModel +export interface IChatAgentMarkdownContentWithVulnerability { + content: IMarkdownString; + title: string; + description: string; + kind: 'markdownVuln'; +} + export type IChatProgress = | IChatContent | IChatMarkdownContent + | IChatAgentContentWithVulnerability + | IChatAgentMarkdownContentWithVulnerability | IChatTreeData | IChatAsyncContent | IChatUsedContext diff --git a/src/vs/workbench/contrib/chat/common/chatViewModel.ts b/src/vs/workbench/contrib/chat/common/chatViewModel.ts index 6fba224d47f..8adf3c7ce3e 100644 --- a/src/vs/workbench/contrib/chat/common/chatViewModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatViewModel.ts @@ -109,6 +109,7 @@ export interface IChatResponseViewModel { currentRenderedHeight: number | undefined; setVote(vote: InteractiveSessionVoteDirection): void; usedReferencesExpanded?: boolean; + vulnerabilitiesListExpanded: boolean; } export class ChatViewModel extends Disposable implements IChatViewModel { @@ -335,7 +336,6 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi currentRenderedHeight: number | undefined; private _usedReferencesExpanded: boolean | undefined; - get usedReferencesExpanded(): boolean | undefined { if (typeof this._usedReferencesExpanded === 'boolean') { return this._usedReferencesExpanded; @@ -348,6 +348,15 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi this._usedReferencesExpanded = v; } + private _vulnerabilitiesListExpanded: boolean = false; + get vulnerabilitiesListExpanded(): boolean { + return this._vulnerabilitiesListExpanded; + } + + set vulnerabilitiesListExpanded(v: boolean) { + this._vulnerabilitiesListExpanded = v; + } + private _contentUpdateTimings: IChatLiveUpdateData | undefined = undefined; get contentUpdateTimings(): IChatLiveUpdateData | undefined { return this._contentUpdateTimings; diff --git a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiline.0.snap b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiline.0.snap new file mode 100644 index 00000000000..6c449047db6 --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiline.0.snap @@ -0,0 +1,11 @@ +[ + { + content: { + value: "some code\nover\nmultiple lines content with vuln\nand\nnewlinesmore code\nwith newline", + isTrusted: false, + supportThemeIcons: false, + supportHtml: false + }, + kind: "markdownContent" + } +] \ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiline.1.snap b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiline.1.snap new file mode 100644 index 00000000000..644f8a772ae --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiline.1.snap @@ -0,0 +1,15 @@ +{ + newText: "some code\nover\nmultiple lines content with vuln\nand\nnewlinesmore code\nwith newline", + vulnerabilities: [ + { + title: "title", + description: "vuln", + range: { + startLineNumber: 3, + startColumn: 16, + endLineNumber: 5, + endColumn: 9 + } + } + ] +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiple_vulns.0.snap b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiple_vulns.0.snap new file mode 100644 index 00000000000..f6b34c1a018 --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiple_vulns.0.snap @@ -0,0 +1,11 @@ +[ + { + content: { + value: "some code\nover\nmultiple lines content with vuln\nand\nnewlinesmore code\nwith newlinecontent with vuln\nand\nnewlines", + isTrusted: false, + supportThemeIcons: false, + supportHtml: false + }, + kind: "markdownContent" + } +] \ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiple_vulns.1.snap b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiple_vulns.1.snap new file mode 100644 index 00000000000..5107938a33a --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_multiple_vulns.1.snap @@ -0,0 +1,25 @@ +{ + newText: "some code\nover\nmultiple lines content with vuln\nand\nnewlinesmore code\nwith newlinecontent with vuln\nand\nnewlines", + vulnerabilities: [ + { + title: "title", + description: "vuln", + range: { + startLineNumber: 3, + startColumn: 16, + endLineNumber: 5, + endColumn: 9 + } + }, + { + title: "title", + description: "vuln", + range: { + startLineNumber: 6, + startColumn: 13, + endLineNumber: 8, + endColumn: 9 + } + } + ] +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_single_line.0.snap b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_single_line.0.snap new file mode 100644 index 00000000000..5fd2e9f33b6 --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_single_line.0.snap @@ -0,0 +1,11 @@ +[ + { + content: { + value: "some code content with vuln after", + isTrusted: false, + supportThemeIcons: false, + supportHtml: false + }, + kind: "markdownContent" + } +] \ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_single_line.1.snap b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_single_line.1.snap new file mode 100644 index 00000000000..d61a6b5926e --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownDecorationsRenderer_extractVulnerabilitiesFromText_single_line.1.snap @@ -0,0 +1,15 @@ +{ + newText: "some code content with vuln after", + vulnerabilities: [ + { + title: "title", + description: "vuln", + range: { + startLineNumber: 1, + startColumn: 11, + endLineNumber: 1, + endColumn: 28 + } + } + ] +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/browser/chatMarkdownDecorationsRenderer.test.ts b/src/vs/workbench/contrib/chat/test/browser/chatMarkdownDecorationsRenderer.test.ts new file mode 100644 index 00000000000..3d8901b789f --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/browser/chatMarkdownDecorationsRenderer.test.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { assertSnapshot } from 'vs/base/test/common/snapshot'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { annotateSpecialMarkdownContent, extractVulnerabilitiesFromText } from 'vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer'; +import { IChatMarkdownContent } from 'vs/workbench/contrib/chat/common/chatService'; + +function content(str: string): IChatMarkdownContent { + return { kind: 'markdownContent', content: new MarkdownString(str) }; +} + +suite('ChatMarkdownDecorationsRenderer', function () { + ensureNoDisposablesAreLeakedInTestSuite(); + + suite('extractVulnerabilitiesFromText', () => { + test('single line', async () => { + const before = 'some code '; + const vulnContent = 'content with vuln'; + const after = ' after'; + const annotatedResult = annotateSpecialMarkdownContent([content(before), { kind: 'markdownVuln', content: new MarkdownString(vulnContent), title: 'title', description: 'vuln' }, content(after)]); + await assertSnapshot(annotatedResult); + + const markdown = annotatedResult[0] as IChatMarkdownContent; + const result = extractVulnerabilitiesFromText(markdown.content.value); + await assertSnapshot(result); + }); + + test('multiline', async () => { + const before = 'some code\nover\nmultiple lines '; + const vulnContent = 'content with vuln\nand\nnewlines'; + const after = 'more code\nwith newline'; + const annotatedResult = annotateSpecialMarkdownContent([content(before), { kind: 'markdownVuln', content: new MarkdownString(vulnContent), title: 'title', description: 'vuln' }, content(after)]); + await assertSnapshot(annotatedResult); + + const markdown = annotatedResult[0] as IChatMarkdownContent; + const result = extractVulnerabilitiesFromText(markdown.content.value); + await assertSnapshot(result); + }); + + test('multiple vulns', async () => { + const before = 'some code\nover\nmultiple lines '; + const vulnContent = 'content with vuln\nand\nnewlines'; + const after = 'more code\nwith newline'; + const annotatedResult = annotateSpecialMarkdownContent([ + content(before), + { kind: 'markdownVuln', content: new MarkdownString(vulnContent), title: 'title', description: 'vuln' }, + content(after), + { kind: 'markdownVuln', content: new MarkdownString(vulnContent), title: 'title', description: 'vuln' }, + ]); + await assertSnapshot(annotatedResult); + + const markdown = annotatedResult[0] as IChatMarkdownContent; + const result = extractVulnerabilitiesFromText(markdown.content.value); + await assertSnapshot(result); + }); + }); +}); diff --git a/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts b/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts index 1005628fe0c..aed7effd215 100644 --- a/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts +++ b/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts @@ -27,6 +27,16 @@ declare module 'vscode' { command?: ChatAgentSlashCommand; } + export interface ChatAgentVulnerability { + title: string; + description: string; + // id: string; // Later we will need to be able to link these across multiple content chunks. + } + + export interface ChatAgentContent { + vulnerability?: ChatAgentVulnerability; + } + export type ChatAgentExtendedProgress = ChatAgentProgress | ChatAgentMarkdownContent | ChatAgentDetectedAgent; From da5ab29656a9f0bef63602fbce10968a7f4db2b5 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 20 Nov 2023 12:02:17 -0600 Subject: [PATCH 72/87] Don't throw when receiving an unknown chat progress update (#198533) * style update * Don't throw when getting a bad progress update --- src/vs/workbench/api/common/extHostChatAgents2.ts | 10 ++++++++++ src/vs/workbench/api/common/extHostTypeConverters.ts | 4 ++-- src/vs/workbench/contrib/chat/browser/media/chat.css | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 144dc007e93..c43db4a08b6 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -96,6 +96,11 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { } const convertedProgress = typeConvert.ChatResponseProgress.from(agent.extension, progress); + if (!convertedProgress) { + this._logService.error('Unknown progress type: ' + JSON.stringify(progress)); + return; + } + if ('placeholder' in progress && 'resolvedContent' in progress) { const resolvedContent = Promise.all([this._proxy.$handleProgressChunk(requestId, convertedProgress), progress.resolvedContent]); raceCancellation(resolvedContent, token).then(res => { @@ -104,6 +109,11 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { } const [progressHandle, progressContent] = res; const convertedContent = typeConvert.ChatResponseProgress.from(agent.extension, progressContent); + if (!convertedContent) { + this._logService.error('Unknown progress type: ' + JSON.stringify(progressContent)); + return; + } + this._proxy.$handleProgressChunk(requestId, convertedContent, progressHandle ?? undefined); }); } else { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index e66f47c9010..f4bc4f410ea 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2295,7 +2295,7 @@ export namespace InteractiveEditorResponseFeedbackKind { } export namespace ChatResponseProgress { - export function from(extension: IExtensionDescription, progress: vscode.ChatAgentExtendedProgress): extHostProtocol.IChatProgressDto { + export function from(extension: IExtensionDescription, progress: vscode.ChatAgentExtendedProgress): extHostProtocol.IChatProgressDto | undefined { if ('placeholder' in progress && 'resolvedContent' in progress) { return { content: progress.placeholder, kind: 'asyncContent' } satisfies extHostProtocol.IChatAsyncContentDto; } else if ('markdownContent' in progress) { @@ -2349,7 +2349,7 @@ export namespace ChatResponseProgress { } else if ('message' in progress) { return { content: progress.message, kind: 'progressMessage' }; } else { - throw new Error('Invalid progress type: ' + JSON.stringify(progress)); + return undefined; } } } diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index bbda4c71f5d..d994f964e0c 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -611,5 +611,6 @@ } .interactive-item-container .progress-steps .progress-step .codicon-check { + font-size: 14px; color: var(--vscode-debugIcon-startForeground); } From ad31b067ab7b8ae349de8d0c46364eec07d1fdd1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 20 Nov 2023 12:10:43 -0800 Subject: [PATCH 73/87] Fix markdown link inserting (#198702) Fixes #190769 Fixes #195349 Also makes some small code cleanups --- .../src/commands/insertResource.ts | 2 +- .../copyFiles/dropResourceProvider.ts | 6 +- .../copyFiles/pasteResourceProvider.ts | 2 +- .../copyFiles/pasteUrlProvider.ts | 2 +- .../src/languageFeatures/copyFiles/shared.ts | 60 ++++++++----------- .../src/test/markdownLink.test.ts | 17 ++++-- 6 files changed, 42 insertions(+), 47 deletions(-) diff --git a/extensions/markdown-language-features/src/commands/insertResource.ts b/extensions/markdown-language-features/src/commands/insertResource.ts index 87de74270ca..598afb9bcdf 100644 --- a/extensions/markdown-language-features/src/commands/insertResource.ts +++ b/extensions/markdown-language-features/src/commands/insertResource.ts @@ -79,7 +79,7 @@ async function insertLink(activeEditor: vscode.TextEditor, selectedFiles: vscode function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: vscode.Uri[], insertAsMedia: boolean, title = '', placeholderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false) { const snippetEdits = coalesce(activeEditor.selections.map((selection, i): vscode.SnippetTextEdit | undefined => { const selectionText = activeEditor.document.getText(selection); - const snippet = createUriListSnippet(activeEditor.document, selectedFiles, [], title, placeholderValue, pasteAsMarkdownLink, isExternalLink, { + const snippet = createUriListSnippet(activeEditor.document, selectedFiles.map(uri => ({ uri })), title, placeholderValue, pasteAsMarkdownLink, isExternalLink, { insertAsMedia, placeholderText: selectionText, placeholderStartIndex: (i + 1) * selectedFiles.length, diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropResourceProvider.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropResourceProvider.ts index 65bd9bf9d7c..6a8bdf88609 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropResourceProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropResourceProvider.ts @@ -44,12 +44,12 @@ class ResourceDropProvider implements vscode.DocumentDropEditProvider { private async _getUriListEdit(document: vscode.TextDocument, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { const urlList = await dataTransfer.get(Mime.textUriList)?.asString(); if (!urlList || token.isCancellationRequested) { - return undefined; + return; } - const snippet = await tryGetUriListSnippet(document, urlList, token); + const snippet = tryGetUriListSnippet(document, urlList); if (!snippet) { - return undefined; + return; } const edit = new vscode.DocumentDropEdit(snippet.snippet); diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteResourceProvider.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteResourceProvider.ts index 23292cbb97d..46b47e5198e 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteResourceProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteResourceProvider.ts @@ -53,7 +53,7 @@ class PasteResourceEditProvider implements vscode.DocumentPasteEditProvider { } const pasteUrlSetting = getPasteUrlAsFormattedLinkSetting(document); - const pasteEdit = await createEditAddingLinksForUriList(document, ranges, uriList, false, pasteUrlSetting === PasteUrlAsFormattedLink.Smart, token); + const pasteEdit = createEditAddingLinksForUriList(document, ranges, uriList, false, pasteUrlSetting === PasteUrlAsFormattedLink.Smart); if (!pasteEdit) { return; } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts index b68b9cdaa2b..de9d6a05d15 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts @@ -32,7 +32,7 @@ class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider { return; } - const pasteEdit = await createEditAddingLinksForUriList(document, ranges, validateLink(urlList).cleanedUrlList, true, pasteUrlSetting === PasteUrlAsFormattedLink.Smart, token); + const pasteEdit = createEditAddingLinksForUriList(document, ranges, validateLink(urlList).cleanedUrlList, true, pasteUrlSetting === PasteUrlAsFormattedLink.Smart); if (!pasteEdit) { return; } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts index dc8c15075f4..12f0f632ec6 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts @@ -75,14 +75,13 @@ export function getPasteUrlAsFormattedLinkSetting(document: vscode.TextDocument) return vscode.workspace.getConfiguration('markdown', document).get('editor.pasteUrlAsFormattedLink.enabled', PasteUrlAsFormattedLink.Smart); } -export async function createEditAddingLinksForUriList( +export function createEditAddingLinksForUriList( document: SkinnyTextDocument, ranges: readonly vscode.Range[], urlList: string, isExternalLink: boolean, - useSmartPaste: boolean, - token: vscode.CancellationToken, -): Promise<{ additionalEdits: vscode.WorkspaceEdit; label: string; markdownLink: boolean } | undefined> { + useSmartPaste: boolean +): { additionalEdits: vscode.WorkspaceEdit; label: string; markdownLink: boolean } | undefined { if (ranges.length === 0) { return; @@ -104,7 +103,7 @@ export async function createEditAddingLinksForUriList( markdownLink = pasteAsMarkdownLink; // FIX: this will only match the last range } - const snippet = await tryGetUriListSnippet(document, urlList, token, document.getText(range), placeHolderValue, pasteAsMarkdownLink, isExternalLink); + const snippet = tryGetUriListSnippet(document, urlList, document.getText(range), placeHolderValue, pasteAsMarkdownLink, isExternalLink); if (!snippet) { return; } @@ -158,21 +157,16 @@ export function validateLink(urlList: string): { isValid: boolean; cleanedUrlLis return { isValid, cleanedUrlList: splitUrlList[0] }; } -export async function tryGetUriListSnippet(document: SkinnyTextDocument, urlList: String, token: vscode.CancellationToken, title = '', placeHolderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false): Promise<{ snippet: vscode.SnippetString; label: string } | undefined> { - if (token.isCancellationRequested) { - return undefined; - } - const uriStrings: string[] = []; - const uris: vscode.Uri[] = []; - for (const resource of urlList.split(/\r?\n/g)) { +export function tryGetUriListSnippet(document: SkinnyTextDocument, urlList: String, title = '', placeHolderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false): { snippet: vscode.SnippetString; label: string } | undefined { + const entries = coalesce(urlList.split(/\r?\n/g).map(resource => { try { - uris.push(vscode.Uri.parse(resource)); - uriStrings.push(resource); + return { uri: vscode.Uri.parse(resource), str: resource }; } catch { - // noop + // Uri parse failure + return undefined; } - } - return createUriListSnippet(document, uris, uriStrings, title, placeHolderValue, pasteAsMarkdownLink, isExternalLink); + })); + return createUriListSnippet(document, entries, title, placeHolderValue, pasteAsMarkdownLink, isExternalLink); } interface UriListSnippetOptions { @@ -193,20 +187,21 @@ interface UriListSnippetOptions { export function appendToLinkSnippet( snippet: vscode.SnippetString, title: string, - uriString: string, + link: string, placeholderValue: number, isExternalLink: boolean, -): vscode.SnippetString { +): void { snippet.appendText('['); snippet.appendPlaceholder(escapeBrackets(title) || 'Title', placeholderValue); - snippet.appendText(`](${escapeMarkdownLinkPath(uriString, isExternalLink)})`); - return snippet; + snippet.appendText(`](${escapeMarkdownLinkPath(link, isExternalLink)})`); } export function createUriListSnippet( document: SkinnyTextDocument, - uris: readonly vscode.Uri[], - uriStrings?: readonly string[], + uris: ReadonlyArray<{ + readonly uri: vscode.Uri; + readonly str?: string; + }>, title = '', placeholderValue = 0, pasteAsMarkdownLink = true, @@ -219,15 +214,15 @@ export function createUriListSnippet( const documentDir = getDocumentDir(document.uri); - let snippet = new vscode.SnippetString(); + const snippet = new vscode.SnippetString(); let insertedLinkCount = 0; let insertedImageCount = 0; let insertedAudioVideoCount = 0; uris.forEach((uri, i) => { - const mdPath = getMdPath(documentDir, uri); + const mdPath = getRelativeMdPath(documentDir, uri.uri) ?? uri.str ?? uri.uri.toString(); - const ext = URI.Utils.extname(uri).toLowerCase().replace('.', ''); + const ext = URI.Utils.extname(uri.uri).toLowerCase().replace('.', ''); const insertAsMedia = typeof options?.insertAsMedia === 'undefined' ? mediaFileExtensions.has(ext) : !!options.insertAsMedia; const insertAsVideo = mediaFileExtensions.get(ext) === MediaKind.Video; const insertAsAudio = mediaFileExtensions.get(ext) === MediaKind.Audio; @@ -257,11 +252,7 @@ export function createUriListSnippet( } } else { insertedLinkCount++; - if (uriStrings && isExternalLink) { - snippet = appendToLinkSnippet(snippet, title, uriStrings[i], placeholderValue, isExternalLink); - } else { - snippet.appendText(escapeMarkdownLinkPath(mdPath, isExternalLink)); - } + appendToLinkSnippet(snippet, title, mdPath, placeholderValue, isExternalLink); } if (i < uris.length - 1 && uris.length > 1) { @@ -349,7 +340,7 @@ export async function createEditForMediaFiles( } } - const snippet = createUriListSnippet(document, fileEntries.map(entry => entry.uri)); + const snippet = createUriListSnippet(document, fileEntries); if (!snippet) { return; } @@ -361,7 +352,7 @@ export async function createEditForMediaFiles( }; } -function getMdPath(dir: vscode.Uri | undefined, file: vscode.Uri) { +function getRelativeMdPath(dir: vscode.Uri | undefined, file: vscode.Uri): string | undefined { if (dir && dir.scheme === file.scheme && dir.authority === file.authority) { if (file.scheme === Schemes.file) { // On windows, we must use the native `path.relative` to generate the relative path @@ -373,8 +364,7 @@ function getMdPath(dir: vscode.Uri | undefined, file: vscode.Uri) { return path.posix.relative(dir.path, file.path); } - - return file.toString(false); + return undefined; } function escapeHtmlAttribute(attr: string): string { diff --git a/extensions/markdown-language-features/src/test/markdownLink.test.ts b/extensions/markdown-language-features/src/test/markdownLink.test.ts index f0133ad85cc..8081d7027d1 100644 --- a/extensions/markdown-language-features/src/test/markdownLink.test.ts +++ b/extensions/markdown-language-features/src/test/markdownLink.test.ts @@ -18,7 +18,7 @@ suite('createEditAddingLinksForUriList', () => { getText: function () { return 'hello world!'; }, }; - const result = await createEditAddingLinksForUriList(skinnyDocument, [new vscode.Range(0, 0, 0, 12)], 'https://www.microsoft.com/', true, true, new vscode.CancellationTokenSource().token); + const result = createEditAddingLinksForUriList(skinnyDocument, [new vscode.Range(0, 0, 0, 12)], 'https://www.microsoft.com/', true, true); // need to check the actual result -> snippet value assert.strictEqual(result?.label, 'Insert Markdown Link'); }); @@ -95,31 +95,36 @@ suite('createEditAddingLinksForUriList', () => { test('Should create snippet with < > when pasted link has an mismatched parentheses', () => { const uriString = 'https://www.mic(rosoft.com'; - const snippet = appendToLinkSnippet(new vscode.SnippetString(''), 'abc', uriString, 0, true); + const snippet = new vscode.SnippetString(''); + appendToLinkSnippet(snippet, 'abc', uriString, 0, true); assert.strictEqual(snippet?.value, '[${0:abc}]()'); }); test('Should create Markdown link snippet when pasteAsMarkdownLink is true', () => { const uriString = 'https://www.microsoft.com'; - const snippet = appendToLinkSnippet(new vscode.SnippetString(''), '', uriString, 0, true); + const snippet = new vscode.SnippetString(''); + appendToLinkSnippet(snippet, '', uriString, 0, true); assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com)'); }); test('Should use an unencoded URI string in Markdown link when passing in an external browser link', () => { const uriString = 'https://www.microsoft.com'; - const snippet = appendToLinkSnippet(new vscode.SnippetString(''), '', uriString, 0, true); + const snippet = new vscode.SnippetString(''); + appendToLinkSnippet(snippet, '', uriString, 0, true); assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com)'); }); test('Should not decode an encoded URI string when passing in an external browser link', () => { const uriString = 'https://www.microsoft.com/%20'; - const snippet = appendToLinkSnippet(new vscode.SnippetString(''), '', uriString, 0, true); + const snippet = new vscode.SnippetString(''); + appendToLinkSnippet(snippet, '', uriString, 0, true); assert.strictEqual(snippet?.value, '[${0:Title}](https://www.microsoft.com/%20)'); }); test('Should not encode an unencoded URI string when passing in an external browser link', () => { const uriString = 'https://www.example.com/path?query=value&another=value#fragment'; - const snippet = appendToLinkSnippet(new vscode.SnippetString(''), '', uriString, 0, true); + const snippet = new vscode.SnippetString(''); + appendToLinkSnippet(snippet, '', uriString, 0, true); assert.strictEqual(snippet?.value, '[${0:Title}](https://www.example.com/path?query=value&another=value#fragment)'); }); }); From 6e91b53c067ac8f50e1b3a6cf0166d8bee5a4926 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 20 Nov 2023 21:19:54 +0100 Subject: [PATCH 74/87] progressive rendering: use the previous word count for the word rate (#198560) * progressive rendering: use the previous word count for the word rate --- .../contrib/chat/common/chatViewModel.ts | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/chatViewModel.ts b/src/vs/workbench/contrib/chat/common/chatViewModel.ts index 8adf3c7ce3e..2ed73840d90 100644 --- a/src/vs/workbench/contrib/chat/common/chatViewModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatViewModel.ts @@ -79,6 +79,7 @@ export interface IChatLiveUpdateData { loadingStartTime: number; lastUpdateTime: number; impliedWordLoadRate: number; + lastWordCount: number; } export interface IChatResponseViewModel { @@ -372,7 +373,8 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi this._contentUpdateTimings = { loadingStartTime: Date.now(), lastUpdateTime: Date.now(), - impliedWordLoadRate: 0 + impliedWordLoadRate: 0, + lastWordCount: 0 }; } @@ -382,18 +384,14 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi const now = Date.now(); const wordCount = countWords(_model.response.asString()); const timeDiff = now - this._contentUpdateTimings!.loadingStartTime; - const impliedWordLoadRate = wordCount / (timeDiff / 1000); - const renderedWordCount = this.renderData?.renderedParts.reduce((acc, part) => acc += ('label' in part ? 0 : part.renderedWordCount), 0); - if (!this.isComplete) { - this.trace('onDidChange', `Update- got ${wordCount} words over ${timeDiff}ms = ${impliedWordLoadRate} words/s. ${renderedWordCount} words are rendered.`); - this._contentUpdateTimings = { - loadingStartTime: this._contentUpdateTimings!.loadingStartTime, - lastUpdateTime: now, - impliedWordLoadRate - }; - } else { - this.trace(`onDidChange`, `Done- got ${wordCount} words over ${timeDiff}ms = ${impliedWordLoadRate} words/s. ${renderedWordCount} words are rendered.`); - } + const impliedWordLoadRate = this._contentUpdateTimings.lastWordCount / (timeDiff / 1000); + this.trace('onDidChange', `Update- got ${this._contentUpdateTimings.lastWordCount} words over ${timeDiff}ms = ${impliedWordLoadRate} words/s. ${wordCount} words are now available.`); + this._contentUpdateTimings = { + loadingStartTime: this._contentUpdateTimings!.loadingStartTime, + lastUpdateTime: now, + impliedWordLoadRate, + lastWordCount: wordCount + }; } else { this.logService.warn('ChatResponseViewModel#onDidChange: got model update but contentUpdateTimings is not initialized'); } From afbfccac68bcc0144c61e2c291ee64d0152e0f75 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Mon, 20 Nov 2023 12:47:01 -0800 Subject: [PATCH 75/87] fix: revert settings search results ordering (#198703) --- .../preferences/browser/settingsTreeModels.ts | 17 ++++++----------- .../common/preferencesContribution.ts | 4 ++-- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index c2be5d08e38..e30c3bb549a 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -866,7 +866,7 @@ export class SearchResultModel extends SettingsTreeModel { viewState: ISettingsEditorViewState, settingsOrderByTocIndex: Map | null, isWorkspaceTrusted: boolean, - @IWorkbenchConfigurationService private readonly configurationService: IWorkbenchConfigurationService, + @IWorkbenchConfigurationService configurationService: IWorkbenchConfigurationService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @ILanguageService languageService: ILanguageService, @IUserDataProfileService userDataProfileService: IUserDataProfileService, @@ -884,18 +884,13 @@ export class SearchResultModel extends SettingsTreeModel { } } - const tocHiddenDuringSearch = this.configurationService.getValue('workbench.settings.settingsSearchTocBehavior') === 'hide'; - if (!tocHiddenDuringSearch) { - // Sort the settings according to internal order if indexed. - if (this.settingsOrderByTocIndex) { - filterMatches.sort((a, b) => compareTwoNullableNumbers(a.setting.internalOrder, b.setting.internalOrder)); - } - return filterMatches; + // The search only has filters, so we can sort by the order in the TOC. + if (!this._viewState.query) { + return filterMatches.sort((a, b) => compareTwoNullableNumbers(a.setting.internalOrder, b.setting.internalOrder)); } - // The table of contents is hidden during the search. - // The settings could appear in a more haphazard order. - // Sort the settings according to their score. + // Sort the settings according to their relevancy. + // https://github.com/microsoft/vscode/issues/197773 filterMatches.sort((a, b) => { if (a.matchType !== b.matchType) { // Sort by match type if the match types are not the same. diff --git a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts index 22ff960b224..ad60f18f80f 100644 --- a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts @@ -152,8 +152,8 @@ registry.registerConfiguration({ 'type': 'string', 'enum': ['hide', 'filter'], 'enumDescriptions': [ - nls.localize('settingsSearchTocBehavior.hide', "Hide the Table of Contents while searching. The search results will not be grouped by category, and instead will be sorted by similarity to the query, with exact keyword matches coming first."), - nls.localize('settingsSearchTocBehavior.filter', "Filter the Table of Contents to just categories that have matching settings. Clicking a category will filter the results to that category. The search results will be grouped by category."), + nls.localize('settingsSearchTocBehavior.hide', "Hide the Table of Contents while searching."), + nls.localize('settingsSearchTocBehavior.filter', "Filter the Table of Contents to just categories that have matching settings. Clicking on a category will filter the results to that category."), ], 'description': nls.localize('settingsSearchTocBehavior', "Controls the behavior of the Settings editor Table of Contents while searching. If this setting is being changed in the Settings editor, the setting will take effect after the search query is modified."), 'default': 'filter', From c59e730e541c3a7c25b4ec6023a1031b9173609d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:37:56 +0100 Subject: [PATCH 76/87] SCM - only refresh the repository node if it exist in the tree (#198704) --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 3e825d51f05..5db197f9ab6 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -2891,7 +2891,7 @@ export class SCMViewPane extends ViewPane { this.asyncOperationSequencer.queue(async () => { const focusedInput = this.inputRenderer.getFocusedInput(); - if (element && (this.alwaysShowRepositories || this.scmViewService.visibleRepositories.length > 1)) { + if (element && this.tree.hasNode(element)) { // Refresh specific repository await this.tree.updateChildren(element); } else { From 032109e5a7b66cd576fc28bc9abf1e084c924810 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 20 Nov 2023 14:08:00 -0800 Subject: [PATCH 77/87] Don't show references on anon function (#198711) Fixes #198389 --- .../src/languageFeatures/codeLens/referencesCodeLens.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts index 73e6335dbad..de7d1f6d900 100644 --- a/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts @@ -77,7 +77,7 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens switch (item.kind) { case PConst.Kind.function: { const showOnAllFunctions = vscode.workspace.getConfiguration(this.language.id).get('referencesCodeLens.showOnAllFunctions'); - if (showOnAllFunctions) { + if (showOnAllFunctions && item.nameSpan) { return getSymbolRange(document, item); } } From ff9fc384d3a3d284a53ce385d0f1923d075681b0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 20 Nov 2023 14:37:00 -0800 Subject: [PATCH 78/87] Clean up and fix markdown url pasting (#198706) Fixes #192568 --- .../markdown-language-features/package.json | 3 +- .../src/client/inMemoryDocument.ts | 24 +- .../copyFiles/pasteUrlProvider.ts | 11 +- .../src/languageFeatures/copyFiles/shared.ts | 99 ++++----- .../src/test/markdownLink.test.ts | 210 +++++++++--------- .../src/types/textDocument.ts | 2 +- .../markdown-language-features/yarn.lock | 5 + 7 files changed, 181 insertions(+), 173 deletions(-) diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 749673f151d..dbbf937e6c6 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -507,7 +507,7 @@ "type": "string", "scope": "resource", "markdownDescription": "%configuration.markdown.editor.pasteUrlAsFormattedLink.enabled%", - "default":"never", + "default": "never", "enum": [ "always", "smart", @@ -734,6 +734,7 @@ "morphdom": "^2.6.1", "picomatch": "^2.3.1", "vscode-languageclient": "^8.0.2", + "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.3" }, "devDependencies": { diff --git a/extensions/markdown-language-features/src/client/inMemoryDocument.ts b/extensions/markdown-language-features/src/client/inMemoryDocument.ts index 0848a79093f..adf7a143c1d 100644 --- a/extensions/markdown-language-features/src/client/inMemoryDocument.ts +++ b/extensions/markdown-language-features/src/client/inMemoryDocument.ts @@ -3,18 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { TextDocument } from 'vscode-languageserver-textdocument'; import * as vscode from 'vscode'; import { ITextDocument } from '../types/textDocument'; export class InMemoryDocument implements ITextDocument { - constructor( - public readonly uri: vscode.Uri, - private readonly _contents: string, - public readonly version = 0, - ) { } + private readonly _doc: TextDocument; - getText(): string { - return this._contents; + public readonly uri: vscode.Uri; + public readonly version: number; + + constructor( + uri: vscode.Uri, + contents: string, + version: number = 0, + ) { + this.uri = uri; + this.version = version; + this._doc = TextDocument.create(this.uri.toString(), 'markdown', 0, contents); + } + + getText(range?: vscode.Range): string { + return this._doc.getText(range); } } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts index de9d6a05d15..814f5e5d9f3 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import { Mime } from '../../util/mimes'; -import { createEditAddingLinksForUriList, getPasteUrlAsFormattedLinkSetting, PasteUrlAsFormattedLink, validateLink } from './shared'; +import { createEditAddingLinksForUriList, findValidUriInText, getPasteUrlAsFormattedLinkSetting, PasteUrlAsFormattedLink } from './shared'; class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider { @@ -28,11 +28,16 @@ class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider { const item = dataTransfer.get(Mime.textPlain); const urlList = await item?.asString(); - if (token.isCancellationRequested || !urlList || !validateLink(urlList).isValid) { + if (token.isCancellationRequested || !urlList) { return; } - const pasteEdit = createEditAddingLinksForUriList(document, ranges, validateLink(urlList).cleanedUrlList, true, pasteUrlSetting === PasteUrlAsFormattedLink.Smart); + const uriText = findValidUriInText(urlList); + if (!uriText) { + return; + } + + const pasteEdit = createEditAddingLinksForUriList(document, ranges, uriText, true, pasteUrlSetting === PasteUrlAsFormattedLink.Smart); if (!pasteEdit) { return; } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts index 12f0f632ec6..5c591457c41 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts @@ -6,6 +6,7 @@ import * as path from 'path'; import * as vscode from 'vscode'; import * as URI from 'vscode-uri'; +import { ITextDocument } from '../../types/textDocument'; import { coalesce } from '../../util/arrays'; import { getDocumentDir } from '../../util/document'; import { mediaMimes } from '../../util/mimes'; @@ -18,7 +19,7 @@ enum MediaKind { Audio, } -export const externalUriSchemes = [ +const externalUriSchemes = [ 'http', 'https', 'mailto', @@ -50,21 +51,6 @@ export const mediaFileExtensions = new Map([ ['wav', MediaKind.Audio], ]); -const smartPasteRegexes = [ - { regex: /(\[[^\[\]]*](?:\([^\(\)]*\)|\[[^\[\]]*]))/g }, // In a Markdown link - { regex: /^```[\s\S]*?```$/gm }, // In a backtick fenced code block - { regex: /^~~~[\s\S]*?~~~$/gm }, // In a tildefenced code block - { regex: /^\$\$[\s\S]*?\$\$$/gm }, // In a fenced math block - { regex: /`[^`]*`/g }, // In inline code - { regex: /\$[^$]*\$/g }, // In inline math -]; - -export interface SkinnyTextDocument { - offsetAt(position: vscode.Position): number; - getText(range?: vscode.Range): string; - readonly uri: vscode.Uri; -} - export enum PasteUrlAsFormattedLink { Always = 'always', Smart = 'smart', @@ -76,16 +62,16 @@ export function getPasteUrlAsFormattedLinkSetting(document: vscode.TextDocument) } export function createEditAddingLinksForUriList( - document: SkinnyTextDocument, + document: ITextDocument, ranges: readonly vscode.Range[], urlList: string, isExternalLink: boolean, - useSmartPaste: boolean + useSmartPaste: boolean, ): { additionalEdits: vscode.WorkspaceEdit; label: string; markdownLink: boolean } | undefined { - - if (ranges.length === 0) { + if (!ranges.length) { return; } + const edits: vscode.SnippetTextEdit[] = []; let placeHolderValue: number = ranges.length; let label: string = ''; @@ -93,13 +79,8 @@ export function createEditAddingLinksForUriList( let markdownLink: boolean = true; for (const range of ranges) { - const selectedRange: vscode.Range = new vscode.Range( - new vscode.Position(range.start.line, document.offsetAt(range.start)), - new vscode.Position(range.end.line, document.offsetAt(range.end)) - ); - if (useSmartPaste) { - pasteAsMarkdownLink = checkSmartPaste(document, selectedRange, range); + pasteAsMarkdownLink = shouldSmartPaste(document, range); markdownLink = pasteAsMarkdownLink; // FIX: this will only match the last range } @@ -120,13 +101,47 @@ export function createEditAddingLinksForUriList( return { additionalEdits, label, markdownLink }; } -export function checkSmartPaste(document: SkinnyTextDocument, selectedRange: vscode.Range, range: vscode.Range): boolean { - if (selectedRange.isEmpty || /^[\s\n]*$/.test(document.getText(range)) || validateLink(document.getText(range)).isValid) { +export function findValidUriInText(text: string): string | undefined { + const trimmedUrlList = text.trim(); + + // Uri must consist of a single sequence of characters without spaces + if (!/^\S+$/.test(trimmedUrlList)) { + return; + } + + let uri: vscode.Uri; + try { + uri = vscode.Uri.parse(trimmedUrlList); + } catch { + // Could not parse + return; + } + + if (!externalUriSchemes.includes(uri.scheme.toLowerCase()) || uri.authority.length <= 1) { + return; + } + + return trimmedUrlList; +} + +const smartPasteRegexes = [ + { regex: /(\[[^\[\]]*](?:\([^\(\)]*\)|\[[^\[\]]*]))/g }, // In a Markdown link + { regex: /^```[\s\S]*?```$/gm }, // In a backtick fenced code block + { regex: /^~~~[\s\S]*?~~~$/gm }, // In a tildefenced code block + { regex: /^\$\$[\s\S]*?\$\$$/gm }, // In a fenced math block + { regex: /`[^`]*`/g }, // In inline code + { regex: /\$[^$]*\$/g }, // In inline math +]; + +export function shouldSmartPaste(document: ITextDocument, selectedRange: vscode.Range): boolean { + if (selectedRange.isEmpty || /^[\s\n]*$/.test(document.getText(selectedRange)) || findValidUriInText(document.getText(selectedRange))) { return false; } - if (/\[.*\]\(.*\)/.test(document.getText(range)) || /!\[.*\]\(.*\)/.test(document.getText(range))) { + + if (/\[.*\]\(.*\)/.test(document.getText(selectedRange)) || /!\[.*\]\(.*\)/.test(document.getText(selectedRange))) { return false; } + for (const regex of smartPasteRegexes) { const matches = [...document.getText().matchAll(regex.regex)]; for (const match of matches) { @@ -138,29 +153,14 @@ export function checkSmartPaste(document: SkinnyTextDocument, selectedRange: vsc } } } + return true; } -export function validateLink(urlList: string): { isValid: boolean; cleanedUrlList: string } { - let isValid = false; - let uri = undefined; - const trimmedUrlList = urlList?.trim(); //remove leading and trailing whitespace and new lines - try { - uri = vscode.Uri.parse(trimmedUrlList); - } catch (error) { - return { isValid: false, cleanedUrlList: urlList }; - } - const splitUrlList = trimmedUrlList.split(' ').filter(item => item !== ''); //split on spaces and remove empty strings - if (uri) { - isValid = splitUrlList.length === 1 && !splitUrlList[0].includes('\n') && externalUriSchemes.includes(vscode.Uri.parse(splitUrlList[0]).scheme) && !!vscode.Uri.parse(splitUrlList[0]).authority; - } - return { isValid, cleanedUrlList: splitUrlList[0] }; -} - -export function tryGetUriListSnippet(document: SkinnyTextDocument, urlList: String, title = '', placeHolderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false): { snippet: vscode.SnippetString; label: string } | undefined { - const entries = coalesce(urlList.split(/\r?\n/g).map(resource => { +export function tryGetUriListSnippet(document: ITextDocument, urlList: String, title = '', placeHolderValue = 0, pasteAsMarkdownLink = true, isExternalLink = false): { snippet: vscode.SnippetString; label: string } | undefined { + const entries = coalesce(urlList.split(/\r?\n/g).map(line => { try { - return { uri: vscode.Uri.parse(resource), str: resource }; + return { uri: vscode.Uri.parse(line), str: line }; } catch { // Uri parse failure return undefined; @@ -197,7 +197,7 @@ export function appendToLinkSnippet( } export function createUriListSnippet( - document: SkinnyTextDocument, + document: ITextDocument, uris: ReadonlyArray<{ readonly uri: vscode.Uri; readonly str?: string; @@ -412,4 +412,3 @@ function needsBracketLink(mdPath: string) { return nestingCount > 0; } - diff --git a/extensions/markdown-language-features/src/test/markdownLink.test.ts b/extensions/markdown-language-features/src/test/markdownLink.test.ts index 8081d7027d1..4d0fffd0585 100644 --- a/extensions/markdown-language-features/src/test/markdownLink.test.ts +++ b/extensions/markdown-language-features/src/test/markdownLink.test.ts @@ -2,92 +2,101 @@ * 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 * as assert from 'assert'; import 'mocha'; -import { SkinnyTextDocument, checkSmartPaste, createEditAddingLinksForUriList, appendToLinkSnippet, validateLink } from '../languageFeatures/copyFiles/shared'; +import * as vscode from 'vscode'; +import { InMemoryDocument } from '../client/inMemoryDocument'; +import { appendToLinkSnippet, createEditAddingLinksForUriList, findValidUriInText, shouldSmartPaste } from '../languageFeatures/copyFiles/shared'; suite('createEditAddingLinksForUriList', () => { test('Markdown Link Pasting should occur for a valid link (end to end)', async () => { // createEditAddingLinksForUriList -> checkSmartPaste -> tryGetUriListSnippet -> createUriListSnippet -> createLinkSnippet - const skinnyDocument: SkinnyTextDocument = { - uri: vscode.Uri.parse('file:///path/to/your/file'), - offsetAt: function () { return 0; }, - getText: function () { return 'hello world!'; }, - }; - - const result = createEditAddingLinksForUriList(skinnyDocument, [new vscode.Range(0, 0, 0, 12)], 'https://www.microsoft.com/', true, true); + const result = createEditAddingLinksForUriList( + new InMemoryDocument(vscode.Uri.file('test.md'), 'hello world!'), [new vscode.Range(0, 0, 0, 12)], 'https://www.microsoft.com/', true, true); // need to check the actual result -> snippet value assert.strictEqual(result?.label, 'Insert Markdown Link'); }); suite('validateLink', () => { - test('Markdown pasting should occur for a valid link.', () => { - const isLink = validateLink('https://www.microsoft.com/').isValid; - assert.strictEqual(isLink, true); + test('Markdown pasting should occur for a valid link', () => { + assert.strictEqual( + findValidUriInText('https://www.microsoft.com/'), + 'https://www.microsoft.com/'); }); - test('Markdown pasting should occur for a valid link preceded by a new line.', () => { - const isLink = validateLink('\r\nhttps://www.microsoft.com/').isValid; - assert.strictEqual(isLink, true); + test('Markdown pasting should occur for a valid link preceded by a new line', () => { + assert.strictEqual( + findValidUriInText('\r\nhttps://www.microsoft.com/'), + 'https://www.microsoft.com/'); }); - test('Markdown pasting should occur for a valid link followed by a new line.', () => { - const isLink = validateLink('https://www.microsoft.com/\r\n').isValid; - assert.strictEqual(isLink, true); + test('Markdown pasting should occur for a valid link followed by a new line', () => { + assert.strictEqual( + findValidUriInText('https://www.microsoft.com/\r\n'), + 'https://www.microsoft.com/'); }); - test('Markdown pasting should not occur for a valid hostname and invalid protool.', () => { - const isLink = validateLink('invalid:www.microsoft.com').isValid; - assert.strictEqual(isLink, false); + test('Markdown pasting should not occur for a valid hostname and invalid protool', () => { + assert.strictEqual( + findValidUriInText('invalid:www.microsoft.com'), + undefined); }); - test('Markdown pasting should not occur for plain text.', () => { - const isLink = validateLink('hello world!').isValid; - assert.strictEqual(isLink, false); + test('Markdown pasting should not occur for plain text', () => { + assert.strictEqual( + findValidUriInText('hello world!'), + undefined); }); - test('Markdown pasting should not occur for plain text including a colon.', () => { - const isLink = validateLink('hello: world!').isValid; - assert.strictEqual(isLink, false); + test('Markdown pasting should not occur for plain text including a colon', () => { + assert.strictEqual( + findValidUriInText('hello: world!'), + undefined); }); - test('Markdown pasting should not occur for plain text including a slashes.', () => { - const isLink = validateLink('helloworld!').isValid; - assert.strictEqual(isLink, false); + test('Markdown pasting should not occur for plain text including a slashes', () => { + assert.strictEqual( + findValidUriInText('helloworld!'), + undefined); }); - test('Markdown pasting should not occur for a link followed by text.', () => { - const isLink = validateLink('https://www.microsoft.com/ hello world!').isValid; - assert.strictEqual(isLink, false); + test('Markdown pasting should not occur for a link followed by text', () => { + assert.strictEqual( + findValidUriInText('https://www.microsoft.com/ hello world!'), + undefined); }); - test('Markdown pasting should occur for a link preceded or followed by spaces.', () => { - const isLink = validateLink(' https://www.microsoft.com/ ').isValid; - assert.strictEqual(isLink, true); + test('Markdown pasting should occur for a link preceded or followed by spaces', () => { + assert.strictEqual( + findValidUriInText(' https://www.microsoft.com/ '), + 'https://www.microsoft.com/'); }); - test('Markdown pasting should not occur for a link with an invalid scheme.', () => { - const isLink = validateLink('hello:www.microsoft.com').isValid; - assert.strictEqual(isLink, false); + test('Markdown pasting should not occur for a link with an invalid scheme', () => { + assert.strictEqual( + findValidUriInText('hello:www.microsoft.com'), + undefined); }); - test('Markdown pasting should not occur for multiple links being pasted.', () => { - const isLink = validateLink('https://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/').isValid; - assert.strictEqual(isLink, false); + test('Markdown pasting should not occur for multiple links being pasted', () => { + assert.strictEqual( + findValidUriInText('https://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/'), + undefined); }); - test('Markdown pasting should not occur for multiple links with spaces being pasted.', () => { - const isLink = validateLink('https://www.microsoft.com/ \r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\n hello \r\nhttps://www.microsoft.com/').isValid; - assert.strictEqual(isLink, false); + test('Markdown pasting should not occur for multiple links with spaces being pasted', () => { + assert.strictEqual( + findValidUriInText('https://www.microsoft.com/ \r\nhttps://www.microsoft.com/\r\nhttps://www.microsoft.com/\r\n hello \r\nhttps://www.microsoft.com/'), + undefined); }); test('Markdown pasting should not occur for just a valid uri scheme', () => { - const isLink = validateLink('https://').isValid; - assert.strictEqual(isLink, false); + assert.strictEqual( + findValidUriInText('https://'), + undefined); }); }); @@ -132,107 +141,86 @@ suite('createEditAddingLinksForUriList', () => { suite('checkSmartPaste', () => { - const skinnyDocument: SkinnyTextDocument = { - uri: vscode.Uri.file('/path/to/your/file'), - offsetAt: function () { return 0; }, - getText: function () { return 'hello world!'; }, - }; - test('Should evaluate pasteAsMarkdownLink as true for selected plain text', () => { - const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 0, 0, 12), new vscode.Range(0, 0, 0, 12)); - assert.strictEqual(pasteAsMarkdownLink, true); + assert.strictEqual( + shouldSmartPaste(makeTestDoc('hello world'), new vscode.Range(0, 0, 0, 12)), + true); }); test('Should evaluate pasteAsMarkdownLink as false for a valid selected link', () => { - skinnyDocument.getText = function () { return 'https://www.microsoft.com'; }; - const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 0, 0, 25), new vscode.Range(0, 0, 0, 25)); - assert.strictEqual(pasteAsMarkdownLink, false); + assert.strictEqual( + shouldSmartPaste(makeTestDoc('https://www.microsoft.com'), new vscode.Range(0, 0, 0, 25)), + false); }); test('Should evaluate pasteAsMarkdownLink as false for a valid selected link with trailing whitespace', () => { - skinnyDocument.getText = function () { return ' https://www.microsoft.com '; }; - const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 0, 0, 30), new vscode.Range(0, 0, 0, 30)); - assert.strictEqual(pasteAsMarkdownLink, false); + assert.strictEqual( + shouldSmartPaste(makeTestDoc(' https://www.microsoft.com '), new vscode.Range(0, 0, 0, 30)), + false); }); test('Should evaluate pasteAsMarkdownLink as true for a link pasted in square brackets', () => { - skinnyDocument.getText = function () { return '[abc]'; }; - const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 1, 0, 4), new vscode.Range(0, 1, 0, 4)); - assert.strictEqual(pasteAsMarkdownLink, true); + assert.strictEqual( + shouldSmartPaste(makeTestDoc('[abc]'), new vscode.Range(0, 1, 0, 4)), + true); }); test('Should evaluate pasteAsMarkdownLink as false for no selection', () => { - const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 0, 0, 0), new vscode.Range(0, 0, 0, 0)); - assert.strictEqual(pasteAsMarkdownLink, false); + assert.strictEqual( + shouldSmartPaste(makeTestDoc('xyz'), new vscode.Range(0, 0, 0, 0)), + false); }); test('Should evaluate pasteAsMarkdownLink as false for selected whitespace and new lines', () => { - skinnyDocument.getText = function () { return ' \r\n\r\n'; }; - const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 0, 0, 7), new vscode.Range(0, 0, 0, 7)); - assert.strictEqual(pasteAsMarkdownLink, false); + assert.strictEqual( + shouldSmartPaste(makeTestDoc(' \r\n\r\n'), new vscode.Range(0, 0, 0, 7)), + false); }); test('Should evaluate pasteAsMarkdownLink as false for pasting within a backtick code block', () => { - skinnyDocument.getText = function () { return '```\r\n\r\n```'; }; - const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 5, 0, 5), new vscode.Range(0, 5, 0, 5)); - assert.strictEqual(pasteAsMarkdownLink, false); + assert.strictEqual( + shouldSmartPaste(makeTestDoc('```\r\n\r\n```'), new vscode.Range(0, 5, 0, 5)), + false); }); test('Should evaluate pasteAsMarkdownLink as false for pasting within a tilde code block', () => { - skinnyDocument.getText = function () { return '~~~\r\n\r\n~~~'; }; - const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 5, 0, 5), new vscode.Range(0, 5, 0, 5)); - assert.strictEqual(pasteAsMarkdownLink, false); + assert.strictEqual( + shouldSmartPaste(makeTestDoc('~~~\r\n\r\n~~~'), new vscode.Range(0, 5, 0, 5)), + false); }); test('Should evaluate pasteAsMarkdownLink as false for pasting within a math block', () => { - skinnyDocument.getText = function () { return '$$$\r\n\r\n$$$'; }; - const pasteAsMarkdownLink = checkSmartPaste(skinnyDocument, new vscode.Range(0, 5, 0, 5), new vscode.Range(0, 5, 0, 5)); - assert.strictEqual(pasteAsMarkdownLink, false); + assert.strictEqual( + shouldSmartPaste(makeTestDoc('$$$\r\n\r\n$$$'), new vscode.Range(0, 5, 0, 5)), + false); }); - const linkSkinnyDoc: SkinnyTextDocument = { - uri: vscode.Uri.file('/path/to/your/file'), - offsetAt: function () { return 0; }, - getText: function () { return '[a](bcdef)'; }, - }; - test('Should evaluate pasteAsMarkdownLink as false for pasting within a Markdown link', () => { - const pasteAsMarkdownLink = checkSmartPaste(linkSkinnyDoc, new vscode.Range(0, 4, 0, 6), new vscode.Range(0, 4, 0, 6)); - assert.strictEqual(pasteAsMarkdownLink, false); + assert.strictEqual( + shouldSmartPaste(makeTestDoc('[a](bcdef)'), new vscode.Range(0, 4, 0, 6)), + false); }); - - const imageLinkSkinnyDoc: SkinnyTextDocument = { - uri: vscode.Uri.file('/path/to/your/file'), - offsetAt: function () { return 0; }, - getText: function () { return '![a](bcdef)'; }, - }; - test('Should evaluate pasteAsMarkdownLink as false for pasting within a Markdown image link', () => { - const pasteAsMarkdownLink = checkSmartPaste(imageLinkSkinnyDoc, new vscode.Range(0, 5, 0, 10), new vscode.Range(0, 5, 0, 10)); - assert.strictEqual(pasteAsMarkdownLink, false); + assert.strictEqual( + shouldSmartPaste(makeTestDoc('![a](bcdef)'), new vscode.Range(0, 5, 0, 10)), + false); }); - const inlineCodeSkinnyCode: SkinnyTextDocument = { - uri: vscode.Uri.file('/path/to/your/file'), - offsetAt: function () { return 0; }, - getText: function () { return '``'; }, - }; - test('Should evaluate pasteAsMarkdownLink as false for pasting within inline code', () => { - const pasteAsMarkdownLink = checkSmartPaste(inlineCodeSkinnyCode, new vscode.Range(0, 1, 0, 1), new vscode.Range(0, 1, 0, 1)); - assert.strictEqual(pasteAsMarkdownLink, false); + assert.strictEqual( + shouldSmartPaste(makeTestDoc('``'), new vscode.Range(0, 1, 0, 1)), + false); }); - const inlineMathSkinnyDoc: SkinnyTextDocument = { - uri: vscode.Uri.file('/path/to/your/file'), - offsetAt: function () { return 0; }, - getText: function () { return '$$'; }, - }; - test('Should evaluate pasteAsMarkdownLink as false for pasting within inline math', () => { - const pasteAsMarkdownLink = checkSmartPaste(inlineMathSkinnyDoc, new vscode.Range(0, 1, 0, 1), new vscode.Range(0, 1, 0, 1)); - assert.strictEqual(pasteAsMarkdownLink, false); + assert.strictEqual( + shouldSmartPaste(makeTestDoc('$$'), new vscode.Range(0, 1, 0, 1)), + false); }); }); }); + +function makeTestDoc(contents: string) { + return new InMemoryDocument(vscode.Uri.file('test.md'), contents); +} diff --git a/extensions/markdown-language-features/src/types/textDocument.ts b/extensions/markdown-language-features/src/types/textDocument.ts index a960e54d220..e57299671f6 100644 --- a/extensions/markdown-language-features/src/types/textDocument.ts +++ b/extensions/markdown-language-features/src/types/textDocument.ts @@ -12,6 +12,6 @@ export interface ITextDocument { readonly uri: vscode.Uri; readonly version: number; - getText(): string; + getText(range?: vscode.Range): string; } diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index bb33b5fe336..5e3d1826af5 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -295,6 +295,11 @@ vscode-languageserver-protocol@3.17.2: vscode-jsonrpc "8.0.2" vscode-languageserver-types "3.17.2" +vscode-languageserver-textdocument@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz#0822a000e7d4dc083312580d7575fe9e3ba2e2bf" + integrity sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA== + vscode-languageserver-textdocument@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.5.tgz#838769940ece626176ec5d5a2aa2d0aa69f5095c" From 2d8e9cbb8fd7efc9acf83f4c9adf168d13c40eae Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 20 Nov 2023 14:53:54 -0800 Subject: [PATCH 79/87] Pick up TS 5.3 final (#198712) --- 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 680720b8a54..8bd2f6d07ce 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -4,7 +4,7 @@ "license": "MIT", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "^5.3.1-rc" + "typescript": "5.3.2" }, "scripts": { "postinstall": "node ./postinstall.mjs" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 504c8370b1d..8f166d8e04e 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.3.1-rc: - version "5.3.1-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.1-rc.tgz#c307d4b69ea0c1c2cd17d4dd7700d30f7197f829" - integrity sha512-NVq/AufFc6KVjmVPcuVwdCkhTQlTcMEyRYJPvaGhPvj+X80MYUF+90qf0//uvINPb2ULg9m91/gbdIOhN2cZqA== +typescript@5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.2.tgz#00d1c7c1c46928c5845c1ee8d0cc2791031d4c43" + integrity sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ== vscode-grammar-updater@^1.1.0: version "1.1.0" From 1d92f24bd8c4d239d430942ed6e1cc2dc34581d8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 20 Nov 2023 15:01:33 -0800 Subject: [PATCH 80/87] Add telemetry for TS ai refactorings (#198708) --- .../src/languageFeatures/quickFix.ts | 5 ++- .../src/languageFeatures/refactor.ts | 6 ++- .../src/languageFeatures/util/copilot.ts | 39 ++++++++++++++++++- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/quickFix.ts b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts index e0fadc0cc43..b37b1cdf7da 100644 --- a/extensions/typescript-language-features/src/languageFeatures/quickFix.ts +++ b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts @@ -223,7 +223,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider{ message: 'Add types to this code. Add separate interfaces when possible. Do not change the code except for adding types.', expand: { kind: 'navtree-function', pos: diagnostic.range.start }, - document + document, + action: { type: 'quickfix', quickfix: action } }], title: '' }; diff --git a/extensions/typescript-language-features/src/languageFeatures/refactor.ts b/extensions/typescript-language-features/src/languageFeatures/refactor.ts index 997d692fe0b..8fa071892f7 100644 --- a/extensions/typescript-language-features/src/languageFeatures/refactor.ts +++ b/extensions/typescript-language-features/src/languageFeatures/refactor.ts @@ -465,7 +465,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider Date: Mon, 20 Nov 2023 15:25:20 -0800 Subject: [PATCH 81/87] Bump hardcoded web TS version (#198713) --- .../typescript-language-features/src/extension.browser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/extension.browser.ts b/extensions/typescript-language-features/src/extension.browser.ts index 65e9d57fc38..66532bc81fc 100644 --- a/extensions/typescript-language-features/src/extension.browser.ts +++ b/extensions/typescript-language-features/src/extension.browser.ts @@ -62,7 +62,7 @@ export async function activate(context: vscode.ExtensionContext): Promise { new TypeScriptVersion( TypeScriptVersionSource.Bundled, vscode.Uri.joinPath(context.extensionUri, 'dist/browser/typescript/tsserver.web.js').toString(), - API.fromSimpleString('5.1.3'))); + API.fromSimpleString('5.3.2'))); let experimentTelemetryReporter: IExperimentationTelemetryReporter | undefined; const packageInfo = getPackageInfo(context); From 05cd2640ec8a106a4ee99cb38e6ee34fbec04f11 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 20 Nov 2023 18:16:05 -0600 Subject: [PATCH 82/87] Add a flag to clear chat response content entirely (#198715) This may become useful for codeblock vulnerability annotation work, before the API is fully finalized and available --- src/vs/workbench/contrib/chat/common/chatModel.ts | 15 ++++++++++++--- .../workbench/contrib/chat/common/chatService.ts | 1 + .../contrib/chat/common/chatServiceImpl.ts | 6 +++--- .../vscode.proposed.chatAgents2Additions.d.ts | 7 +++++++ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 217fa004ea4..2b8e3e3292a 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -115,6 +115,11 @@ export class Response implements IResponse { return this._responseRepr; } + clear(): void { + this._responseParts = []; + this._updateRepr(true); + } + updateContent(progress: IChatProgressResponseContent | IChatContent, quiet?: boolean): void { if (progress.kind === 'content' || progress.kind === 'markdownContent') { const responsePartLength = this._responseParts.length - 1; @@ -310,7 +315,11 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel this._onDidChange.fire(); } - complete(): void { + complete(errorDetails?: IChatResponseErrorDetails): void { + if (errorDetails?.responseIsRedacted) { + this._response.clear(); + } + this._isComplete = true; this._onDidChange.fire(); } @@ -689,12 +698,12 @@ export class ChatModel extends Disposable implements IChatModel { request.response.setErrorDetails(rawResponse.errorDetails); } - completeResponse(request: ChatRequestModel): void { + completeResponse(request: ChatRequestModel, errorDetails: IChatResponseErrorDetails | undefined): void { if (!request.response) { throw new Error('Call setResponse before completeResponse'); } - request.response.complete(); + request.response.complete(errorDetails); } setFollowups(request: ChatRequestModel, followups: IChatFollowup[] | undefined): void { diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index c69e9da1b5e..a5376b97e0e 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -36,6 +36,7 @@ export interface IChatResponseErrorDetails { message: string; responseIsIncomplete?: boolean; responseIsFiltered?: boolean; + responseIsRedacted?: boolean; } export interface IChatResponse { diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 93946df9488..a6775318b6c 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -590,10 +590,10 @@ export class ChatService extends Disposable implements IChatService { if (agentOrCommandFollowups) { agentOrCommandFollowups.then(followups => { model.setFollowups(request, followups); - model.completeResponse(request); + model.completeResponse(request, rawResponse?.errorDetails); }); } else { - model.completeResponse(request); + model.completeResponse(request, rawResponse?.errorDetails); } } } finally { @@ -656,7 +656,7 @@ export class ChatService extends Disposable implements IChatService { if (response.followups !== undefined) { model.setFollowups(request, response.followups); } - model.completeResponse(request); + model.completeResponse(request, response.errorDetails); } cancelCurrentRequestForSession(sessionId: string): void { diff --git a/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts b/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts index aed7effd215..c0e889d41e0 100644 --- a/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts +++ b/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts @@ -15,6 +15,13 @@ declare module 'vscode' { supportIssueReporting?: boolean; } + export interface ChatAgentErrorDetails { + /** + * If set to true, the message content is completely hidden. Only ChatAgentErrorDetails#message will be shown. + */ + responseIsRedacted?: boolean; + } + /** * This is temporary until inline references are fully supported and adopted */ From 2e5e0760311327e38b81c66862a19edf54abb450 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 21 Nov 2023 06:28:22 +0100 Subject: [PATCH 83/87] aux window - commands to detach editors, groups and to restore (#198729) --- .../parts/editor/editor.contribution.ts | 9 +- .../browser/parts/editor/editorActions.ts | 144 ++++++++++++++++-- .../browser/parts/editor/editorPart.ts | 10 +- .../browser/parts/editor/editorParts.ts | 4 +- .../editor/common/editorGroupsService.ts | 4 +- .../test/browser/editorGroupsService.test.ts | 2 +- .../test/browser/workbenchTestServices.ts | 2 +- 7 files changed, 151 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 0f325909c06..8ba56b807fb 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -41,7 +41,7 @@ import { ReOpenInTextEditorAction, DuplicateGroupDownAction, DuplicateGroupLeftAction, DuplicateGroupRightAction, DuplicateGroupUpAction, ToggleEditorTypeAction, SplitEditorToAboveGroupAction, SplitEditorToBelowGroupAction, SplitEditorToFirstGroupAction, SplitEditorToLastGroupAction, SplitEditorToLeftGroupAction, SplitEditorToNextGroupAction, SplitEditorToPreviousGroupAction, SplitEditorToRightGroupAction, NavigateForwardInEditsAction, NavigateBackwardsInEditsAction, NavigateForwardInNavigationsAction, NavigateBackwardsInNavigationsAction, NavigatePreviousInNavigationsAction, NavigatePreviousInEditsAction, NavigateToLastNavigationLocationAction, - MaximizeGroupHideSidebarAction, ExperimentalMoveEditorIntoNewWindowAction, ToggleMaximizeEditorGroupAction, MinimizeOtherGroupsHideSidebarAction + MaximizeGroupHideSidebarAction, MoveEditorToNewDetachedWindowAction, CopyEditorToNewDetachedWindowAction, RestoreEditorsToMainWindowAction, ToggleMaximizeEditorGroupAction, MinimizeOtherGroupsHideSidebarAction, CopyEditorGroupToNewDetachedWindowAction, MoveEditorGroupToNewDetachedWindowAction } from 'vs/workbench/browser/parts/editor/editorActions'; import { CLOSE_EDITORS_AND_GROUP_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_EDITOR_GROUP_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, @@ -292,7 +292,12 @@ registerAction2(QuickAccessLeastRecentlyUsedEditorAction); registerAction2(QuickAccessPreviousRecentlyUsedEditorInGroupAction); registerAction2(QuickAccessLeastRecentlyUsedEditorInGroupAction); registerAction2(QuickAccessPreviousEditorFromHistoryAction); -registerAction2(ExperimentalMoveEditorIntoNewWindowAction); + +registerAction2(MoveEditorToNewDetachedWindowAction); +registerAction2(CopyEditorToNewDetachedWindowAction); +registerAction2(MoveEditorGroupToNewDetachedWindowAction); +registerAction2(CopyEditorGroupToNewDetachedWindowAction); +registerAction2(RestoreEditorsToMainWindowAction); const quickAccessNavigateNextInEditorPickerId = 'workbench.action.quickOpenNavigateNextInEditorPicker'; KeybindingsRegistry.registerCommandAndKeybindingRule({ diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index d199084e616..3b1850cc88d 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -14,7 +14,7 @@ import { GoFilter, IHistoryService } from 'vs/workbench/services/history/common/ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveCopyArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, COPY_ACTIVE_EDITOR_COMMAND_ID, SPLIT_EDITOR, resolveCommandsContext, getCommandsContext, TOGGLE_MAXIMIZE_EDITOR_GROUP } from 'vs/workbench/browser/parts/editor/editorCommands'; -import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder, MergeGroupMode } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; @@ -33,9 +33,10 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILogService } from 'vs/platform/log/common/log'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; -import { ActiveEditorAvailableEditorIdsContext, ActiveEditorContext, ActiveEditorGroupEmptyContext, AuxiliaryBarVisibleContext, EditorPartMaximizedEditorGroupContext, EditorPartMultipleEditorGroupsContext, MultipleEditorGroupsContext, SideBarVisibleContext } from 'vs/workbench/common/contextkeys'; +import { ActiveEditorAvailableEditorIdsContext, ActiveEditorContext, ActiveEditorGroupEmptyContext, AuxiliaryBarVisibleContext, EditorPartMaximizedEditorGroupContext, EditorPartMultipleEditorGroupsContext, IsAuxiliaryWindowFocusedContext, MultipleEditorGroupsContext, SideBarVisibleContext } from 'vs/workbench/common/contextkeys'; import { URI } from 'vs/base/common/uri'; import { getActiveDocument } from 'vs/base/browser/dom'; +import { ICommandActionTitle } from 'vs/platform/action/common/action'; class ExecuteCommandAction extends Action2 { @@ -225,7 +226,7 @@ export class JoinAllGroupsAction extends Action2 { override async run(accessor: ServicesAccessor): Promise { const editorGroupService = accessor.get(IEditorGroupsService); - editorGroupService.mergeAllGroups(); + editorGroupService.mergeAllGroups(editorGroupService.activeGroup); } } @@ -2485,16 +2486,17 @@ export class ReOpenInTextEditorAction extends Action2 { } } -export class ExperimentalMoveEditorIntoNewWindowAction extends Action2 { - constructor() { +abstract class BaseMoveCopyEditorToNewDetachedWindowAction extends Action2 { + + constructor( + id: string, + title: ICommandActionTitle, + private readonly move: boolean + ) { super({ - id: 'workbench.action.experimentalMoveEditorIntoNewWindowAction', - title: { - value: localize('popEditorOut', "Move Active Editor into a New Window (Experimental)"), - mnemonicTitle: localize({ key: 'miPopEditorOut', comment: ['&& denotes a mnemonic'] }, "&&Move Active Editor into a New Window (Experimental)"), - original: 'Move Active Editor into a New Window (Experimental)' - }, + id, + title, category: Categories.View, precondition: ActiveEditorContext, f1: true @@ -2512,6 +2514,124 @@ export class ExperimentalMoveEditorIntoNewWindowAction extends Action2 { const auxiliaryEditorPart = await editorGroupService.createAuxiliaryEditorPart(); - activeEditorPane.group.moveEditor(activeEditorPane.input, auxiliaryEditorPart.activeGroup); + if (this.move) { + activeEditorPane.group.moveEditor(activeEditorPane.input, auxiliaryEditorPart.activeGroup); + } else { + activeEditorPane.group.copyEditor(activeEditorPane.input, auxiliaryEditorPart.activeGroup); + } + + auxiliaryEditorPart.activeGroup.focus(); + } +} + +export class MoveEditorToNewDetachedWindowAction extends BaseMoveCopyEditorToNewDetachedWindowAction { + + constructor() { + super( + 'workbench.action.moveEditorToNewDetachedWindow', + { + value: localize('moveEditorToNewDetachedWindow', "Move Editor into a New Detached Window"), + mnemonicTitle: localize({ key: 'miMoveEditorToNewDetachedWindow', comment: ['&& denotes a mnemonic'] }, "&&Move Editor into a New Detached Window"), + original: 'Move Editor into a New Detached Window' + }, + true + ); + } +} + +export class CopyEditorToNewDetachedWindowAction extends BaseMoveCopyEditorToNewDetachedWindowAction { + + constructor() { + super( + 'workbench.action.copyEditorToNewDetachedWindow', + { + value: localize('copyEditorToNewDetachedWindow', "Copy Editor into a New Detached Window"), + mnemonicTitle: localize({ key: 'miCopyEditorToNewDetachedWindow', comment: ['&& denotes a mnemonic'] }, "&&Copy Editor into a New Detached Window"), + original: 'Copy Editor into a New Detached Window' + }, + false + ); + } +} + +abstract class BaseMoveCopyEditorGroupToNewDetachedWindowAction extends Action2 { + + constructor( + id: string, + title: ICommandActionTitle, + private readonly move: boolean + ) { + super({ + id, + title, + category: Categories.View, + f1: true + }); + } + + override async run(accessor: ServicesAccessor): Promise { + const editorGroupService = accessor.get(IEditorGroupsService); + const activeGroup = editorGroupService.activeGroup; + + const auxiliaryEditorPart = await editorGroupService.createAuxiliaryEditorPart(); + + editorGroupService.mergeGroup(activeGroup, auxiliaryEditorPart.activeGroup, { + mode: this.move ? MergeGroupMode.MOVE_EDITORS : MergeGroupMode.COPY_EDITORS + }); + + auxiliaryEditorPart.activeGroup.focus(); + } +} + +export class MoveEditorGroupToNewDetachedWindowAction extends BaseMoveCopyEditorGroupToNewDetachedWindowAction { + + constructor() { + super( + 'workbench.action.moveEditorGroupToNewDetachedWindow', + { + value: localize('moveEditorGroupToNewDetachedWindow', "Move Editor Group into a New Detached Window"), + mnemonicTitle: localize({ key: 'miMoveEditorGroupToNewDetachedWindow', comment: ['&& denotes a mnemonic'] }, "&&Move Editor Group into a New Detached Window"), + original: 'Move Editor Group into a New Detached Window' + }, + true + ); + } +} + +export class CopyEditorGroupToNewDetachedWindowAction extends BaseMoveCopyEditorGroupToNewDetachedWindowAction { + + constructor() { + super( + 'workbench.action.copyEditorGroupToNewDetachedWindow', + { + value: localize('copyEditorGroupToNewDetachedWindow', "Copy Editor Group into a New Detached Window"), + mnemonicTitle: localize({ key: 'miCopyEditorGroupToNewDetachedWindow', comment: ['&& denotes a mnemonic'] }, "&&Copy Editor Group into a New Detached Window"), + original: 'Copy Editor Group into a New Detached Window' + }, + false + ); + } +} + +export class RestoreEditorsToMainWindowAction extends Action2 { + + constructor() { + super({ + id: 'workbench.action.restoreDetachedEditorsToMainWindow', + title: { + value: localize('restoreDetachedEditorsToMainWindow', "Restore Detached Editors into Main Window"), + mnemonicTitle: localize({ key: 'miRestoreDetachedEditorsToMainWindow', comment: ['&& denotes a mnemonic'] }, "&&Restore Detached Editors into Main Window"), + original: 'Restore Detached Editors into Main Window' + }, + f1: true, + precondition: IsAuxiliaryWindowFocusedContext, + category: Categories.View + }); + } + + override async run(accessor: ServicesAccessor): Promise { + const editorGroupService = accessor.get(IEditorGroupsService); + + editorGroupService.mergeAllGroups(editorGroupService.mainPart.activeGroup); } } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 932001f18ca..dee3a78e751 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -910,16 +910,18 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { return targetView; } - mergeAllGroups(target = this.activeGroup): IEditorGroupView { + mergeAllGroups(target: IEditorGroupView | GroupIdentifier): IEditorGroupView { + const targetView = this.assertGroupView(target); + for (const group of this.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE)) { - if (group === target) { + if (group === targetView) { continue; // keep target } - this.mergeGroup(group, target); + this.mergeGroup(group, targetView); } - return target; + return targetView; } protected assertGroupView(group: IEditorGroupView | GroupIdentifier): IEditorGroupView { diff --git a/src/vs/workbench/browser/parts/editor/editorParts.ts b/src/vs/workbench/browser/parts/editor/editorParts.ts index 234ed040043..e616262b6cd 100644 --- a/src/vs/workbench/browser/parts/editor/editorParts.ts +++ b/src/vs/workbench/browser/parts/editor/editorParts.ts @@ -335,8 +335,8 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd return this.getPart(group).mergeGroup(group, target, options); } - mergeAllGroups(): IEditorGroupView { - return this.activePart.mergeAllGroups(); + mergeAllGroups(target: IEditorGroupView | GroupIdentifier): IEditorGroupView { + return this.activePart.mergeAllGroups(target); } copyGroup(group: IEditorGroupView | GroupIdentifier, location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView { diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index 1874fbe5728..8f441972a6d 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -361,9 +361,9 @@ export interface IEditorGroupsContainer { mergeGroup(group: IEditorGroup | GroupIdentifier, target: IEditorGroup | GroupIdentifier, options?: IMergeGroupOptions): IEditorGroup; /** - * Merge all editor groups into the active one. + * Merge all editor groups into the target one. */ - mergeAllGroups(): IEditorGroup; + mergeAllGroups(target: IEditorGroup | GroupIdentifier): IEditorGroup; /** * Copy a group to a new group in the container. 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 071e78a9f0e..b95fe353242 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -431,7 +431,7 @@ suite('EditorGroupsService', () => { assert.strictEqual(rootGroup.count, 1); - const result = part.mergeAllGroups(); + const result = part.mergeAllGroups(part.activeGroup); assert.strictEqual(result.id, rootGroup.id); assert.strictEqual(rootGroup.count, 3); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 170d5d05d98..14d46d46f4c 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -870,7 +870,7 @@ export class TestEditorGroupsService implements IEditorGroupsService { 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'); } - mergeAllGroups(): IEditorGroup { throw new Error('not implemented'); } + mergeAllGroups(_group: number | IEditorGroup): IEditorGroup { throw new Error('not implemented'); } copyGroup(_group: number | IEditorGroup, _location: number | IEditorGroup, _direction: GroupDirection): IEditorGroup { throw new Error('not implemented'); } centerLayout(active: boolean): void { } isLayoutCentered(): boolean { return false; } From 266cccf8960e5909e5eabb09b09ae31e413b4a74 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Mon, 20 Nov 2023 22:24:59 -0800 Subject: [PATCH 84/87] fix: use view range for glyph margin widgets (#198733) --- .../browser/viewParts/glyphMargin/glyphMargin.ts | 5 +++-- src/vs/editor/common/viewModel/viewModelLines.ts | 13 +++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts index 74e92ed7867..73236152541 100644 --- a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts +++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts @@ -275,13 +275,14 @@ export class GlyphMarginWidgets extends ViewPart { for (const widget of Object.values(this._widgets)) { const range = widget.preference.range; - if (range.endLineNumber < visibleStartLineNumber || range.startLineNumber > visibleEndLineNumber) { + const { startLineNumber, endLineNumber } = this._context.viewModel.coordinatesConverter.convertModelRangeToViewRange(Range.lift(range)); + if (!startLineNumber || !endLineNumber || endLineNumber < visibleStartLineNumber || startLineNumber > visibleEndLineNumber) { // The widget is not in the viewport continue; } // The widget is in the viewport, find a good line for it - const widgetLineNumber = Math.max(range.startLineNumber, visibleStartLineNumber); + const widgetLineNumber = Math.max(startLineNumber, visibleStartLineNumber); const lane = Math.min(widget.preference.lane, this._glyphMarginDecorationLaneCount); requests.push(new WidgetBasedGlyphRenderRequest(widgetLineNumber, lane, widget.preference.zIndex, widget)); } diff --git a/src/vs/editor/common/viewModel/viewModelLines.ts b/src/vs/editor/common/viewModel/viewModelLines.ts index c9fb7f3f662..bcabeffed2b 100644 --- a/src/vs/editor/common/viewModel/viewModelLines.ts +++ b/src/vs/editor/common/viewModel/viewModelLines.ts @@ -1308,6 +1308,19 @@ class IdentityCoordinatesConverter implements ICoordinatesConverter { return true; } + public modelRangeIsVisible(modelRange: Range): boolean { + const lineCount = this._lines.model.getLineCount(); + if (modelRange.startLineNumber < 1 || modelRange.startLineNumber > lineCount) { + // invalid arguments + return false; + } + if (modelRange.endLineNumber < 1 || modelRange.endLineNumber > lineCount) { + // invalid arguments + return false; + } + return true; + } + public getModelLineViewLineCount(modelLineNumber: number): number { return 1; } From 874468718cb4397f9091c80a259c155b32b59c54 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 21 Nov 2023 07:40:36 +0100 Subject: [PATCH 85/87] Browsing product icons through command palette shows tofu characters (fix #198687) (#198731) --- src/vs/code/electron-sandbox/workbench/workbench-dev.html | 1 + src/vs/code/electron-sandbox/workbench/workbench.html | 1 + 2 files changed, 2 insertions(+) diff --git a/src/vs/code/electron-sandbox/workbench/workbench-dev.html b/src/vs/code/electron-sandbox/workbench/workbench-dev.html index 02f40f0b97e..a92bea242a4 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench-dev.html +++ b/src/vs/code/electron-sandbox/workbench/workbench-dev.html @@ -42,6 +42,7 @@ 'self' vscode-remote-resource: vscode-managed-remote-resource: + https://*.vscode-unpkg.net ; require-trusted-types-for 'script' diff --git a/src/vs/code/electron-sandbox/workbench/workbench.html b/src/vs/code/electron-sandbox/workbench/workbench.html index c787d56e30b..eb525bd59f6 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench.html +++ b/src/vs/code/electron-sandbox/workbench/workbench.html @@ -42,6 +42,7 @@ 'self' vscode-remote-resource: vscode-managed-remote-resource: + https://*.vscode-unpkg.net ; require-trusted-types-for 'script' From bb6d92668e4deefc4d6d8d8d1b5970ee45d83ae0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 21 Nov 2023 07:41:06 +0100 Subject: [PATCH 86/87] Aux window: make `findGroup` work across multiple editor groups (fix #198651) (#198734) --- .../browser/parts/editor/editorPanes.ts | 2 +- .../browser/parts/editor/editorParts.ts | 63 ++++++++++++++++--- .../browser/workbench.contribution.ts | 2 +- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorPanes.ts b/src/vs/workbench/browser/parts/editor/editorPanes.ts index f57b9e54e1c..2f72fc9740e 100644 --- a/src/vs/workbench/browser/parts/editor/editorPanes.ts +++ b/src/vs/workbench/browser/parts/editor/editorPanes.ts @@ -132,7 +132,7 @@ export class EditorPanes extends Disposable { // Assert the `EditorInputCapabilities.AuxWindowUnsupported` condition if (getWindow(this.editorPanesParent) !== mainWindow && editor.hasCapability(EditorInputCapabilities.AuxWindowUnsupported)) { - return await this.doShowError(createEditorOpenError(localize('editorUnsupportedInAuxWindow', "This type of editor cannot be opened in floating windows yet."), [ + return await this.doShowError(createEditorOpenError(localize('editorUnsupportedInAuxWindow', "This type of editor cannot be opened in detached windows yet."), [ toAction({ id: 'workbench.editor.action.closeEditor', label: localize('openFolder', "Close Editor"), run: async () => { return this.groupView.closeEditor(editor); diff --git a/src/vs/workbench/browser/parts/editor/editorParts.ts b/src/vs/workbench/browser/parts/editor/editorParts.ts index e616262b6cd..2ce17e894ca 100644 --- a/src/vs/workbench/browser/parts/editor/editorParts.ts +++ b/src/vs/workbench/browser/parts/editor/editorParts.ts @@ -261,6 +261,21 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd return this.mainPart.getGroup(identifier); } + private assertGroupView(group: IEditorGroupView | GroupIdentifier): IEditorGroupView { + let groupView: IEditorGroupView | undefined; + if (typeof group === 'number') { + groupView = this.getGroup(group); + } else { + groupView = group; + } + + if (!groupView) { + throw new Error('Invalid editor group provided!'); + } + + return groupView; + } + activateGroup(group: IEditorGroupView | GroupIdentifier): IEditorGroupView { return this.getPart(group).activateGroup(group); } @@ -305,18 +320,46 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd this.activePart.setGroupOrientation(orientation); } - findGroup(scope: IFindGroupScope, source?: IEditorGroupView | GroupIdentifier, wrap?: boolean): IEditorGroupView | undefined { - if (scope.location === GroupLocation.FIRST || scope.location === GroupLocation.LAST) { - // TODO implement support for all scopes with multiple editor parts - // https://github.com/microsoft/vscode/issues/198651 - return scope.location === GroupLocation.FIRST ? this.groups[0] : this.groups[this.groups.length - 1]; + findGroup(scope: IFindGroupScope, source: IEditorGroupView | GroupIdentifier = this.activeGroup, wrap?: boolean): IEditorGroupView | undefined { + const sourcePart = this.getPart(source); + if (this._parts.size > 1) { + const groups = this.getGroups(GroupsOrder.GRID_APPEARANCE); + + // Ensure that FIRST/LAST dispatches globally over all parts + if (scope.location === GroupLocation.FIRST || scope.location === GroupLocation.LAST) { + return scope.location === GroupLocation.FIRST ? groups[0] : groups[groups.length - 1]; + } + + // Try to find in target part first without wrapping + const group = sourcePart.findGroup(scope, source, false); + if (group) { + return group; + } + + // Ensure that NEXT/PREVIOUS dispatches globally over all parts + if (scope.location === GroupLocation.NEXT || scope.location === GroupLocation.PREVIOUS) { + const sourceGroup = this.assertGroupView(source); + const index = groups.indexOf(sourceGroup); + + if (scope.location === GroupLocation.NEXT) { + let nextGroup: IEditorGroupView | undefined = groups[index + 1]; + if (!nextGroup && wrap) { + nextGroup = groups[0]; + } + + return nextGroup; + } else { + let previousGroup: IEditorGroupView | undefined = groups[index - 1]; + if (!previousGroup && wrap) { + previousGroup = groups[groups.length - 1]; + } + + return previousGroup; + } + } } - if (source) { - return this.getPart(source).findGroup(scope, source, wrap); - } - - return this.activePart.findGroup(scope, source, wrap); + return sourcePart.findGroup(scope, source, wrap); } addGroup(location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index a0c22da313c..a435aa0ccdf 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -247,7 +247,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'workbench.editor.dragToOpenWindow': { 'type': 'boolean', 'default': product.quality !== 'stable', - 'markdownDescription': localize('dragToOpenWindow', "Controls if editors can be dragged out of the window to open them in a new floating window. Press and hold `Alt`-key while dragging to toggle this dynamically.") + 'markdownDescription': localize('dragToOpenWindow', "Controls if editors can be dragged out of the window to open them in a new detached window. Press and hold `Alt`-key while dragging to toggle this dynamically.") }, 'workbench.editor.focusRecentEditorAfterClose': { 'type': 'boolean', From 633efd8c55aabe982be0ea5db97983c85e1363d0 Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Tue, 21 Nov 2023 09:22:26 +0100 Subject: [PATCH 87/87] Update codicons: add lightbulb-sparkle (#198705) --- .../browser/ui/codicons/codicon/codicon.ttf | Bin 76140 -> 76544 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 0694339a3f89b59dd3b47bc1cac2cd5f526330de..38e4c223beaf1dabad53dc93cd02ed784ae6a94a 100644 GIT binary patch delta 4412 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 delta 3938 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 diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index c314f5169b4..16c6df2478c 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -580,6 +580,7 @@ export const Codicon = { micFilled: register('mic-filled', 0xec1c), gitFetch: register('git-fetch', 0xec1d), copilot: register('copilot', 0xec1e), + lightbulbSparkle: register('lightbulb-sparkle', 0xec1f), // derived icons, that could become separate icons