From 51ea68dcd23eeb0235e07b854c51f03c1ce60ca5 Mon Sep 17 00:00:00 2001 From: Jean Pierre Date: Mon, 11 Dec 2023 16:39:38 -0500 Subject: [PATCH 0001/1175] Fixes #200590 --- src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 7094cc9c41e..b84a3d8a8b3 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -57,6 +57,7 @@ export class BreadcrumbsWidget { private _focusedItemIdx: number = -1; private _selectedItemIdx: number = -1; + private _pendingDimLayout: IDisposable | undefined; private _pendingLayout: IDisposable | undefined; private _dimension: dom.Dimension | undefined; @@ -100,6 +101,7 @@ export class BreadcrumbsWidget { dispose(): void { this._disposables.dispose(); this._pendingLayout?.dispose(); + this._pendingDimLayout?.dispose(); this._onDidSelectItem.dispose(); this._onDidFocusItem.dispose(); this._onDidChangeFocus.dispose(); @@ -112,11 +114,12 @@ export class BreadcrumbsWidget { if (dim && dom.Dimension.equals(dim, this._dimension)) { return; } - this._pendingLayout?.dispose(); if (dim) { // only measure - this._pendingLayout = this._updateDimensions(dim); + this._pendingDimLayout?.dispose(); + this._pendingDimLayout = this._updateDimensions(dim); } else { + this._pendingLayout?.dispose(); this._pendingLayout = this._updateScrollbar(); } } From bac1926029c174998664be4a0ae977e2e787481d Mon Sep 17 00:00:00 2001 From: Russell Davis <551404+russelldavis@users.noreply.github.com> Date: Tue, 17 Oct 2023 20:30:41 -0700 Subject: [PATCH 0002/1175] Fix decreaseIndentPattern for javascript and typescript Fixes #201424 It wasn't matching closing parens, which resulted in these issues: * Pressing enter with just a closing paren to the right of the caret wouldn't result in a dedent on the next line * With the caret at the start of the line below a line containing only a closing paren, pressing tab would result in an extra level of indentation --- extensions/javascript/javascript-language-configuration.json | 2 +- extensions/typescript-basics/language-configuration.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/javascript/javascript-language-configuration.json b/extensions/javascript/javascript-language-configuration.json index 4029985233a..12f6e5cac1f 100644 --- a/extensions/javascript/javascript-language-configuration.json +++ b/extensions/javascript/javascript-language-configuration.json @@ -111,7 +111,7 @@ }, "indentationRules": { "decreaseIndentPattern": { - "pattern": "^((?!.*?/\\*).*\\*\/)?\\s*[\\}\\]].*$" + "pattern": "^((?!.*?/\\*).*\\*\/)?\\s*[\\}\\]\\)].*$" }, "increaseIndentPattern": { "pattern": "^((?!//).)*(\\{([^}\"'`/]*|(\\t|[ ])*//.*)|\\([^)\"'`/]*|\\[[^\\]\"'`/]*)$" diff --git a/extensions/typescript-basics/language-configuration.json b/extensions/typescript-basics/language-configuration.json index 03f06fa04d5..45657928dbc 100644 --- a/extensions/typescript-basics/language-configuration.json +++ b/extensions/typescript-basics/language-configuration.json @@ -129,7 +129,7 @@ }, "indentationRules": { "decreaseIndentPattern": { - "pattern": "^((?!.*?/\\*).*\\*\/)?\\s*[\\}\\]].*$" + "pattern": "^((?!.*?/\\*).*\\*\/)?\\s*[\\}\\]\\)].*$" }, "increaseIndentPattern": { "pattern": "^((?!//).)*(\\{([^}\"'`/]*|(\\t|[ ])*//.*)|\\([^)\"'`/]*|\\[[^\\]\"'`/]*)$" From dad5b331eaee2d4e7f87061b412097c21e98ef81 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 9 Feb 2024 10:07:17 -0600 Subject: [PATCH 0003/1175] add basics --- src/vs/platform/terminal/common/terminal.ts | 4 + .../terminal/browser/media/terminal.css | 11 ++ .../contrib/terminal/common/terminal.ts | 4 + .../terminal/common/terminalContextKey.ts | 17 ++ .../contrib/terminal/terminal.all.ts | 1 + .../browser/terminal.chat.contribution.ts | 160 ++++++++++++++++++ .../chat/browser/terminalChatWidget.ts | 76 +++++++++ 7 files changed, 273 insertions(+) create mode 100644 src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts create mode 100644 src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index ffe0e56a189..56ebf9ddae5 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -121,6 +121,10 @@ export const enum TerminalSettingId { StickyScrollEnabled = 'terminal.integrated.stickyScroll.enabled', StickyScrollMaxLineCount = 'terminal.integrated.stickyScroll.maxLineCount', MouseWheelZoom = 'terminal.integrated.mouseWheelZoom', + FocusChat = 'workbench.action.terminal.focusChat', + HideChat = 'workbench.action.terminal.hideChat', + SubmitChat = 'workbench.action.terminal.submitChat', + CancelChat = 'workbench.action.terminal.cancelChat', // Debug settings that are hidden from user diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 488815cf258..a6ea80d22d6 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -565,3 +565,14 @@ .monaco-workbench .xterm.terminal.hide { visibility: hidden; } + +.monaco-workbench .terminal-chat-widget { + z-index: 33 !important; + position: absolute; + bottom: 30px; + left: 10px; +} + +.monaco-workbench .terminal-chat-widget.hide { + visibility: hidden; +} diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index fc925b646bb..0579628b521 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -500,6 +500,10 @@ export const enum TerminalCommandId { FontZoomIn = 'workbench.action.terminal.fontZoomIn', FontZoomOut = 'workbench.action.terminal.fontZoomOut', FontZoomReset = 'workbench.action.terminal.fontZoomReset', + FocusChat = 'workbench.action.terminal.focusChat', + HideChat = 'workbench.action.terminal.hideChat', + SubmitChat = 'workbench.action.terminal.submitChat', + CancelChat = 'workbench.action.terminal.cancelChat', // Developer commands diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index 72335480aee..1de53d61930 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -39,6 +39,10 @@ export const enum TerminalContextKeyStrings { ShellType = 'terminalShellType', InTerminalRunCommandPicker = 'inTerminalRunCommandPicker', TerminalShellIntegrationEnabled = 'terminalShellIntegrationEnabled', + ChatFocus = 'terminalChatFocus', + ChatVisible = 'terminalChatVisible', + ChatSessionInProgress = 'terminalChatSessionInProgress', + ChatInputHasText = 'terminalChatInputHasText', } export namespace TerminalContextKeys { @@ -158,4 +162,17 @@ export namespace TerminalContextKeys { ContextKeyExpr.equals(`config.${TerminalSettingId.TabsShowActions}`, 'always') ) ); + + + /** Whether the chat widget is focused */ + export const chatFocused = new RawContextKey(TerminalContextKeyStrings.ChatFocus, false, localize('chatFocusedContextKey', "Whether the chat view is focused.")); + + /** Whether the chat widget is visible */ + export const chatVisible = new RawContextKey(TerminalContextKeyStrings.ChatVisible, false, localize('chatVisibleContextKey', "Whether the chat view is visible.")); + + /** Whether a chat session is in progress */ + export const chatSessionInProgress = new RawContextKey(TerminalContextKeyStrings.ChatSessionInProgress, false, localize('chatSessionInProgressContextKey', "Whether a chat session is in progress.")); + + /** Whether the chat input has text */ + export const chatInputHasText = new RawContextKey(TerminalContextKeyStrings.ChatInputHasText, false, localize('chatInputHasTextContextKey', "Whether the chat input has text.")); } diff --git a/src/vs/workbench/contrib/terminal/terminal.all.ts b/src/vs/workbench/contrib/terminal/terminal.all.ts index b49aa829f7b..e9cdc253212 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/chat/browser/terminal.chat.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/zoom/browser/terminal.zoom.contribution'; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts new file mode 100644 index 00000000000..bb9cbd24540 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -0,0 +1,160 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDimension } from 'vs/base/browser/dom'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { Lazy } from 'vs/base/common/lazy'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { localize2 } from 'vs/nls'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; +import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; +import { ITerminalProcessInfo, ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; +import { Codicon } from 'vs/base/common/codicons'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget'; + +export class TerminalChatContribution extends Disposable implements ITerminalContribution { + static readonly ID = 'terminal.Chat'; + + /** + * Currently focused Chat widget. This is used to track action context since + * 'active terminals' are only tracked for non-detached terminal instanecs. + */ + static activeChatWidget?: TerminalChatContribution; + + static get(instance: ITerminalInstance | IDetachedTerminalInstance): TerminalChatContribution | null { + return instance.getContribution(TerminalChatContribution.ID); + } + + private _chatWidget: Lazy | undefined; + private _lastLayoutDimensions: IDimension | undefined; + + get chatWidget(): TerminalChatWidget | undefined { return this._chatWidget?.value; } + + constructor( + private readonly _instance: ITerminalInstance | IDetachedTerminalInstance, + processManager: ITerminalProcessManager | ITerminalProcessInfo, + widgetManager: TerminalWidgetManager, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ITerminalService private readonly _terminalService: ITerminalService + ) { + super(); + } + + layout(_xterm: IXtermTerminal & { raw: RawXtermTerminal }, dimension: IDimension): void { + this._lastLayoutDimensions = dimension; + this._chatWidget?.rawValue?.layout(dimension.width); + } + + xtermReady(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { + this._chatWidget = new Lazy(() => { + const chatWidget = this._instantiationService.createInstance(TerminalChatWidget, this._instance.domElement!, this._instance); + + // Track focus and set state so we can force the scroll bar to be visible + chatWidget.onDidFocus(() => { + TerminalChatContribution.activeChatWidget = this; + this._instance.forceScrollbarVisibility(); + if (!isDetachedTerminalInstance(this._instance)) { + this._terminalService.setActiveInstance(this._instance); + } + }); + // chatWidget.onDidBlur(() => { + // TerminalChatContribution.activeChatWidget = undefined; + // this._instance.resetScrollbarVisibility(); + // }); + + if (!this._instance.domElement) { + throw new Error('FindWidget expected terminal DOM to be initialized'); + } + + // this._instance.domElement?.appendChild(chatWidget.getDomNode()); + if (this._lastLayoutDimensions) { + chatWidget.layout(this._lastLayoutDimensions.width); + } + + return chatWidget; + }); + } + + override dispose() { + if (TerminalChatContribution.activeChatWidget === this) { + TerminalChatContribution.activeChatWidget = undefined; + } + super.dispose(); + this._chatWidget?.rawValue?.dispose(); + } +} +registerTerminalContribution(TerminalChatContribution.ID, TerminalChatContribution, true); + +registerActiveXtermAction({ + id: TerminalCommandId.FocusChat, + title: localize2('workbench.action.terminal.focusChat', 'Terminal: Focus Chat'), + keybinding: { + primary: KeyMod.CtrlCmd | KeyCode.KeyI, + when: ContextKeyExpr.or(TerminalContextKeys.chatFocused, TerminalContextKeys.focusInAny), + weight: KeybindingWeight.WorkbenchContrib + }, + f1: true, + precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + run: (_xterm, _accessor, activeInstance) => { + const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); + contr?.chatWidget?.reveal(); + } +}); + +registerActiveXtermAction({ + id: TerminalCommandId.HideChat, + title: localize2('workbench.action.terminal.hideChat', 'Terminal: Hide Chat'), + keybinding: { + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape], + when: ContextKeyExpr.and(TerminalContextKeys.chatFocused, TerminalContextKeys.chatVisible), + weight: KeybindingWeight.WorkbenchContrib + }, + f1: true, + precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + run: (_xterm, _accessor, activeInstance) => { + const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); + contr?.chatWidget?.hide(); + } +}); + +registerActiveXtermAction({ + id: TerminalCommandId.SubmitChat, + title: localize2('workbench.action.terminal.submitChat', 'Terminal: Submit Chat'), + precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.chatInputHasText), + icon: Codicon.send, + menu: { + id: MenuId.ChatExecute, + when: TerminalContextKeys.chatSessionInProgress.negate(), + group: 'navigation', + }, + run: (_xterm, _accessor, activeInstance) => { + const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); + contr?.chatWidget?.acceptInput(); + } +}); + +registerActiveXtermAction({ + id: TerminalCommandId.CancelChat, + title: localize2('workbench.action.terminal.cancelChat', 'Cancel Chat'), + precondition: TerminalContextKeys.chatSessionInProgress, + icon: Codicon.debugStop, + menu: { + id: MenuId.ChatExecute, + group: 'navigation', + }, + run: (_xterm, _accessor, activeInstance) => { + const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); + contr?.chatWidget?.cancel(); + } +}); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts new file mode 100644 index 00000000000..5b44b0a0cf7 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { editorBackground, editorForeground, inputBackground } from 'vs/platform/theme/common/colorRegistry'; +import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; +import { IDetachedTerminalInstance, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; + +export class TerminalChatWidget extends Disposable { + private _widget: ChatWidget | undefined; + private _scopedInstantiationService: IInstantiationService; + private readonly _onDidFocus = this._register(new Emitter()); + readonly onDidFocus = this._onDidFocus.event; + private _widgetContainer: HTMLElement | undefined; + private _chatWidgetFocused: IContextKey; + private _chatWidgetVisible: IContextKey; + constructor( + private readonly _container: HTMLElement, + private readonly _instance: ITerminalInstance | IDetachedTerminalInstance, + + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService) { + super(); + const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._container)); + this._scopedInstantiationService = this._instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); + this._chatWidgetFocused = TerminalContextKeys.chatFocused.bindTo(this._contextKeyService); + this._chatWidgetVisible = TerminalContextKeys.chatVisible.bindTo(this._contextKeyService); + } + reveal(): void { + this._widgetContainer = document.createElement('div'); + this._widgetContainer.classList.add('terminal-chat-widget'); + this._widget = this._register(this._scopedInstantiationService.createInstance( + ChatWidget, + { viewId: 'terminal' }, + { supportsFileReferences: false, renderStyle: 'compact' }, + { + listForeground: editorForeground, + listBackground: editorBackground, + inputEditorBackground: inputBackground, + resultEditorBackground: editorBackground + })); + this._widget.render(this._widgetContainer); + this._container.appendChild(this._widgetContainer); + this._register(this._widget.onDidFocus(() => { + this._onDidFocus.fire(); + this._chatWidgetFocused.set(true); + })); + this._widget.setVisible(true); + this._chatWidgetVisible.set(true); + this._widget.setInput('@terminal'); + this._widget.setInputPlaceholder('Request a terminal command'); + this._widget.focusInput(); + } + hide(): void { + if (this._widgetContainer) { + this._container.removeChild(this._widgetContainer); + } + this._chatWidgetVisible.set(false); + } + cancel(): void { + this._widget?.clear(); + } + acceptInput(): void { + this._widget?.acceptInput(); + } + layout(width: number): void { + this._widget?.layout(40, width); + } +} From 4f219d7a161c595d66dce6e376aa77f6703a5158 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 9 Feb 2024 10:11:24 -0600 Subject: [PATCH 0004/1175] fix some issues --- .../browser/terminal.chat.contribution.ts | 36 +++---------------- .../chat/browser/terminalChatWidget.ts | 5 +-- 2 files changed, 6 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index bb9cbd24540..0f803200129 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -11,7 +11,7 @@ import { localize2 } from 'vs/nls'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; @@ -25,12 +25,6 @@ import { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/br export class TerminalChatContribution extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.Chat'; - /** - * Currently focused Chat widget. This is used to track action context since - * 'active terminals' are only tracked for non-detached terminal instanecs. - */ - static activeChatWidget?: TerminalChatContribution; - static get(instance: ITerminalInstance | IDetachedTerminalInstance): TerminalChatContribution | null { return instance.getContribution(TerminalChatContribution.ID); } @@ -59,19 +53,6 @@ export class TerminalChatContribution extends Disposable implements ITerminalCon this._chatWidget = new Lazy(() => { const chatWidget = this._instantiationService.createInstance(TerminalChatWidget, this._instance.domElement!, this._instance); - // Track focus and set state so we can force the scroll bar to be visible - chatWidget.onDidFocus(() => { - TerminalChatContribution.activeChatWidget = this; - this._instance.forceScrollbarVisibility(); - if (!isDetachedTerminalInstance(this._instance)) { - this._terminalService.setActiveInstance(this._instance); - } - }); - // chatWidget.onDidBlur(() => { - // TerminalChatContribution.activeChatWidget = undefined; - // this._instance.resetScrollbarVisibility(); - // }); - if (!this._instance.domElement) { throw new Error('FindWidget expected terminal DOM to be initialized'); } @@ -86,9 +67,6 @@ export class TerminalChatContribution extends Disposable implements ITerminalCon } override dispose() { - if (TerminalChatContribution.activeChatWidget === this) { - TerminalChatContribution.activeChatWidget = undefined; - } super.dispose(); this._chatWidget?.rawValue?.dispose(); } @@ -106,8 +84,7 @@ registerActiveXtermAction({ f1: true, precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), run: (_xterm, _accessor, activeInstance) => { - const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); - contr?.chatWidget?.reveal(); + TerminalChatContribution.get(activeInstance)?.chatWidget?.reveal(); } }); @@ -123,8 +100,7 @@ registerActiveXtermAction({ f1: true, precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), run: (_xterm, _accessor, activeInstance) => { - const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); - contr?.chatWidget?.hide(); + TerminalChatContribution.get(activeInstance)?.chatWidget?.hide(); } }); @@ -139,8 +115,7 @@ registerActiveXtermAction({ group: 'navigation', }, run: (_xterm, _accessor, activeInstance) => { - const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); - contr?.chatWidget?.acceptInput(); + TerminalChatContribution.get(activeInstance)?.chatWidget?.acceptInput(); } }); @@ -154,7 +129,6 @@ registerActiveXtermAction({ group: 'navigation', }, run: (_xterm, _accessor, activeInstance) => { - const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); - contr?.chatWidget?.cancel(); + TerminalChatContribution.get(activeInstance)?.chatWidget?.cancel(); } }); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 5b44b0a0cf7..b6dbcfa67ee 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -16,8 +15,6 @@ import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/termin export class TerminalChatWidget extends Disposable { private _widget: ChatWidget | undefined; private _scopedInstantiationService: IInstantiationService; - private readonly _onDidFocus = this._register(new Emitter()); - readonly onDidFocus = this._onDidFocus.event; private _widgetContainer: HTMLElement | undefined; private _chatWidgetFocused: IContextKey; private _chatWidgetVisible: IContextKey; @@ -49,7 +46,6 @@ export class TerminalChatWidget extends Disposable { this._widget.render(this._widgetContainer); this._container.appendChild(this._widgetContainer); this._register(this._widget.onDidFocus(() => { - this._onDidFocus.fire(); this._chatWidgetFocused.set(true); })); this._widget.setVisible(true); @@ -63,6 +59,7 @@ export class TerminalChatWidget extends Disposable { this._container.removeChild(this._widgetContainer); } this._chatWidgetVisible.set(false); + this._instance.focus(); } cancel(): void { this._widget?.clear(); From ab4a6959f7298fbd463daf364309350e16c40812 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 9 Feb 2024 10:44:29 -0600 Subject: [PATCH 0005/1175] add hold for speech --- .../contrib/inlineChat/electron-sandbox/inlineChatActions.ts | 5 +++-- .../chat/browser/terminal.chat.contribution.ts | 4 ++-- .../terminalContrib/chat/browser/terminalChatWidget.ts | 4 +--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts index d30d230a2b7..ec8c7cd1c47 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts @@ -20,16 +20,17 @@ import { HasSpeechProvider, ISpeechService } from 'vs/workbench/contrib/speech/c import { localize2 } from 'vs/nls'; import { Action2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; export class HoldToSpeak extends AbstractInlineChatAction { constructor() { super({ id: 'inlineChat.holdForSpeech', - precondition: ContextKeyExpr.and(HasSpeechProvider, CTX_INLINE_CHAT_VISIBLE), + precondition: ContextKeyExpr.and(HasSpeechProvider, ContextKeyExpr.or(CTX_INLINE_CHAT_VISIBLE, TerminalContextKeys.chatVisible)), title: localize2('holdForSpeech', "Hold for Speech"), keybinding: { - when: EditorContextKeys.textInputFocus, + when: ContextKeyExpr.or(EditorContextKeys.textInputFocus, TerminalContextKeys.chatFocused), weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.KeyI, }, diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index 0f803200129..46bef94963c 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -39,7 +39,7 @@ export class TerminalChatContribution extends Disposable implements ITerminalCon processManager: ITerminalProcessManager | ITerminalProcessInfo, widgetManager: TerminalWidgetManager, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @ITerminalService private readonly _terminalService: ITerminalService + @ITerminalService terminalService: ITerminalService ) { super(); } @@ -78,7 +78,7 @@ registerActiveXtermAction({ title: localize2('workbench.action.terminal.focusChat', 'Terminal: Focus Chat'), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyI, - when: ContextKeyExpr.or(TerminalContextKeys.chatFocused, TerminalContextKeys.focusInAny), + when: ContextKeyExpr.and(TerminalContextKeys.chatFocused.negate(), TerminalContextKeys.focusInAny), weight: KeybindingWeight.WorkbenchContrib }, f1: true, diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index b6dbcfa67ee..cb2db4bb1be 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -45,9 +45,7 @@ export class TerminalChatWidget extends Disposable { })); this._widget.render(this._widgetContainer); this._container.appendChild(this._widgetContainer); - this._register(this._widget.onDidFocus(() => { - this._chatWidgetFocused.set(true); - })); + this._register(this._widget.onDidFocus(() => this._chatWidgetFocused.set(true))); this._widget.setVisible(true); this._chatWidgetVisible.set(true); this._widget.setInput('@terminal'); From dc4ebd856d49a1ae73b93214f635d755a1f0ad58 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 9 Feb 2024 11:04:23 -0600 Subject: [PATCH 0006/1175] fix some issues --- .../terminal/browser/media/terminal.css | 3 +-- .../chat/browser/terminalChatWidget.ts | 20 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index a6ea80d22d6..8fc9e6be0eb 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -569,8 +569,7 @@ .monaco-workbench .terminal-chat-widget { z-index: 33 !important; position: absolute; - bottom: 30px; - left: 10px; + top: 10px; } .monaco-workbench .terminal-chat-widget.hide { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index cb2db4bb1be..4f0c787fc6b 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -13,9 +13,9 @@ import { IDetachedTerminalInstance, ITerminalInstance } from 'vs/workbench/contr import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; export class TerminalChatWidget extends Disposable { - private _widget: ChatWidget | undefined; + private _widget: ChatWidget; private _scopedInstantiationService: IInstantiationService; - private _widgetContainer: HTMLElement | undefined; + private _widgetContainer: HTMLElement; private _chatWidgetFocused: IContextKey; private _chatWidgetVisible: IContextKey; constructor( @@ -29,10 +29,9 @@ export class TerminalChatWidget extends Disposable { this._scopedInstantiationService = this._instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); this._chatWidgetFocused = TerminalContextKeys.chatFocused.bindTo(this._contextKeyService); this._chatWidgetVisible = TerminalContextKeys.chatVisible.bindTo(this._contextKeyService); - } - reveal(): void { this._widgetContainer = document.createElement('div'); this._widgetContainer.classList.add('terminal-chat-widget'); + this._container.appendChild(this._widgetContainer); this._widget = this._register(this._scopedInstantiationService.createInstance( ChatWidget, { viewId: 'terminal' }, @@ -44,19 +43,22 @@ export class TerminalChatWidget extends Disposable { resultEditorBackground: editorBackground })); this._widget.render(this._widgetContainer); - this._container.appendChild(this._widgetContainer); this._register(this._widget.onDidFocus(() => this._chatWidgetFocused.set(true))); + } + reveal(): void { + this._widgetContainer.classList.remove('hide'); this._widget.setVisible(true); + this._chatWidgetFocused.set(true); this._chatWidgetVisible.set(true); this._widget.setInput('@terminal'); this._widget.setInputPlaceholder('Request a terminal command'); this._widget.focusInput(); } hide(): void { - if (this._widgetContainer) { - this._container.removeChild(this._widgetContainer); - } + this._widgetContainer.classList.add('hide'); + this._chatWidgetFocused.set(false); this._chatWidgetVisible.set(false); + this._widget.clear(); this._instance.focus(); } cancel(): void { @@ -66,6 +68,6 @@ export class TerminalChatWidget extends Disposable { this._widget?.acceptInput(); } layout(width: number): void { - this._widget?.layout(40, width); + this._widget?.layout(100, width < 300 ? 300 : width); } } From eedd668787628d52ff190f60a52a6f49522500b6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:23:24 -0800 Subject: [PATCH 0007/1175] Add setting to disable term inline chat by default --- src/vs/platform/terminal/common/terminal.ts | 5 +-- .../terminal/common/terminalConfiguration.ts | 5 +++ .../browser/terminal.chat.contribution.ts | 33 ++++++++++++++++--- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 56ebf9ddae5..6d291481c7f 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -121,10 +121,7 @@ export const enum TerminalSettingId { StickyScrollEnabled = 'terminal.integrated.stickyScroll.enabled', StickyScrollMaxLineCount = 'terminal.integrated.stickyScroll.maxLineCount', MouseWheelZoom = 'terminal.integrated.mouseWheelZoom', - FocusChat = 'workbench.action.terminal.focusChat', - HideChat = 'workbench.action.terminal.hideChat', - SubmitChat = 'workbench.action.terminal.submitChat', - CancelChat = 'workbench.action.terminal.cancelChat', + ExperimentalInlineChat = 'workbench.action.terminal.experimentalInlineChat', // Debug settings that are hidden from user diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index ac0903b8ae3..397ff3bc97a 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -653,6 +653,11 @@ const terminalConfiguration: IConfigurationNode = { type: 'boolean', default: false }, + [TerminalSettingId.ExperimentalInlineChat]: { + markdownDescription: localize('terminal.integrated.experimentalInlineChat', "Whether to enable the upcoming experimental inline terminal chat UI."), + type: 'boolean', + default: false + } } }; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index 46bef94963c..c0ee3d62f1b 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -21,6 +21,8 @@ import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; import { Codicon } from 'vs/base/common/codicons'; import { MenuId } from 'vs/platform/actions/common/actions'; import { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget'; +import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class TerminalChatContribution extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.Chat'; @@ -39,17 +41,27 @@ export class TerminalChatContribution extends Disposable implements ITerminalCon processManager: ITerminalProcessManager | ITerminalProcessInfo, widgetManager: TerminalWidgetManager, @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IConfigurationService private _configurationService: IConfigurationService, @ITerminalService terminalService: ITerminalService ) { super(); + if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { + return; + } } layout(_xterm: IXtermTerminal & { raw: RawXtermTerminal }, dimension: IDimension): void { + if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { + return; + } this._lastLayoutDimensions = dimension; this._chatWidget?.rawValue?.layout(dimension.width); } xtermReady(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { + if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { + return; + } this._chatWidget = new Lazy(() => { const chatWidget = this._instantiationService.createInstance(TerminalChatWidget, this._instance.domElement!, this._instance); @@ -82,7 +94,10 @@ registerActiveXtermAction({ weight: KeybindingWeight.WorkbenchContrib }, f1: true, - precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + ), run: (_xterm, _accessor, activeInstance) => { TerminalChatContribution.get(activeInstance)?.chatWidget?.reveal(); } @@ -98,7 +113,10 @@ registerActiveXtermAction({ weight: KeybindingWeight.WorkbenchContrib }, f1: true, - precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + ), run: (_xterm, _accessor, activeInstance) => { TerminalChatContribution.get(activeInstance)?.chatWidget?.hide(); } @@ -107,7 +125,11 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalCommandId.SubmitChat, title: localize2('workbench.action.terminal.submitChat', 'Terminal: Submit Chat'), - precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.chatInputHasText), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + TerminalContextKeys.chatInputHasText + ), icon: Codicon.send, menu: { id: MenuId.ChatExecute, @@ -122,7 +144,10 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalCommandId.CancelChat, title: localize2('workbench.action.terminal.cancelChat', 'Cancel Chat'), - precondition: TerminalContextKeys.chatSessionInProgress, + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + TerminalContextKeys.chatSessionInProgress, + ), icon: Codicon.debugStop, menu: { id: MenuId.ChatExecute, From 0d202eff3d87ae7cb3e943fb82fa9a98f6aeffcd Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 9 Feb 2024 11:46:48 -0600 Subject: [PATCH 0008/1175] widget per terminal --- .../browser/terminal.chat.contribution.ts | 33 ++++++++++++++----- .../chat/browser/terminalChatWidget.ts | 6 ++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index c0ee3d62f1b..b9fab74a3a2 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -11,7 +11,7 @@ import { localize2 } from 'vs/nls'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; @@ -30,7 +30,11 @@ export class TerminalChatContribution extends Disposable implements ITerminalCon static get(instance: ITerminalInstance | IDetachedTerminalInstance): TerminalChatContribution | null { return instance.getContribution(TerminalChatContribution.ID); } - + /** + * Currently focused chat widget. This is used to track action context since + * 'active terminals' are only tracked for non-detached terminal instanecs. + */ + static activeChatWidget?: TerminalChatContribution; private _chatWidget: Lazy | undefined; private _lastLayoutDimensions: IDimension | undefined; @@ -42,7 +46,7 @@ export class TerminalChatContribution extends Disposable implements ITerminalCon widgetManager: TerminalWidgetManager, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IConfigurationService private _configurationService: IConfigurationService, - @ITerminalService terminalService: ITerminalService + @ITerminalService private readonly _terminalService: ITerminalService ) { super(); if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { @@ -64,7 +68,16 @@ export class TerminalChatContribution extends Disposable implements ITerminalCon } this._chatWidget = new Lazy(() => { const chatWidget = this._instantiationService.createInstance(TerminalChatWidget, this._instance.domElement!, this._instance); - + chatWidget.focusTracker.onDidFocus(() => { + TerminalChatContribution.activeChatWidget = this; + if (!isDetachedTerminalInstance(this._instance)) { + this._terminalService.setActiveInstance(this._instance); + } + }); + chatWidget.focusTracker.onDidBlur(() => { + TerminalChatContribution.activeChatWidget = undefined; + this._instance.resetScrollbarVisibility(); + }); if (!this._instance.domElement) { throw new Error('FindWidget expected terminal DOM to be initialized'); } @@ -99,7 +112,8 @@ registerActiveXtermAction({ ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), ), run: (_xterm, _accessor, activeInstance) => { - TerminalChatContribution.get(activeInstance)?.chatWidget?.reveal(); + const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); + contr?.chatWidget?.reveal(); } }); @@ -118,7 +132,8 @@ registerActiveXtermAction({ ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), ), run: (_xterm, _accessor, activeInstance) => { - TerminalChatContribution.get(activeInstance)?.chatWidget?.hide(); + const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); + contr?.chatWidget?.hide(); } }); @@ -137,7 +152,8 @@ registerActiveXtermAction({ group: 'navigation', }, run: (_xterm, _accessor, activeInstance) => { - TerminalChatContribution.get(activeInstance)?.chatWidget?.acceptInput(); + const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); + contr?.chatWidget?.acceptInput(); } }); @@ -154,6 +170,7 @@ registerActiveXtermAction({ group: 'navigation', }, run: (_xterm, _accessor, activeInstance) => { - TerminalChatContribution.get(activeInstance)?.chatWidget?.cancel(); + const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); + contr?.chatWidget?.cancel(); } }); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 4f0c787fc6b..de48c32fcf3 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IFocusTracker, trackFocus } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -18,6 +19,7 @@ export class TerminalChatWidget extends Disposable { private _widgetContainer: HTMLElement; private _chatWidgetFocused: IContextKey; private _chatWidgetVisible: IContextKey; + private readonly _focusTracker: IFocusTracker; constructor( private readonly _container: HTMLElement, private readonly _instance: ITerminalInstance | IDetachedTerminalInstance, @@ -44,6 +46,7 @@ export class TerminalChatWidget extends Disposable { })); this._widget.render(this._widgetContainer); this._register(this._widget.onDidFocus(() => this._chatWidgetFocused.set(true))); + this._focusTracker = this._register(trackFocus(this._widgetContainer)); } reveal(): void { this._widgetContainer.classList.remove('hide'); @@ -70,4 +73,7 @@ export class TerminalChatWidget extends Disposable { layout(width: number): void { this._widget?.layout(100, width < 300 ? 300 : width); } + public get focusTracker(): IFocusTracker { + return this._focusTracker; + } } From 10e7518fbe0d4e9da230165470ed87558a70b86d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:57:23 -0800 Subject: [PATCH 0009/1175] WIP inline chat widget integration --- .../chat/browser/media/chat.css | 13 ++++ .../browser/terminal.chat.contribution.ts | 1 + .../chat/browser/terminalChatWidget.ts | 72 +++++++++++++------ 3 files changed, 65 insertions(+), 21 deletions(-) create mode 100644 src/vs/workbench/contrib/terminalContrib/chat/browser/media/chat.css diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/chat.css new file mode 100644 index 00000000000..e81dc6b4752 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/chat.css @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.terminal-inline-chat { + position: absolute; + left: 0; + top: 0; + bottom: 0; + right: 0; + z-index: 100; +} diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index c0ee3d62f1b..8881c8f664a 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/chat'; import { IDimension } from 'vs/base/browser/dom'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Lazy } from 'vs/base/common/lazy'; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 4f0c787fc6b..76482c5c987 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -4,61 +4,91 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { editorBackground, editorForeground, inputBackground } from 'vs/platform/theme/common/colorRegistry'; import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; +import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; +import { MENU_CELL_CHAT_INPUT, MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_FEEDBACK, MENU_CELL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController'; import { IDetachedTerminalInstance, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; export class TerminalChatWidget extends Disposable { - private _widget: ChatWidget; + private _widget!: ChatWidget; private _scopedInstantiationService: IInstantiationService; private _widgetContainer: HTMLElement; private _chatWidgetFocused: IContextKey; private _chatWidgetVisible: IContextKey; + + private readonly _inlineChatWidget: InlineChatWidget; + constructor( private readonly _container: HTMLElement, private readonly _instance: ITerminalInstance | IDetachedTerminalInstance, - @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService) { super(); const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._container)); - this._scopedInstantiationService = this._instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); + this._scopedInstantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); this._chatWidgetFocused = TerminalContextKeys.chatFocused.bindTo(this._contextKeyService); this._chatWidgetVisible = TerminalContextKeys.chatVisible.bindTo(this._contextKeyService); this._widgetContainer = document.createElement('div'); - this._widgetContainer.classList.add('terminal-chat-widget'); + this._widgetContainer.classList.add('terminal-inline-chat'); this._container.appendChild(this._widgetContainer); - this._widget = this._register(this._scopedInstantiationService.createInstance( - ChatWidget, - { viewId: 'terminal' }, - { supportsFileReferences: false, renderStyle: 'compact' }, + // this._widget = this._register(this._scopedInstantiationService.createInstance( + // ChatWidget, + // { viewId: 'terminal' }, + // { supportsFileReferences: false, renderStyle: 'compact' }, + // { + // listForeground: editorForeground, + // listBackground: editorBackground, + // inputEditorBackground: inputBackground, + // resultEditorBackground: editorBackground + // })); + // this._widget.render(this._widgetContainer); + // this._register(this._widget.onDidFocus(() => this._chatWidgetFocused.set(true))); + + const fakeParentEditorElement = document.createElement('div'); + + // const editorConstructionOptions = this.inputEditorOptions.getEditorConstructionOptions(); + // this.setPlaceholderFontStyles(editorConstructionOptions.fontFamily!, editorConstructionOptions.fontSize!, editorConstructionOptions.lineHeight!); + + const fakeParentEditor = this._scopedInstantiationService.createInstance( + CodeEditorWidget, + fakeParentEditorElement, + {}, + { isSimpleWidget: true } + ); + + this._inlineChatWidget = this._scopedInstantiationService.createInstance( + InlineChatWidget, + fakeParentEditor, { - listForeground: editorForeground, - listBackground: editorBackground, - inputEditorBackground: inputBackground, - resultEditorBackground: editorBackground - })); - this._widget.render(this._widgetContainer); - this._register(this._widget.onDidFocus(() => this._chatWidgetFocused.set(true))); + menuId: MENU_CELL_CHAT_INPUT, + widgetMenuId: MENU_CELL_CHAT_WIDGET, + statusMenuId: MENU_CELL_CHAT_WIDGET_STATUS, + feedbackMenuId: MENU_CELL_CHAT_WIDGET_FEEDBACK + } + ); + + this._widgetContainer.appendChild(this._inlineChatWidget.domNode); } reveal(): void { this._widgetContainer.classList.remove('hide'); - this._widget.setVisible(true); + // this._widget.setVisible(true); this._chatWidgetFocused.set(true); this._chatWidgetVisible.set(true); - this._widget.setInput('@terminal'); - this._widget.setInputPlaceholder('Request a terminal command'); - this._widget.focusInput(); + // this._widget.setInput('@terminal'); + // this._widget.setInputPlaceholder('Request a terminal command'); + // this._widget.focusInput(); } hide(): void { this._widgetContainer.classList.add('hide'); this._chatWidgetFocused.set(false); this._chatWidgetVisible.set(false); - this._widget.clear(); + // this._widget.clear(); this._instance.focus(); } cancel(): void { From 73e7e46e8056b36e74118d23304d65c8c959819a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:17:35 -0800 Subject: [PATCH 0010/1175] More styled inline chat --- .../contrib/inlineChat/browser/inlineChat.css | 150 +++++++++--------- .../{chat.css => terminalChatWidget.css} | 5 +- .../browser/terminal.chat.contribution.ts | 1 - .../chat/browser/terminalChatWidget.ts | 13 +- 4 files changed, 85 insertions(+), 84 deletions(-) rename src/vs/workbench/contrib/terminalContrib/chat/browser/media/{chat.css => terminalChatWidget.css} (86%) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css index ee46f6df25e..862f0183354 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css @@ -11,7 +11,7 @@ background-color: var(--vscode-inlineChat-regionHighlight); } -.monaco-editor .inline-chat { +.inline-chat { color: inherit; padding: 6px; margin-top: 6px; @@ -23,11 +23,11 @@ /* body */ -.monaco-editor .inline-chat .body { +.inline-chat .body { display: flex; } -.monaco-editor .inline-chat .body .content { +.inline-chat .body .content { display: flex; box-sizing: border-box; outline: 1px solid var(--vscode-inlineChatInput-border); @@ -35,11 +35,11 @@ border-radius: 2px; } -.monaco-editor .inline-chat .body .content.synthetic-focus { +.inline-chat .body .content.synthetic-focus { outline: 1px solid var(--vscode-inlineChatInput-focusBorder); } -.monaco-editor .inline-chat .body .content .input { +.inline-chat .body .content .input { display: flex; align-items: center; justify-content: space-between; @@ -48,11 +48,11 @@ cursor: text; } -.monaco-editor .inline-chat .body .content .input .monaco-editor-background { +.inline-chat .body .content .input .monaco-editor-background { background-color: var(--vscode-inlineChatInput-background); } -.monaco-editor .inline-chat .body .content .input .editor-placeholder { +.inline-chat .body .content .input .editor-placeholder { position: absolute; z-index: 1; color: var(--vscode-inlineChatInput-placeholderForeground); @@ -61,14 +61,14 @@ text-overflow: ellipsis; } -.monaco-editor .inline-chat .body .content .input .editor-placeholder.hidden { +.inline-chat .body .content .input .editor-placeholder.hidden { display: none; } -.monaco-editor .inline-chat .body .content .input .editor-container { +.inline-chat .body .content .input .editor-container { vertical-align: middle; } -.monaco-editor .inline-chat .body .toolbar { +.inline-chat .body .toolbar { display: flex; flex-direction: column; align-self: stretch; @@ -78,47 +78,47 @@ background: var(--vscode-inlineChatInput-background); } -.monaco-editor .inline-chat .body .toolbar .actions-container { +.inline-chat .body .toolbar .actions-container { display: flex; flex-direction: row; gap: 4px; } -.monaco-editor .inline-chat .body > .widget-toolbar { +.inline-chat .body > .widget-toolbar { padding-left: 4px; } /* progress bit */ -.monaco-editor .inline-chat .progress { +.inline-chat .progress { position: relative; width: calc(100% - 18px); left: 19px; } /* UGLY - fighting against workbench styles */ -.monaco-workbench .part.editor > .content .monaco-editor .inline-chat .progress .monaco-progress-container { +.monaco-workbench .part.editor > .content .inline-chat .progress .monaco-progress-container { top: 0; } /* status */ -.monaco-editor .inline-chat .status { +.inline-chat .status { margin-top: 4px; display: flex; justify-content: space-between; align-items: center; } -.monaco-editor .inline-chat .status.actions { +.inline-chat .status.actions { margin-top: 4px; } -.monaco-editor .inline-chat .status .actions.hidden { +.inline-chat .status .actions.hidden { display: none; } -.monaco-editor .inline-chat .status .label { +.inline-chat .status .label { overflow: hidden; color: var(--vscode-descriptionForeground); font-size: 11px; @@ -126,60 +126,60 @@ display: inline-flex; } -.monaco-editor .inline-chat .status .label.hidden { +.inline-chat .status .label.hidden { display: none; } -.monaco-editor .inline-chat .status .label.info { +.inline-chat .status .label.info { margin-right: auto; padding-left: 2px; } -.monaco-editor .inline-chat .status .label.info > .codicon { +.inline-chat .status .label.info > .codicon { padding: 0 5px; font-size: 12px; line-height: 18px; } -.monaco-editor .inline-chat .status .label.status { +.inline-chat .status .label.status { padding-left: 10px; padding-right: 4px; margin-left: auto; } -.monaco-editor .inline-chat .status .label .slash-command-pill CODE { +.inline-chat .status .label .slash-command-pill CODE { border-radius: 3px; padding: 0 1px; background-color: var(--vscode-chat-slashCommandBackground); color: var(--vscode-chat-slashCommandForeground); } -.monaco-editor .inline-chat .detectedIntent { +.inline-chat .detectedIntent { color: var(--vscode-descriptionForeground); padding: 5px 0px 5px 5px; } -.monaco-editor .inline-chat .detectedIntent.hidden { +.inline-chat .detectedIntent.hidden { display: none; } -.monaco-editor .inline-chat .detectedIntent .slash-command-pill CODE { +.inline-chat .detectedIntent .slash-command-pill CODE { border-radius: 3px; padding: 0 1px; background-color: var(--vscode-chat-slashCommandBackground); color: var(--vscode-chat-slashCommandForeground); } -.monaco-editor .inline-chat .detectedIntent .slash-command-pill a { +.inline-chat .detectedIntent .slash-command-pill a { color: var(--vscode-textLink-foreground); cursor: pointer; } -/* .monaco-editor .inline-chat .markdownMessage .message * { +/* .inline-chat .markdownMessage .message * { margin: unset; } -.monaco-editor .inline-chat .markdownMessage .message code { +.inline-chat .markdownMessage .message code { font-family: var(--monaco-monospace-font); font-size: 12px; color: var(--vscode-textPreformat-foreground); @@ -189,7 +189,7 @@ } */ -.monaco-editor .inline-chat .chatMessage .chatMessageContent .value { +.inline-chat .chatMessage .chatMessageContent .value { -webkit-line-clamp: initial; -webkit-box-orient: vertical; overflow: hidden; @@ -198,130 +198,130 @@ user-select: text; } -.monaco-editor .inline-chat .chatMessage .chatMessageContent[state="cropped"] .value { +.inline-chat .chatMessage .chatMessageContent[state="cropped"] .value { -webkit-line-clamp: var(--vscode-inline-chat-cropped, 3); } -.monaco-editor .inline-chat .chatMessage .chatMessageContent[state="expanded"] .value { +.inline-chat .chatMessage .chatMessageContent[state="expanded"] .value { -webkit-line-clamp: var(--vscode-inline-chat-expanded, 10); } -.monaco-editor .inline-chat .followUps { +.inline-chat .followUps { padding: 5px 5px; } -.monaco-editor .inline-chat .followUps .interactive-session-followups .monaco-button { +.inline-chat .followUps .interactive-session-followups .monaco-button { display: block; color: var(--vscode-textLink-foreground); font-size: 12px; } -.monaco-editor .inline-chat .followUps.hidden { +.inline-chat .followUps.hidden { display: none; } -.monaco-editor .inline-chat .chatMessage { +.inline-chat .chatMessage { padding: 8px 3px; } -.monaco-editor .inline-chat .chatMessage .chatMessageContent { +.inline-chat .chatMessage .chatMessageContent { padding: 2px 2px; } -.monaco-editor .inline-chat .chatMessage.hidden { +.inline-chat .chatMessage.hidden { display: none; } -.monaco-editor .inline-chat .status .label A { +.inline-chat .status .label A { color: var(--vscode-textLink-foreground); cursor: pointer; } -.monaco-editor .inline-chat .status .label.error { +.inline-chat .status .label.error { color: var(--vscode-errorForeground); } -.monaco-editor .inline-chat .status .label.warn { +.inline-chat .status .label.warn { color: var(--vscode-editorWarning-foreground); } -.monaco-editor .inline-chat .status .actions { +.inline-chat .status .actions { display: flex; } -.monaco-editor .inline-chat .status .actions > .monaco-button, -.monaco-editor .inline-chat .status .actions > .monaco-button-dropdown { +.inline-chat .status .actions > .monaco-button, +.inline-chat .status .actions > .monaco-button-dropdown { margin-right: 6px; } -.monaco-editor .inline-chat .status .actions > .monaco-button-dropdown > .monaco-dropdown-button { +.inline-chat .status .actions > .monaco-button-dropdown > .monaco-dropdown-button { display: flex; align-items: center; padding: 0 4px; } -.monaco-editor .inline-chat .status .actions > .monaco-button.codicon { +.inline-chat .status .actions > .monaco-button.codicon { display: flex; } -.monaco-editor .inline-chat .status .actions > .monaco-button.codicon::before { +.inline-chat .status .actions > .monaco-button.codicon::before { align-self: center; } -.monaco-editor .inline-chat .status .actions .monaco-text-button { +.inline-chat .status .actions .monaco-text-button { padding: 2px 4px; white-space: nowrap; } -.monaco-editor .inline-chat .status .monaco-toolbar .action-item { +.inline-chat .status .monaco-toolbar .action-item { padding: 0 2px; } /* TODO@jrieken not needed? */ -.monaco-editor .inline-chat .status .monaco-toolbar .action-label.checked { +.inline-chat .status .monaco-toolbar .action-label.checked { color: var(--vscode-inputOption-activeForeground); background-color: var(--vscode-inputOption-activeBackground); outline: 1px solid var(--vscode-inputOption-activeBorder); } -.monaco-editor .inline-chat .status .monaco-toolbar .action-item.button-item .action-label:is(:hover, :focus) { +.inline-chat .status .monaco-toolbar .action-item.button-item .action-label:is(:hover, :focus) { background-color: var(--vscode-button-hoverBackground); } /* preview */ -.monaco-editor .inline-chat .preview { +.inline-chat .preview { display: none; } -.monaco-editor .inline-chat .previewDiff, -.monaco-editor .inline-chat .previewCreate { +.inline-chat .previewDiff, +.inline-chat .previewCreate { display: inherit; border: 1px solid var(--vscode-inlineChat-border); border-radius: 2px; margin: 6px 0px; } -.monaco-editor .inline-chat .previewCreateTitle { +.inline-chat .previewCreateTitle { padding-top: 6px; } -.monaco-editor .inline-chat .previewDiff.hidden, -.monaco-editor .inline-chat .previewCreate.hidden, -.monaco-editor .inline-chat .previewCreateTitle.hidden { +.inline-chat .previewDiff.hidden, +.inline-chat .previewCreate.hidden, +.inline-chat .previewCreateTitle.hidden { display: none; } -.monaco-editor .inline-chat-toolbar { +.inline-chat-toolbar { display: flex; } -.monaco-editor .inline-chat-toolbar > .monaco-button{ +.inline-chat-toolbar > .monaco-button{ margin-right: 6px; } -.monaco-editor .inline-chat-toolbar .action-label.checked { +.inline-chat-toolbar .action-label.checked { color: var(--vscode-inputOption-activeForeground); background-color: var(--vscode-inputOption-activeBackground); outline: 1px solid var(--vscode-inputOption-activeBorder); @@ -329,65 +329,65 @@ /* decoration styles */ -.monaco-editor .inline-chat-inserted-range { +.inline-chat-inserted-range { background-color: var(--vscode-inlineChatDiff-inserted); } -.monaco-editor .inline-chat-inserted-range-linehighlight { +.inline-chat-inserted-range-linehighlight { background-color: var(--vscode-diffEditor-insertedLineBackground); } -.monaco-editor .inline-chat-original-zone2 { +.inline-chat-original-zone2 { background-color: var(--vscode-diffEditor-removedLineBackground); opacity: 0.8; } -.monaco-editor .inline-chat-lines-inserted-range { +.inline-chat-lines-inserted-range { background-color: var(--vscode-diffEditor-insertedTextBackground); } -.monaco-editor .inline-chat-block-selection { +.inline-chat-block-selection { background-color: var(--vscode-inlineChat-regionHighlight); } -.monaco-editor .inline-chat-slash-command { +.inline-chat-slash-command { opacity: 0; } -.monaco-editor .inline-chat-slash-command-detail { +.inline-chat-slash-command-detail { opacity: 0.5; } /* diff zone */ -.monaco-editor .inline-chat-diff-widget .monaco-diff-editor .monaco-editor-background, -.monaco-editor .inline-chat-diff-widget .monaco-diff-editor .monaco-editor .margin-view-overlays { +.inline-chat-diff-widget .monaco-diff-editor .monaco-editor-background, +.inline-chat-diff-widget .monaco-diff-editor .monaco-editor .margin-view-overlays { background-color: var(--vscode-inlineChat-regionHighlight); } /* create zone */ -.monaco-editor .inline-chat-newfile-widget { +.inline-chat-newfile-widget { background-color: var(--vscode-inlineChat-regionHighlight); } -.monaco-editor .inline-chat-newfile-widget .title { +.inline-chat-newfile-widget .title { display: flex; align-items: center; justify-content: space-between; } -.monaco-editor .inline-chat-newfile-widget .title .detail { +.inline-chat-newfile-widget .title .detail { margin-left: 4px; } -.monaco-editor .inline-chat-newfile-widget .buttonbar-widget { +.inline-chat-newfile-widget .buttonbar-widget { display: flex; margin-left: auto; margin-right: 8px; } -.monaco-editor .inline-chat-newfile-widget .buttonbar-widget > .monaco-button { +.inline-chat-newfile-widget .buttonbar-widget > .monaco-button { display: inline-flex; white-space: nowrap; margin-left: 4px; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css similarity index 86% rename from src/vs/workbench/contrib/terminalContrib/chat/browser/media/chat.css rename to src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css index e81dc6b4752..5c8676bbe6e 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css @@ -6,8 +6,9 @@ .terminal-inline-chat { position: absolute; left: 0; - top: 0; bottom: 0; - right: 0; z-index: 100; } +/* .terminal-inline-chat .inline-chat .body { + display: flex; +} */ diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index c210f1c4494..b9fab74a3a2 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/chat'; import { IDimension } from 'vs/base/browser/dom'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Lazy } from 'vs/base/common/lazy'; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index d8c1d5180c7..7c67b161aba 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -3,20 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IFocusTracker, trackFocus } from 'vs/base/browser/dom'; +import 'vs/css!./media/terminalChatWidget'; +import { Dimension, IFocusTracker, trackFocus } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { MENU_CELL_CHAT_INPUT, MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_FEEDBACK, MENU_CELL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController'; import { IDetachedTerminalInstance, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; export class TerminalChatWidget extends Disposable { - private _widget!: ChatWidget; private _scopedInstantiationService: IInstantiationService; private _widgetContainer: HTMLElement; private _chatWidgetFocused: IContextKey; @@ -81,6 +80,8 @@ export class TerminalChatWidget extends Disposable { this._focusTracker = this._register(trackFocus(this._widgetContainer)); } reveal(): void { + this._inlineChatWidget.layout(new Dimension(400, 150)); + this._widgetContainer.classList.remove('hide'); // this._widget.setVisible(true); this._chatWidgetFocused.set(true); @@ -97,13 +98,13 @@ export class TerminalChatWidget extends Disposable { this._instance.focus(); } cancel(): void { - this._widget?.clear(); + // this._widget?.clear(); } acceptInput(): void { - this._widget?.acceptInput(); + // this._widget?.acceptInput(); } layout(width: number): void { - this._widget?.layout(100, width < 300 ? 300 : width); + // this._widget?.layout(100, width < 300 ? 300 : width); } public get focusTracker(): IFocusTracker { return this._focusTracker; From 22670a1031a0af3df9cc13f267eaa3dd16626bb2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:21:09 -0800 Subject: [PATCH 0011/1175] Add temp placeholders --- .../contrib/terminalContrib/chat/browser/terminalChatWidget.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 7c67b161aba..041ec3b9573 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -14,6 +14,7 @@ import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inline import { MENU_CELL_CHAT_INPUT, MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_FEEDBACK, MENU_CELL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController'; import { IDetachedTerminalInstance, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { localize } from 'vs/nls'; export class TerminalChatWidget extends Disposable { private _scopedInstantiationService: IInstantiationService; @@ -74,6 +75,8 @@ export class TerminalChatWidget extends Disposable { feedbackMenuId: MENU_CELL_CHAT_WIDGET_FEEDBACK } ); + this._inlineChatWidget.placeholder = localize('default.placeholder', "Ask how to do something in the terminal"); + this._inlineChatWidget.updateInfo(localize('welcome.1', "AI-generated code may be incorrect")); this._widgetContainer.appendChild(this._inlineChatWidget.domNode); From 792fc7a419483cf0b1bd6a496e4b68307574d479 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 9 Feb 2024 12:24:13 -0600 Subject: [PATCH 0012/1175] fix focus issues --- src/vs/workbench/contrib/terminal/browser/media/terminal.css | 2 +- .../contrib/terminalContrib/chat/browser/terminalChatWidget.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 8fc9e6be0eb..8fcb3c71172 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -572,6 +572,6 @@ top: 10px; } -.monaco-workbench .terminal-chat-widget.hide { +.monaco-workbench .terminal-inline-chat.hide { visibility: hidden; } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 041ec3b9573..89c2518dd8e 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -92,6 +92,7 @@ export class TerminalChatWidget extends Disposable { // this._widget.setInput('@terminal'); // this._widget.setInputPlaceholder('Request a terminal command'); // this._widget.focusInput(); + this._inlineChatWidget.focus(); } hide(): void { this._widgetContainer.classList.add('hide'); From dfed5c7b09b6ff0dd6d91de17875c8278e66e3d2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:31:03 -0800 Subject: [PATCH 0013/1175] Remove detached terminal support --- .../browser/terminal.chat.contribution.ts | 34 +++++++++++++------ .../chat/browser/terminalChatWidget.ts | 14 ++++---- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index b9fab74a3a2..e54914120bd 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -3,31 +3,31 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; import { IDimension } from 'vs/base/browser/dom'; +import { Codicon } from 'vs/base/common/codicons'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Lazy } from 'vs/base/common/lazy'; import { Disposable } from 'vs/base/common/lifecycle'; import { localize2 } from 'vs/nls'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ITerminalProcessInfo, ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; -import { Codicon } from 'vs/base/common/codicons'; -import { MenuId } from 'vs/platform/actions/common/actions'; import { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget'; -import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class TerminalChatContribution extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.Chat'; - static get(instance: ITerminalInstance | IDetachedTerminalInstance): TerminalChatContribution | null { + static get(instance: ITerminalInstance): TerminalChatContribution | null { return instance.getContribution(TerminalChatContribution.ID); } /** @@ -41,8 +41,8 @@ export class TerminalChatContribution extends Disposable implements ITerminalCon get chatWidget(): TerminalChatWidget | undefined { return this._chatWidget?.value; } constructor( - private readonly _instance: ITerminalInstance | IDetachedTerminalInstance, - processManager: ITerminalProcessManager | ITerminalProcessInfo, + private readonly _instance: ITerminalInstance, + processManager: ITerminalProcessManager, widgetManager: TerminalWidgetManager, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IConfigurationService private _configurationService: IConfigurationService, @@ -96,7 +96,7 @@ export class TerminalChatContribution extends Disposable implements ITerminalCon this._chatWidget?.rawValue?.dispose(); } } -registerTerminalContribution(TerminalChatContribution.ID, TerminalChatContribution, true); +registerTerminalContribution(TerminalChatContribution.ID, TerminalChatContribution, false); registerActiveXtermAction({ id: TerminalCommandId.FocusChat, @@ -112,6 +112,9 @@ registerActiveXtermAction({ ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), ), run: (_xterm, _accessor, activeInstance) => { + if (isDetachedTerminalInstance(activeInstance)) { + return; + } const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); contr?.chatWidget?.reveal(); } @@ -132,6 +135,9 @@ registerActiveXtermAction({ ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), ), run: (_xterm, _accessor, activeInstance) => { + if (isDetachedTerminalInstance(activeInstance)) { + return; + } const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); contr?.chatWidget?.hide(); } @@ -152,6 +158,9 @@ registerActiveXtermAction({ group: 'navigation', }, run: (_xterm, _accessor, activeInstance) => { + if (isDetachedTerminalInstance(activeInstance)) { + return; + } const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); contr?.chatWidget?.acceptInput(); } @@ -170,6 +179,9 @@ registerActiveXtermAction({ group: 'navigation', }, run: (_xterm, _accessor, activeInstance) => { + if (isDetachedTerminalInstance(activeInstance)) { + return; + } const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); contr?.chatWidget?.cancel(); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 89c2518dd8e..3a99cd5ebef 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -12,7 +12,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { MENU_CELL_CHAT_INPUT, MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_FEEDBACK, MENU_CELL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController'; -import { IDetachedTerminalInstance, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { localize } from 'vs/nls'; @@ -28,10 +28,10 @@ export class TerminalChatWidget extends Disposable { constructor( private readonly _container: HTMLElement, - private readonly _instance: ITerminalInstance | IDetachedTerminalInstance, - + private readonly _instance: ITerminalInstance, @IInstantiationService instantiationService: IInstantiationService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService) { + @IContextKeyService private readonly _contextKeyService: IContextKeyService + ) { super(); const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._container)); this._scopedInstantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); @@ -61,7 +61,9 @@ export class TerminalChatWidget extends Disposable { const fakeParentEditor = this._scopedInstantiationService.createInstance( CodeEditorWidget, fakeParentEditorElement, - {}, + { + + }, { isSimpleWidget: true } ); @@ -76,7 +78,7 @@ export class TerminalChatWidget extends Disposable { } ); this._inlineChatWidget.placeholder = localize('default.placeholder', "Ask how to do something in the terminal"); - this._inlineChatWidget.updateInfo(localize('welcome.1', "AI-generated code may be incorrect")); + this._inlineChatWidget.updateInfo(localize('welcome.1', "AI-generated commands may be incorrect")); this._widgetContainer.appendChild(this._inlineChatWidget.domNode); From cfeb85db2be149ab3c0e5a6e466f02876acf5330 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:49:57 -0800 Subject: [PATCH 0014/1175] Anchor term chat to bottom --- .../terminalContrib/chat/browser/media/terminalChatWidget.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css index 5c8676bbe6e..633b6778dfe 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css @@ -8,6 +8,7 @@ left: 0; bottom: 0; z-index: 100; + height: auto !important; } /* .terminal-inline-chat .inline-chat .body { display: flex; From 0a8994cf3ac712a7ee9e5fa1ddbf82a70c8ce891 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 9 Feb 2024 12:53:06 -0600 Subject: [PATCH 0015/1175] use terminal menu ID --- src/vs/platform/actions/common/actions.ts | 1 + .../actions/voiceChatActions.ts | 6 ++++++ .../chat/browser/terminalChatWidget.ts | 20 +++++++++---------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 376627398ac..ec2810e91fb 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -208,6 +208,7 @@ export class MenuId { static readonly ChatCodeBlock = new MenuId('ChatCodeblock'); static readonly ChatMessageTitle = new MenuId('ChatMessageTitle'); static readonly ChatExecute = new MenuId('ChatExecute'); + static readonly TerminalChat = new MenuId('TerminalChat'); static readonly ChatInputSide = new MenuId('ChatInputSide'); static readonly AccessibleView = new MenuId('AccessibleView'); static readonly MultiDiffEditorFileToolbar = new MenuId('MultiDiffEditorFileToolbar'); diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 3b605a096d5..75f3ab3c832 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -475,6 +475,12 @@ export class StartVoiceChatAction extends Action2 { when: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS.negate()), group: 'main', order: -1 + }, + { + id: MenuId.TerminalChat, + when: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS.negate()), + group: 'main', + order: -1 }] }); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 3a99cd5ebef..de24d36312d 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -11,10 +11,11 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; -import { MENU_CELL_CHAT_INPUT, MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_FEEDBACK, MENU_CELL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController'; -import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IDetachedTerminalInstance, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { localize } from 'vs/nls'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_STATUS, MENU_CELL_CHAT_WIDGET_FEEDBACK } from 'vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController'; export class TerminalChatWidget extends Disposable { private _scopedInstantiationService: IInstantiationService; @@ -28,10 +29,10 @@ export class TerminalChatWidget extends Disposable { constructor( private readonly _container: HTMLElement, - private readonly _instance: ITerminalInstance, + private readonly _instance: ITerminalInstance | IDetachedTerminalInstance, + @IInstantiationService instantiationService: IInstantiationService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService - ) { + @IContextKeyService private readonly _contextKeyService: IContextKeyService) { super(); const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._container)); this._scopedInstantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); @@ -61,9 +62,7 @@ export class TerminalChatWidget extends Disposable { const fakeParentEditor = this._scopedInstantiationService.createInstance( CodeEditorWidget, fakeParentEditorElement, - { - - }, + {}, { isSimpleWidget: true } ); @@ -71,15 +70,14 @@ export class TerminalChatWidget extends Disposable { InlineChatWidget, fakeParentEditor, { - menuId: MENU_CELL_CHAT_INPUT, + menuId: MenuId.TerminalChat, widgetMenuId: MENU_CELL_CHAT_WIDGET, statusMenuId: MENU_CELL_CHAT_WIDGET_STATUS, feedbackMenuId: MENU_CELL_CHAT_WIDGET_FEEDBACK } ); this._inlineChatWidget.placeholder = localize('default.placeholder', "Ask how to do something in the terminal"); - this._inlineChatWidget.updateInfo(localize('welcome.1', "AI-generated commands may be incorrect")); - + this._inlineChatWidget.updateInfo(localize('welcome.1', "AI-generated code may be incorrect")); this._widgetContainer.appendChild(this._inlineChatWidget.domNode); this._focusTracker = this._register(trackFocus(this._widgetContainer)); From 9e07b3ae1e411647d86531822b31178e044a4df5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:54:57 -0800 Subject: [PATCH 0016/1175] Remove detached terminal support --- .../terminalContrib/chat/browser/terminalChatWidget.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index de24d36312d..bef5d94f3d8 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -11,7 +11,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; -import { IDetachedTerminalInstance, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { localize } from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; @@ -29,7 +29,7 @@ export class TerminalChatWidget extends Disposable { constructor( private readonly _container: HTMLElement, - private readonly _instance: ITerminalInstance | IDetachedTerminalInstance, + private readonly _instance: ITerminalInstance, @IInstantiationService instantiationService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService) { From 5e3bd936bcb55db758af7de1346549eff0d3f2f5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:59:31 -0800 Subject: [PATCH 0017/1175] Fix panel background overriding input's --- src/vs/workbench/browser/parts/panel/media/panelpart.css | 6 +++--- .../terminalContrib/chat/browser/terminalChatWidget.ts | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css index 4ccb53b0c80..7b17465b2d9 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -35,9 +35,9 @@ border-right-width: 0; /* no border when main editor area is hiden */ } -.monaco-workbench .part.panel > .content .monaco-editor, -.monaco-workbench .part.panel > .content .monaco-editor .margin, -.monaco-workbench .part.panel > .content .monaco-editor .monaco-editor-background { +.monaco-workbench .part.panel > .content .monaco-editor:not(.ignore-panel-bg), +.monaco-workbench .part.panel > .content .monaco-editor:not(.ignore-panel-bg) .margin, +.monaco-workbench .part.panel > .content .monaco-editor:not(.ignore-panel-bg) .monaco-editor-background { background-color: var(--vscode-panel-background); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index bef5d94f3d8..fc85c9a5a50 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -62,7 +62,9 @@ export class TerminalChatWidget extends Disposable { const fakeParentEditor = this._scopedInstantiationService.createInstance( CodeEditorWidget, fakeParentEditorElement, - {}, + { + extraEditorClassName: 'ignore-panel-bg' + }, { isSimpleWidget: true } ); From 2611151ba8966ba0c52c27ea0885a6cadd39c99a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 9 Feb 2024 11:10:10 -0800 Subject: [PATCH 0018/1175] Add wip make request button --- .../contrib/terminal/common/terminal.ts | 2 +- .../browser/terminal.chat.contribution.ts | 22 ++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 0579628b521..9a11ee3252c 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -502,7 +502,7 @@ export const enum TerminalCommandId { FontZoomReset = 'workbench.action.terminal.fontZoomReset', FocusChat = 'workbench.action.terminal.focusChat', HideChat = 'workbench.action.terminal.hideChat', - SubmitChat = 'workbench.action.terminal.submitChat', + MakeChatRequest = 'workbench.action.terminal.submitChat', CancelChat = 'workbench.action.terminal.cancelChat', // Developer commands diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index e54914120bd..6c44b62619c 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -100,7 +100,7 @@ registerTerminalContribution(TerminalChatContribution.ID, TerminalChatContributi registerActiveXtermAction({ id: TerminalCommandId.FocusChat, - title: localize2('workbench.action.terminal.focusChat', 'Terminal: Focus Chat'), + title: localize2('workbench.action.terminal.focusChat', 'Focus Chat'), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyI, when: ContextKeyExpr.and(TerminalContextKeys.chatFocused.negate(), TerminalContextKeys.focusInAny), @@ -122,7 +122,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalCommandId.HideChat, - title: localize2('workbench.action.terminal.hideChat', 'Terminal: Hide Chat'), + title: localize2('workbench.action.terminal.hideChat', 'Hide Chat'), keybinding: { primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], @@ -144,18 +144,28 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.SubmitChat, - title: localize2('workbench.action.terminal.submitChat', 'Terminal: Submit Chat'), + id: TerminalCommandId.MakeChatRequest, + title: localize2('workbench.action.terminal.submitChat', 'Make Chat Request'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.chatInputHasText ), icon: Codicon.send, + keybinding: { + when: TerminalContextKeys.chatSessionInProgress, + // TODO: + // when: CTX_INLINE_CHAT_FOCUSED, + weight: KeybindingWeight.EditorCore + 7, + primary: KeyCode.Enter + }, menu: { - id: MenuId.ChatExecute, + id: MenuId.TerminalChat, + group: 'main', + order: 1, when: TerminalContextKeys.chatSessionInProgress.negate(), - group: 'navigation', + // TODO: + // when: CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST.isEqualTo(false) }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { From a8cda8716fb80b41170aeb251b99a26b276e2bfc Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 9 Feb 2024 11:10:25 -0800 Subject: [PATCH 0019/1175] Fix command name --- src/vs/workbench/contrib/terminal/common/terminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 9a11ee3252c..5ed9e674af2 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -502,7 +502,7 @@ export const enum TerminalCommandId { FontZoomReset = 'workbench.action.terminal.fontZoomReset', FocusChat = 'workbench.action.terminal.focusChat', HideChat = 'workbench.action.terminal.hideChat', - MakeChatRequest = 'workbench.action.terminal.submitChat', + MakeChatRequest = 'workbench.action.terminal.makeChatRequest', CancelChat = 'workbench.action.terminal.cancelChat', // Developer commands From 4e63128a7e9820f62c236913bcc0ac67e1da833c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chanchevrier?= Date: Mon, 12 Feb 2024 18:12:40 +0100 Subject: [PATCH 0020/1175] fix: inverted resize for direction.right --- src/vs/workbench/contrib/terminal/browser/terminalGroup.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index d993243770f..163b64068d2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -69,6 +69,7 @@ class SplitPaneContainer extends Disposable { // Resize the entire pane as a whole if ( (this.orientation === Orientation.HORIZONTAL && direction === Direction.Down) || + (this.orientation === Orientation.VERTICAL && direction === Direction.Right) || (part === Parts.SIDEBAR_PART && direction === Direction.Left) || (part === Parts.AUXILIARYBAR_PART && direction === Direction.Right) ) { From 18fde1e863ce5c56f50e90b36095b50281196c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chanchevrier?= Date: Mon, 12 Feb 2024 18:13:11 +0100 Subject: [PATCH 0021/1175] fix: inverted resize direction for left-positionned terminal panel --- .../workbench/contrib/terminal/browser/terminalGroup.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index 163b64068d2..7e944aa9753 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -577,11 +577,18 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { } const isHorizontal = (direction === Direction.Left || direction === Direction.Right); + + // Left-positionned panels have inverted controls + // see https://github.com/microsoft/vscode/issues/140873 + const shouldInvertHorizontalResize = (isHorizontal && this._panelPosition === Position.LEFT); + + const resizeDirection = shouldInvertHorizontalResize ? direction === Direction.Left ? Direction.Right : Direction.Left : direction; + const font = this._terminalService.configHelper.getFont(getWindow(this._groupElement)); // TODO: Support letter spacing and line height const charSize = (isHorizontal ? font.charWidth : font.charHeight); if (charSize) { - this._splitPaneContainer.resizePane(this._activeInstanceIndex, direction, charSize * Constants.ResizePartCellCount, getPartByLocation(this._terminalLocation)); + this._splitPaneContainer.resizePane(this._activeInstanceIndex, resizeDirection, charSize * Constants.ResizePartCellCount, getPartByLocation(this._terminalLocation)); } } From 5451a4e69a305a8146808e1957f67252195dcd81 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 12 Feb 2024 11:49:24 -0800 Subject: [PATCH 0022/1175] Try use shell integration in debug terminals Fixes #204694 --- src/vs/platform/terminal/common/terminal.ts | 6 ++++++ src/vs/platform/terminal/node/terminalEnvironment.ts | 2 +- src/vs/workbench/api/browser/mainThreadTerminalService.ts | 1 + src/vs/workbench/api/common/extHost.protocol.ts | 1 + src/vs/workbench/api/common/extHostTerminalService.ts | 2 ++ src/vs/workbench/api/node/extHostDebugService.ts | 3 +++ 6 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index ffe0e56a189..4dea050e336 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -608,6 +608,12 @@ export interface IShellLaunchConfig { */ isTransient?: boolean; + /** + * Attempt to force shell integration to be enabled by bypassing the {@link isFeatureTerminal} + * equals false requirement. + */ + forceShellIntegration?: boolean; + /** * Create a terminal without shell integration even when it's enabled */ diff --git a/src/vs/platform/terminal/node/terminalEnvironment.ts b/src/vs/platform/terminal/node/terminalEnvironment.ts index 066ca8f6bc1..703e0fec623 100644 --- a/src/vs/platform/terminal/node/terminalEnvironment.ts +++ b/src/vs/platform/terminal/node/terminalEnvironment.ts @@ -117,7 +117,7 @@ export function getShellIntegrationInjection( // - There is no executable (not sure what script to run) // - The terminal is used by a feature like tasks or debugging const useWinpty = isWindows && (!options.windowsEnableConpty || getWindowsBuildNumber() < 18309); - if (!options.shellIntegration.enabled || !shellLaunchConfig.executable || shellLaunchConfig.isFeatureTerminal || shellLaunchConfig.hideFromUser || shellLaunchConfig.ignoreShellIntegration || useWinpty) { + if (!options.shellIntegration.enabled || !shellLaunchConfig.executable || (shellLaunchConfig.isFeatureTerminal && !shellLaunchConfig.forceShellIntegration) || shellLaunchConfig.hideFromUser || shellLaunchConfig.ignoreShellIntegration || useWinpty) { return undefined; } diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 2293708c63b..28bc27cbb12 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -152,6 +152,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape ? (id, cols, rows) => new TerminalProcessExtHostProxy(id, cols, rows, this._terminalService) : undefined, extHostTerminalId, + forceShellIntegration: launchConfig.forceShellIntegration, isFeatureTerminal: launchConfig.isFeatureTerminal, isExtensionOwnedTerminal: launchConfig.isExtensionOwnedTerminal, useShellEnvironment: launchConfig.useShellEnvironment, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 964afc507dc..4ed54e7b0e3 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -495,6 +495,7 @@ export interface TerminalLaunchConfig { strictEnv?: boolean; hideFromUser?: boolean; isExtensionCustomPtyTerminal?: boolean; + forceShellIntegration?: boolean; isFeatureTerminal?: boolean; isExtensionOwnedTerminal?: boolean; useShellEnvironment?: boolean; diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index e004acba809..cbe28de19ca 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -63,6 +63,7 @@ interface IEnvironmentVariableCollection extends vscode.EnvironmentVariableColle export interface ITerminalInternalOptions { cwd?: string | URI; isFeatureTerminal?: boolean; + forceShellIntegration?: boolean; useShellEnvironment?: boolean; resolvedExtHostIdentifier?: ExtHostTerminalIdentifier; /** @@ -165,6 +166,7 @@ export class ExtHostTerminal { initialText: options.message ?? undefined, strictEnv: options.strictEnv ?? undefined, hideFromUser: options.hideFromUser ?? undefined, + forceShellIntegration: internalOptions?.forceShellIntegration ?? undefined, isFeatureTerminal: internalOptions?.isFeatureTerminal ?? undefined, isExtensionOwnedTerminal: true, useShellEnvironment: internalOptions?.useShellEnvironment ?? undefined, diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 1ccfd2101da..90b977aa644 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -106,6 +106,9 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { giveShellTimeToInitialize = true; terminal = this._terminalService.createTerminalFromOptions(options, { isFeatureTerminal: true, + // Since debug termnials are REPLs, we want shell integration to be enabled. + // Ignore isFeatureTerminal when evaluating shell integration enablement. + forceShellIntegration: true, useShellEnvironment: true }); this._integratedTerminalInstances.insert(terminal, shellConfig); From b79d48c296a18057450205fe65029dabebb08ddc Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 12 Feb 2024 13:52:59 -0600 Subject: [PATCH 0023/1175] wip make request action --- .../chat/browser/terminal.chat.contribution.ts | 6 +++--- .../chat/browser/terminalChatWidget.ts | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index 6c44b62619c..c2bbe8aa74a 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -149,11 +149,11 @@ registerActiveXtermAction({ precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - TerminalContextKeys.chatInputHasText + // TerminalContextKeys.chatInputHasText ), icon: Codicon.send, keybinding: { - when: TerminalContextKeys.chatSessionInProgress, + when: TerminalContextKeys.chatSessionInProgress.negate(), // TODO: // when: CTX_INLINE_CHAT_FOCUSED, weight: KeybindingWeight.EditorCore + 7, @@ -163,7 +163,7 @@ registerActiveXtermAction({ id: MenuId.TerminalChat, group: 'main', order: 1, - when: TerminalContextKeys.chatSessionInProgress.negate(), + // when: TerminalContextKeys.chatSessionInProgress.negate(), // TODO: // when: CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST.isEqualTo(false) }, diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index fc85c9a5a50..1467a9a86ef 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -16,6 +16,9 @@ import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/termin import { localize } from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; import { MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_STATUS, MENU_CELL_CHAT_WIDGET_FEEDBACK } from 'vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController'; +import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; +import { ChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; +import { CancellationToken } from 'vs/base/common/cancellation'; export class TerminalChatWidget extends Disposable { private _scopedInstantiationService: IInstantiationService; @@ -27,12 +30,15 @@ export class TerminalChatWidget extends Disposable { private readonly _focusTracker: IFocusTracker; + private _chatModel: ChatModel | undefined; + constructor( private readonly _container: HTMLElement, private readonly _instance: ITerminalInstance, @IInstantiationService instantiationService: IInstantiationService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService) { + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IChatService private readonly _chatService: IChatService) { super(); const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._container)); this._scopedInstantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); @@ -41,6 +47,7 @@ export class TerminalChatWidget extends Disposable { this._widgetContainer = document.createElement('div'); this._widgetContainer.classList.add('terminal-inline-chat'); this._container.appendChild(this._widgetContainer); + // this._widget = this._register(this._scopedInstantiationService.createInstance( // ChatWidget, // { viewId: 'terminal' }, @@ -108,6 +115,14 @@ export class TerminalChatWidget extends Disposable { } acceptInput(): void { // this._widget?.acceptInput(); + this._chatModel ??= this._chatService.startSession('terminal', CancellationToken.None); + + // if (!this._model) { + // throw new Error('Could not start chat session'); + // } + this._chatService?.sendRequest(this._chatModel?.sessionId!, this._inlineChatWidget.value); + this._inlineChatWidget.value = ''; + // this.widget.setModel(this.model, { inputValue: this._currentQuery }); } layout(width: number): void { // this._widget?.layout(100, width < 300 ? 300 : width); From a0d97ae9449d8665a531d8dd1dd58c4fe24332fe Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 12 Feb 2024 15:49:32 -0600 Subject: [PATCH 0024/1175] contrib => controller --- .../terminal/common/terminalContextKey.ts | 6 +- .../browser/terminal.chat.contribution.ts | 101 ++------------- .../chat/browser/terminalChatController.ts | 119 ++++++++++++++++++ .../chat/browser/terminalChatWidget.ts | 5 +- 4 files changed, 136 insertions(+), 95 deletions(-) create mode 100644 src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index 1de53d61930..78fd7df6712 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -41,7 +41,7 @@ export const enum TerminalContextKeyStrings { TerminalShellIntegrationEnabled = 'terminalShellIntegrationEnabled', ChatFocus = 'terminalChatFocus', ChatVisible = 'terminalChatVisible', - ChatSessionInProgress = 'terminalChatSessionInProgress', + ChatActiveRequest = 'terminalChatActiveRequest', ChatInputHasText = 'terminalChatInputHasText', } @@ -170,8 +170,8 @@ export namespace TerminalContextKeys { /** Whether the chat widget is visible */ export const chatVisible = new RawContextKey(TerminalContextKeyStrings.ChatVisible, false, localize('chatVisibleContextKey', "Whether the chat view is visible.")); - /** Whether a chat session is in progress */ - export const chatSessionInProgress = new RawContextKey(TerminalContextKeyStrings.ChatSessionInProgress, false, localize('chatSessionInProgressContextKey', "Whether a chat session is in progress.")); + /** Whether there is an active chat request */ + export const chatRequestActive = new RawContextKey(TerminalContextKeyStrings.ChatActiveRequest, false, localize('chatRequestActiveContextKey', "Whether there is an active chat request.")); /** Whether the chat input has text */ export const chatInputHasText = new RawContextKey(TerminalContextKeyStrings.ChatInputHasText, false, localize('chatInputHasTextContextKey', "Whether the chat input has text.")); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index c2bbe8aa74a..1d909e6fc30 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -3,100 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; -import { IDimension } from 'vs/base/browser/dom'; import { Codicon } from 'vs/base/common/codicons'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { Lazy } from 'vs/base/common/lazy'; -import { Disposable } from 'vs/base/common/lifecycle'; import { localize2 } from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; -import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; -import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget'; +import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; -export class TerminalChatContribution extends Disposable implements ITerminalContribution { - static readonly ID = 'terminal.Chat'; - - static get(instance: ITerminalInstance): TerminalChatContribution | null { - return instance.getContribution(TerminalChatContribution.ID); - } - /** - * Currently focused chat widget. This is used to track action context since - * 'active terminals' are only tracked for non-detached terminal instanecs. - */ - static activeChatWidget?: TerminalChatContribution; - private _chatWidget: Lazy | undefined; - private _lastLayoutDimensions: IDimension | undefined; - - get chatWidget(): TerminalChatWidget | undefined { return this._chatWidget?.value; } - - constructor( - private readonly _instance: ITerminalInstance, - processManager: ITerminalProcessManager, - widgetManager: TerminalWidgetManager, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IConfigurationService private _configurationService: IConfigurationService, - @ITerminalService private readonly _terminalService: ITerminalService - ) { - super(); - if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { - return; - } - } - - layout(_xterm: IXtermTerminal & { raw: RawXtermTerminal }, dimension: IDimension): void { - if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { - return; - } - this._lastLayoutDimensions = dimension; - this._chatWidget?.rawValue?.layout(dimension.width); - } - - xtermReady(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { - if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { - return; - } - this._chatWidget = new Lazy(() => { - const chatWidget = this._instantiationService.createInstance(TerminalChatWidget, this._instance.domElement!, this._instance); - chatWidget.focusTracker.onDidFocus(() => { - TerminalChatContribution.activeChatWidget = this; - if (!isDetachedTerminalInstance(this._instance)) { - this._terminalService.setActiveInstance(this._instance); - } - }); - chatWidget.focusTracker.onDidBlur(() => { - TerminalChatContribution.activeChatWidget = undefined; - this._instance.resetScrollbarVisibility(); - }); - if (!this._instance.domElement) { - throw new Error('FindWidget expected terminal DOM to be initialized'); - } - - // this._instance.domElement?.appendChild(chatWidget.getDomNode()); - if (this._lastLayoutDimensions) { - chatWidget.layout(this._lastLayoutDimensions.width); - } - - return chatWidget; - }); - } - - override dispose() { - super.dispose(); - this._chatWidget?.rawValue?.dispose(); - } -} -registerTerminalContribution(TerminalChatContribution.ID, TerminalChatContribution, false); +registerTerminalContribution(TerminalChatController.ID, TerminalChatController, false); registerActiveXtermAction({ id: TerminalCommandId.FocusChat, @@ -115,7 +36,7 @@ registerActiveXtermAction({ if (isDetachedTerminalInstance(activeInstance)) { return; } - const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); contr?.chatWidget?.reveal(); } }); @@ -138,7 +59,7 @@ registerActiveXtermAction({ if (isDetachedTerminalInstance(activeInstance)) { return; } - const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); contr?.chatWidget?.hide(); } }); @@ -153,7 +74,7 @@ registerActiveXtermAction({ ), icon: Codicon.send, keybinding: { - when: TerminalContextKeys.chatSessionInProgress.negate(), + when: TerminalContextKeys.chatRequestActive.negate(), // TODO: // when: CTX_INLINE_CHAT_FOCUSED, weight: KeybindingWeight.EditorCore + 7, @@ -171,8 +92,8 @@ registerActiveXtermAction({ if (isDetachedTerminalInstance(activeInstance)) { return; } - const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); - contr?.chatWidget?.acceptInput(); + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + contr?.acceptInput(); } }); @@ -181,7 +102,7 @@ registerActiveXtermAction({ title: localize2('workbench.action.terminal.cancelChat', 'Cancel Chat'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalContextKeys.chatSessionInProgress, + TerminalContextKeys.chatRequestActive, ), icon: Codicon.debugStop, menu: { @@ -192,7 +113,7 @@ registerActiveXtermAction({ if (isDetachedTerminalInstance(activeInstance)) { return; } - const contr = TerminalChatContribution.activeChatWidget || TerminalChatContribution.get(activeInstance); + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); contr?.chatWidget?.cancel(); } }); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts new file mode 100644 index 00000000000..ae9ded44aec --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -0,0 +1,119 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget'; +import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { IDimension } from 'vs/base/browser/dom'; +import { Lazy } from 'vs/base/common/lazy'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; +import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; +import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; + +export class TerminalChatController extends Disposable implements ITerminalContribution { + static readonly ID = 'terminal.Chat'; + + static get(instance: ITerminalInstance): TerminalChatController | null { + return instance.getContribution(TerminalChatController.ID); + } + /** + * Currently focused chat widget. This is used to track action context since + * 'active terminals' are only tracked for non-detached terminal instanecs. + */ + static activeChatWidget?: TerminalChatController; + private _chatWidget: Lazy | undefined; + private _lastLayoutDimensions: IDimension | undefined; + + get chatWidget(): TerminalChatWidget | undefined { return this._chatWidget?.value; } + + // private _sessionCtor: CancelablePromise | undefined; + private _activeSession?: Session; + // private readonly _ctxHasActiveRequest: IContextKey; + // private _isVisible: boolean = false; + // private _strategy: EditStrategy | undefined; + + // private _inlineChatListener: IDisposable | undefined; + // private _toolbar: MenuWorkbenchToolBar | undefined; + // private readonly _ctxLastResponseType: IContextKey; + // private _widgetDisposableStore: DisposableStore = this._register(new DisposableStore()); + + constructor( + private readonly _instance: ITerminalInstance, + processManager: ITerminalProcessManager, + widgetManager: TerminalWidgetManager, + @IConfigurationService private _configurationService: IConfigurationService, + @ITerminalService private readonly _terminalService: ITerminalService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IInlineChatSessionService private readonly _inlineChatSessionService: IInlineChatSessionService, + // @IContextKeyService private readonly _contextKeyService: IContextKeyService, + // @IInstantiationService private readonly _instantiationService: IInstantiationService, + // @ICommandService private readonly _commandService: ICommandService, + // @IInlineChatSavingService private readonly _inlineChatSavingService: IInlineChatSavingService + ) { + super(); + if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { + return; + } + // this._ctxHasActiveRequest = TerminalContextKeys.chatRequestActive.bindTo(this._contextKeyService); + // this._ctxLastResponseType = CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.bindTo(this._contextKeyService); + } + + layout(_xterm: IXtermTerminal & { raw: RawXtermTerminal }, dimension: IDimension): void { + if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { + return; + } + this._lastLayoutDimensions = dimension; + this._chatWidget?.rawValue?.layout(dimension.width); + } + + xtermReady(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { + if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { + return; + } + this._chatWidget = new Lazy(() => { + const chatWidget = this._instantiationService.createInstance(TerminalChatWidget, this._instance.domElement!, this._instance); + chatWidget.focusTracker.onDidFocus(() => { + TerminalChatController.activeChatWidget = this; + if (!isDetachedTerminalInstance(this._instance)) { + this._terminalService.setActiveInstance(this._instance); + } + }); + chatWidget.focusTracker.onDidBlur(() => { + TerminalChatController.activeChatWidget = undefined; + this._instance.resetScrollbarVisibility(); + }); + if (!this._instance.domElement) { + throw new Error('FindWidget expected terminal DOM to be initialized'); + } + + // this._instance.domElement?.appendChild(chatWidget.getDomNode()); + if (this._lastLayoutDimensions) { + chatWidget.layout(this._lastLayoutDimensions.width); + } + + return chatWidget; + }); + } + + async acceptInput(): Promise { + // TODO: create session, deal with response + this._chatWidget?.rawValue?.acceptInput(); + } + + reveal(): void { + this._chatWidget?.rawValue?.reveal(); + } + + override dispose() { + super.dispose(); + this._chatWidget?.rawValue?.dispose(); + } +} + diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 1467a9a86ef..14a6ca567b4 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -115,12 +115,13 @@ export class TerminalChatWidget extends Disposable { } acceptInput(): void { // this._widget?.acceptInput(); - this._chatModel ??= this._chatService.startSession('terminal', CancellationToken.None); + // this._chatModel ??= this._chatService.startSession('terminal', CancellationToken.None); // if (!this._model) { // throw new Error('Could not start chat session'); // } - this._chatService?.sendRequest(this._chatModel?.sessionId!, this._inlineChatWidget.value); + // this._chatService?.sendRequest(this._chatModel?.sessionId!, this._inlineChatWidget.value); + this._inlineChatWidget.value = ''; // this.widget.setModel(this.model, { inputValue: this._currentQuery }); } From fd0784486582c2cff44a8c4bb28fa01b38904473 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 12 Feb 2024 16:27:38 -0600 Subject: [PATCH 0025/1175] wip make request action --- .../chat/browser/terminalChatController.ts | 20 +++++++++- .../chat/browser/terminalChatWidget.ts | 39 ++++++++++++++++--- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index ae9ded44aec..5392e1787dd 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -14,8 +14,8 @@ import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/wid import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; export class TerminalChatController extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.Chat'; @@ -51,7 +51,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr @IConfigurationService private _configurationService: IConfigurationService, @ITerminalService private readonly _terminalService: ITerminalService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IInlineChatSessionService private readonly _inlineChatSessionService: IInlineChatSessionService, + @IChatAgentService private readonly _chatAgentService: IChatAgentService // @IContextKeyService private readonly _contextKeyService: IContextKeyService, // @IInstantiationService private readonly _instantiationService: IInstantiationService, // @ICommandService private readonly _commandService: ICommandService, @@ -104,6 +104,22 @@ export class TerminalChatController extends Disposable implements ITerminalContr async acceptInput(): Promise { // TODO: create session, deal with response + // this._activeSession = new Session(EditMode.Live, , this._instance); + // const initVariableData: IChatRequestVariableData = { message: getPromptText(parsedRequest.parts), variables: {} }; + // request = model.addRequest(parsedRequest, initVariableData, agent, agentSlashCommandPart?.command); + // const variableData = await this.chatVariablesService.resolveVariables(parsedRequest, model, token); + // const requestProps: IChatAgentRequest = { + // sessionId: 'sessionId', + // requestId: 'fake', + // agentId: 'terminal', + // message: this._chatWidget?.rawValue?.getValue() || '', + // // variables: variableData.variables, + // // command: agentSlashCommandPart?.command.name, + // // variables2: asVariablesData2(parsedRequest, variableData) + // }; + // const agentResult = await this._chatAgentService.invokeAgent('terminal', requestProps, progressCallback, undefined, token); + // const rawResult = agentResult; + // const agentOrCommandFollowups = this._chatAgentService.getFollowups('terminal', agentResult, followupsCancelToken); this._chatWidget?.rawValue?.acceptInput(); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 14a6ca567b4..ed78f6292e7 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -16,8 +16,9 @@ import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/termin import { localize } from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; import { MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_STATUS, MENU_CELL_CHAT_WIDGET_FEEDBACK } from 'vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController'; -import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; -import { ChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; +import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; +import { generateUuid } from 'vs/base/common/uuid'; +import { IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { CancellationToken } from 'vs/base/common/cancellation'; export class TerminalChatWidget extends Disposable { @@ -30,7 +31,6 @@ export class TerminalChatWidget extends Disposable { private readonly _focusTracker: IFocusTracker; - private _chatModel: ChatModel | undefined; constructor( private readonly _container: HTMLElement, @@ -38,7 +38,7 @@ export class TerminalChatWidget extends Disposable { @IInstantiationService instantiationService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IChatService private readonly _chatService: IChatService) { + @IChatAgentService private readonly _chatAgentService: IChatAgentService) { super(); const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._container)); this._scopedInstantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); @@ -113,7 +113,7 @@ export class TerminalChatWidget extends Disposable { cancel(): void { // this._widget?.clear(); } - acceptInput(): void { + async acceptInput(): Promise { // this._widget?.acceptInput(); // this._chatModel ??= this._chatService.startSession('terminal', CancellationToken.None); @@ -121,9 +121,36 @@ export class TerminalChatWidget extends Disposable { // throw new Error('Could not start chat session'); // } // this._chatService?.sendRequest(this._chatModel?.sessionId!, this._inlineChatWidget.value); + // this._activeSession = new Session(EditMode.Live, , this._instance); + // const initVariableData: IChatRequestVariableData = { message: getPromptText(parsedRequest.parts), variables: {} }; + // request = model.addRequest(parsedRequest, initVariableData, agent, agentSlashCommandPart?.command); + // const variableData = await this.chatVariablesService.resolveVariables(parsedRequest, model, token); + const progressCallback = (progress: IChatProgress) => { + // if (token.isCancellationRequested) { + // return; + // } + console.log(progress); + // gotProgress = true; + + if (progress.kind === 'content' || progress.kind === 'markdownContent') { + // this.trace('sendRequest', `Provider returned progress for session ${model.sessionId}, ${typeof progress.content === 'string' ? progress.content.length : progress.content.value.length} chars`); + } else { + // this.trace('sendRequest', `Provider returned progress: ${JSON.stringify(progress)}`); + } + // model.acceptResponseProgress(request, progress); + }; + const requestProps: IChatAgentRequest = { + sessionId: generateUuid(), + requestId: generateUuid(), + agentId: 'terminal', + message: this._inlineChatWidget.value || '', + variables: new Map() as any, + variables2: {} as any + }; + const agentResult = await this._chatAgentService.invokeAgent('terminal', requestProps, progressCallback, [], CancellationToken.None); + console.log(agentResult); this._inlineChatWidget.value = ''; - // this.widget.setModel(this.model, { inputValue: this._currentQuery }); } layout(width: number): void { // this._widget?.layout(100, width < 300 ? 300 : width); From 2f785c99624573f441d445d7327fd48790f899e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chanchevrier?= Date: Tue, 13 Feb 2024 09:14:21 +0100 Subject: [PATCH 0026/1175] fix: resize direction for terminals in the sidebar --- .../workbench/contrib/terminal/browser/terminalGroup.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index 7e944aa9753..30a95315f33 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -70,7 +70,6 @@ class SplitPaneContainer extends Disposable { if ( (this.orientation === Orientation.HORIZONTAL && direction === Direction.Down) || (this.orientation === Orientation.VERTICAL && direction === Direction.Right) || - (part === Parts.SIDEBAR_PART && direction === Direction.Left) || (part === Parts.AUXILIARYBAR_PART && direction === Direction.Right) ) { amount *= -1; @@ -578,9 +577,13 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { const isHorizontal = (direction === Direction.Left || direction === Direction.Right); + const part = getPartByLocation(this._terminalLocation); + + const isTerminalLeft = this._panelPosition === Position.LEFT || part === Parts.SIDEBAR_PART; + // Left-positionned panels have inverted controls // see https://github.com/microsoft/vscode/issues/140873 - const shouldInvertHorizontalResize = (isHorizontal && this._panelPosition === Position.LEFT); + const shouldInvertHorizontalResize = (isHorizontal && isTerminalLeft); const resizeDirection = shouldInvertHorizontalResize ? direction === Direction.Left ? Direction.Right : Direction.Left : direction; @@ -588,7 +591,7 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { // TODO: Support letter spacing and line height const charSize = (isHorizontal ? font.charWidth : font.charHeight); if (charSize) { - this._splitPaneContainer.resizePane(this._activeInstanceIndex, resizeDirection, charSize * Constants.ResizePartCellCount, getPartByLocation(this._terminalLocation)); + this._splitPaneContainer.resizePane(this._activeInstanceIndex, resizeDirection, charSize * Constants.ResizePartCellCount, part); } } From 4d542ef5d144449f1c399dc5c06cbf296380b7fe Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Tue, 13 Feb 2024 13:55:49 +0000 Subject: [PATCH 0027/1175] Make channel log level settable from output view --- .../contrib/logs/common/logs.contribution.ts | 4 +- .../contrib/logs/common/logsActions.ts | 17 ++++++--- .../output/browser/output.contribution.ts | 37 ++++++++++++++++++- .../contrib/output/browser/outputServices.ts | 9 ++++- .../services/output/common/output.ts | 2 + 5 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index 19257411641..2b95a4341ce 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -36,8 +36,8 @@ registerAction2(class extends Action2 { f1: true }); } - run(servicesAccessor: ServicesAccessor): Promise { - return servicesAccessor.get(IInstantiationService).createInstance(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.TITLE.value).run(); + run(servicesAccessor: ServicesAccessor, channelId?: string): Promise { + return servicesAccessor.get(IInstantiationService).createInstance(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.TITLE.value).run(channelId); } }); diff --git a/src/vs/workbench/contrib/logs/common/logsActions.ts b/src/vs/workbench/contrib/logs/common/logsActions.ts index 519fab7f93a..f295d14a03c 100644 --- a/src/vs/workbench/contrib/logs/common/logsActions.ts +++ b/src/vs/workbench/contrib/logs/common/logsActions.ts @@ -12,7 +12,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { dirname, basename, isEqual } from 'vs/base/common/resources'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IOutputService } from 'vs/workbench/services/output/common/output'; +import { IOutputChannelDescriptor, IOutputService } from 'vs/workbench/services/output/common/output'; import { extensionTelemetryLogChannelId, telemetryLogId } from 'vs/platform/telemetry/common/telemetryUtils'; import { IDefaultLogLevelsService } from 'vs/workbench/contrib/logs/common/defaultLogLevels'; import { Codicon } from 'vs/base/common/codicons'; @@ -36,8 +36,8 @@ export class SetLogLevelAction extends Action { super(id, label); } - override async run(): Promise { - const logLevelOrChannel = await this.selectLogLevelOrChannel(); + override async run(channelId?: string): Promise { + const logLevelOrChannel = await this.getLogLevelOrChannel(channelId); if (logLevelOrChannel !== null) { if (isLogLevel(logLevelOrChannel)) { this.loggerService.setLogLevel(logLevelOrChannel); @@ -47,16 +47,19 @@ export class SetLogLevelAction extends Action { } } - private async selectLogLevelOrChannel(): Promise { + private async getLogLevelOrChannel(channelId?: string): Promise { const defaultLogLevels = await this.defaultLogLevelsService.getDefaultLogLevels(); const extensionLogs: LogChannelQuickPickItem[] = [], logs: LogChannelQuickPickItem[] = []; const logLevel = this.loggerService.getLogLevel(); for (const channel of this.outputService.getChannelDescriptors()) { - if (!channel.log || !channel.file || channel.id === telemetryLogId || channel.id === extensionTelemetryLogChannelId) { + if (!SetLogLevelAction.isLevelSettable(channel) || !channel.file) { continue; } const channelLogLevel = this.loggerService.getLogLevel(channel.file) ?? logLevel; const item: LogChannelQuickPickItem = { id: channel.id, resource: channel.file, label: channel.label, description: channelLogLevel !== logLevel ? this.getLabel(channelLogLevel) : undefined, extensionId: channel.extensionId }; + if (channelId && channel.id === channelId) { + return item; + } if (channel.extensionId) { extensionLogs.push(item); } else { @@ -96,6 +99,10 @@ export class SetLogLevelAction extends Action { }); } + static isLevelSettable(channel: IOutputChannelDescriptor): boolean { + return channel.log && channel.file !== undefined && channel.id !== telemetryLogId && channel.id !== extensionTelemetryLogChannelId; + } + private async setLogLevelForChannel(logChannel: LogChannelQuickPickItem): Promise { const defaultLogLevels = await this.defaultLogLevelsService.getDefaultLogLevels(); const defaultLogLevel = defaultLogLevels.extensions.find(e => e[0] === logChannel.extensionId?.toLowerCase())?.[1] ?? defaultLogLevels.default; diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index 794a8e60d57..6ef9f9764ea 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { MenuId, registerAction2, Action2, MenuRegistry } from 'vs/platform/actions/common/actions'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { OutputService } from 'vs/workbench/contrib/output/browser/outputServices'; -import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_VIEW_ID, IOutputService, CONTEXT_IN_OUTPUT, LOG_MODE_ID, LOG_MIME, CONTEXT_ACTIVE_FILE_OUTPUT, CONTEXT_OUTPUT_SCROLL_LOCK, IOutputChannelDescriptor, IFileOutputChannelDescriptor, ACTIVE_OUTPUT_CHANNEL_CONTEXT, IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; +import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_VIEW_ID, IOutputService, CONTEXT_IN_OUTPUT, LOG_MODE_ID, LOG_MIME, CONTEXT_ACTIVE_FILE_OUTPUT, CONTEXT_OUTPUT_SCROLL_LOCK, IOutputChannelDescriptor, IFileOutputChannelDescriptor, ACTIVE_OUTPUT_CHANNEL_CONTEXT, CONTEXT_ACTIVE_OUTPUT_LEVEL_SETTABLE, IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; import { OutputViewPane } from 'vs/workbench/contrib/output/browser/outputView'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -30,6 +30,8 @@ import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { AccessibilitySignal, IAccessibilitySignalService } from 'vs/platform/accessibilitySignal/browser/accessibilitySignalService'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { SetLogLevelAction } from 'vs/workbench/contrib/logs/common/logsActions'; // Register Service registerSingleton(IOutputService, OutputService, InstantiationType.Delayed); @@ -99,6 +101,7 @@ class OutputContribution extends Disposable implements IWorkbenchContribution { this.registerOpenActiveOutputFileInAuxWindowAction(); this.registerShowLogsAction(); this.registerOpenLogFileAction(); + this.registerConfigureActiveOutputLogLevelAction(); } private registerSwitchOutputAction(): void { @@ -334,6 +337,38 @@ class OutputContribution extends Disposable implements IWorkbenchContribution { return null; } + private registerConfigureActiveOutputLogLevelAction(): void { + const that = this; + this._register(registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.action.configureActiveOutputLogLevel`, + title: nls.localize2('configureActiveOutputLogLevel', "Configure Log Level..."), + menu: [{ + id: MenuId.ViewTitle, + when: ContextKeyExpr.equals('view', OUTPUT_VIEW_ID), + group: 'navigation', + order: 6, + isHiddenByDefault: true + }], + icon: Codicon.gear, + precondition: CONTEXT_ACTIVE_OUTPUT_LEVEL_SETTABLE + }); + } + async run(accessor: ServicesAccessor): Promise { + const commandService = accessor.get(ICommandService); + that.configureActiveOutputLogLevel(commandService); + } + })); + } + + private async configureActiveOutputLogLevel(commandService: ICommandService): Promise { + const channel = this.outputService.getActiveChannel(); + if (channel) { + await commandService.executeCommand(SetLogLevelAction.ID, channel.id); + } + } + private registerShowLogsAction(): void { this._register(registerAction2(class extends Action2 { constructor() { diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index 26fed6ffc4e..e004aaa86bc 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -9,7 +9,7 @@ import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IOutputChannel, IOutputService, OUTPUT_VIEW_ID, OUTPUT_SCHEME, LOG_MIME, OUTPUT_MIME, OutputChannelUpdateMode, IOutputChannelDescriptor, Extensions, IOutputChannelRegistry, ACTIVE_OUTPUT_CHANNEL_CONTEXT, CONTEXT_ACTIVE_FILE_OUTPUT } from 'vs/workbench/services/output/common/output'; +import { IOutputChannel, IOutputService, OUTPUT_VIEW_ID, OUTPUT_SCHEME, LOG_MIME, OUTPUT_MIME, OutputChannelUpdateMode, IOutputChannelDescriptor, Extensions, IOutputChannelRegistry, ACTIVE_OUTPUT_CHANNEL_CONTEXT, CONTEXT_ACTIVE_FILE_OUTPUT, CONTEXT_ACTIVE_OUTPUT_LEVEL_SETTABLE } from 'vs/workbench/services/output/common/output'; import { OutputLinkProvider } from 'vs/workbench/contrib/output/browser/outputLinkProvider'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { ITextModel } from 'vs/editor/common/model'; @@ -21,6 +21,7 @@ import { OutputViewPane } from 'vs/workbench/contrib/output/browser/outputView'; import { IOutputChannelModelService } from 'vs/workbench/contrib/output/common/outputChannelModelService'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { SetLogLevelAction } from 'vs/workbench/contrib/logs/common/logsActions'; const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel'; @@ -74,6 +75,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo private readonly activeOutputChannelContext: IContextKey; private readonly activeFileOutputChannelContext: IContextKey; + private readonly activeOutputChannelLevelSettableContext: IContextKey; constructor( @IStorageService private readonly storageService: IStorageService, @@ -91,6 +93,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo this._register(this.onActiveOutputChannel(channel => this.activeOutputChannelContext.set(channel))); this.activeFileOutputChannelContext = CONTEXT_ACTIVE_FILE_OUTPUT.bindTo(contextKeyService); + this.activeOutputChannelLevelSettableContext = CONTEXT_ACTIVE_OUTPUT_LEVEL_SETTABLE.bindTo(contextKeyService); // Register as text model content provider for output textModelResolverService.registerTextModelContentProvider(OUTPUT_SCHEME, this); @@ -196,7 +199,9 @@ export class OutputService extends Disposable implements IOutputService, ITextMo private setActiveChannel(channel: OutputChannel | undefined): void { this.activeChannel = channel; - this.activeFileOutputChannelContext.set(!!channel?.outputChannelDescriptor?.file); + const descriptor = channel?.outputChannelDescriptor; + this.activeFileOutputChannelContext.set(!!descriptor?.file); + this.activeOutputChannelLevelSettableContext.set(descriptor !== undefined && SetLogLevelAction.isLevelSettable(descriptor)); if (this.activeChannel) { this.storageService.store(OUTPUT_ACTIVE_CHANNEL_KEY, this.activeChannel.id, StorageScope.WORKSPACE, StorageTarget.MACHINE); diff --git a/src/vs/workbench/services/output/common/output.ts b/src/vs/workbench/services/output/common/output.ts index 012855aaaee..716bb3bcfd6 100644 --- a/src/vs/workbench/services/output/common/output.ts +++ b/src/vs/workbench/services/output/common/output.ts @@ -43,6 +43,8 @@ export const CONTEXT_IN_OUTPUT = new RawContextKey('inOutput', false); export const CONTEXT_ACTIVE_FILE_OUTPUT = new RawContextKey('activeLogOutput', false); +export const CONTEXT_ACTIVE_OUTPUT_LEVEL_SETTABLE = new RawContextKey('activeLogOutput.levelSettable', false); + export const CONTEXT_OUTPUT_SCROLL_LOCK = new RawContextKey(`outputView.scrollLock`, false); export const IOutputService = createDecorator('outputService'); From 760ee5b9976bc9ee647e5aa5ecfa26c42780fab4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 10:22:14 -0600 Subject: [PATCH 0028/1175] wip --- .../chat/browser/terminalChatController.ts | 65 ++++++++++++------- .../chat/browser/terminalChatWidget.ts | 64 ++++++------------ 2 files changed, 63 insertions(+), 66 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 5392e1787dd..8308105d9f5 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -14,8 +14,11 @@ import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/wid import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; -import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; +import { generateUuid } from 'vs/base/common/uuid'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; export class TerminalChatController extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.Chat'; @@ -34,7 +37,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr get chatWidget(): TerminalChatWidget | undefined { return this._chatWidget?.value; } // private _sessionCtor: CancelablePromise | undefined; - private _activeSession?: Session; + // private _activeSession?: Session; // private readonly _ctxHasActiveRequest: IContextKey; // private _isVisible: boolean = false; // private _strategy: EditStrategy | undefined; @@ -51,7 +54,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr @IConfigurationService private _configurationService: IConfigurationService, @ITerminalService private readonly _terminalService: ITerminalService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IChatAgentService private readonly _chatAgentService: IChatAgentService + @IChatAgentService private readonly _chatAgentService: IChatAgentService, // @IContextKeyService private readonly _contextKeyService: IContextKeyService, // @IInstantiationService private readonly _instantiationService: IInstantiationService, // @ICommandService private readonly _commandService: ICommandService, @@ -93,7 +96,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr throw new Error('FindWidget expected terminal DOM to be initialized'); } - // this._instance.domElement?.appendChild(chatWidget.getDomNode()); if (this._lastLayoutDimensions) { chatWidget.layout(this._lastLayoutDimensions.width); } @@ -103,24 +105,41 @@ export class TerminalChatController extends Disposable implements ITerminalContr } async acceptInput(): Promise { - // TODO: create session, deal with response - // this._activeSession = new Session(EditMode.Live, , this._instance); - // const initVariableData: IChatRequestVariableData = { message: getPromptText(parsedRequest.parts), variables: {} }; - // request = model.addRequest(parsedRequest, initVariableData, agent, agentSlashCommandPart?.command); - // const variableData = await this.chatVariablesService.resolveVariables(parsedRequest, model, token); - // const requestProps: IChatAgentRequest = { - // sessionId: 'sessionId', - // requestId: 'fake', - // agentId: 'terminal', - // message: this._chatWidget?.rawValue?.getValue() || '', - // // variables: variableData.variables, - // // command: agentSlashCommandPart?.command.name, - // // variables2: asVariablesData2(parsedRequest, variableData) - // }; - // const agentResult = await this._chatAgentService.invokeAgent('terminal', requestProps, progressCallback, undefined, token); - // const rawResult = agentResult; - // const agentOrCommandFollowups = this._chatAgentService.getFollowups('terminal', agentResult, followupsCancelToken); - this._chatWidget?.rawValue?.acceptInput(); + let message = ''; + const progressCallback = (progress: IChatProgress) => { + // if (token.isCancellationRequested) { + // return; + // } + + + // gotProgress = true; + + if (progress.kind === 'content' || progress.kind === 'markdownContent') { + // this.trace('sendRequest', `Provider returned progress for session ${model.sessionId}, ${typeof progress.content === 'string' ? progress.content.length : progress.content.value.length} chars`); + message += progress.content; + } else { + // this.trace('sendRequest', `Provider returned progress: ${JSON.stringify(progress)}`); + } + + // model.acceptResponseProgress(request, progress); + }; + const resolvedVariables: Record = {}; + + const requestProps: IChatAgentRequest = { + sessionId: generateUuid(), + requestId: generateUuid(), + agentId: 'terminal', + message: this._chatWidget?.rawValue?.input() || '', + variables: resolvedVariables, + variables2: { message: this._chatWidget?.rawValue?.input() || '', variables: [] } + }; + // TODO: use token + const agentResult = await this._chatAgentService.invokeAgent('terminal', requestProps, progressCallback, [], CancellationToken.None); + console.log(agentResult); + console.log(message); + alert(message); + this._chatWidget?.rawValue?.setValue(); + // TODO: accessibility announcement, help dialog } reveal(): void { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index ed78f6292e7..ee0a778cc0f 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -15,11 +15,8 @@ import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/termina import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { localize } from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; -import { MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_STATUS, MENU_CELL_CHAT_WIDGET_FEEDBACK } from 'vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController'; -import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; -import { generateUuid } from 'vs/base/common/uuid'; -import { IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_FEEDBACK, MENU_CELL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext'; export class TerminalChatWidget extends Disposable { private _scopedInstantiationService: IInstantiationService; @@ -113,45 +110,26 @@ export class TerminalChatWidget extends Disposable { cancel(): void { // this._widget?.clear(); } - async acceptInput(): Promise { - // this._widget?.acceptInput(); - // this._chatModel ??= this._chatService.startSession('terminal', CancellationToken.None); - - // if (!this._model) { - // throw new Error('Could not start chat session'); - // } - // this._chatService?.sendRequest(this._chatModel?.sessionId!, this._inlineChatWidget.value); - // this._activeSession = new Session(EditMode.Live, , this._instance); - // const initVariableData: IChatRequestVariableData = { message: getPromptText(parsedRequest.parts), variables: {} }; - // request = model.addRequest(parsedRequest, initVariableData, agent, agentSlashCommandPart?.command); - // const variableData = await this.chatVariablesService.resolveVariables(parsedRequest, model, token); - const progressCallback = (progress: IChatProgress) => { - // if (token.isCancellationRequested) { - // return; - // } - console.log(progress); - // gotProgress = true; - - if (progress.kind === 'content' || progress.kind === 'markdownContent') { - // this.trace('sendRequest', `Provider returned progress for session ${model.sessionId}, ${typeof progress.content === 'string' ? progress.content.length : progress.content.value.length} chars`); - } else { - // this.trace('sendRequest', `Provider returned progress: ${JSON.stringify(progress)}`); - } - - // model.acceptResponseProgress(request, progress); - }; - const requestProps: IChatAgentRequest = { - sessionId: generateUuid(), - requestId: generateUuid(), - agentId: 'terminal', - message: this._inlineChatWidget.value || '', - variables: new Map() as any, - variables2: {} as any - }; - const agentResult = await this._chatAgentService.invokeAgent('terminal', requestProps, progressCallback, [], CancellationToken.None); - console.log(agentResult); - this._inlineChatWidget.value = ''; + input(): string { + return this._inlineChatWidget.value; + } + setValue(value?: string) { + this._inlineChatWidget.value = value ?? ''; } + // async acceptInput(): Promise { + // // this._widget?.acceptInput(); + // // this._chatModel ??= this._chatService.startSession('terminal', CancellationToken.None); + + // // if (!this._model) { + // // throw new Error('Could not start chat session'); + // // } + // // this._chatService?.sendRequest(this._chatModel?.sessionId!, this._inlineChatWidget.value); + // // this._activeSession = new Session(EditMode.Live, , this._instance); + // // const initVariableData: IChatRequestVariableData = { message: getPromptText(parsedRequest.parts), variables: {} }; + // // request = model.addRequest(parsedRequest, initVariableData, agent, agentSlashCommandPart?.command); + // // const variableData = await this.chatVariablesService.resolveVariables(parsedRequest, model, token); + // this._inlineChatWidget.value = ''; + // } layout(width: number): void { // this._widget?.layout(100, width < 300 ? 300 : width); } From b998b6432bd6c080a26b59ccd8a03f69b06a0169 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 08:30:12 -0800 Subject: [PATCH 0029/1175] Add menus for terminal chat to the terminalContrib --- src/vs/platform/actions/common/actions.ts | 1 - .../chat/browser/terminal.chat.contribution.ts | 11 +++++++++-- .../chat/browser/terminalChat.ts | 12 ++++++++++++ .../chat/browser/terminalChatWidget.ts | 17 ++++++++--------- 4 files changed, 29 insertions(+), 12 deletions(-) create mode 100644 src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 3102938a3be..0b3015ddf75 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -211,7 +211,6 @@ export class MenuId { static readonly ChatCodeBlock = new MenuId('ChatCodeblock'); static readonly ChatMessageTitle = new MenuId('ChatMessageTitle'); static readonly ChatExecute = new MenuId('ChatExecute'); - static readonly TerminalChat = new MenuId('TerminalChat'); static readonly ChatInputSide = new MenuId('ChatInputSide'); static readonly AccessibleView = new MenuId('AccessibleView'); static readonly MultiDiffEditorFileToolbar = new MenuId('MultiDiffEditorFileToolbar'); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index 1d909e6fc30..fd88e4c0982 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -15,6 +15,7 @@ import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; registerTerminalContribution(TerminalChatController.ID, TerminalChatController, false); @@ -43,13 +44,19 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalCommandId.HideChat, - title: localize2('workbench.action.terminal.hideChat', 'Hide Chat'), + title: localize2('workbench.action.terminal.closeChat', 'Close Chat'), keybinding: { primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], when: ContextKeyExpr.and(TerminalContextKeys.chatFocused, TerminalContextKeys.chatVisible), weight: KeybindingWeight.WorkbenchContrib }, + icon: Codicon.close, + menu: { + id: MENU_TERMINAL_CHAT_WIDGET, + group: 'main', + order: 2 + }, f1: true, precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), @@ -81,7 +88,7 @@ registerActiveXtermAction({ primary: KeyCode.Enter }, menu: { - id: MenuId.TerminalChat, + id: MENU_TERMINAL_CHAT_INPUT, group: 'main', order: 1, // when: TerminalContextKeys.chatSessionInProgress.negate(), diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts new file mode 100644 index 00000000000..8b74aae7aeb --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { MenuId } from 'vs/platform/actions/common/actions'; + +export const MENU_TERMINAL_CHAT_INPUT = MenuId.for('terminalChatInput'); +export const MENU_TERMINAL_CHAT_WIDGET = MenuId.for('terminalChatWidget'); +export const MENU_TERMINAL_CHAT_WIDGET_STATUS = MenuId.for('terminalChatWidget.status'); +export const MENU_TERMINAL_CHAT_WIDGET_FEEDBACK = MenuId.for('terminalChatWidget.feedback'); +export const MENU_TERMINAL_CHAT_WIDGET_TOOLBAR = MenuId.for('terminalChatWidget.toolbar'); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index ee0a778cc0f..7f30c3bca53 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -3,20 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/terminalChatWidget'; import { Dimension, IFocusTracker, trackFocus } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; +import 'vs/css!./media/terminalChatWidget'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { localize } from 'vs/nls'; -import { MenuId } from 'vs/platform/actions/common/actions'; -import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; -import { MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_FEEDBACK, MENU_CELL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext'; +import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, MENU_TERMINAL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; export class TerminalChatWidget extends Disposable { private _scopedInstantiationService: IInstantiationService; @@ -76,10 +75,10 @@ export class TerminalChatWidget extends Disposable { InlineChatWidget, fakeParentEditor, { - menuId: MenuId.TerminalChat, - widgetMenuId: MENU_CELL_CHAT_WIDGET, - statusMenuId: MENU_CELL_CHAT_WIDGET_STATUS, - feedbackMenuId: MENU_CELL_CHAT_WIDGET_FEEDBACK + menuId: MENU_TERMINAL_CHAT_INPUT, + widgetMenuId: MENU_TERMINAL_CHAT_WIDGET, + statusMenuId: MENU_TERMINAL_CHAT_WIDGET_STATUS, + feedbackMenuId: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK } ); this._inlineChatWidget.placeholder = localize('default.placeholder', "Ask how to do something in the terminal"); From e4e9562df987263f6315e4104a7e968bf45ef87a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 08:33:18 -0800 Subject: [PATCH 0030/1175] Clean up chat widget --- .../chat/browser/terminalChatWidget.ts | 30 ++++--------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 7f30c3bca53..330be0b35a5 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -31,10 +31,10 @@ export class TerminalChatWidget extends Disposable { constructor( private readonly _container: HTMLElement, private readonly _instance: ITerminalInstance, - @IInstantiationService instantiationService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IChatAgentService private readonly _chatAgentService: IChatAgentService) { + @IChatAgentService private readonly _chatAgentService: IChatAgentService + ) { super(); const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._container)); this._scopedInstantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); @@ -44,24 +44,9 @@ export class TerminalChatWidget extends Disposable { this._widgetContainer.classList.add('terminal-inline-chat'); this._container.appendChild(this._widgetContainer); - // this._widget = this._register(this._scopedInstantiationService.createInstance( - // ChatWidget, - // { viewId: 'terminal' }, - // { supportsFileReferences: false, renderStyle: 'compact' }, - // { - // listForeground: editorForeground, - // listBackground: editorBackground, - // inputEditorBackground: inputBackground, - // resultEditorBackground: editorBackground - // })); - // this._widget.render(this._widgetContainer); - // this._register(this._widget.onDidFocus(() => this._chatWidgetFocused.set(true))); - + // The inline chat widget requires a parent editor that it bases the diff view on, since the + // terminal doesn't use that feature we can just pass in an unattached editor instance. const fakeParentEditorElement = document.createElement('div'); - - // const editorConstructionOptions = this.inputEditorOptions.getEditorConstructionOptions(); - // this.setPlaceholderFontStyles(editorConstructionOptions.fontFamily!, editorConstructionOptions.fontSize!, editorConstructionOptions.lineHeight!); - const fakeParentEditor = this._scopedInstantiationService.createInstance( CodeEditorWidget, fakeParentEditorElement, @@ -91,23 +76,18 @@ export class TerminalChatWidget extends Disposable { this._inlineChatWidget.layout(new Dimension(400, 150)); this._widgetContainer.classList.remove('hide'); - // this._widget.setVisible(true); this._chatWidgetFocused.set(true); this._chatWidgetVisible.set(true); - // this._widget.setInput('@terminal'); - // this._widget.setInputPlaceholder('Request a terminal command'); - // this._widget.focusInput(); this._inlineChatWidget.focus(); } hide(): void { this._widgetContainer.classList.add('hide'); this._chatWidgetFocused.set(false); this._chatWidgetVisible.set(false); - // this._widget.clear(); this._instance.focus(); } cancel(): void { - // this._widget?.clear(); + // TODO: Impl } input(): string { return this._inlineChatWidget.value; From 32b3e7ed0e0fb868f3b2ede89ffa94b898f7a4c1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 08:45:29 -0800 Subject: [PATCH 0031/1175] Standardize chat command ns, add feedback placeholders --- .../contrib/terminal/common/terminal.ts | 11 +-- .../browser/terminal.chat.contribution.ts | 70 +++++++++++++++++-- 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 5ed9e674af2..f2167db7f17 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -500,10 +500,13 @@ export const enum TerminalCommandId { FontZoomIn = 'workbench.action.terminal.fontZoomIn', FontZoomOut = 'workbench.action.terminal.fontZoomOut', FontZoomReset = 'workbench.action.terminal.fontZoomReset', - FocusChat = 'workbench.action.terminal.focusChat', - HideChat = 'workbench.action.terminal.hideChat', - MakeChatRequest = 'workbench.action.terminal.makeChatRequest', - CancelChat = 'workbench.action.terminal.cancelChat', + ChatFocus = 'workbench.action.terminal.chat.focus', + ChatHide = 'workbench.action.terminal.chat.close', + ChatMakeRequest = 'workbench.action.terminal.chat.makeRequest', + ChatCancel = 'workbench.action.terminal.chat.cancel', + ChatFeedbackHelpful = 'workbench.action.terminal.chat.feedbackHelpful', + ChatFeedbackUnhelpful = 'workbench.action.terminal.chat.feedbackUnhelpful', + ChatFeedbackReportIssue = 'workbench.action.terminal.chat.feedbackReportIssue', // Developer commands diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index fd88e4c0982..2972104787f 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -15,13 +15,13 @@ import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; +import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; registerTerminalContribution(TerminalChatController.ID, TerminalChatController, false); registerActiveXtermAction({ - id: TerminalCommandId.FocusChat, + id: TerminalCommandId.ChatFocus, title: localize2('workbench.action.terminal.focusChat', 'Focus Chat'), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyI, @@ -43,7 +43,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.HideChat, + id: TerminalCommandId.ChatHide, title: localize2('workbench.action.terminal.closeChat', 'Close Chat'), keybinding: { primary: KeyCode.Escape, @@ -72,7 +72,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.MakeChatRequest, + id: TerminalCommandId.ChatMakeRequest, title: localize2('workbench.action.terminal.submitChat', 'Make Chat Request'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), @@ -105,7 +105,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.CancelChat, + id: TerminalCommandId.ChatCancel, title: localize2('workbench.action.terminal.cancelChat', 'Cancel Chat'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), @@ -124,3 +124,63 @@ registerActiveXtermAction({ contr?.chatWidget?.cancel(); } }); + +registerActiveXtermAction({ + id: TerminalCommandId.ChatFeedbackHelpful, + title: localize2('feedbackHelpful', 'Helpful'), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + TerminalContextKeys.chatRequestActive, + ), + icon: Codicon.thumbsup, + menu: { + id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, + group: 'inline', + order: 1, + // TODO: Fill in ctx + // when: CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.notEqualsTo(undefined), + }, + run: (_xterm, _accessor, activeInstance) => { + // TODO: Impl + } +}); + +registerActiveXtermAction({ + id: TerminalCommandId.ChatFeedbackUnhelpful, + title: localize2('feedbackUnhelpful', 'Helpful'), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + TerminalContextKeys.chatRequestActive, + ), + icon: Codicon.thumbsup, + menu: { + id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, + group: 'inline', + order: 2, + // TODO: Fill in ctx + // when: CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.notEqualsTo(undefined), + }, + run: (_xterm, _accessor, activeInstance) => { + // TODO: Impl + } +}); + +registerActiveXtermAction({ + id: TerminalCommandId.ChatFeedbackReportIssue, + title: localize2('reportIssue', 'Report Issue'), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + TerminalContextKeys.chatRequestActive, + ), + icon: Codicon.thumbsup, + menu: { + id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, + group: 'inline', + order: 3, + // TODO: Fill in ctx + // when: CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.notEqualsTo(undefined), + }, + run: (_xterm, _accessor, activeInstance) => { + // TODO: Impl + } +}); From 611fd5cf88efac77f8fee0aed18bbef8aaeef768 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 08:46:57 -0800 Subject: [PATCH 0032/1175] Move chat command IDs into contrib --- .../contrib/terminal/common/terminal.ts | 7 ------- .../chat/browser/terminal.chat.contribution.ts | 17 ++++++++--------- .../chat/browser/terminalChat.ts | 10 ++++++++++ 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index f2167db7f17..fc925b646bb 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -500,13 +500,6 @@ export const enum TerminalCommandId { FontZoomIn = 'workbench.action.terminal.fontZoomIn', FontZoomOut = 'workbench.action.terminal.fontZoomOut', FontZoomReset = 'workbench.action.terminal.fontZoomReset', - ChatFocus = 'workbench.action.terminal.chat.focus', - ChatHide = 'workbench.action.terminal.chat.close', - ChatMakeRequest = 'workbench.action.terminal.chat.makeRequest', - ChatCancel = 'workbench.action.terminal.chat.cancel', - ChatFeedbackHelpful = 'workbench.action.terminal.chat.feedbackHelpful', - ChatFeedbackUnhelpful = 'workbench.action.terminal.chat.feedbackUnhelpful', - ChatFeedbackReportIssue = 'workbench.action.terminal.chat.feedbackReportIssue', // Developer commands diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index 2972104787f..f8a1dd4b111 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -13,15 +13,14 @@ import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; -import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; +import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, TerminalChatCommandId } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; registerTerminalContribution(TerminalChatController.ID, TerminalChatController, false); registerActiveXtermAction({ - id: TerminalCommandId.ChatFocus, + id: TerminalChatCommandId.Focus, title: localize2('workbench.action.terminal.focusChat', 'Focus Chat'), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyI, @@ -43,7 +42,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.ChatHide, + id: TerminalChatCommandId.Hide, title: localize2('workbench.action.terminal.closeChat', 'Close Chat'), keybinding: { primary: KeyCode.Escape, @@ -72,7 +71,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.ChatMakeRequest, + id: TerminalChatCommandId.MakeRequest, title: localize2('workbench.action.terminal.submitChat', 'Make Chat Request'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), @@ -105,7 +104,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.ChatCancel, + id: TerminalChatCommandId.Cancel, title: localize2('workbench.action.terminal.cancelChat', 'Cancel Chat'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), @@ -126,7 +125,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.ChatFeedbackHelpful, + id: TerminalChatCommandId.FeedbackHelpful, title: localize2('feedbackHelpful', 'Helpful'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), @@ -146,7 +145,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.ChatFeedbackUnhelpful, + id: TerminalChatCommandId.FeedbackUnhelpful, title: localize2('feedbackUnhelpful', 'Helpful'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), @@ -166,7 +165,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.ChatFeedbackReportIssue, + id: TerminalChatCommandId.FeedbackReportIssue, title: localize2('reportIssue', 'Report Issue'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts index 8b74aae7aeb..431a0e9c715 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts @@ -5,6 +5,16 @@ import { MenuId } from 'vs/platform/actions/common/actions'; +export const enum TerminalChatCommandId { + Focus = 'workbench.action.terminal.chat.focus', + Hide = 'workbench.action.terminal.chat.close', + MakeRequest = 'workbench.action.terminal.chat.makeRequest', + Cancel = 'workbench.action.terminal.chat.cancel', + FeedbackHelpful = 'workbench.action.terminal.chat.feedbackHelpful', + FeedbackUnhelpful = 'workbench.action.terminal.chat.feedbackUnhelpful', + FeedbackReportIssue = 'workbench.action.terminal.chat.feedbackReportIssue', +} + export const MENU_TERMINAL_CHAT_INPUT = MenuId.for('terminalChatInput'); export const MENU_TERMINAL_CHAT_WIDGET = MenuId.for('terminalChatWidget'); export const MENU_TERMINAL_CHAT_WIDGET_STATUS = MenuId.for('terminalChatWidget.status'); From 430db3ac9e053dada843e609422050da6938cf45 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 08:49:30 -0800 Subject: [PATCH 0033/1175] Move term chat actions into own file --- .../browser/terminal.chat.contribution.ts | 176 +---------------- .../chat/browser/terminalChatActions.ts | 182 ++++++++++++++++++ 2 files changed, 183 insertions(+), 175 deletions(-) create mode 100644 src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index f8a1dd4b111..a3a7b55757a 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -3,183 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Codicon } from 'vs/base/common/codicons'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { localize2 } from 'vs/nls'; -import { MenuId } from 'vs/platform/actions/common/actions'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; -import { isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; -import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, TerminalChatCommandId } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; registerTerminalContribution(TerminalChatController.ID, TerminalChatController, false); -registerActiveXtermAction({ - id: TerminalChatCommandId.Focus, - title: localize2('workbench.action.terminal.focusChat', 'Focus Chat'), - keybinding: { - primary: KeyMod.CtrlCmd | KeyCode.KeyI, - when: ContextKeyExpr.and(TerminalContextKeys.chatFocused.negate(), TerminalContextKeys.focusInAny), - weight: KeybindingWeight.WorkbenchContrib - }, - f1: true, - precondition: ContextKeyExpr.and( - ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - ), - run: (_xterm, _accessor, activeInstance) => { - if (isDetachedTerminalInstance(activeInstance)) { - return; - } - const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); - contr?.chatWidget?.reveal(); - } -}); - -registerActiveXtermAction({ - id: TerminalChatCommandId.Hide, - title: localize2('workbench.action.terminal.closeChat', 'Close Chat'), - keybinding: { - primary: KeyCode.Escape, - secondary: [KeyMod.Shift | KeyCode.Escape], - when: ContextKeyExpr.and(TerminalContextKeys.chatFocused, TerminalContextKeys.chatVisible), - weight: KeybindingWeight.WorkbenchContrib - }, - icon: Codicon.close, - menu: { - id: MENU_TERMINAL_CHAT_WIDGET, - group: 'main', - order: 2 - }, - f1: true, - precondition: ContextKeyExpr.and( - ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - ), - run: (_xterm, _accessor, activeInstance) => { - if (isDetachedTerminalInstance(activeInstance)) { - return; - } - const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); - contr?.chatWidget?.hide(); - } -}); - -registerActiveXtermAction({ - id: TerminalChatCommandId.MakeRequest, - title: localize2('workbench.action.terminal.submitChat', 'Make Chat Request'), - precondition: ContextKeyExpr.and( - ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - // TerminalContextKeys.chatInputHasText - ), - icon: Codicon.send, - keybinding: { - when: TerminalContextKeys.chatRequestActive.negate(), - // TODO: - // when: CTX_INLINE_CHAT_FOCUSED, - weight: KeybindingWeight.EditorCore + 7, - primary: KeyCode.Enter - }, - menu: { - id: MENU_TERMINAL_CHAT_INPUT, - group: 'main', - order: 1, - // when: TerminalContextKeys.chatSessionInProgress.negate(), - // TODO: - // when: CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST.isEqualTo(false) - }, - run: (_xterm, _accessor, activeInstance) => { - if (isDetachedTerminalInstance(activeInstance)) { - return; - } - const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); - contr?.acceptInput(); - } -}); - -registerActiveXtermAction({ - id: TerminalChatCommandId.Cancel, - title: localize2('workbench.action.terminal.cancelChat', 'Cancel Chat'), - precondition: ContextKeyExpr.and( - ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalContextKeys.chatRequestActive, - ), - icon: Codicon.debugStop, - menu: { - id: MenuId.ChatExecute, - group: 'navigation', - }, - run: (_xterm, _accessor, activeInstance) => { - if (isDetachedTerminalInstance(activeInstance)) { - return; - } - const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); - contr?.chatWidget?.cancel(); - } -}); - -registerActiveXtermAction({ - id: TerminalChatCommandId.FeedbackHelpful, - title: localize2('feedbackHelpful', 'Helpful'), - precondition: ContextKeyExpr.and( - ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalContextKeys.chatRequestActive, - ), - icon: Codicon.thumbsup, - menu: { - id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, - group: 'inline', - order: 1, - // TODO: Fill in ctx - // when: CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.notEqualsTo(undefined), - }, - run: (_xterm, _accessor, activeInstance) => { - // TODO: Impl - } -}); - -registerActiveXtermAction({ - id: TerminalChatCommandId.FeedbackUnhelpful, - title: localize2('feedbackUnhelpful', 'Helpful'), - precondition: ContextKeyExpr.and( - ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalContextKeys.chatRequestActive, - ), - icon: Codicon.thumbsup, - menu: { - id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, - group: 'inline', - order: 2, - // TODO: Fill in ctx - // when: CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.notEqualsTo(undefined), - }, - run: (_xterm, _accessor, activeInstance) => { - // TODO: Impl - } -}); - -registerActiveXtermAction({ - id: TerminalChatCommandId.FeedbackReportIssue, - title: localize2('reportIssue', 'Report Issue'), - precondition: ContextKeyExpr.and( - ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalContextKeys.chatRequestActive, - ), - icon: Codicon.thumbsup, - menu: { - id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, - group: 'inline', - order: 3, - // TODO: Fill in ctx - // when: CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.notEqualsTo(undefined), - }, - run: (_xterm, _accessor, activeInstance) => { - // TODO: Impl - } -}); +import 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions'; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts new file mode 100644 index 00000000000..161b8515478 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -0,0 +1,182 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { localize2 } from 'vs/nls'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, TerminalChatCommandId } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; +import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; + +registerActiveXtermAction({ + id: TerminalChatCommandId.Focus, + title: localize2('workbench.action.terminal.focusChat', 'Focus Chat'), + keybinding: { + primary: KeyMod.CtrlCmd | KeyCode.KeyI, + when: ContextKeyExpr.and(TerminalContextKeys.chatFocused.negate(), TerminalContextKeys.focusInAny), + weight: KeybindingWeight.WorkbenchContrib + }, + f1: true, + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + ), + run: (_xterm, _accessor, activeInstance) => { + if (isDetachedTerminalInstance(activeInstance)) { + return; + } + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + contr?.chatWidget?.reveal(); + } +}); + +registerActiveXtermAction({ + id: TerminalChatCommandId.Hide, + title: localize2('workbench.action.terminal.closeChat', 'Close Chat'), + keybinding: { + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape], + when: ContextKeyExpr.and(TerminalContextKeys.chatFocused, TerminalContextKeys.chatVisible), + weight: KeybindingWeight.WorkbenchContrib + }, + icon: Codicon.close, + menu: { + id: MENU_TERMINAL_CHAT_WIDGET, + group: 'main', + order: 2 + }, + f1: true, + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + ), + run: (_xterm, _accessor, activeInstance) => { + if (isDetachedTerminalInstance(activeInstance)) { + return; + } + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + contr?.chatWidget?.hide(); + } +}); + +registerActiveXtermAction({ + id: TerminalChatCommandId.MakeRequest, + title: localize2('workbench.action.terminal.submitChat', 'Make Chat Request'), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + // TerminalContextKeys.chatInputHasText + ), + icon: Codicon.send, + keybinding: { + when: TerminalContextKeys.chatRequestActive.negate(), + // TODO: + // when: CTX_INLINE_CHAT_FOCUSED, + weight: KeybindingWeight.EditorCore + 7, + primary: KeyCode.Enter + }, + menu: { + id: MENU_TERMINAL_CHAT_INPUT, + group: 'main', + order: 1, + // when: TerminalContextKeys.chatSessionInProgress.negate(), + // TODO: + // when: CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST.isEqualTo(false) + }, + run: (_xterm, _accessor, activeInstance) => { + if (isDetachedTerminalInstance(activeInstance)) { + return; + } + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + contr?.acceptInput(); + } +}); + +registerActiveXtermAction({ + id: TerminalChatCommandId.Cancel, + title: localize2('workbench.action.terminal.cancelChat', 'Cancel Chat'), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + TerminalContextKeys.chatRequestActive, + ), + icon: Codicon.debugStop, + menu: { + id: MenuId.ChatExecute, + group: 'navigation', + }, + run: (_xterm, _accessor, activeInstance) => { + if (isDetachedTerminalInstance(activeInstance)) { + return; + } + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + contr?.chatWidget?.cancel(); + } +}); + +registerActiveXtermAction({ + id: TerminalChatCommandId.FeedbackHelpful, + title: localize2('feedbackHelpful', 'Helpful'), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + TerminalContextKeys.chatRequestActive, + ), + icon: Codicon.thumbsup, + menu: { + id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, + group: 'inline', + order: 1, + // TODO: Fill in ctx + // when: CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.notEqualsTo(undefined), + }, + run: (_xterm, _accessor, activeInstance) => { + // TODO: Impl + } +}); + +registerActiveXtermAction({ + id: TerminalChatCommandId.FeedbackUnhelpful, + title: localize2('feedbackUnhelpful', 'Helpful'), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + TerminalContextKeys.chatRequestActive, + ), + icon: Codicon.thumbsup, + menu: { + id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, + group: 'inline', + order: 2, + // TODO: Fill in ctx + // when: CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.notEqualsTo(undefined), + }, + run: (_xterm, _accessor, activeInstance) => { + // TODO: Impl + } +}); + +registerActiveXtermAction({ + id: TerminalChatCommandId.FeedbackReportIssue, + title: localize2('reportIssue', 'Report Issue'), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + TerminalContextKeys.chatRequestActive, + ), + icon: Codicon.thumbsup, + menu: { + id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, + group: 'inline', + order: 3, + // TODO: Fill in ctx + // when: CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.notEqualsTo(undefined), + }, + run: (_xterm, _accessor, activeInstance) => { + // TODO: Impl + } +}); From ab50eb72b355613d6f028289e41fccb4fcd9dc91 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 09:01:19 -0800 Subject: [PATCH 0034/1175] Fix compile error, add todo for layer breaker --- .../contrib/chat/electron-sandbox/actions/voiceChatActions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index b41804eb8cc..74ec952ba1c 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -54,6 +54,7 @@ import { IVoiceChatService } from 'vs/workbench/contrib/chat/common/voiceChat'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ThemeIcon } from 'vs/base/common/themables'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { MENU_TERMINAL_CHAT_INPUT } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when getting ready for receiving voice input from the microphone for voice chat.") }); const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when voice recording from microphone is in progress for voice chat.") }); @@ -528,7 +529,8 @@ export class StartVoiceChatAction extends Action2 { order: -1 }, { - id: MenuId.TerminalChat, + // TODO: Fix layer breaker, chat can't depend on terminalContrib + id: MENU_TERMINAL_CHAT_INPUT, when: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS.negate()), group: 'main', order: -1 From 283b211fc8544baaa577021b2d507fb966ac1975 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 11:09:21 -0600 Subject: [PATCH 0035/1175] alert single code block --- .../chat/browser/terminalChatController.ts | 13 ++++++++----- .../chat/browser/terminalChatWidget.ts | 4 +--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 8308105d9f5..c8f3ec69958 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -19,6 +19,7 @@ import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; import { generateUuid } from 'vs/base/common/uuid'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; +import { marked } from 'vs/base/common/marked/marked'; export class TerminalChatController extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.Chat'; @@ -133,12 +134,14 @@ export class TerminalChatController extends Disposable implements ITerminalContr variables: resolvedVariables, variables2: { message: this._chatWidget?.rawValue?.input() || '', variables: [] } }; - // TODO: use token - const agentResult = await this._chatAgentService.invokeAgent('terminal', requestProps, progressCallback, [], CancellationToken.None); - console.log(agentResult); - console.log(message); - alert(message); this._chatWidget?.rawValue?.setValue(); + + // TODO: use token + await this._chatAgentService.invokeAgent('terminal', requestProps, progressCallback, [], CancellationToken.None); + const codeBlock = marked.lexer(message).filter(token => token.type === 'code')?.[0]?.raw.replaceAll('```', ''); + if (codeBlock) { + alert(codeBlock); + } // TODO: accessibility announcement, help dialog } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 330be0b35a5..4a5dbb55994 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -11,7 +11,6 @@ import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; @@ -32,8 +31,7 @@ export class TerminalChatWidget extends Disposable { private readonly _container: HTMLElement, private readonly _instance: ITerminalInstance, @IInstantiationService instantiationService: IInstantiationService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IChatAgentService private readonly _chatAgentService: IChatAgentService + @IContextKeyService private readonly _contextKeyService: IContextKeyService ) { super(); const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._container)); From 582b0bea5a9e5f9090573fa3a2b4dd8587030100 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 11:29:51 -0600 Subject: [PATCH 0036/1175] add response editor --- .../terminal/browser/media/terminal.css | 4 ++ .../chat/browser/terminalChatController.ts | 6 +-- .../chat/browser/terminalChatWidget.ts | 42 ++++++++++++++++++- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 8fcb3c71172..9053a746f89 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -575,3 +575,7 @@ .monaco-workbench .terminal-inline-chat.hide { visibility: hidden; } + +.monaco-workbench .terminal-inline-chat-response.hide { + visibility: hidden; +} diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index c8f3ec69958..05383e3e7f8 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -34,7 +34,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr static activeChatWidget?: TerminalChatController; private _chatWidget: Lazy | undefined; private _lastLayoutDimensions: IDimension | undefined; - + private _requestId: number = 0; get chatWidget(): TerminalChatWidget | undefined { return this._chatWidget?.value; } // private _sessionCtor: CancelablePromise | undefined; @@ -140,9 +140,9 @@ export class TerminalChatController extends Disposable implements ITerminalContr await this._chatAgentService.invokeAgent('terminal', requestProps, progressCallback, [], CancellationToken.None); const codeBlock = marked.lexer(message).filter(token => token.type === 'code')?.[0]?.raw.replaceAll('```', ''); if (codeBlock) { - alert(codeBlock); + // TODO: check the SR experience + this._chatWidget?.rawValue?.renderResponse(codeBlock, this._requestId++); } - // TODO: accessibility announcement, help dialog } reveal(): void { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 4a5dbb55994..88f17fe5c58 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -5,12 +5,16 @@ import { Dimension, IFocusTracker, trackFocus } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/terminalChatWidget'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { ITextModel } from 'vs/editor/common/model'; +import { IModelService } from 'vs/editor/common/services/model'; import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; @@ -23,7 +27,8 @@ export class TerminalChatWidget extends Disposable { private _chatWidgetVisible: IContextKey; private readonly _inlineChatWidget: InlineChatWidget; - + private _responseWidget: CodeEditorWidget | undefined; + private _responseContainer: HTMLElement | undefined; private readonly _focusTracker: IFocusTracker; @@ -31,7 +36,9 @@ export class TerminalChatWidget extends Disposable { private readonly _container: HTMLElement, private readonly _instance: ITerminalInstance, @IInstantiationService instantiationService: IInstantiationService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService, + @IModelService private readonly _modelService: IModelService ) { super(); const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._container)); @@ -70,6 +77,33 @@ export class TerminalChatWidget extends Disposable { this._focusTracker = this._register(trackFocus(this._widgetContainer)); } + renderResponse(codeBlock: string, requestId: number): void { + this._chatAccessibilityService.acceptResponse(codeBlock, requestId); + if (!this._responseWidget) { + this._responseContainer = document.createElement('div'); + this._responseContainer.classList.add('terminal-inline-chat-response'); + this._responseWidget = this._scopedInstantiationService.createInstance(CodeEditorWidget, this._responseContainer, {}, { isSimpleWidget: true }); + this._getTextModel(URI.from({ path: `terminal-inline-chat-${this._instance.instanceId}`, scheme: 'terminal-inline-chat', fragment: codeBlock })).then((model) => { + if (!model || !this._responseWidget) { + return; + } + this._responseWidget.setModel(model); + this._responseWidget.layout(new Dimension(400, 150)); + this._widgetContainer.prepend(this._responseContainer!); + }); + } else { + this._responseWidget.setValue(codeBlock); + } + this._responseContainer?.classList.remove('hide'); + } + + private async _getTextModel(resource: URI): Promise { + const existing = this._modelService.getModel(resource); + if (existing && !existing.isDisposed()) { + return existing; + } + return this._modelService.createModel(resource.fragment, null, resource, false); + } reveal(): void { this._inlineChatWidget.layout(new Dimension(400, 150)); @@ -79,6 +113,7 @@ export class TerminalChatWidget extends Disposable { this._inlineChatWidget.focus(); } hide(): void { + this._responseContainer?.classList.add('hide'); this._widgetContainer.classList.add('hide'); this._chatWidgetFocused.set(false); this._chatWidgetVisible.set(false); @@ -92,6 +127,9 @@ export class TerminalChatWidget extends Disposable { } setValue(value?: string) { this._inlineChatWidget.value = value ?? ''; + if (!value) { + this._responseContainer?.classList.add('hide'); + } } // async acceptInput(): Promise { // // this._widget?.acceptInput(); From c4519ce005d7e3f1d52a280d022b1cab5df4aa00 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 11:44:13 -0600 Subject: [PATCH 0037/1175] wip render message when no code block comes through --- .../terminal/browser/media/terminal.css | 4 +++ .../chat/browser/terminalChatController.ts | 5 +++- .../chat/browser/terminalChatWidget.ts | 26 ++++++++++++------- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 9053a746f89..ab84f0258b1 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -579,3 +579,7 @@ .monaco-workbench .terminal-inline-chat-response.hide { visibility: hidden; } + +.monaco-workbench .terminal-inline-chat-response.message { + width: 400px !important; +} diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 05383e3e7f8..c4755a08c47 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -139,9 +139,12 @@ export class TerminalChatController extends Disposable implements ITerminalContr // TODO: use token await this._chatAgentService.invokeAgent('terminal', requestProps, progressCallback, [], CancellationToken.None); const codeBlock = marked.lexer(message).filter(token => token.type === 'code')?.[0]?.raw.replaceAll('```', ''); + this._requestId++; if (codeBlock) { // TODO: check the SR experience - this._chatWidget?.rawValue?.renderResponse(codeBlock, this._requestId++); + this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._requestId); + } else { + this._chatWidget?.rawValue?.renderMessage(message, this._requestId); } } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 88f17fe5c58..476c808dce1 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -28,7 +28,7 @@ export class TerminalChatWidget extends Disposable { private readonly _inlineChatWidget: InlineChatWidget; private _responseWidget: CodeEditorWidget | undefined; - private _responseContainer: HTMLElement | undefined; + private _responseElement: HTMLElement; private readonly _focusTracker: IFocusTracker; @@ -49,6 +49,10 @@ export class TerminalChatWidget extends Disposable { this._widgetContainer.classList.add('terminal-inline-chat'); this._container.appendChild(this._widgetContainer); + this._responseElement = document.createElement('div'); + this._responseElement.classList.add('terminal-inline-chat-response'); + this._widgetContainer.prepend(this._responseElement); + // The inline chat widget requires a parent editor that it bases the diff view on, since the // terminal doesn't use that feature we can just pass in an unattached editor instance. const fakeParentEditorElement = document.createElement('div'); @@ -77,24 +81,28 @@ export class TerminalChatWidget extends Disposable { this._focusTracker = this._register(trackFocus(this._widgetContainer)); } - renderResponse(codeBlock: string, requestId: number): void { + renderTerminalCommand(codeBlock: string, requestId: number): void { + this._responseElement.classList.remove('message', 'hide'); this._chatAccessibilityService.acceptResponse(codeBlock, requestId); if (!this._responseWidget) { - this._responseContainer = document.createElement('div'); - this._responseContainer.classList.add('terminal-inline-chat-response'); - this._responseWidget = this._scopedInstantiationService.createInstance(CodeEditorWidget, this._responseContainer, {}, { isSimpleWidget: true }); + this._responseWidget = this._scopedInstantiationService.createInstance(CodeEditorWidget, this._responseElement, {}, { isSimpleWidget: true }); this._getTextModel(URI.from({ path: `terminal-inline-chat-${this._instance.instanceId}`, scheme: 'terminal-inline-chat', fragment: codeBlock })).then((model) => { if (!model || !this._responseWidget) { return; } this._responseWidget.setModel(model); this._responseWidget.layout(new Dimension(400, 150)); - this._widgetContainer.prepend(this._responseContainer!); }); } else { this._responseWidget.setValue(codeBlock); } - this._responseContainer?.classList.remove('hide'); + } + + renderMessage(message: string, requestId: number): void { + this._responseElement?.classList.remove('hide'); + this._responseElement.classList.add('message'); + this._chatAccessibilityService.acceptResponse(message, requestId); + this._responseElement.textContent = message; } private async _getTextModel(resource: URI): Promise { @@ -113,7 +121,7 @@ export class TerminalChatWidget extends Disposable { this._inlineChatWidget.focus(); } hide(): void { - this._responseContainer?.classList.add('hide'); + this._responseElement?.classList.add('hide'); this._widgetContainer.classList.add('hide'); this._chatWidgetFocused.set(false); this._chatWidgetVisible.set(false); @@ -128,7 +136,7 @@ export class TerminalChatWidget extends Disposable { setValue(value?: string) { this._inlineChatWidget.value = value ?? ''; if (!value) { - this._responseContainer?.classList.add('hide'); + this._responseElement?.classList.add('hide'); } } // async acceptInput(): Promise { From c9c98f010a12da31afa70b7b60b280260f911bac Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 11:56:08 -0600 Subject: [PATCH 0038/1175] get chat cancellation action to show up --- .../chat/browser/terminalChatActions.ts | 7 ++--- .../chat/browser/terminalChatController.ts | 29 +++++++++++++++---- .../chat/browser/terminalChatWidget.ts | 2 +- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 161b8515478..b9544ce5feb 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -6,7 +6,6 @@ import { Codicon } from 'vs/base/common/codicons'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { localize2 } from 'vs/nls'; -import { MenuId } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; @@ -73,7 +72,7 @@ registerActiveXtermAction({ precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - // TerminalContextKeys.chatInputHasText + TerminalContextKeys.chatRequestActive.negate() ), icon: Codicon.send, keybinding: { @@ -109,8 +108,8 @@ registerActiveXtermAction({ ), icon: Codicon.debugStop, menu: { - id: MenuId.ChatExecute, - group: 'navigation', + id: MENU_TERMINAL_CHAT_INPUT, + group: 'main', }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index c4755a08c47..5465c3abed3 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -17,9 +17,12 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; import { generateUuid } from 'vs/base/common/uuid'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; import { marked } from 'vs/base/common/marked/marked'; +import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; export class TerminalChatController extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.Chat'; @@ -39,7 +42,10 @@ export class TerminalChatController extends Disposable implements ITerminalContr // private _sessionCtor: CancelablePromise | undefined; // private _activeSession?: Session; - // private readonly _ctxHasActiveRequest: IContextKey; + private readonly _ctxHasActiveRequest!: IContextKey; + + private _cancellationTokenSource!: CancellationTokenSource; + // private _isVisible: boolean = false; // private _strategy: EditStrategy | undefined; @@ -56,7 +62,8 @@ export class TerminalChatController extends Disposable implements ITerminalContr @ITerminalService private readonly _terminalService: ITerminalService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IChatAgentService private readonly _chatAgentService: IChatAgentService, - // @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService // @IInstantiationService private readonly _instantiationService: IInstantiationService, // @ICommandService private readonly _commandService: ICommandService, // @IInlineChatSavingService private readonly _inlineChatSavingService: IInlineChatSavingService @@ -65,7 +72,8 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { return; } - // this._ctxHasActiveRequest = TerminalContextKeys.chatRequestActive.bindTo(this._contextKeyService); + this._ctxHasActiveRequest = TerminalContextKeys.chatRequestActive.bindTo(this._contextKeyService); + this._cancellationTokenSource = new CancellationTokenSource(); // this._ctxLastResponseType = CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.bindTo(this._contextKeyService); } @@ -105,8 +113,15 @@ export class TerminalChatController extends Disposable implements ITerminalContr }); } + cancel(): void { + this._cancellationTokenSource.cancel(); + } + async acceptInput(): Promise { let message = ''; + this._chatAccessibilityService.acceptRequest(); + this._ctxHasActiveRequest.set(true); + const cancellationToken = this._cancellationTokenSource.token; const progressCallback = (progress: IChatProgress) => { // if (token.isCancellationRequested) { // return; @@ -137,15 +152,19 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.setValue(); // TODO: use token - await this._chatAgentService.invokeAgent('terminal', requestProps, progressCallback, [], CancellationToken.None); + await this._chatAgentService.invokeAgent('terminal', requestProps, progressCallback, [], cancellationToken); const codeBlock = marked.lexer(message).filter(token => token.type === 'code')?.[0]?.raw.replaceAll('```', ''); this._requestId++; + if (cancellationToken.isCancellationRequested) { + return; + } if (codeBlock) { // TODO: check the SR experience this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._requestId); } else { this._chatWidget?.rawValue?.renderMessage(message, this._requestId); } + this._ctxHasActiveRequest.set(false); } reveal(): void { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 476c808dce1..4c84b3656bc 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -31,7 +31,6 @@ export class TerminalChatWidget extends Disposable { private _responseElement: HTMLElement; private readonly _focusTracker: IFocusTracker; - constructor( private readonly _container: HTMLElement, private readonly _instance: ITerminalInstance, @@ -129,6 +128,7 @@ export class TerminalChatWidget extends Disposable { } cancel(): void { // TODO: Impl + this._inlineChatWidget.value = ''; } input(): string { return this._inlineChatWidget.value; From cf634dfa386325d041f05d86cf24a2654defc04a Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 11:57:24 -0600 Subject: [PATCH 0039/1175] only show if active --- .../terminalContrib/chat/browser/terminalChatActions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index b9544ce5feb..57e3cee5de0 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -86,7 +86,7 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_INPUT, group: 'main', order: 1, - // when: TerminalContextKeys.chatSessionInProgress.negate(), + when: TerminalContextKeys.chatRequestActive.negate(), // TODO: // when: CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST.isEqualTo(false) }, @@ -110,6 +110,7 @@ registerActiveXtermAction({ menu: { id: MENU_TERMINAL_CHAT_INPUT, group: 'main', + when: TerminalContextKeys.chatRequestActive, }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { From 7d80e6d9396dc3bf91d8880cd65758a31e3cef81 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 12:02:49 -0600 Subject: [PATCH 0040/1175] clean up --- .../chat/browser/terminalChatActions.ts | 2 -- .../chat/browser/terminalChatController.ts | 15 +++------------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 57e3cee5de0..bb1a012026a 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -87,8 +87,6 @@ registerActiveXtermAction({ group: 'main', order: 1, when: TerminalContextKeys.chatRequestActive.negate(), - // TODO: - // when: CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST.isEqualTo(false) }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 5465c3abed3..a9babf3a5de 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -123,21 +123,13 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._ctxHasActiveRequest.set(true); const cancellationToken = this._cancellationTokenSource.token; const progressCallback = (progress: IChatProgress) => { - // if (token.isCancellationRequested) { - // return; - // } - - - // gotProgress = true; + if (cancellationToken.isCancellationRequested) { + return; + } if (progress.kind === 'content' || progress.kind === 'markdownContent') { - // this.trace('sendRequest', `Provider returned progress for session ${model.sessionId}, ${typeof progress.content === 'string' ? progress.content.length : progress.content.value.length} chars`); message += progress.content; - } else { - // this.trace('sendRequest', `Provider returned progress: ${JSON.stringify(progress)}`); } - - // model.acceptResponseProgress(request, progress); }; const resolvedVariables: Record = {}; @@ -151,7 +143,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr }; this._chatWidget?.rawValue?.setValue(); - // TODO: use token await this._chatAgentService.invokeAgent('terminal', requestProps, progressCallback, [], cancellationToken); const codeBlock = marked.lexer(message).filter(token => token.type === 'code')?.[0]?.raw.replaceAll('```', ''); this._requestId++; From 44d4700aae178618cbb9aa6f888e0b81db5e66f2 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 12:03:37 -0600 Subject: [PATCH 0041/1175] remove things --- .../chat/browser/terminalChatController.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index a9babf3a5de..22810165660 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -40,20 +40,10 @@ export class TerminalChatController extends Disposable implements ITerminalContr private _requestId: number = 0; get chatWidget(): TerminalChatWidget | undefined { return this._chatWidget?.value; } - // private _sessionCtor: CancelablePromise | undefined; - // private _activeSession?: Session; private readonly _ctxHasActiveRequest!: IContextKey; private _cancellationTokenSource!: CancellationTokenSource; - // private _isVisible: boolean = false; - // private _strategy: EditStrategy | undefined; - - // private _inlineChatListener: IDisposable | undefined; - // private _toolbar: MenuWorkbenchToolBar | undefined; - // private readonly _ctxLastResponseType: IContextKey; - // private _widgetDisposableStore: DisposableStore = this._register(new DisposableStore()); - constructor( private readonly _instance: ITerminalInstance, processManager: ITerminalProcessManager, @@ -64,9 +54,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr @IChatAgentService private readonly _chatAgentService: IChatAgentService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService - // @IInstantiationService private readonly _instantiationService: IInstantiationService, - // @ICommandService private readonly _commandService: ICommandService, - // @IInlineChatSavingService private readonly _inlineChatSavingService: IInlineChatSavingService ) { super(); if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { @@ -74,7 +61,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr } this._ctxHasActiveRequest = TerminalContextKeys.chatRequestActive.bindTo(this._contextKeyService); this._cancellationTokenSource = new CancellationTokenSource(); - // this._ctxLastResponseType = CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.bindTo(this._contextKeyService); } layout(_xterm: IXtermTerminal & { raw: RawXtermTerminal }, dimension: IDimension): void { From c4af34eae27344c9bf3030f7c84d2f2e0cf69d8d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 12:04:44 -0600 Subject: [PATCH 0042/1175] more clean up --- .../terminalContrib/chat/browser/terminalChatController.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 22810165660..85d7ba44296 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -108,6 +108,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatAccessibilityService.acceptRequest(); this._ctxHasActiveRequest.set(true); const cancellationToken = this._cancellationTokenSource.token; + const agentId = 'terminal'; const progressCallback = (progress: IChatProgress) => { if (cancellationToken.isCancellationRequested) { return; @@ -122,14 +123,14 @@ export class TerminalChatController extends Disposable implements ITerminalContr const requestProps: IChatAgentRequest = { sessionId: generateUuid(), requestId: generateUuid(), - agentId: 'terminal', + agentId, message: this._chatWidget?.rawValue?.input() || '', variables: resolvedVariables, variables2: { message: this._chatWidget?.rawValue?.input() || '', variables: [] } }; this._chatWidget?.rawValue?.setValue(); - await this._chatAgentService.invokeAgent('terminal', requestProps, progressCallback, [], cancellationToken); + await this._chatAgentService.invokeAgent(agentId, requestProps, progressCallback, [], cancellationToken); const codeBlock = marked.lexer(message).filter(token => token.type === 'code')?.[0]?.raw.replaceAll('```', ''); this._requestId++; if (cancellationToken.isCancellationRequested) { From a9cfba287544762341fa7a2e84fce90f568f4cbb Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 12:11:41 -0600 Subject: [PATCH 0043/1175] catch error --- .../chat/browser/terminalChatController.ts | 11 ++++++++++- .../chat/browser/terminalChatWidget.ts | 18 ++++-------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 85d7ba44296..432d5fd0cbf 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -117,6 +117,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (progress.kind === 'content' || progress.kind === 'markdownContent') { message += progress.content; } + this._chatWidget?.rawValue?.updateProgress(progress); }; const resolvedVariables: Record = {}; @@ -130,7 +131,14 @@ export class TerminalChatController extends Disposable implements ITerminalContr }; this._chatWidget?.rawValue?.setValue(); - await this._chatAgentService.invokeAgent(agentId, requestProps, progressCallback, [], cancellationToken); + try { + await this._chatAgentService.invokeAgent(agentId, requestProps, progressCallback, [], cancellationToken); + } catch (e) { + // Provider is not ready + this._ctxHasActiveRequest.set(false); + this._chatWidget?.rawValue?.updateProgress(); + return; + } const codeBlock = marked.lexer(message).filter(token => token.type === 'code')?.[0]?.raw.replaceAll('```', ''); this._requestId++; if (cancellationToken.isCancellationRequested) { @@ -143,6 +151,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.renderMessage(message, this._requestId); } this._ctxHasActiveRequest.set(false); + this._chatWidget?.rawValue?.updateProgress(); } reveal(): void { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 4c84b3656bc..985fc071cb2 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -15,6 +15,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; +import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; @@ -139,20 +140,9 @@ export class TerminalChatWidget extends Disposable { this._responseElement?.classList.add('hide'); } } - // async acceptInput(): Promise { - // // this._widget?.acceptInput(); - // // this._chatModel ??= this._chatService.startSession('terminal', CancellationToken.None); - - // // if (!this._model) { - // // throw new Error('Could not start chat session'); - // // } - // // this._chatService?.sendRequest(this._chatModel?.sessionId!, this._inlineChatWidget.value); - // // this._activeSession = new Session(EditMode.Live, , this._instance); - // // const initVariableData: IChatRequestVariableData = { message: getPromptText(parsedRequest.parts), variables: {} }; - // // request = model.addRequest(parsedRequest, initVariableData, agent, agentSlashCommandPart?.command); - // // const variableData = await this.chatVariablesService.resolveVariables(parsedRequest, model, token); - // this._inlineChatWidget.value = ''; - // } + updateProgress(progress?: IChatProgress): void { + this._inlineChatWidget.updateProgress(progress?.kind === 'content' || progress?.kind === 'markdownContent'); + } layout(width: number): void { // this._widget?.layout(100, width < 300 ? 300 : width); } From 1617ebe843cb56d7ff8fdd2c17c3fbaa2de9f7ec Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 12:24:10 -0600 Subject: [PATCH 0044/1175] ensure terminal agent is registered --- .../contrib/terminal/common/terminalContextKey.ts | 4 ++++ .../chat/browser/terminalChatActions.ts | 4 +++- .../chat/browser/terminalChatController.ts | 11 +++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index 78fd7df6712..7f777f94f62 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -43,6 +43,7 @@ export const enum TerminalContextKeyStrings { ChatVisible = 'terminalChatVisible', ChatActiveRequest = 'terminalChatActiveRequest', ChatInputHasText = 'terminalChatInputHasText', + ChatAgentRegistered = 'terminalChatAgentRegistered', } export namespace TerminalContextKeys { @@ -175,4 +176,7 @@ export namespace TerminalContextKeys { /** Whether the chat input has text */ export const chatInputHasText = new RawContextKey(TerminalContextKeyStrings.ChatInputHasText, false, localize('chatInputHasTextContextKey', "Whether the chat input has text.")); + + /** Whether the terminal chat agent has been registered */ + export const chatAgentRegistered = new RawContextKey(TerminalContextKeyStrings.ChatAgentRegistered, false, localize('chatAgentRegisteredContextKey', "Whether the terminal chat agent has been registered.")); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index bb1a012026a..dcf2aec0617 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -72,7 +72,8 @@ registerActiveXtermAction({ precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - TerminalContextKeys.chatRequestActive.negate() + TerminalContextKeys.chatRequestActive.negate(), + TerminalContextKeys.chatAgentRegistered ), icon: Codicon.send, keybinding: { @@ -103,6 +104,7 @@ registerActiveXtermAction({ precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), TerminalContextKeys.chatRequestActive, + TerminalContextKeys.chatAgentRegistered ), icon: Codicon.debugStop, menu: { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 432d5fd0cbf..b3b9c002aad 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -41,6 +41,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr get chatWidget(): TerminalChatWidget | undefined { return this._chatWidget?.value; } private readonly _ctxHasActiveRequest!: IContextKey; + private readonly _ctxHasTerminalAgent!: IContextKey; private _cancellationTokenSource!: CancellationTokenSource; @@ -60,6 +61,16 @@ export class TerminalChatController extends Disposable implements ITerminalContr return; } this._ctxHasActiveRequest = TerminalContextKeys.chatRequestActive.bindTo(this._contextKeyService); + this._ctxHasTerminalAgent = TerminalContextKeys.chatAgentRegistered.bindTo(this._contextKeyService); + if (!this._chatAgentService.hasAgent('terminal')) { + this._register(this._chatAgentService.onDidChangeAgents(() => { + if (this._chatAgentService.getAgent('terminal')) { + this._ctxHasTerminalAgent.set(true); + } + })); + } else { + this._ctxHasTerminalAgent.set(true); + } this._cancellationTokenSource = new CancellationTokenSource(); } From d5527a86b26e1586b487dfaadc43001c32b4bd64 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 12:56:50 -0600 Subject: [PATCH 0045/1175] add view in chat action even though it doesn't work atm --- .../inlineChat/browser/inlineChatActions.ts | 17 +++++++++++++---- .../terminal/browser/media/terminal.css | 2 +- .../chat/browser/terminalChatActions.ts | 8 ++++---- .../chat/browser/terminalChatController.ts | 18 +++++++++++------- .../chat/browser/terminalChatWidget.ts | 11 +++++------ 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 85033c64166..635233002c4 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -30,6 +30,9 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +// TODO: fix eslint-disable-next-line local/code-import-patterns +import { MENU_TERMINAL_CHAT_INPUT } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; +import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; CommandsRegistry.registerCommandAlias('interactiveEditor.start', 'inlineChat.start'); CommandsRegistry.registerCommandAlias('interactive.acceptChanges', ACTION_ACCEPT_CHANGES); @@ -682,13 +685,19 @@ export class ViewInChatAction extends AbstractInlineChatAction { id: ACTION_VIEW_IN_CHAT, title: localize('viewInChat', 'View in Chat'), icon: Codicon.commentDiscussion, - precondition: CTX_INLINE_CHAT_VISIBLE, - menu: { + precondition: ContextKeyExpr.or(TerminalContextKeys.chatVisible, CTX_INLINE_CHAT_VISIBLE), + menu: [{ id: MENU_INLINE_CHAT_WIDGET_STATUS, - when: CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyMessages), + when: ContextKeyExpr.and(CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyMessages), CTX_INLINE_CHAT_VISIBLE), group: '0_main', order: 1 - } + }, + { + id: MENU_TERMINAL_CHAT_INPUT, + when: ContextKeyExpr.and(TerminalContextKeys.chatVisible, CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyMessages)), + group: 'inline', + order: 1 + }] }); } override runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController, _editor: ICodeEditor, ..._args: any[]): void { diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index ab84f0258b1..1aaf1516c5e 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -580,6 +580,6 @@ visibility: hidden; } -.monaco-workbench .terminal-inline-chat-response.message { +.monaco-workbench .terminal-inline-chat .chatMessageContent { width: 400px !important; } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index dcf2aec0617..60032254028 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -9,6 +9,7 @@ import { localize2 } from 'vs/nls'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; @@ -73,13 +74,12 @@ registerActiveXtermAction({ ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.chatRequestActive.negate(), - TerminalContextKeys.chatAgentRegistered + TerminalContextKeys.chatAgentRegistered, + CTX_INLINE_CHAT_EMPTY.negate() ), icon: Codicon.send, keybinding: { - when: TerminalContextKeys.chatRequestActive.negate(), - // TODO: - // when: CTX_INLINE_CHAT_FOCUSED, + when: ContextKeyExpr.and(CTX_INLINE_CHAT_FOCUSED, TerminalContextKeys.chatRequestActive.negate()), weight: KeybindingWeight.EditorCore + 7, primary: KeyCode.Enter }, diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index b3b9c002aad..9312e56e19b 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -23,6 +23,7 @@ import { marked } from 'vs/base/common/marked/marked'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; +import { CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; export class TerminalChatController extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.Chat'; @@ -37,11 +38,12 @@ export class TerminalChatController extends Disposable implements ITerminalContr static activeChatWidget?: TerminalChatController; private _chatWidget: Lazy | undefined; private _lastLayoutDimensions: IDimension | undefined; - private _requestId: number = 0; + private _accessibilityRequestId: number = 0; get chatWidget(): TerminalChatWidget | undefined { return this._chatWidget?.value; } private readonly _ctxHasActiveRequest!: IContextKey; private readonly _ctxHasTerminalAgent!: IContextKey; + private readonly _ctxLastResponseType!: IContextKey; private _cancellationTokenSource!: CancellationTokenSource; @@ -54,7 +56,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr @IInstantiationService private readonly _instantiationService: IInstantiationService, @IChatAgentService private readonly _chatAgentService: IChatAgentService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService + @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService, ) { super(); if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { @@ -62,6 +64,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr } this._ctxHasActiveRequest = TerminalContextKeys.chatRequestActive.bindTo(this._contextKeyService); this._ctxHasTerminalAgent = TerminalContextKeys.chatAgentRegistered.bindTo(this._contextKeyService); + this._ctxLastResponseType = CTX_INLINE_CHAT_RESPONSE_TYPES.bindTo(this._contextKeyService); if (!this._chatAgentService.hasAgent('terminal')) { this._register(this._chatAgentService.onDidChangeAgents(() => { if (this._chatAgentService.getAgent('terminal')) { @@ -131,10 +134,10 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.updateProgress(progress); }; const resolvedVariables: Record = {}; - + const requestId = generateUuid(); const requestProps: IChatAgentRequest = { sessionId: generateUuid(), - requestId: generateUuid(), + requestId, agentId, message: this._chatWidget?.rawValue?.input() || '', variables: resolvedVariables, @@ -151,15 +154,16 @@ export class TerminalChatController extends Disposable implements ITerminalContr return; } const codeBlock = marked.lexer(message).filter(token => token.type === 'code')?.[0]?.raw.replaceAll('```', ''); - this._requestId++; + this._accessibilityRequestId++; if (cancellationToken.isCancellationRequested) { return; } if (codeBlock) { // TODO: check the SR experience - this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._requestId); + this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._accessibilityRequestId); } else { - this._chatWidget?.rawValue?.renderMessage(message, this._requestId); + this._chatWidget?.rawValue?.renderMessage(message, this._accessibilityRequestId, requestId); + this._ctxLastResponseType.set(InlineChatResponseTypes.OnlyMessages); } this._ctxHasActiveRequest.set(false); this._chatWidget?.rawValue?.updateProgress(); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 985fc071cb2..761708aec35 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Dimension, IFocusTracker, trackFocus } from 'vs/base/browser/dom'; +import { MarkdownString } from 'vs/base/common/htmlContent'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/terminalChatWidget'; @@ -82,8 +83,8 @@ export class TerminalChatWidget extends Disposable { this._focusTracker = this._register(trackFocus(this._widgetContainer)); } renderTerminalCommand(codeBlock: string, requestId: number): void { - this._responseElement.classList.remove('message', 'hide'); this._chatAccessibilityService.acceptResponse(codeBlock, requestId); + this._responseElement.classList.remove('hide'); if (!this._responseWidget) { this._responseWidget = this._scopedInstantiationService.createInstance(CodeEditorWidget, this._responseElement, {}, { isSimpleWidget: true }); this._getTextModel(URI.from({ path: `terminal-inline-chat-${this._instance.instanceId}`, scheme: 'terminal-inline-chat', fragment: codeBlock })).then((model) => { @@ -98,11 +99,9 @@ export class TerminalChatWidget extends Disposable { } } - renderMessage(message: string, requestId: number): void { - this._responseElement?.classList.remove('hide'); - this._responseElement.classList.add('message'); - this._chatAccessibilityService.acceptResponse(message, requestId); - this._responseElement.textContent = message; + renderMessage(message: string, accessibilityRequestId: number, requestId: string): void { + this._inlineChatWidget.updateChatMessage({ message: new MarkdownString(message), requestId, providerId: 'terminal' }); + this._chatAccessibilityService.acceptResponse(message, accessibilityRequestId); } private async _getTextModel(resource: URI): Promise { From 9af101d1807a52d95714f19621c86425b342ba01 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 12:59:40 -0600 Subject: [PATCH 0046/1175] revert View in Chat action since it doesn't work --- .../inlineChat/browser/inlineChatActions.ts | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 635233002c4..85033c64166 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -30,9 +30,6 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; -// TODO: fix eslint-disable-next-line local/code-import-patterns -import { MENU_TERMINAL_CHAT_INPUT } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; -import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; CommandsRegistry.registerCommandAlias('interactiveEditor.start', 'inlineChat.start'); CommandsRegistry.registerCommandAlias('interactive.acceptChanges', ACTION_ACCEPT_CHANGES); @@ -685,19 +682,13 @@ export class ViewInChatAction extends AbstractInlineChatAction { id: ACTION_VIEW_IN_CHAT, title: localize('viewInChat', 'View in Chat'), icon: Codicon.commentDiscussion, - precondition: ContextKeyExpr.or(TerminalContextKeys.chatVisible, CTX_INLINE_CHAT_VISIBLE), - menu: [{ + precondition: CTX_INLINE_CHAT_VISIBLE, + menu: { id: MENU_INLINE_CHAT_WIDGET_STATUS, - when: ContextKeyExpr.and(CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyMessages), CTX_INLINE_CHAT_VISIBLE), + when: CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyMessages), group: '0_main', order: 1 - }, - { - id: MENU_TERMINAL_CHAT_INPUT, - when: ContextKeyExpr.and(TerminalContextKeys.chatVisible, CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyMessages)), - group: 'inline', - order: 1 - }] + } }); } override runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController, _editor: ICodeEditor, ..._args: any[]): void { From 7ad358bdb5ee2688a7df65fbd1381fb8ebffb0e7 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 13:14:43 -0600 Subject: [PATCH 0047/1175] add accept command action --- .../chat/browser/terminalChat.ts | 1 + .../chat/browser/terminalChatActions.ts | 36 ++++++++++++++++++- .../chat/browser/terminalChatController.ts | 5 +++ .../chat/browser/terminalChatWidget.ts | 13 +++++-- 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts index 431a0e9c715..882baf8c26b 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts @@ -13,6 +13,7 @@ export const enum TerminalChatCommandId { FeedbackHelpful = 'workbench.action.terminal.chat.feedbackHelpful', FeedbackUnhelpful = 'workbench.action.terminal.chat.feedbackUnhelpful', FeedbackReportIssue = 'workbench.action.terminal.chat.feedbackReportIssue', + AcceptCommand = 'workbench.action.terminal.chat.acceptCommand', } export const MENU_TERMINAL_CHAT_INPUT = MenuId.for('terminalChatInput'); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 60032254028..73ebf9631a9 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -9,7 +9,7 @@ import { localize2 } from 'vs/nls'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; -import { CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; @@ -67,6 +67,40 @@ registerActiveXtermAction({ } }); + + + +registerActiveXtermAction({ + id: TerminalChatCommandId.AcceptCommand, + title: localize2('workbench.action.terminal.acceptCommand', 'Accept Command'), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + TerminalContextKeys.chatRequestActive.negate(), + TerminalContextKeys.chatAgentRegistered, + CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyEdits) + ), + icon: Codicon.check, + keybinding: { + when: ContextKeyExpr.and(CTX_INLINE_CHAT_FOCUSED, TerminalContextKeys.chatRequestActive.negate()), + weight: KeybindingWeight.EditorCore + 7, + primary: KeyCode.Enter + }, + menu: { + id: MENU_TERMINAL_CHAT_WIDGET, + group: 'main', + order: 0, + when: CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyEdits), + }, + run: (_xterm, _accessor, activeInstance) => { + if (isDetachedTerminalInstance(activeInstance)) { + return; + } + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + contr?.acceptCommand(); + } +}); + registerActiveXtermAction({ id: TerminalChatCommandId.MakeRequest, title: localize2('workbench.action.terminal.submitChat', 'Make Chat Request'), diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 9312e56e19b..0a943b362fd 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -161,6 +161,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (codeBlock) { // TODO: check the SR experience this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._accessibilityRequestId); + this._ctxLastResponseType.set(InlineChatResponseTypes.OnlyEdits); } else { this._chatWidget?.rawValue?.renderMessage(message, this._accessibilityRequestId, requestId); this._ctxLastResponseType.set(InlineChatResponseTypes.OnlyMessages); @@ -169,6 +170,10 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.updateProgress(); } + acceptCommand(): void { + this._chatWidget?.rawValue?.acceptCommand(); + } + reveal(): void { this._chatWidget?.rawValue?.reveal(); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 761708aec35..0ccea8a61cd 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -91,8 +91,10 @@ export class TerminalChatWidget extends Disposable { if (!model || !this._responseWidget) { return; } + this._responseWidget.layout(new Dimension(400, 0)); this._responseWidget.setModel(model); - this._responseWidget.layout(new Dimension(400, 150)); + const height = this._responseWidget.getContentHeight(); + this._responseWidget.layout(new Dimension(400, height)); }); } else { this._responseWidget.setValue(codeBlock); @@ -113,7 +115,6 @@ export class TerminalChatWidget extends Disposable { } reveal(): void { this._inlineChatWidget.layout(new Dimension(400, 150)); - this._widgetContainer.classList.remove('hide'); this._chatWidgetFocused.set(true); this._chatWidgetVisible.set(true); @@ -139,6 +140,14 @@ export class TerminalChatWidget extends Disposable { this._responseElement?.classList.add('hide'); } } + acceptCommand(): void { + const value = this._responseWidget?.getValue(); + if (!value) { + return; + } + this._instance.sendText(value, false, true); + this.hide(); + } updateProgress(progress?: IChatProgress): void { this._inlineChatWidget.updateProgress(progress?.kind === 'content' || progress?.kind === 'markdownContent'); } From 9992d7da51e1d97858721263038a82e6c80149cd Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 11:18:10 -0800 Subject: [PATCH 0048/1175] Fix terminal voice menu ids --- .../chat/electron-sandbox/actions/voiceChatActions.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 74ec952ba1c..e41ae049b6e 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -54,7 +54,6 @@ import { IVoiceChatService } from 'vs/workbench/contrib/chat/common/voiceChat'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ThemeIcon } from 'vs/base/common/themables'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { MENU_TERMINAL_CHAT_INPUT } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when getting ready for receiving voice input from the microphone for voice chat.") }); const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when voice recording from microphone is in progress for voice chat.") }); @@ -529,8 +528,7 @@ export class StartVoiceChatAction extends Action2 { order: -1 }, { - // TODO: Fix layer breaker, chat can't depend on terminalContrib - id: MENU_TERMINAL_CHAT_INPUT, + id: MenuId.for('terminalChatInput'), when: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS.negate()), group: 'main', order: -1 @@ -580,6 +578,11 @@ export class InstallVoiceChatAction extends Action2 { when: HasSpeechProvider.negate(), group: 'main', order: -1 + }, { + id: MenuId.for('terminalChatInput'), + when: HasSpeechProvider.negate(), + group: 'main', + order: -1 }] }); } From 702535987ca16b9ba1ed8d86443a1d24a5400538 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 13:29:56 -0600 Subject: [PATCH 0049/1175] add accept command keybinding --- .../terminal/common/terminalContextKey.ts | 4 +++ .../chat/browser/terminalChatActions.ts | 8 ++--- .../chat/browser/terminalChatController.ts | 2 +- .../chat/browser/terminalChatWidget.ts | 35 ++++++++++++------- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index 7f777f94f62..8ea1469a4b1 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -44,6 +44,7 @@ export const enum TerminalContextKeyStrings { ChatActiveRequest = 'terminalChatActiveRequest', ChatInputHasText = 'terminalChatInputHasText', ChatAgentRegistered = 'terminalChatAgentRegistered', + ChatResponseEditorFocused = 'terminalChatResponseEditorFocused', } export namespace TerminalContextKeys { @@ -179,4 +180,7 @@ export namespace TerminalContextKeys { /** Whether the terminal chat agent has been registered */ export const chatAgentRegistered = new RawContextKey(TerminalContextKeyStrings.ChatAgentRegistered, false, localize('chatAgentRegisteredContextKey', "Whether the terminal chat agent has been registered.")); + + /** Whether the chat response editor is focused */ + export const chatResponseEditorFocused = new RawContextKey(TerminalContextKeyStrings.ChatResponseEditorFocused, false, localize('chatResponseEditorFocusedContextKey', "Whether the chat response editor is focused.")); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 73ebf9631a9..37cf45c77fc 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -78,19 +78,19 @@ registerActiveXtermAction({ ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.chatRequestActive.negate(), TerminalContextKeys.chatAgentRegistered, - CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyEdits) + CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.Empty) ), icon: Codicon.check, keybinding: { - when: ContextKeyExpr.and(CTX_INLINE_CHAT_FOCUSED, TerminalContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(TerminalContextKeys.chatResponseEditorFocused, TerminalContextKeys.chatRequestActive.negate()), weight: KeybindingWeight.EditorCore + 7, - primary: KeyCode.Enter + primary: KeyMod.CtrlCmd | KeyCode.Enter, }, menu: { id: MENU_TERMINAL_CHAT_WIDGET, group: 'main', order: 0, - when: CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyEdits), + when: CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.Empty), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 0a943b362fd..00b117d6661 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -65,6 +65,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._ctxHasActiveRequest = TerminalContextKeys.chatRequestActive.bindTo(this._contextKeyService); this._ctxHasTerminalAgent = TerminalContextKeys.chatAgentRegistered.bindTo(this._contextKeyService); this._ctxLastResponseType = CTX_INLINE_CHAT_RESPONSE_TYPES.bindTo(this._contextKeyService); + if (!this._chatAgentService.hasAgent('terminal')) { this._register(this._chatAgentService.onDidChangeAgents(() => { if (this._chatAgentService.getAgent('terminal')) { @@ -161,7 +162,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (codeBlock) { // TODO: check the SR experience this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._accessibilityRequestId); - this._ctxLastResponseType.set(InlineChatResponseTypes.OnlyEdits); } else { this._chatWidget?.rawValue?.renderMessage(message, this._accessibilityRequestId, requestId); this._ctxLastResponseType.set(InlineChatResponseTypes.OnlyMessages); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 0ccea8a61cd..41332e575fb 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -23,15 +23,16 @@ import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/termin import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, MENU_TERMINAL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; export class TerminalChatWidget extends Disposable { - private _scopedInstantiationService: IInstantiationService; - private _widgetContainer: HTMLElement; - private _chatWidgetFocused: IContextKey; - private _chatWidgetVisible: IContextKey; + private readonly _scopedInstantiationService: IInstantiationService; + private readonly _widgetContainer: HTMLElement; + private readonly _ctxChatWidgetFocused: IContextKey; + private readonly _ctxChatWidgetVisible: IContextKey; + private readonly _ctxResponseEditorFocused!: IContextKey; private readonly _inlineChatWidget: InlineChatWidget; - private _responseWidget: CodeEditorWidget | undefined; - private _responseElement: HTMLElement; + private readonly _responseElement: HTMLElement; private readonly _focusTracker: IFocusTracker; + private _responseWidget: CodeEditorWidget | undefined; constructor( private readonly _container: HTMLElement, @@ -44,8 +45,10 @@ export class TerminalChatWidget extends Disposable { super(); const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._container)); this._scopedInstantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); - this._chatWidgetFocused = TerminalContextKeys.chatFocused.bindTo(this._contextKeyService); - this._chatWidgetVisible = TerminalContextKeys.chatVisible.bindTo(this._contextKeyService); + this._ctxChatWidgetFocused = TerminalContextKeys.chatFocused.bindTo(this._contextKeyService); + this._ctxChatWidgetVisible = TerminalContextKeys.chatVisible.bindTo(this._contextKeyService); + this._ctxResponseEditorFocused = TerminalContextKeys.chatResponseEditorFocused.bindTo(this._contextKeyService); + this._widgetContainer = document.createElement('div'); this._widgetContainer.classList.add('terminal-inline-chat'); this._container.appendChild(this._widgetContainer); @@ -86,7 +89,7 @@ export class TerminalChatWidget extends Disposable { this._chatAccessibilityService.acceptResponse(codeBlock, requestId); this._responseElement.classList.remove('hide'); if (!this._responseWidget) { - this._responseWidget = this._scopedInstantiationService.createInstance(CodeEditorWidget, this._responseElement, {}, { isSimpleWidget: true }); + this._responseWidget = this._register(this._scopedInstantiationService.createInstance(CodeEditorWidget, this._responseElement, {}, { isSimpleWidget: true })); this._getTextModel(URI.from({ path: `terminal-inline-chat-${this._instance.instanceId}`, scheme: 'terminal-inline-chat', fragment: codeBlock })).then((model) => { if (!model || !this._responseWidget) { return; @@ -96,6 +99,12 @@ export class TerminalChatWidget extends Disposable { const height = this._responseWidget.getContentHeight(); this._responseWidget.layout(new Dimension(400, height)); }); + this._register(this._responseWidget.onDidFocusEditorText(() => { + this._ctxResponseEditorFocused.set(true); + })); + this._register(this._responseWidget.onDidBlurEditorText(() => { + this._ctxResponseEditorFocused.set(false); + })); } else { this._responseWidget.setValue(codeBlock); } @@ -116,15 +125,15 @@ export class TerminalChatWidget extends Disposable { reveal(): void { this._inlineChatWidget.layout(new Dimension(400, 150)); this._widgetContainer.classList.remove('hide'); - this._chatWidgetFocused.set(true); - this._chatWidgetVisible.set(true); + this._ctxChatWidgetFocused.set(true); + this._ctxChatWidgetVisible.set(true); this._inlineChatWidget.focus(); } hide(): void { this._responseElement?.classList.add('hide'); this._widgetContainer.classList.add('hide'); - this._chatWidgetFocused.set(false); - this._chatWidgetVisible.set(false); + this._ctxChatWidgetFocused.set(false); + this._ctxChatWidgetVisible.set(false); this._instance.focus(); } cancel(): void { From b93cd296b072da5ae170af15695e00d865c449f8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 12:15:54 -0800 Subject: [PATCH 0050/1175] Working terminal voice chat --- .../actions/media/voiceChatActions.css | 9 ++- .../actions/voiceChatActions.ts | 77 ++++++++++++++++--- .../electron-sandbox/chat.contribution.ts | 3 +- .../chat/browser/terminalChatController.ts | 52 +++++++++++++ .../chat/browser/terminalChatWidget.ts | 8 ++ 5 files changed, 136 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/media/voiceChatActions.css b/src/vs/workbench/contrib/chat/electron-sandbox/actions/media/voiceChatActions.css index 7a43cef7d12..87b1d68f988 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/media/voiceChatActions.css +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/media/voiceChatActions.css @@ -7,7 +7,8 @@ * Replace with "microphone" icon. */ .monaco-workbench .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before, -.monaco-workbench .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before { +.monaco-workbench .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before, +.monaco-workbench .terminal-inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before { content: "\ec1c"; } @@ -15,7 +16,8 @@ * Clear animation styles when reduced motion is enabled. */ .monaco-workbench.reduce-motion .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled), -.monaco-workbench.reduce-motion .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled) { +.monaco-workbench.reduce-motion .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled), +.monaco-workbench.reduce-motion .terminal-inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled) { animation: none; } @@ -23,6 +25,7 @@ * Replace with "stop" icon when reduced motion is enabled. */ .monaco-workbench.reduce-motion .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before, -.monaco-workbench.reduce-motion .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before { +.monaco-workbench.reduce-motion .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before, +.monaco-workbench.reduce-motion .terminal-inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before { content: "\ead7"; } diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index e41ae049b6e..aeb85706fc6 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -54,18 +54,23 @@ import { IVoiceChatService } from 'vs/workbench/contrib/chat/common/voiceChat'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ThemeIcon } from 'vs/base/common/themables'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +// TODO: The chat needs to move into contrib/terminal/ as we don't want anything importing from terminalContrib/ +// eslint-disable-next-line local/code-import-patterns +import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when getting ready for receiving voice input from the microphone for voice chat.") }); const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when voice recording from microphone is in progress for voice chat.") }); const CONTEXT_QUICK_VOICE_CHAT_IN_PROGRESS = new RawContextKey('quickVoiceChatInProgress', false, { type: 'boolean', description: localize('quickVoiceChatInProgress', "True when voice recording from microphone is in progress for quick chat.") }); const CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS = new RawContextKey('inlineVoiceChatInProgress', false, { type: 'boolean', description: localize('inlineVoiceChatInProgress', "True when voice recording from microphone is in progress for inline chat.") }); +const CONTEXT_TERMINAL_VOICE_CHAT_IN_PROGRESS = new RawContextKey('terminalVoiceChatInProgress', false, { type: 'boolean', description: localize('terminalVoiceChatInProgress', "True when voice recording from microphone is in progress for terminal chat.") }); const CONTEXT_VOICE_CHAT_IN_VIEW_IN_PROGRESS = new RawContextKey('voiceChatInViewInProgress', false, { type: 'boolean', description: localize('voiceChatInViewInProgress', "True when voice recording from microphone is in progress in the chat view.") }); const CONTEXT_VOICE_CHAT_IN_EDITOR_IN_PROGRESS = new RawContextKey('voiceChatInEditorInProgress', false, { type: 'boolean', description: localize('voiceChatInEditorInProgress', "True when voice recording from microphone is in progress in the chat editor.") }); const CanVoiceChat = ContextKeyExpr.and(CONTEXT_PROVIDER_EXISTS, HasSpeechProvider); -type VoiceChatSessionContext = 'inline' | 'quick' | 'view' | 'editor'; +type VoiceChatSessionContext = 'inline' | 'terminal' | 'quick' | 'view' | 'editor'; interface IVoiceChatSessionController { @@ -89,8 +94,9 @@ class VoiceChatSessionControllerFactory { static create(accessor: ServicesAccessor, context: 'quick'): Promise; static create(accessor: ServicesAccessor, context: 'view'): Promise; static create(accessor: ServicesAccessor, context: 'focused'): Promise; - static create(accessor: ServicesAccessor, context: 'inline' | 'quick' | 'view' | 'focused'): Promise; - static async create(accessor: ServicesAccessor, context: 'inline' | 'quick' | 'view' | 'focused'): Promise { + static create(accessor: ServicesAccessor, context: 'terminal'): Promise; + static create(accessor: ServicesAccessor, context: 'inline' | 'terminal' | 'quick' | 'view' | 'focused'): Promise; + static async create(accessor: ServicesAccessor, context: 'inline' | 'terminal' | 'quick' | 'view' | 'focused'): Promise { const chatWidgetService = accessor.get(IChatWidgetService); const chatService = accessor.get(IChatService); const viewsService = accessor.get(IViewsService); @@ -98,6 +104,7 @@ class VoiceChatSessionControllerFactory { const quickChatService = accessor.get(IQuickChatService); const layoutService = accessor.get(IWorkbenchLayoutService); const editorService = accessor.get(IEditorService); + const terminalService = accessor.get(ITerminalService); // Currently Focused Context if (context === 'focused') { @@ -132,6 +139,15 @@ class VoiceChatSessionControllerFactory { return VoiceChatSessionControllerFactory.doCreateForInlineChat(inlineChat); } } + + // Try with the terminal chat + const activeInstance = terminalService.activeInstance; + if (activeInstance) { + const terminalChat = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + if (terminalChat?.hasFocus()) { + return VoiceChatSessionControllerFactory.doCreateForTerminalChat(terminalChat); + } + } } // View Chat @@ -156,6 +172,17 @@ class VoiceChatSessionControllerFactory { } } + // Terminal Chat + if (context === 'terminal') { + const activeInstance = terminalService.activeInstance; + if (activeInstance) { + const terminalChat = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + if (terminalChat) { + return VoiceChatSessionControllerFactory.doCreateForTerminalChat(terminalChat); + } + } + } + // Quick Chat if (context === 'quick') { quickChatService.open(); @@ -224,6 +251,20 @@ class VoiceChatSessionControllerFactory { clearInputPlaceholder: () => inlineChat.resetPlaceholder() }; } + + private static doCreateForTerminalChat(terminalChat: TerminalChatController): IVoiceChatSessionController { + return { + context: 'terminal', + onDidAcceptInput: terminalChat.onDidAcceptInput, + onDidCancelInput: terminalChat.onDidCancelInput, + focusInput: () => terminalChat.focus(), + acceptInput: () => terminalChat.acceptInput(), + updateInput: text => terminalChat.updateInput(text, false), + getInput: () => terminalChat.getInput(), + setInputPlaceholder: text => terminalChat.setPlaceholder(text), + clearInputPlaceholder: () => terminalChat.resetPlaceholder() + }; + } } interface IVoiceChatSession { @@ -255,6 +296,7 @@ class VoiceChatSessions { private quickVoiceChatInProgressKey = CONTEXT_QUICK_VOICE_CHAT_IN_PROGRESS.bindTo(this.contextKeyService); private inlineVoiceChatInProgressKey = CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS.bindTo(this.contextKeyService); + private terminalVoiceChatInProgressKey = CONTEXT_TERMINAL_VOICE_CHAT_IN_PROGRESS.bindTo(this.contextKeyService); private voiceChatInViewInProgressKey = CONTEXT_VOICE_CHAT_IN_VIEW_IN_PROGRESS.bindTo(this.contextKeyService); private voiceChatInEditorInProgressKey = CONTEXT_VOICE_CHAT_IN_EDITOR_IN_PROGRESS.bindTo(this.contextKeyService); @@ -345,6 +387,9 @@ class VoiceChatSessions { case 'inline': this.inlineVoiceChatInProgressKey.set(true); break; + case 'terminal': + this.terminalVoiceChatInProgressKey.set(true); + break; case 'quick': this.quickVoiceChatInProgressKey.set(true); break; @@ -387,6 +432,7 @@ class VoiceChatSessions { this.quickVoiceChatInProgressKey.set(false); this.inlineVoiceChatInProgressKey.set(false); + this.terminalVoiceChatInProgressKey.set(false); this.voiceChatInViewInProgressKey.set(false); this.voiceChatInEditorInProgressKey.set(false); } @@ -510,7 +556,8 @@ export class StartVoiceChatAction extends Action2 { CONTEXT_VOICE_CHAT_IN_VIEW_IN_PROGRESS.negate(), CONTEXT_QUICK_VOICE_CHAT_IN_PROGRESS.negate(), CONTEXT_VOICE_CHAT_IN_EDITOR_IN_PROGRESS.negate(), - CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS.negate() + CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS.negate(), + CONTEXT_TERMINAL_VOICE_CHAT_IN_PROGRESS.negate() ), primary: KeyMod.CtrlCmd | KeyCode.KeyI }, @@ -529,7 +576,7 @@ export class StartVoiceChatAction extends Action2 { }, { id: MenuId.for('terminalChatInput'), - when: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS.negate()), + when: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_TERMINAL_VOICE_CHAT_IN_PROGRESS.negate()), group: 'main', order: -1 }] @@ -629,7 +676,7 @@ class BaseStopListeningAction extends Action2 { constructor( desc: { id: string; icon?: ThemeIcon; f1?: boolean }, - private readonly target: 'inline' | 'quick' | 'view' | 'editor' | undefined, + private readonly target: 'inline' | 'terminal' | 'quick' | 'view' | 'editor' | undefined, context: RawContextKey, menu: MenuId | undefined, group: 'navigation' | 'main' = 'navigation' @@ -703,6 +750,15 @@ export class StopListeningInInlineChatAction extends BaseStopListeningAction { } } +export class StopListeningInTerminalChatAction extends BaseStopListeningAction { + + static readonly ID = 'workbench.action.chat.stopListeningInTerminalChat'; + + constructor() { + super({ id: StopListeningInTerminalChatAction.ID, icon: spinningLoading }, 'terminal', CONTEXT_TERMINAL_VOICE_CHAT_IN_PROGRESS, MenuId.for('terminalChatInput'), 'main'); + } +} + export class StopListeningAndSubmitAction extends Action2 { static readonly ID = 'workbench.action.chat.stopListeningAndSubmit'; @@ -745,7 +801,8 @@ registerThemingParticipant((theme, collector) => { // Show a "microphone" icon when recording is in progress that glows via outline. collector.addRule(` .monaco-workbench:not(.reduce-motion) .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled), - .monaco-workbench:not(.reduce-motion) .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled) { + .monaco-workbench:not(.reduce-motion) .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled), + .monaco-workbench:not(.reduce-motion) .terminal-inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled) { color: ${activeRecordingColor}; outline: 1px solid ${activeRecordingColor}; outline-offset: -1px; @@ -754,7 +811,8 @@ registerThemingParticipant((theme, collector) => { } .monaco-workbench:not(.reduce-motion) .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before, - .monaco-workbench:not(.reduce-motion) .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before { + .monaco-workbench:not(.reduce-motion) .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before, + .monaco-workbench:not(.reduce-motion) .terminal-inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before { position: absolute; outline: 1px solid ${activeRecordingColor}; outline-offset: 2px; @@ -764,7 +822,8 @@ registerThemingParticipant((theme, collector) => { } .monaco-workbench:not(.reduce-motion) .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::after, - .monaco-workbench:not(.reduce-motion) .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::after { + .monaco-workbench:not(.reduce-motion) .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::after, + .monaco-workbench:not(.reduce-motion) .terminal-inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::after { content: ''; position: absolute; outline: 1px solid ${activeRecordingDimmedColor}; diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts index 2f69e1b13f8..02383b6b3f3 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { InlineVoiceChatAction, QuickVoiceChatAction, StartVoiceChatAction, StopListeningInInlineChatAction, StopListeningInQuickChatAction, StopListeningInChatEditorAction, StopListeningInChatViewAction, VoiceChatInChatViewAction, StopListeningAction, StopListeningAndSubmitAction, KeywordActivationContribution, InstallVoiceChatAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; +import { InlineVoiceChatAction, QuickVoiceChatAction, StartVoiceChatAction, StopListeningInInlineChatAction, StopListeningInQuickChatAction, StopListeningInChatEditorAction, StopListeningInChatViewAction, VoiceChatInChatViewAction, StopListeningAction, StopListeningAndSubmitAction, KeywordActivationContribution, InstallVoiceChatAction, StopListeningInTerminalChatAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; import { registerAction2 } from 'vs/platform/actions/common/actions'; import { WorkbenchPhase, registerWorkbenchContribution2 } from 'vs/workbench/common/contributions'; @@ -21,5 +21,6 @@ registerAction2(StopListeningInChatViewAction); registerAction2(StopListeningInChatEditorAction); registerAction2(StopListeningInQuickChatAction); registerAction2(StopListeningInInlineChatAction); +registerAction2(StopListeningInTerminalChatAction); registerWorkbenchContribution2(KeywordActivationContribution.ID, KeywordActivationContribution, WorkbenchPhase.AfterRestored); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 00b117d6661..86287257f8f 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -24,6 +24,18 @@ import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/termin import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; import { CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { Emitter, Event } from 'vs/base/common/event'; + +const enum Message { + NONE = 0, + ACCEPT_SESSION = 1 << 0, + CANCEL_SESSION = 1 << 1, + PAUSE_SESSION = 1 << 2, + CANCEL_REQUEST = 1 << 3, + CANCEL_INPUT = 1 << 4, + ACCEPT_INPUT = 1 << 5, + RERUN_INPUT = 1 << 6, +} export class TerminalChatController extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.Chat'; @@ -47,6 +59,11 @@ export class TerminalChatController extends Disposable implements ITerminalContr private _cancellationTokenSource!: CancellationTokenSource; + private _messages = this._store.add(new Emitter()); + + readonly onDidAcceptInput = Event.filter(this._messages.event, m => m === Message.ACCEPT_INPUT, this._store); + readonly onDidCancelInput = Event.filter(this._messages.event, m => m === Message.CANCEL_INPUT || m === Message.CANCEL_SESSION, this._store); + constructor( private readonly _instance: ITerminalInstance, processManager: ITerminalProcessManager, @@ -118,6 +135,18 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._cancellationTokenSource.cancel(); } + setPlaceholder(text: string): void { + // TODO: Impl + // this._forcedPlaceholder = text; + // this._updatePlaceholder(); + } + + resetPlaceholder(): void { + // TODO: Impl + // this._forcedPlaceholder = undefined; + // this._updatePlaceholder(); + } + async acceptInput(): Promise { let message = ''; this._chatAccessibilityService.acceptRequest(); @@ -168,6 +197,29 @@ export class TerminalChatController extends Disposable implements ITerminalContr } this._ctxHasActiveRequest.set(false); this._chatWidget?.rawValue?.updateProgress(); + this._messages.fire(Message.ACCEPT_INPUT); + } + + updateInput(text: string, selectAll = true): void { + const widget = this._chatWidget?.rawValue?.inlineChatWidget; + if (widget) { + widget.value = text; + if (selectAll) { + widget.selectAll(); + } + } + } + + getInput(): string { + return this._chatWidget?.rawValue?.input() ?? ''; + } + + focus(): void { + this._chatWidget?.rawValue?.focus(); + } + + hasFocus(): boolean { + return !!this._chatWidget?.rawValue?.hasFocus(); } acceptCommand(): void { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 41332e575fb..6374e1cc991 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -34,6 +34,8 @@ export class TerminalChatWidget extends Disposable { private readonly _focusTracker: IFocusTracker; private _responseWidget: CodeEditorWidget | undefined; + public get inlineChatWidget(): InlineChatWidget { return this._inlineChatWidget; } + constructor( private readonly _container: HTMLElement, private readonly _instance: ITerminalInstance, @@ -140,6 +142,12 @@ export class TerminalChatWidget extends Disposable { // TODO: Impl this._inlineChatWidget.value = ''; } + focus(): void { + this._inlineChatWidget.focus(); + } + hasFocus(): boolean { + return this._inlineChatWidget.hasFocus(); + } input(): string { return this._inlineChatWidget.value; } From 3960db9ce3b00189765b88601fefa1913ffdd1e9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 12:19:10 -0800 Subject: [PATCH 0051/1175] Add voice placeholder --- .../chat/browser/terminalChatController.ts | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 86287257f8f..f3d7ef85326 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -135,16 +135,29 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._cancellationTokenSource.cancel(); } + private _forcedPlaceholder: string | undefined = undefined; + + private _updatePlaceholder(): void { + const inlineChatWidget = this._chatWidget?.rawValue?.inlineChatWidget; + if (inlineChatWidget) { + inlineChatWidget.placeholder = this._getPlaceholderText(); + } + } + + private _getPlaceholderText(): string { + return this._forcedPlaceholder ?? ''; + // TODO: Pass through session placeholder + // return this._forcedPlaceholder ?? this._session?.session.placeholder ?? ''; + } + setPlaceholder(text: string): void { - // TODO: Impl - // this._forcedPlaceholder = text; - // this._updatePlaceholder(); + this._forcedPlaceholder = text; + this._updatePlaceholder(); } resetPlaceholder(): void { - // TODO: Impl - // this._forcedPlaceholder = undefined; - // this._updatePlaceholder(); + this._forcedPlaceholder = undefined; + this._updatePlaceholder(); } async acceptInput(): Promise { From bf1bb656259791cf84051759a1d264409b5239c0 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 14:50:05 -0600 Subject: [PATCH 0052/1175] clear on hide --- .../contrib/terminalContrib/chat/browser/terminalChatWidget.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 6374e1cc991..0e8b82043ea 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -134,6 +134,8 @@ export class TerminalChatWidget extends Disposable { hide(): void { this._responseElement?.classList.add('hide'); this._widgetContainer.classList.add('hide'); + this._inlineChatWidget.value = ''; + this._responseWidget?.setValue(''); this._ctxChatWidgetFocused.set(false); this._ctxChatWidgetVisible.set(false); this._instance.focus(); From 83f8ee26b6c832c68f7ba37f7b3f083bc2b76292 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 14:53:59 -0600 Subject: [PATCH 0053/1175] rm unused --- .../chat/browser/terminalChatController.ts | 14 -------------- .../chat/browser/terminalChatWidget.ts | 11 ++--------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index f3d7ef85326..83adeff265d 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -6,7 +6,6 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget'; import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { IDimension } from 'vs/base/browser/dom'; import { Lazy } from 'vs/base/common/lazy'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; @@ -49,7 +48,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr */ static activeChatWidget?: TerminalChatController; private _chatWidget: Lazy | undefined; - private _lastLayoutDimensions: IDimension | undefined; private _accessibilityRequestId: number = 0; get chatWidget(): TerminalChatWidget | undefined { return this._chatWidget?.value; } @@ -95,14 +93,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._cancellationTokenSource = new CancellationTokenSource(); } - layout(_xterm: IXtermTerminal & { raw: RawXtermTerminal }, dimension: IDimension): void { - if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { - return; - } - this._lastLayoutDimensions = dimension; - this._chatWidget?.rawValue?.layout(dimension.width); - } - xtermReady(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { return; @@ -123,10 +113,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr throw new Error('FindWidget expected terminal DOM to be initialized'); } - if (this._lastLayoutDimensions) { - chatWidget.layout(this._lastLayoutDimensions.width); - } - return chatWidget; }); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 0e8b82043ea..61aa2c4a409 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -92,6 +92,8 @@ export class TerminalChatWidget extends Disposable { this._responseElement.classList.remove('hide'); if (!this._responseWidget) { this._responseWidget = this._register(this._scopedInstantiationService.createInstance(CodeEditorWidget, this._responseElement, {}, { isSimpleWidget: true })); + this._register(this._responseWidget.onDidFocusEditorText(() => this._ctxResponseEditorFocused.set(true))); + this._register(this._responseWidget.onDidBlurEditorText(() => this._ctxResponseEditorFocused.set(false))); this._getTextModel(URI.from({ path: `terminal-inline-chat-${this._instance.instanceId}`, scheme: 'terminal-inline-chat', fragment: codeBlock })).then((model) => { if (!model || !this._responseWidget) { return; @@ -101,12 +103,6 @@ export class TerminalChatWidget extends Disposable { const height = this._responseWidget.getContentHeight(); this._responseWidget.layout(new Dimension(400, height)); }); - this._register(this._responseWidget.onDidFocusEditorText(() => { - this._ctxResponseEditorFocused.set(true); - })); - this._register(this._responseWidget.onDidBlurEditorText(() => { - this._ctxResponseEditorFocused.set(false); - })); } else { this._responseWidget.setValue(codeBlock); } @@ -170,9 +166,6 @@ export class TerminalChatWidget extends Disposable { updateProgress(progress?: IChatProgress): void { this._inlineChatWidget.updateProgress(progress?.kind === 'content' || progress?.kind === 'markdownContent'); } - layout(width: number): void { - // this._widget?.layout(100, width < 300 ? 300 : width); - } public get focusTracker(): IFocusTracker { return this._focusTracker; } From 1d262b3146aa7f3c254d28fc0144f9ae08bf9717 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 15:05:59 -0600 Subject: [PATCH 0054/1175] update variables after rob's change --- .../terminalContrib/chat/browser/terminalChatController.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 83adeff265d..9c9cb0a2ff8 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -17,7 +17,6 @@ import { IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/ import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; import { generateUuid } from 'vs/base/common/uuid'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; import { marked } from 'vs/base/common/marked/marked'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -162,15 +161,13 @@ export class TerminalChatController extends Disposable implements ITerminalContr } this._chatWidget?.rawValue?.updateProgress(progress); }; - const resolvedVariables: Record = {}; const requestId = generateUuid(); const requestProps: IChatAgentRequest = { sessionId: generateUuid(), requestId, agentId, message: this._chatWidget?.rawValue?.input() || '', - variables: resolvedVariables, - variables2: { message: this._chatWidget?.rawValue?.input() || '', variables: [] } + variables: { variables: [] }, }; this._chatWidget?.rawValue?.setValue(); From cd261754e852064d15516f6321a65ac17c906a3f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 13:06:30 -0800 Subject: [PATCH 0055/1175] Simplify response editor presentation --- .../chat/browser/terminalChatWidget.ts | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 61aa2c4a409..0c174ed3555 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -91,7 +91,41 @@ export class TerminalChatWidget extends Disposable { this._chatAccessibilityService.acceptResponse(codeBlock, requestId); this._responseElement.classList.remove('hide'); if (!this._responseWidget) { - this._responseWidget = this._register(this._scopedInstantiationService.createInstance(CodeEditorWidget, this._responseElement, {}, { isSimpleWidget: true })); + this._responseWidget = this._register(this._scopedInstantiationService.createInstance(CodeEditorWidget, this._responseElement, { + padding: { top: 2, bottom: 2 }, + overviewRulerLanes: 0, + glyphMargin: false, + lineNumbers: 'off', + folding: false, + hideCursorInOverviewRuler: true, + selectOnLineNumbers: false, + selectionHighlight: false, + scrollbar: { + useShadows: false, + vertical: 'hidden', + horizontal: 'auto', + alwaysConsumeMouseWheel: false + }, + lineDecorationsWidth: 0, + overviewRulerBorder: false, + scrollBeyondLastLine: false, + renderLineHighlight: 'none', + fixedOverflowWidgets: true, + dragAndDrop: false, + revealHorizontalRightPadding: 5, + minimap: { enabled: false }, + guides: { indentation: false }, + rulers: [], + renderWhitespace: 'none', + dropIntoEditor: { enabled: true }, + quickSuggestions: false, + suggest: { + showIcons: false, + showSnippets: false, + showWords: true, + showStatusBar: false, + }, + }, { isSimpleWidget: true })); this._register(this._responseWidget.onDidFocusEditorText(() => this._ctxResponseEditorFocused.set(true))); this._register(this._responseWidget.onDidBlurEditorText(() => this._ctxResponseEditorFocused.set(false))); this._getTextModel(URI.from({ path: `terminal-inline-chat-${this._instance.instanceId}`, scheme: 'terminal-inline-chat', fragment: codeBlock })).then((model) => { From 84fb0821a9347a1b8c563f29259962e758ca6e87 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 15:56:53 -0600 Subject: [PATCH 0056/1175] extract code content, language --- .../terminalContrib/chat/browser/terminalChatController.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 9c9cb0a2ff8..f7edc4b5fd6 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -179,7 +179,12 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.updateProgress(); return; } - const codeBlock = marked.lexer(message).filter(token => token.type === 'code')?.[0]?.raw.replaceAll('```', ''); + const firstCodeBlockContent = marked.lexer(message).filter(token => token.type === 'code')?.[0]?.raw; + const regex = /```(?\w+)\n(?[\s\S]*?)```/g; + const match = regex.exec(firstCodeBlockContent); + const codeBlock = match?.groups?.content; + // TODO: map to editor known language, set editor language + // const language = match?.groups?.language; this._accessibilityRequestId++; if (cancellationToken.isCancellationRequested) { return; From fa111adf63ac35b6b8921db9ece8cb7880a7831c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Feb 2024 16:08:25 -0600 Subject: [PATCH 0057/1175] add language support --- .../chat/browser/terminalChatController.ts | 5 ++--- .../chat/browser/terminalChatWidget.ts | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index f7edc4b5fd6..221c010600b 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -183,15 +183,14 @@ export class TerminalChatController extends Disposable implements ITerminalContr const regex = /```(?\w+)\n(?[\s\S]*?)```/g; const match = regex.exec(firstCodeBlockContent); const codeBlock = match?.groups?.content; - // TODO: map to editor known language, set editor language - // const language = match?.groups?.language; + const shellType = match?.groups?.language; this._accessibilityRequestId++; if (cancellationToken.isCancellationRequested) { return; } if (codeBlock) { // TODO: check the SR experience - this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._accessibilityRequestId); + this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._accessibilityRequestId, shellType); } else { this._chatWidget?.rawValue?.renderMessage(message, this._accessibilityRequestId, requestId); this._ctxLastResponseType.set(InlineChatResponseTypes.OnlyMessages); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 0c174ed3555..abb8807cdd9 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -87,7 +87,7 @@ export class TerminalChatWidget extends Disposable { this._focusTracker = this._register(trackFocus(this._widgetContainer)); } - renderTerminalCommand(codeBlock: string, requestId: number): void { + renderTerminalCommand(codeBlock: string, requestId: number, shellType?: string): void { this._chatAccessibilityService.acceptResponse(codeBlock, requestId); this._responseElement.classList.remove('hide'); if (!this._responseWidget) { @@ -140,6 +140,20 @@ export class TerminalChatWidget extends Disposable { } else { this._responseWidget.setValue(codeBlock); } + this._responseWidget.getModel()?.setLanguage(this._getLanguageFromShell(shellType)); + } + + private _getLanguageFromShell(shell?: string): string { + switch (shell) { + case 'sh': + case 'bash': + case 'zsh': + return 'shellscript'; + case 'pwsh': + return 'powershell'; + default: + return 'plaintext'; + } } renderMessage(message: string, accessibilityRequestId: number, requestId: string): void { From 16f1f4bfc1cb918a397c5c4cd4a1904d88ec983b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:29:44 -0800 Subject: [PATCH 0058/1175] Add shell language id fallback --- .../terminalContrib/chat/browser/terminalChatWidget.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index abb8807cdd9..c3efebdc5ca 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -9,6 +9,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/terminalChatWidget'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { ILanguageService } from 'vs/editor/common/languages/language'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/model'; import { localize } from 'vs/nls'; @@ -42,6 +43,7 @@ export class TerminalChatWidget extends Disposable { @IInstantiationService instantiationService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService, + @ILanguageService private readonly _languageService: ILanguageService, @IModelService private readonly _modelService: IModelService ) { super(); @@ -145,9 +147,13 @@ export class TerminalChatWidget extends Disposable { private _getLanguageFromShell(shell?: string): string { switch (shell) { - case 'sh': - case 'bash': + case 'fish': + return this._languageService.isRegisteredLanguageId('fish') ? 'fish' : 'shellscript'; case 'zsh': + return this._languageService.isRegisteredLanguageId('zsh') ? 'zsh' : 'shellscript'; + case 'bash': + return this._languageService.isRegisteredLanguageId('bash') ? 'bash' : 'shellscript'; + case 'sh': return 'shellscript'; case 'pwsh': return 'powershell'; From 8112339b211fcdb807e4e7f9b57f39142fbe289b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:49:37 -0800 Subject: [PATCH 0059/1175] Improve progress feedback --- .../chat/browser/terminalChatController.ts | 17 +++++++++++++---- .../chat/browser/terminalChatWidget.ts | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 221c010600b..36db8ad2ab9 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -23,6 +23,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; import { CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { Emitter, Event } from 'vs/base/common/event'; +import { localize } from 'vs/nls'; const enum Message { NONE = 0, @@ -172,12 +173,20 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.setValue(); try { - await this._chatAgentService.invokeAgent(agentId, requestProps, progressCallback, [], cancellationToken); + const task = this._chatAgentService.invokeAgent(agentId, requestProps, progressCallback, [], cancellationToken); + this._chatWidget?.rawValue?.inlineChatWidget.updateChatMessage(undefined); + this._chatWidget?.rawValue?.inlineChatWidget.updateFollowUps(undefined); + this._chatWidget?.rawValue?.inlineChatWidget.updateProgress(true); + this._chatWidget?.rawValue?.inlineChatWidget.updateInfo(localize('thinking', "Thinking\u2026")); + // TODO: this._zone.value.widget.updateInfo(!this._session.lastExchange ? localize('thinking', "Thinking\u2026") : ''); + await task; } catch (e) { - // Provider is not ready + + } finally { this._ctxHasActiveRequest.set(false); - this._chatWidget?.rawValue?.updateProgress(); - return; + this._chatWidget?.rawValue?.inlineChatWidget.updateProgress(false); + this._chatWidget?.rawValue?.inlineChatWidget.updateInfo(''); + this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); } const firstCodeBlockContent = marked.lexer(message).filter(token => token.type === 'code')?.[0]?.raw; const regex = /```(?\w+)\n(?[\s\S]*?)```/g; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index c3efebdc5ca..402f8638558 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -84,7 +84,7 @@ export class TerminalChatWidget extends Disposable { } ); this._inlineChatWidget.placeholder = localize('default.placeholder', "Ask how to do something in the terminal"); - this._inlineChatWidget.updateInfo(localize('welcome.1', "AI-generated code may be incorrect")); + this._inlineChatWidget.updateInfo(localize('welcome.1', "AI-generated commands may be incorrect")); this._widgetContainer.appendChild(this._inlineChatWidget.domNode); this._focusTracker = this._register(trackFocus(this._widgetContainer)); From 3c306e9b87596190decd7749f68b00b0acc52525 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:52:43 -0800 Subject: [PATCH 0060/1175] Fix feedback icons --- .../chat/browser/terminalChatActions.ts | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 37cf45c77fc..a62ed944245 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -162,6 +162,7 @@ registerActiveXtermAction({ ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), TerminalContextKeys.chatRequestActive, ), + // TODO: toggled: CTX_INLINE_CHAT_LAST_FEEDBACK.isEqualTo('helpful'), icon: Codicon.thumbsup, menu: { id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, @@ -177,12 +178,13 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.FeedbackUnhelpful, - title: localize2('feedbackUnhelpful', 'Helpful'), + title: localize2('feedbackUnhelpful', 'Unhelpful'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), TerminalContextKeys.chatRequestActive, ), - icon: Codicon.thumbsup, + // TODO: toggled: CTX_INLINE_CHAT_LAST_FEEDBACK.isEqualTo('unhelpful'), + icon: Codicon.thumbsdown, menu: { id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, group: 'inline', @@ -202,14 +204,19 @@ registerActiveXtermAction({ ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), TerminalContextKeys.chatRequestActive, ), - icon: Codicon.thumbsup, - menu: { + // TODO: precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.Empty)), + icon: Codicon.report, + menu: [/*{ + // TODO: Enable this id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, - group: 'inline', - order: 3, - // TODO: Fill in ctx - // when: CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.notEqualsTo(undefined), - }, + when: ContextKeyExpr.and(CTX_TERMINAL_CHAT_SUPPORT_ISSUE_REPORTING, CTX_TERMINAL_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.Empty)), + group: '2_feedback', + order: 3 + }, */{ + id: MENU_TERMINAL_CHAT_WIDGET, + group: 'config', + order: 3 + }], run: (_xterm, _accessor, activeInstance) => { // TODO: Impl } From 3119ae83f7fc220079e3a8ef9b42d254fc0ead34 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:57:38 -0800 Subject: [PATCH 0061/1175] Move accept to status menu --- .../terminalContrib/chat/browser/terminalChatActions.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index a62ed944245..348d5bee159 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -13,7 +13,7 @@ import { CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_RESPONS import { isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, TerminalChatCommandId } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; +import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, MENU_TERMINAL_CHAT_WIDGET_STATUS, TerminalChatCommandId } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; registerActiveXtermAction({ @@ -72,7 +72,8 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.AcceptCommand, - title: localize2('workbench.action.terminal.acceptCommand', 'Accept Command'), + title: localize2('acceptCommand', 'Accept Command'), + shortTitle: localize2('accept', 'Accept'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), @@ -87,8 +88,8 @@ registerActiveXtermAction({ primary: KeyMod.CtrlCmd | KeyCode.Enter, }, menu: { - id: MENU_TERMINAL_CHAT_WIDGET, - group: 'main', + id: MENU_TERMINAL_CHAT_WIDGET_STATUS, + group: '0_main', order: 0, when: CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.Empty), }, From 802e74686aab506468f23b1097034c72a22c057c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:58:43 -0800 Subject: [PATCH 0062/1175] Simplify localize id --- .../terminalContrib/chat/browser/terminalChatActions.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 348d5bee159..d0205008d74 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -18,7 +18,7 @@ import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/cha registerActiveXtermAction({ id: TerminalChatCommandId.Focus, - title: localize2('workbench.action.terminal.focusChat', 'Focus Chat'), + title: localize2('focusChat', 'Focus Chat'), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyI, when: ContextKeyExpr.and(TerminalContextKeys.chatFocused.negate(), TerminalContextKeys.focusInAny), @@ -40,7 +40,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.Hide, - title: localize2('workbench.action.terminal.closeChat', 'Close Chat'), + title: localize2('closeChat', 'Close Chat'), keybinding: { primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], @@ -104,7 +104,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.MakeRequest, - title: localize2('workbench.action.terminal.submitChat', 'Make Chat Request'), + title: localize2('makeChatRequest', 'Make Chat Request'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), @@ -135,7 +135,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.Cancel, - title: localize2('workbench.action.terminal.cancelChat', 'Cancel Chat'), + title: localize2('cancelChat', 'Cancel Chat'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), TerminalContextKeys.chatRequestActive, From ee47ee25bb67793e707129ed258b219419152216 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 14 Feb 2024 12:18:38 -0600 Subject: [PATCH 0063/1175] get accept action to show up, add view in chat action --- .../inlineChat/browser/inlineChatWidget.ts | 2 +- .../chat/browser/terminalChat.ts | 1 + .../chat/browser/terminalChatActions.ts | 29 ++++++++++++- .../chat/browser/terminalChatController.ts | 41 ++++++++++++++----- .../chat/browser/terminalChatWidget.ts | 1 + 5 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index ca909ba98e0..537c7b8951f 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -388,7 +388,7 @@ export class InlineChatWidget { buttonConfigProvider: action => { if (action.id === ACTION_REGENERATE_RESPONSE) { return { showIcon: true, showLabel: false, isSecondary: true }; - } else if (action.id === ACTION_VIEW_IN_CHAT || action.id === ACTION_ACCEPT_CHANGES) { + } else if ([ACTION_VIEW_IN_CHAT, ACTION_ACCEPT_CHANGES, 'workbench.action.terminal.chat.acceptCommand', 'workbench.action.terminal.chat.viewInChat'].includes(action.id)) { return { isSecondary: false }; } else { return { isSecondary: true }; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts index 882baf8c26b..ddea1c2a53b 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts @@ -14,6 +14,7 @@ export const enum TerminalChatCommandId { FeedbackUnhelpful = 'workbench.action.terminal.chat.feedbackUnhelpful', FeedbackReportIssue = 'workbench.action.terminal.chat.feedbackReportIssue', AcceptCommand = 'workbench.action.terminal.chat.acceptCommand', + ViewInChat = 'workbench.action.terminal.chat.viewInChat', } export const MENU_TERMINAL_CHAT_INPUT = MenuId.for('terminalChatInput'); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index d0205008d74..d6ba18a272e 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -91,7 +91,7 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 0, - when: CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.Empty), + when: ContextKeyExpr.and(CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.Empty), TerminalContextKeys.chatRequestActive.negate()), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -102,6 +102,32 @@ registerActiveXtermAction({ } }); +registerActiveXtermAction({ + id: TerminalChatCommandId.ViewInChat, + title: localize2('viewInChat', 'View in Chat'), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + TerminalContextKeys.chatRequestActive.negate(), + TerminalContextKeys.chatAgentRegistered, + CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyMessages) + ), + icon: Codicon.commentDiscussion, + menu: { + id: MENU_TERMINAL_CHAT_WIDGET_STATUS, + group: '0_main', + order: 1, + when: ContextKeyExpr.and(CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyMessages), TerminalContextKeys.chatRequestActive.negate()), + }, + run: (_xterm, _accessor, activeInstance) => { + if (isDetachedTerminalInstance(activeInstance)) { + return; + } + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + contr?.viewInChat(); + } +}); + registerActiveXtermAction({ id: TerminalChatCommandId.MakeRequest, title: localize2('makeChatRequest', 'Make Chat Request'), @@ -222,3 +248,4 @@ registerActiveXtermAction({ // TODO: Impl } }); + diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 36db8ad2ab9..da56f4d4041 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -14,13 +14,13 @@ import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/te import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; -import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatProgress, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { generateUuid } from 'vs/base/common/uuid'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { marked } from 'vs/base/common/marked/marked'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; +import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { Emitter, Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; @@ -62,6 +62,9 @@ export class TerminalChatController extends Disposable implements ITerminalContr readonly onDidAcceptInput = Event.filter(this._messages.event, m => m === Message.ACCEPT_INPUT, this._store); readonly onDidCancelInput = Event.filter(this._messages.event, m => m === Message.CANCEL_INPUT || m === Message.CANCEL_SESSION, this._store); + private _lastInput: string | undefined; + private _lastResponseContent: string | undefined; + constructor( private readonly _instance: ITerminalInstance, processManager: ITerminalProcessManager, @@ -72,6 +75,8 @@ export class TerminalChatController extends Disposable implements ITerminalContr @IChatAgentService private readonly _chatAgentService: IChatAgentService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService, + @IChatService private readonly _chatService: IChatService, + @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService ) { super(); if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { @@ -147,18 +152,22 @@ export class TerminalChatController extends Disposable implements ITerminalContr } async acceptInput(): Promise { - let message = ''; + this._lastInput = this._chatWidget?.rawValue?.input(); + if (!this._lastInput) { + return; + } this._chatAccessibilityService.acceptRequest(); this._ctxHasActiveRequest.set(true); const cancellationToken = this._cancellationTokenSource.token; const agentId = 'terminal'; + let responseContent = ''; const progressCallback = (progress: IChatProgress) => { if (cancellationToken.isCancellationRequested) { return; } if (progress.kind === 'content' || progress.kind === 'markdownContent') { - message += progress.content; + responseContent += progress.content; } this._chatWidget?.rawValue?.updateProgress(progress); }; @@ -167,11 +176,9 @@ export class TerminalChatController extends Disposable implements ITerminalContr sessionId: generateUuid(), requestId, agentId, - message: this._chatWidget?.rawValue?.input() || '', + message: this._lastInput, variables: { variables: [] }, }; - this._chatWidget?.rawValue?.setValue(); - try { const task = this._chatAgentService.invokeAgent(agentId, requestProps, progressCallback, [], cancellationToken); this._chatWidget?.rawValue?.inlineChatWidget.updateChatMessage(undefined); @@ -188,7 +195,8 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.inlineChatWidget.updateInfo(''); this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); } - const firstCodeBlockContent = marked.lexer(message).filter(token => token.type === 'code')?.[0]?.raw; + this._lastResponseContent = responseContent; + const firstCodeBlockContent = marked.lexer(responseContent).filter(token => token.type === 'code')?.[0]?.raw; const regex = /```(?\w+)\n(?[\s\S]*?)```/g; const match = regex.exec(firstCodeBlockContent); const codeBlock = match?.groups?.content; @@ -200,12 +208,12 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (codeBlock) { // TODO: check the SR experience this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._accessibilityRequestId, shellType); + this._ctxLastResponseType.set(InlineChatResponseTypes.Empty); } else { - this._chatWidget?.rawValue?.renderMessage(message, this._accessibilityRequestId, requestId); + this._chatWidget?.rawValue?.renderMessage(responseContent, this._accessibilityRequestId, requestId); this._ctxLastResponseType.set(InlineChatResponseTypes.OnlyMessages); } - this._ctxHasActiveRequest.set(false); - this._chatWidget?.rawValue?.updateProgress(); + this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); this._messages.fire(Message.ACCEPT_INPUT); } @@ -239,6 +247,17 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.reveal(); } + async viewInChat(): Promise { + if (!this._lastInput || !this._lastResponseContent) { + return; + } + const widget = await this._chatWidgetService.revealViewForProvider('copilot'); + if (widget && widget.viewModel) { + this._chatService.addCompleteRequest(widget.viewModel.sessionId, this._lastInput, undefined, { message: this._lastResponseContent }); + widget.focusLastMessage(); + } + } + override dispose() { super.dispose(); this._chatWidget?.rawValue?.dispose(); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 402f8638558..b8c73ef1ffb 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -163,6 +163,7 @@ export class TerminalChatWidget extends Disposable { } renderMessage(message: string, accessibilityRequestId: number, requestId: string): void { + this._responseElement.classList.add('hide'); this._inlineChatWidget.updateChatMessage({ message: new MarkdownString(message), requestId, providerId: 'terminal' }); this._chatAccessibilityService.acceptResponse(message, accessibilityRequestId); } From bfc4df0453af87e35090ddb39e1a992c3fcf1eaa Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 14 Feb 2024 12:24:03 -0600 Subject: [PATCH 0064/1175] add show/hide commands for editor --- .../chat/browser/terminalChatWidget.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index b8c73ef1ffb..e522765f213 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -91,7 +91,7 @@ export class TerminalChatWidget extends Disposable { } renderTerminalCommand(codeBlock: string, requestId: number, shellType?: string): void { this._chatAccessibilityService.acceptResponse(codeBlock, requestId); - this._responseElement.classList.remove('hide'); + this.showTerminalCommandEditor(); if (!this._responseWidget) { this._responseWidget = this._register(this._scopedInstantiationService.createInstance(CodeEditorWidget, this._responseElement, { padding: { top: 2, bottom: 2 }, @@ -163,7 +163,7 @@ export class TerminalChatWidget extends Disposable { } renderMessage(message: string, accessibilityRequestId: number, requestId: string): void { - this._responseElement.classList.add('hide'); + this.hideTerminalCommandEditor(); this._inlineChatWidget.updateChatMessage({ message: new MarkdownString(message), requestId, providerId: 'terminal' }); this._chatAccessibilityService.acceptResponse(message, accessibilityRequestId); } @@ -183,7 +183,7 @@ export class TerminalChatWidget extends Disposable { this._inlineChatWidget.focus(); } hide(): void { - this._responseElement?.classList.add('hide'); + this.hideTerminalCommandEditor(); this._widgetContainer.classList.add('hide'); this._inlineChatWidget.value = ''; this._responseWidget?.setValue(''); @@ -207,7 +207,7 @@ export class TerminalChatWidget extends Disposable { setValue(value?: string) { this._inlineChatWidget.value = value ?? ''; if (!value) { - this._responseElement?.classList.add('hide'); + this.hideTerminalCommandEditor(); } } acceptCommand(): void { @@ -224,4 +224,10 @@ export class TerminalChatWidget extends Disposable { public get focusTracker(): IFocusTracker { return this._focusTracker; } + hideTerminalCommandEditor(): void { + this._responseElement.classList.add('hide'); + } + showTerminalCommandEditor(): void { + this._responseElement.classList.remove('hide'); + } } From 2b7e5e52d276a902db01ee05e7c9e157e6a0a265 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 14 Feb 2024 12:36:27 -0600 Subject: [PATCH 0065/1175] rename and reorder things in terminalChatWidget --- .../chat/browser/terminalChatWidget.ts | 106 +++++++++--------- 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index e522765f213..49eef7120fa 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -24,21 +24,24 @@ import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/termin import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, MENU_TERMINAL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; export class TerminalChatWidget extends Disposable { - private readonly _scopedInstantiationService: IInstantiationService; - private readonly _widgetContainer: HTMLElement; - private readonly _ctxChatWidgetFocused: IContextKey; - private readonly _ctxChatWidgetVisible: IContextKey; - private readonly _ctxResponseEditorFocused!: IContextKey; + + private readonly _container: HTMLElement; private readonly _inlineChatWidget: InlineChatWidget; - private readonly _responseElement: HTMLElement; + public get inlineChatWidget(): InlineChatWidget { return this._inlineChatWidget; } + + private readonly _terminalCommandWidgetContainer: HTMLElement; + private _terminalCommandWidget: CodeEditorWidget | undefined; + private readonly _focusTracker: IFocusTracker; - private _responseWidget: CodeEditorWidget | undefined; - public get inlineChatWidget(): InlineChatWidget { return this._inlineChatWidget; } + private readonly _scopedInstantiationService: IInstantiationService; + private readonly _focusedContextKey: IContextKey; + private readonly _visibleContextKey: IContextKey; + private readonly _responseEditorFocusedContextKey!: IContextKey; constructor( - private readonly _container: HTMLElement, + terminalElement: HTMLElement, private readonly _instance: ITerminalInstance, @IInstantiationService instantiationService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @@ -47,19 +50,19 @@ export class TerminalChatWidget extends Disposable { @IModelService private readonly _modelService: IModelService ) { super(); - const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._container)); + const scopedContextKeyService = this._register(this._contextKeyService.createScoped(terminalElement)); this._scopedInstantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); - this._ctxChatWidgetFocused = TerminalContextKeys.chatFocused.bindTo(this._contextKeyService); - this._ctxChatWidgetVisible = TerminalContextKeys.chatVisible.bindTo(this._contextKeyService); - this._ctxResponseEditorFocused = TerminalContextKeys.chatResponseEditorFocused.bindTo(this._contextKeyService); + this._focusedContextKey = TerminalContextKeys.chatFocused.bindTo(this._contextKeyService); + this._visibleContextKey = TerminalContextKeys.chatVisible.bindTo(this._contextKeyService); + this._responseEditorFocusedContextKey = TerminalContextKeys.chatResponseEditorFocused.bindTo(this._contextKeyService); - this._widgetContainer = document.createElement('div'); - this._widgetContainer.classList.add('terminal-inline-chat'); - this._container.appendChild(this._widgetContainer); + this._container = document.createElement('div'); + this._container.classList.add('terminal-inline-chat'); + terminalElement.appendChild(this._container); - this._responseElement = document.createElement('div'); - this._responseElement.classList.add('terminal-inline-chat-response'); - this._widgetContainer.prepend(this._responseElement); + this._terminalCommandWidgetContainer = document.createElement('div'); + this._terminalCommandWidgetContainer.classList.add('terminal-inline-chat-response'); + this._container.prepend(this._terminalCommandWidgetContainer); // The inline chat widget requires a parent editor that it bases the diff view on, since the // terminal doesn't use that feature we can just pass in an unattached editor instance. @@ -85,15 +88,16 @@ export class TerminalChatWidget extends Disposable { ); this._inlineChatWidget.placeholder = localize('default.placeholder', "Ask how to do something in the terminal"); this._inlineChatWidget.updateInfo(localize('welcome.1', "AI-generated commands may be incorrect")); - this._widgetContainer.appendChild(this._inlineChatWidget.domNode); + this._container.appendChild(this._inlineChatWidget.domNode); - this._focusTracker = this._register(trackFocus(this._widgetContainer)); + this._focusTracker = this._register(trackFocus(this._container)); } - renderTerminalCommand(codeBlock: string, requestId: number, shellType?: string): void { - this._chatAccessibilityService.acceptResponse(codeBlock, requestId); - this.showTerminalCommandEditor(); - if (!this._responseWidget) { - this._responseWidget = this._register(this._scopedInstantiationService.createInstance(CodeEditorWidget, this._responseElement, { + + renderTerminalCommand(command: string, requestId: number, shellType?: string): void { + this._chatAccessibilityService.acceptResponse(command, requestId); + this.showTerminalCommandWidget(); + if (!this._terminalCommandWidget) { + this._terminalCommandWidget = this._register(this._scopedInstantiationService.createInstance(CodeEditorWidget, this._terminalCommandWidgetContainer, { padding: { top: 2, bottom: 2 }, overviewRulerLanes: 0, glyphMargin: false, @@ -128,21 +132,21 @@ export class TerminalChatWidget extends Disposable { showStatusBar: false, }, }, { isSimpleWidget: true })); - this._register(this._responseWidget.onDidFocusEditorText(() => this._ctxResponseEditorFocused.set(true))); - this._register(this._responseWidget.onDidBlurEditorText(() => this._ctxResponseEditorFocused.set(false))); - this._getTextModel(URI.from({ path: `terminal-inline-chat-${this._instance.instanceId}`, scheme: 'terminal-inline-chat', fragment: codeBlock })).then((model) => { - if (!model || !this._responseWidget) { + this._register(this._terminalCommandWidget.onDidFocusEditorText(() => this._responseEditorFocusedContextKey.set(true))); + this._register(this._terminalCommandWidget.onDidBlurEditorText(() => this._responseEditorFocusedContextKey.set(false))); + this._getTextModel(URI.from({ path: `terminal-inline-chat-${this._instance.instanceId}`, scheme: 'terminal-inline-chat', fragment: command })).then((model) => { + if (!model || !this._terminalCommandWidget) { return; } - this._responseWidget.layout(new Dimension(400, 0)); - this._responseWidget.setModel(model); - const height = this._responseWidget.getContentHeight(); - this._responseWidget.layout(new Dimension(400, height)); + this._terminalCommandWidget.layout(new Dimension(400, 0)); + this._terminalCommandWidget.setModel(model); + const height = this._terminalCommandWidget.getContentHeight(); + this._terminalCommandWidget.layout(new Dimension(400, height)); }); } else { - this._responseWidget.setValue(codeBlock); + this._terminalCommandWidget.setValue(command); } - this._responseWidget.getModel()?.setLanguage(this._getLanguageFromShell(shellType)); + this._terminalCommandWidget.getModel()?.setLanguage(this._getLanguageFromShell(shellType)); } private _getLanguageFromShell(shell?: string): string { @@ -163,7 +167,7 @@ export class TerminalChatWidget extends Disposable { } renderMessage(message: string, accessibilityRequestId: number, requestId: string): void { - this.hideTerminalCommandEditor(); + this.hideTerminalCommandWidget(); this._inlineChatWidget.updateChatMessage({ message: new MarkdownString(message), requestId, providerId: 'terminal' }); this._chatAccessibilityService.acceptResponse(message, accessibilityRequestId); } @@ -177,18 +181,18 @@ export class TerminalChatWidget extends Disposable { } reveal(): void { this._inlineChatWidget.layout(new Dimension(400, 150)); - this._widgetContainer.classList.remove('hide'); - this._ctxChatWidgetFocused.set(true); - this._ctxChatWidgetVisible.set(true); + this._container.classList.remove('hide'); + this._focusedContextKey.set(true); + this._visibleContextKey.set(true); this._inlineChatWidget.focus(); } hide(): void { - this.hideTerminalCommandEditor(); - this._widgetContainer.classList.add('hide'); + this.hideTerminalCommandWidget(); + this._container.classList.add('hide'); this._inlineChatWidget.value = ''; - this._responseWidget?.setValue(''); - this._ctxChatWidgetFocused.set(false); - this._ctxChatWidgetVisible.set(false); + this._terminalCommandWidget?.setValue(''); + this._focusedContextKey.set(false); + this._visibleContextKey.set(false); this._instance.focus(); } cancel(): void { @@ -207,11 +211,11 @@ export class TerminalChatWidget extends Disposable { setValue(value?: string) { this._inlineChatWidget.value = value ?? ''; if (!value) { - this.hideTerminalCommandEditor(); + this.hideTerminalCommandWidget(); } } acceptCommand(): void { - const value = this._responseWidget?.getValue(); + const value = this._terminalCommandWidget?.getValue(); if (!value) { return; } @@ -224,10 +228,10 @@ export class TerminalChatWidget extends Disposable { public get focusTracker(): IFocusTracker { return this._focusTracker; } - hideTerminalCommandEditor(): void { - this._responseElement.classList.add('hide'); + hideTerminalCommandWidget(): void { + this._terminalCommandWidgetContainer.classList.add('hide'); } - showTerminalCommandEditor(): void { - this._responseElement.classList.remove('hide'); + showTerminalCommandWidget(): void { + this._terminalCommandWidgetContainer.classList.remove('hide'); } } From 1565341d4496a71da7860467d3ea32a72103dbab Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 14 Feb 2024 12:50:49 -0600 Subject: [PATCH 0066/1175] reset everything on hide of widget --- .../terminalContrib/chat/browser/terminalChatController.ts | 1 - .../terminalContrib/chat/browser/terminalChatWidget.ts | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index da56f4d4041..a5d73cd459a 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -169,7 +169,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (progress.kind === 'content' || progress.kind === 'markdownContent') { responseContent += progress.content; } - this._chatWidget?.rawValue?.updateProgress(progress); }; const requestId = generateUuid(); const requestProps: IChatAgentRequest = { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 49eef7120fa..a485dd40c4f 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -191,6 +191,10 @@ export class TerminalChatWidget extends Disposable { this._container.classList.add('hide'); this._inlineChatWidget.value = ''; this._terminalCommandWidget?.setValue(''); + this._inlineChatWidget.updateChatMessage(undefined); + this._inlineChatWidget.updateFollowUps(undefined); + this._inlineChatWidget.updateProgress(false); + this._inlineChatWidget.updateToolbar(false); this._focusedContextKey.set(false); this._visibleContextKey.set(false); this._instance.focus(); From 55aca1a766b3223d5bd2fa164cbb17ff54615b9f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 14 Feb 2024 13:06:47 -0600 Subject: [PATCH 0067/1175] more clean up --- .../terminal/common/terminalContextKey.ts | 1 + .../chat/browser/terminalChatController.ts | 46 ++++++++++--------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index 8ea1469a4b1..a7805bfac46 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -45,6 +45,7 @@ export const enum TerminalContextKeyStrings { ChatInputHasText = 'terminalChatInputHasText', ChatAgentRegistered = 'terminalChatAgentRegistered', ChatResponseEditorFocused = 'terminalChatResponseEditorFocused', + ChatLastResponseType = 'terminalChatLastResponseType' } export namespace TerminalContextKeys { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index a5d73cd459a..5c105fee353 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -21,9 +21,9 @@ import { marked } from 'vs/base/common/marked/marked'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; -import { CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { Emitter, Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; +import { CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; const enum Message { NONE = 0, @@ -48,22 +48,25 @@ export class TerminalChatController extends Disposable implements ITerminalContr */ static activeChatWidget?: TerminalChatController; private _chatWidget: Lazy | undefined; - private _accessibilityRequestId: number = 0; get chatWidget(): TerminalChatWidget | undefined { return this._chatWidget?.value; } - private readonly _ctxHasActiveRequest!: IContextKey; - private readonly _ctxHasTerminalAgent!: IContextKey; - private readonly _ctxLastResponseType!: IContextKey; + private readonly _requestActiveContextKey!: IContextKey; + private readonly _terminalAgentRegisteredContextKey!: IContextKey; + private readonly _lastResponseTypeContextKey!: IContextKey; private _cancellationTokenSource!: CancellationTokenSource; + private _accessibilityRequestId: number = 0; + private _messages = this._store.add(new Emitter()); + private _lastInput: string | undefined; + private _lastResponseContent: string | undefined; + readonly onDidAcceptInput = Event.filter(this._messages.event, m => m === Message.ACCEPT_INPUT, this._store); readonly onDidCancelInput = Event.filter(this._messages.event, m => m === Message.CANCEL_INPUT || m === Message.CANCEL_SESSION, this._store); - private _lastInput: string | undefined; - private _lastResponseContent: string | undefined; + private _terminalAgentId = 'terminal'; constructor( private readonly _instance: ITerminalInstance, @@ -82,18 +85,18 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { return; } - this._ctxHasActiveRequest = TerminalContextKeys.chatRequestActive.bindTo(this._contextKeyService); - this._ctxHasTerminalAgent = TerminalContextKeys.chatAgentRegistered.bindTo(this._contextKeyService); - this._ctxLastResponseType = CTX_INLINE_CHAT_RESPONSE_TYPES.bindTo(this._contextKeyService); + this._requestActiveContextKey = TerminalContextKeys.chatRequestActive.bindTo(this._contextKeyService); + this._terminalAgentRegisteredContextKey = TerminalContextKeys.chatAgentRegistered.bindTo(this._contextKeyService); + this._lastResponseTypeContextKey = CTX_INLINE_CHAT_RESPONSE_TYPES.bindTo(this._contextKeyService); - if (!this._chatAgentService.hasAgent('terminal')) { + if (!this._chatAgentService.hasAgent(this._terminalAgentId)) { this._register(this._chatAgentService.onDidChangeAgents(() => { - if (this._chatAgentService.getAgent('terminal')) { - this._ctxHasTerminalAgent.set(true); + if (this._chatAgentService.getAgent(this._terminalAgentId)) { + this._terminalAgentRegisteredContextKey.set(true); } })); } else { - this._ctxHasTerminalAgent.set(true); + this._terminalAgentRegisteredContextKey.set(true); } this._cancellationTokenSource = new CancellationTokenSource(); } @@ -157,9 +160,8 @@ export class TerminalChatController extends Disposable implements ITerminalContr return; } this._chatAccessibilityService.acceptRequest(); - this._ctxHasActiveRequest.set(true); + this._requestActiveContextKey.set(true); const cancellationToken = this._cancellationTokenSource.token; - const agentId = 'terminal'; let responseContent = ''; const progressCallback = (progress: IChatProgress) => { if (cancellationToken.isCancellationRequested) { @@ -174,12 +176,13 @@ export class TerminalChatController extends Disposable implements ITerminalContr const requestProps: IChatAgentRequest = { sessionId: generateUuid(), requestId, - agentId, + agentId: this._terminalAgentId, message: this._lastInput, + // TODO: ? variables: { variables: [] }, }; try { - const task = this._chatAgentService.invokeAgent(agentId, requestProps, progressCallback, [], cancellationToken); + const task = this._chatAgentService.invokeAgent(this._terminalAgentId, requestProps, progressCallback, [], cancellationToken); this._chatWidget?.rawValue?.inlineChatWidget.updateChatMessage(undefined); this._chatWidget?.rawValue?.inlineChatWidget.updateFollowUps(undefined); this._chatWidget?.rawValue?.inlineChatWidget.updateProgress(true); @@ -189,7 +192,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr } catch (e) { } finally { - this._ctxHasActiveRequest.set(false); + this._requestActiveContextKey.set(false); this._chatWidget?.rawValue?.inlineChatWidget.updateProgress(false); this._chatWidget?.rawValue?.inlineChatWidget.updateInfo(''); this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); @@ -205,12 +208,11 @@ export class TerminalChatController extends Disposable implements ITerminalContr return; } if (codeBlock) { - // TODO: check the SR experience this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._accessibilityRequestId, shellType); - this._ctxLastResponseType.set(InlineChatResponseTypes.Empty); + this._lastResponseTypeContextKey.set(InlineChatResponseTypes.Empty); } else { this._chatWidget?.rawValue?.renderMessage(responseContent, this._accessibilityRequestId, requestId); - this._ctxLastResponseType.set(InlineChatResponseTypes.OnlyMessages); + this._lastResponseTypeContextKey.set(InlineChatResponseTypes.OnlyMessages); } this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); this._messages.fire(Message.ACCEPT_INPUT); From 57857d6546a713f47a0639a55db18ea4224edf54 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 14 Feb 2024 13:22:39 -0600 Subject: [PATCH 0068/1175] rm unused --- src/vs/workbench/contrib/terminal/common/terminalContextKey.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index a7805bfac46..8ea1469a4b1 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -45,7 +45,6 @@ export const enum TerminalContextKeyStrings { ChatInputHasText = 'terminalChatInputHasText', ChatAgentRegistered = 'terminalChatAgentRegistered', ChatResponseEditorFocused = 'terminalChatResponseEditorFocused', - ChatLastResponseType = 'terminalChatLastResponseType' } export namespace TerminalContextKeys { From a9b4e6fe4446bd2e90c5507e0d32f08e196b292c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 14 Feb 2024 13:43:46 -0600 Subject: [PATCH 0069/1175] add accessible view --- .../browser/accessibilityConfiguration.ts | 8 +++- .../terminal/common/terminalContextKey.ts | 3 ++ .../browser/terminal.chat.contribution.ts | 6 +++ .../browser/terminalChatAccessibleView.ts | 41 +++++++++++++++++++ .../chat/browser/terminalChatController.ts | 1 + 5 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index afd920ac1f9..a5d30356fed 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -52,7 +52,8 @@ export const enum AccessibilityVerbositySettingId { Hover = 'accessibility.verbosity.hover', Notification = 'accessibility.verbosity.notification', EmptyEditorHint = 'accessibility.verbosity.emptyEditorHint', - Comments = 'accessibility.verbosity.comments' + Comments = 'accessibility.verbosity.comments', + TerminalInlineChat = 'accessibility.verbosity.terminalInlineChat' } export const enum AccessibleViewProviderId { @@ -62,6 +63,7 @@ export const enum AccessibleViewProviderId { Chat = 'panelChat', InlineChat = 'inlineChat', InlineCompletions = 'inlineCompletions', + TerminalInlineChat = 'terminalInlineChat', KeybindingsEditor = 'keybindingsEditor', Notebook = 'notebook', Editor = 'editor', @@ -168,6 +170,10 @@ const configuration: IConfigurationNode = { description: localize('verbosity.comments', 'Provide information about actions that can be taken in the comment widget or in a file which contains comments.'), ...baseVerbosityProperty }, + [AccessibilityVerbositySettingId.TerminalInlineChat]: { + description: localize('verbosity.terminalInlineChat', 'Provide information about actions that can be taken in the terminal inline chat widget.'), + ...baseVerbosityProperty + }, [AccessibilityAlertSettingId.Save]: { 'markdownDescription': localize('announcement.save', "Indicates when a file is saved. Also see {0}.", '`#audioCues.save#`'), 'enum': ['userGesture', 'always', 'never'], diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index 8ea1469a4b1..5ac4cdca5ac 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -183,4 +183,7 @@ export namespace TerminalContextKeys { /** Whether the chat response editor is focused */ export const chatResponseEditorFocused = new RawContextKey(TerminalContextKeyStrings.ChatResponseEditorFocused, false, localize('chatResponseEditorFocusedContextKey', "Whether the chat response editor is focused.")); + + /** Whether the chat response editor is focused */ + export const chatResponseMessageFocused = new RawContextKey(TerminalContextKeyStrings.ChatResponseEditorFocused, false, localize('chatResponseMessageFocusedContextKey', "Whether the chat response message is focused.")); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index a3a7b55757a..eebaa8b3b8a 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -5,7 +5,13 @@ import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; registerTerminalContribution(TerminalChatController.ID, TerminalChatController, false); import 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { TerminalInlineChatAccessibleViewContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView'; +const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchContributionsRegistry.registerWorkbenchContribution(TerminalInlineChatAccessibleViewContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts new file mode 100644 index 00000000000..949a2d8cc51 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; +import { AccessibleViewAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; +import { CTX_INLINE_CHAT_RESPONSE_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; + +export class TerminalInlineChatAccessibleViewContribution extends Disposable { + static ID: 'terminalInlineChatAccessibleViewContribution'; + constructor() { + super(); + this._register(AccessibleViewAction.addImplementation(105, 'terminalInlineChat', accessor => { + const accessibleViewService = accessor.get(IAccessibleViewService); + const terminalService = accessor.get(ITerminalService); + const controller: TerminalChatController | undefined = terminalService.activeInstance?.getContribution(TerminalChatController.ID) ?? undefined; + if (!controller?.lastResponseContent) { + return false; + } + const responseContent = controller.lastResponseContent; + accessibleViewService.show({ + id: AccessibleViewProviderId.TerminalInlineChat, + verbositySettingKey: AccessibilityVerbositySettingId.TerminalInlineChat, + provideContent(): string { return responseContent; }, + onClose() { + controller.focus(); + }, + + options: { type: AccessibleViewType.View } + }); + return true; + }, ContextKeyExpr.and(TerminalContextKeys.chatFocused, CTX_INLINE_CHAT_RESPONSE_FOCUSED))); + } +} diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 5c105fee353..515d7b895e8 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -62,6 +62,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr private _lastInput: string | undefined; private _lastResponseContent: string | undefined; + get lastResponseContent(): string | undefined { return this._lastResponseContent; } readonly onDidAcceptInput = Event.filter(this._messages.event, m => m === Message.ACCEPT_INPUT, this._store); readonly onDidCancelInput = Event.filter(this._messages.event, m => m === Message.CANCEL_INPUT || m === Message.CANCEL_SESSION, this._store); From d881ddbae52bec5cdc5e87351d6c3f8f7fa5c67f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 14 Feb 2024 15:30:36 -0600 Subject: [PATCH 0070/1175] Add accessibility help dialog --- .../browser/accessibilityConfiguration.ts | 5 -- .../inlineChat/browser/inlineChatActions.ts | 3 +- .../terminal/common/terminalContextKey.ts | 3 - .../browser/terminal.chat.contribution.ts | 2 + .../chat/browser/terminalChat.ts | 2 +- .../browser/terminalChatAccessibilityHelp.ts | 64 +++++++++++++++++++ .../browser/terminalChatAccessibleView.ts | 3 +- .../chat/browser/terminalChatActions.ts | 14 ++-- 8 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index a5d30356fed..bba5a1f648e 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -53,7 +53,6 @@ export const enum AccessibilityVerbositySettingId { Notification = 'accessibility.verbosity.notification', EmptyEditorHint = 'accessibility.verbosity.emptyEditorHint', Comments = 'accessibility.verbosity.comments', - TerminalInlineChat = 'accessibility.verbosity.terminalInlineChat' } export const enum AccessibleViewProviderId { @@ -170,10 +169,6 @@ const configuration: IConfigurationNode = { description: localize('verbosity.comments', 'Provide information about actions that can be taken in the comment widget or in a file which contains comments.'), ...baseVerbosityProperty }, - [AccessibilityVerbositySettingId.TerminalInlineChat]: { - description: localize('verbosity.terminalInlineChat', 'Provide information about actions that can be taken in the terminal inline chat widget.'), - ...baseVerbosityProperty - }, [AccessibilityAlertSettingId.Save]: { 'markdownDescription': localize('announcement.save', "Indicates when a file is saved. Also see {0}.", '`#audioCues.save#`'), 'enum': ['userGesture', 'always', 'never'], diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 85033c64166..02cf92cfa93 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -30,6 +30,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; CommandsRegistry.registerCommandAlias('interactiveEditor.start', 'inlineChat.start'); CommandsRegistry.registerCommandAlias('interactive.acceptChanges', ACTION_ACCEPT_CHANGES); @@ -745,6 +746,6 @@ export class InlineAccessibilityHelpContribution extends Disposable { return; } runAccessibilityHelpAction(accessor, codeEditor, 'inlineChat'); - }, ContextKeyExpr.or(CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_FOCUSED))); + }, ContextKeyExpr.and(ContextKeyExpr.or(CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_FOCUSED), TerminalContextKeys.chatFocused.negate()))); } } diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index 5ac4cdca5ac..8ea1469a4b1 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -183,7 +183,4 @@ export namespace TerminalContextKeys { /** Whether the chat response editor is focused */ export const chatResponseEditorFocused = new RawContextKey(TerminalContextKeyStrings.ChatResponseEditorFocused, false, localize('chatResponseEditorFocusedContextKey', "Whether the chat response editor is focused.")); - - /** Whether the chat response editor is focused */ - export const chatResponseMessageFocused = new RawContextKey(TerminalContextKeyStrings.ChatResponseEditorFocused, false, localize('chatResponseMessageFocusedContextKey', "Whether the chat response message is focused.")); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index eebaa8b3b8a..70804842c3e 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -13,5 +13,7 @@ import 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions'; import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { TerminalInlineChatAccessibleViewContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView'; +import { TerminalInlineChatAccessibilityHelpContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp'; const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchContributionsRegistry.registerWorkbenchContribution(TerminalInlineChatAccessibleViewContribution, LifecyclePhase.Eventually); +workbenchContributionsRegistry.registerWorkbenchContribution(TerminalInlineChatAccessibilityHelpContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts index ddea1c2a53b..f75c9c3d9a7 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts @@ -6,7 +6,7 @@ import { MenuId } from 'vs/platform/actions/common/actions'; export const enum TerminalChatCommandId { - Focus = 'workbench.action.terminal.chat.focus', + Start = 'workbench.action.terminal.chat.start', Hide = 'workbench.action.terminal.chat.close', MakeRequest = 'workbench.action.terminal.chat.makeRequest', Cancel = 'workbench.action.terminal.chat.cancel', diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts new file mode 100644 index 00000000000..5bbe76db85f --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { localize } from 'vs/nls'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; +import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; +import { CTX_INLINE_CHAT_RESPONSE_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { TerminalChatCommandId } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; +import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; + +export class TerminalInlineChatAccessibilityHelpContribution extends Disposable { + constructor() { + super(); + this._register(AccessibilityHelpAction.addImplementation(106, 'terminalInlineChat', accessor => { + const terminalService = accessor.get(ITerminalService); + const accessibleViewService = accessor.get(IAccessibleViewService); + const controller: TerminalChatController | undefined = terminalService.activeInstance?.getContribution(TerminalChatController.ID) ?? undefined; + if (controller === undefined) { + return false; + } + const helpContent = getAccessibilityHelpText(accessor); + accessibleViewService.show({ + id: AccessibleViewProviderId.TerminalInlineChat, + verbositySettingKey: AccessibilityVerbositySettingId.InlineChat, + provideContent(): string { return helpContent; }, + onClose() { + controller.focus(); + }, + options: { type: AccessibleViewType.Help } + }); + return true; + }, ContextKeyExpr.or(TerminalContextKeys.chatFocused, TerminalContextKeys.chatResponseEditorFocused, CTX_INLINE_CHAT_RESPONSE_FOCUSED))); + } +} + + +export function getAccessibilityHelpText(accessor: ServicesAccessor): string { + const keybindingService = accessor.get(IKeybindingService); + const content = []; + const openAccessibleViewKeybinding = keybindingService.lookupKeybinding('editor.action.accessibleView')?.getAriaLabel(); + const acceptCommandKeybinding = keybindingService.lookupKeybinding(TerminalChatCommandId.AcceptCommand)?.getAriaLabel(); + const makeRequestKeybinding = keybindingService.lookupKeybinding(TerminalChatCommandId.MakeRequest)?.getAriaLabel(); + //TODO: using this instead of the terminal command bc by definition the inline terminal chat is focused when this dialog is invoked. + const startChatKeybinding = keybindingService.lookupKeybinding('inlineChat.start')?.getAriaLabel(); + content.push(localize('inlineChat.overview', "Inline chat occurs within a terminal. It is useful for suggesting terminal commands. Keep in mind that AI generated code may be incorrect.")); + content.push(localize('inlineChat.access', "It can be activated using the command: Terminal: Start Chat ({0}), which will focus the input box.", startChatKeybinding)); + content.push(makeRequestKeybinding ? localize('inlineChat.input', "The input box is where the user can type a request and can make the request ({0}). The widget will be closed and all content will be discarded when the Escape key is pressed and the terminal will regain focus.", makeRequestKeybinding) : localize('inlineChat.inputNoKb', "The input box is where the user can type a request and can make the request by tabbing to the Make Request button, which is not currently triggerable via keybindings. The widget will be closed and all content will be discarded when the Escape key is pressed and the terminal will regain focus.")); + content.push(localize('inlineChat.results', "A result may contain a terminal command or just a message. In either case, the result will be announced.")); + content.push(openAccessibleViewKeybinding ? localize('inlineChat.inspectResponseMessage', 'If just a message comes back, it can be inspected in the accessible view ({0}).', openAccessibleViewKeybinding) : localize('inlineChat.inspectResponseNoKb', 'With the input box focused, inspect the response in the accessible view via the Open Accessible View command, which is currently not triggerable by a keybinding.')); + content.push(localize('inlineChat.inspectTerminalCommand', 'If a terminal command comes back, it can be inspected in an editor reached via Shift+Tab.')); + content.push(acceptCommandKeybinding ? localize('inlineChat.acceptCommand', 'With focus in the command editor, the Terminal: Accept Chat Command ({0}) action.', acceptCommandKeybinding) : localize('inlineChat.acceptCommandNoKb', 'Accept a command by tabbing to the button as the action is currently not triggerable by a keybinding.')); + content.push(localize('inlineChat.toolbar', "Use tab to reach conditional parts like commands, status, message responses and more.")); + content.push(localize('chat.signals', "Accessibility Signals can be changed via settings with a prefix of signals.chat. By default, if a request takes more than 4 seconds, you will hear a sound indicating that progress is still occurring.")); + return content.join('\n\n'); +} diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts index 949a2d8cc51..8c146698482 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts @@ -27,12 +27,11 @@ export class TerminalInlineChatAccessibleViewContribution extends Disposable { const responseContent = controller.lastResponseContent; accessibleViewService.show({ id: AccessibleViewProviderId.TerminalInlineChat, - verbositySettingKey: AccessibilityVerbositySettingId.TerminalInlineChat, + verbositySettingKey: AccessibilityVerbositySettingId.InlineChat, provideContent(): string { return responseContent; }, onClose() { controller.focus(); }, - options: { type: AccessibleViewType.View } }); return true; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index d6ba18a272e..641737229dc 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -17,8 +17,8 @@ import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; registerActiveXtermAction({ - id: TerminalChatCommandId.Focus, - title: localize2('focusChat', 'Focus Chat'), + id: TerminalChatCommandId.Start, + title: localize2('startChat', 'Terminal: Start Chat'), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyI, when: ContextKeyExpr.and(TerminalContextKeys.chatFocused.negate(), TerminalContextKeys.focusInAny), @@ -40,7 +40,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.Hide, - title: localize2('closeChat', 'Close Chat'), + title: localize2('closeChat', 'Terminal: Close Chat'), keybinding: { primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], @@ -72,7 +72,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.AcceptCommand, - title: localize2('acceptCommand', 'Accept Command'), + title: localize2('acceptCommand', 'Terminal: Accept Chat Command'), shortTitle: localize2('accept', 'Accept'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), @@ -104,7 +104,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.ViewInChat, - title: localize2('viewInChat', 'View in Chat'), + title: localize2('viewInChat', 'Terminal: View in Chat'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), @@ -130,7 +130,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.MakeRequest, - title: localize2('makeChatRequest', 'Make Chat Request'), + title: localize2('makeChatRequest', 'Terminal: Make Chat Request'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), @@ -161,7 +161,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.Cancel, - title: localize2('cancelChat', 'Cancel Chat'), + title: localize2('cancelChat', 'Terminal: Cancel Chat'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), TerminalContextKeys.chatRequestActive, From b01c303b90fe931c2f4e816172fa8fdaf13ee963 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 10:50:32 -0600 Subject: [PATCH 0071/1175] add session support, get cancellation to work --- .../inlineChat/browser/inlineChatSession.ts | 9 +- .../chat/browser/terminalChatActions.ts | 4 +- .../chat/browser/terminalChatController.ts | 107 ++++++++++++++---- .../chat/browser/terminalChatWidget.ts | 17 +-- 4 files changed, 96 insertions(+), 41 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index 693fde05bab..678c8177976 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -316,7 +316,7 @@ export class SessionExchange { constructor( readonly prompt: SessionPrompt, - readonly response: ReplyResponse | EmptyResponse | ErrorResponse + readonly response: ReplyResponse | EmptyResponse | ErrorResponse | TerminalResponse ) { } } @@ -324,6 +324,13 @@ export class EmptyResponse { } +export class TerminalResponse { + readonly message: string; + constructor(message: string) { + this.message = message; + } +} + export class ErrorResponse { readonly message: string; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 641737229dc..7aef66f5631 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -178,7 +178,7 @@ registerActiveXtermAction({ return; } const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); - contr?.chatWidget?.cancel(); + contr?.cancel(); } }); @@ -234,7 +234,7 @@ registerActiveXtermAction({ // TODO: precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.Empty)), icon: Codicon.report, menu: [/*{ - // TODO: Enable this + // TODO: Enable id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, when: ContextKeyExpr.and(CTX_TERMINAL_CHAT_SUPPORT_ISSUE_REPORTING, CTX_TERMINAL_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.Empty)), group: '2_feedback', diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 515d7b895e8..944fba8dbba 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -16,14 +16,24 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { IChatProgress, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { generateUuid } from 'vs/base/common/uuid'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { marked } from 'vs/base/common/marked/marked'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { Emitter, Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; -import { CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { CTX_INLINE_CHAT_RESPONSE_TYPES, EditMode, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { EmptyResponse, Session, SessionExchange, SessionPrompt, TerminalResponse } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { assertType } from 'vs/base/common/types'; +import { IModelService } from 'vs/editor/common/services/model'; +import { ITextModel } from 'vs/editor/common/model'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; const enum Message { NONE = 0, @@ -54,15 +64,17 @@ export class TerminalChatController extends Disposable implements ITerminalContr private readonly _terminalAgentRegisteredContextKey!: IContextKey; private readonly _lastResponseTypeContextKey!: IContextKey; - private _cancellationTokenSource!: CancellationTokenSource; + private _scopedInstantiationService: IInstantiationService | undefined; private _accessibilityRequestId: number = 0; private _messages = this._store.add(new Emitter()); - private _lastInput: string | undefined; - private _lastResponseContent: string | undefined; - get lastResponseContent(): string | undefined { return this._lastResponseContent; } + private _activeSession?: Session; + + private _fakeEditor: CodeEditorWidget | undefined; + + get lastResponseContent(): string | undefined { return (this._activeSession?.lastExchange?.response as TerminalResponse).message; } readonly onDidAcceptInput = Event.filter(this._messages.event, m => m === Message.ACCEPT_INPUT, this._store); readonly onDidCancelInput = Event.filter(this._messages.event, m => m === Message.CANCEL_INPUT || m === Message.CANCEL_SESSION, this._store); @@ -80,7 +92,9 @@ export class TerminalChatController extends Disposable implements ITerminalContr @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService, @IChatService private readonly _chatService: IChatService, - @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService + @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, + @IInlineChatSessionService private readonly _inlineChatSessionService: IInlineChatSessionService, + @IModelService private readonly _modelService: IModelService ) { super(); if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { @@ -99,7 +113,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr } else { this._terminalAgentRegisteredContextKey.set(true); } - this._cancellationTokenSource = new CancellationTokenSource(); } xtermReady(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { @@ -107,7 +120,25 @@ export class TerminalChatController extends Disposable implements ITerminalContr return; } this._chatWidget = new Lazy(() => { - const chatWidget = this._instantiationService.createInstance(TerminalChatWidget, this._instance.domElement!, this._instance); + const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._instance.domElement!)); + this._scopedInstantiationService = this._instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); + // The inline chat widget requires a parent editor that it bases the diff view on, since the + // terminal doesn't use that feature we can just pass in an unattached editor instance. + const fakeParentEditorElement = document.createElement('div'); + this._fakeEditor = this._scopedInstantiationService.createInstance( + CodeEditorWidget, + fakeParentEditorElement, + { + extraEditorClassName: 'ignore-panel-bg' + }, + { isSimpleWidget: true } + ); + + const path = `terminal-chat-input-${this._instance.instanceId}`; + const inputUri = URI.from({ path: path, scheme: Schemas.untitled, fragment: '' }); + const result: ITextModel = this._modelService.createModel('', null, inputUri, false); + this._fakeEditor.setModel(result); + const chatWidget = this._instantiationService.createInstance(TerminalChatWidget, this._instance.domElement!, this._fakeEditor!, this._instance); chatWidget.focusTracker.onDidFocus(() => { TerminalChatController.activeChatWidget = this; if (!isDetachedTerminalInstance(this._instance)) { @@ -127,7 +158,28 @@ export class TerminalChatController extends Disposable implements ITerminalContr } cancel(): void { - this._cancellationTokenSource.cancel(); + if (this._activeSession) { + this._inlineChatSessionService.releaseSession(this._activeSession); + this._activeSession = undefined; + } + } + + private async _startSession(editor: IActiveCodeEditor, token: CancellationToken) { + if (this._activeSession) { + this._inlineChatSessionService.releaseSession(this._activeSession); + } + + const session = await this._inlineChatSessionService.createSession( + editor, + { editMode: EditMode.Live }, + token + ); + + if (!session) { + return; + } + + this._activeSession = session; } private _forcedPlaceholder: string | undefined = undefined; @@ -156,13 +208,15 @@ export class TerminalChatController extends Disposable implements ITerminalContr } async acceptInput(): Promise { - this._lastInput = this._chatWidget?.rawValue?.input(); - if (!this._lastInput) { - return; - } this._chatAccessibilityService.acceptRequest(); this._requestActiveContextKey.set(true); - const cancellationToken = this._cancellationTokenSource.token; + const cancellationToken = new CancellationTokenSource().token; + if (this._fakeEditor?.hasModel()) { + await this._startSession(this._fakeEditor, cancellationToken); + } + assertType(this._activeSession); + const inputValue = this.chatWidget!.inlineChatWidget.value; + this._activeSession!.addInput(new SessionPrompt(inputValue)); let responseContent = ''; const progressCallback = (progress: IChatProgress) => { if (cancellationToken.isCancellationRequested) { @@ -178,10 +232,11 @@ export class TerminalChatController extends Disposable implements ITerminalContr sessionId: generateUuid(), requestId, agentId: this._terminalAgentId, - message: this._lastInput, + message: inputValue, // TODO: ? variables: { variables: [] }, }; + let response: EmptyResponse | TerminalResponse = EmptyResponse; try { const task = this._chatAgentService.invokeAgent(this._terminalAgentId, requestProps, progressCallback, [], cancellationToken); this._chatWidget?.rawValue?.inlineChatWidget.updateChatMessage(undefined); @@ -191,14 +246,16 @@ export class TerminalChatController extends Disposable implements ITerminalContr // TODO: this._zone.value.widget.updateInfo(!this._session.lastExchange ? localize('thinking', "Thinking\u2026") : ''); await task; } catch (e) { - + response = e; } finally { this._requestActiveContextKey.set(false); this._chatWidget?.rawValue?.inlineChatWidget.updateProgress(false); this._chatWidget?.rawValue?.inlineChatWidget.updateInfo(''); this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); + if (response === EmptyResponse) { + response = new TerminalResponse(responseContent); + } } - this._lastResponseContent = responseContent; const firstCodeBlockContent = marked.lexer(responseContent).filter(token => token.type === 'code')?.[0]?.raw; const regex = /```(?\w+)\n(?[\s\S]*?)```/g; const match = regex.exec(firstCodeBlockContent); @@ -215,6 +272,9 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.renderMessage(responseContent, this._accessibilityRequestId, requestId); this._lastResponseTypeContextKey.set(InlineChatResponseTypes.OnlyMessages); } + if (this._activeSession?.lastInput) { + this._activeSession.addExchange(new SessionExchange(this._activeSession.lastInput, response)); + } this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); this._messages.fire(Message.ACCEPT_INPUT); } @@ -250,12 +310,15 @@ export class TerminalChatController extends Disposable implements ITerminalContr } async viewInChat(): Promise { - if (!this._lastInput || !this._lastResponseContent) { - return; - } const widget = await this._chatWidgetService.revealViewForProvider('copilot'); if (widget && widget.viewModel) { - this._chatService.addCompleteRequest(widget.viewModel.sessionId, this._lastInput, undefined, { message: this._lastResponseContent }); + const request = this._activeSession?.lastExchange; + const input = request?.prompt.value; + const response = request?.response as TerminalResponse; + if (!input || !response) { + return; + } + this._chatService.addCompleteRequest(widget.viewModel.sessionId, input, undefined, response); widget.focusLastMessage(); } } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index a485dd40c4f..bbe822e01a6 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -42,6 +42,7 @@ export class TerminalChatWidget extends Disposable { constructor( terminalElement: HTMLElement, + fakeParentEditor: CodeEditorWidget, private readonly _instance: ITerminalInstance, @IInstantiationService instantiationService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @@ -64,18 +65,6 @@ export class TerminalChatWidget extends Disposable { this._terminalCommandWidgetContainer.classList.add('terminal-inline-chat-response'); this._container.prepend(this._terminalCommandWidgetContainer); - // The inline chat widget requires a parent editor that it bases the diff view on, since the - // terminal doesn't use that feature we can just pass in an unattached editor instance. - const fakeParentEditorElement = document.createElement('div'); - const fakeParentEditor = this._scopedInstantiationService.createInstance( - CodeEditorWidget, - fakeParentEditorElement, - { - extraEditorClassName: 'ignore-panel-bg' - }, - { isSimpleWidget: true } - ); - this._inlineChatWidget = this._scopedInstantiationService.createInstance( InlineChatWidget, fakeParentEditor, @@ -199,10 +188,6 @@ export class TerminalChatWidget extends Disposable { this._visibleContextKey.set(false); this._instance.focus(); } - cancel(): void { - // TODO: Impl - this._inlineChatWidget.value = ''; - } focus(): void { this._inlineChatWidget.focus(); } From 216b941cd96e4022e50b7c92d446d090c64379ae Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 10:28:12 -0800 Subject: [PATCH 0072/1175] Move to registerWorkbenchContribution2 --- .../chat/browser/terminal.chat.contribution.ts | 16 +++++++--------- .../browser/terminalChatAccessibilityHelp.ts | 1 + 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index 70804842c3e..0c2dba50035 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -3,17 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { WorkbenchPhase, registerWorkbenchContribution2 } from 'vs/workbench/common/contributions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; +import { TerminalInlineChatAccessibilityHelpContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp'; +import { TerminalInlineChatAccessibleViewContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; + +import 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions'; registerTerminalContribution(TerminalChatController.ID, TerminalChatController, false); -import 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { TerminalInlineChatAccessibleViewContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView'; -import { TerminalInlineChatAccessibilityHelpContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp'; -const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(TerminalInlineChatAccessibleViewContribution, LifecyclePhase.Eventually); -workbenchContributionsRegistry.registerWorkbenchContribution(TerminalInlineChatAccessibilityHelpContribution, LifecyclePhase.Eventually); +registerWorkbenchContribution2(TerminalInlineChatAccessibleViewContribution.ID, TerminalInlineChatAccessibleViewContribution, WorkbenchPhase.Eventually); +registerWorkbenchContribution2(TerminalInlineChatAccessibilityHelpContribution.ID, TerminalInlineChatAccessibilityHelpContribution, WorkbenchPhase.Eventually); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts index 5bbe76db85f..2fe29ea5d8a 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts @@ -18,6 +18,7 @@ import { TerminalChatCommandId } from 'vs/workbench/contrib/terminalContrib/chat import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; export class TerminalInlineChatAccessibilityHelpContribution extends Disposable { + static ID: 'terminalInlineChatAccessibilityHelpContribution'; constructor() { super(); this._register(AccessibilityHelpAction.addImplementation(106, 'terminalInlineChat', accessor => { From 58f5274002e29d30dba54da2ac53a4dc8c5a0b3b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 10:36:59 -0800 Subject: [PATCH 0073/1175] Remove scoped services, they were causing an error and breaking language highlighting --- .../chat/browser/terminalChatController.ts | 56 +++++++++---------- .../chat/browser/terminalChatWidget.ts | 12 ++-- 2 files changed, 31 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 944fba8dbba..86c17c99d22 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -3,37 +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 { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget'; -import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { Lazy } from 'vs/base/common/lazy'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; -import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; -import { IChatProgress, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; -import { generateUuid } from 'vs/base/common/uuid'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Lazy } from 'vs/base/common/lazy'; +import { Disposable } from 'vs/base/common/lifecycle'; import { marked } from 'vs/base/common/marked/marked'; -import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { Schemas } from 'vs/base/common/network'; +import { assertType } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; +import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { ITextModel } from 'vs/editor/common/model'; +import { IModelService } from 'vs/editor/common/services/model'; +import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; -import { Emitter, Event } from 'vs/base/common/event'; -import { localize } from 'vs/nls'; -import { CTX_INLINE_CHAT_RESPONSE_TYPES, EditMode, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; -import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatProgress, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { EmptyResponse, Session, SessionExchange, SessionPrompt, TerminalResponse } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; -import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { assertType } from 'vs/base/common/types'; -import { IModelService } from 'vs/editor/common/services/model'; -import { ITextModel } from 'vs/editor/common/model'; -import { Schemas } from 'vs/base/common/network'; -import { URI } from 'vs/base/common/uri'; +import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; +import { CTX_INLINE_CHAT_RESPONSE_TYPES, EditMode, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; +import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget'; const enum Message { NONE = 0, @@ -64,8 +63,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr private readonly _terminalAgentRegisteredContextKey!: IContextKey; private readonly _lastResponseTypeContextKey!: IContextKey; - private _scopedInstantiationService: IInstantiationService | undefined; - private _accessibilityRequestId: number = 0; private _messages = this._store.add(new Emitter()); @@ -97,6 +94,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr @IModelService private readonly _modelService: IModelService ) { super(); + if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { return; } @@ -120,12 +118,10 @@ export class TerminalChatController extends Disposable implements ITerminalContr return; } this._chatWidget = new Lazy(() => { - const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._instance.domElement!)); - this._scopedInstantiationService = this._instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); // The inline chat widget requires a parent editor that it bases the diff view on, since the // terminal doesn't use that feature we can just pass in an unattached editor instance. const fakeParentEditorElement = document.createElement('div'); - this._fakeEditor = this._scopedInstantiationService.createInstance( + this._fakeEditor = this._instantiationService.createInstance( CodeEditorWidget, fakeParentEditorElement, { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index bbe822e01a6..67d671aae28 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -15,7 +15,6 @@ import { IModelService } from 'vs/editor/common/services/model'; import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; @@ -35,7 +34,7 @@ export class TerminalChatWidget extends Disposable { private readonly _focusTracker: IFocusTracker; - private readonly _scopedInstantiationService: IInstantiationService; + // private readonly _scopedInstantiationService: IInstantiationService; private readonly _focusedContextKey: IContextKey; private readonly _visibleContextKey: IContextKey; private readonly _responseEditorFocusedContextKey!: IContextKey; @@ -44,15 +43,14 @@ export class TerminalChatWidget extends Disposable { terminalElement: HTMLElement, fakeParentEditor: CodeEditorWidget, private readonly _instance: ITerminalInstance, - @IInstantiationService instantiationService: IInstantiationService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService, @ILanguageService private readonly _languageService: ILanguageService, @IModelService private readonly _modelService: IModelService ) { super(); - const scopedContextKeyService = this._register(this._contextKeyService.createScoped(terminalElement)); - this._scopedInstantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); + this._focusedContextKey = TerminalContextKeys.chatFocused.bindTo(this._contextKeyService); this._visibleContextKey = TerminalContextKeys.chatVisible.bindTo(this._contextKeyService); this._responseEditorFocusedContextKey = TerminalContextKeys.chatResponseEditorFocused.bindTo(this._contextKeyService); @@ -65,7 +63,7 @@ export class TerminalChatWidget extends Disposable { this._terminalCommandWidgetContainer.classList.add('terminal-inline-chat-response'); this._container.prepend(this._terminalCommandWidgetContainer); - this._inlineChatWidget = this._scopedInstantiationService.createInstance( + this._inlineChatWidget = this._instantiationService.createInstance( InlineChatWidget, fakeParentEditor, { @@ -86,7 +84,7 @@ export class TerminalChatWidget extends Disposable { this._chatAccessibilityService.acceptResponse(command, requestId); this.showTerminalCommandWidget(); if (!this._terminalCommandWidget) { - this._terminalCommandWidget = this._register(this._scopedInstantiationService.createInstance(CodeEditorWidget, this._terminalCommandWidgetContainer, { + this._terminalCommandWidget = this._register(this._instantiationService.createInstance(CodeEditorWidget, this._terminalCommandWidgetContainer, { padding: { top: 2, bottom: 2 }, overviewRulerLanes: 0, glyphMargin: false, From 804a4816393bb289678a0457e8653ce72032d1b7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:00:18 -0800 Subject: [PATCH 0074/1175] Improve command suggestion presentation --- .../chat/browser/media/terminalChatWidget.css | 23 ++++++++++++++++--- .../chat/browser/terminalChatWidget.ts | 23 ++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css index 633b6778dfe..a916f2dd0d7 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css @@ -10,6 +10,23 @@ z-index: 100; height: auto !important; } -/* .terminal-inline-chat .inline-chat .body { - display: flex; -} */ + +.terminal-inline-chat .terminal-inline-chat-response { + border: 1px solid var(--vscode-input-border, transparent); + background-color: var(--vscode-interactive-result-editor-background-color); +} + +.terminal-inline-chat .terminal-inline-chat-response:has(.monaco-editor.focused) { + border-color: var(--vscode-focusBorder, transparent); +} + +.terminal-inline-chat .terminal-inline-chat-response, +.terminal-inline-chat .terminal-inline-chat-response .monaco-editor, +.terminal-inline-chat .terminal-inline-chat-response .monaco-editor .overflow-guard { + border-radius: 4px; +} + +.terminal-inline-chat .terminal-inline-chat-response { + margin: 0 0 8px 0; + padding-left: 8px; +} diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 67d671aae28..9f4f960ee5e 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -13,8 +13,10 @@ import { ILanguageService } from 'vs/editor/common/languages/language'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/model'; import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; @@ -34,7 +36,6 @@ export class TerminalChatWidget extends Disposable { private readonly _focusTracker: IFocusTracker; - // private readonly _scopedInstantiationService: IInstantiationService; private readonly _focusedContextKey: IContextKey; private readonly _visibleContextKey: IContextKey; private readonly _responseEditorFocusedContextKey!: IContextKey; @@ -44,6 +45,7 @@ export class TerminalChatWidget extends Disposable { fakeParentEditor: CodeEditorWidget, private readonly _instance: ITerminalInstance, @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IConfigurationService private readonly _configurationService: IConfigurationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService, @ILanguageService private readonly _languageService: ILanguageService, @@ -80,12 +82,25 @@ export class TerminalChatWidget extends Disposable { this._focusTracker = this._register(trackFocus(this._container)); } + + private _getAriaLabel(): string { + const verbose = this._configurationService.getValue(AccessibilityVerbositySettingId.Chat); + if (verbose) { + // TODO: Add verbose description + } + return localize('terminalChatInput', "Terminal Chat Input"); + } + renderTerminalCommand(command: string, requestId: number, shellType?: string): void { this._chatAccessibilityService.acceptResponse(command, requestId); this.showTerminalCommandWidget(); if (!this._terminalCommandWidget) { this._terminalCommandWidget = this._register(this._instantiationService.createInstance(CodeEditorWidget, this._terminalCommandWidgetContainer, { - padding: { top: 2, bottom: 2 }, + readOnly: false, + ariaLabel: this._getAriaLabel(), + fontSize: 13, + lineHeight: 20, + padding: { top: 8, bottom: 8 }, overviewRulerLanes: 0, glyphMargin: false, lineNumbers: 'off', @@ -133,7 +148,9 @@ export class TerminalChatWidget extends Disposable { } else { this._terminalCommandWidget.setValue(command); } - this._terminalCommandWidget.getModel()?.setLanguage(this._getLanguageFromShell(shellType)); + const languageId = this._getLanguageFromShell(shellType); + console.log('languageId', languageId); + this._terminalCommandWidget.getModel()?.setLanguage(languageId); } private _getLanguageFromShell(shell?: string): string { From bf87f8dc1e9afcef04eb8465f221628ee13645d3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 12:20:22 -0600 Subject: [PATCH 0075/1175] Revert "add session support, get cancellation to work" This reverts commit b01c303b90fe931c2f4e816172fa8fdaf13ee963. --- .../inlineChat/browser/inlineChatSession.ts | 9 +- .../chat/browser/terminalChatActions.ts | 4 +- .../chat/browser/terminalChatController.ts | 102 +++++++----------- .../chat/browser/terminalChatWidget.ts | 17 ++- 4 files changed, 60 insertions(+), 72 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index 678c8177976..693fde05bab 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -316,7 +316,7 @@ export class SessionExchange { constructor( readonly prompt: SessionPrompt, - readonly response: ReplyResponse | EmptyResponse | ErrorResponse | TerminalResponse + readonly response: ReplyResponse | EmptyResponse | ErrorResponse ) { } } @@ -324,13 +324,6 @@ export class EmptyResponse { } -export class TerminalResponse { - readonly message: string; - constructor(message: string) { - this.message = message; - } -} - export class ErrorResponse { readonly message: string; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 7aef66f5631..641737229dc 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -178,7 +178,7 @@ registerActiveXtermAction({ return; } const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); - contr?.cancel(); + contr?.chatWidget?.cancel(); } }); @@ -234,7 +234,7 @@ registerActiveXtermAction({ // TODO: precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.Empty)), icon: Codicon.report, menu: [/*{ - // TODO: Enable + // TODO: Enable this id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, when: ContextKeyExpr.and(CTX_TERMINAL_CHAT_SUPPORT_ISSUE_REPORTING, CTX_TERMINAL_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.Empty)), group: '2_feedback', diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 86c17c99d22..54430cd8fee 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -25,14 +25,25 @@ import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { IChatProgress, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; -import { EmptyResponse, Session, SessionExchange, SessionPrompt, TerminalResponse } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; -import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; -import { CTX_INLINE_CHAT_RESPONSE_TYPES, EditMode, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; -import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import { generateUuid } from 'vs/base/common/uuid'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { marked } from 'vs/base/common/marked/marked'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; +import { Emitter, Event } from 'vs/base/common/event'; +import { localize } from 'vs/nls'; +import { CTX_INLINE_CHAT_RESPONSE_TYPES, EditMode, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { EmptyResponse, Session, SessionExchange, SessionPrompt, TerminalResponse } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { assertType } from 'vs/base/common/types'; +import { IModelService } from 'vs/editor/common/services/model'; +import { ITextModel } from 'vs/editor/common/model'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; const enum Message { NONE = 0, @@ -63,15 +74,15 @@ export class TerminalChatController extends Disposable implements ITerminalContr private readonly _terminalAgentRegisteredContextKey!: IContextKey; private readonly _lastResponseTypeContextKey!: IContextKey; + private _scopedInstantiationService: IInstantiationService | undefined; + private _accessibilityRequestId: number = 0; private _messages = this._store.add(new Emitter()); - private _activeSession?: Session; - - private _fakeEditor: CodeEditorWidget | undefined; - - get lastResponseContent(): string | undefined { return (this._activeSession?.lastExchange?.response as TerminalResponse).message; } + private _lastInput: string | undefined; + private _lastResponseContent: string | undefined; + get lastResponseContent(): string | undefined { return this._lastResponseContent; } readonly onDidAcceptInput = Event.filter(this._messages.event, m => m === Message.ACCEPT_INPUT, this._store); readonly onDidCancelInput = Event.filter(this._messages.event, m => m === Message.CANCEL_INPUT || m === Message.CANCEL_SESSION, this._store); @@ -89,9 +100,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService, @IChatService private readonly _chatService: IChatService, - @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, - @IInlineChatSessionService private readonly _inlineChatSessionService: IInlineChatSessionService, - @IModelService private readonly _modelService: IModelService + @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService ) { super(); @@ -111,6 +120,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr } else { this._terminalAgentRegisteredContextKey.set(true); } + this._cancellationTokenSource = new CancellationTokenSource(); } xtermReady(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { @@ -118,10 +128,12 @@ export class TerminalChatController extends Disposable implements ITerminalContr return; } this._chatWidget = new Lazy(() => { + const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._instance.domElement!)); + this._scopedInstantiationService = this._instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); // The inline chat widget requires a parent editor that it bases the diff view on, since the // terminal doesn't use that feature we can just pass in an unattached editor instance. const fakeParentEditorElement = document.createElement('div'); - this._fakeEditor = this._instantiationService.createInstance( + this._fakeEditor = this._scopedInstantiationService.createInstance( CodeEditorWidget, fakeParentEditorElement, { @@ -154,28 +166,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr } cancel(): void { - if (this._activeSession) { - this._inlineChatSessionService.releaseSession(this._activeSession); - this._activeSession = undefined; - } - } - - private async _startSession(editor: IActiveCodeEditor, token: CancellationToken) { - if (this._activeSession) { - this._inlineChatSessionService.releaseSession(this._activeSession); - } - - const session = await this._inlineChatSessionService.createSession( - editor, - { editMode: EditMode.Live }, - token - ); - - if (!session) { - return; - } - - this._activeSession = session; + this._cancellationTokenSource.cancel(); } private _forcedPlaceholder: string | undefined = undefined; @@ -204,15 +195,13 @@ export class TerminalChatController extends Disposable implements ITerminalContr } async acceptInput(): Promise { + this._lastInput = this._chatWidget?.rawValue?.input(); + if (!this._lastInput) { + return; + } this._chatAccessibilityService.acceptRequest(); this._requestActiveContextKey.set(true); - const cancellationToken = new CancellationTokenSource().token; - if (this._fakeEditor?.hasModel()) { - await this._startSession(this._fakeEditor, cancellationToken); - } - assertType(this._activeSession); - const inputValue = this.chatWidget!.inlineChatWidget.value; - this._activeSession!.addInput(new SessionPrompt(inputValue)); + const cancellationToken = this._cancellationTokenSource.token; let responseContent = ''; const progressCallback = (progress: IChatProgress) => { if (cancellationToken.isCancellationRequested) { @@ -228,11 +217,10 @@ export class TerminalChatController extends Disposable implements ITerminalContr sessionId: generateUuid(), requestId, agentId: this._terminalAgentId, - message: inputValue, + message: this._lastInput, // TODO: ? variables: { variables: [] }, }; - let response: EmptyResponse | TerminalResponse = EmptyResponse; try { const task = this._chatAgentService.invokeAgent(this._terminalAgentId, requestProps, progressCallback, [], cancellationToken); this._chatWidget?.rawValue?.inlineChatWidget.updateChatMessage(undefined); @@ -242,16 +230,14 @@ export class TerminalChatController extends Disposable implements ITerminalContr // TODO: this._zone.value.widget.updateInfo(!this._session.lastExchange ? localize('thinking', "Thinking\u2026") : ''); await task; } catch (e) { - response = e; + } finally { this._requestActiveContextKey.set(false); this._chatWidget?.rawValue?.inlineChatWidget.updateProgress(false); this._chatWidget?.rawValue?.inlineChatWidget.updateInfo(''); this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); - if (response === EmptyResponse) { - response = new TerminalResponse(responseContent); - } } + this._lastResponseContent = responseContent; const firstCodeBlockContent = marked.lexer(responseContent).filter(token => token.type === 'code')?.[0]?.raw; const regex = /```(?\w+)\n(?[\s\S]*?)```/g; const match = regex.exec(firstCodeBlockContent); @@ -268,9 +254,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.renderMessage(responseContent, this._accessibilityRequestId, requestId); this._lastResponseTypeContextKey.set(InlineChatResponseTypes.OnlyMessages); } - if (this._activeSession?.lastInput) { - this._activeSession.addExchange(new SessionExchange(this._activeSession.lastInput, response)); - } this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); this._messages.fire(Message.ACCEPT_INPUT); } @@ -306,15 +289,12 @@ export class TerminalChatController extends Disposable implements ITerminalContr } async viewInChat(): Promise { + if (!this._lastInput || !this._lastResponseContent) { + return; + } const widget = await this._chatWidgetService.revealViewForProvider('copilot'); if (widget && widget.viewModel) { - const request = this._activeSession?.lastExchange; - const input = request?.prompt.value; - const response = request?.response as TerminalResponse; - if (!input || !response) { - return; - } - this._chatService.addCompleteRequest(widget.viewModel.sessionId, input, undefined, response); + this._chatService.addCompleteRequest(widget.viewModel.sessionId, this._lastInput, undefined, { message: this._lastResponseContent }); widget.focusLastMessage(); } } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 9f4f960ee5e..39892d8a381 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -42,7 +42,6 @@ export class TerminalChatWidget extends Disposable { constructor( terminalElement: HTMLElement, - fakeParentEditor: CodeEditorWidget, private readonly _instance: ITerminalInstance, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IConfigurationService private readonly _configurationService: IConfigurationService, @@ -65,6 +64,18 @@ export class TerminalChatWidget extends Disposable { this._terminalCommandWidgetContainer.classList.add('terminal-inline-chat-response'); this._container.prepend(this._terminalCommandWidgetContainer); + // The inline chat widget requires a parent editor that it bases the diff view on, since the + // terminal doesn't use that feature we can just pass in an unattached editor instance. + const fakeParentEditorElement = document.createElement('div'); + const fakeParentEditor = this._instantiationService.createInstance( + CodeEditorWidget, + fakeParentEditorElement, + { + extraEditorClassName: 'ignore-panel-bg' + }, + { isSimpleWidget: true } + ); + this._inlineChatWidget = this._instantiationService.createInstance( InlineChatWidget, fakeParentEditor, @@ -203,6 +214,10 @@ export class TerminalChatWidget extends Disposable { this._visibleContextKey.set(false); this._instance.focus(); } + cancel(): void { + // TODO: Impl + this._inlineChatWidget.value = ''; + } focus(): void { this._inlineChatWidget.focus(); } From 55b7c86d9a818977b50d602a01d2bdfe7e72f74b Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 13:00:27 -0600 Subject: [PATCH 0076/1175] use chat model --- .../chat/browser/terminalChatActions.ts | 4 +- .../chat/browser/terminalChatController.ts | 73 +++++++++++++++++-- 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 641737229dc..a1561e9b63c 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -63,7 +63,7 @@ registerActiveXtermAction({ return; } const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); - contr?.chatWidget?.hide(); + contr?.clear(); } }); @@ -178,7 +178,7 @@ registerActiveXtermAction({ return; } const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); - contr?.chatWidget?.cancel(); + contr?.cancel(); } }); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 54430cd8fee..6daeb2bd3cb 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -44,6 +44,7 @@ import { IModelService } from 'vs/editor/common/services/model'; import { ITextModel } from 'vs/editor/common/model'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; +import { ChatRequestModel } from 'vs/workbench/contrib/chat/common/chatModel'; const enum Message { NONE = 0, @@ -80,15 +81,22 @@ export class TerminalChatController extends Disposable implements ITerminalContr private _messages = this._store.add(new Emitter()); + private _currentRequest: ChatRequestModel | undefined; + private _lastInput: string | undefined; private _lastResponseContent: string | undefined; - get lastResponseContent(): string | undefined { return this._lastResponseContent; } + get lastResponseContent(): string | undefined { + // TODO: use model + return this._lastResponseContent; + } readonly onDidAcceptInput = Event.filter(this._messages.event, m => m === Message.ACCEPT_INPUT, this._store); readonly onDidCancelInput = Event.filter(this._messages.event, m => m === Message.CANCEL_INPUT || m === Message.CANCEL_SESSION, this._store); private _terminalAgentId = 'terminal'; + private _model: ChatModel | undefined; + constructor( private readonly _instance: ITerminalInstance, processManager: ITerminalProcessManager, @@ -99,8 +107,8 @@ export class TerminalChatController extends Disposable implements ITerminalContr @IChatAgentService private readonly _chatAgentService: IChatAgentService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService, + @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, @IChatService private readonly _chatService: IChatService, - @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService ) { super(); @@ -166,7 +174,9 @@ export class TerminalChatController extends Disposable implements ITerminalContr } cancel(): void { - this._cancellationTokenSource.cancel(); + if (this._currentRequest) { + this._model?.cancelRequest(this._currentRequest); + } } private _forcedPlaceholder: string | undefined = undefined; @@ -194,7 +204,24 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._updatePlaceholder(); } + clear(): void { + this._model?.dispose(); + this._model = undefined; + this.updateModel(); + this._chatWidget?.rawValue?.hide(); + this._chatWidget?.rawValue?.setValue(undefined); + } + + private updateModel(): void { + const providerInfo = this._chatService.getProviderInfos()?.[0]; + if (!providerInfo) { + return; + } + this._model ??= this._chatService.startSession(providerInfo.id, CancellationToken.None); + } + async acceptInput(): Promise { + this.updateModel(); this._lastInput = this._chatWidget?.rawValue?.input(); if (!this._lastInput) { return; @@ -211,6 +238,9 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (progress.kind === 'content' || progress.kind === 'markdownContent') { responseContent += progress.content; } + if (this._currentRequest) { + this._model?.acceptResponseProgress(this._currentRequest, progress); + } }; const requestId = generateUuid(); const requestProps: IChatAgentRequest = { @@ -221,6 +251,16 @@ export class TerminalChatController extends Disposable implements ITerminalContr // TODO: ? variables: { variables: [] }, }; + // TODO: fix requester usrname, responder username + this._model?.initialize({ id: this._accessibilityRequestId, requesterUsername: 'userGesture', responderUsername: 'terminal' }, undefined); + const request: IParsedChatRequest = { + text: this._lastInput, + parts: [] + }; + const requestVarData: IChatRequestVariableData = { + variables: [] + }; + this._currentRequest = this._model?.addRequest(request, requestVarData); try { const task = this._chatAgentService.invokeAgent(this._terminalAgentId, requestProps, progressCallback, [], cancellationToken); this._chatWidget?.rawValue?.inlineChatWidget.updateChatMessage(undefined); @@ -236,6 +276,9 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.inlineChatWidget.updateProgress(false); this._chatWidget?.rawValue?.inlineChatWidget.updateInfo(''); this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); + if (this._currentRequest) { + this._model?.completeResponse(this._currentRequest); + } } this._lastResponseContent = responseContent; const firstCodeBlockContent = marked.lexer(responseContent).filter(token => token.type === 'code')?.[0]?.raw; @@ -289,18 +332,34 @@ export class TerminalChatController extends Disposable implements ITerminalContr } async viewInChat(): Promise { - if (!this._lastInput || !this._lastResponseContent) { + const providerInfo = this._chatService.getProviderInfos()?.[0]; + if (!providerInfo) { return; } - const widget = await this._chatWidgetService.revealViewForProvider('copilot'); - if (widget && widget.viewModel) { - this._chatService.addCompleteRequest(widget.viewModel.sessionId, this._lastInput, undefined, { message: this._lastResponseContent }); + const widget = await this._chatWidgetService.revealViewForProvider(providerInfo.id); + if (widget && widget.viewModel && this._model) { + for (const request of this._model.getRequests()) { + if (request.response?.response.value || request.response?.result) { + this._chatService.addCompleteRequest(widget.viewModel.sessionId, + request.message as IParsedChatRequest, + request.variableData, + { + message: request.response.response.value, + result: request.response.result, + followups: request.response.followups + }); + } + } widget.focusLastMessage(); } } override dispose() { + if (this._currentRequest) { + this._model?.cancelRequest(this._currentRequest); + } super.dispose(); + this.clear(); this._chatWidget?.rawValue?.dispose(); } } From 79ef477d826dc46e9617a8b2db3477841c89e4e4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 13:11:42 -0600 Subject: [PATCH 0077/1175] rm unused --- .../chat/browser/terminalChatController.ts | 66 ++++--------------- 1 file changed, 14 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 6daeb2bd3cb..bfafbb102fc 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -9,42 +9,25 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Lazy } from 'vs/base/common/lazy'; import { Disposable } from 'vs/base/common/lifecycle'; import { marked } from 'vs/base/common/marked/marked'; -import { Schemas } from 'vs/base/common/network'; -import { assertType } from 'vs/base/common/types'; -import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; -import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { ITextModel } from 'vs/editor/common/model'; -import { IModelService } from 'vs/editor/common/services/model'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; -import { IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; -import { IChatProgress, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; -import { generateUuid } from 'vs/base/common/uuid'; -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { marked } from 'vs/base/common/marked/marked'; +import { IChatAgentService, IChatAgentRequest } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; +import { IChatService, IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; +import { InlineChatResponseTypes, CTX_INLINE_CHAT_RESPONSE_TYPES } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; +import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; -import { Emitter, Event } from 'vs/base/common/event'; -import { localize } from 'vs/nls'; -import { CTX_INLINE_CHAT_RESPONSE_TYPES, EditMode, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; -import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { EmptyResponse, Session, SessionExchange, SessionPrompt, TerminalResponse } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; -import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { assertType } from 'vs/base/common/types'; -import { IModelService } from 'vs/editor/common/services/model'; -import { ITextModel } from 'vs/editor/common/model'; -import { Schemas } from 'vs/base/common/network'; -import { URI } from 'vs/base/common/uri'; -import { ChatRequestModel } from 'vs/workbench/contrib/chat/common/chatModel'; +import { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget'; + + +import { ChatModel, ChatRequestModel, IChatRequestVariableData } from 'vs/workbench/contrib/chat/common/chatModel'; const enum Message { NONE = 0, @@ -74,9 +57,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr private readonly _requestActiveContextKey!: IContextKey; private readonly _terminalAgentRegisteredContextKey!: IContextKey; private readonly _lastResponseTypeContextKey!: IContextKey; - - private _scopedInstantiationService: IInstantiationService | undefined; - private _accessibilityRequestId: number = 0; private _messages = this._store.add(new Emitter()); @@ -128,7 +108,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr } else { this._terminalAgentRegisteredContextKey.set(true); } - this._cancellationTokenSource = new CancellationTokenSource(); } xtermReady(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { @@ -136,25 +115,8 @@ export class TerminalChatController extends Disposable implements ITerminalContr return; } this._chatWidget = new Lazy(() => { - const scopedContextKeyService = this._register(this._contextKeyService.createScoped(this._instance.domElement!)); - this._scopedInstantiationService = this._instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])); - // The inline chat widget requires a parent editor that it bases the diff view on, since the - // terminal doesn't use that feature we can just pass in an unattached editor instance. - const fakeParentEditorElement = document.createElement('div'); - this._fakeEditor = this._scopedInstantiationService.createInstance( - CodeEditorWidget, - fakeParentEditorElement, - { - extraEditorClassName: 'ignore-panel-bg' - }, - { isSimpleWidget: true } - ); - - const path = `terminal-chat-input-${this._instance.instanceId}`; - const inputUri = URI.from({ path: path, scheme: Schemas.untitled, fragment: '' }); - const result: ITextModel = this._modelService.createModel('', null, inputUri, false); - this._fakeEditor.setModel(result); - const chatWidget = this._instantiationService.createInstance(TerminalChatWidget, this._instance.domElement!, this._fakeEditor!, this._instance); + + const chatWidget = this._instantiationService.createInstance(TerminalChatWidget, this._instance.domElement!, this._instance); chatWidget.focusTracker.onDidFocus(() => { TerminalChatController.activeChatWidget = this; if (!isDetachedTerminalInstance(this._instance)) { @@ -228,7 +190,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr } this._chatAccessibilityService.acceptRequest(); this._requestActiveContextKey.set(true); - const cancellationToken = this._cancellationTokenSource.token; + const cancellationToken = new CancellationTokenSource().token; let responseContent = ''; const progressCallback = (progress: IChatProgress) => { if (cancellationToken.isCancellationRequested) { From f7915fc65f7639a109599dc133f7dd2fa17282a3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:06:19 -0800 Subject: [PATCH 0078/1175] Accept -> Run, add insert button, improve keybindings --- .../chat/browser/terminalChat.ts | 3 +- .../browser/terminalChatAccessibilityHelp.ts | 2 +- .../chat/browser/terminalChatActions.ts | 44 ++++++++++++++++--- .../chat/browser/terminalChatController.ts | 4 +- .../chat/browser/terminalChatWidget.ts | 4 +- 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts index f75c9c3d9a7..d409361d517 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts @@ -13,7 +13,8 @@ export const enum TerminalChatCommandId { FeedbackHelpful = 'workbench.action.terminal.chat.feedbackHelpful', FeedbackUnhelpful = 'workbench.action.terminal.chat.feedbackUnhelpful', FeedbackReportIssue = 'workbench.action.terminal.chat.feedbackReportIssue', - AcceptCommand = 'workbench.action.terminal.chat.acceptCommand', + RunCommand = 'workbench.action.terminal.chat.runCommand', + InsertCommand = 'workbench.action.terminal.chat.insertCommand', ViewInChat = 'workbench.action.terminal.chat.viewInChat', } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts index 2fe29ea5d8a..d851609de14 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts @@ -48,7 +48,7 @@ export function getAccessibilityHelpText(accessor: ServicesAccessor): string { const keybindingService = accessor.get(IKeybindingService); const content = []; const openAccessibleViewKeybinding = keybindingService.lookupKeybinding('editor.action.accessibleView')?.getAriaLabel(); - const acceptCommandKeybinding = keybindingService.lookupKeybinding(TerminalChatCommandId.AcceptCommand)?.getAriaLabel(); + const acceptCommandKeybinding = keybindingService.lookupKeybinding(TerminalChatCommandId.RunCommand)?.getAriaLabel(); const makeRequestKeybinding = keybindingService.lookupKeybinding(TerminalChatCommandId.MakeRequest)?.getAriaLabel(); //TODO: using this instead of the terminal command bc by definition the inline terminal chat is focused when this dialog is invoked. const startChatKeybinding = keybindingService.lookupKeybinding('inlineChat.start')?.getAriaLabel(); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index a1561e9b63c..901f536f0bf 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -71,9 +71,9 @@ registerActiveXtermAction({ registerActiveXtermAction({ - id: TerminalChatCommandId.AcceptCommand, - title: localize2('acceptCommand', 'Terminal: Accept Chat Command'), - shortTitle: localize2('accept', 'Accept'), + id: TerminalChatCommandId.RunCommand, + title: localize2('runCommand', 'Terminal: Run Chat Command'), + shortTitle: localize2('run', 'Run'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), @@ -83,8 +83,8 @@ registerActiveXtermAction({ ), icon: Codicon.check, keybinding: { - when: ContextKeyExpr.and(TerminalContextKeys.chatResponseEditorFocused, TerminalContextKeys.chatRequestActive.negate()), - weight: KeybindingWeight.EditorCore + 7, + when: TerminalContextKeys.chatRequestActive.negate(), + weight: KeybindingWeight.WorkbenchContrib + 7, primary: KeyMod.CtrlCmd | KeyCode.Enter, }, menu: { @@ -98,7 +98,39 @@ registerActiveXtermAction({ return; } const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); - contr?.acceptCommand(); + contr?.acceptCommand(true); + } +}); + +registerActiveXtermAction({ + id: TerminalChatCommandId.InsertCommand, + title: localize2('insertCommand', 'Terminal: Insert Chat Command'), + shortTitle: localize2('insert', 'Insert'), + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + TerminalContextKeys.chatRequestActive.negate(), + TerminalContextKeys.chatAgentRegistered, + CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.Empty) + ), + icon: Codicon.check, + keybinding: { + when: TerminalContextKeys.chatRequestActive.negate(), + weight: KeybindingWeight.WorkbenchContrib + 7, + primary: KeyMod.Alt | KeyCode.Enter, + }, + menu: { + id: MENU_TERMINAL_CHAT_WIDGET_STATUS, + group: '0_main', + order: 1, + when: ContextKeyExpr.and(CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.Empty), TerminalContextKeys.chatRequestActive.negate()), + }, + run: (_xterm, _accessor, activeInstance) => { + if (isDetachedTerminalInstance(activeInstance)) { + return; + } + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + contr?.acceptCommand(false); } }); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index bfafbb102fc..903c81b74fb 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -285,8 +285,8 @@ export class TerminalChatController extends Disposable implements ITerminalContr return !!this._chatWidget?.rawValue?.hasFocus(); } - acceptCommand(): void { - this._chatWidget?.rawValue?.acceptCommand(); + acceptCommand(shouldExecute: boolean): void { + this._chatWidget?.rawValue?.acceptCommand(shouldExecute); } reveal(): void { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 39892d8a381..53a67093ffb 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -233,12 +233,12 @@ export class TerminalChatWidget extends Disposable { this.hideTerminalCommandWidget(); } } - acceptCommand(): void { + acceptCommand(shouldExecute: boolean): void { const value = this._terminalCommandWidget?.getValue(); if (!value) { return; } - this._instance.sendText(value, false, true); + this._instance.runCommand(value, shouldExecute); this.hide(); } updateProgress(progress?: IChatProgress): void { From a9c300cd68ec0b2f1290aa32141367fbb0abf6da Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:11:28 -0800 Subject: [PATCH 0079/1175] Trim suggestion to prevent running on alt+enter --- .../terminalContrib/chat/browser/terminalChatController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 903c81b74fb..d0d9590813e 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -246,7 +246,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr const firstCodeBlockContent = marked.lexer(responseContent).filter(token => token.type === 'code')?.[0]?.raw; const regex = /```(?\w+)\n(?[\s\S]*?)```/g; const match = regex.exec(firstCodeBlockContent); - const codeBlock = match?.groups?.content; + const codeBlock = match?.groups?.content.trim(); const shellType = match?.groups?.language; this._accessibilityRequestId++; if (cancellationToken.isCancellationRequested) { From 4d81b4e49afb5581431a2534463ed522894a0b36 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 13:15:53 -0600 Subject: [PATCH 0080/1175] cancel request on clear --- .../terminalContrib/chat/browser/terminalChatController.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index d0d9590813e..6f292f51fde 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -167,9 +167,11 @@ export class TerminalChatController extends Disposable implements ITerminalContr } clear(): void { + if (this._currentRequest) { + this._model?.cancelRequest(this._currentRequest); + } this._model?.dispose(); this._model = undefined; - this.updateModel(); this._chatWidget?.rawValue?.hide(); this._chatWidget?.rawValue?.setValue(undefined); } From 50a0433578968dea132f628203144e55c991246f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 13:17:31 -0600 Subject: [PATCH 0081/1175] rename --- .../chat/browser/terminalChatController.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 6f292f51fde..2585ae766da 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -57,7 +57,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr private readonly _requestActiveContextKey!: IContextKey; private readonly _terminalAgentRegisteredContextKey!: IContextKey; private readonly _lastResponseTypeContextKey!: IContextKey; - private _accessibilityRequestId: number = 0; + private _requestId: number = 0; private _messages = this._store.add(new Emitter()); @@ -216,7 +216,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr variables: { variables: [] }, }; // TODO: fix requester usrname, responder username - this._model?.initialize({ id: this._accessibilityRequestId, requesterUsername: 'userGesture', responderUsername: 'terminal' }, undefined); + this._model?.initialize({ id: this._requestId, requesterUsername: 'userGesture', responderUsername: 'terminal' }, undefined); const request: IParsedChatRequest = { text: this._lastInput, parts: [] @@ -250,15 +250,15 @@ export class TerminalChatController extends Disposable implements ITerminalContr const match = regex.exec(firstCodeBlockContent); const codeBlock = match?.groups?.content.trim(); const shellType = match?.groups?.language; - this._accessibilityRequestId++; + this._requestId++; if (cancellationToken.isCancellationRequested) { return; } if (codeBlock) { - this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._accessibilityRequestId, shellType); + this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._requestId, shellType); this._lastResponseTypeContextKey.set(InlineChatResponseTypes.Empty); } else { - this._chatWidget?.rawValue?.renderMessage(responseContent, this._accessibilityRequestId, requestId); + this._chatWidget?.rawValue?.renderMessage(responseContent, this._requestId, requestId); this._lastResponseTypeContextKey.set(InlineChatResponseTypes.OnlyMessages); } this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); From 1a0199379a05a6a2f9f5c9e80ec410b2808334a0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:22:56 -0800 Subject: [PATCH 0082/1175] More tweaks to response style --- .../terminalContrib/chat/browser/media/terminalChatWidget.css | 3 ++- .../terminalContrib/chat/browser/terminalChatActions.ts | 2 ++ .../contrib/terminalContrib/chat/browser/terminalChatWidget.ts | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css index a916f2dd0d7..1feb4831c23 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css @@ -13,7 +13,8 @@ .terminal-inline-chat .terminal-inline-chat-response { border: 1px solid var(--vscode-input-border, transparent); - background-color: var(--vscode-interactive-result-editor-background-color); + /* TODO: Make themeable */ + background-color: #181818; } .terminal-inline-chat .terminal-inline-chat-response:has(.monaco-editor.focused) { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 901f536f0bf..50c1587b124 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -35,6 +35,8 @@ registerActiveXtermAction({ } const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); contr?.chatWidget?.reveal(); + // TODO: Remove this before merging to main + contr?.chatWidget?.setValue('list files'); } }); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 53a67093ffb..637e2f77104 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -122,7 +122,7 @@ export class TerminalChatWidget extends Disposable { scrollbar: { useShadows: false, vertical: 'hidden', - horizontal: 'auto', + horizontal: 'hidden', alwaysConsumeMouseWheel: false }, lineDecorationsWidth: 0, From 64e1a39e1bcf91e38860ead296912c06976f8f60 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:23:34 -0800 Subject: [PATCH 0083/1175] Remove category prefix from commands --- .../chat/browser/terminalChatActions.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 50c1587b124..19d9fdf140c 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -18,7 +18,7 @@ import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/cha registerActiveXtermAction({ id: TerminalChatCommandId.Start, - title: localize2('startChat', 'Terminal: Start Chat'), + title: localize2('startChat', 'Start Chat'), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyI, when: ContextKeyExpr.and(TerminalContextKeys.chatFocused.negate(), TerminalContextKeys.focusInAny), @@ -42,7 +42,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.Hide, - title: localize2('closeChat', 'Terminal: Close Chat'), + title: localize2('closeChat', 'Close Chat'), keybinding: { primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], @@ -74,7 +74,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.RunCommand, - title: localize2('runCommand', 'Terminal: Run Chat Command'), + title: localize2('runCommand', 'Run Chat Command'), shortTitle: localize2('run', 'Run'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), @@ -106,7 +106,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.InsertCommand, - title: localize2('insertCommand', 'Terminal: Insert Chat Command'), + title: localize2('insertCommand', 'Insert Chat Command'), shortTitle: localize2('insert', 'Insert'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), @@ -138,7 +138,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.ViewInChat, - title: localize2('viewInChat', 'Terminal: View in Chat'), + title: localize2('viewInChat', 'View in Chat'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), @@ -164,7 +164,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.MakeRequest, - title: localize2('makeChatRequest', 'Terminal: Make Chat Request'), + title: localize2('makeChatRequest', 'Make Chat Request'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), @@ -195,7 +195,7 @@ registerActiveXtermAction({ registerActiveXtermAction({ id: TerminalChatCommandId.Cancel, - title: localize2('cancelChat', 'Terminal: Cancel Chat'), + title: localize2('cancelChat', 'Cancel Chat'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), TerminalContextKeys.chatRequestActive, From 85aaa473fbf47de6762d990d6982c42e53886340 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:25:54 -0800 Subject: [PATCH 0084/1175] Hide command border before it's shown --- .../chat/browser/terminalChatWidget.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 637e2f77104..bc7442b5f48 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -31,7 +31,7 @@ export class TerminalChatWidget extends Disposable { private readonly _inlineChatWidget: InlineChatWidget; public get inlineChatWidget(): InlineChatWidget { return this._inlineChatWidget; } - private readonly _terminalCommandWidgetContainer: HTMLElement; + private _terminalCommandWidgetContainer: HTMLElement | undefined; private _terminalCommandWidget: CodeEditorWidget | undefined; private readonly _focusTracker: IFocusTracker; @@ -60,10 +60,6 @@ export class TerminalChatWidget extends Disposable { this._container.classList.add('terminal-inline-chat'); terminalElement.appendChild(this._container); - this._terminalCommandWidgetContainer = document.createElement('div'); - this._terminalCommandWidgetContainer.classList.add('terminal-inline-chat-response'); - this._container.prepend(this._terminalCommandWidgetContainer); - // The inline chat widget requires a parent editor that it bases the diff view on, since the // terminal doesn't use that feature we can just pass in an unattached editor instance. const fakeParentEditorElement = document.createElement('div'); @@ -106,6 +102,9 @@ export class TerminalChatWidget extends Disposable { this._chatAccessibilityService.acceptResponse(command, requestId); this.showTerminalCommandWidget(); if (!this._terminalCommandWidget) { + this._terminalCommandWidgetContainer = document.createElement('div'); + this._terminalCommandWidgetContainer.classList.add('terminal-inline-chat-response'); + this._container.prepend(this._terminalCommandWidgetContainer); this._terminalCommandWidget = this._register(this._instantiationService.createInstance(CodeEditorWidget, this._terminalCommandWidgetContainer, { readOnly: false, ariaLabel: this._getAriaLabel(), @@ -248,9 +247,9 @@ export class TerminalChatWidget extends Disposable { return this._focusTracker; } hideTerminalCommandWidget(): void { - this._terminalCommandWidgetContainer.classList.add('hide'); + this._terminalCommandWidgetContainer?.classList.add('hide'); } showTerminalCommandWidget(): void { - this._terminalCommandWidgetContainer.classList.remove('hide'); + this._terminalCommandWidgetContainer?.classList.remove('hide'); } } From 45e54c6f405e759e91e736a35ae8a58b1209cceb Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:28:54 -0800 Subject: [PATCH 0085/1175] Wider widget, resize suggestion based on content height --- .../chat/browser/terminalChatWidget.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index bc7442b5f48..d65b9a0ffc3 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -105,7 +105,7 @@ export class TerminalChatWidget extends Disposable { this._terminalCommandWidgetContainer = document.createElement('div'); this._terminalCommandWidgetContainer.classList.add('terminal-inline-chat-response'); this._container.prepend(this._terminalCommandWidgetContainer); - this._terminalCommandWidget = this._register(this._instantiationService.createInstance(CodeEditorWidget, this._terminalCommandWidgetContainer, { + const widget = this._terminalCommandWidget = this._register(this._instantiationService.createInstance(CodeEditorWidget, this._terminalCommandWidgetContainer, { readOnly: false, ariaLabel: this._getAriaLabel(), fontSize: 13, @@ -146,14 +146,18 @@ export class TerminalChatWidget extends Disposable { }, { isSimpleWidget: true })); this._register(this._terminalCommandWidget.onDidFocusEditorText(() => this._responseEditorFocusedContextKey.set(true))); this._register(this._terminalCommandWidget.onDidBlurEditorText(() => this._responseEditorFocusedContextKey.set(false))); + this._register(this._terminalCommandWidget.onDidChangeModelContent(e => { + const height = widget.getContentHeight(); + widget.layout(new Dimension(640, height)); + })); this._getTextModel(URI.from({ path: `terminal-inline-chat-${this._instance.instanceId}`, scheme: 'terminal-inline-chat', fragment: command })).then((model) => { if (!model || !this._terminalCommandWidget) { return; } - this._terminalCommandWidget.layout(new Dimension(400, 0)); + this._terminalCommandWidget.layout(new Dimension(640, 0)); this._terminalCommandWidget.setModel(model); const height = this._terminalCommandWidget.getContentHeight(); - this._terminalCommandWidget.layout(new Dimension(400, height)); + this._terminalCommandWidget.layout(new Dimension(640, height)); }); } else { this._terminalCommandWidget.setValue(command); @@ -194,7 +198,7 @@ export class TerminalChatWidget extends Disposable { return this._modelService.createModel(resource.fragment, null, resource, false); } reveal(): void { - this._inlineChatWidget.layout(new Dimension(400, 150)); + this._inlineChatWidget.layout(new Dimension(640, 150)); this._container.classList.remove('hide'); this._focusedContextKey.set(true); this._visibleContextKey.set(true); From 0dadd8f5402a6e2b21c696d16750c72b34f03ec3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:29:53 -0800 Subject: [PATCH 0086/1175] Trim command before accepting --- .../contrib/terminalContrib/chat/browser/terminalChatWidget.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index d65b9a0ffc3..c049e71d013 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -237,7 +237,8 @@ export class TerminalChatWidget extends Disposable { } } acceptCommand(shouldExecute: boolean): void { - const value = this._terminalCommandWidget?.getValue(); + // Trim command to remove any whitespace, otherwise this may execute the command + const value = this._terminalCommandWidget?.getValue().trim(); if (!value) { return; } From 69592b6d498fa3dab8c4c7a1710843f7c12aae37 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:33:37 -0800 Subject: [PATCH 0087/1175] Standardize on workbench contrib weight --- .../chat/browser/terminalChatActions.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 19d9fdf140c..3ad13dbb898 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -22,7 +22,7 @@ registerActiveXtermAction({ keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyI, when: ContextKeyExpr.and(TerminalContextKeys.chatFocused.negate(), TerminalContextKeys.focusInAny), - weight: KeybindingWeight.WorkbenchContrib + weight: KeybindingWeight.WorkbenchContrib, }, f1: true, precondition: ContextKeyExpr.and( @@ -47,7 +47,7 @@ registerActiveXtermAction({ primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], when: ContextKeyExpr.and(TerminalContextKeys.chatFocused, TerminalContextKeys.chatVisible), - weight: KeybindingWeight.WorkbenchContrib + weight: KeybindingWeight.WorkbenchContrib, }, icon: Codicon.close, menu: { @@ -86,7 +86,7 @@ registerActiveXtermAction({ icon: Codicon.check, keybinding: { when: TerminalContextKeys.chatRequestActive.negate(), - weight: KeybindingWeight.WorkbenchContrib + 7, + weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.Enter, }, menu: { @@ -118,7 +118,7 @@ registerActiveXtermAction({ icon: Codicon.check, keybinding: { when: TerminalContextKeys.chatRequestActive.negate(), - weight: KeybindingWeight.WorkbenchContrib + 7, + weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.Alt | KeyCode.Enter, }, menu: { @@ -175,7 +175,7 @@ registerActiveXtermAction({ icon: Codicon.send, keybinding: { when: ContextKeyExpr.and(CTX_INLINE_CHAT_FOCUSED, TerminalContextKeys.chatRequestActive.negate()), - weight: KeybindingWeight.EditorCore + 7, + weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.Enter }, menu: { From 06afd593735a230025ed5b9b5010574ef5748d22 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:36:50 -0800 Subject: [PATCH 0088/1175] Await model (fixes lang mode) --- .../chat/browser/terminalChatWidget.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index c049e71d013..91bec4acda0 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -98,9 +98,10 @@ export class TerminalChatWidget extends Disposable { return localize('terminalChatInput', "Terminal Chat Input"); } - renderTerminalCommand(command: string, requestId: number, shellType?: string): void { + async renderTerminalCommand(command: string, requestId: number, shellType?: string): Promise { this._chatAccessibilityService.acceptResponse(command, requestId); this.showTerminalCommandWidget(); + let model: ITextModel | null | void = null; if (!this._terminalCommandWidget) { this._terminalCommandWidgetContainer = document.createElement('div'); this._terminalCommandWidgetContainer.classList.add('terminal-inline-chat-response'); @@ -150,7 +151,7 @@ export class TerminalChatWidget extends Disposable { const height = widget.getContentHeight(); widget.layout(new Dimension(640, height)); })); - this._getTextModel(URI.from({ path: `terminal-inline-chat-${this._instance.instanceId}`, scheme: 'terminal-inline-chat', fragment: command })).then((model) => { + model = await this._getTextModel(URI.from({ path: `terminal-inline-chat-${this._instance.instanceId}`, scheme: 'terminal-inline-chat', fragment: command })).then((model) => { if (!model || !this._terminalCommandWidget) { return; } @@ -162,9 +163,13 @@ export class TerminalChatWidget extends Disposable { } else { this._terminalCommandWidget.setValue(command); } - const languageId = this._getLanguageFromShell(shellType); - console.log('languageId', languageId); - this._terminalCommandWidget.getModel()?.setLanguage(languageId); + if (!model) { + model = this._terminalCommandWidget.getModel(); + } + if (model) { + const languageId = this._getLanguageFromShell(shellType); + model.setLanguage(languageId); + } } private _getLanguageFromShell(shell?: string): string { From b269a4b6a536614f5f6357fa686572e2bc8aa1b3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:44:46 -0800 Subject: [PATCH 0089/1175] Improve handling of wrapping in response editor --- .../chat/browser/terminalChatWidget.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 91bec4acda0..32e1e685ba8 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Dimension, IFocusTracker, trackFocus } from 'vs/base/browser/dom'; +import { Event } from 'vs/base/common/event'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; @@ -144,21 +145,22 @@ export class TerminalChatWidget extends Disposable { showWords: true, showStatusBar: false, }, + wordWrap: 'on' }, { isSimpleWidget: true })); this._register(this._terminalCommandWidget.onDidFocusEditorText(() => this._responseEditorFocusedContextKey.set(true))); this._register(this._terminalCommandWidget.onDidBlurEditorText(() => this._responseEditorFocusedContextKey.set(false))); - this._register(this._terminalCommandWidget.onDidChangeModelContent(e => { + this._register(Event.any(this._terminalCommandWidget.onDidChangeModelContent, this._terminalCommandWidget.onDidChangeModelDecorations)(() => { const height = widget.getContentHeight(); widget.layout(new Dimension(640, height)); })); model = await this._getTextModel(URI.from({ path: `terminal-inline-chat-${this._instance.instanceId}`, scheme: 'terminal-inline-chat', fragment: command })).then((model) => { - if (!model || !this._terminalCommandWidget) { + if (!model) { return; } - this._terminalCommandWidget.layout(new Dimension(640, 0)); - this._terminalCommandWidget.setModel(model); - const height = this._terminalCommandWidget.getContentHeight(); - this._terminalCommandWidget.layout(new Dimension(640, height)); + widget.layout(new Dimension(640, 0)); + widget.setModel(model); + const height = widget.getContentHeight(); + widget.layout(new Dimension(640, height)); }); } else { this._terminalCommandWidget.setValue(command); From aa5626dc23146a651ae078db2860e436d9e9104a Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 13:57:34 -0600 Subject: [PATCH 0090/1175] fix issue with cancel --- .../terminalContrib/chat/browser/terminalChatController.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 2585ae766da..8f9c3defee0 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -139,6 +139,10 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (this._currentRequest) { this._model?.cancelRequest(this._currentRequest); } + this._requestActiveContextKey.set(false); + this._chatWidget?.rawValue?.inlineChatWidget.updateProgress(false); + this._chatWidget?.rawValue?.inlineChatWidget.updateInfo(''); + this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); } private _forcedPlaceholder: string | undefined = undefined; From a1ac397ba2b9c4404836b3606b5b52bc580a880f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 12:06:24 -0800 Subject: [PATCH 0091/1175] Pull response editor into separate class --- .../chat/browser/terminalChatWidget.ts | 256 ++++++++++-------- 1 file changed, 145 insertions(+), 111 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 32e1e685ba8..014f746c786 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -6,7 +6,7 @@ import { Dimension, IFocusTracker, trackFocus } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; import { MarkdownString } from 'vs/base/common/htmlContent'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/terminalChatWidget'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; @@ -33,29 +33,24 @@ export class TerminalChatWidget extends Disposable { public get inlineChatWidget(): InlineChatWidget { return this._inlineChatWidget; } private _terminalCommandWidgetContainer: HTMLElement | undefined; - private _terminalCommandWidget: CodeEditorWidget | undefined; + private _responseEditor: TerminalChatResponseEditor | undefined; private readonly _focusTracker: IFocusTracker; private readonly _focusedContextKey: IContextKey; private readonly _visibleContextKey: IContextKey; - private readonly _responseEditorFocusedContextKey!: IContextKey; constructor( terminalElement: HTMLElement, private readonly _instance: ITerminalInstance, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IConfigurationService private readonly _configurationService: IConfigurationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService, - @ILanguageService private readonly _languageService: ILanguageService, - @IModelService private readonly _modelService: IModelService ) { super(); this._focusedContextKey = TerminalContextKeys.chatFocused.bindTo(this._contextKeyService); this._visibleContextKey = TerminalContextKeys.chatVisible.bindTo(this._contextKeyService); - this._responseEditorFocusedContextKey = TerminalContextKeys.chatResponseEditorFocused.bindTo(this._contextKeyService); this._container = document.createElement('div'); this._container.classList.add('terminal-inline-chat'); @@ -90,105 +85,13 @@ export class TerminalChatWidget extends Disposable { this._focusTracker = this._register(trackFocus(this._container)); } - - private _getAriaLabel(): string { - const verbose = this._configurationService.getValue(AccessibilityVerbositySettingId.Chat); - if (verbose) { - // TODO: Add verbose description - } - return localize('terminalChatInput', "Terminal Chat Input"); - } - async renderTerminalCommand(command: string, requestId: number, shellType?: string): Promise { this._chatAccessibilityService.acceptResponse(command, requestId); this.showTerminalCommandWidget(); - let model: ITextModel | null | void = null; - if (!this._terminalCommandWidget) { - this._terminalCommandWidgetContainer = document.createElement('div'); - this._terminalCommandWidgetContainer.classList.add('terminal-inline-chat-response'); - this._container.prepend(this._terminalCommandWidgetContainer); - const widget = this._terminalCommandWidget = this._register(this._instantiationService.createInstance(CodeEditorWidget, this._terminalCommandWidgetContainer, { - readOnly: false, - ariaLabel: this._getAriaLabel(), - fontSize: 13, - lineHeight: 20, - padding: { top: 8, bottom: 8 }, - overviewRulerLanes: 0, - glyphMargin: false, - lineNumbers: 'off', - folding: false, - hideCursorInOverviewRuler: true, - selectOnLineNumbers: false, - selectionHighlight: false, - scrollbar: { - useShadows: false, - vertical: 'hidden', - horizontal: 'hidden', - alwaysConsumeMouseWheel: false - }, - lineDecorationsWidth: 0, - overviewRulerBorder: false, - scrollBeyondLastLine: false, - renderLineHighlight: 'none', - fixedOverflowWidgets: true, - dragAndDrop: false, - revealHorizontalRightPadding: 5, - minimap: { enabled: false }, - guides: { indentation: false }, - rulers: [], - renderWhitespace: 'none', - dropIntoEditor: { enabled: true }, - quickSuggestions: false, - suggest: { - showIcons: false, - showSnippets: false, - showWords: true, - showStatusBar: false, - }, - wordWrap: 'on' - }, { isSimpleWidget: true })); - this._register(this._terminalCommandWidget.onDidFocusEditorText(() => this._responseEditorFocusedContextKey.set(true))); - this._register(this._terminalCommandWidget.onDidBlurEditorText(() => this._responseEditorFocusedContextKey.set(false))); - this._register(Event.any(this._terminalCommandWidget.onDidChangeModelContent, this._terminalCommandWidget.onDidChangeModelDecorations)(() => { - const height = widget.getContentHeight(); - widget.layout(new Dimension(640, height)); - })); - model = await this._getTextModel(URI.from({ path: `terminal-inline-chat-${this._instance.instanceId}`, scheme: 'terminal-inline-chat', fragment: command })).then((model) => { - if (!model) { - return; - } - widget.layout(new Dimension(640, 0)); - widget.setModel(model); - const height = widget.getContentHeight(); - widget.layout(new Dimension(640, height)); - }); - } else { - this._terminalCommandWidget.setValue(command); - } - if (!model) { - model = this._terminalCommandWidget.getModel(); - } - if (model) { - const languageId = this._getLanguageFromShell(shellType); - model.setLanguage(languageId); - } - } - - private _getLanguageFromShell(shell?: string): string { - switch (shell) { - case 'fish': - return this._languageService.isRegisteredLanguageId('fish') ? 'fish' : 'shellscript'; - case 'zsh': - return this._languageService.isRegisteredLanguageId('zsh') ? 'zsh' : 'shellscript'; - case 'bash': - return this._languageService.isRegisteredLanguageId('bash') ? 'bash' : 'shellscript'; - case 'sh': - return 'shellscript'; - case 'pwsh': - return 'powershell'; - default: - return 'plaintext'; + if (!this._responseEditor) { + this._responseEditor = this._instantiationService.createInstance(TerminalChatResponseEditor, command, shellType, this._container, this._instance); } + this._responseEditor.setValue(command); } renderMessage(message: string, accessibilityRequestId: number, requestId: string): void { @@ -197,13 +100,6 @@ export class TerminalChatWidget extends Disposable { this._chatAccessibilityService.acceptResponse(message, accessibilityRequestId); } - private async _getTextModel(resource: URI): Promise { - const existing = this._modelService.getModel(resource); - if (existing && !existing.isDisposed()) { - return existing; - } - return this._modelService.createModel(resource.fragment, null, resource, false); - } reveal(): void { this._inlineChatWidget.layout(new Dimension(640, 150)); this._container.classList.remove('hide'); @@ -215,7 +111,8 @@ export class TerminalChatWidget extends Disposable { this.hideTerminalCommandWidget(); this._container.classList.add('hide'); this._inlineChatWidget.value = ''; - this._terminalCommandWidget?.setValue(''); + this._responseEditor?.dispose(); + this._responseEditor = undefined; this._inlineChatWidget.updateChatMessage(undefined); this._inlineChatWidget.updateFollowUps(undefined); this._inlineChatWidget.updateProgress(false); @@ -245,7 +142,7 @@ export class TerminalChatWidget extends Disposable { } acceptCommand(shouldExecute: boolean): void { // Trim command to remove any whitespace, otherwise this may execute the command - const value = this._terminalCommandWidget?.getValue().trim(); + const value = this._responseEditor?.getValue().trim(); if (!value) { return; } @@ -265,3 +162,140 @@ export class TerminalChatWidget extends Disposable { this._terminalCommandWidgetContainer?.classList.remove('hide'); } } + +class TerminalChatResponseEditor extends Disposable { + private readonly _editorContainer: HTMLElement; + private readonly _editor: CodeEditorWidget; + + private readonly _responseEditorFocusedContextKey: IContextKey; + + readonly model: Promise; + + constructor( + initialCommandResponse: string, + shellType: string | undefined, + private readonly _container: HTMLElement, + private readonly _instance: ITerminalInstance, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ILanguageService private readonly _languageService: ILanguageService, + @IModelService private readonly _modelService: IModelService, + ) { + super(); + + this._responseEditorFocusedContextKey = TerminalContextKeys.chatResponseEditorFocused.bindTo(this._contextKeyService); + + this._editorContainer = document.createElement('div'); + this._editorContainer.classList.add('terminal-inline-chat-response'); + this._container.prepend(this._editorContainer); + this._register(toDisposable(() => this._editorContainer.remove())); + const editor = this._register(this._instantiationService.createInstance(CodeEditorWidget, this._editorContainer, { + readOnly: false, + ariaLabel: this._getAriaLabel(), + fontSize: 13, + lineHeight: 20, + padding: { top: 8, bottom: 8 }, + overviewRulerLanes: 0, + glyphMargin: false, + lineNumbers: 'off', + folding: false, + hideCursorInOverviewRuler: true, + selectOnLineNumbers: false, + selectionHighlight: false, + scrollbar: { + useShadows: false, + vertical: 'hidden', + horizontal: 'hidden', + alwaysConsumeMouseWheel: false + }, + lineDecorationsWidth: 0, + overviewRulerBorder: false, + scrollBeyondLastLine: false, + renderLineHighlight: 'none', + fixedOverflowWidgets: true, + dragAndDrop: false, + revealHorizontalRightPadding: 5, + minimap: { enabled: false }, + guides: { indentation: false }, + rulers: [], + renderWhitespace: 'none', + dropIntoEditor: { enabled: true }, + quickSuggestions: false, + suggest: { + showIcons: false, + showSnippets: false, + showWords: true, + showStatusBar: false, + }, + wordWrap: 'on' + }, { isSimpleWidget: true })); + this._editor = editor; + this._register(editor.onDidFocusEditorText(() => this._responseEditorFocusedContextKey.set(true))); + this._register(editor.onDidBlurEditorText(() => this._responseEditorFocusedContextKey.set(false))); + this._register(Event.any(editor.onDidChangeModelContent, editor.onDidChangeModelDecorations)(() => { + const height = editor.getContentHeight(); + editor.layout(new Dimension(640, height)); + })); + + this.model = this._getTextModel(URI.from({ + path: `terminal-inline-chat-${this._instance.instanceId}`, + scheme: 'terminal-inline-chat', + fragment: initialCommandResponse + })); + this.model.then(model => { + if (model) { + // Initial layout + editor.layout(new Dimension(640, 0)); + editor.setModel(model); + const height = editor.getContentHeight(); + editor.layout(new Dimension(640, height)); + + // Initialize language + const languageId = this._getLanguageFromShell(shellType); + model.setLanguage(languageId); + } + }); + } + + private _getAriaLabel(): string { + const verbose = this._configurationService.getValue(AccessibilityVerbositySettingId.Chat); + if (verbose) { + // TODO: Add verbose description + } + return localize('terminalChatInput', "Terminal Chat Input"); + } + + private async _getTextModel(resource: URI): Promise { + const existing = this._modelService.getModel(resource); + if (existing && !existing.isDisposed()) { + return existing; + } + return this._modelService.createModel(resource.fragment, null, resource, false); + } + + private _getLanguageFromShell(shell?: string): string { + switch (shell) { + case 'fish': + return this._languageService.isRegisteredLanguageId('fish') ? 'fish' : 'shellscript'; + case 'zsh': + return this._languageService.isRegisteredLanguageId('zsh') ? 'zsh' : 'shellscript'; + case 'bash': + return this._languageService.isRegisteredLanguageId('bash') ? 'bash' : 'shellscript'; + case 'sh': + return 'shellscript'; + case 'pwsh': + return 'powershell'; + default: + return 'plaintext'; + } + } + + setValue(value: string) { + this._editor.setValue(value); + } + + getValue(): string { + return this._editor.getValue(); + } +} From 98e01f9016d920127d70b468731b75e5ae65dfb4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 14:07:54 -0600 Subject: [PATCH 0092/1175] rm unused --- .../terminalContrib/chat/browser/terminalChatWidget.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 014f746c786..8448d7e5c61 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -121,10 +121,6 @@ export class TerminalChatWidget extends Disposable { this._visibleContextKey.set(false); this._instance.focus(); } - cancel(): void { - // TODO: Impl - this._inlineChatWidget.value = ''; - } focus(): void { this._inlineChatWidget.focus(); } From 204e5ba8295196b5305ae15747ddfbb7159405e1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 12:08:26 -0800 Subject: [PATCH 0093/1175] Register chat widget against contr --- .../chat/browser/terminalChatController.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 8f9c3defee0..e609ce9067f 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -116,17 +116,17 @@ export class TerminalChatController extends Disposable implements ITerminalContr } this._chatWidget = new Lazy(() => { - const chatWidget = this._instantiationService.createInstance(TerminalChatWidget, this._instance.domElement!, this._instance); - chatWidget.focusTracker.onDidFocus(() => { + const chatWidget = this._register(this._instantiationService.createInstance(TerminalChatWidget, this._instance.domElement!, this._instance)); + this._register(chatWidget.focusTracker.onDidFocus(() => { TerminalChatController.activeChatWidget = this; if (!isDetachedTerminalInstance(this._instance)) { this._terminalService.setActiveInstance(this._instance); } - }); - chatWidget.focusTracker.onDidBlur(() => { + })); + this._register(chatWidget.focusTracker.onDidBlur(() => { TerminalChatController.activeChatWidget = undefined; this._instance.resetScrollbarVisibility(); - }); + })); if (!this._instance.domElement) { throw new Error('FindWidget expected terminal DOM to be initialized'); } From 2453accbe856fa1a9266459fda3d84572b1fdd89 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 12:09:49 -0800 Subject: [PATCH 0094/1175] Pull placeholder from model --- .../terminalContrib/chat/browser/terminalChatController.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index e609ce9067f..4c0ef9145d8 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -155,9 +155,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr } private _getPlaceholderText(): string { - return this._forcedPlaceholder ?? ''; - // TODO: Pass through session placeholder - // return this._forcedPlaceholder ?? this._session?.session.placeholder ?? ''; + return this._forcedPlaceholder ?? this._model?.inputPlaceholder ?? ''; } setPlaceholder(text: string): void { From 342570da6a7973b7030b00ae45ecb524944bb64a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 12:14:59 -0800 Subject: [PATCH 0095/1175] Bring back initial placeholder and info on hide --- .../terminalContrib/chat/browser/terminalChatWidget.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 8448d7e5c61..a4bf3b55990 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -78,13 +78,17 @@ export class TerminalChatWidget extends Disposable { feedbackMenuId: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK } ); - this._inlineChatWidget.placeholder = localize('default.placeholder', "Ask how to do something in the terminal"); - this._inlineChatWidget.updateInfo(localize('welcome.1', "AI-generated commands may be incorrect")); + this._reset(); this._container.appendChild(this._inlineChatWidget.domNode); this._focusTracker = this._register(trackFocus(this._container)); } + private _reset() { + this._inlineChatWidget.placeholder = localize('default.placeholder', "Ask how to do something in the terminal"); + this._inlineChatWidget.updateInfo(localize('welcome.1', "AI-generated commands may be incorrect")); + } + async renderTerminalCommand(command: string, requestId: number, shellType?: string): Promise { this._chatAccessibilityService.acceptResponse(command, requestId); this.showTerminalCommandWidget(); @@ -110,7 +114,7 @@ export class TerminalChatWidget extends Disposable { hide(): void { this.hideTerminalCommandWidget(); this._container.classList.add('hide'); - this._inlineChatWidget.value = ''; + this._reset(); this._responseEditor?.dispose(); this._responseEditor = undefined; this._inlineChatWidget.updateChatMessage(undefined); From 4de80e1c389db99170b22f785587c7b93bcb5f3f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 12:19:11 -0800 Subject: [PATCH 0096/1175] Handle hide/show in editor class --- .../chat/browser/terminalChatWidget.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index a4bf3b55990..499d7e375da 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Dimension, IFocusTracker, trackFocus } from 'vs/base/browser/dom'; +import { Dimension, IFocusTracker, hide, show, trackFocus } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -32,7 +32,6 @@ export class TerminalChatWidget extends Disposable { private readonly _inlineChatWidget: InlineChatWidget; public get inlineChatWidget(): InlineChatWidget { return this._inlineChatWidget; } - private _terminalCommandWidgetContainer: HTMLElement | undefined; private _responseEditor: TerminalChatResponseEditor | undefined; private readonly _focusTracker: IFocusTracker; @@ -91,7 +90,7 @@ export class TerminalChatWidget extends Disposable { async renderTerminalCommand(command: string, requestId: number, shellType?: string): Promise { this._chatAccessibilityService.acceptResponse(command, requestId); - this.showTerminalCommandWidget(); + this._responseEditor?.show(); if (!this._responseEditor) { this._responseEditor = this._instantiationService.createInstance(TerminalChatResponseEditor, command, shellType, this._container, this._instance); } @@ -99,7 +98,7 @@ export class TerminalChatWidget extends Disposable { } renderMessage(message: string, accessibilityRequestId: number, requestId: string): void { - this.hideTerminalCommandWidget(); + this._responseEditor?.hide(); this._inlineChatWidget.updateChatMessage({ message: new MarkdownString(message), requestId, providerId: 'terminal' }); this._chatAccessibilityService.acceptResponse(message, accessibilityRequestId); } @@ -112,7 +111,6 @@ export class TerminalChatWidget extends Disposable { this._inlineChatWidget.focus(); } hide(): void { - this.hideTerminalCommandWidget(); this._container.classList.add('hide'); this._reset(); this._responseEditor?.dispose(); @@ -137,7 +135,7 @@ export class TerminalChatWidget extends Disposable { setValue(value?: string) { this._inlineChatWidget.value = value ?? ''; if (!value) { - this.hideTerminalCommandWidget(); + this._responseEditor?.hide(); } } acceptCommand(shouldExecute: boolean): void { @@ -155,12 +153,6 @@ export class TerminalChatWidget extends Disposable { public get focusTracker(): IFocusTracker { return this._focusTracker; } - hideTerminalCommandWidget(): void { - this._terminalCommandWidgetContainer?.classList.add('hide'); - } - showTerminalCommandWidget(): void { - this._terminalCommandWidgetContainer?.classList.remove('hide'); - } } class TerminalChatResponseEditor extends Disposable { @@ -298,4 +290,12 @@ class TerminalChatResponseEditor extends Disposable { getValue(): string { return this._editor.getValue(); } + + hide() { + hide(this._editorContainer); + } + + show() { + show(this._editorContainer); + } } From 556c0e67c9df1257fefbf9fada21a519ee6c4897 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 14:25:55 -0600 Subject: [PATCH 0097/1175] await initialization --- .../terminalContrib/chat/browser/terminalChatController.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 4c0ef9145d8..c96dd7326eb 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -217,8 +217,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr // TODO: ? variables: { variables: [] }, }; - // TODO: fix requester usrname, responder username - this._model?.initialize({ id: this._requestId, requesterUsername: 'userGesture', responderUsername: 'terminal' }, undefined); + await this._model?.waitForInitialization(); const request: IParsedChatRequest = { text: this._lastInput, parts: [] From 7162515cd775bae7eec4af2b04f3ba6d6574bb08 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 14:40:35 -0600 Subject: [PATCH 0098/1175] open in chat view --- .../terminalContrib/chat/browser/terminalChatActions.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 3ad13dbb898..1a813210025 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -144,15 +144,20 @@ registerActiveXtermAction({ ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.chatRequestActive.negate(), TerminalContextKeys.chatAgentRegistered, - CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyMessages) ), icon: Codicon.commentDiscussion, - menu: { + menu: [{ id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 1, when: ContextKeyExpr.and(CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyMessages), TerminalContextKeys.chatRequestActive.negate()), }, + { + id: MENU_TERMINAL_CHAT_WIDGET, + group: 'main', + order: 1, + when: ContextKeyExpr.and(CTX_INLINE_CHAT_EMPTY.negate(), CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.Empty), TerminalContextKeys.chatRequestActive.negate()), + }], run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { return; From 2408cc098b7feb83e9b48054e3e59b90dfb444e2 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 14:48:23 -0600 Subject: [PATCH 0099/1175] if no model, focus input like quick chat does --- .../terminalContrib/chat/browser/terminalChatController.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index c96dd7326eb..9c274d92b9a 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -316,6 +316,8 @@ export class TerminalChatController extends Disposable implements ITerminalContr } } widget.focusLastMessage(); + } else if (!this._model) { + widget?.focusInput(); } } From a3602c3db9bb1c8fc41a050c727fe8a5090dfbc6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 15 Feb 2024 12:53:30 -0800 Subject: [PATCH 0100/1175] Remove margin between chat and response --- .../chat/browser/media/terminalChatWidget.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css index 1feb4831c23..25d492a063c 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css @@ -11,6 +11,10 @@ height: auto !important; } +.terminal-inline-chat .inline-chat { + margin-top: 0 !important; +} + .terminal-inline-chat .terminal-inline-chat-response { border: 1px solid var(--vscode-input-border, transparent); /* TODO: Make themeable */ @@ -28,6 +32,5 @@ } .terminal-inline-chat .terminal-inline-chat-response { - margin: 0 0 8px 0; padding-left: 8px; } From 3c0c5106b81dc4f2c169415e816b361d6d13010a Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 15:08:56 -0600 Subject: [PATCH 0101/1175] add chatResponseType context key for terminal --- .../terminal/common/terminalContextKey.ts | 9 +++++++++ .../chat/browser/terminalChatActions.ts | 16 ++++++++-------- .../chat/browser/terminalChatController.ts | 11 +++++------ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index 8ea1469a4b1..dcca7e64e68 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -45,6 +45,12 @@ export const enum TerminalContextKeyStrings { ChatInputHasText = 'terminalChatInputHasText', ChatAgentRegistered = 'terminalChatAgentRegistered', ChatResponseEditorFocused = 'terminalChatResponseEditorFocused', + ChatResponseType = 'terminalChatResponseType', +} + +export const enum TerminalChatResponseTypes { + Message = 'message', + TerminalCommand = 'terminalCommand' } export namespace TerminalContextKeys { @@ -183,4 +189,7 @@ export namespace TerminalContextKeys { /** Whether the chat response editor is focused */ export const chatResponseEditorFocused = new RawContextKey(TerminalContextKeyStrings.ChatResponseEditorFocused, false, localize('chatResponseEditorFocusedContextKey', "Whether the chat response editor is focused.")); + + /** The type of chat response, if any */ + export const chatResponseType = new RawContextKey(TerminalContextKeyStrings.ChatResponseType, undefined, localize('chatResponseTypeContextKey', "The type of chat response, if any")); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 1a813210025..aa8f4e045d1 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -9,10 +9,10 @@ import { localize2 } from 'vs/nls'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; -import { CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; -import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { TerminalChatResponseTypes, TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, MENU_TERMINAL_CHAT_WIDGET_STATUS, TerminalChatCommandId } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; @@ -81,7 +81,7 @@ registerActiveXtermAction({ ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.chatRequestActive.negate(), TerminalContextKeys.chatAgentRegistered, - CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.Empty) + TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) ), icon: Codicon.check, keybinding: { @@ -93,7 +93,7 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 0, - when: ContextKeyExpr.and(CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.Empty), TerminalContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalContextKeys.chatRequestActive.negate()), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -113,7 +113,7 @@ registerActiveXtermAction({ ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.chatRequestActive.negate(), TerminalContextKeys.chatAgentRegistered, - CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.Empty) + TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) ), icon: Codicon.check, keybinding: { @@ -125,7 +125,7 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 1, - when: ContextKeyExpr.and(CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.Empty), TerminalContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalContextKeys.chatRequestActive.negate()), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -150,13 +150,13 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 1, - when: ContextKeyExpr.and(CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.OnlyMessages), TerminalContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.Message), TerminalContextKeys.chatRequestActive.negate()), }, { id: MENU_TERMINAL_CHAT_WIDGET, group: 'main', order: 1, - when: ContextKeyExpr.and(CTX_INLINE_CHAT_EMPTY.negate(), CTX_INLINE_CHAT_RESPONSE_TYPES.isEqualTo(InlineChatResponseTypes.Empty), TerminalContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(CTX_INLINE_CHAT_EMPTY.negate(), TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalContextKeys.chatRequestActive.negate()), }], run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 9c274d92b9a..fd5f072846b 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -19,11 +19,10 @@ import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/cont import { IChatAgentService, IChatAgentRequest } from 'vs/workbench/contrib/chat/common/chatAgents'; import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { IChatService, IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; -import { InlineChatResponseTypes, CTX_INLINE_CHAT_RESPONSE_TYPES } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; -import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { TerminalChatResponseTypes, TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget'; @@ -56,7 +55,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr private readonly _requestActiveContextKey!: IContextKey; private readonly _terminalAgentRegisteredContextKey!: IContextKey; - private readonly _lastResponseTypeContextKey!: IContextKey; + private readonly _responseTypeContextKey!: IContextKey; private _requestId: number = 0; private _messages = this._store.add(new Emitter()); @@ -97,7 +96,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr } this._requestActiveContextKey = TerminalContextKeys.chatRequestActive.bindTo(this._contextKeyService); this._terminalAgentRegisteredContextKey = TerminalContextKeys.chatAgentRegistered.bindTo(this._contextKeyService); - this._lastResponseTypeContextKey = CTX_INLINE_CHAT_RESPONSE_TYPES.bindTo(this._contextKeyService); + this._responseTypeContextKey = TerminalContextKeys.chatResponseType.bindTo(this._contextKeyService); if (!this._chatAgentService.hasAgent(this._terminalAgentId)) { this._register(this._chatAgentService.onDidChangeAgents(() => { @@ -257,10 +256,10 @@ export class TerminalChatController extends Disposable implements ITerminalContr } if (codeBlock) { this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._requestId, shellType); - this._lastResponseTypeContextKey.set(InlineChatResponseTypes.Empty); + this._responseTypeContextKey.set(TerminalChatResponseTypes.TerminalCommand); } else { this._chatWidget?.rawValue?.renderMessage(responseContent, this._requestId, requestId); - this._lastResponseTypeContextKey.set(InlineChatResponseTypes.OnlyMessages); + this._responseTypeContextKey.set(TerminalChatResponseTypes.Message); } this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); this._messages.fire(Message.ACCEPT_INPUT); From de3d5a4c4b128f59afa06a25253c1d243dd720e4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 15:30:14 -0600 Subject: [PATCH 0102/1175] rm terminal stuff from inline chat actions --- .../contrib/inlineChat/electron-sandbox/inlineChatActions.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts index 57c29d41e9d..99862b01d7b 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts @@ -20,17 +20,16 @@ import { HasSpeechProvider, ISpeechService } from 'vs/workbench/contrib/speech/c import { localize2 } from 'vs/nls'; import { Action2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; export class HoldToSpeak extends AbstractInlineChatAction { constructor() { super({ id: 'inlineChat.holdForSpeech', - precondition: ContextKeyExpr.and(HasSpeechProvider, ContextKeyExpr.or(CTX_INLINE_CHAT_VISIBLE, TerminalContextKeys.chatVisible)), + precondition: ContextKeyExpr.and(HasSpeechProvider, CTX_INLINE_CHAT_VISIBLE), title: localize2('holdForSpeech', "Hold for Speech"), keybinding: { - when: ContextKeyExpr.or(EditorContextKeys.textInputFocus, TerminalContextKeys.chatFocused), + when: EditorContextKeys.textInputFocus, weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.KeyI, }, From a9f226d8eba2370051e88cfedf8e47f716e261f8 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 16:02:41 -0600 Subject: [PATCH 0103/1175] fix requestId, sessionId, wip feedback --- .../chat/browser/terminalChatActions.ts | 28 ++++++++---- .../chat/browser/terminalChatController.ts | 43 +++++++++++++------ 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index aa8f4e045d1..187d16f6d51 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -226,7 +226,7 @@ registerActiveXtermAction({ title: localize2('feedbackHelpful', 'Helpful'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalContextKeys.chatRequestActive, + TerminalContextKeys.chatResponseType.notEqualsTo(undefined) ), // TODO: toggled: CTX_INLINE_CHAT_LAST_FEEDBACK.isEqualTo('helpful'), icon: Codicon.thumbsup, @@ -234,11 +234,14 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, group: 'inline', order: 1, - // TODO: Fill in ctx - // when: CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.notEqualsTo(undefined), + when: TerminalContextKeys.chatResponseType.notEqualsTo(undefined), }, run: (_xterm, _accessor, activeInstance) => { - // TODO: Impl + if (isDetachedTerminalInstance(activeInstance)) { + return; + } + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + contr?.acceptFeedback(true); } }); @@ -247,7 +250,7 @@ registerActiveXtermAction({ title: localize2('feedbackUnhelpful', 'Unhelpful'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalContextKeys.chatRequestActive, + TerminalContextKeys.chatResponseType.notEqualsTo(undefined), ), // TODO: toggled: CTX_INLINE_CHAT_LAST_FEEDBACK.isEqualTo('unhelpful'), icon: Codicon.thumbsdown, @@ -255,11 +258,14 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, group: 'inline', order: 2, - // TODO: Fill in ctx - // when: CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.notEqualsTo(undefined), + when: TerminalContextKeys.chatResponseType.notEqualsTo(undefined), }, run: (_xterm, _accessor, activeInstance) => { - // TODO: Impl + if (isDetachedTerminalInstance(activeInstance)) { + return; + } + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + contr?.acceptFeedback(false); } }); @@ -284,7 +290,11 @@ registerActiveXtermAction({ order: 3 }], run: (_xterm, _accessor, activeInstance) => { - // TODO: Impl + // if (isDetachedTerminalInstance(activeInstance)) { + // return; + // } + // const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + // contr?.acceptFeedback(true); } }); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index fd5f072846b..030e2bb8fdf 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -9,7 +9,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Lazy } from 'vs/base/common/lazy'; import { Disposable } from 'vs/base/common/lifecycle'; import { marked } from 'vs/base/common/marked/marked'; -import { generateUuid } from 'vs/base/common/uuid'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -18,7 +17,7 @@ import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatAgentService, IChatAgentRequest } from 'vs/workbench/contrib/chat/common/chatAgents'; import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChatService, IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatService, IChatProgress, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -134,6 +133,25 @@ export class TerminalChatController extends Disposable implements ITerminalContr }); } + acceptFeedback(helpful: boolean): void { + const providerId = this._chatService.getProviderInfos()?.[0]?.id; + if (!providerId || !this._currentRequest || !this._model) { + return; + } + this._chatService.notifyUserAction({ + providerId, + sessionId: this._model?.sessionId, + requestId: this._currentRequest.id, + agentId: this._terminalAgentId, + //TODO: fill in error details if any etc. + result: {}, + action: { + kind: 'vote', + direction: helpful ? InteractiveSessionVoteDirection.Up : InteractiveSessionVoteDirection.Down + }, + }); + } + cancel(): void { if (this._currentRequest) { this._model?.cancelRequest(this._currentRequest); @@ -175,6 +193,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._model = undefined; this._chatWidget?.rawValue?.hide(); this._chatWidget?.rawValue?.setValue(undefined); + this._responseTypeContextKey.reset(); } private updateModel(): void { @@ -207,15 +226,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._model?.acceptResponseProgress(this._currentRequest, progress); } }; - const requestId = generateUuid(); - const requestProps: IChatAgentRequest = { - sessionId: generateUuid(), - requestId, - agentId: this._terminalAgentId, - message: this._lastInput, - // TODO: ? - variables: { variables: [] }, - }; + await this._model?.waitForInitialization(); const request: IParsedChatRequest = { text: this._lastInput, @@ -225,6 +236,14 @@ export class TerminalChatController extends Disposable implements ITerminalContr variables: [] }; this._currentRequest = this._model?.addRequest(request, requestVarData); + const requestProps: IChatAgentRequest = { + sessionId: this._model!.sessionId, + requestId: this._currentRequest!.id, + agentId: this._terminalAgentId, + message: this._lastInput, + // TODO: ? + variables: { variables: [] }, + }; try { const task = this._chatAgentService.invokeAgent(this._terminalAgentId, requestProps, progressCallback, [], cancellationToken); this._chatWidget?.rawValue?.inlineChatWidget.updateChatMessage(undefined); @@ -258,7 +277,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.renderTerminalCommand(codeBlock, this._requestId, shellType); this._responseTypeContextKey.set(TerminalChatResponseTypes.TerminalCommand); } else { - this._chatWidget?.rawValue?.renderMessage(responseContent, this._requestId, requestId); + this._chatWidget?.rawValue?.renderMessage(responseContent, this._requestId, this._currentRequest!.id); this._responseTypeContextKey.set(TerminalChatResponseTypes.Message); } this._chatWidget?.rawValue?.inlineChatWidget.updateToolbar(true); From 4b295eda6f9de1762e44a818085d1736976bef8f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 16:08:57 -0600 Subject: [PATCH 0104/1175] fix feedback action --- .../chat/browser/terminalChatController.ts | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 030e2bb8fdf..baa3943274c 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -138,18 +138,22 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (!providerId || !this._currentRequest || !this._model) { return; } - this._chatService.notifyUserAction({ - providerId, - sessionId: this._model?.sessionId, - requestId: this._currentRequest.id, - agentId: this._terminalAgentId, - //TODO: fill in error details if any etc. - result: {}, - action: { - kind: 'vote', - direction: helpful ? InteractiveSessionVoteDirection.Up : InteractiveSessionVoteDirection.Down - }, - }); + // TODO:extract into helper method + for (const request of this._model.getRequests()) { + if (request.response?.response.value || request.response?.result) { + this._chatService.notifyUserAction({ + providerId, + sessionId: request.session.sessionId, + requestId: request.id, + agentId: request.response?.agent?.id, + result: request.response?.result, + action: { + kind: 'vote', + direction: helpful ? InteractiveSessionVoteDirection.Up : InteractiveSessionVoteDirection.Down + }, + }); + } + } } cancel(): void { From ed4a3ce3ec379421c165b7fb1af78c5c241dd95d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 15 Feb 2024 16:12:04 -0600 Subject: [PATCH 0105/1175] fix feedback action, add styling --- .../terminalContrib/chat/browser/terminalChatController.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index baa3943274c..f8e97671171 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -154,6 +154,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr }); } } + this._chatWidget?.rawValue?.inlineChatWidget.updateStatus('Thank you for your feedback!', { resetAfter: 1250 }); } cancel(): void { From 559a3413bc85864e22f29df1fb7a207d5aba7590 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 16 Feb 2024 08:57:13 -0600 Subject: [PATCH 0106/1175] add support for issue reporting when terminal agent supports that --- .../terminal/common/terminalContextKey.ts | 4 +++ .../chat/browser/terminalChatActions.ts | 36 +++++++++---------- .../chat/browser/terminalChatController.ts | 24 +++++++++---- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index dcca7e64e68..d823a5b7fd9 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -46,6 +46,7 @@ export const enum TerminalContextKeyStrings { ChatAgentRegistered = 'terminalChatAgentRegistered', ChatResponseEditorFocused = 'terminalChatResponseEditorFocused', ChatResponseType = 'terminalChatResponseType', + ChatResponseSupportsIssueReporting = 'terminalChatResponseSupportsIssueReporting' } export const enum TerminalChatResponseTypes { @@ -192,4 +193,7 @@ export namespace TerminalContextKeys { /** The type of chat response, if any */ export const chatResponseType = new RawContextKey(TerminalContextKeyStrings.ChatResponseType, undefined, localize('chatResponseTypeContextKey', "The type of chat response, if any")); + + /** Whether the response supports issue reporting */ + export const chatResponseSupportsIssueReporting = new RawContextKey(TerminalContextKeyStrings.ChatResponseSupportsIssueReporting, false, localize('chatResponseSupportsIssueReportingContextKey', "Whether the response supports issue reporting")); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 187d16f6d51..d1586f8c4ba 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -228,7 +228,6 @@ registerActiveXtermAction({ ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), TerminalContextKeys.chatResponseType.notEqualsTo(undefined) ), - // TODO: toggled: CTX_INLINE_CHAT_LAST_FEEDBACK.isEqualTo('helpful'), icon: Codicon.thumbsup, menu: { id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, @@ -252,7 +251,6 @@ registerActiveXtermAction({ ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), TerminalContextKeys.chatResponseType.notEqualsTo(undefined), ), - // TODO: toggled: CTX_INLINE_CHAT_LAST_FEEDBACK.isEqualTo('unhelpful'), icon: Codicon.thumbsdown, menu: { id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, @@ -274,27 +272,29 @@ registerActiveXtermAction({ title: localize2('reportIssue', 'Report Issue'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalContextKeys.chatRequestActive, + TerminalContextKeys.chatRequestActive.negate(), + TerminalContextKeys.chatResponseType.notEqualsTo(undefined), + TerminalContextKeys.chatResponseSupportsIssueReporting ), - // TODO: precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.Empty)), icon: Codicon.report, - menu: [/*{ - // TODO: Enable this + menu: [{ id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, - when: ContextKeyExpr.and(CTX_TERMINAL_CHAT_SUPPORT_ISSUE_REPORTING, CTX_TERMINAL_CHAT_RESPONSE_TYPES.notEqualsTo(InlineChatResponseTypes.Empty)), - group: '2_feedback', + when: ContextKeyExpr.and(TerminalContextKeys.chatResponseType.notEqualsTo(undefined), TerminalContextKeys.chatResponseSupportsIssueReporting), + group: 'inline', order: 3 - }, */{ - id: MENU_TERMINAL_CHAT_WIDGET, - group: 'config', - order: 3 - }], + }], + // { + // id: MENU_TERMINAL_CHAT_WIDGET, + // when: ContextKeyExpr.and(TerminalContextKeys.chatResponseType.notEqualsTo(undefined), TerminalContextKeys.chatResponseSupportsIssueReporting), + // group: 'config', + // order: 3 + // }], run: (_xterm, _accessor, activeInstance) => { - // if (isDetachedTerminalInstance(activeInstance)) { - // return; - // } - // const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); - // contr?.acceptFeedback(true); + if (isDetachedTerminalInstance(activeInstance)) { + return; + } + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + contr?.acceptFeedback(); } }); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index f8e97671171..996effa0743 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -17,7 +17,7 @@ import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatAgentService, IChatAgentRequest } from 'vs/workbench/contrib/chat/common/chatAgents'; import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChatService, IChatProgress, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatService, IChatProgress, InteractiveSessionVoteDirection, ChatUserAction } from 'vs/workbench/contrib/chat/common/chatService'; import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -55,6 +55,8 @@ export class TerminalChatController extends Disposable implements ITerminalContr private readonly _requestActiveContextKey!: IContextKey; private readonly _terminalAgentRegisteredContextKey!: IContextKey; private readonly _responseTypeContextKey!: IContextKey; + private readonly __responseSupportsIssueReportingContextKey!: IContextKey; + private _requestId: number = 0; private _messages = this._store.add(new Emitter()); @@ -96,6 +98,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._requestActiveContextKey = TerminalContextKeys.chatRequestActive.bindTo(this._contextKeyService); this._terminalAgentRegisteredContextKey = TerminalContextKeys.chatAgentRegistered.bindTo(this._contextKeyService); this._responseTypeContextKey = TerminalContextKeys.chatResponseType.bindTo(this._contextKeyService); + this.__responseSupportsIssueReportingContextKey = TerminalContextKeys.chatResponseSupportsIssueReporting.bindTo(this._contextKeyService); if (!this._chatAgentService.hasAgent(this._terminalAgentId)) { this._register(this._chatAgentService.onDidChangeAgents(() => { @@ -133,11 +136,17 @@ export class TerminalChatController extends Disposable implements ITerminalContr }); } - acceptFeedback(helpful: boolean): void { + acceptFeedback(helpful?: boolean): void { const providerId = this._chatService.getProviderInfos()?.[0]?.id; if (!providerId || !this._currentRequest || !this._model) { return; } + let action: ChatUserAction; + if (helpful === undefined) { + action = { kind: 'bug' }; + } else { + action = { kind: 'vote', direction: helpful ? InteractiveSessionVoteDirection.Up : InteractiveSessionVoteDirection.Down }; + } // TODO:extract into helper method for (const request of this._model.getRequests()) { if (request.response?.response.value || request.response?.result) { @@ -147,10 +156,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr requestId: request.id, agentId: request.response?.agent?.id, result: request.response?.result, - action: { - kind: 'vote', - direction: helpful ? InteractiveSessionVoteDirection.Up : InteractiveSessionVoteDirection.Down - }, + action }); } } @@ -267,8 +273,12 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (this._currentRequest) { this._model?.completeResponse(this._currentRequest); } + const supportIssueReporting = this._currentRequest?.response?.agent?.metadata?.supportIssueReporting; + if (supportIssueReporting !== undefined) { + this.__responseSupportsIssueReportingContextKey.set(supportIssueReporting); + } + this._lastResponseContent = responseContent; } - this._lastResponseContent = responseContent; const firstCodeBlockContent = marked.lexer(responseContent).filter(token => token.type === 'code')?.[0]?.raw; const regex = /```(?\w+)\n(?[\s\S]*?)```/g; const match = regex.exec(firstCodeBlockContent); From 705d61ad975a564f0b7b30d6c3e12fefda85dfb3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 16 Feb 2024 09:02:11 -0600 Subject: [PATCH 0107/1175] rm todo --- .../terminalContrib/chat/browser/terminalChatController.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 996effa0743..70ee41baa68 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -66,7 +66,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr private _lastInput: string | undefined; private _lastResponseContent: string | undefined; get lastResponseContent(): string | undefined { - // TODO: use model return this._lastResponseContent; } From 881321da5cfac6fe0ce6894fe40b2ba6623cba0c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 16 Feb 2024 09:22:53 -0600 Subject: [PATCH 0108/1175] fix aria label for terminal response editor --- .../chat/browser/terminalChatAccessibilityHelp.ts | 6 ++++-- .../terminalContrib/chat/browser/terminalChatWidget.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts index d851609de14..890fb103292 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts @@ -48,7 +48,8 @@ export function getAccessibilityHelpText(accessor: ServicesAccessor): string { const keybindingService = accessor.get(IKeybindingService); const content = []; const openAccessibleViewKeybinding = keybindingService.lookupKeybinding('editor.action.accessibleView')?.getAriaLabel(); - const acceptCommandKeybinding = keybindingService.lookupKeybinding(TerminalChatCommandId.RunCommand)?.getAriaLabel(); + const runCommandKeybinding = keybindingService.lookupKeybinding(TerminalChatCommandId.RunCommand)?.getAriaLabel(); + const insertCommandKeybinding = keybindingService.lookupKeybinding(TerminalChatCommandId.InsertCommand)?.getAriaLabel(); const makeRequestKeybinding = keybindingService.lookupKeybinding(TerminalChatCommandId.MakeRequest)?.getAriaLabel(); //TODO: using this instead of the terminal command bc by definition the inline terminal chat is focused when this dialog is invoked. const startChatKeybinding = keybindingService.lookupKeybinding('inlineChat.start')?.getAriaLabel(); @@ -58,7 +59,8 @@ export function getAccessibilityHelpText(accessor: ServicesAccessor): string { content.push(localize('inlineChat.results', "A result may contain a terminal command or just a message. In either case, the result will be announced.")); content.push(openAccessibleViewKeybinding ? localize('inlineChat.inspectResponseMessage', 'If just a message comes back, it can be inspected in the accessible view ({0}).', openAccessibleViewKeybinding) : localize('inlineChat.inspectResponseNoKb', 'With the input box focused, inspect the response in the accessible view via the Open Accessible View command, which is currently not triggerable by a keybinding.')); content.push(localize('inlineChat.inspectTerminalCommand', 'If a terminal command comes back, it can be inspected in an editor reached via Shift+Tab.')); - content.push(acceptCommandKeybinding ? localize('inlineChat.acceptCommand', 'With focus in the command editor, the Terminal: Accept Chat Command ({0}) action.', acceptCommandKeybinding) : localize('inlineChat.acceptCommandNoKb', 'Accept a command by tabbing to the button as the action is currently not triggerable by a keybinding.')); + content.push(runCommandKeybinding ? localize('inlineChat.runCommand', 'With focus in the input box or command editor, the Terminal: Run Chat Command ({0}) action.', runCommandKeybinding) : localize('inlineChat.runCommandNoKb', 'Run a command by tabbing to the button as the action is currently not triggerable by a keybinding.')); + content.push(insertCommandKeybinding ? localize('inlineChat.insertCommand', 'With focus in the input box command editor, the Terminal: Insert Chat Command ({0}) action.', insertCommandKeybinding) : localize('inlineChat.insertCommandNoKb', 'Insert a command by tabbing to the button as the action is currently not triggerable by a keybinding.')); content.push(localize('inlineChat.toolbar', "Use tab to reach conditional parts like commands, status, message responses and more.")); content.push(localize('chat.signals', "Accessibility Signals can be changed via settings with a prefix of signals.chat. By default, if a request takes more than 4 seconds, you will hear a sound indicating that progress is still occurring.")); return content.join('\n\n'); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 499d7e375da..2cc70c7eb07 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -255,7 +255,7 @@ class TerminalChatResponseEditor extends Disposable { if (verbose) { // TODO: Add verbose description } - return localize('terminalChatInput', "Terminal Chat Input"); + return localize('terminalResponseEditor', "Terminal Response Editor"); } private async _getTextModel(resource: URI): Promise { From ee1cb48bb1005d7299e97f8445783a7c976f9c08 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 16 Feb 2024 10:17:11 -0600 Subject: [PATCH 0109/1175] add discard action --- .../chat/browser/terminalChat.ts | 3 +- .../chat/browser/terminalChatActions.ts | 28 ++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts index d409361d517..a2349bc25c1 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts @@ -7,7 +7,8 @@ import { MenuId } from 'vs/platform/actions/common/actions'; export const enum TerminalChatCommandId { Start = 'workbench.action.terminal.chat.start', - Hide = 'workbench.action.terminal.chat.close', + Close = 'workbench.action.terminal.chat.close', + Discard = 'workbench.action.terminal.chat.discard', MakeRequest = 'workbench.action.terminal.chat.makeRequest', Cancel = 'workbench.action.terminal.chat.cancel', FeedbackHelpful = 'workbench.action.terminal.chat.feedbackHelpful', diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index d1586f8c4ba..abf356fbdff 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -41,7 +41,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalChatCommandId.Hide, + id: TerminalChatCommandId.Close, title: localize2('closeChat', 'Close Chat'), keybinding: { primary: KeyCode.Escape, @@ -70,6 +70,32 @@ registerActiveXtermAction({ }); +registerActiveXtermAction({ + id: TerminalChatCommandId.Discard, + title: localize2('discard', 'Discard'), + icon: Codicon.discard, + menu: { + id: MENU_TERMINAL_CHAT_WIDGET_STATUS, + group: '0_main', + order: 2, + when: ContextKeyExpr.and(TerminalContextKeys.chatFocused, + TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand)) + }, + f1: true, + precondition: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), + ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + TerminalContextKeys.chatFocused, + TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) + ), + run: (_xterm, _accessor, activeInstance) => { + if (isDetachedTerminalInstance(activeInstance)) { + return; + } + const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); + contr?.clear(); + } +}); registerActiveXtermAction({ From 29924c918fc21025c13354b62ff260678fda83db Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 16 Feb 2024 10:32:42 -0600 Subject: [PATCH 0110/1175] add feedback context key, get styling to apply --- .../contrib/terminal/common/terminalContextKey.ts | 6 +++++- .../chat/browser/terminalChatActions.ts | 2 ++ .../chat/browser/terminalChatController.ts | 11 ++++++++--- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index d823a5b7fd9..0c577a9df8a 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -46,7 +46,8 @@ export const enum TerminalContextKeyStrings { ChatAgentRegistered = 'terminalChatAgentRegistered', ChatResponseEditorFocused = 'terminalChatResponseEditorFocused', ChatResponseType = 'terminalChatResponseType', - ChatResponseSupportsIssueReporting = 'terminalChatResponseSupportsIssueReporting' + ChatResponseSupportsIssueReporting = 'terminalChatResponseSupportsIssueReporting', + ChatSessionResponseVote = 'terminalChatSessionResponseVote', } export const enum TerminalChatResponseTypes { @@ -196,4 +197,7 @@ export namespace TerminalContextKeys { /** Whether the response supports issue reporting */ export const chatResponseSupportsIssueReporting = new RawContextKey(TerminalContextKeyStrings.ChatResponseSupportsIssueReporting, false, localize('chatResponseSupportsIssueReportingContextKey', "Whether the response supports issue reporting")); + + /** The chat vote, if any for the response, if any */ + export const chatSessionResponseVote = new RawContextKey(TerminalContextKeyStrings.ChatSessionResponseVote, undefined, { type: 'string', description: localize('interactiveSessionResponseVote', "When the response has been voted up, is set to 'up'. When voted down, is set to 'down'. Otherwise an empty string.") }); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index abf356fbdff..cd984a4d61e 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -255,6 +255,7 @@ registerActiveXtermAction({ TerminalContextKeys.chatResponseType.notEqualsTo(undefined) ), icon: Codicon.thumbsup, + toggled: TerminalContextKeys.chatSessionResponseVote.isEqualTo('up'), menu: { id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, group: 'inline', @@ -277,6 +278,7 @@ registerActiveXtermAction({ ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), TerminalContextKeys.chatResponseType.notEqualsTo(undefined), ), + toggled: TerminalContextKeys.chatSessionResponseVote.isEqualTo('down'), icon: Codicon.thumbsdown, menu: { id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 70ee41baa68..ca3bdbab893 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -55,7 +55,8 @@ export class TerminalChatController extends Disposable implements ITerminalContr private readonly _requestActiveContextKey!: IContextKey; private readonly _terminalAgentRegisteredContextKey!: IContextKey; private readonly _responseTypeContextKey!: IContextKey; - private readonly __responseSupportsIssueReportingContextKey!: IContextKey; + private readonly _responseSupportsIssueReportingContextKey!: IContextKey; + private readonly _sessionResponseVoteContextKey!: IContextKey; private _requestId: number = 0; @@ -97,7 +98,8 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._requestActiveContextKey = TerminalContextKeys.chatRequestActive.bindTo(this._contextKeyService); this._terminalAgentRegisteredContextKey = TerminalContextKeys.chatAgentRegistered.bindTo(this._contextKeyService); this._responseTypeContextKey = TerminalContextKeys.chatResponseType.bindTo(this._contextKeyService); - this.__responseSupportsIssueReportingContextKey = TerminalContextKeys.chatResponseSupportsIssueReporting.bindTo(this._contextKeyService); + this._responseSupportsIssueReportingContextKey = TerminalContextKeys.chatResponseSupportsIssueReporting.bindTo(this._contextKeyService); + this._sessionResponseVoteContextKey = TerminalContextKeys.chatSessionResponseVote.bindTo(this._contextKeyService); if (!this._chatAgentService.hasAgent(this._terminalAgentId)) { this._register(this._chatAgentService.onDidChangeAgents(() => { @@ -144,6 +146,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (helpful === undefined) { action = { kind: 'bug' }; } else { + this._sessionResponseVoteContextKey.set(helpful ? 'up' : 'down'); action = { kind: 'vote', direction: helpful ? InteractiveSessionVoteDirection.Up : InteractiveSessionVoteDirection.Down }; } // TODO:extract into helper method @@ -204,6 +207,8 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.hide(); this._chatWidget?.rawValue?.setValue(undefined); this._responseTypeContextKey.reset(); + this._sessionResponseVoteContextKey.reset(); + this._requestActiveContextKey.reset(); } private updateModel(): void { @@ -274,7 +279,7 @@ export class TerminalChatController extends Disposable implements ITerminalContr } const supportIssueReporting = this._currentRequest?.response?.agent?.metadata?.supportIssueReporting; if (supportIssueReporting !== undefined) { - this.__responseSupportsIssueReportingContextKey.set(supportIssueReporting); + this._responseSupportsIssueReportingContextKey.set(supportIssueReporting); } this._lastResponseContent = responseContent; } From ba24d929651af34cd13b52b21cf88d5521068194 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 16 Feb 2024 13:07:26 -0600 Subject: [PATCH 0111/1175] move css to right file --- .../terminal/browser/media/terminal.css | 18 ------------------ .../chat/browser/media/terminalChatWidget.css | 12 ++++++++++++ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 1aaf1516c5e..488815cf258 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -565,21 +565,3 @@ .monaco-workbench .xterm.terminal.hide { visibility: hidden; } - -.monaco-workbench .terminal-chat-widget { - z-index: 33 !important; - position: absolute; - top: 10px; -} - -.monaco-workbench .terminal-inline-chat.hide { - visibility: hidden; -} - -.monaco-workbench .terminal-inline-chat-response.hide { - visibility: hidden; -} - -.monaco-workbench .terminal-inline-chat .chatMessageContent { - width: 400px !important; -} diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css index 25d492a063c..602560618cc 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css @@ -15,6 +15,18 @@ margin-top: 0 !important; } +.terminal-inline-chat.hide { + visibility: hidden; +} + +.terminal-inline-chat-response.hide { + visibility: hidden; +} + +.terminal-inline-chat .chatMessageContent { + width: 400px !important; +} + .terminal-inline-chat .terminal-inline-chat-response { border: 1px solid var(--vscode-input-border, transparent); /* TODO: Make themeable */ From c57d3061b82d738f3062365127a60660af3929da Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 16 Feb 2024 12:23:50 -0800 Subject: [PATCH 0112/1175] Move a11y help provider into terminal chat folder --- .../browser/accessibilityConfiguration.ts | 2 + .../inlineChat/browser/inlineChatActions.ts | 3 +- .../browser/terminal.chat.contribution.ts | 2 + ...rminalChatAccessibilityHelpContribution.ts | 48 +++++++++++++++++++ 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index bba5a1f648e..ca7ebdca1a0 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -45,6 +45,7 @@ export const enum AccessibilityVerbositySettingId { DiffEditor = 'accessibility.verbosity.diffEditor', Chat = 'accessibility.verbosity.panelChat', InlineChat = 'accessibility.verbosity.inlineChat', + TerminalChat = 'accessibility.verbosity.terminalChat', InlineCompletions = 'accessibility.verbosity.inlineCompletions', KeybindingsEditor = 'accessibility.verbosity.keybindingsEditor', Notebook = 'accessibility.verbosity.notebook', @@ -57,6 +58,7 @@ export const enum AccessibilityVerbositySettingId { export const enum AccessibleViewProviderId { Terminal = 'terminal', + TerminalChat = 'terminal-chat', TerminalHelp = 'terminal-help', DiffEditor = 'diffEditor', Chat = 'panelChat', diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index d9f725ed61d..8deb70a073e 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -30,7 +30,6 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; -import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; CommandsRegistry.registerCommandAlias('interactiveEditor.start', 'inlineChat.start'); CommandsRegistry.registerCommandAlias('interactive.acceptChanges', ACTION_ACCEPT_CHANGES); @@ -786,6 +785,6 @@ export class InlineAccessibilityHelpContribution extends Disposable { return; } runAccessibilityHelpAction(accessor, codeEditor, 'inlineChat'); - }, ContextKeyExpr.and(ContextKeyExpr.or(CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_FOCUSED), TerminalContextKeys.chatFocused.negate()))); + }, ContextKeyExpr.or(CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_FOCUSED))); } } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index 0c2dba50035..0049340fadd 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -8,6 +8,7 @@ import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/brow import { TerminalInlineChatAccessibilityHelpContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp'; import { TerminalInlineChatAccessibleViewContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; +import { TerminalChatAccessibilityHelpContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution'; import 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions'; @@ -15,3 +16,4 @@ registerTerminalContribution(TerminalChatController.ID, TerminalChatController, registerWorkbenchContribution2(TerminalInlineChatAccessibleViewContribution.ID, TerminalInlineChatAccessibleViewContribution, WorkbenchPhase.Eventually); registerWorkbenchContribution2(TerminalInlineChatAccessibilityHelpContribution.ID, TerminalInlineChatAccessibilityHelpContribution, WorkbenchPhase.Eventually); +registerWorkbenchContribution2(TerminalChatAccessibilityHelpContribution.ID, TerminalChatAccessibilityHelpContribution, WorkbenchPhase.Eventually); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts new file mode 100644 index 00000000000..6e5f79e1cb1 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { localize } from 'vs/nls'; +import type { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; +import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; + +export class TerminalChatAccessibilityHelpContribution extends Disposable { + static ID = 'terminalChatAccessiblityHelp'; + constructor() { + super(); + this._register(AccessibilityHelpAction.addImplementation(110, 'terminalChat', runAccessibilityHelpAction, TerminalContextKeys.chatFocused)); + } +} + +export async function runAccessibilityHelpAction(accessor: ServicesAccessor): Promise { + const accessibleViewService = accessor.get(IAccessibleViewService); + const terminalService = accessor.get(ITerminalService); + + const instance = terminalService.activeInstance; + if (!instance) { + return; + } + + const helpText = getAccessibilityHelpText(accessor); + accessibleViewService.show({ + id: AccessibleViewProviderId.TerminalChat, + verbositySettingKey: AccessibilityVerbositySettingId.TerminalChat, + provideContent: () => helpText, + onClose: () => TerminalChatController.get(instance)?.focus(), + options: { type: AccessibleViewType.Help } + }); +} + +export function getAccessibilityHelpText(accessor: ServicesAccessor): string { + const content = []; + // TODO: Fill in more help text + content.push(localize('chat.overview', 'The terminal chat view is comprised of an input box, an editor where suggested commands are provided (Shift+Tab) and buttons to action the suggestion.')); + return content.join('\n\n'); +} From ab75d8d74b5dc30c84c65581e3674ee19f3744bd Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 16 Feb 2024 12:27:24 -0800 Subject: [PATCH 0113/1175] Remove terminal action ids from inline button config provider --- src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts | 2 +- .../contrib/terminalContrib/chat/browser/terminalChatActions.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 537c7b8951f..3aeb25cd2e2 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -388,7 +388,7 @@ export class InlineChatWidget { buttonConfigProvider: action => { if (action.id === ACTION_REGENERATE_RESPONSE) { return { showIcon: true, showLabel: false, isSecondary: true }; - } else if ([ACTION_VIEW_IN_CHAT, ACTION_ACCEPT_CHANGES, 'workbench.action.terminal.chat.acceptCommand', 'workbench.action.terminal.chat.viewInChat'].includes(action.id)) { + } else if ([ACTION_VIEW_IN_CHAT, ACTION_ACCEPT_CHANGES].includes(action.id)) { return { isSecondary: false }; } else { return { isSecondary: true }; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index cd984a4d61e..f374aee77e8 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -116,6 +116,7 @@ registerActiveXtermAction({ primary: KeyMod.CtrlCmd | KeyCode.Enter, }, menu: { + // TODO: Allow action to be made primary, the action list is hardcoded within InlineChatWidget id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 0, From ae04cff1450064534d4490d7b21fa138fb06e03e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 16 Feb 2024 12:31:56 -0800 Subject: [PATCH 0114/1175] Move context keys and response types into terminalContrib --- .../terminal/common/terminalContextKey.ts | 42 ---------- .../chat/browser/terminalChat.ts | 49 ++++++++++++ .../browser/terminalChatAccessibilityHelp.ts | 5 +- ...rminalChatAccessibilityHelpContribution.ts | 4 +- .../browser/terminalChatAccessibleView.ts | 4 +- .../chat/browser/terminalChatActions.ts | 80 +++++++++---------- .../chat/browser/terminalChatController.ts | 12 +-- .../chat/browser/terminalChatWidget.ts | 9 +-- 8 files changed, 105 insertions(+), 100 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index 0c577a9df8a..72335480aee 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -39,20 +39,6 @@ export const enum TerminalContextKeyStrings { ShellType = 'terminalShellType', InTerminalRunCommandPicker = 'inTerminalRunCommandPicker', TerminalShellIntegrationEnabled = 'terminalShellIntegrationEnabled', - ChatFocus = 'terminalChatFocus', - ChatVisible = 'terminalChatVisible', - ChatActiveRequest = 'terminalChatActiveRequest', - ChatInputHasText = 'terminalChatInputHasText', - ChatAgentRegistered = 'terminalChatAgentRegistered', - ChatResponseEditorFocused = 'terminalChatResponseEditorFocused', - ChatResponseType = 'terminalChatResponseType', - ChatResponseSupportsIssueReporting = 'terminalChatResponseSupportsIssueReporting', - ChatSessionResponseVote = 'terminalChatSessionResponseVote', -} - -export const enum TerminalChatResponseTypes { - Message = 'message', - TerminalCommand = 'terminalCommand' } export namespace TerminalContextKeys { @@ -172,32 +158,4 @@ export namespace TerminalContextKeys { ContextKeyExpr.equals(`config.${TerminalSettingId.TabsShowActions}`, 'always') ) ); - - - /** Whether the chat widget is focused */ - export const chatFocused = new RawContextKey(TerminalContextKeyStrings.ChatFocus, false, localize('chatFocusedContextKey', "Whether the chat view is focused.")); - - /** Whether the chat widget is visible */ - export const chatVisible = new RawContextKey(TerminalContextKeyStrings.ChatVisible, false, localize('chatVisibleContextKey', "Whether the chat view is visible.")); - - /** Whether there is an active chat request */ - export const chatRequestActive = new RawContextKey(TerminalContextKeyStrings.ChatActiveRequest, false, localize('chatRequestActiveContextKey', "Whether there is an active chat request.")); - - /** Whether the chat input has text */ - export const chatInputHasText = new RawContextKey(TerminalContextKeyStrings.ChatInputHasText, false, localize('chatInputHasTextContextKey', "Whether the chat input has text.")); - - /** Whether the terminal chat agent has been registered */ - export const chatAgentRegistered = new RawContextKey(TerminalContextKeyStrings.ChatAgentRegistered, false, localize('chatAgentRegisteredContextKey', "Whether the terminal chat agent has been registered.")); - - /** Whether the chat response editor is focused */ - export const chatResponseEditorFocused = new RawContextKey(TerminalContextKeyStrings.ChatResponseEditorFocused, false, localize('chatResponseEditorFocusedContextKey', "Whether the chat response editor is focused.")); - - /** The type of chat response, if any */ - export const chatResponseType = new RawContextKey(TerminalContextKeyStrings.ChatResponseType, undefined, localize('chatResponseTypeContextKey', "The type of chat response, if any")); - - /** Whether the response supports issue reporting */ - export const chatResponseSupportsIssueReporting = new RawContextKey(TerminalContextKeyStrings.ChatResponseSupportsIssueReporting, false, localize('chatResponseSupportsIssueReportingContextKey', "Whether the response supports issue reporting")); - - /** The chat vote, if any for the response, if any */ - export const chatSessionResponseVote = new RawContextKey(TerminalContextKeyStrings.ChatSessionResponseVote, undefined, { type: 'string', description: localize('interactiveSessionResponseVote', "When the response has been voted up, is set to 'up'. When voted down, is set to 'down'. Otherwise an empty string.") }); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts index a2349bc25c1..5aefe757469 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts @@ -3,7 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; export const enum TerminalChatCommandId { Start = 'workbench.action.terminal.chat.start', @@ -24,3 +26,50 @@ export const MENU_TERMINAL_CHAT_WIDGET = MenuId.for('terminalChatWidget'); export const MENU_TERMINAL_CHAT_WIDGET_STATUS = MenuId.for('terminalChatWidget.status'); export const MENU_TERMINAL_CHAT_WIDGET_FEEDBACK = MenuId.for('terminalChatWidget.feedback'); export const MENU_TERMINAL_CHAT_WIDGET_TOOLBAR = MenuId.for('terminalChatWidget.toolbar'); + +export const enum TerminalChatContextKeyStrings { + ChatFocus = 'terminalChatFocus', + ChatVisible = 'terminalChatVisible', + ChatActiveRequest = 'terminalChatActiveRequest', + ChatInputHasText = 'terminalChatInputHasText', + ChatAgentRegistered = 'terminalChatAgentRegistered', + ChatResponseEditorFocused = 'terminalChatResponseEditorFocused', + ChatResponseType = 'terminalChatResponseType', + ChatResponseSupportsIssueReporting = 'terminalChatResponseSupportsIssueReporting', + ChatSessionResponseVote = 'terminalChatSessionResponseVote', +} + +export const enum TerminalChatResponseTypes { + Message = 'message', + TerminalCommand = 'terminalCommand' +} + +export namespace TerminalChatContextKeys { + + /** Whether the chat widget is focused */ + export const chatFocused = new RawContextKey(TerminalChatContextKeyStrings.ChatFocus, false, localize('chatFocusedContextKey', "Whether the chat view is focused.")); + + /** Whether the chat widget is visible */ + export const chatVisible = new RawContextKey(TerminalChatContextKeyStrings.ChatVisible, false, localize('chatVisibleContextKey', "Whether the chat view is visible.")); + + /** Whether there is an active chat request */ + export const chatRequestActive = new RawContextKey(TerminalChatContextKeyStrings.ChatActiveRequest, false, localize('chatRequestActiveContextKey', "Whether there is an active chat request.")); + + /** Whether the chat input has text */ + export const chatInputHasText = new RawContextKey(TerminalChatContextKeyStrings.ChatInputHasText, false, localize('chatInputHasTextContextKey', "Whether the chat input has text.")); + + /** Whether the terminal chat agent has been registered */ + export const chatAgentRegistered = new RawContextKey(TerminalChatContextKeyStrings.ChatAgentRegistered, false, localize('chatAgentRegisteredContextKey', "Whether the terminal chat agent has been registered.")); + + /** Whether the chat response editor is focused */ + export const chatResponseEditorFocused = new RawContextKey(TerminalChatContextKeyStrings.ChatResponseEditorFocused, false, localize('chatResponseEditorFocusedContextKey', "Whether the chat response editor is focused.")); + + /** The type of chat response, if any */ + export const chatResponseType = new RawContextKey(TerminalChatContextKeyStrings.ChatResponseType, undefined, localize('chatResponseTypeContextKey', "The type of chat response, if any")); + + /** Whether the response supports issue reporting */ + export const chatResponseSupportsIssueReporting = new RawContextKey(TerminalChatContextKeyStrings.ChatResponseSupportsIssueReporting, false, localize('chatResponseSupportsIssueReportingContextKey', "Whether the response supports issue reporting")); + + /** The chat vote, if any for the response, if any */ + export const chatSessionResponseVote = new RawContextKey(TerminalChatContextKeyStrings.ChatSessionResponseVote, undefined, { type: 'string', description: localize('interactiveSessionResponseVote', "When the response has been voted up, is set to 'up'. When voted down, is set to 'down'. Otherwise an empty string.") }); +} diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts index 890fb103292..c9631f7fa1d 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts @@ -13,8 +13,7 @@ import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; import { CTX_INLINE_CHAT_RESPONSE_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { TerminalChatCommandId } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; +import { TerminalChatCommandId, TerminalChatContextKeys } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; export class TerminalInlineChatAccessibilityHelpContribution extends Disposable { @@ -39,7 +38,7 @@ export class TerminalInlineChatAccessibilityHelpContribution extends Disposable options: { type: AccessibleViewType.Help } }); return true; - }, ContextKeyExpr.or(TerminalContextKeys.chatFocused, TerminalContextKeys.chatResponseEditorFocused, CTX_INLINE_CHAT_RESPONSE_FOCUSED))); + }, ContextKeyExpr.or(TerminalChatContextKeys.chatFocused, TerminalChatContextKeys.chatResponseEditorFocused, CTX_INLINE_CHAT_RESPONSE_FOCUSED))); } } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts index 6e5f79e1cb1..afc24a15163 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts @@ -10,14 +10,14 @@ import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/wo import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { TerminalChatContextKeys } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; export class TerminalChatAccessibilityHelpContribution extends Disposable { static ID = 'terminalChatAccessiblityHelp'; constructor() { super(); - this._register(AccessibilityHelpAction.addImplementation(110, 'terminalChat', runAccessibilityHelpAction, TerminalContextKeys.chatFocused)); + this._register(AccessibilityHelpAction.addImplementation(110, 'terminalChat', runAccessibilityHelpAction, TerminalChatContextKeys.chatFocused)); } } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts index 8c146698482..93854689479 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts @@ -10,7 +10,7 @@ import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib import { AccessibleViewAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; import { CTX_INLINE_CHAT_RESPONSE_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { TerminalChatContextKeys } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; export class TerminalInlineChatAccessibleViewContribution extends Disposable { @@ -35,6 +35,6 @@ export class TerminalInlineChatAccessibleViewContribution extends Disposable { options: { type: AccessibleViewType.View } }); return true; - }, ContextKeyExpr.and(TerminalContextKeys.chatFocused, CTX_INLINE_CHAT_RESPONSE_FOCUSED))); + }, ContextKeyExpr.and(TerminalChatContextKeys.chatFocused, CTX_INLINE_CHAT_RESPONSE_FOCUSED))); } } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index f374aee77e8..7f8dc50524d 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -12,8 +12,8 @@ import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; -import { TerminalChatResponseTypes, TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, MENU_TERMINAL_CHAT_WIDGET_STATUS, TerminalChatCommandId } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; +import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, MENU_TERMINAL_CHAT_WIDGET_STATUS, TerminalChatCommandId, TerminalChatContextKeys, TerminalChatResponseTypes } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; registerActiveXtermAction({ @@ -21,7 +21,7 @@ registerActiveXtermAction({ title: localize2('startChat', 'Start Chat'), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyI, - when: ContextKeyExpr.and(TerminalContextKeys.chatFocused.negate(), TerminalContextKeys.focusInAny), + when: ContextKeyExpr.and(TerminalChatContextKeys.chatFocused.negate(), TerminalContextKeys.focusInAny), weight: KeybindingWeight.WorkbenchContrib, }, f1: true, @@ -46,7 +46,7 @@ registerActiveXtermAction({ keybinding: { primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], - when: ContextKeyExpr.and(TerminalContextKeys.chatFocused, TerminalContextKeys.chatVisible), + when: ContextKeyExpr.and(TerminalChatContextKeys.chatFocused, TerminalChatContextKeys.chatVisible), weight: KeybindingWeight.WorkbenchContrib, }, icon: Codicon.close, @@ -78,15 +78,15 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 2, - when: ContextKeyExpr.and(TerminalContextKeys.chatFocused, - TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand)) + when: ContextKeyExpr.and(TerminalChatContextKeys.chatFocused, + TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand)) }, f1: true, precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - TerminalContextKeys.chatFocused, - TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) + TerminalChatContextKeys.chatFocused, + TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) ), run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -105,13 +105,13 @@ registerActiveXtermAction({ precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - TerminalContextKeys.chatRequestActive.negate(), - TerminalContextKeys.chatAgentRegistered, - TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) + TerminalChatContextKeys.chatRequestActive.negate(), + TerminalChatContextKeys.chatAgentRegistered, + TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) ), icon: Codicon.check, keybinding: { - when: TerminalContextKeys.chatRequestActive.negate(), + when: TerminalChatContextKeys.chatRequestActive.negate(), weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.Enter, }, @@ -120,7 +120,7 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 0, - when: ContextKeyExpr.and(TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalChatContextKeys.chatRequestActive.negate()), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -138,13 +138,13 @@ registerActiveXtermAction({ precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - TerminalContextKeys.chatRequestActive.negate(), - TerminalContextKeys.chatAgentRegistered, - TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) + TerminalChatContextKeys.chatRequestActive.negate(), + TerminalChatContextKeys.chatAgentRegistered, + TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) ), icon: Codicon.check, keybinding: { - when: TerminalContextKeys.chatRequestActive.negate(), + when: TerminalChatContextKeys.chatRequestActive.negate(), weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.Alt | KeyCode.Enter, }, @@ -152,7 +152,7 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 1, - when: ContextKeyExpr.and(TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalChatContextKeys.chatRequestActive.negate()), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -169,21 +169,21 @@ registerActiveXtermAction({ precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - TerminalContextKeys.chatRequestActive.negate(), - TerminalContextKeys.chatAgentRegistered, + TerminalChatContextKeys.chatRequestActive.negate(), + TerminalChatContextKeys.chatAgentRegistered, ), icon: Codicon.commentDiscussion, menu: [{ id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 1, - when: ContextKeyExpr.and(TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.Message), TerminalContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.Message), TerminalChatContextKeys.chatRequestActive.negate()), }, { id: MENU_TERMINAL_CHAT_WIDGET, group: 'main', order: 1, - when: ContextKeyExpr.and(CTX_INLINE_CHAT_EMPTY.negate(), TerminalContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(CTX_INLINE_CHAT_EMPTY.negate(), TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalChatContextKeys.chatRequestActive.negate()), }], run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -200,13 +200,13 @@ registerActiveXtermAction({ precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - TerminalContextKeys.chatRequestActive.negate(), - TerminalContextKeys.chatAgentRegistered, + TerminalChatContextKeys.chatRequestActive.negate(), + TerminalChatContextKeys.chatAgentRegistered, CTX_INLINE_CHAT_EMPTY.negate() ), icon: Codicon.send, keybinding: { - when: ContextKeyExpr.and(CTX_INLINE_CHAT_FOCUSED, TerminalContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(CTX_INLINE_CHAT_FOCUSED, TerminalChatContextKeys.chatRequestActive.negate()), weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.Enter }, @@ -214,7 +214,7 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_INPUT, group: 'main', order: 1, - when: TerminalContextKeys.chatRequestActive.negate(), + when: TerminalChatContextKeys.chatRequestActive.negate(), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -230,14 +230,14 @@ registerActiveXtermAction({ title: localize2('cancelChat', 'Cancel Chat'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalContextKeys.chatRequestActive, - TerminalContextKeys.chatAgentRegistered + TerminalChatContextKeys.chatRequestActive, + TerminalChatContextKeys.chatAgentRegistered ), icon: Codicon.debugStop, menu: { id: MENU_TERMINAL_CHAT_INPUT, group: 'main', - when: TerminalContextKeys.chatRequestActive, + when: TerminalChatContextKeys.chatRequestActive, }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -253,15 +253,15 @@ registerActiveXtermAction({ title: localize2('feedbackHelpful', 'Helpful'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalContextKeys.chatResponseType.notEqualsTo(undefined) + TerminalChatContextKeys.chatResponseType.notEqualsTo(undefined) ), icon: Codicon.thumbsup, - toggled: TerminalContextKeys.chatSessionResponseVote.isEqualTo('up'), + toggled: TerminalChatContextKeys.chatSessionResponseVote.isEqualTo('up'), menu: { id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, group: 'inline', order: 1, - when: TerminalContextKeys.chatResponseType.notEqualsTo(undefined), + when: TerminalChatContextKeys.chatResponseType.notEqualsTo(undefined), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -277,15 +277,15 @@ registerActiveXtermAction({ title: localize2('feedbackUnhelpful', 'Unhelpful'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalContextKeys.chatResponseType.notEqualsTo(undefined), + TerminalChatContextKeys.chatResponseType.notEqualsTo(undefined), ), - toggled: TerminalContextKeys.chatSessionResponseVote.isEqualTo('down'), + toggled: TerminalChatContextKeys.chatSessionResponseVote.isEqualTo('down'), icon: Codicon.thumbsdown, menu: { id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, group: 'inline', order: 2, - when: TerminalContextKeys.chatResponseType.notEqualsTo(undefined), + when: TerminalChatContextKeys.chatResponseType.notEqualsTo(undefined), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -301,20 +301,20 @@ registerActiveXtermAction({ title: localize2('reportIssue', 'Report Issue'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalContextKeys.chatRequestActive.negate(), - TerminalContextKeys.chatResponseType.notEqualsTo(undefined), - TerminalContextKeys.chatResponseSupportsIssueReporting + TerminalChatContextKeys.chatRequestActive.negate(), + TerminalChatContextKeys.chatResponseType.notEqualsTo(undefined), + TerminalChatContextKeys.chatResponseSupportsIssueReporting ), icon: Codicon.report, menu: [{ id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, - when: ContextKeyExpr.and(TerminalContextKeys.chatResponseType.notEqualsTo(undefined), TerminalContextKeys.chatResponseSupportsIssueReporting), + when: ContextKeyExpr.and(TerminalChatContextKeys.chatResponseType.notEqualsTo(undefined), TerminalChatContextKeys.chatResponseSupportsIssueReporting), group: 'inline', order: 3 }], // { // id: MENU_TERMINAL_CHAT_WIDGET, - // when: ContextKeyExpr.and(TerminalContextKeys.chatResponseType.notEqualsTo(undefined), TerminalContextKeys.chatResponseSupportsIssueReporting), + // when: ContextKeyExpr.and(TerminalChatContextKeys.chatResponseType.notEqualsTo(undefined), TerminalChatContextKeys.chatResponseSupportsIssueReporting), // group: 'config', // order: 3 // }], diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index ca3bdbab893..c3409674e12 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -21,11 +21,11 @@ import { IChatService, IChatProgress, InteractiveSessionVoteDirection, ChatUserA import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; -import { TerminalChatResponseTypes, TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { TerminalChatWidget } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget'; import { ChatModel, ChatRequestModel, IChatRequestVariableData } from 'vs/workbench/contrib/chat/common/chatModel'; +import { TerminalChatContextKeys, TerminalChatResponseTypes } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; const enum Message { NONE = 0, @@ -95,11 +95,11 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { return; } - this._requestActiveContextKey = TerminalContextKeys.chatRequestActive.bindTo(this._contextKeyService); - this._terminalAgentRegisteredContextKey = TerminalContextKeys.chatAgentRegistered.bindTo(this._contextKeyService); - this._responseTypeContextKey = TerminalContextKeys.chatResponseType.bindTo(this._contextKeyService); - this._responseSupportsIssueReportingContextKey = TerminalContextKeys.chatResponseSupportsIssueReporting.bindTo(this._contextKeyService); - this._sessionResponseVoteContextKey = TerminalContextKeys.chatSessionResponseVote.bindTo(this._contextKeyService); + this._requestActiveContextKey = TerminalChatContextKeys.chatRequestActive.bindTo(this._contextKeyService); + this._terminalAgentRegisteredContextKey = TerminalChatContextKeys.chatAgentRegistered.bindTo(this._contextKeyService); + this._responseTypeContextKey = TerminalChatContextKeys.chatResponseType.bindTo(this._contextKeyService); + this._responseSupportsIssueReportingContextKey = TerminalChatContextKeys.chatResponseSupportsIssueReporting.bindTo(this._contextKeyService); + this._sessionResponseVoteContextKey = TerminalChatContextKeys.chatSessionResponseVote.bindTo(this._contextKeyService); if (!this._chatAgentService.hasAgent(this._terminalAgentId)) { this._register(this._chatAgentService.onDidChangeAgents(() => { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 2cc70c7eb07..82f68ccf9f4 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -22,8 +22,7 @@ import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/cha import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, MENU_TERMINAL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; +import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, MENU_TERMINAL_CHAT_WIDGET_STATUS, TerminalChatContextKeys } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; export class TerminalChatWidget extends Disposable { @@ -48,8 +47,8 @@ export class TerminalChatWidget extends Disposable { ) { super(); - this._focusedContextKey = TerminalContextKeys.chatFocused.bindTo(this._contextKeyService); - this._visibleContextKey = TerminalContextKeys.chatVisible.bindTo(this._contextKeyService); + this._focusedContextKey = TerminalChatContextKeys.chatFocused.bindTo(this._contextKeyService); + this._visibleContextKey = TerminalChatContextKeys.chatVisible.bindTo(this._contextKeyService); this._container = document.createElement('div'); this._container.classList.add('terminal-inline-chat'); @@ -176,7 +175,7 @@ class TerminalChatResponseEditor extends Disposable { ) { super(); - this._responseEditorFocusedContextKey = TerminalContextKeys.chatResponseEditorFocused.bindTo(this._contextKeyService); + this._responseEditorFocusedContextKey = TerminalChatContextKeys.chatResponseEditorFocused.bindTo(this._contextKeyService); this._editorContainer = document.createElement('div'); this._editorContainer.classList.add('terminal-inline-chat-response'); From 9b38a276ce04615099802f185e911b2f086274e3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 16 Feb 2024 12:33:19 -0800 Subject: [PATCH 0115/1175] Remove 'chat' from context keys as it's in the namespace --- .../chat/browser/terminalChat.ts | 18 ++--- .../browser/terminalChatAccessibilityHelp.ts | 2 +- ...rminalChatAccessibilityHelpContribution.ts | 2 +- .../browser/terminalChatAccessibleView.ts | 2 +- .../chat/browser/terminalChatActions.ts | 74 +++++++++---------- .../chat/browser/terminalChatController.ts | 10 +-- .../chat/browser/terminalChatWidget.ts | 6 +- 7 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts index 5aefe757469..89893d6d31e 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts @@ -47,29 +47,29 @@ export const enum TerminalChatResponseTypes { export namespace TerminalChatContextKeys { /** Whether the chat widget is focused */ - export const chatFocused = new RawContextKey(TerminalChatContextKeyStrings.ChatFocus, false, localize('chatFocusedContextKey', "Whether the chat view is focused.")); + export const focused = new RawContextKey(TerminalChatContextKeyStrings.ChatFocus, false, localize('chatFocusedContextKey', "Whether the chat view is focused.")); /** Whether the chat widget is visible */ - export const chatVisible = new RawContextKey(TerminalChatContextKeyStrings.ChatVisible, false, localize('chatVisibleContextKey', "Whether the chat view is visible.")); + export const visible = new RawContextKey(TerminalChatContextKeyStrings.ChatVisible, false, localize('chatVisibleContextKey', "Whether the chat view is visible.")); /** Whether there is an active chat request */ - export const chatRequestActive = new RawContextKey(TerminalChatContextKeyStrings.ChatActiveRequest, false, localize('chatRequestActiveContextKey', "Whether there is an active chat request.")); + export const requestActive = new RawContextKey(TerminalChatContextKeyStrings.ChatActiveRequest, false, localize('chatRequestActiveContextKey', "Whether there is an active chat request.")); /** Whether the chat input has text */ - export const chatInputHasText = new RawContextKey(TerminalChatContextKeyStrings.ChatInputHasText, false, localize('chatInputHasTextContextKey', "Whether the chat input has text.")); + export const inputHasText = new RawContextKey(TerminalChatContextKeyStrings.ChatInputHasText, false, localize('chatInputHasTextContextKey', "Whether the chat input has text.")); /** Whether the terminal chat agent has been registered */ - export const chatAgentRegistered = new RawContextKey(TerminalChatContextKeyStrings.ChatAgentRegistered, false, localize('chatAgentRegisteredContextKey', "Whether the terminal chat agent has been registered.")); + export const agentRegistered = new RawContextKey(TerminalChatContextKeyStrings.ChatAgentRegistered, false, localize('chatAgentRegisteredContextKey', "Whether the terminal chat agent has been registered.")); /** Whether the chat response editor is focused */ - export const chatResponseEditorFocused = new RawContextKey(TerminalChatContextKeyStrings.ChatResponseEditorFocused, false, localize('chatResponseEditorFocusedContextKey', "Whether the chat response editor is focused.")); + export const responseEditorFocused = new RawContextKey(TerminalChatContextKeyStrings.ChatResponseEditorFocused, false, localize('chatResponseEditorFocusedContextKey', "Whether the chat response editor is focused.")); /** The type of chat response, if any */ - export const chatResponseType = new RawContextKey(TerminalChatContextKeyStrings.ChatResponseType, undefined, localize('chatResponseTypeContextKey', "The type of chat response, if any")); + export const responseType = new RawContextKey(TerminalChatContextKeyStrings.ChatResponseType, undefined, localize('chatResponseTypeContextKey', "The type of chat response, if any")); /** Whether the response supports issue reporting */ - export const chatResponseSupportsIssueReporting = new RawContextKey(TerminalChatContextKeyStrings.ChatResponseSupportsIssueReporting, false, localize('chatResponseSupportsIssueReportingContextKey', "Whether the response supports issue reporting")); + export const responseSupportsIssueReporting = new RawContextKey(TerminalChatContextKeyStrings.ChatResponseSupportsIssueReporting, false, localize('chatResponseSupportsIssueReportingContextKey', "Whether the response supports issue reporting")); /** The chat vote, if any for the response, if any */ - export const chatSessionResponseVote = new RawContextKey(TerminalChatContextKeyStrings.ChatSessionResponseVote, undefined, { type: 'string', description: localize('interactiveSessionResponseVote', "When the response has been voted up, is set to 'up'. When voted down, is set to 'down'. Otherwise an empty string.") }); + export const sessionResponseVote = new RawContextKey(TerminalChatContextKeyStrings.ChatSessionResponseVote, undefined, { type: 'string', description: localize('interactiveSessionResponseVote', "When the response has been voted up, is set to 'up'. When voted down, is set to 'down'. Otherwise an empty string.") }); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts index c9631f7fa1d..d330683af2e 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts @@ -38,7 +38,7 @@ export class TerminalInlineChatAccessibilityHelpContribution extends Disposable options: { type: AccessibleViewType.Help } }); return true; - }, ContextKeyExpr.or(TerminalChatContextKeys.chatFocused, TerminalChatContextKeys.chatResponseEditorFocused, CTX_INLINE_CHAT_RESPONSE_FOCUSED))); + }, ContextKeyExpr.or(TerminalChatContextKeys.focused, TerminalChatContextKeys.responseEditorFocused, CTX_INLINE_CHAT_RESPONSE_FOCUSED))); } } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts index afc24a15163..61d11998690 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts @@ -17,7 +17,7 @@ export class TerminalChatAccessibilityHelpContribution extends Disposable { static ID = 'terminalChatAccessiblityHelp'; constructor() { super(); - this._register(AccessibilityHelpAction.addImplementation(110, 'terminalChat', runAccessibilityHelpAction, TerminalChatContextKeys.chatFocused)); + this._register(AccessibilityHelpAction.addImplementation(110, 'terminalChat', runAccessibilityHelpAction, TerminalChatContextKeys.focused)); } } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts index 93854689479..798c0bf9774 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView.ts @@ -35,6 +35,6 @@ export class TerminalInlineChatAccessibleViewContribution extends Disposable { options: { type: AccessibleViewType.View } }); return true; - }, ContextKeyExpr.and(TerminalChatContextKeys.chatFocused, CTX_INLINE_CHAT_RESPONSE_FOCUSED))); + }, ContextKeyExpr.and(TerminalChatContextKeys.focused, CTX_INLINE_CHAT_RESPONSE_FOCUSED))); } } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 7f8dc50524d..fc0777f9cde 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -21,7 +21,7 @@ registerActiveXtermAction({ title: localize2('startChat', 'Start Chat'), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyI, - when: ContextKeyExpr.and(TerminalChatContextKeys.chatFocused.negate(), TerminalContextKeys.focusInAny), + when: ContextKeyExpr.and(TerminalChatContextKeys.focused.negate(), TerminalContextKeys.focusInAny), weight: KeybindingWeight.WorkbenchContrib, }, f1: true, @@ -46,7 +46,7 @@ registerActiveXtermAction({ keybinding: { primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], - when: ContextKeyExpr.and(TerminalChatContextKeys.chatFocused, TerminalChatContextKeys.chatVisible), + when: ContextKeyExpr.and(TerminalChatContextKeys.focused, TerminalChatContextKeys.visible), weight: KeybindingWeight.WorkbenchContrib, }, icon: Codicon.close, @@ -78,15 +78,15 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 2, - when: ContextKeyExpr.and(TerminalChatContextKeys.chatFocused, - TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand)) + when: ContextKeyExpr.and(TerminalChatContextKeys.focused, + TerminalChatContextKeys.responseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand)) }, f1: true, precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - TerminalChatContextKeys.chatFocused, - TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) + TerminalChatContextKeys.focused, + TerminalChatContextKeys.responseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) ), run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -105,13 +105,13 @@ registerActiveXtermAction({ precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - TerminalChatContextKeys.chatRequestActive.negate(), - TerminalChatContextKeys.chatAgentRegistered, - TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) + TerminalChatContextKeys.requestActive.negate(), + TerminalChatContextKeys.agentRegistered, + TerminalChatContextKeys.responseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) ), icon: Codicon.check, keybinding: { - when: TerminalChatContextKeys.chatRequestActive.negate(), + when: TerminalChatContextKeys.requestActive.negate(), weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.Enter, }, @@ -120,7 +120,7 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 0, - when: ContextKeyExpr.and(TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalChatContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(TerminalChatContextKeys.responseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalChatContextKeys.requestActive.negate()), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -138,13 +138,13 @@ registerActiveXtermAction({ precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - TerminalChatContextKeys.chatRequestActive.negate(), - TerminalChatContextKeys.chatAgentRegistered, - TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) + TerminalChatContextKeys.requestActive.negate(), + TerminalChatContextKeys.agentRegistered, + TerminalChatContextKeys.responseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand) ), icon: Codicon.check, keybinding: { - when: TerminalChatContextKeys.chatRequestActive.negate(), + when: TerminalChatContextKeys.requestActive.negate(), weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.Alt | KeyCode.Enter, }, @@ -152,7 +152,7 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 1, - when: ContextKeyExpr.and(TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalChatContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(TerminalChatContextKeys.responseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalChatContextKeys.requestActive.negate()), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -169,21 +169,21 @@ registerActiveXtermAction({ precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - TerminalChatContextKeys.chatRequestActive.negate(), - TerminalChatContextKeys.chatAgentRegistered, + TerminalChatContextKeys.requestActive.negate(), + TerminalChatContextKeys.agentRegistered, ), icon: Codicon.commentDiscussion, menu: [{ id: MENU_TERMINAL_CHAT_WIDGET_STATUS, group: '0_main', order: 1, - when: ContextKeyExpr.and(TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.Message), TerminalChatContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(TerminalChatContextKeys.responseType.isEqualTo(TerminalChatResponseTypes.Message), TerminalChatContextKeys.requestActive.negate()), }, { id: MENU_TERMINAL_CHAT_WIDGET, group: 'main', order: 1, - when: ContextKeyExpr.and(CTX_INLINE_CHAT_EMPTY.negate(), TerminalChatContextKeys.chatResponseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalChatContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(CTX_INLINE_CHAT_EMPTY.negate(), TerminalChatContextKeys.responseType.isEqualTo(TerminalChatResponseTypes.TerminalCommand), TerminalChatContextKeys.requestActive.negate()), }], run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -200,13 +200,13 @@ registerActiveXtermAction({ precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - TerminalChatContextKeys.chatRequestActive.negate(), - TerminalChatContextKeys.chatAgentRegistered, + TerminalChatContextKeys.requestActive.negate(), + TerminalChatContextKeys.agentRegistered, CTX_INLINE_CHAT_EMPTY.negate() ), icon: Codicon.send, keybinding: { - when: ContextKeyExpr.and(CTX_INLINE_CHAT_FOCUSED, TerminalChatContextKeys.chatRequestActive.negate()), + when: ContextKeyExpr.and(CTX_INLINE_CHAT_FOCUSED, TerminalChatContextKeys.requestActive.negate()), weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.Enter }, @@ -214,7 +214,7 @@ registerActiveXtermAction({ id: MENU_TERMINAL_CHAT_INPUT, group: 'main', order: 1, - when: TerminalChatContextKeys.chatRequestActive.negate(), + when: TerminalChatContextKeys.requestActive.negate(), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -230,14 +230,14 @@ registerActiveXtermAction({ title: localize2('cancelChat', 'Cancel Chat'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalChatContextKeys.chatRequestActive, - TerminalChatContextKeys.chatAgentRegistered + TerminalChatContextKeys.requestActive, + TerminalChatContextKeys.agentRegistered ), icon: Codicon.debugStop, menu: { id: MENU_TERMINAL_CHAT_INPUT, group: 'main', - when: TerminalChatContextKeys.chatRequestActive, + when: TerminalChatContextKeys.requestActive, }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -253,15 +253,15 @@ registerActiveXtermAction({ title: localize2('feedbackHelpful', 'Helpful'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalChatContextKeys.chatResponseType.notEqualsTo(undefined) + TerminalChatContextKeys.responseType.notEqualsTo(undefined) ), icon: Codicon.thumbsup, - toggled: TerminalChatContextKeys.chatSessionResponseVote.isEqualTo('up'), + toggled: TerminalChatContextKeys.sessionResponseVote.isEqualTo('up'), menu: { id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, group: 'inline', order: 1, - when: TerminalChatContextKeys.chatResponseType.notEqualsTo(undefined), + when: TerminalChatContextKeys.responseType.notEqualsTo(undefined), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -277,15 +277,15 @@ registerActiveXtermAction({ title: localize2('feedbackUnhelpful', 'Unhelpful'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalChatContextKeys.chatResponseType.notEqualsTo(undefined), + TerminalChatContextKeys.responseType.notEqualsTo(undefined), ), - toggled: TerminalChatContextKeys.chatSessionResponseVote.isEqualTo('down'), + toggled: TerminalChatContextKeys.sessionResponseVote.isEqualTo('down'), icon: Codicon.thumbsdown, menu: { id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, group: 'inline', order: 2, - when: TerminalChatContextKeys.chatResponseType.notEqualsTo(undefined), + when: TerminalChatContextKeys.responseType.notEqualsTo(undefined), }, run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { @@ -301,14 +301,14 @@ registerActiveXtermAction({ title: localize2('reportIssue', 'Report Issue'), precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), - TerminalChatContextKeys.chatRequestActive.negate(), - TerminalChatContextKeys.chatResponseType.notEqualsTo(undefined), - TerminalChatContextKeys.chatResponseSupportsIssueReporting + TerminalChatContextKeys.requestActive.negate(), + TerminalChatContextKeys.responseType.notEqualsTo(undefined), + TerminalChatContextKeys.responseSupportsIssueReporting ), icon: Codicon.report, menu: [{ id: MENU_TERMINAL_CHAT_WIDGET_FEEDBACK, - when: ContextKeyExpr.and(TerminalChatContextKeys.chatResponseType.notEqualsTo(undefined), TerminalChatContextKeys.chatResponseSupportsIssueReporting), + when: ContextKeyExpr.and(TerminalChatContextKeys.responseType.notEqualsTo(undefined), TerminalChatContextKeys.responseSupportsIssueReporting), group: 'inline', order: 3 }], diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index c3409674e12..0226d6536f0 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -95,11 +95,11 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (!this._configurationService.getValue(TerminalSettingId.ExperimentalInlineChat)) { return; } - this._requestActiveContextKey = TerminalChatContextKeys.chatRequestActive.bindTo(this._contextKeyService); - this._terminalAgentRegisteredContextKey = TerminalChatContextKeys.chatAgentRegistered.bindTo(this._contextKeyService); - this._responseTypeContextKey = TerminalChatContextKeys.chatResponseType.bindTo(this._contextKeyService); - this._responseSupportsIssueReportingContextKey = TerminalChatContextKeys.chatResponseSupportsIssueReporting.bindTo(this._contextKeyService); - this._sessionResponseVoteContextKey = TerminalChatContextKeys.chatSessionResponseVote.bindTo(this._contextKeyService); + this._requestActiveContextKey = TerminalChatContextKeys.requestActive.bindTo(this._contextKeyService); + this._terminalAgentRegisteredContextKey = TerminalChatContextKeys.agentRegistered.bindTo(this._contextKeyService); + this._responseTypeContextKey = TerminalChatContextKeys.responseType.bindTo(this._contextKeyService); + this._responseSupportsIssueReportingContextKey = TerminalChatContextKeys.responseSupportsIssueReporting.bindTo(this._contextKeyService); + this._sessionResponseVoteContextKey = TerminalChatContextKeys.sessionResponseVote.bindTo(this._contextKeyService); if (!this._chatAgentService.hasAgent(this._terminalAgentId)) { this._register(this._chatAgentService.onDidChangeAgents(() => { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 82f68ccf9f4..86b3fd8de33 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -47,8 +47,8 @@ export class TerminalChatWidget extends Disposable { ) { super(); - this._focusedContextKey = TerminalChatContextKeys.chatFocused.bindTo(this._contextKeyService); - this._visibleContextKey = TerminalChatContextKeys.chatVisible.bindTo(this._contextKeyService); + this._focusedContextKey = TerminalChatContextKeys.focused.bindTo(this._contextKeyService); + this._visibleContextKey = TerminalChatContextKeys.visible.bindTo(this._contextKeyService); this._container = document.createElement('div'); this._container.classList.add('terminal-inline-chat'); @@ -175,7 +175,7 @@ class TerminalChatResponseEditor extends Disposable { ) { super(); - this._responseEditorFocusedContextKey = TerminalChatContextKeys.chatResponseEditorFocused.bindTo(this._contextKeyService); + this._responseEditorFocusedContextKey = TerminalChatContextKeys.responseEditorFocused.bindTo(this._contextKeyService); this._editorContainer = document.createElement('div'); this._editorContainer.classList.add('terminal-inline-chat-response'); From 31df2b2df478f28cc65dc45ce7c7d11500052ba0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 16 Feb 2024 12:34:40 -0800 Subject: [PATCH 0116/1175] Revert unneeded change to InlineChatWidget --- src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 3aeb25cd2e2..ca909ba98e0 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -388,7 +388,7 @@ export class InlineChatWidget { buttonConfigProvider: action => { if (action.id === ACTION_REGENERATE_RESPONSE) { return { showIcon: true, showLabel: false, isSecondary: true }; - } else if ([ACTION_VIEW_IN_CHAT, ACTION_ACCEPT_CHANGES].includes(action.id)) { + } else if (action.id === ACTION_VIEW_IN_CHAT || action.id === ACTION_ACCEPT_CHANGES) { return { isSecondary: false }; } else { return { isSecondary: true }; From b9f9060711db2ab23c91e16687065523ff5b6161 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 16 Feb 2024 12:37:54 -0800 Subject: [PATCH 0117/1175] Clarify lint suppression reason --- .../contrib/chat/electron-sandbox/actions/voiceChatActions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index aeb85706fc6..fe9ddb2f8f0 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -55,7 +55,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ThemeIcon } from 'vs/base/common/themables'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; -// TODO: The chat needs to move into contrib/terminal/ as we don't want anything importing from terminalContrib/ + +// This is a one-off/safe import, changing the eslint rules would require duplicating/complicating the rules // eslint-disable-next-line local/code-import-patterns import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; From ec00f84dce8115176af91c856894bc749e2367db Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 16 Feb 2024 13:01:13 -0800 Subject: [PATCH 0118/1175] Remove test string --- .../contrib/terminalContrib/chat/browser/terminalChatActions.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index fc0777f9cde..72eac948274 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -35,8 +35,6 @@ registerActiveXtermAction({ } const contr = TerminalChatController.activeChatWidget || TerminalChatController.get(activeInstance); contr?.chatWidget?.reveal(); - // TODO: Remove this before merging to main - contr?.chatWidget?.setValue('list files'); } }); From 29d9a36ba6ea6f23dba59caec8ab344a4db10606 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 16 Feb 2024 15:08:12 -0600 Subject: [PATCH 0119/1175] set vertical position --- .../chat/browser/terminalChatWidget.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 86b3fd8de33..0653f403de0 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -108,6 +108,18 @@ export class TerminalChatWidget extends Disposable { this._focusedContextKey.set(true); this._visibleContextKey.set(true); this._inlineChatWidget.focus(); + const font = this._instance.xterm?.getFont(); + if (!font?.charHeight) { + return; + } + const cursorY = this._instance.xterm?.raw.buffer.active.cursorY ?? 0; + const height = font.charHeight * font.lineHeight; + const top = (cursorY + .5) * height; + this._container.style.top = `${top}px`; + const terminalHeight = this._instance.domElement.clientHeight; + if (terminalHeight && top > terminalHeight - this._inlineChatWidget.getHeight()) { + this._container.style.top = ''; + } } hide(): void { this._container.classList.add('hide'); From f70e23725040fca3ae5517c2d26a4b0765a9e444 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 16 Feb 2024 13:09:23 -0800 Subject: [PATCH 0120/1175] Resolve simple todos, remove duplication --- .../chat/browser/media/terminalChatWidget.css | 3 +- .../browser/terminal.chat.contribution.ts | 4 +- .../browser/terminalChatAccessibilityHelp.ts | 44 ++++++++--------- ...rminalChatAccessibilityHelpContribution.ts | 48 ------------------- .../chat/browser/terminalChatController.ts | 2 - 5 files changed, 23 insertions(+), 78 deletions(-) delete mode 100644 src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css index 602560618cc..64253f67db5 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/media/terminalChatWidget.css @@ -29,8 +29,7 @@ .terminal-inline-chat .terminal-inline-chat-response { border: 1px solid var(--vscode-input-border, transparent); - /* TODO: Make themeable */ - background-color: #181818; + background-color: var(--vscode-panel-background); } .terminal-inline-chat .terminal-inline-chat-response:has(.monaco-editor.focused) { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index 0049340fadd..44eabbf13f6 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -5,15 +5,13 @@ import { WorkbenchPhase, registerWorkbenchContribution2 } from 'vs/workbench/common/contributions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; -import { TerminalInlineChatAccessibilityHelpContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp'; import { TerminalInlineChatAccessibleViewContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; -import { TerminalChatAccessibilityHelpContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution'; import 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions'; +import { TerminalChatAccessibilityHelpContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp'; registerTerminalContribution(TerminalChatController.ID, TerminalChatController, false); registerWorkbenchContribution2(TerminalInlineChatAccessibleViewContribution.ID, TerminalInlineChatAccessibleViewContribution, WorkbenchPhase.Eventually); -registerWorkbenchContribution2(TerminalInlineChatAccessibilityHelpContribution.ID, TerminalInlineChatAccessibilityHelpContribution, WorkbenchPhase.Eventually); registerWorkbenchContribution2(TerminalChatAccessibilityHelpContribution.ID, TerminalChatAccessibilityHelpContribution, WorkbenchPhase.Eventually); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts index d330683af2e..c29c7394c3f 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts @@ -5,43 +5,41 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; -import { CTX_INLINE_CHAT_RESPONSE_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalChatCommandId, TerminalChatContextKeys } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; -export class TerminalInlineChatAccessibilityHelpContribution extends Disposable { - static ID: 'terminalInlineChatAccessibilityHelpContribution'; +export class TerminalChatAccessibilityHelpContribution extends Disposable { + static ID = 'terminalChatAccessiblityHelp'; constructor() { super(); - this._register(AccessibilityHelpAction.addImplementation(106, 'terminalInlineChat', accessor => { - const terminalService = accessor.get(ITerminalService); - const accessibleViewService = accessor.get(IAccessibleViewService); - const controller: TerminalChatController | undefined = terminalService.activeInstance?.getContribution(TerminalChatController.ID) ?? undefined; - if (controller === undefined) { - return false; - } - const helpContent = getAccessibilityHelpText(accessor); - accessibleViewService.show({ - id: AccessibleViewProviderId.TerminalInlineChat, - verbositySettingKey: AccessibilityVerbositySettingId.InlineChat, - provideContent(): string { return helpContent; }, - onClose() { - controller.focus(); - }, - options: { type: AccessibleViewType.Help } - }); - return true; - }, ContextKeyExpr.or(TerminalChatContextKeys.focused, TerminalChatContextKeys.responseEditorFocused, CTX_INLINE_CHAT_RESPONSE_FOCUSED))); + this._register(AccessibilityHelpAction.addImplementation(110, 'terminalChat', runAccessibilityHelpAction, TerminalChatContextKeys.focused)); } } +export async function runAccessibilityHelpAction(accessor: ServicesAccessor): Promise { + const accessibleViewService = accessor.get(IAccessibleViewService); + const terminalService = accessor.get(ITerminalService); + + const instance = terminalService.activeInstance; + if (!instance) { + return; + } + + const helpText = getAccessibilityHelpText(accessor); + accessibleViewService.show({ + id: AccessibleViewProviderId.TerminalChat, + verbositySettingKey: AccessibilityVerbositySettingId.TerminalChat, + provideContent: () => helpText, + onClose: () => TerminalChatController.get(instance)?.focus(), + options: { type: AccessibleViewType.Help } + }); +} export function getAccessibilityHelpText(accessor: ServicesAccessor): string { const keybindingService = accessor.get(IKeybindingService); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts deleted file mode 100644 index 61d11998690..00000000000 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelpContribution.ts +++ /dev/null @@ -1,48 +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 { localize } from 'vs/nls'; -import type { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; -import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; -import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; -import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { TerminalChatContextKeys } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; -import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; - -export class TerminalChatAccessibilityHelpContribution extends Disposable { - static ID = 'terminalChatAccessiblityHelp'; - constructor() { - super(); - this._register(AccessibilityHelpAction.addImplementation(110, 'terminalChat', runAccessibilityHelpAction, TerminalChatContextKeys.focused)); - } -} - -export async function runAccessibilityHelpAction(accessor: ServicesAccessor): Promise { - const accessibleViewService = accessor.get(IAccessibleViewService); - const terminalService = accessor.get(ITerminalService); - - const instance = terminalService.activeInstance; - if (!instance) { - return; - } - - const helpText = getAccessibilityHelpText(accessor); - accessibleViewService.show({ - id: AccessibleViewProviderId.TerminalChat, - verbositySettingKey: AccessibilityVerbositySettingId.TerminalChat, - provideContent: () => helpText, - onClose: () => TerminalChatController.get(instance)?.focus(), - options: { type: AccessibleViewType.Help } - }); -} - -export function getAccessibilityHelpText(accessor: ServicesAccessor): string { - const content = []; - // TODO: Fill in more help text - content.push(localize('chat.overview', 'The terminal chat view is comprised of an input box, an editor where suggested commands are provided (Shift+Tab) and buttons to action the suggestion.')); - return content.join('\n\n'); -} diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 0226d6536f0..47025af317b 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -256,7 +256,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr requestId: this._currentRequest!.id, agentId: this._terminalAgentId, message: this._lastInput, - // TODO: ? variables: { variables: [] }, }; try { @@ -265,7 +264,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._chatWidget?.rawValue?.inlineChatWidget.updateFollowUps(undefined); this._chatWidget?.rawValue?.inlineChatWidget.updateProgress(true); this._chatWidget?.rawValue?.inlineChatWidget.updateInfo(localize('thinking', "Thinking\u2026")); - // TODO: this._zone.value.widget.updateInfo(!this._session.lastExchange ? localize('thinking', "Thinking\u2026") : ''); await task; } catch (e) { From fa685221f2d1598d0da2d229ce3885a16b61a492 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 16 Feb 2024 15:19:53 -0600 Subject: [PATCH 0121/1175] rm todo --- .../chat/browser/terminalChatAccessibilityHelp.ts | 3 +-- .../terminalContrib/chat/browser/terminalChatActions.ts | 2 +- .../contrib/terminalContrib/chat/browser/terminalChatWidget.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts index c29c7394c3f..dad7747e7f1 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp.ts @@ -48,8 +48,7 @@ export function getAccessibilityHelpText(accessor: ServicesAccessor): string { const runCommandKeybinding = keybindingService.lookupKeybinding(TerminalChatCommandId.RunCommand)?.getAriaLabel(); const insertCommandKeybinding = keybindingService.lookupKeybinding(TerminalChatCommandId.InsertCommand)?.getAriaLabel(); const makeRequestKeybinding = keybindingService.lookupKeybinding(TerminalChatCommandId.MakeRequest)?.getAriaLabel(); - //TODO: using this instead of the terminal command bc by definition the inline terminal chat is focused when this dialog is invoked. - const startChatKeybinding = keybindingService.lookupKeybinding('inlineChat.start')?.getAriaLabel(); + const startChatKeybinding = keybindingService.lookupKeybinding(TerminalChatCommandId.Start)?.getAriaLabel(); content.push(localize('inlineChat.overview', "Inline chat occurs within a terminal. It is useful for suggesting terminal commands. Keep in mind that AI generated code may be incorrect.")); content.push(localize('inlineChat.access', "It can be activated using the command: Terminal: Start Chat ({0}), which will focus the input box.", startChatKeybinding)); content.push(makeRequestKeybinding ? localize('inlineChat.input', "The input box is where the user can type a request and can make the request ({0}). The widget will be closed and all content will be discarded when the Escape key is pressed and the terminal will regain focus.", makeRequestKeybinding) : localize('inlineChat.inputNoKb', "The input box is where the user can type a request and can make the request by tabbing to the Make Request button, which is not currently triggerable via keybindings. The widget will be closed and all content will be discarded when the Escape key is pressed and the terminal will regain focus.")); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 72eac948274..04ff3b3a472 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -21,7 +21,7 @@ registerActiveXtermAction({ title: localize2('startChat', 'Start Chat'), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyI, - when: ContextKeyExpr.and(TerminalChatContextKeys.focused.negate(), TerminalContextKeys.focusInAny), + when: ContextKeyExpr.and(TerminalContextKeys.focusInAny), weight: KeybindingWeight.WorkbenchContrib, }, f1: true, diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 0653f403de0..4e9bed0f354 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -114,7 +114,7 @@ export class TerminalChatWidget extends Disposable { } const cursorY = this._instance.xterm?.raw.buffer.active.cursorY ?? 0; const height = font.charHeight * font.lineHeight; - const top = (cursorY + .5) * height; + const top = cursorY * height; this._container.style.top = `${top}px`; const terminalHeight = this._instance.domElement.clientHeight; if (terminalHeight && top > terminalHeight - this._inlineChatWidget.getHeight()) { From 27d1fc77f4fee8fdf442f9ae3f1175c1105760aa Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 16 Feb 2024 16:52:57 -0600 Subject: [PATCH 0122/1175] Fix position --- .../contrib/terminalContrib/chat/browser/terminalChatWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 4e9bed0f354..180d51c1549 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -114,7 +114,7 @@ export class TerminalChatWidget extends Disposable { } const cursorY = this._instance.xterm?.raw.buffer.active.cursorY ?? 0; const height = font.charHeight * font.lineHeight; - const top = cursorY * height; + const top = cursorY * height + 10; this._container.style.top = `${top}px`; const terminalHeight = this._instance.domElement.clientHeight; if (terminalHeight && top > terminalHeight - this._inlineChatWidget.getHeight()) { From 227096140942ce2b08a2358d55b6f28fa1dce573 Mon Sep 17 00:00:00 2001 From: NriotHrreion Date: Mon, 19 Feb 2024 09:27:00 +0800 Subject: [PATCH 0123/1175] fix #205353 --- src/vs/editor/browser/widget/hoverWidget/hoverWidget.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/editor/browser/widget/hoverWidget/hoverWidget.ts b/src/vs/editor/browser/widget/hoverWidget/hoverWidget.ts index d6f6249675c..8b4814a92c9 100644 --- a/src/vs/editor/browser/widget/hoverWidget/hoverWidget.ts +++ b/src/vs/editor/browser/widget/hoverWidget/hoverWidget.ts @@ -331,10 +331,7 @@ export class HoverWidget extends Widget implements IHoverWidget { }; const targetBounds = this._target.targetElements.map(e => getZoomAccountedBoundingClientRect(e)); - const top = Math.min(...targetBounds.map(e => e.top)); - const right = Math.max(...targetBounds.map(e => e.right)); - const bottom = Math.max(...targetBounds.map(e => e.bottom)); - const left = Math.min(...targetBounds.map(e => e.left)); + const { top, right, bottom, left } = targetBounds[0]; const width = right - left; const height = bottom - top; From 0f282e2a59809a32606fb6f2720c82a034c0856e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= Date: Fri, 23 Feb 2024 11:16:40 +0100 Subject: [PATCH 0124/1175] Fix fullscreen container dimension detection when not directly on body (#205884) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix container dimension detection when not directly on body * Add some comments Co-authored-by: Benjamin Pasero --------- Co-authored-by: Loïc Mangeonjean Co-authored-by: Benjamin Pasero --- src/vs/workbench/browser/layout.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index a2b6007fb79..797a1713dd8 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1541,7 +1541,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi layout(): void { if (!this.disposed) { - this._mainContainerDimension = getClientArea(this.parent); + this._mainContainerDimension = getClientArea(this.state.runtime.mainWindowFullscreen ? + mainWindow.document.body : // in fullscreen mode, make sure to use element because + this.parent // in that case the workbench will span the entire site + ); this.logService.trace(`Layout#layout, height: ${this._mainContainerDimension.height}, width: ${this._mainContainerDimension.width}`); position(this.mainContainer, 0, 0, 0, 0, 'relative'); From 3658a5124f2b9d58cefd62f1826e4e180f7c337d Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 23 Feb 2024 11:33:33 +0100 Subject: [PATCH 0125/1175] More hover adoptions and debt (#205972) * Hover Fixes * Updated hover behavior and accessibility attributes across multiple components --- .../ui/dropdown/dropdownActionViewItem.ts | 2 +- src/vs/base/browser/ui/hover/hoverDelegate.ts | 2 +- .../browser/ui/iconLabel/iconLabelHover.ts | 28 ++++++++-- src/vs/platform/hover/browser/hover.ts | 10 ++-- .../browser/parts/compositeBarActions.ts | 4 +- .../workbench/browser/parts/compositePart.ts | 10 ++-- .../notifications/notificationsViewer.ts | 28 +++++----- .../browser/parts/paneCompositePart.ts | 2 +- .../workbench/browser/parts/views/viewPane.ts | 10 ++-- .../contrib/comments/browser/commentReply.ts | 4 +- .../comments/browser/commentsTreeViewer.ts | 4 +- .../contrib/comments/browser/timestamp.ts | 9 ++-- .../contrib/debug/browser/baseDebugView.ts | 7 +-- .../contrib/debug/browser/breakpointsView.ts | 29 +++++----- .../contrib/debug/browser/callStackView.ts | 29 ++++++---- .../debug/browser/debugActionViewItems.ts | 7 ++- .../contrib/debug/browser/replViewer.ts | 4 +- .../files/browser/views/explorerView.ts | 2 +- .../notebook/browser/notebookEditor.ts | 5 +- .../browser/view/cellParts/cellActionView.ts | 5 +- .../browser/view/cellParts/cellToolbars.ts | 14 +++-- .../viewParts/notebookEditorToolbar.ts | 53 +++++++++++-------- .../browser/viewParts/notebookKernelView.ts | 5 +- .../contrib/remote/browser/tunnelView.ts | 2 +- 24 files changed, 172 insertions(+), 103 deletions(-) diff --git a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts index 419658a21bb..995b8f40817 100644 --- a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -93,7 +93,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { this.element.setAttribute('aria-haspopup', 'true'); this.element.setAttribute('aria-expanded', 'false'); if (this._action.label) { - this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.element, this._action.label)); + this._register(setupCustomHover(this.options.hoverDelegate ?? getDefaultHoverDelegate('mouse'), this.element, this._action.label)); } this.element.ariaLabel = this._action.label || ''; diff --git a/src/vs/base/browser/ui/hover/hoverDelegate.ts b/src/vs/base/browser/ui/hover/hoverDelegate.ts index 6682d739c26..6d2dfef371a 100644 --- a/src/vs/base/browser/ui/hover/hoverDelegate.ts +++ b/src/vs/base/browser/ui/hover/hoverDelegate.ts @@ -21,7 +21,7 @@ export function setHoverDelegateFactory(hoverDelegateProvider: ((placement: 'mou } export function getDefaultHoverDelegate(placement: 'mouse' | 'element'): IHoverDelegate; -export function getDefaultHoverDelegate(placement: 'mouse' | 'element', enableInstantHover: true): IScopedHoverDelegate; +export function getDefaultHoverDelegate(placement: 'element', enableInstantHover: true): IScopedHoverDelegate; export function getDefaultHoverDelegate(placement: 'mouse' | 'element', enableInstantHover?: boolean): IHoverDelegate | IScopedHoverDelegate { if (enableInstantHover) { // If instant hover is enabled, the consumer is responsible for disposing the hover delegate diff --git a/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts b/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts index 20ad4662b0d..bdcdfa7c7da 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts @@ -163,9 +163,24 @@ class UpdatableHoverWidget implements IDisposable { } } +function getHoverTargetElement(element: HTMLElement, stopElement?: HTMLElement): HTMLElement { + stopElement = stopElement ?? dom.getWindow(element).document.body; + while (!element.hasAttribute('custom-hover') && element !== stopElement) { + element = element.parentElement!; + } + return element; +} + export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, content: IHoverContentOrFactory, options?: IUpdatableHoverOptions): ICustomHover { - let hoverPreparation: IDisposable | undefined; + htmlElement.setAttribute('custom-hover', 'true'); + + if (htmlElement.title !== '') { + console.warn('HTML element already has a title attribute, which will conflict with the custom hover. Please remove the title attribute.'); + console.trace('Stack trace:', htmlElement.title); + htmlElement.title = ''; + } + let hoverPreparation: IDisposable | undefined; let hoverWidget: UpdatableHoverWidget | undefined; const hideHover = (disposeWidget: boolean, disposePreparation: boolean) => { @@ -206,7 +221,7 @@ export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTM hideHover(false, (e).fromElement === htmlElement); }, true); - const onMouseOver = () => { + const onMouseOver = (e: MouseEvent) => { if (hoverPreparation) { return; } @@ -221,15 +236,20 @@ export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTM // track the mouse position const onMouseMove = (e: MouseEvent) => { target.x = e.x + 10; - if ((e.target instanceof HTMLElement) && e.target.classList.contains('action-label')) { + if ((e.target instanceof HTMLElement) && getHoverTargetElement(e.target, htmlElement) !== htmlElement) { hideHover(true, true); } }; toDispose.add(dom.addDisposableListener(htmlElement, dom.EventType.MOUSE_MOVE, onMouseMove, true)); } - toDispose.add(triggerShowHover(hoverDelegate.delay, false, target)); hoverPreparation = toDispose; + + if ((e.target instanceof HTMLElement) && getHoverTargetElement(e.target as HTMLElement, htmlElement) !== htmlElement) { + return; // Do not show hover when the mouse is over another hover target + } + + toDispose.add(triggerShowHover(hoverDelegate.delay, false, target)); }; const mouseOverDomEmitter = dom.addDisposableListener(htmlElement, dom.EventType.MOUSE_OVER, onMouseOver, true); diff --git a/src/vs/platform/hover/browser/hover.ts b/src/vs/platform/hover/browser/hover.ts index c9edff5d2a3..82d9574ca06 100644 --- a/src/vs/platform/hover/browser/hover.ts +++ b/src/vs/platform/hover/browser/hover.ts @@ -282,14 +282,18 @@ export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate return this.hoverService.showHover({ ...options, persistence: { - hideOnHover: true + hideOnHover: true, + hideOnKeyDown: true, }, ...overrideOptions }, focus); } - setOptions(options: Partial | ((options: IHoverDelegateOptions, focus?: boolean) => Partial)): void { - this.overrideOptions = options; + setInstantHoverTimeLimit(timeLimit: number): void { + if (!this.instantHover) { + throw new Error('Instant hover is not enabled'); + } + this.timeLimit = timeLimit; } onDidHideHover(): void { diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index fb06c28c67d..cfa2d348aa0 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -706,12 +706,12 @@ export class CompositeActionViewItem extends CompositeBarActionViewItem { protected override updateChecked(): void { if (this.action.checked) { this.container.classList.add('checked'); - this.container.setAttribute('aria-label', this.container.title); + this.container.setAttribute('aria-label', this.getTooltip() ?? this.container.title); this.container.setAttribute('aria-expanded', 'true'); this.container.setAttribute('aria-selected', 'true'); } else { this.container.classList.remove('checked'); - this.container.setAttribute('aria-label', this.container.title); + this.container.setAttribute('aria-label', this.getTooltip() ?? this.container.title); this.container.setAttribute('aria-expanded', 'false'); this.container.setAttribute('aria-selected', 'false'); } diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index b4d406a6364..81a034ffa91 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -35,6 +35,7 @@ import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash'; import { IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; export interface ICompositeTitleLabel { @@ -62,7 +63,7 @@ export abstract class CompositePart extends Part { protected toolBar: WorkbenchToolBar | undefined; protected titleLabelElement: HTMLElement | undefined; - protected readonly hoverDelegate: IHoverDelegate; + protected readonly toolbarHoverDelegate: IHoverDelegate; private readonly mapCompositeToCompositeContainer = new Map(); private readonly mapActionsBindingToComposite = new Map void>(); @@ -96,7 +97,7 @@ export abstract class CompositePart extends Part { super(id, options, themeService, storageService, layoutService); this.lastActiveCompositeId = storageService.get(activeCompositeSettingsKey, StorageScope.WORKSPACE, this.defaultCompositeId); - this.hoverDelegate = this._register(getDefaultHoverDelegate('element', true)); + this.toolbarHoverDelegate = this._register(getDefaultHoverDelegate('element', true)); } protected openComposite(id: string, focus?: boolean): Composite | undefined { @@ -407,7 +408,7 @@ export abstract class CompositePart extends Part { anchorAlignmentProvider: () => this.getTitleAreaDropDownAnchorAlignment(), toggleMenuTitle: localize('viewsAndMoreActions', "Views and More Actions..."), telemetrySource: this.nameForTelemetry, - hoverDelegate: this.hoverDelegate + hoverDelegate: this.toolbarHoverDelegate })); this.collectCompositeActions()(); @@ -419,6 +420,7 @@ export abstract class CompositePart extends Part { const titleContainer = append(parent, $('.title-label')); const titleLabel = append(titleContainer, $('h2')); this.titleLabelElement = titleLabel; + const hover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), titleLabel, '')); const $this = this; return { @@ -426,7 +428,7 @@ export abstract class CompositePart extends Part { // The title label is shared for all composites in the base CompositePart if (!this.activeComposite || this.activeComposite.getId() === id) { titleLabel.innerText = title; - titleLabel.title = keybinding ? localize('titleTooltip', "{0} ({1})", title, keybinding) : title; + hover.update(keybinding ? localize('titleTooltip', "{0} ({1})", title, keybinding) : title); } }, diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index 484ee51adb4..e66dc53ad61 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -29,6 +29,8 @@ import { Event } from 'vs/base/common/event'; import { defaultButtonStyles, defaultProgressBarStyles } from 'vs/platform/theme/browser/defaultStyles'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export class NotificationsListDelegate implements IListVirtualDelegate { @@ -235,7 +237,7 @@ export class NotificationRenderer implements IListRenderer { + actionViewItemProvider: (action, options) => { if (action instanceof ConfigureNotificationAction) { return data.toDispose.add(new DropdownMenuActionViewItem(action, { getActions() { @@ -262,6 +264,7 @@ export class NotificationRenderer implements IListRenderer this.openerService.open(URI.parse(link), { allowCommands: true }), @@ -425,11 +430,8 @@ export class NotificationTemplateRenderer extends Disposable { })); const messageOverflows = notification.canCollapse && !notification.expanded && this.template.message.scrollWidth > this.template.message.clientWidth; - if (messageOverflows) { - this.template.message.title = this.template.message.textContent + ''; - } else { - this.template.message.removeAttribute('title'); - } + + customHover.update(messageOverflows ? this.template.message.textContent + '' : ''); return messageOverflows; } @@ -470,13 +472,13 @@ export class NotificationTemplateRenderer extends Disposable { actions.forEach(action => this.template.toolbar.push(action, { icon: true, label: false, keybinding: this.getKeybindingLabel(action) })); } - private renderSource(notification: INotificationViewItem): void { + private renderSource(notification: INotificationViewItem, sourceCustomHover: ICustomHover): void { if (notification.expanded && notification.source) { this.template.source.textContent = localize('notificationSource', "Source: {0}", notification.source); - this.template.source.title = notification.source; + sourceCustomHover.update(notification.source); } else { this.template.source.textContent = ''; - this.template.source.removeAttribute('title'); + sourceCustomHover.update(''); } } diff --git a/src/vs/workbench/browser/parts/paneCompositePart.ts b/src/vs/workbench/browser/parts/paneCompositePart.ts index 6ef4dfe09a3..3a1cb5382e4 100644 --- a/src/vs/workbench/browser/parts/paneCompositePart.ts +++ b/src/vs/workbench/browser/parts/paneCompositePart.ts @@ -307,7 +307,7 @@ export abstract class AbstractPaneCompositePart extends CompositePart this.keybindingService.lookupKeybinding(action.id), anchorAlignmentProvider: () => this.getTitleAreaDropDownAnchorAlignment(), toggleMenuTitle: localize('moreActions', "More Actions..."), - hoverDelegate: this.hoverDelegate + hoverDelegate: this.toolbarHoverDelegate })); this.updateGlobalToolbarActions(); diff --git a/src/vs/workbench/browser/parts/views/viewPane.ts b/src/vs/workbench/browser/parts/views/viewPane.ts index 978baf437cd..9cc1b35c354 100644 --- a/src/vs/workbench/browser/parts/views/viewPane.ts +++ b/src/vs/workbench/browser/parts/views/viewPane.ts @@ -47,6 +47,8 @@ import { FilterWidget, IFilterWidgetOptions } from 'vs/workbench/browser/parts/v import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { defaultButtonStyles, defaultProgressBarStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export enum ViewPaneShowActions { /** Show the actions when the view is hovered. This is the default behavior. */ @@ -342,6 +344,7 @@ export abstract class ViewPane extends Pane implements IView { private titleContainer?: HTMLElement; private titleDescriptionContainer?: HTMLElement; private iconContainer?: HTMLElement; + private iconContainerHover?: ICustomHover; protected twistiesContainer?: HTMLElement; private viewWelcomeController!: ViewWelcomeController; @@ -519,13 +522,14 @@ export abstract class ViewPane extends Pane implements IView { } const calculatedTitle = this.calculateTitle(title); - this.titleContainer = append(container, $('h3.title', { title: calculatedTitle }, calculatedTitle)); + this.titleContainer = append(container, $('h3.title', {}, calculatedTitle)); + setupCustomHover(getDefaultHoverDelegate('mouse'), this.titleContainer, calculatedTitle); if (this._titleDescription) { this.setTitleDescription(this._titleDescription); } - this.iconContainer.title = calculatedTitle; + this.iconContainerHover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.iconContainer, calculatedTitle)); this.iconContainer.setAttribute('aria-label', calculatedTitle); } @@ -537,7 +541,7 @@ export abstract class ViewPane extends Pane implements IView { } if (this.iconContainer) { - this.iconContainer.title = calculatedTitle; + this.iconContainerHover?.update(calculatedTitle); this.iconContainer.setAttribute('aria-label', calculatedTitle); } diff --git a/src/vs/workbench/contrib/comments/browser/commentReply.ts b/src/vs/workbench/contrib/comments/browser/commentReply.ts index 21aeb3f22d5..4d2a9e4b887 100644 --- a/src/vs/workbench/contrib/comments/browser/commentReply.ts +++ b/src/vs/workbench/contrib/comments/browser/commentReply.ts @@ -30,6 +30,8 @@ import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/comme import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { LayoutableEditor, MIN_EDITOR_HEIGHT, SimpleCommentEditor, calculateEditorHeight } from './simpleCommentEditor'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; const COMMENT_SCHEME = 'comment'; let INMEM_MODEL_ID = 0; @@ -355,7 +357,7 @@ export class CommentReply extends Disposable { private createReplyButton(commentEditor: ICodeEditor, commentForm: HTMLElement) { this._reviewThreadReplyButton = dom.append(commentForm, dom.$(`button.review-thread-reply-button.${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`)); - this._reviewThreadReplyButton.title = this._commentOptions?.prompt || nls.localize('reply', "Reply..."); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this._reviewThreadReplyButton, this._commentOptions?.prompt || nls.localize('reply', "Reply..."))); this._reviewThreadReplyButton.textContent = this._commentOptions?.prompt || nls.localize('reply', "Reply..."); // bind click/escape actions for reviewThreadReplyButton and textArea diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index 69ffc3d3f6d..585d253442c 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -32,6 +32,8 @@ import { IStyleOverride } from 'vs/platform/theme/browser/defaultStyles'; import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { ILocalizedString } from 'vs/platform/action/common/action'; import { CommentsModel } from 'vs/workbench/contrib/comments/browser/commentsModel'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export const COMMENTS_VIEW_ID = 'workbench.panel.comments'; export const COMMENTS_VIEW_STORAGE_ID = 'Comments'; @@ -219,7 +221,7 @@ export class CommentNodeRenderer implements IListRenderer const renderedComment = this.getRenderedComment(originalComment.comment.body, disposables); templateData.disposables.push(renderedComment); templateData.threadMetadata.commentPreview.appendChild(renderedComment.element.firstElementChild ?? renderedComment.element); - templateData.threadMetadata.commentPreview.title = renderedComment.element.textContent ?? ''; + templateData.disposables.push(setupCustomHover(getDefaultHoverDelegate('mouse'), templateData.threadMetadata.commentPreview, renderedComment.element.textContent ?? '')); } if (node.element.range) { diff --git a/src/vs/workbench/contrib/comments/browser/timestamp.ts b/src/vs/workbench/contrib/comments/browser/timestamp.ts index 2b9f79d4a88..2d1fcf15b48 100644 --- a/src/vs/workbench/contrib/comments/browser/timestamp.ts +++ b/src/vs/workbench/contrib/comments/browser/timestamp.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { fromNow } from 'vs/base/common/date'; import { Disposable } from 'vs/base/common/lifecycle'; import { language } from 'vs/base/common/platform'; @@ -15,12 +17,15 @@ export class TimestampWidget extends Disposable { private _timestamp: Date | undefined; private _useRelativeTime: boolean; + private hover: ICustomHover; + constructor(private configurationService: IConfigurationService, container: HTMLElement, timeStamp?: Date) { super(); this._date = dom.append(container, dom.$('span.timestamp')); this._date.style.display = 'none'; this._useRelativeTime = this.useRelativeTimeSetting; this.setTimestamp(timeStamp); + this.hover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this._date, '')); } private get useRelativeTimeSetting(): boolean { @@ -52,9 +57,7 @@ export class TimestampWidget extends Disposable { } this._date.textContent = textContent; - if (tooltip) { - this._date.title = tooltip; - } + this.hover.update(tooltip ?? ''); } } diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index ac626b82f40..90aba19a7f2 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -7,6 +7,8 @@ import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { IInputValidationOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { IAsyncDataSource, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { Codicon } from 'vs/base/common/codicons'; @@ -187,19 +189,18 @@ export abstract class AbstractExpressionsRenderer implements IT abstract get templateId(): string; renderTemplate(container: HTMLElement): IExpressionTemplateData { + const templateDisposable = new DisposableStore(); const expression = dom.append(container, $('.expression')); const name = dom.append(expression, $('span.name')); const lazyButton = dom.append(expression, $('span.lazy-button')); lazyButton.classList.add(...ThemeIcon.asClassNameArray(Codicon.eye)); - lazyButton.title = localize('debug.lazyButton.tooltip', "Click to expand"); + templateDisposable.add(setupCustomHover(getDefaultHoverDelegate('mouse'), lazyButton, localize('debug.lazyButton.tooltip', "Click to expand"))); const value = dom.append(expression, $('span.value')); const label = new HighlightedLabel(name); const inputBoxContainer = dom.append(expression, $('.inputBoxContainer')); - const templateDisposable = new DisposableStore(); - let actionBar: ActionBar | undefined; if (this.renderActionBar) { dom.append(expression, $('.span.actionbar-spacer')); diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index e60c8079a14..5de52259df4 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -8,7 +8,9 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Gesture } from 'vs/base/browser/touch'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { AriaRole } from 'vs/base/browser/ui/aria/aria'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { IListContextMenuEvent, IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; @@ -549,7 +551,7 @@ class BreakpointsRenderer implements IListRenderer t.stopped); @@ -603,11 +608,11 @@ class SessionsRenderer implements ICompressibleTreeRenderer, _index: number, data: IThreadTemplateData): void { const thread = element.element; - data.thread.title = thread.name; + data.elementDisposable.add(setupCustomHover(getDefaultHoverDelegate('mouse'), data.thread, thread.name)); data.label.set(thread.name, createMatches(element.filterData)); data.stateLabel.textContent = thread.stateLabel; data.stateLabel.classList.toggle('exception', thread.stoppedDetails?.reason === 'exception'); @@ -743,10 +748,12 @@ class StackFramesRenderer implements ICompressibleTreeRenderer, index: number, data: IErrorTemplateData): void { const error = element.element; data.label.textContent = error; - data.label.title = error; + data.templateDisposable.add(setupCustomHover(getDefaultHoverDelegate('mouse'), data.label, error)); } renderCompressedElements(node: ITreeNode, FuzzyScore>, index: number, templateData: IErrorTemplateData, height: number | undefined): void { diff --git a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts index 433a562e4bf..2f31d9c5aaf 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActionViewItems.ts @@ -22,6 +22,8 @@ import { BaseActionViewItem, IBaseActionViewItemOptions, SelectActionViewItem } import { debugStart } from 'vs/workbench/contrib/debug/browser/debugIcons'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { defaultSelectBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; const $ = dom.$; @@ -74,9 +76,10 @@ export class StartDebugActionViewItem extends BaseActionViewItem { this.start = dom.append(container, $(ThemeIcon.asCSSSelector(debugStart))); const keybinding = this.keybindingService.lookupKeybinding(this.action.id)?.getLabel(); const keybindingLabel = keybinding ? ` (${keybinding})` : ''; - this.start.title = this.action.label + keybindingLabel; + const title = this.action.label + keybindingLabel; + this.toDispose.push(setupCustomHover(getDefaultHoverDelegate('mouse'), this.start, title)); this.start.setAttribute('role', 'button'); - this.start.ariaLabel = this.start.title; + this.start.ariaLabel = title; this.toDispose.push(dom.addDisposableListener(this.start, dom.EventType.CLICK, () => { this.start.blur(); diff --git a/src/vs/workbench/contrib/debug/browser/replViewer.ts b/src/vs/workbench/contrib/debug/browser/replViewer.ts index 1ba443a95f1..64c3f3d6ccd 100644 --- a/src/vs/workbench/contrib/debug/browser/replViewer.ts +++ b/src/vs/workbench/contrib/debug/browser/replViewer.ts @@ -28,6 +28,8 @@ import { IDebugConfiguration, IDebugService, IDebugSession, IExpression, IExpres import { Variable } from 'vs/workbench/contrib/debug/common/debugModel'; import { RawObjectReplElement, ReplEvaluationInput, ReplEvaluationResult, ReplGroup, ReplOutputElement, ReplVariableElement } from 'vs/workbench/contrib/debug/common/replModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; const $ = dom.$; @@ -199,7 +201,7 @@ export class ReplOutputElementRenderer implements ITreeRenderer element.sourceData; } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 71960988d83..d7e695e1958 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -276,7 +276,7 @@ export class ExplorerView extends ViewPane implements IExplorerView { const workspace = this.contextService.getWorkspace(); const title = workspace.folders.map(folder => folder.name).join(); titleElement.textContent = this.name; - titleElement.title = title; + this.updateTitle(title); this.ariaHeaderLabel = nls.localize('explorerSection', "Explorer Section: {0}", this.name); titleElement.setAttribute('aria-label', this.ariaHeaderLabel); }; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 10ae44a781d..0a4cc62f4ed 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -47,6 +47,7 @@ import { streamToBuffer } from 'vs/base/common/buffer'; import { ILogService } from 'vs/platform/log/common/log'; import { INotebookEditorWorkerService } from 'vs/workbench/contrib/notebook/common/services/notebookWorkerService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; const NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'NotebookEditorViewState'; @@ -137,10 +138,10 @@ export class NotebookEditor extends EditorPane implements INotebookEditorPane { this._rootElement.id = `notebook-editor-element-${generateUuid()}`; } - override getActionViewItem(action: IAction): IActionViewItem | undefined { + override getActionViewItem(action: IAction, options: IActionViewItemOptions): IActionViewItem | undefined { if (action.id === SELECT_KERNEL_ID) { // this is being disposed by the consumer - return this._instantiationService.createInstance(NotebooKernelActionViewItem, action, this); + return this._instantiationService.createInstance(NotebooKernelActionViewItem, action, this, options); } return undefined; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts index c2c7f3e740a..35007bedeb8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts @@ -57,7 +57,7 @@ export class UnifiedSubmenuActionView extends SubmenuEntryActionViewItem { @IContextMenuService _contextMenuService: IContextMenuService, @IThemeService _themeService: IThemeService ) { - super(action, options, _keybindingService, _contextMenuService, _themeService); + super(action, { ...options, hoverDelegate: options?.hoverDelegate ?? getDefaultHoverDelegate('element') }, _keybindingService, _contextMenuService, _themeService); } override render(container: HTMLElement): void { @@ -66,8 +66,7 @@ export class UnifiedSubmenuActionView extends SubmenuEntryActionViewItem { this._actionLabel = document.createElement('a'); container.appendChild(this._actionLabel); - const hoverDelegate = this.options.hoverDelegate ?? getDefaultHoverDelegate('element'); - this._hover = this._register(setupCustomHover(hoverDelegate, this._actionLabel, '')); + this._hover = this._register(setupCustomHover(this.options.hoverDelegate ?? getDefaultHoverDelegate('element'), this._actionLabel, '')); this.updateLabel(); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index a7b79d85684..12b86db201e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -22,6 +22,8 @@ import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/vie import { CellOverlayPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; import { registerCellToolbarStickyScroll } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll'; import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; export class BetweenCellToolbar extends CellOverlayPart { private _betweenCellToolbar: ToolBar | undefined; @@ -165,15 +167,16 @@ export class CellTitleToolbarPart extends CellOverlayPart { if (this._view) { return this._view; } - + const hoverDelegate = this._register(getDefaultHoverDelegate('element', true)); const toolbar = this._register(this.instantiationService.createInstance(WorkbenchToolBar, this.toolbarContainer, { actionViewItemProvider: (action, options) => { return createActionViewItem(this.instantiationService, action, options); }, - renderDropdownAsChildElement: true + renderDropdownAsChildElement: true, + hoverDelegate })); - const deleteToolbar = this._register(this.instantiationService.invokeFunction(accessor => createDeleteToolbar(accessor, this.toolbarContainer, 'cell-delete-toolbar'))); + const deleteToolbar = this._register(this.instantiationService.invokeFunction(accessor => createDeleteToolbar(accessor, this.toolbarContainer, hoverDelegate, 'cell-delete-toolbar'))); if (model.deleteActions.primary.length !== 0 || model.deleteActions.secondary.length !== 0) { deleteToolbar.setActions(model.deleteActions.primary, model.deleteActions.secondary); } @@ -269,7 +272,7 @@ function getCellToolbarActions(menu: IMenu): { primary: IAction[]; secondary: IA return result; } -function createDeleteToolbar(accessor: ServicesAccessor, container: HTMLElement, elementClass?: string): ToolBar { +function createDeleteToolbar(accessor: ServicesAccessor, container: HTMLElement, hoverDelegate: IHoverDelegate, elementClass?: string): ToolBar { const contextMenuService = accessor.get(IContextMenuService); const keybindingService = accessor.get(IKeybindingService); const instantiationService = accessor.get(IInstantiationService); @@ -278,7 +281,8 @@ function createDeleteToolbar(accessor: ServicesAccessor, container: HTMLElement, actionViewItemProvider: (action, options) => { return createActionViewItem(instantiationService, action, options); }, - renderDropdownAsChildElement: true + renderDropdownAsChildElement: true, + hoverDelegate }); if (elementClass) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts index e2972e82e8a..969127bc917 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts @@ -28,6 +28,8 @@ import { NotebookOptions } from 'vs/workbench/contrib/notebook/browser/notebookO import { IActionViewItem, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { disposableTimeout } from 'vs/base/common/async'; import { HiddenItemStrategy, IWorkbenchToolBarOptions, WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { WorkbenchHoverDelegate } from 'vs/platform/hover/browser/hover'; interface IActionModel { action: IAction; @@ -75,18 +77,18 @@ class WorkbenchAlwaysLabelStrategy implements IActionLayoutStrategy { readonly goToMenu: IMenu, readonly instantiationService: IInstantiationService) { } - actionProvider(action: IAction): IActionViewItem | undefined { + actionProvider(action: IAction, options: IActionViewItemOptions): IActionViewItem | undefined { if (action.id === SELECT_KERNEL_ID) { // this is being disposed by the consumer - return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor); + return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor, options); } if (action instanceof MenuItemAction) { - return this.instantiationService.createInstance(ActionViewWithLabel, action, undefined); + return this.instantiationService.createInstance(ActionViewWithLabel, action, { hoverDelegate: options.hoverDelegate }); } if (action instanceof SubmenuItemAction && action.item.submenu.id === MenuId.NotebookCellExecuteGoTo.id) { - return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, undefined, true, { + return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, { hoverDelegate: options.hoverDelegate }, true, { getActions: () => { return this.goToMenu.getActions().find(([group]) => group === 'navigation/execute')?.[1] ?? []; } @@ -115,25 +117,25 @@ class WorkbenchNeverLabelStrategy implements IActionLayoutStrategy { readonly goToMenu: IMenu, readonly instantiationService: IInstantiationService) { } - actionProvider(action: IAction): IActionViewItem | undefined { + actionProvider(action: IAction, options: IActionViewItemOptions): IActionViewItem | undefined { if (action.id === SELECT_KERNEL_ID) { // this is being disposed by the consumer - return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor); + return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor, options); } if (action instanceof MenuItemAction) { - return this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined); + return this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }); } if (action instanceof SubmenuItemAction) { if (action.item.submenu.id === MenuId.NotebookCellExecuteGoTo.id) { - return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, undefined, false, { + return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, { hoverDelegate: options.hoverDelegate }, false, { getActions: () => { return this.goToMenu.getActions().find(([group]) => group === 'navigation/execute')?.[1] ?? []; } }, this.actionProvider.bind(this)); } else { - return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action, undefined); + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }); } } @@ -159,20 +161,20 @@ class WorkbenchDynamicLabelStrategy implements IActionLayoutStrategy { readonly goToMenu: IMenu, readonly instantiationService: IInstantiationService) { } - actionProvider(action: IAction): IActionViewItem | undefined { + actionProvider(action: IAction, options: IActionViewItemOptions): IActionViewItem | undefined { if (action.id === SELECT_KERNEL_ID) { // this is being disposed by the consumer - return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor); + return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor, options); } const a = this.editorToolbar.primaryActions.find(a => a.action.id === action.id); if (!a || a.renderLabel) { if (action instanceof MenuItemAction) { - return this.instantiationService.createInstance(ActionViewWithLabel, action, undefined); + return this.instantiationService.createInstance(ActionViewWithLabel, action, { hoverDelegate: options.hoverDelegate }); } if (action instanceof SubmenuItemAction && action.item.submenu.id === MenuId.NotebookCellExecuteGoTo.id) { - return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, undefined, true, { + return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, { hoverDelegate: options.hoverDelegate }, true, { getActions: () => { return this.goToMenu.getActions().find(([group]) => group === 'navigation/execute')?.[1] ?? []; } @@ -182,18 +184,18 @@ class WorkbenchDynamicLabelStrategy implements IActionLayoutStrategy { return undefined; } else { if (action instanceof MenuItemAction) { - this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined); + this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }); } if (action instanceof SubmenuItemAction) { if (action.item.submenu.id === MenuId.NotebookCellExecuteGoTo.id) { - return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, undefined, false, { + return this.instantiationService.createInstance(UnifiedSubmenuActionView, action, { hoverDelegate: options.hoverDelegate }, false, { getActions: () => { return this.goToMenu.getActions().find(([group]) => group === 'navigation/execute')?.[1] ?? []; } }, this.actionProvider.bind(this)); } else { - return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action, undefined); + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }); } } @@ -321,24 +323,29 @@ export class NotebookEditorWorkbenchToolbar extends Disposable { notebookEditor: this.notebookEditor }; - const actionProvider = (action: IAction) => { + const actionProvider = (action: IAction, options: IActionViewItemOptions) => { if (action.id === SELECT_KERNEL_ID) { // this is being disposed by the consumer - return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor); + return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this.notebookEditor, options); } if (this._renderLabel !== RenderLabel.Never) { const a = this._primaryActions.find(a => a.action.id === action.id); if (a && a.renderLabel) { - return action instanceof MenuItemAction ? this.instantiationService.createInstance(ActionViewWithLabel, action, undefined) : undefined; + return action instanceof MenuItemAction ? this.instantiationService.createInstance(ActionViewWithLabel, action, { hoverDelegate: options.hoverDelegate }) : undefined; } else { - return action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) : undefined; + return action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }) : undefined; } } else { - return action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) : undefined; + return action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }) : undefined; } }; + // Make sure both toolbars have the same hover delegate for instant hover to work + // Due to the elements being further apart than normal toolbars, the default time limit is to short and has to be increased + const hoverDelegate = this._register(this.instantiationService.createInstance(WorkbenchHoverDelegate, 'element', true, {})); + hoverDelegate.setInstantHoverTimeLimit(600); + const leftToolbarOptions: IWorkbenchToolBarOptions = { hiddenItemStrategy: HiddenItemStrategy.RenderInSecondaryGroup, resetMenu: MenuId.NotebookToolbar, @@ -347,6 +354,7 @@ export class NotebookEditorWorkbenchToolbar extends Disposable { }, getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), renderDropdownAsChildElement: true, + hoverDelegate }; this._notebookLeftToolbar = this.instantiationService.createInstance( @@ -363,7 +371,8 @@ export class NotebookEditorWorkbenchToolbar extends Disposable { this._notebookRightToolbar = new ToolBar(this._notebookTopRightToolbarContainer, this.contextMenuService, { getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), actionViewItemProvider: actionProvider, - renderDropdownAsChildElement: true + renderDropdownAsChildElement: true, + hoverDelegate }); this._register(this._notebookRightToolbar); this._notebookRightToolbar.context = context; diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView.ts index 34958f1e7c0..8c3ecd82436 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { ActionViewItem, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { Action, IAction } from 'vs/base/common/actions'; import { Event } from 'vs/base/common/event'; import { localize, localize2 } from 'vs/nls'; @@ -136,13 +136,14 @@ export class NotebooKernelActionViewItem extends ActionViewItem { constructor( actualAction: IAction, private readonly _editor: { onDidChangeModel: Event; textModel: NotebookTextModel | undefined; scopedContextKeyService?: IContextKeyService } | INotebookEditor, + options: IActionViewItemOptions, @INotebookKernelService private readonly _notebookKernelService: INotebookKernelService, @INotebookKernelHistoryService private readonly _notebookKernelHistoryService: INotebookKernelHistoryService, ) { super( undefined, new Action('fakeAction', undefined, ThemeIcon.asClassName(selectKernelIcon), true, (event) => actualAction.run(event)), - { label: false, icon: true } + { ...options, label: false, icon: true } ); this._register(_editor.onDidChangeModel(this._update, this)); this._register(_notebookKernelService.onDidAddKernel(this._update, this)); diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 102de6b52f8..8b0318487b6 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -355,7 +355,7 @@ class ActionBarRenderer extends Disposable implements ITableRenderer Date: Fri, 23 Feb 2024 11:33:58 +0100 Subject: [PATCH 0126/1175] chore - rename chatProvider-files to languageModel (#206054) chore - rename chatProvider-file to languageModel --- .../api/browser/extensionHost.contribution.ts | 2 +- ...rovider.ts => mainThreadLanguageModels.ts} | 30 ++++++++-------- .../workbench/api/common/extHost.api.impl.ts | 4 +-- .../workbench/api/common/extHost.protocol.ts | 14 ++++---- ...atProvider.ts => extHostLanguageModels.ts} | 16 ++++----- .../api/common/extHostTypeConverters.ts | 2 +- .../contrib/chat/browser/chat.contribution.ts | 4 +-- .../contrib/chat/common/chatServiceImpl.ts | 2 +- .../contrib/chat/common/chatSlashCommands.ts | 2 +- .../{chatProvider.ts => languageModels.ts} | 34 +++++++++---------- 10 files changed, 55 insertions(+), 55 deletions(-) rename src/vs/workbench/api/browser/{mainThreadChatProvider.ts => mainThreadLanguageModels.ts} (86%) rename src/vs/workbench/api/common/{extHostChatProvider.ts => extHostLanguageModels.ts} (93%) rename src/vs/workbench/contrib/chat/common/{chatProvider.ts => languageModels.ts} (62%) diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index c986c67b5e9..2b59166259d 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -17,7 +17,7 @@ import { StatusBarItemsExtensionPoint } from 'vs/workbench/api/browser/statusBar // --- mainThread participants import './mainThreadLocalization'; import './mainThreadBulkEdits'; -import './mainThreadChatProvider'; +import './mainThreadLanguageModels'; import './mainThreadChatAgents2'; import './mainThreadChatVariables'; import './mainThreadCodeInsets'; diff --git a/src/vs/workbench/api/browser/mainThreadChatProvider.ts b/src/vs/workbench/api/browser/mainThreadLanguageModels.ts similarity index 86% rename from src/vs/workbench/api/browser/mainThreadChatProvider.ts rename to src/vs/workbench/api/browser/mainThreadLanguageModels.ts index 4b7665abdb6..6e33d7da99d 100644 --- a/src/vs/workbench/api/browser/mainThreadChatProvider.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageModels.ts @@ -11,24 +11,24 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgress, Progress } from 'vs/platform/progress/common/progress'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ExtHostChatProviderShape, ExtHostContext, MainContext, MainThreadChatProviderShape } from 'vs/workbench/api/common/extHost.protocol'; -import { IChatResponseProviderMetadata, IChatResponseFragment, IChatProviderService, IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; +import { ExtHostLanguageModelsShape, ExtHostContext, MainContext, MainThreadLanguageModelsShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ILanguageModelChatMetadata, IChatResponseFragment, ILanguageModelsService, IChatMessage } from 'vs/workbench/contrib/chat/common/languageModels'; import { AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationProvider, IAuthenticationProviderCreateSessionOptions, IAuthenticationService, INTERNAL_AUTH_PROVIDER_PREFIX } from 'vs/workbench/services/authentication/common/authentication'; import { Extensions, IExtensionFeaturesManagementService, IExtensionFeaturesRegistry } from 'vs/workbench/services/extensionManagement/common/extensionFeatures'; import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -@extHostNamedCustomer(MainContext.MainThreadChatProvider) -export class MainThreadChatProvider implements MainThreadChatProviderShape { +@extHostNamedCustomer(MainContext.MainThreadLanguageModels) +export class MainThreadLanguageModels implements MainThreadLanguageModelsShape { - private readonly _proxy: ExtHostChatProviderShape; + private readonly _proxy: ExtHostLanguageModelsShape; private readonly _store = new DisposableStore(); private readonly _providerRegistrations = new DisposableMap(); private readonly _pendingProgress = new Map>(); constructor( extHostContext: IExtHostContext, - @IChatProviderService private readonly _chatProviderService: IChatProviderService, + @ILanguageModelsService private readonly _chatProviderService: ILanguageModelsService, @IExtensionFeaturesManagementService private readonly _extensionFeaturesManagementService: IExtensionFeaturesManagementService, @ILogService private readonly _logService: ILogService, @IAuthenticationService private readonly _authenticationService: IAuthenticationService, @@ -36,8 +36,8 @@ export class MainThreadChatProvider implements MainThreadChatProviderShape { ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostChatProvider); - this._proxy.$updateLanguageModels({ added: _chatProviderService.getProviders() }); - this._store.add(_chatProviderService.onDidChangeProviders(this._proxy.$updateLanguageModels, this._proxy)); + this._proxy.$updateLanguageModels({ added: _chatProviderService.getLanguageModelIds() }); + this._store.add(_chatProviderService.onDidChangeLanguageModels(this._proxy.$updateLanguageModels, this._proxy)); } dispose(): void { @@ -45,9 +45,9 @@ export class MainThreadChatProvider implements MainThreadChatProviderShape { this._store.dispose(); } - $registerProvider(handle: number, identifier: string, metadata: IChatResponseProviderMetadata): void { + $registerLanguageModelProvider(handle: number, identifier: string, metadata: ILanguageModelChatMetadata): void { const dipsosables = new DisposableStore(); - dipsosables.add(this._chatProviderService.registerChatResponseProvider(identifier, { + dipsosables.add(this._chatProviderService.registerLanguageModelChat(identifier, { metadata, provideChatResponse: async (messages, from, options, progress, token) => { const requestId = (Math.random() * 1e6) | 0; @@ -80,10 +80,10 @@ export class MainThreadChatProvider implements MainThreadChatProviderShape { this._providerRegistrations.deleteAndDispose(handle); } - async $prepareChatAccess(extension: ExtensionIdentifier, providerId: string, justification?: string): Promise { + async $prepareChatAccess(extension: ExtensionIdentifier, providerId: string, justification?: string): Promise { const activate = this._extensionService.activateByEvent(`onLanguageModelAccess:${providerId}`); - const metadata = this._chatProviderService.lookupChatResponseProvider(providerId); + const metadata = this._chatProviderService.lookupLanguageModel(providerId); if (metadata) { return metadata; @@ -91,10 +91,10 @@ export class MainThreadChatProvider implements MainThreadChatProviderShape { await Promise.race([ activate, - Event.toPromise(Event.filter(this._chatProviderService.onDidChangeProviders, e => Boolean(e.added?.includes(providerId)))) + Event.toPromise(Event.filter(this._chatProviderService.onDidChangeLanguageModels, e => Boolean(e.added?.includes(providerId)))) ]); - return this._chatProviderService.lookupChatResponseProvider(providerId); + return this._chatProviderService.lookupLanguageModel(providerId); } async $fetchResponse(extension: ExtensionIdentifier, providerId: string, requestId: number, messages: IChatMessage[], options: {}, token: CancellationToken): Promise { @@ -102,7 +102,7 @@ export class MainThreadChatProvider implements MainThreadChatProviderShape { this._logService.debug('[CHAT] extension request STARTED', extension.value, requestId); - const task = this._chatProviderService.fetchChatResponse(providerId, extension, messages, options, new Progress(value => { + const task = this._chatProviderService.makeLanguageModelChatRequest(providerId, extension, messages, options, new Progress(value => { this._proxy.$handleResponseFragment(requestId, value); }), token); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 71ec5250fb9..f89c741dd49 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -29,7 +29,7 @@ import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentica import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits'; import { ExtHostChat } from 'vs/workbench/api/common/extHostChat'; import { ExtHostChatAgents2 } from 'vs/workbench/api/common/extHostChatAgents2'; -import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider'; +import { ExtHostLanguageModels } from 'vs/workbench/api/common/extHostLanguageModels'; import { ExtHostChatVariables } from 'vs/workbench/api/common/extHostChatVariables'; import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard'; import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets'; @@ -207,7 +207,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostProfileContentHandlers = rpcProtocol.set(ExtHostContext.ExtHostProfileContentHandlers, new ExtHostProfileContentHandlers(rpcProtocol)); rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands, extHostLogService)); const extHostInteractiveEditor = rpcProtocol.set(ExtHostContext.ExtHostInlineChat, new ExtHostInteractiveEditor(rpcProtocol, extHostCommands, extHostDocuments, extHostLogService)); - const extHostChatProvider = rpcProtocol.set(ExtHostContext.ExtHostChatProvider, new ExtHostChatProvider(rpcProtocol, extHostLogService, extHostAuthentication)); + const extHostChatProvider = rpcProtocol.set(ExtHostContext.ExtHostChatProvider, new ExtHostLanguageModels(rpcProtocol, extHostLogService, extHostAuthentication)); const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostLogService, extHostCommands)); const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol)); const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol)); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 5c18ab8657a..01c49f8d261 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -52,7 +52,7 @@ import { IRevealOptions, ITreeItem, IViewBadge } from 'vs/workbench/common/views import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { IChatAgentCommand, IChatAgentMetadata, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents'; import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel'; -import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } from 'vs/workbench/contrib/chat/common/chatProvider'; +import { IChatMessage, IChatResponseFragment, ILanguageModelChatMetadata } from 'vs/workbench/contrib/chat/common/languageModels'; import { IChatDynamicRequest, IChatProgress, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection, IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue, IChatVariableData, IChatVariableResolverProgress } from 'vs/workbench/contrib/chat/common/chatVariables'; import { DebugConfigurationProviderTriggerKind, MainThreadDebugVisualization, IAdapterDescriptor, IConfig, IDebugSessionReplMode, IDebugVisualization, IDebugVisualizationContext, IDebugVisualizationTreeItem } from 'vs/workbench/contrib/debug/common/debug'; @@ -1178,16 +1178,16 @@ export interface ExtHostSpeechShape { $cancelKeywordRecognitionSession(session: number): Promise; } -export interface MainThreadChatProviderShape extends IDisposable { - $registerProvider(handle: number, identifier: string, metadata: IChatResponseProviderMetadata): void; +export interface MainThreadLanguageModelsShape extends IDisposable { + $registerLanguageModelProvider(handle: number, identifier: string, metadata: ILanguageModelChatMetadata): void; $unregisterProvider(handle: number): void; $handleProgressChunk(requestId: number, chunk: IChatResponseFragment): Promise; - $prepareChatAccess(extension: ExtensionIdentifier, providerId: string, justification?: string): Promise; + $prepareChatAccess(extension: ExtensionIdentifier, providerId: string, justification?: string): Promise; $fetchResponse(extension: ExtensionIdentifier, provider: string, requestId: number, messages: IChatMessage[], options: {}, token: CancellationToken): Promise; } -export interface ExtHostChatProviderShape { +export interface ExtHostLanguageModelsShape { $updateLanguageModels(data: { added?: string[]; removed?: string[] }): void; $updateModelAccesslist(data: { from: ExtensionIdentifier; to: ExtensionIdentifier; enabled: boolean }[]): void; $provideLanguageModelResponse(handle: number, requestId: number, from: ExtensionIdentifier, messages: IChatMessage[], options: { [name: string]: any }, token: CancellationToken): Promise; @@ -2765,7 +2765,7 @@ export interface MainThreadTestingShape { export const MainContext = { MainThreadAuthentication: createProxyIdentifier('MainThreadAuthentication'), MainThreadBulkEdits: createProxyIdentifier('MainThreadBulkEdits'), - MainThreadChatProvider: createProxyIdentifier('MainThreadChatProvider'), + MainThreadLanguageModels: createProxyIdentifier('MainThreadLanguageModels'), MainThreadChatAgents2: createProxyIdentifier('MainThreadChatAgents2'), MainThreadChatVariables: createProxyIdentifier('MainThreadChatVariables'), MainThreadClipboard: createProxyIdentifier('MainThreadClipboard'), @@ -2890,7 +2890,7 @@ export const ExtHostContext = { ExtHostChat: createProxyIdentifier('ExtHostChat'), ExtHostChatAgents2: createProxyIdentifier('ExtHostChatAgents'), ExtHostChatVariables: createProxyIdentifier('ExtHostChatVariables'), - ExtHostChatProvider: createProxyIdentifier('ExtHostChatProvider'), + ExtHostChatProvider: createProxyIdentifier('ExtHostChatProvider'), ExtHostSpeech: createProxyIdentifier('ExtHostSpeech'), ExtHostAiRelatedInformation: createProxyIdentifier('ExtHostAiRelatedInformation'), ExtHostAiEmbeddingVector: createProxyIdentifier('ExtHostAiEmbeddingVector'), diff --git a/src/vs/workbench/api/common/extHostChatProvider.ts b/src/vs/workbench/api/common/extHostLanguageModels.ts similarity index 93% rename from src/vs/workbench/api/common/extHostChatProvider.ts rename to src/vs/workbench/api/common/extHostLanguageModels.ts index 8ad8318f9e1..7baf1d9353d 100644 --- a/src/vs/workbench/api/common/extHostChatProvider.ts +++ b/src/vs/workbench/api/common/extHostLanguageModels.ts @@ -6,11 +6,11 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; -import { ExtHostChatProviderShape, IMainContext, MainContext, MainThreadChatProviderShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostLanguageModelsShape, IMainContext, MainContext, MainThreadLanguageModelsShape } from 'vs/workbench/api/common/extHost.protocol'; import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; import type * as vscode from 'vscode'; import { Progress } from 'vs/platform/progress/common/progress'; -import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } from 'vs/workbench/contrib/chat/common/chatProvider'; +import { IChatMessage, IChatResponseFragment, ILanguageModelChatMetadata } from 'vs/workbench/contrib/chat/common/languageModels'; import { ExtensionIdentifier, ExtensionIdentifierMap, ExtensionIdentifierSet, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { AsyncIterableSource } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; @@ -100,11 +100,11 @@ class LanguageModelRequest { } -export class ExtHostChatProvider implements ExtHostChatProviderShape { +export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { private static _idPool = 1; - private readonly _proxy: MainThreadChatProviderShape; + private readonly _proxy: MainThreadLanguageModelsShape; private readonly _onDidChangeModelAccess = new Emitter<{ from: ExtensionIdentifier; to: ExtensionIdentifier }>(); private readonly _onDidChangeProviders = new Emitter(); readonly onDidChangeProviders = this._onDidChangeProviders.event; @@ -120,7 +120,7 @@ export class ExtHostChatProvider implements ExtHostChatProviderShape { private readonly _logService: ILogService, private readonly _extHostAuthentication: ExtHostAuthentication, ) { - this._proxy = mainContext.getProxy(MainContext.MainThreadChatProvider); + this._proxy = mainContext.getProxy(MainContext.MainThreadLanguageModels); } dispose(): void { @@ -130,7 +130,7 @@ export class ExtHostChatProvider implements ExtHostChatProviderShape { registerLanguageModel(extension: IExtensionDescription, identifier: string, provider: vscode.ChatResponseProvider, metadata: vscode.ChatResponseProviderMetadata): IDisposable { - const handle = ExtHostChatProvider._idPool++; + const handle = ExtHostLanguageModels._idPool++; this._languageModels.set(handle, { extension: extension.identifier, provider }); let auth; if (metadata.auth) { @@ -139,7 +139,7 @@ export class ExtHostChatProvider implements ExtHostChatProviderShape { accountLabel: typeof metadata.auth === 'object' ? metadata.auth.label : undefined }; } - this._proxy.$registerProvider(handle, identifier, { + this._proxy.$registerLanguageModelProvider(handle, identifier, { extension: extension.identifier, model: metadata.name ?? '', auth @@ -299,7 +299,7 @@ export class ExtHostChatProvider implements ExtHostChatProviderShape { this.$updateModelAccesslist([{ from: from.identifier, to: to.identifier, enabled: true }]); } - private _isUsingAuth(from: ExtensionIdentifier, toMetadata: IChatResponseProviderMetadata): toMetadata is IChatResponseProviderMetadata & { auth: NonNullable } { + private _isUsingAuth(from: ExtensionIdentifier, toMetadata: ILanguageModelChatMetadata): toMetadata is ILanguageModelChatMetadata & { auth: NonNullable } { // If the 'to' extension uses an auth check return !!toMetadata.auth // And we're asking from a different extension diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index db879caea6b..816f0227361 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -38,7 +38,7 @@ import { getPrivateApiFor } from 'vs/workbench/api/common/extHostTestingPrivateA import { DEFAULT_EDITOR_ASSOCIATION, SaveReason } from 'vs/workbench/common/editor'; import { IViewBadge } from 'vs/workbench/common/views'; import { IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents'; -import * as chatProvider from 'vs/workbench/contrib/chat/common/chatProvider'; +import * as chatProvider from 'vs/workbench/contrib/chat/common/languageModels'; import { IChatCommandButton, IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatProgressMessage, IChatTreeData, IChatUserActionEvent } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; import { DebugTreeItemCollapsibleState, IDebugVisualizationTreeItem } from 'vs/workbench/contrib/debug/common/debug'; diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index d1cdc123a6b..0d34b49aff8 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -46,7 +46,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { ChatWelcomeMessageModel } from 'vs/workbench/contrib/chat/common/chatModel'; import { IMarkdownString, MarkdownString, isMarkdownString } from 'vs/base/common/htmlContent'; -import { ChatProviderService, IChatProviderService } from 'vs/workbench/contrib/chat/common/chatProvider'; +import { LanguageModelsService, ILanguageModelsService } from 'vs/workbench/contrib/chat/common/languageModels'; import { ChatSlashCommandService, IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; import { alertFocusChange } from 'vs/workbench/contrib/accessibility/browser/accessibilityContributions'; import { AccessibleViewAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; @@ -328,7 +328,7 @@ registerSingleton(IChatWidgetService, ChatWidgetService, InstantiationType.Delay registerSingleton(IQuickChatService, QuickChatService, InstantiationType.Delayed); registerSingleton(IChatAccessibilityService, ChatAccessibilityService, InstantiationType.Delayed); registerSingleton(IChatWidgetHistoryService, ChatWidgetHistoryService, InstantiationType.Delayed); -registerSingleton(IChatProviderService, ChatProviderService, InstantiationType.Delayed); +registerSingleton(ILanguageModelsService, LanguageModelsService, InstantiationType.Delayed); registerSingleton(IChatSlashCommandService, ChatSlashCommandService, InstantiationType.Delayed); registerSingleton(IChatAgentService, ChatAgentService, InstantiationType.Delayed); registerSingleton(IChatVariablesService, ChatVariablesService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 89081fe437d..6d89b981afc 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -24,7 +24,7 @@ import { IChatAgentRequest, IChatAgentResult, IChatAgentService } from 'vs/workb import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { ChatModel, ChatModelInitState, ChatRequestModel, ChatWelcomeMessageModel, IChatModel, IChatRequestVariableData, ISerializableChatData, ISerializableChatsData, getHistoryEntriesFromModel, updateRanges } from 'vs/workbench/contrib/chat/common/chatModel'; import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, IParsedChatRequest, getPromptText } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { ChatMessageRole, IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; +import { ChatMessageRole, IChatMessage } from 'vs/workbench/contrib/chat/common/languageModels'; import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser'; import { ChatCopyKind, IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatProvider, IChatProviderInfo, IChatSendRequestData, IChatService, IChatTransferredSessionData, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; diff --git a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts index 3d758da3113..2d43f1c0396 100644 --- a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts +++ b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts @@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgress } from 'vs/platform/progress/common/progress'; -import { IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; +import { IChatMessage } from 'vs/workbench/contrib/chat/common/languageModels'; import { IChatFollowup, IChatProgress, IChatResponseProgressFileTreeData } from 'vs/workbench/contrib/chat/common/chatService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; diff --git a/src/vs/workbench/contrib/chat/common/chatProvider.ts b/src/vs/workbench/contrib/chat/common/languageModels.ts similarity index 62% rename from src/vs/workbench/contrib/chat/common/chatProvider.ts rename to src/vs/workbench/contrib/chat/common/languageModels.ts index c393a73de98..7f93594aee3 100644 --- a/src/vs/workbench/contrib/chat/common/chatProvider.ts +++ b/src/vs/workbench/contrib/chat/common/languageModels.ts @@ -26,7 +26,7 @@ export interface IChatResponseFragment { part: string; } -export interface IChatResponseProviderMetadata { +export interface ILanguageModelChatMetadata { readonly extension: ExtensionIdentifier; readonly model: string; readonly description?: string; @@ -36,50 +36,50 @@ export interface IChatResponseProviderMetadata { }; } -export interface IChatResponseProvider { - metadata: IChatResponseProviderMetadata; +export interface ILanguageModelChat { + metadata: ILanguageModelChatMetadata; provideChatResponse(messages: IChatMessage[], from: ExtensionIdentifier, options: { [name: string]: any }, progress: IProgress, token: CancellationToken): Promise; } -export const IChatProviderService = createDecorator('chatProviderService'); +export const ILanguageModelsService = createDecorator('ILanguageModelsService'); -export interface IChatProviderService { +export interface ILanguageModelsService { readonly _serviceBrand: undefined; - onDidChangeProviders: Event<{ added?: string[]; removed?: string[] }>; + onDidChangeLanguageModels: Event<{ added?: string[]; removed?: string[] }>; - getProviders(): string[]; + getLanguageModelIds(): string[]; - lookupChatResponseProvider(identifier: string): IChatResponseProviderMetadata | undefined; + lookupLanguageModel(identifier: string): ILanguageModelChatMetadata | undefined; - registerChatResponseProvider(identifier: string, provider: IChatResponseProvider): IDisposable; + registerLanguageModelChat(identifier: string, provider: ILanguageModelChat): IDisposable; - fetchChatResponse(identifier: string, from: ExtensionIdentifier, messages: IChatMessage[], options: { [name: string]: any }, progress: IProgress, token: CancellationToken): Promise; + makeLanguageModelChatRequest(identifier: string, from: ExtensionIdentifier, messages: IChatMessage[], options: { [name: string]: any }, progress: IProgress, token: CancellationToken): Promise; } -export class ChatProviderService implements IChatProviderService { +export class LanguageModelsService implements ILanguageModelsService { readonly _serviceBrand: undefined; - private readonly _providers: Map = new Map(); + private readonly _providers: Map = new Map(); private readonly _onDidChangeProviders = new Emitter<{ added?: string[]; removed?: string[] }>(); - readonly onDidChangeProviders: Event<{ added?: string[]; removed?: string[] }> = this._onDidChangeProviders.event; + readonly onDidChangeLanguageModels: Event<{ added?: string[]; removed?: string[] }> = this._onDidChangeProviders.event; dispose() { this._onDidChangeProviders.dispose(); this._providers.clear(); } - getProviders(): string[] { + getLanguageModelIds(): string[] { return Array.from(this._providers.keys()); } - lookupChatResponseProvider(identifier: string): IChatResponseProviderMetadata | undefined { + lookupLanguageModel(identifier: string): ILanguageModelChatMetadata | undefined { return this._providers.get(identifier)?.metadata; } - registerChatResponseProvider(identifier: string, provider: IChatResponseProvider): IDisposable { + registerLanguageModelChat(identifier: string, provider: ILanguageModelChat): IDisposable { if (this._providers.has(identifier)) { throw new Error(`Chat response provider with identifier ${identifier} is already registered.`); } @@ -92,7 +92,7 @@ export class ChatProviderService implements IChatProviderService { }); } - fetchChatResponse(identifier: string, from: ExtensionIdentifier, messages: IChatMessage[], options: { [name: string]: any }, progress: IProgress, token: CancellationToken): Promise { + makeLanguageModelChatRequest(identifier: string, from: ExtensionIdentifier, messages: IChatMessage[], options: { [name: string]: any }, progress: IProgress, token: CancellationToken): Promise { const provider = this._providers.get(identifier); if (!provider) { throw new Error(`Chat response provider with identifier ${identifier} is not registered.`); From 64355fa7f44db5bb01cbcc3bc7bbc704866f1879 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 23 Feb 2024 11:55:32 +0100 Subject: [PATCH 0127/1175] Fixes #195384 (#206004) --- .../browser/widget/diffEditor/diffEditorWidget.ts | 15 +++++---------- .../diffEditor/features/revertButtonsFeature.ts | 12 ++++++++---- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts index 6b57de66397..5ad6e0543ef 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts @@ -20,10 +20,11 @@ import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/wi import { AccessibleDiffViewer, AccessibleDiffViewerModelFromEditors } from 'vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer'; import { DiffEditorDecorations } from 'vs/editor/browser/widget/diffEditor/components/diffEditorDecorations'; import { DiffEditorSash } from 'vs/editor/browser/widget/diffEditor/components/diffEditorSash'; -import { HideUnchangedRegionsFeature } from 'vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature'; import { DiffEditorViewZones } from 'vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones'; +import { HideUnchangedRegionsFeature } from 'vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature'; import { MovedBlocksLinesFeature } from 'vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature'; import { OverviewRulerFeature } from 'vs/editor/browser/widget/diffEditor/features/overviewRulerFeature'; +import { RevertButtonsFeature } from 'vs/editor/browser/widget/diffEditor/features/revertButtonsFeature'; import { CSSStyle, ObservableElementSizeObserver, applyStyle, applyViewZones, bindContextKey, readHotReloadableExport, translatePosition } from 'vs/editor/browser/widget/diffEditor/utils'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IDimension } from 'vs/editor/common/core/dimension'; @@ -31,7 +32,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, RangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { LineRangeMapping, 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'; @@ -40,11 +41,10 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; -import { DelegatingEditor } from './delegatingEditorImpl'; import { DiffEditorEditors } from './components/diffEditorEditors'; +import { DelegatingEditor } from './delegatingEditorImpl'; import { DiffEditorOptions } from './diffEditorOptions'; import { DiffEditorViewModel, DiffMapping, DiffState } from './diffEditorViewModel'; -import { RevertButtonsFeature } from 'vs/editor/browser/widget/diffEditor/features/revertButtonsFeature'; export interface IDiffCodeEditorWidgetOptions { originalEditor?: ICodeEditorWidgetOptions; @@ -477,12 +477,7 @@ export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor { }; } - revert(diff: DetailedLineRangeMapping): void { - if (diff.innerChanges) { - this.revertRangeMappings(diff.innerChanges); - return; - } - + revert(diff: LineRangeMapping): void { const model = this._diffModel.get(); if (!model || !model.isDiffUpToDate.get()) { return; } diff --git a/src/vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts index c82167c3d13..532786f2da1 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts @@ -15,7 +15,7 @@ import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditor/diffEdi import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { LineRange, LineRangeSet } from 'vs/editor/common/core/lineRange'; import { Range } from 'vs/editor/common/core/range'; -import { RangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { LineRangeMapping, RangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { GlyphMarginLane } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; @@ -62,7 +62,7 @@ export class RevertButtonsFeature extends Disposable { const btn = store.add(new RevertButton( m.lineRangeMapping.modified.startLineNumber, this._widget, - m.lineRangeMapping.innerChanges, + m.lineRangeMapping, false )); this._editors.modified.addGlyphMarginWidget(btn); @@ -122,7 +122,7 @@ export class RevertButton extends Disposable implements IGlyphMarginWidget { constructor( private readonly _lineNumber: number, private readonly _widget: DiffEditorWidget, - private readonly _diffs: RangeMapping[], + private readonly _diffs: RangeMapping[] | LineRangeMapping, private readonly _revertSelection: boolean, ) { super(); @@ -142,7 +142,11 @@ export class RevertButton extends Disposable implements IGlyphMarginWidget { })); this._register(addDisposableListener(this._domNode, EventType.CLICK, (e) => { - this._widget.revertRangeMappings(this._diffs); + if (this._diffs instanceof LineRangeMapping) { + this._widget.revert(this._diffs); + } else { + this._widget.revertRangeMappings(this._diffs); + } e.stopPropagation(); e.preventDefault(); })); From 3363acb98bf941b741e4d11e7ea4764142e6b3f2 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 23 Feb 2024 12:28:04 +0100 Subject: [PATCH 0128/1175] Fixes revealing unchanged code bug (#206065) --- .../diffEditor/features/hideUnchangedRegionsFeature.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts index f2eb90c6d2f..6767a375a3c 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts @@ -349,9 +349,13 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { didMove = didMove || Math.abs(delta) > 2; const lineDelta = Math.round(delta / editor.getOption(EditorOption.lineHeight)); const newVal = Math.max(0, Math.min(cur - lineDelta, this._unchangedRegion.getMaxVisibleLineCountBottom())); - const top = editor.getTopForLineNumber(this._unchangedRegionRange.endLineNumberExclusive); + const top = this._unchangedRegionRange.endLineNumberExclusive > editor.getModel()!.getLineCount() + ? editor.getContentHeight() + : editor.getTopForLineNumber(this._unchangedRegionRange.endLineNumberExclusive); this._unchangedRegion.visibleLineCountBottom.set(newVal, undefined); - const top2 = editor.getTopForLineNumber(this._unchangedRegionRange.endLineNumberExclusive); + const top2 = this._unchangedRegionRange.endLineNumberExclusive > editor.getModel()!.getLineCount() + ? editor.getContentHeight() + : editor.getTopForLineNumber(this._unchangedRegionRange.endLineNumberExclusive); editor.setScrollTop(editor.getScrollTop() + (top2 - top)); }); From 640131f6e32a65b82a65c390c53e868690c469cf Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 23 Feb 2024 13:28:00 +0100 Subject: [PATCH 0129/1175] bumping up the version from package json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f734771d907..1c3d3fc095f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-oss-dev", - "version": "1.87.0", + "version": "1.88.0", "distro": "b314654a31bdba8cd2b0c7548e931916d03416bf", "author": { "name": "Microsoft Corporation" From 7127151f30bdba6f87ce18bdd446e26d83b249a4 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 23 Feb 2024 13:46:26 +0100 Subject: [PATCH 0130/1175] Move hoverWidget to hoverService (#206070) --- .../{widget/hoverWidget => services/hoverService}/hover.css | 0 .../editor/browser/services/{ => hoverService}/hoverService.ts | 2 +- .../hoverWidget => services/hoverService}/hoverWidget.ts | 0 src/vs/editor/standalone/browser/standaloneServices.ts | 2 +- src/vs/workbench/workbench.common.main.ts | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) rename src/vs/editor/browser/{widget/hoverWidget => services/hoverService}/hover.css (100%) rename src/vs/editor/browser/services/{ => hoverService}/hoverService.ts (99%) rename src/vs/editor/browser/{widget/hoverWidget => services/hoverService}/hoverWidget.ts (100%) diff --git a/src/vs/editor/browser/widget/hoverWidget/hover.css b/src/vs/editor/browser/services/hoverService/hover.css similarity index 100% rename from src/vs/editor/browser/widget/hoverWidget/hover.css rename to src/vs/editor/browser/services/hoverService/hover.css diff --git a/src/vs/editor/browser/services/hoverService.ts b/src/vs/editor/browser/services/hoverService/hoverService.ts similarity index 99% rename from src/vs/editor/browser/services/hoverService.ts rename to src/vs/editor/browser/services/hoverService/hoverService.ts index 9dea5dab812..f7338ae7b52 100644 --- a/src/vs/editor/browser/services/hoverService.ts +++ b/src/vs/editor/browser/services/hoverService/hoverService.ts @@ -9,7 +9,7 @@ import { editorHoverBorder } from 'vs/platform/theme/common/colorRegistry'; import { IHoverService, IHoverOptions } from 'vs/platform/hover/browser/hover'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { HoverWidget } from 'vs/editor/browser/widget/hoverWidget/hoverWidget'; +import { HoverWidget } from 'vs/editor/browser/services/hoverService/hoverWidget'; import { IContextViewProvider, IDelegate } from 'vs/base/browser/ui/contextview/contextview'; import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { addDisposableListener, EventType, getActiveElement, isAncestorOfActiveElement, isAncestor, getWindow } from 'vs/base/browser/dom'; diff --git a/src/vs/editor/browser/widget/hoverWidget/hoverWidget.ts b/src/vs/editor/browser/services/hoverService/hoverWidget.ts similarity index 100% rename from src/vs/editor/browser/widget/hoverWidget/hoverWidget.ts rename to src/vs/editor/browser/services/hoverService/hoverWidget.ts diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 351f85537d8..c815e1b3d13 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -10,7 +10,7 @@ import 'vs/platform/undoRedo/common/undoRedoService'; import 'vs/editor/common/services/languageFeatureDebounce'; import 'vs/editor/common/services/semanticTokensStylingService'; import 'vs/editor/common/services/languageFeaturesService'; -import 'vs/editor/browser/services/hoverService'; +import 'vs/editor/browser/services/hoverService/hoverService'; import * as strings from 'vs/base/common/strings'; import * as dom from 'vs/base/browser/dom'; diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index a7d0a54f6dd..61028e388dd 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -104,7 +104,7 @@ import 'vs/workbench/services/views/browser/viewsService'; import 'vs/workbench/services/quickinput/browser/quickInputService'; import 'vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService'; import 'vs/workbench/services/authentication/browser/authenticationService'; -import 'vs/editor/browser/services/hoverService'; +import 'vs/editor/browser/services/hoverService/hoverService'; import 'vs/workbench/services/assignment/common/assignmentService'; import 'vs/workbench/services/outline/browser/outlineService'; import 'vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl'; From 22508a50008764da21bc5704712c3473d6a566f6 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 23 Feb 2024 14:30:27 +0100 Subject: [PATCH 0131/1175] Moves testDiffProviderFactoryService (#206078) --- .../{ => test}/browser/diff/testDiffProviderFactoryService.ts | 2 +- .../inlineChat/test/browser/inlineChatController.test.ts | 2 +- .../contrib/inlineChat/test/browser/inlineChatSession.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/vs/editor/{ => test}/browser/diff/testDiffProviderFactoryService.ts (95%) diff --git a/src/vs/editor/browser/diff/testDiffProviderFactoryService.ts b/src/vs/editor/test/browser/diff/testDiffProviderFactoryService.ts similarity index 95% rename from src/vs/editor/browser/diff/testDiffProviderFactoryService.ts rename to src/vs/editor/test/browser/diff/testDiffProviderFactoryService.ts index 08ed249b8b9..275c4995988 100644 --- a/src/vs/editor/browser/diff/testDiffProviderFactoryService.ts +++ b/src/vs/editor/test/browser/diff/testDiffProviderFactoryService.ts @@ -18,7 +18,7 @@ export class TestDiffProviderFactoryService implements IDiffProviderFactoryServi } } -export class SyncDocumentDiffProvider implements IDocumentDiffProvider { +class SyncDocumentDiffProvider implements IDocumentDiffProvider { computeDiff(original: ITextModel, modified: ITextModel, options: IDocumentDiffProviderOptions, cancellationToken: CancellationToken): Promise { const result = linesDiffComputers.getDefault().computeDiff(original.getLinesContent(), modified.getLinesContent(), options); return Promise.resolve({ diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index 932cb5056b1..f998c0969d7 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -11,7 +11,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { mock } from 'vs/base/test/common/mock'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; -import { TestDiffProviderFactoryService } from 'vs/editor/browser/diff/testDiffProviderFactoryService'; +import { TestDiffProviderFactoryService } from 'vs/editor/test/browser/diff/testDiffProviderFactoryService'; import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffProviderFactoryService } from 'vs/editor/browser/widget/diffEditor/diffProviderFactoryService'; import { Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts index 53b0cb87519..16556bef773 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { mock } from 'vs/base/test/common/mock'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; -import { TestDiffProviderFactoryService } from 'vs/editor/browser/diff/testDiffProviderFactoryService'; +import { TestDiffProviderFactoryService } from 'vs/editor/test/browser/diff/testDiffProviderFactoryService'; import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffProviderFactoryService } from 'vs/editor/browser/widget/diffEditor/diffProviderFactoryService'; import { Range } from 'vs/editor/common/core/range'; From 1c16af45f1e08ad444b32ea044e49a129854683a Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 23 Feb 2024 14:34:07 +0100 Subject: [PATCH 0132/1175] multiDiffEditorWidget -> multiDiffEditor (#206075) --- .../{multiDiffEditorWidget => multiDiffEditor}/colors.ts | 0 .../diffEditorItemTemplate.ts | 4 ++-- .../{multiDiffEditorWidget => multiDiffEditor}/model.ts | 0 .../multiDiffEditorViewModel.ts | 2 +- .../multiDiffEditorWidget.ts | 8 ++++---- .../multiDiffEditorWidgetImpl.ts | 4 ++-- .../objectPool.ts | 0 .../{multiDiffEditorWidget => multiDiffEditor}/style.css | 0 .../{multiDiffEditorWidget => multiDiffEditor}/utils.ts | 0 .../workbenchUIElementFactory.ts | 0 src/vs/editor/standalone/browser/standaloneEditor.ts | 2 +- .../contrib/bulkEdit/browser/preview/bulkEditPane.ts | 2 +- .../contrib/multiDiffEditor/browser/multiDiffEditor.ts | 8 ++++---- .../multiDiffEditor/browser/multiDiffEditorInput.ts | 4 ++-- 14 files changed, 17 insertions(+), 17 deletions(-) rename src/vs/editor/browser/widget/{multiDiffEditorWidget => multiDiffEditor}/colors.ts (100%) rename src/vs/editor/browser/widget/{multiDiffEditorWidget => multiDiffEditor}/diffEditorItemTemplate.ts (99%) rename src/vs/editor/browser/widget/{multiDiffEditorWidget => multiDiffEditor}/model.ts (100%) rename src/vs/editor/browser/widget/{multiDiffEditorWidget => multiDiffEditor}/multiDiffEditorViewModel.ts (98%) rename src/vs/editor/browser/widget/{multiDiffEditorWidget => multiDiffEditor}/multiDiffEditorWidget.ts (95%) rename src/vs/editor/browser/widget/{multiDiffEditorWidget => multiDiffEditor}/multiDiffEditorWidgetImpl.ts (99%) rename src/vs/editor/browser/widget/{multiDiffEditorWidget => multiDiffEditor}/objectPool.ts (100%) rename src/vs/editor/browser/widget/{multiDiffEditorWidget => multiDiffEditor}/style.css (100%) rename src/vs/editor/browser/widget/{multiDiffEditorWidget => multiDiffEditor}/utils.ts (100%) rename src/vs/editor/browser/widget/{multiDiffEditorWidget => multiDiffEditor}/workbenchUIElementFactory.ts (100%) diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/colors.ts b/src/vs/editor/browser/widget/multiDiffEditor/colors.ts similarity index 100% rename from src/vs/editor/browser/widget/multiDiffEditorWidget/colors.ts rename to src/vs/editor/browser/widget/multiDiffEditor/colors.ts diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts b/src/vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts similarity index 99% rename from src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts rename to src/vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts index f5433ba99d7..84cffd7e94e 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts @@ -10,8 +10,8 @@ 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 { DocumentDiffItemViewModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel'; -import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory'; +import { DocumentDiffItemViewModel } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel'; +import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditor/workbenchUIElementFactory'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/model.ts b/src/vs/editor/browser/widget/multiDiffEditor/model.ts similarity index 100% rename from src/vs/editor/browser/widget/multiDiffEditorWidget/model.ts rename to src/vs/editor/browser/widget/multiDiffEditor/model.ts diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts similarity index 98% rename from src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts rename to src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts index 3a26a82c526..5a225008e03 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts @@ -8,7 +8,7 @@ import { observableFromEvent, observableValue, transaction } from 'vs/base/commo import { mapObservableArrayCached } from 'vs/base/common/observableInternal/utils'; import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditor/diffEditorOptions'; import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditor/diffEditorViewModel'; -import { IDocumentDiffItem, IMultiDiffEditorModel, LazyPromise } from 'vs/editor/browser/widget/multiDiffEditorWidget/model'; +import { IDocumentDiffItem, IMultiDiffEditorModel, LazyPromise } from 'vs/editor/browser/widget/multiDiffEditor/model'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Selection } from 'vs/editor/common/core/selection'; import { IDiffEditorViewModel } from 'vs/editor/common/editorCommon'; diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget.ts b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts similarity index 95% rename from src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget.ts rename to src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts index 6867bb84ac7..af73b75af6a 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts @@ -7,13 +7,13 @@ import { Dimension } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; 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 { IMultiDiffEditorViewState, IMultiDiffResource, MultiDiffEditorWidgetImpl } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl'; +import { IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditor/model'; +import { IMultiDiffEditorViewState, IMultiDiffResource, MultiDiffEditorWidgetImpl } from 'vs/editor/browser/widget/multiDiffEditor/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 { DiffEditorItemTemplate } from 'vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate'; +import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditor/workbenchUIElementFactory'; import { Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { IDiffEditor } from 'vs/editor/common/editorCommon'; diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts similarity index 99% rename from src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts rename to src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts index 36a568f2524..4e23c4e6cb6 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts @@ -14,8 +14,8 @@ import { URI } from 'vs/base/common/uri'; import 'vs/css!./style'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ObservableElementSizeObserver } from 'vs/editor/browser/widget/diffEditor/utils'; -import { RevealOptions } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget'; -import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory'; +import { RevealOptions } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget'; +import { IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditor/workbenchUIElementFactory'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { IRange } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/objectPool.ts b/src/vs/editor/browser/widget/multiDiffEditor/objectPool.ts similarity index 100% rename from src/vs/editor/browser/widget/multiDiffEditorWidget/objectPool.ts rename to src/vs/editor/browser/widget/multiDiffEditor/objectPool.ts diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/style.css b/src/vs/editor/browser/widget/multiDiffEditor/style.css similarity index 100% rename from src/vs/editor/browser/widget/multiDiffEditorWidget/style.css rename to src/vs/editor/browser/widget/multiDiffEditor/style.css diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/utils.ts b/src/vs/editor/browser/widget/multiDiffEditor/utils.ts similarity index 100% rename from src/vs/editor/browser/widget/multiDiffEditorWidget/utils.ts rename to src/vs/editor/browser/widget/multiDiffEditor/utils.ts diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory.ts b/src/vs/editor/browser/widget/multiDiffEditor/workbenchUIElementFactory.ts similarity index 100% rename from src/vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory.ts rename to src/vs/editor/browser/widget/multiDiffEditor/workbenchUIElementFactory.ts diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 059a4928862..0fa038765af 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -39,7 +39,7 @@ import { ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IMarker, IMarkerData, IMarkerService } from 'vs/platform/markers/common/markers'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { MultiDiffEditorWidget } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget'; +import { MultiDiffEditorWidget } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget'; /** * Create a new editor under `domElement`. diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index 278de48c47c..ba85dee93b2 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -37,7 +37,7 @@ import { ButtonBar } from 'vs/base/browser/ui/button/button'; import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles'; import { Mutable } from 'vs/base/common/types'; import { IResourceDiffEditorInput } from 'vs/workbench/common/editor'; -import { IMultiDiffEditorOptions } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl'; +import { IMultiDiffEditorOptions } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl'; import { IRange } from 'vs/editor/common/core/range'; const enum State { diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts index c1491431828..6f66dbec274 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts @@ -5,8 +5,8 @@ import * as DOM from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { MultiDiffEditorWidget } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget'; -import { IResourceLabel, IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory'; +import { MultiDiffEditorWidget } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget'; +import { IResourceLabel, IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditor/workbenchUIElementFactory'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; @@ -22,8 +22,8 @@ import { MultiDiffEditorInput } from 'vs/workbench/contrib/multiDiffEditor/brows import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { URI } from 'vs/base/common/uri'; -import { MultiDiffEditorViewModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel'; -import { IMultiDiffEditorOptions, IMultiDiffEditorViewState } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl'; +import { MultiDiffEditorViewModel } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel'; +import { IMultiDiffEditorOptions, IMultiDiffEditorViewState } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffEditor } from 'vs/editor/common/editorCommon'; import { Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts index 058373fd465..4344d015873 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts @@ -16,8 +16,8 @@ import { constObservable, mapObservableArrayCached } from 'vs/base/common/observ import { ThemeIcon } from 'vs/base/common/themables'; import { isDefined, isObject } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; -import { ConstLazyPromise, IDocumentDiffItem, IMultiDiffEditorModel, LazyPromise } from 'vs/editor/browser/widget/multiDiffEditorWidget/model'; -import { MultiDiffEditorViewModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel'; +import { ConstLazyPromise, IDocumentDiffItem, IMultiDiffEditorModel, LazyPromise } from 'vs/editor/browser/widget/multiDiffEditor/model'; +import { MultiDiffEditorViewModel } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; From af1f874650e7d7bde5513d091619f1202aed8e71 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 23 Feb 2024 14:34:41 +0100 Subject: [PATCH 0133/1175] add "Chat" as Category --- src/vs/platform/extensions/common/extensions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 413c1db06f1..62977ee07e3 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -255,6 +255,7 @@ export const EXTENSION_CATEGORIES = [ 'Testing', 'Themes', 'Visualization', + 'Chat', 'Other', ]; From 0bd70d48ad8b3e2fb1922aa54f87c786ff2b4bd8 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 23 Feb 2024 14:35:45 +0100 Subject: [PATCH 0134/1175] code editor move (#206074) --- .../codeEditorContributions.ts | 0 .../{ => codeEditor}/codeEditorWidget.ts | 95 +++++++++---------- .../widget/{media => codeEditor}/editor.css | 0 .../embeddedCodeEditorWidget.ts | 54 +---------- .../components/diffEditorEditors.ts | 2 +- .../diffEditorViewZones.ts | 2 +- .../inlineDiffDeletedCodeMargin.ts | 2 +- .../widget/diffEditor/delegatingEditorImpl.ts | 2 +- .../widget/diffEditor/diffEditorWidget.ts | 2 +- .../diffEditor/embeddedDiffEditorWidget.ts | 55 +++++++++++ .../features/overviewRulerFeature.ts | 2 +- src/vs/editor/contrib/dnd/browser/dnd.ts | 2 +- .../gotoSymbol/browser/goToCommands.ts | 2 +- .../browser/peek/referencesWidget.ts | 2 +- .../contrib/peekView/browser/peekView.ts | 2 +- .../browser/stickyScrollWidget.ts | 2 +- .../contrib/suggest/browser/suggestWidget.ts | 2 +- src/vs/editor/editor.all.ts | 2 +- .../browser/standaloneCodeEditor.ts | 2 +- src/vs/editor/test/browser/testCodeEditor.ts | 2 +- src/vs/workbench/browser/codeeditor.ts | 2 +- .../browser/parts/editor/textCodeEditor.ts | 2 +- .../accessibility/browser/accessibleView.ts | 2 +- .../browser/callHierarchyPeek.ts | 2 +- .../contrib/chat/browser/chatInputPart.ts | 2 +- .../contrib/chat/browser/codeBlockPart.ts | 2 +- .../codeEditor/browser/diffEditorHelper.ts | 2 +- .../codeEditor/browser/simpleEditorOptions.ts | 2 +- .../suggestEnabledInput.ts | 2 +- .../comments/browser/commentsController.ts | 2 +- .../comments/browser/simpleCommentEditor.ts | 2 +- .../contrib/debug/browser/breakpointWidget.ts | 2 +- .../workbench/contrib/debug/browser/repl.ts | 2 +- .../contrib/files/browser/fileCommands.ts | 2 +- .../inlineChat/browser/inlineChatActions.ts | 3 +- .../browser/inlineChatLivePreviewWidget.ts | 3 +- .../inlineChat/browser/inlineChatWidget.ts | 5 +- .../browser/interactive.contribution.ts | 2 +- .../interactive/browser/interactiveEditor.ts | 2 +- .../contrib/mergeEditor/browser/utils.ts | 2 +- .../mergeEditor/browser/view/editorGutter.ts | 2 +- .../browser/view/editors/codeEditorView.ts | 2 +- .../browser/view/scrollSynchronizer.ts | 2 +- .../controller/chat/notebookChatController.ts | 2 +- .../notebook/browser/diff/diffComponents.ts | 2 +- .../browser/diff/notebookDiffEditorBrowser.ts | 2 +- .../notebook/browser/diff/notebookDiffList.ts | 2 +- .../browser/view/cellParts/markupCell.ts | 2 +- .../browser/view/renderers/cellRenderer.ts | 2 +- .../contrib/scm/browser/dirtydiffDecorator.ts | 2 +- .../contrib/scm/browser/scmViewPane.ts | 2 +- .../contrib/search/browser/searchView.ts | 2 +- .../searchEditor/browser/searchEditor.ts | 2 +- .../testing/browser/testingOutputPeek.ts | 5 +- .../browser/typeHierarchyPeek.ts | 2 +- .../browser/walkThroughPart.ts | 2 +- .../dialogs/browser/fileDialogService.ts | 2 +- 57 files changed, 164 insertions(+), 152 deletions(-) rename src/vs/editor/browser/widget/{ => codeEditor}/codeEditorContributions.ts (100%) rename src/vs/editor/browser/widget/{ => codeEditor}/codeEditorWidget.ts (99%) rename src/vs/editor/browser/widget/{media => codeEditor}/editor.css (100%) rename src/vs/editor/browser/widget/{ => codeEditor}/embeddedCodeEditorWidget.ts (59%) create mode 100644 src/vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget.ts diff --git a/src/vs/editor/browser/widget/codeEditorContributions.ts b/src/vs/editor/browser/widget/codeEditor/codeEditorContributions.ts similarity index 100% rename from src/vs/editor/browser/widget/codeEditorContributions.ts rename to src/vs/editor/browser/widget/codeEditor/codeEditorContributions.ts diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts similarity index 99% rename from src/vs/editor/browser/widget/codeEditorWidget.ts rename to src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts index 5466f913d24..39cba20b268 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/editor/browser/services/markerDecorations'; - import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; @@ -14,7 +13,7 @@ import { Emitter, EmitterOptions, Event, EventDeliveryQueue, createEventDelivery import { hash } from 'vs/base/common/hash'; import { Disposable, DisposableStore, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; -import 'vs/css!./media/editor'; +import 'vs/css!./editor'; import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; import { EditorConfiguration, IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { TabFocus } from 'vs/editor/browser/config/tabFocus'; @@ -25,7 +24,7 @@ import { IContentWidgetData, IGlyphMarginWidgetData, IOverlayWidgetData, View } import { DOMLineBreaksComputerFactory } from 'vs/editor/browser/view/domLineBreaksComputer'; import { ICommandDelegate } from 'vs/editor/browser/view/viewController'; import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents'; -import { CodeEditorContributions } from 'vs/editor/browser/widget/codeEditorContributions'; +import { CodeEditorContributions } from 'vs/editor/browser/widget/codeEditor/codeEditorContributions'; import { IEditorConfiguration } from 'vs/editor/common/config/editorConfiguration'; import { ConfigurationChangedEvent, EditorLayoutInfo, EditorOption, FindComputedEditorOptionValueById, IComputedEditorOptions, IEditorOptions, filterValidationDecorations } from 'vs/editor/common/config/editorOptions'; import { CursorColumns } from 'vs/editor/common/core/cursorColumns'; @@ -61,51 +60,6 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { editorErrorForeground, editorHintForeground, editorInfoForeground, editorWarningForeground } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -let EDITOR_ID = 0; - -export interface ICodeEditorWidgetOptions { - /** - * Is this a simple widget (not a real code editor)? - * Defaults to false. - */ - isSimpleWidget?: boolean; - - /** - * Contributions to instantiate. - * When provided, only the contributions included will be instantiated. - * To include the defaults, those must be provided as well via [...EditorExtensionsRegistry.getEditorContributions()] - * Defaults to EditorExtensionsRegistry.getEditorContributions(). - */ - contributions?: IEditorContributionDescription[]; - - /** - * Telemetry data associated with this CodeEditorWidget. - * Defaults to null. - */ - telemetryData?: object; -} - -class ModelData { - constructor( - public readonly model: ITextModel, - public readonly viewModel: ViewModel, - public readonly view: View, - public readonly hasRealView: boolean, - public readonly listenersToRemove: IDisposable[], - public readonly attachedView: IAttachedView, - ) { - } - - public dispose(): void { - dispose(this.listenersToRemove); - this.model.onBeforeDetached(this.attachedView); - if (this.hasRealView) { - this.view.dispose(); - } - this.viewModel.dispose(); - } -} - export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeEditor { private static readonly dropIntoEditorDecorationOptions = ModelDecorationOptions.register({ @@ -1932,6 +1886,51 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } } +let EDITOR_ID = 0; + +export interface ICodeEditorWidgetOptions { + /** + * Is this a simple widget (not a real code editor)? + * Defaults to false. + */ + isSimpleWidget?: boolean; + + /** + * Contributions to instantiate. + * When provided, only the contributions included will be instantiated. + * To include the defaults, those must be provided as well via [...EditorExtensionsRegistry.getEditorContributions()] + * Defaults to EditorExtensionsRegistry.getEditorContributions(). + */ + contributions?: IEditorContributionDescription[]; + + /** + * Telemetry data associated with this CodeEditorWidget. + * Defaults to null. + */ + telemetryData?: object; +} + +class ModelData { + constructor( + public readonly model: ITextModel, + public readonly viewModel: ViewModel, + public readonly view: View, + public readonly hasRealView: boolean, + public readonly listenersToRemove: IDisposable[], + public readonly attachedView: IAttachedView, + ) { + } + + public dispose(): void { + dispose(this.listenersToRemove); + this.model.onBeforeDetached(this.attachedView); + if (this.hasRealView) { + this.view.dispose(); + } + this.viewModel.dispose(); + } +} + const enum BooleanEventValue { NotSet, False, diff --git a/src/vs/editor/browser/widget/media/editor.css b/src/vs/editor/browser/widget/codeEditor/editor.css similarity index 100% rename from src/vs/editor/browser/widget/media/editor.css rename to src/vs/editor/browser/widget/codeEditor/editor.css diff --git a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts similarity index 59% rename from src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts rename to src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts index 4a5dffa5347..9fb3a8e69c2 100644 --- a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts @@ -4,24 +4,20 @@ *--------------------------------------------------------------------------------------------*/ import * as objects from 'vs/base/common/objects'; -import { ICodeEditor, IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffEditorWidget, IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; -import { ConfigurationChangedEvent, IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; +import { ConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { IAccessibilitySignalService } from 'vs/platform/accessibilitySignal/browser/accessibilitySignalService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { IThemeService } from 'vs/platform/theme/common/themeService'; export class EmbeddedCodeEditorWidget extends CodeEditorWidget { - private readonly _parentEditor: ICodeEditor; private readonly _overwriteOptions: IEditorOptions; @@ -38,7 +34,7 @@ export class EmbeddedCodeEditorWidget extends CodeEditorWidget { @INotificationService notificationService: INotificationService, @IAccessibilityService accessibilityService: IAccessibilityService, @ILanguageConfigurationService languageConfigurationService: ILanguageConfigurationService, - @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, + @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService ) { super(domElement, { ...parentEditor.getRawOptions(), overflowWidgetsDomNode: parentEditor.getOverflowWidgetsDomNode() }, codeEditorWidgetOptions, instantiationService, codeEditorService, commandService, contextKeyService, themeService, notificationService, accessibilityService, languageConfigurationService, languageFeaturesService); @@ -65,45 +61,3 @@ export class EmbeddedCodeEditorWidget extends CodeEditorWidget { super.updateOptions(this._overwriteOptions); } } - -export class EmbeddedDiffEditorWidget extends DiffEditorWidget { - - private readonly _parentEditor: ICodeEditor; - private readonly _overwriteOptions: IDiffEditorOptions; - - constructor( - domElement: HTMLElement, - options: Readonly, - codeEditorWidgetOptions: IDiffCodeEditorWidgetOptions, - parentEditor: ICodeEditor, - @IContextKeyService contextKeyService: IContextKeyService, - @IInstantiationService instantiationService: IInstantiationService, - @ICodeEditorService codeEditorService: ICodeEditorService, - @IAccessibilitySignalService accessibilitySignalService: IAccessibilitySignalService, - @IEditorProgressService editorProgressService: IEditorProgressService, - ) { - super(domElement, parentEditor.getRawOptions(), codeEditorWidgetOptions, contextKeyService, instantiationService, codeEditorService, accessibilitySignalService, editorProgressService); - - this._parentEditor = parentEditor; - this._overwriteOptions = options; - - // Overwrite parent's options - super.updateOptions(this._overwriteOptions); - - this._register(parentEditor.onDidChangeConfiguration(e => this._onParentConfigurationChanged(e))); - } - - getParentEditor(): ICodeEditor { - return this._parentEditor; - } - - private _onParentConfigurationChanged(e: ConfigurationChangedEvent): void { - super.updateOptions(this._parentEditor.getRawOptions()); - super.updateOptions(this._overwriteOptions); - } - - override updateOptions(newOptions: IEditorOptions): void { - objects.mixin(this._overwriteOptions, newOptions, true); - super.updateOptions(this._overwriteOptions); - } -} diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts index 79d6509b225..1818ead661f 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts @@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IObservable, IReader, autorunHandleChanges, derivedOpts, observableFromEvent } from 'vs/base/common/observable'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; -import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { OverviewRulerFeature } from 'vs/editor/browser/widget/diffEditor/features/overviewRulerFeature'; import { EditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts index ac5b8886ac3..d6bcac91567 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts @@ -12,7 +12,7 @@ import { IObservable, autorun, derived, derivedWithStore, observableFromEvent, o import { ThemeIcon } from 'vs/base/common/themables'; import { assertIsDefined } from 'vs/base/common/types'; import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { diffDeleteDecoration, diffRemoveIcon } from 'vs/editor/browser/widget/diffEditor/registrations.contribution'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditor/components/diffEditorEditors'; import { DiffEditorViewModel, DiffMapping } from 'vs/editor/browser/widget/diffEditor/diffEditorViewModel'; diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/inlineDiffDeletedCodeMargin.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/inlineDiffDeletedCodeMargin.ts index ced12b99f7b..f6d88c3afe7 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/inlineDiffDeletedCodeMargin.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/inlineDiffDeletedCodeMargin.ts @@ -10,7 +10,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { isIOS } from 'vs/base/common/platform'; import { ThemeIcon } from 'vs/base/common/themables'; import { IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; diff --git a/src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts b/src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts index ff3b66c5e09..96a85f35eef 100644 --- a/src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts +++ b/src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts @@ -5,7 +5,7 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IDimension } from 'vs/editor/common/core/dimension'; import { IPosition, Position } from 'vs/editor/common/core/position'; diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts index 5ad6e0543ef..7499fa8e0bc 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts @@ -16,7 +16,7 @@ import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions } from 'vs/edi import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll'; -import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { AccessibleDiffViewer, AccessibleDiffViewerModelFromEditors } from 'vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer'; import { DiffEditorDecorations } from 'vs/editor/browser/widget/diffEditor/components/diffEditorDecorations'; import { DiffEditorSash } from 'vs/editor/browser/widget/diffEditor/components/diffEditorSash'; diff --git a/src/vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget.ts new file mode 100644 index 00000000000..9156c17ead3 --- /dev/null +++ b/src/vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as objects from 'vs/base/common/objects'; +import { ICodeEditor, IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { DiffEditorWidget, IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; +import { ConfigurationChangedEvent, IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IAccessibilitySignalService } from 'vs/platform/accessibilitySignal/browser/accessibilitySignalService'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IEditorProgressService } from 'vs/platform/progress/common/progress'; +export class EmbeddedDiffEditorWidget extends DiffEditorWidget { + + private readonly _parentEditor: ICodeEditor; + private readonly _overwriteOptions: IDiffEditorOptions; + + constructor( + domElement: HTMLElement, + options: Readonly, + codeEditorWidgetOptions: IDiffCodeEditorWidgetOptions, + parentEditor: ICodeEditor, + @IContextKeyService contextKeyService: IContextKeyService, + @IInstantiationService instantiationService: IInstantiationService, + @ICodeEditorService codeEditorService: ICodeEditorService, + @IAccessibilitySignalService accessibilitySignalService: IAccessibilitySignalService, + @IEditorProgressService editorProgressService: IEditorProgressService + ) { + super(domElement, parentEditor.getRawOptions(), codeEditorWidgetOptions, contextKeyService, instantiationService, codeEditorService, accessibilitySignalService, editorProgressService); + + this._parentEditor = parentEditor; + this._overwriteOptions = options; + + // Overwrite parent's options + super.updateOptions(this._overwriteOptions); + + this._register(parentEditor.onDidChangeConfiguration(e => this._onParentConfigurationChanged(e))); + } + + getParentEditor(): ICodeEditor { + return this._parentEditor; + } + + private _onParentConfigurationChanged(e: ConfigurationChangedEvent): void { + super.updateOptions(this._parentEditor.getRawOptions()); + super.updateOptions(this._overwriteOptions); + } + + override updateOptions(newOptions: IEditorOptions): void { + objects.mixin(this._overwriteOptions, newOptions, true); + super.updateOptions(this._overwriteOptions); + } +} diff --git a/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts index bd1997491e1..8141cd9452c 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts @@ -10,7 +10,7 @@ import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { Color } from 'vs/base/common/color'; import { Disposable } from 'vs/base/common/lifecycle'; import { IObservable, autorun, autorunWithStore, derived, observableFromEvent, observableSignalFromEvent } from 'vs/base/common/observable'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditor/components/diffEditorEditors'; import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditor/diffEditorViewModel'; import { appendRemoveOnDispose } from 'vs/editor/browser/widget/diffEditor/utils'; diff --git a/src/vs/editor/contrib/dnd/browser/dnd.ts b/src/vs/editor/contrib/dnd/browser/dnd.ts index d4576e66c8d..9e355410d48 100644 --- a/src/vs/editor/contrib/dnd/browser/dnd.ts +++ b/src/vs/editor/contrib/dnd/browser/dnd.ts @@ -11,7 +11,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import 'vs/css!./dnd'; import { ICodeEditor, IEditorMouseEvent, IMouseTarget, IPartialEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { CursorChangeReason } from 'vs/editor/common/cursorEvents'; import { Position } from 'vs/editor/common/core/position'; diff --git a/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts index b968f248904..aa6b7865439 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts @@ -13,7 +13,7 @@ import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/edit import { IActiveCodeEditor, ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction2, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { EditorOption, GoToLocationValues } from 'vs/editor/common/config/editorOptions'; import * as corePosition from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts index d23e38b81ab..b80e75d47ec 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts @@ -16,7 +16,7 @@ import { Schemas } from 'vs/base/common/network'; import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; import 'vs/css!./referencesWidget'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; diff --git a/src/vs/editor/contrib/peekView/browser/peekView.ts b/src/vs/editor/contrib/peekView/browser/peekView.ts index 5c0882b8db4..a0f2dfd914e 100644 --- a/src/vs/editor/contrib/peekView/browser/peekView.ts +++ b/src/vs/editor/contrib/peekView/browser/peekView.ts @@ -17,7 +17,7 @@ import 'vs/css!./media/peekViewWidget'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IOptions, IStyles, ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget'; diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 06fb2ce428f..bdcaafb4891 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -11,7 +11,7 @@ import { ThemeIcon } from 'vs/base/common/themables'; import 'vs/css!./stickyScroll'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { getColumnOfNodeOffset } from 'vs/editor/browser/viewParts/lines/viewLine'; -import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { EditorLayoutInfo, EditorOption, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { StringBuilder } from 'vs/editor/common/core/stringBuilder'; diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index fc41e265fc4..2eeb94d99b6 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -16,7 +16,7 @@ import { clamp } from 'vs/base/common/numbers'; import * as strings from 'vs/base/common/strings'; import 'vs/css!./media/suggest'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; -import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IPosition } from 'vs/editor/common/core/position'; import { SuggestWidgetStatus } from 'vs/editor/contrib/suggest/browser/suggestWidgetStatus'; diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index 227595d6224..37ed9f9f4fd 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/editor/browser/coreCommands'; -import 'vs/editor/browser/widget/codeEditorWidget'; +import 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import 'vs/editor/browser/widget/diffEditor/diffEditor.contribution'; import 'vs/editor/contrib/anchorSelect/browser/anchorSelect'; import 'vs/editor/contrib/bracketMatching/browser/bracketMatching'; diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 479bb75745c..3e903d5bab9 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -7,7 +7,7 @@ import * as aria from 'vs/base/browser/ui/aria/aria'; import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { InternalEditorAction } from 'vs/editor/common/editorAction'; import { IModelChangedEvent } from 'vs/editor/common/editorCommon'; diff --git a/src/vs/editor/test/browser/testCodeEditor.ts b/src/vs/editor/test/browser/testCodeEditor.ts index b29e05c8053..c0a4b9d1cf3 100644 --- a/src/vs/editor/test/browser/testCodeEditor.ts +++ b/src/vs/editor/test/browser/testCodeEditor.ts @@ -9,7 +9,7 @@ import { EditorConfiguration } from 'vs/editor/browser/config/editorConfiguratio import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { View } from 'vs/editor/browser/view'; -import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import * as editorOptions from 'vs/editor/common/config/editorOptions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ILanguageService } from 'vs/editor/common/languages/language'; diff --git a/src/vs/workbench/browser/codeeditor.ts b/src/vs/workbench/browser/codeeditor.ts index 33358b10156..dcce3224ba9 100644 --- a/src/vs/workbench/browser/codeeditor.ts +++ b/src/vs/workbench/browser/codeeditor.ts @@ -9,7 +9,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference, isCodeEditor, isCompositeEditor } from 'vs/editor/browser/editorBrowser'; -import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IRange } from 'vs/editor/common/core/range'; import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/cursorEvents'; diff --git a/src/vs/workbench/browser/parts/editor/textCodeEditor.ts b/src/vs/workbench/browser/parts/editor/textCodeEditor.ts index 2efc763e041..2edad61d83c 100644 --- a/src/vs/workbench/browser/parts/editor/textCodeEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textCodeEditor.ts @@ -12,7 +12,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { isEqual } from 'vs/base/common/resources'; import { IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { IEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { AbstractTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 5f3a2df9d06..cfbfa70a242 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -18,7 +18,7 @@ import { ThemeIcon } from 'vs/base/common/themables'; import { URI } from 'vs/base/common/uri'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; -import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/model'; diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts index 6eb9f63cba9..3dada0c5698 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts @@ -19,7 +19,7 @@ import { SplitView, Orientation, Sizing } from 'vs/base/browser/ui/splitview/spl import { Dimension, isKeyboardEvent } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index f4692878598..e149d81cadb 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -14,7 +14,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/model'; import { HoverController } from 'vs/editor/contrib/hover/browser/hover'; diff --git a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts index 17b169bc4f1..8669cadcce7 100644 --- a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts +++ b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts @@ -15,7 +15,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { EDITOR_FONT_DEFAULTS, EditorOption, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts index 204ebdd1358..a19d88cca33 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts @@ -10,7 +10,7 @@ import { registerDiffEditorContribution } from 'vs/editor/browser/editorExtensio import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { AccessibleDiffViewerNext, AccessibleDiffViewerPrev } from 'vs/editor/browser/widget/diffEditor/diffEditor.contribution'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; -import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget'; import { IDiffEditorContribution } from 'vs/editor/common/editorCommon'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/simpleEditorOptions.ts b/src/vs/workbench/contrib/codeEditor/browser/simpleEditorOptions.ts index 6acfd4495b7..b4f68265026 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/simpleEditorOptions.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/simpleEditorOptions.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { ContextMenuController } from 'vs/editor/contrib/contextmenu/browser/contextmenu'; import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts index 32ac5cb4dec..60e13398790 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts @@ -11,7 +11,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { mixin } from 'vs/base/common/objects'; import { isMacintosh } from 'vs/base/common/platform'; import { URI as uri } from 'vs/base/common/uri'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; diff --git a/src/vs/workbench/contrib/comments/browser/commentsController.ts b/src/vs/workbench/contrib/comments/browser/commentsController.ts index d2fa2c78e8a..4faf49116c0 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsController.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsController.ts @@ -25,7 +25,7 @@ import { CommentGlyphWidget } from 'vs/workbench/contrib/comments/browser/commen import { ICommentInfo, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService'; import { isMouseUpEventDragFromMouseDown, parseMouseDownInfoFromEvent, ReviewZoneWidget } from 'vs/workbench/contrib/comments/browser/commentThreadZoneWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; import { COMMENTS_VIEW_ID } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; diff --git a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts index 2a4f2c3a2bb..d41c334028d 100644 --- a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts +++ b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts @@ -6,7 +6,7 @@ import { EditorOption, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { EditorAction, EditorContributionInstantiation, EditorExtensionsRegistry, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ICommandService } from 'vs/platform/commands/common/commands'; diff --git a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts index 26ae95ccf1d..7289d25ba29 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts @@ -16,7 +16,7 @@ import 'vs/css!./media/breakpointWidget'; import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorCommand, ServicesAccessor, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { EditorOption, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index ffcda601ff9..21f7ed8ba47 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -25,7 +25,7 @@ import 'vs/css!./media/repl'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 6fa04545a0e..da1e9c4dcd5 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -37,7 +37,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { coalesce } from 'vs/base/common/arrays'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { isCancellationError } from 'vs/base/common/errors'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 8deb70a073e..75ca16a1f24 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -7,7 +7,8 @@ import { Codicon } from 'vs/base/common/codicons'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; -import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/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_INPUT, MENU_INLINE_CHAT_WIDGET_DISCARD, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_EDIT_MODE, EditMode, 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, CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_USER_DID_EDIT, 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_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, MENU_INLINE_CHAT_WIDGET } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts index 1af9ecfa2f1..85d9762dc22 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts @@ -7,7 +7,8 @@ import { Dimension, getWindow, h, runAtThisOrScheduleAtNextAnimationFrame } from import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { assertType } from 'vs/base/common/types'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { EditorOption, IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { IModelDecorationOptions, ITextModel } from 'vs/editor/common/model'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index f65363a736b..50bba01479e 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -19,9 +19,10 @@ import 'vs/css!./inlineChat'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { IActiveCodeEditor, ICodeEditor, IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { AccessibleDiffViewer, IAccessibleDiffViewerModel } from 'vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer'; -import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { EditorLayoutInfo, EditorOption, IComputedEditorOptions } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; diff --git a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts index 05d7d253673..6d300714340 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts @@ -12,7 +12,7 @@ import { extname, isEqual } from 'vs/base/common/resources'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; import { ITextModel } from 'vs/editor/common/model'; diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts index f9c766ca849..af62b50a84c 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts @@ -10,7 +10,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { ICodeEditorViewState, IDecorationOptions } from 'vs/editor/common/editorCommon'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts index d2853645051..b43d06358f8 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts @@ -7,7 +7,7 @@ import { ArrayQueue, CompareResult } from 'vs/base/common/arrays'; import { BugIndicatingError, onUnexpectedError } from 'vs/base/common/errors'; import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, autorunOpts, observableFromEvent } from 'vs/base/common/observable'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { IModelDeltaDecoration } from 'vs/editor/common/model'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts index 9d349b594ec..b751b6bc0ad 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts @@ -6,7 +6,7 @@ import { h, reset } from 'vs/base/browser/dom'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { autorun, IReader, observableFromEvent, observableSignal, observableSignalFromEvent, transaction } from 'vs/base/common/observable'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; export class EditorGutter extends Disposable { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts index 38266ed0600..8cd243e3777 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts @@ -9,7 +9,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IObservable, autorun, derived, observableFromEvent } from 'vs/base/common/observable'; import { EditorExtensionsRegistry, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/scrollSynchronizer.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/scrollSynchronizer.ts index bbbe978f302..94857ee4726 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/scrollSynchronizer.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/scrollSynchronizer.ts @@ -5,7 +5,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { autorunWithStore, IObservable } from 'vs/base/common/observable'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { DocumentLineRangeMap } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; import { ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils'; diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts index 61810983632..93a98b32d60 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts @@ -15,7 +15,7 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import { assertType } from 'vs/base/common/types'; import { generateUuid } from 'vs/base/common/uuid'; import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 23e8dec4478..c1218b15ce8 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -9,7 +9,7 @@ import { Schemas } from 'vs/base/common/network'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DiffElementViewModelBase, getFormattedMetadataJSON, getFormattedOutputJSON, OutputComparison, outputEqual, OUTPUT_EDITOR_HEIGHT_MAGIC, PropertyFoldingState, SideBySideDiffElementViewModel, SingleSideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel'; import { CellDiffSideBySideRenderTemplate, CellDiffSingleSideRenderTemplate, DiffSide, DIFF_CELL_MARGIN, INotebookTextDiffEditor, NOTEBOOK_DIFF_CELL_INPUT, NOTEBOOK_DIFF_CELL_PROPERTY, NOTEBOOK_DIFF_CELL_PROPERTY_EXPANDED } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser'; -import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { IModelService } from 'vs/editor/common/services/model'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { CellEditType, CellUri, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts index 6741f2a3ab8..031c2afdc7f 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts @@ -9,7 +9,7 @@ import { Event } from 'vs/base/common/event'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts index 11aa9bfb6b1..9248d1cee6e 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts @@ -17,7 +17,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { DiffElementViewModelBase, SideBySideDiffElementViewModel, SingleSideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel'; import { CellDiffSideBySideRenderTemplate, CellDiffSingleSideRenderTemplate, DIFF_CELL_MARGIN, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser'; import { DeletedElement, getOptimizedNestedCodeEditorWidgetOptions, InsertElement, ModifiedElement } from 'vs/workbench/contrib/notebook/browser/diff/diffComponents'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts index 5806dd97b5f..d334bb1db3f 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts @@ -11,7 +11,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ILanguageService } from 'vs/editor/common/languages/language'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 69133337125..25a1310af43 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -9,7 +9,7 @@ import { FastDomNode } from 'vs/base/browser/fastDomNode'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 742c76b7d37..adcf99c35cd 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -29,7 +29,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { rot } from 'vs/base/common/numbers'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget'; import { IDiffEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Action, IAction, ActionRunner } from 'vs/base/common/actions'; import { IActionBarOptions } from 'vs/base/browser/ui/actionbar/actionbar'; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index f91410f9b8d..f35fe3e0eda 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -43,7 +43,7 @@ import { flatten } from 'vs/base/common/arrays'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; -import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 649a025c5c3..6911cdad058 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -23,7 +23,7 @@ import * as network from 'vs/base/common/network'; import 'vs/css!./media/searchview'; import { getCodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Selection } from 'vs/editor/common/core/selection'; import { IEditor } from 'vs/editor/common/editorCommon'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 5f36473df15..dec67977c05 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -13,7 +13,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/searchEditor'; -import { ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index a64fc0274f3..7f5dcfb7920 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -37,9 +37,10 @@ import 'vs/css!./testingOutputPeek'; import { ICodeEditor, IDiffEditorConstructionOptions, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; -import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts b/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts index 8753d7436f5..d5bf22e671a 100644 --- a/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts +++ b/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts @@ -15,7 +15,7 @@ import { FuzzyScore } from 'vs/base/common/filters'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts index 177b1a4a14e..50efd6cfc84 100644 --- a/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts @@ -17,7 +17,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { WalkThroughInput } from 'vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughInput'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { localize } from 'vs/nls'; diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts index c55daa6fdc3..de6a4ad8227 100644 --- a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -19,7 +19,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { extractFileListData } from 'vs/platform/dnd/browser/dnd'; import { Iterable } from 'vs/base/common/iterator'; import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; -import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; export class FileDialogService extends AbstractFileDialogService implements IFileDialogService { From c18d58d9629f4b45e0359fb14812526b96ebf67d Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 23 Feb 2024 14:36:18 +0100 Subject: [PATCH 0135/1175] Adopt Custom Hover for search/find/replace views/widgets (#206077) * Adopt Custom Hover for search/find/replace views * tree search filters --- src/vs/base/browser/ui/button/button.ts | 23 ++++++++++++++----- src/vs/base/browser/ui/findinput/findInput.ts | 6 +++++ .../browser/ui/findinput/findInputToggles.ts | 6 +++++ .../base/browser/ui/findinput/replaceInput.ts | 4 +++- src/vs/base/browser/ui/toggle/toggle.ts | 4 +++- src/vs/base/browser/ui/tree/abstractTree.ts | 10 ++++++-- .../contrib/find/browser/findOptionsWidget.ts | 6 +++++ .../editor/contrib/find/browser/findWidget.ts | 17 +++++++++++++- .../search/browser/patternInputWidget.ts | 3 +++ .../search/browser/searchResultsView.ts | 8 +++++-- .../contrib/search/browser/searchView.ts | 8 +++++-- .../contrib/search/browser/searchWidget.ts | 7 ++++-- .../searchEditor/browser/searchEditor.ts | 5 +++- 13 files changed, 89 insertions(+), 18 deletions(-) diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index 4675ee8c5ee..e7ca41dc8b3 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -10,6 +10,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { renderMarkdown, renderStringAsPlaintext } from 'vs/base/browser/markdownRenderer'; import { Gesture, EventType as TouchEventType } from 'vs/base/browser/touch'; import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { Action, IAction, IActionRunner } from 'vs/base/common/actions'; @@ -29,6 +30,7 @@ export interface IButtonOptions extends Partial { readonly supportIcons?: boolean; readonly supportShortLabel?: boolean; readonly secondary?: boolean; + readonly hoverDelegate?: IHoverDelegate; } export interface IButtonStyles { @@ -115,6 +117,10 @@ export class Button extends Disposable implements IButton { this._element.classList.add('monaco-text-button-with-short-label'); } + if (typeof options.title === 'string') { + this.setTitle(options.title); + } + if (typeof options.ariaLabel === 'string') { this._element.setAttribute('aria-label', options.ariaLabel); } @@ -249,16 +255,13 @@ export class Button extends Disposable implements IButton { } else if (this.options.title) { title = renderStringAsPlaintext(value); } - if (!this._hover) { - this._hover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this._element, title)); - } else { - this._hover.update(title); - } + + this.setTitle(title); if (typeof this.options.ariaLabel === 'string') { this._element.setAttribute('aria-label', this.options.ariaLabel); } else if (this.options.ariaLabel) { - this._element.setAttribute('aria-label', this._element.title); + this._element.setAttribute('aria-label', title); } this._label = value; @@ -299,6 +302,14 @@ export class Button extends Disposable implements IButton { return !this._element.classList.contains('disabled'); } + private setTitle(title: string) { + if (!this._hover) { + this._hover = this._register(setupCustomHover(this.options.hoverDelegate ?? getDefaultHoverDelegate('mouse'), this._element, title)); + } else { + this._hover.update(title); + } + } + focus(): void { this._element.focus(); } diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index 76af849c278..c119ad43779 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -16,6 +16,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import 'vs/css!./findInput'; import * as nls from 'vs/nls'; import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export interface IFindInputOptions { @@ -113,10 +114,13 @@ export class FindInput extends Widget { inputBoxStyles: options.inputBoxStyles, })); + const hoverDelegate = this._register(getDefaultHoverDelegate('element', true)); + if (this.showCommonFindToggles) { this.regex = this._register(new RegexToggle({ appendTitle: appendRegexLabel, isChecked: false, + hoverDelegate, ...options.toggleStyles })); this._register(this.regex.onChange(viaKeyboard => { @@ -133,6 +137,7 @@ export class FindInput extends Widget { this.wholeWords = this._register(new WholeWordsToggle({ appendTitle: appendWholeWordsLabel, isChecked: false, + hoverDelegate, ...options.toggleStyles })); this._register(this.wholeWords.onChange(viaKeyboard => { @@ -146,6 +151,7 @@ export class FindInput extends Widget { this.caseSensitive = this._register(new CaseSensitiveToggle({ appendTitle: appendCaseSensitiveLabel, isChecked: false, + hoverDelegate, ...options.toggleStyles })); this._register(this.caseSensitive.onChange(viaKeyboard => { diff --git a/src/vs/base/browser/ui/findinput/findInputToggles.ts b/src/vs/base/browser/ui/findinput/findInputToggles.ts index 591ab981577..8b3ea12580f 100644 --- a/src/vs/base/browser/ui/findinput/findInputToggles.ts +++ b/src/vs/base/browser/ui/findinput/findInputToggles.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { Codicon } from 'vs/base/common/codicons'; import * as nls from 'vs/nls'; @@ -13,6 +15,7 @@ export interface IFindInputToggleOpts { readonly inputActiveOptionBorder: string | undefined; readonly inputActiveOptionForeground: string | undefined; readonly inputActiveOptionBackground: string | undefined; + readonly hoverDelegate?: IHoverDelegate; } const NLS_CASE_SENSITIVE_TOGGLE_LABEL = nls.localize('caseDescription', "Match Case"); @@ -25,6 +28,7 @@ export class CaseSensitiveToggle extends Toggle { icon: Codicon.caseSensitive, title: NLS_CASE_SENSITIVE_TOGGLE_LABEL + opts.appendTitle, isChecked: opts.isChecked, + hoverDelegate: opts.hoverDelegate ?? getDefaultHoverDelegate('element'), inputActiveOptionBorder: opts.inputActiveOptionBorder, inputActiveOptionForeground: opts.inputActiveOptionForeground, inputActiveOptionBackground: opts.inputActiveOptionBackground @@ -38,6 +42,7 @@ export class WholeWordsToggle extends Toggle { icon: Codicon.wholeWord, title: NLS_WHOLE_WORD_TOGGLE_LABEL + opts.appendTitle, isChecked: opts.isChecked, + hoverDelegate: opts.hoverDelegate ?? getDefaultHoverDelegate('element'), inputActiveOptionBorder: opts.inputActiveOptionBorder, inputActiveOptionForeground: opts.inputActiveOptionForeground, inputActiveOptionBackground: opts.inputActiveOptionBackground @@ -51,6 +56,7 @@ export class RegexToggle extends Toggle { icon: Codicon.regex, title: NLS_REGEX_TOGGLE_LABEL + opts.appendTitle, isChecked: opts.isChecked, + hoverDelegate: opts.hoverDelegate ?? getDefaultHoverDelegate('element'), inputActiveOptionBorder: opts.inputActiveOptionBorder, inputActiveOptionForeground: opts.inputActiveOptionForeground, inputActiveOptionBackground: opts.inputActiveOptionBackground diff --git a/src/vs/base/browser/ui/findinput/replaceInput.ts b/src/vs/base/browser/ui/findinput/replaceInput.ts index 6cd7d4fb1c6..8e20025d757 100644 --- a/src/vs/base/browser/ui/findinput/replaceInput.ts +++ b/src/vs/base/browser/ui/findinput/replaceInput.ts @@ -16,6 +16,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import 'vs/css!./findInput'; import * as nls from 'vs/nls'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export interface IReplaceInputOptions { @@ -44,9 +45,10 @@ class PreserveCaseToggle extends Toggle { icon: Codicon.preserveCase, title: NLS_PRESERVE_CASE_LABEL + opts.appendTitle, isChecked: opts.isChecked, + hoverDelegate: opts.hoverDelegate ?? getDefaultHoverDelegate('element'), inputActiveOptionBorder: opts.inputActiveOptionBorder, inputActiveOptionForeground: opts.inputActiveOptionForeground, - inputActiveOptionBackground: opts.inputActiveOptionBackground + inputActiveOptionBackground: opts.inputActiveOptionBackground, }); } } diff --git a/src/vs/base/browser/ui/toggle/toggle.ts b/src/vs/base/browser/ui/toggle/toggle.ts index 54b9130d9e4..540bb17db2f 100644 --- a/src/vs/base/browser/ui/toggle/toggle.ts +++ b/src/vs/base/browser/ui/toggle/toggle.ts @@ -15,6 +15,7 @@ import 'vs/css!./toggle'; import { isActiveElement, $, addDisposableListener, EventType } from 'vs/base/browser/dom'; import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; export interface IToggleOpts extends IToggleStyles { readonly actionClassName?: string; @@ -22,6 +23,7 @@ export interface IToggleOpts extends IToggleStyles { readonly title: string; readonly isChecked: boolean; readonly notFocusable?: boolean; + readonly hoverDelegate?: IHoverDelegate; } export interface IToggleStyles { @@ -130,7 +132,7 @@ export class Toggle extends Widget { } this.domNode = document.createElement('div'); - this._hover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.domNode, this._opts.title)); + this._hover = this._register(setupCustomHover(opts.hoverDelegate ?? getDefaultHoverDelegate('mouse'), this.domNode, this._opts.title)); this.domNode.classList.add(...classes); if (!this._opts.notFocusable) { this.domNode.tabIndex = 0; diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 16f985264d4..c42cd0ba336 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -33,6 +33,8 @@ import { ISpliceable } from 'vs/base/common/sequence'; import { isNumber } from 'vs/base/common/types'; import 'vs/css!./media/tree'; import { localize } from 'vs/nls'; +import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; class TreeElementsDragAndDropData extends ElementsDragAndDropData { @@ -679,6 +681,7 @@ export interface ITreeFindToggleOpts { readonly inputActiveOptionBorder: string | undefined; readonly inputActiveOptionForeground: string | undefined; readonly inputActiveOptionBackground: string | undefined; + readonly hoverDelegate?: IHoverDelegate; } export class ModeToggle extends Toggle { @@ -687,6 +690,7 @@ export class ModeToggle extends Toggle { icon: Codicon.listFilter, title: localize('filter', "Filter"), isChecked: opts.isChecked ?? false, + hoverDelegate: opts.hoverDelegate ?? getDefaultHoverDelegate('element'), inputActiveOptionBorder: opts.inputActiveOptionBorder, inputActiveOptionForeground: opts.inputActiveOptionForeground, inputActiveOptionBackground: opts.inputActiveOptionBackground @@ -700,6 +704,7 @@ export class FuzzyToggle extends Toggle { icon: Codicon.searchFuzzy, title: localize('fuzzySearch', "Fuzzy Match"), isChecked: opts.isChecked ?? false, + hoverDelegate: opts.hoverDelegate ?? getDefaultHoverDelegate('element'), inputActiveOptionBorder: opts.inputActiveOptionBorder, inputActiveOptionForeground: opts.inputActiveOptionForeground, inputActiveOptionBackground: opts.inputActiveOptionBackground @@ -802,8 +807,9 @@ class FindWidget extends Disposable { this.elements.root.style.boxShadow = `0 0 8px 2px ${styles.listFilterWidgetShadow}`; } - this.modeToggle = this._register(new ModeToggle({ ...styles.toggleStyles, isChecked: mode === TreeFindMode.Filter })); - this.matchTypeToggle = this._register(new FuzzyToggle({ ...styles.toggleStyles, isChecked: matchType === TreeFindMatchType.Fuzzy })); + const toggleHoverDelegate = this._register(getDefaultHoverDelegate('element', true)); + this.modeToggle = this._register(new ModeToggle({ ...styles.toggleStyles, isChecked: mode === TreeFindMode.Filter, hoverDelegate: toggleHoverDelegate })); + this.matchTypeToggle = this._register(new FuzzyToggle({ ...styles.toggleStyles, isChecked: matchType === TreeFindMatchType.Fuzzy, hoverDelegate: toggleHoverDelegate })); this.onDidChangeMode = Event.map(this.modeToggle.onChange, () => this.modeToggle.checked ? TreeFindMode.Filter : TreeFindMode.Highlight, this._store); this.onDidChangeMatchType = Event.map(this.matchTypeToggle.onChange, () => this.matchTypeToggle.checked ? TreeFindMatchType.Fuzzy : TreeFindMatchType.Contiguous, this._store); diff --git a/src/vs/editor/contrib/find/browser/findOptionsWidget.ts b/src/vs/editor/contrib/find/browser/findOptionsWidget.ts index 9ebf4167820..7b693a1f28c 100644 --- a/src/vs/editor/contrib/find/browser/findOptionsWidget.ts +++ b/src/vs/editor/contrib/find/browser/findOptionsWidget.ts @@ -13,6 +13,7 @@ import { FIND_IDS } from 'vs/editor/contrib/find/browser/findModel'; import { FindReplaceState } from 'vs/editor/contrib/find/browser/findState'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { asCssVariable, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export class FindOptionsWidget extends Widget implements IOverlayWidget { @@ -52,9 +53,12 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget { inputActiveOptionBackground: asCssVariable(inputActiveOptionBackground), }; + const hoverDelegate = this._register(getDefaultHoverDelegate('element', true)); + this.caseSensitive = this._register(new CaseSensitiveToggle({ appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleCaseSensitiveCommand), isChecked: this._state.matchCase, + hoverDelegate, ...toggleStyles })); this._domNode.appendChild(this.caseSensitive.domNode); @@ -67,6 +71,7 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget { this.wholeWords = this._register(new WholeWordsToggle({ appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleWholeWordCommand), isChecked: this._state.wholeWord, + hoverDelegate, ...toggleStyles })); this._domNode.appendChild(this.wholeWords.domNode); @@ -79,6 +84,7 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget { this.regex = this._register(new RegexToggle({ appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleRegexCommand), isChecked: this._state.isRegex, + hoverDelegate, ...toggleStyles })); this._domNode.appendChild(this.regex.domNode); diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts index f89e3d81837..76189e64741 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.ts +++ b/src/vs/editor/contrib/find/browser/findWidget.ts @@ -43,6 +43,9 @@ import { isHighContrast } from 'vs/platform/theme/common/theme'; import { assertIsDefined } from 'vs/base/common/types'; import { defaultInputBoxStyles, defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; import { Selection } from 'vs/editor/common/core/selection'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; const findSelectionIcon = registerIcon('find-selection', Codicon.selection, nls.localize('findSelectionIcon', 'Icon for \'Find in Selection\' in the editor find widget.')); const findCollapsedIcon = registerIcon('find-collapsed', Codicon.chevronRight, nls.localize('findCollapsedIcon', 'Icon to indicate that the editor find widget is collapsed.')); @@ -1010,10 +1013,14 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL this._matchesCount.className = 'matchesCount'; this._updateMatchesCount(); + // Create a scoped hover delegate for all find related buttons + const hoverDelegate = getDefaultHoverDelegate('element', true); + // Previous button this._prevBtn = this._register(new SimpleButton({ label: NLS_PREVIOUS_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.PreviousMatchFindAction), icon: findPreviousMatchIcon, + hoverDelegate, onTrigger: () => { assertIsDefined(this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction)).run().then(undefined, onUnexpectedError); } @@ -1023,6 +1030,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL this._nextBtn = this._register(new SimpleButton({ label: NLS_NEXT_MATCH_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.NextMatchFindAction), icon: findNextMatchIcon, + hoverDelegate, onTrigger: () => { assertIsDefined(this._codeEditor.getAction(FIND_IDS.NextMatchFindAction)).run().then(undefined, onUnexpectedError); } @@ -1077,6 +1085,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL this._closeBtn = this._register(new SimpleButton({ label: NLS_CLOSE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.CloseFindWidgetCommand), icon: widgetClose, + hoverDelegate, onTrigger: () => { this._state.change({ isRevealed: false, searchScope: null }, false); }, @@ -1138,10 +1147,14 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL } })); + // Create scoped hover delegate for replace actions + const replaceHoverDelegate = this._register(getDefaultHoverDelegate('element', true)); + // Replace one button this._replaceBtn = this._register(new SimpleButton({ label: NLS_REPLACE_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceOneAction), icon: findReplaceIcon, + hoverDelegate: replaceHoverDelegate, onTrigger: () => { this._controller.replace(); }, @@ -1157,6 +1170,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL this._replaceAllBtn = this._register(new SimpleButton({ label: NLS_REPLACE_ALL_BTN_LABEL + this._keybindingLabelFor(FIND_IDS.ReplaceAllAction), icon: findReplaceAllIcon, + hoverDelegate: replaceHoverDelegate, onTrigger: () => { this._controller.replaceAll(); } @@ -1299,6 +1313,7 @@ export interface ISimpleButtonOpts { readonly label: string; readonly className?: string; readonly icon?: ThemeIcon; + readonly hoverDelegate?: IHoverDelegate; readonly onTrigger: () => void; readonly onKeyDown?: (e: IKeyboardEvent) => void; } @@ -1321,11 +1336,11 @@ export class SimpleButton extends Widget { } this._domNode = document.createElement('div'); - this._domNode.title = this._opts.label; this._domNode.tabIndex = 0; this._domNode.className = className; this._domNode.setAttribute('role', 'button'); this._domNode.setAttribute('aria-label', this._opts.label); + this._register(setupCustomHover(opts.hoverDelegate ?? getDefaultHoverDelegate('element'), this._domNode, this._opts.label)); this.onclick(this._domNode, (e) => { this._opts.onTrigger(); diff --git a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts index 6d167046d37..c7dfc7eae60 100644 --- a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts @@ -19,6 +19,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export interface IOptions { placeholder?: string; @@ -219,6 +220,7 @@ export class IncludePatternInputWidget extends PatternInputWidget { icon: Codicon.book, title: nls.localize('onlySearchInOpenEditors', "Search only in Open Editors"), isChecked: false, + hoverDelegate: getDefaultHoverDelegate('element'), ...defaultToggleStyles })); this._register(this.useSearchInEditorsBox.onChange(viaKeyboard => { @@ -271,6 +273,7 @@ export class ExcludePatternInputWidget extends PatternInputWidget { actionClassName: 'useExcludesAndIgnoreFiles', title: nls.localize('useExcludesAndIgnoreFilesDescription', "Use Exclude Settings and Ignore Files"), isChecked: true, + hoverDelegate: getDefaultHoverDelegate('element'), ...defaultToggleStyles })); this._register(this.useExcludesAndIgnoreFilesBox.onChange(viaKeyboard => { diff --git a/src/vs/workbench/contrib/search/browser/searchResultsView.ts b/src/vs/workbench/contrib/search/browser/searchResultsView.ts index 3f71827f12c..38a4f536403 100644 --- a/src/vs/workbench/contrib/search/browser/searchResultsView.ts +++ b/src/vs/workbench/contrib/search/browser/searchResultsView.ts @@ -30,6 +30,8 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { defaultCountBadgeStyles } from 'vs/platform/theme/browser/defaultStyles'; import { SearchContext } from 'vs/workbench/contrib/search/common/constants'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; interface IFolderMatchTemplate { label: IResourceLabel; @@ -360,7 +362,9 @@ export class MatchRenderer extends Disposable implements ICompressibleTreeRender templateData.match.classList.toggle('replace', replace); templateData.replace.textContent = replace ? match.replaceString : ''; templateData.after.textContent = preview.after; - templateData.parent.title = (preview.fullBefore + (replace ? match.replaceString : preview.inside) + preview.after).trim().substr(0, 999); + + const title = (preview.fullBefore + (replace ? match.replaceString : preview.inside) + preview.after).trim().substr(0, 999); + templateData.disposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), templateData.parent, title)); SearchContext.IsEditableItemKey.bindTo(templateData.contextKeyService).set(!(match instanceof MatchInNotebook && match.isReadonly())); @@ -372,7 +376,7 @@ export class MatchRenderer extends Disposable implements ICompressibleTreeRender templateData.lineNumber.classList.toggle('show', (numLines > 0) || showLineNumbers); templateData.lineNumber.textContent = lineNumberStr + extraLinesStr; - templateData.lineNumber.setAttribute('title', this.getMatchTitle(match, showLineNumbers)); + templateData.disposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), templateData.lineNumber, this.getMatchTitle(match, showLineNumbers))); templateData.actions.context = { viewer: this.searchView.getControl(), element: match }; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 6911cdad058..4a0a156fcf6 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -81,6 +81,8 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { ILogService } from 'vs/platform/log/common/log'; import { AccessibilitySignal, IAccessibilitySignalService } from 'vs/platform/accessibilitySignal/browser/accessibilitySignalService'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; const $ = dom.$; @@ -405,7 +407,8 @@ export class SearchView extends ViewPane { // Toggle query details button this.toggleQueryDetailsButton = dom.append(this.queryDetails, - $('.more' + ThemeIcon.asCSSSelector(searchDetailsIcon), { tabindex: 0, role: 'button', title: nls.localize('moreSearch', "Toggle Search Details") })); + $('.more' + ThemeIcon.asCSSSelector(searchDetailsIcon), { tabindex: 0, role: 'button' })); + this._register(setupCustomHover(getDefaultHoverDelegate('element'), this.toggleQueryDetailsButton, nls.localize('moreSearch', "Toggle Search Details"))); this._register(dom.addDisposableListener(this.toggleQueryDetailsButton, dom.EventType.CLICK, e => { dom.EventHelper.stop(e); @@ -2133,7 +2136,8 @@ class SearchLinkButton extends Disposable { constructor(label: string, handler: (e: dom.EventLike) => unknown, tooltip?: string) { super(); - this.element = $('a.pointer', { tabindex: 0, title: tooltip }, label); + this.element = $('a.pointer', { tabindex: 0 }, label); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.element, tooltip)); this.addEventHandlers(handler); } diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 26acd42d459..731653a4828 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -42,6 +42,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { GroupModelChangeKind } from 'vs/workbench/common/editor'; import { SearchFindInput } from 'vs/workbench/contrib/search/browser/searchFindInput'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; /** Specified in searchview.css */ const SingleLineInputHeight = 26; @@ -370,7 +371,9 @@ export class SearchWidget extends Widget { buttonSecondaryBackground: undefined, buttonSecondaryForeground: undefined, buttonSecondaryHoverBackground: undefined, - buttonSeparator: undefined + buttonSeparator: undefined, + title: nls.localize('search.replace.toggle.button.title', "Toggle Replace"), + hoverDelegate: getDefaultHoverDelegate('element'), }; this.toggleReplaceButton = this._register(new Button(parent, opts)); this.toggleReplaceButton.element.setAttribute('aria-expanded', 'false'); @@ -378,7 +381,6 @@ export class SearchWidget extends Widget { this.toggleReplaceButton.icon = searchHideReplaceIcon; // TODO@joao need to dispose this listener eventually this.toggleReplaceButton.onDidClick(() => this.onToggleReplaceButton()); - this.toggleReplaceButton.element.title = nls.localize('search.replace.toggle.button.title', "Toggle Replace"); } private renderSearchInput(parent: HTMLElement, options: ISearchWidgetOptions): void { @@ -441,6 +443,7 @@ export class SearchWidget extends Widget { isChecked: false, title: appendKeyBindingLabel(nls.localize('showContext', "Toggle Context Lines"), this.keybindingService.lookupKeybinding(ToggleSearchEditorContextLinesCommandId)), icon: searchShowContextIcon, + hoverDelegate: getDefaultHoverDelegate('element'), ...defaultToggleStyles }); this._register(this.showContextToggle.onChange(() => this.onContextLinesChanged())); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index dec67977c05..02b833d6737 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -62,6 +62,8 @@ import { UnusualLineTerminatorsDetector } from 'vs/editor/contrib/unusualLineTer import { defaultToggleStyles, getInputBoxStyle } from 'vs/platform/theme/browser/defaultStyles'; import { ILogService } from 'vs/platform/log/common/log'; import { SearchContext } from 'vs/workbench/contrib/search/common/constants'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; const RESULT_LINE_REGEX = /^(\s+)(\d+)(: | )(\s*)(.*)$/; const FILE_LINE_REGEX = /^(\S.*):$/; @@ -161,7 +163,8 @@ export class SearchEditor extends AbstractTextCodeEditor this.includesExcludesContainer = DOM.append(container, DOM.$('.includes-excludes')); // Toggle query details button - this.toggleQueryDetailsButton = DOM.append(this.includesExcludesContainer, DOM.$('.expand' + ThemeIcon.asCSSSelector(searchDetailsIcon), { tabindex: 0, role: 'button', title: localize('moreSearch', "Toggle Search Details") })); + this.toggleQueryDetailsButton = DOM.append(this.includesExcludesContainer, DOM.$('.expand' + ThemeIcon.asCSSSelector(searchDetailsIcon), { tabindex: 0, role: 'button' })); + this._register(setupCustomHover(getDefaultHoverDelegate('element'), this.toggleQueryDetailsButton, localize('moreSearch', "Toggle Search Details"))); this._register(DOM.addDisposableListener(this.toggleQueryDetailsButton, DOM.EventType.CLICK, e => { DOM.EventHelper.stop(e); this.toggleIncludesExcludes(); From 11a6b428f829f0b843f14fc278412d79ab4f2dc7 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 23 Feb 2024 15:06:44 +0100 Subject: [PATCH 0136/1175] Custom hover adoption for test explorer (#206086) ustom hover adoption for test explorer --- .../testing/browser/testingExplorerFilter.ts | 5 ++- .../testing/browser/testingExplorerView.ts | 42 +++++++++++-------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts index ba9050a610b..a7490c873cf 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts @@ -5,7 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { BaseActionViewItem, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { Action, IAction, IActionRunner, Separator } from 'vs/base/common/actions'; @@ -46,11 +46,12 @@ export class TestingExplorerFilter extends BaseActionViewItem { constructor( action: IAction, + options: IBaseActionViewItemOptions, @ITestExplorerFilterState private readonly state: ITestExplorerFilterState, @IInstantiationService private readonly instantiationService: IInstantiationService, @ITestService private readonly testService: ITestService, ) { - super(null, action); + super(null, action, options); this.updateFilterActiveState(); this._register(testService.excluded.onTestExclusionsChanged(this.updateFilterActiveState, this)); } diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index ba7f2800830..8eb58e118a3 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -5,8 +5,11 @@ import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { ActionBar, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { Button } from 'vs/base/browser/ui/button/button'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { DefaultKeyboardNavigationDelegate, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; @@ -285,18 +288,18 @@ export class TestingExplorerView extends ViewPane { } /** @override */ - public override getActionViewItem(action: IAction): IActionViewItem | undefined { + public override getActionViewItem(action: IAction, options: IActionViewItemOptions): IActionViewItem | undefined { switch (action.id) { case TestCommandId.FilterAction: - this.filter.value = this.instantiationService.createInstance(TestingExplorerFilter, action); + this.filter.value = this.instantiationService.createInstance(TestingExplorerFilter, action, options); this.filterFocusListener.value = this.filter.value.onDidFocus(() => this.lastFocusState = LastFocusState.Input); return this.filter.value; case TestCommandId.RunSelectedAction: - return this.getRunGroupDropdown(TestRunProfileBitset.Run, action); + return this.getRunGroupDropdown(TestRunProfileBitset.Run, action, options); case TestCommandId.DebugSelectedAction: - return this.getRunGroupDropdown(TestRunProfileBitset.Debug, action); + return this.getRunGroupDropdown(TestRunProfileBitset.Debug, action, options); default: - return super.getActionViewItem(action); + return super.getActionViewItem(action, options); } } @@ -380,10 +383,10 @@ export class TestingExplorerView extends ViewPane { super.saveState(); } - private getRunGroupDropdown(group: TestRunProfileBitset, defaultAction: IAction) { + private getRunGroupDropdown(group: TestRunProfileBitset, defaultAction: IAction, options: IActionViewItemOptions) { const dropdownActions = this.getTestConfigGroupActions(group); if (dropdownActions.length < 2) { - return super.getActionViewItem(defaultAction); + return super.getActionViewItem(defaultAction, options); } const primaryAction = this.instantiationService.createInstance(MenuItemAction, { @@ -401,13 +404,13 @@ export class TestingExplorerView extends ViewPane { primaryAction, dropdownAction, dropdownActions, '', this.contextMenuService, - {} + options ); } private createFilterActionBar() { const bar = new ActionBar(this.treeHeader, { - actionViewItemProvider: action => this.getActionViewItem(action), + actionViewItemProvider: (action, options) => this.getActionViewItem(action, options), triggerKeys: { keyDown: false, keys: [] }, }); bar.push(new Action(TestCommandId.FilterAction)); @@ -442,6 +445,7 @@ class ResultSummaryView extends Disposable { private elementsWereAttached = false; private badgeType: TestingCountBadge; private lastBadge?: NumberBadge | IconBadge; + private countHover: ICustomHover; private readonly badgeDisposable = this._register(new MutableDisposable()); private readonly renderLoop = this._register(new RunOnceScheduler(() => this.render(), SUMMARY_RENDER_INTERVAL)); private readonly elements = dom.h('div.result-summary', [ @@ -472,6 +476,8 @@ class ResultSummaryView extends Disposable { } })); + this.countHover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.elements.count, '')); + const ab = this._register(new ActionBar(this.elements.rerun, { actionViewItemProvider: (action, options) => createActionViewItem(instantiationService, action, options), })); @@ -518,7 +524,7 @@ class ResultSummaryView extends Disposable { } count.textContent = `${counts.passed}/${counts.totalWillBeRun}`; - count.title = getTestProgressText(counts); + this.countHover.update(getTestProgressText(counts)); this.renderActivityBadge(counts); if (!this.elementsWereAttached) { @@ -1301,6 +1307,7 @@ class IdentityProvider implements IIdentityProvider { interface IErrorTemplateData { label: HTMLElement; + disposable: DisposableStore; } class ErrorRenderer implements ITreeRenderer { @@ -1318,7 +1325,7 @@ class ErrorRenderer implements ITreeRenderer, _: number, data: IErrorTemplateData): void { @@ -1330,12 +1337,11 @@ class ErrorRenderer implements ITreeRenderer + actionViewItemProvider: (action, options) => action instanceof MenuItemAction - ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) + ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }) : undefined })); @@ -1451,7 +1457,7 @@ class TestItemRenderer extends Disposable data.icon.className += ' retired'; } - data.label.title = getLabelForTestTreeElement(node.element); + data.elementDisposable.add(setupCustomHover(getDefaultHoverDelegate('mouse'), data.label, getLabelForTestTreeElement(node.element))); if (node.element.test.item.label.trim()) { dom.reset(data.label, ...renderLabelWithIcons(node.element.test.item.label)); } else { From 529e73d71b794d152671942cb3ae42a33e4c7d3b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 23 Feb 2024 15:15:06 +0100 Subject: [PATCH 0137/1175] api - sketch up `vscode.lm.makeChatRequest` alternative to requesting chat access (#206088) re https://github.com/microsoft/vscode/issues/205800 --- .../workbench/api/common/extHost.api.impl.ts | 16 ++++++++- .../api/common/extHostLanguageModels.ts | 36 +++++++++++++++++++ .../vscode.proposed.languageModels.d.ts | 26 ++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index f89c741dd49..3bf78d8ed24 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken, 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'; @@ -1436,6 +1436,20 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I onDidChangeLanguageModels: (listener, thisArgs?, disposables?) => { checkProposedApiEnabled(extension, 'languageModels'); return extHostChatProvider.onDidChangeProviders(listener, thisArgs, disposables); + }, + makeChatRequest(languageModel: string, messages: vscode.LanguageModelMessage[], optionsOrToken: { [name: string]: any } | vscode.CancellationToken, token?: vscode.CancellationToken) { + checkProposedApiEnabled(extension, 'languageModels'); + let options: Record; + if (CancellationToken.isCancellationToken(optionsOrToken)) { + options = {}; + token = optionsOrToken; + } else if (CancellationToken.isCancellationToken(token)) { + options = optionsOrToken; + token = token; + } else { + throw new Error('Invalid arguments'); + } + return extHostChatProvider.makeChatRequest(extension, languageModel, messages, options, token); } }; diff --git a/src/vs/workbench/api/common/extHostLanguageModels.ts b/src/vs/workbench/api/common/extHostLanguageModels.ts index 7baf1d9353d..aa45f8f58aa 100644 --- a/src/vs/workbench/api/common/extHostLanguageModels.ts +++ b/src/vs/workbench/api/common/extHostLanguageModels.ts @@ -38,6 +38,10 @@ class LanguageModelResponseStream { class LanguageModelRequest { + static fromError(err: Error): vscode.LanguageModelResponse { + return new LanguageModelRequest(Promise.reject(err), new CancellationTokenSource()).apiObject; + } + readonly apiObject: vscode.LanguageModelResponse; private readonly _responseStreams = new Map(); @@ -223,6 +227,38 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { } } + async makeChatRequest(extension: IExtensionDescription, languageModelId: string, messages: vscode.LanguageModelMessage[], options: Record, token: CancellationToken) { + + const from = extension.identifier; + // const justification = options?.justification; // TODO@jrieken + const metadata = await this._proxy.$prepareChatAccess(from, languageModelId, undefined); + + if (!metadata || !this._languageModelIds.has(languageModelId)) { + return LanguageModelRequest.fromError(new Error(`Language model ${languageModelId} is unknown`)); + } + + if (this._isUsingAuth(from, metadata)) { + await this._getAuthAccess(extension, { identifier: metadata.extension, displayName: metadata.auth.providerLabel }, undefined); + + if (!this._modelAccessList.get(from)?.has(metadata.extension)) { + return LanguageModelRequest.fromError(new Error('Access to chat has been revoked')); + } + } + + const cts = new CancellationTokenSource(token); + const requestId = (Math.random() * 1e6) | 0; + const requestPromise = this._proxy.$fetchResponse(from, languageModelId, requestId, messages.map(typeConvert.LanguageModelMessage.from), options ?? {}, cts.token); + const res = new LanguageModelRequest(requestPromise, cts); + this._pendingRequest.set(requestId, { languageModelId, res }); + + requestPromise.finally(() => { + this._pendingRequest.delete(requestId); + cts.dispose(); + }); + + return res.apiObject; + } + async requestLanguageModelAccess(extension: IExtensionDescription, languageModelId: string, options?: vscode.LanguageModelAccessOptions): Promise { const from = extension.identifier; const justification = options?.justification; diff --git a/src/vscode-dts/vscode.proposed.languageModels.d.ts b/src/vscode-dts/vscode.proposed.languageModels.d.ts index 0e35351833b..bde1bd4aad4 100644 --- a/src/vscode-dts/vscode.proposed.languageModels.d.ts +++ b/src/vscode-dts/vscode.proposed.languageModels.d.ts @@ -171,6 +171,32 @@ declare module 'vscode' { */ export function requestLanguageModelAccess(id: string, options?: LanguageModelAccessOptions): Thenable; + + + /** + * Make a chat request using a language model. + * + * *Note* that language model use may be subject to access restrictions and user consent. This function always returns a response-object + * but its {@link LanguageModelResponse.result `result`}-property may indicate failure, e.g. due to + * + * - user consent not given + * - quote limits exceeded + * - model does not exist + * + * @param languageModel A language model identifier. See {@link languageModels} for aviailable values. + * @param messages + * @param options + * @param token + */ + // TODO@API refine doc + // TODO@API define specific error types? + export function makeChatRequest(languageModel: string, messages: LanguageModelMessage[], options: { [name: string]: any }, token: CancellationToken): Thenable; + + /** + * @see {@link makeChatRequest} + */ + export function makeChatRequest(languageModel: string, messages: LanguageModelMessage[], token: CancellationToken): Thenable; + /** * The identifiers of all language models that are currently available. */ From aca9718d33e8f489cfd26903bf3eb2b7dedb714b Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 23 Feb 2024 15:17:44 +0100 Subject: [PATCH 0138/1175] Register treeview hover delegate (#206089) dispose treeview hover delegate --- src/vs/workbench/browser/parts/views/treeView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 0245752c809..0eb478a3544 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -1106,7 +1106,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer this.rerender())); this._register(this.themeService.onDidColorThemeChange(() => this.rerender())); this._register(checkboxStateHandler.onDidChangeCheckboxState(items => { From 95e10d938f4681a2d41df588a0b38788ebf5a43e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 23 Feb 2024 15:19:33 +0100 Subject: [PATCH 0139/1175] Remove `livePreview` and its implementation (#206073) * Remove `livePreview` and its implementation fixes https://github.com/microsoft/vscode/issues/205535 * make compiler happy --- .../browser/inlineChatController.ts | 5 +- .../browser/inlineChatFileCreationWidget.ts | 256 +++++++++ .../browser/inlineChatLivePreviewWidget.ts | 532 ------------------ .../browser/inlineChatStrategies.ts | 161 +----- .../contrib/inlineChat/common/inlineChat.ts | 4 +- .../test/browser/inlineChatController.test.ts | 9 +- 6 files changed, 262 insertions(+), 705 deletions(-) create mode 100644 src/vs/workbench/contrib/inlineChat/browser/inlineChatFileCreationWidget.ts delete mode 100644 src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 38ea05687a1..775b62fee68 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -41,7 +41,7 @@ import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IInlineChatSavingService } from './inlineChatSavingService'; import { EmptyResponse, ErrorResponse, ExpansionState, ReplyResponse, Session, SessionExchange, SessionPrompt } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { IInlineChatSessionService } from './inlineChatSessionService'; -import { EditModeStrategy, IEditObserver, LivePreviewStrategy, LiveStrategy, PreviewStrategy, ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies'; +import { EditModeStrategy, IEditObserver, LiveStrategy, PreviewStrategy, ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies'; import { IInlineChatMessageAppender, InlineChatZoneWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_RESPONSE_TYPES, CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING, CTX_INLINE_CHAT_USER_DID_EDIT, EditMode, IInlineChatProgressItem, IInlineChatRequest, IInlineChatResponse, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseFeedbackKind, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -342,9 +342,6 @@ export class InlineChatController implements IEditorContribution { case EditMode.Preview: this._strategy = this._instaService.createInstance(PreviewStrategy, session, this._editor, this._zone.value); break; - case EditMode.LivePreview: - this._strategy = this._instaService.createInstance(LivePreviewStrategy, session, this._editor, this._zone.value); - break; case EditMode.Live: default: this._strategy = this._instaService.createInstance(LiveStrategy, session, this._editor, this._zone.value); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatFileCreationWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatFileCreationWidget.ts new file mode 100644 index 00000000000..eca335cb375 --- /dev/null +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatFileCreationWidget.ts @@ -0,0 +1,256 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Dimension, h } from 'vs/base/browser/dom'; +import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { Range } from 'vs/editor/common/core/range'; +import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; +import * as editorColorRegistry from 'vs/editor/common/core/editorColorRegistry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { INLINE_CHAT_ID, inlineChatRegionHighlight } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { Position } from 'vs/editor/common/core/position'; +import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; +import { ResourceLabel } from 'vs/workbench/browser/labels'; +import { FileKind } from 'vs/platform/files/common/files'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { ButtonBar, IButton } from 'vs/base/browser/ui/button/button'; +import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { SaveReason, SideBySideEditor } from 'vs/workbench/common/editor'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IAction, toAction } from 'vs/base/common/actions'; +import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; +import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { Codicon } from 'vs/base/common/codicons'; +import { TAB_ACTIVE_MODIFIED_BORDER } from 'vs/workbench/common/theme'; +import { localize } from 'vs/nls'; +import { Event } from 'vs/base/common/event'; + +export class InlineChatFileCreatePreviewWidget extends ZoneWidget { + + private static TitleHeight = 35; + + private readonly _elements = h('div.inline-chat-newfile-widget@domNode', [ + h('div.title@title', [ + h('span.name.show-file-icons@name'), + h('span.detail@detail'), + ]), + h('div.editor@editor'), + ]); + + private readonly _name: ResourceLabel; + private readonly _previewEditor: ICodeEditor; + private readonly _previewStore = new MutableDisposable(); + private readonly _buttonBar: ButtonBarWidget; + private _dim: Dimension | undefined; + + constructor( + parentEditor: ICodeEditor, + @IInstantiationService instaService: IInstantiationService, + @IThemeService themeService: IThemeService, + @ITextModelService private readonly _textModelResolverService: ITextModelService, + @IEditorService private readonly _editorService: IEditorService, + ) { + super(parentEditor, { + showArrow: false, + showFrame: true, + frameColor: colorRegistry.asCssVariable(TAB_ACTIVE_MODIFIED_BORDER), + frameWidth: 1, + isResizeable: true, + isAccessible: true, + showInHiddenAreas: true, + ordinal: 10000 + 2 + }); + super.create(); + + this._name = instaService.createInstance(ResourceLabel, this._elements.name, { supportIcons: true }); + this._elements.detail.appendChild(renderIcon(Codicon.circleFilled)); + + const contributions = EditorExtensionsRegistry + .getEditorContributions() + .filter(c => c.id !== INLINE_CHAT_ID); + + this._previewEditor = instaService.createInstance(EmbeddedCodeEditorWidget, this._elements.editor, { + scrollBeyondLastLine: false, + stickyScroll: { enabled: false }, + minimap: { enabled: false }, + scrollbar: { alwaysConsumeMouseWheel: false, useShadows: true, ignoreHorizontalScrollbarInContentHeight: true, }, + }, { isSimpleWidget: true, contributions }, parentEditor); + + const doStyle = () => { + const theme = themeService.getColorTheme(); + const overrides: [target: string, source: string][] = [ + [colorRegistry.editorBackground, inlineChatRegionHighlight], + [editorColorRegistry.editorGutter, inlineChatRegionHighlight], + ]; + + for (const [target, source] of overrides) { + const value = theme.getColor(source); + if (value) { + this._elements.domNode.style.setProperty(colorRegistry.asCssVariableName(target), String(value)); + } + } + }; + doStyle(); + this._disposables.add(themeService.onDidColorThemeChange(doStyle)); + + this._buttonBar = instaService.createInstance(ButtonBarWidget); + this._elements.title.appendChild(this._buttonBar.domNode); + } + + override dispose(): void { + this._name.dispose(); + this._buttonBar.dispose(); + this._previewEditor.dispose(); + this._previewStore.dispose(); + super.dispose(); + } + + protected override _fillContainer(container: HTMLElement): void { + container.appendChild(this._elements.domNode); + } + + override show(): void { + throw new Error('Use showFileCreation'); + } + + async showCreation(where: Position, untitledTextModel: IUntitledTextEditorModel): Promise { + + const store = new DisposableStore(); + this._previewStore.value = store; + + this._name.element.setFile(untitledTextModel.resource, { + fileKind: FileKind.FILE, + fileDecorations: { badges: true, colors: true } + }); + + const actionSave = toAction({ + id: '1', + label: localize('save', "Create"), + run: () => untitledTextModel.save({ reason: SaveReason.EXPLICIT }) + }); + const actionSaveAs = toAction({ + id: '2', + label: localize('saveAs', "Create As"), + run: async () => { + const ids = this._editorService.findEditors(untitledTextModel.resource, { supportSideBySide: SideBySideEditor.ANY }); + await this._editorService.save(ids.slice(), { saveAs: true, reason: SaveReason.EXPLICIT }); + } + }); + + this._buttonBar.update([ + [actionSave, actionSaveAs], + [(toAction({ id: '3', label: localize('discard', "Discard"), run: () => untitledTextModel.revert() }))] + ]); + + store.add(Event.any( + untitledTextModel.onDidRevert, + untitledTextModel.onDidSave, + untitledTextModel.onDidChangeDirty, + untitledTextModel.onWillDispose + )(() => this.hide())); + + await untitledTextModel.resolve(); + + const ref = await this._textModelResolverService.createModelReference(untitledTextModel.resource); + store.add(ref); + + const model = ref.object.textEditorModel; + this._previewEditor.setModel(model); + + const lineHeight = this.editor.getOption(EditorOption.lineHeight); + + this._elements.title.style.height = `${InlineChatFileCreatePreviewWidget.TitleHeight}px`; + const titleHightInLines = InlineChatFileCreatePreviewWidget.TitleHeight / lineHeight; + + const maxLines = Math.max(4, Math.floor((this.editor.getLayoutInfo().height / lineHeight) * .33)); + const lines = Math.min(maxLines, model.getLineCount()); + + super.show(where, titleHightInLines + lines); + } + + override hide(): void { + this._previewStore.clear(); + super.hide(); + } + + // --- layout + + protected override revealRange(range: Range, isLastLine: boolean): void { + // ignore + } + + protected override _onWidth(widthInPixel: number): void { + if (this._dim) { + this._doLayout(this._dim.height, widthInPixel); + } + } + + protected override _doLayout(heightInPixel: number, widthInPixel: number): void { + + const { lineNumbersLeft } = this.editor.getLayoutInfo(); + this._elements.title.style.marginLeft = `${lineNumbersLeft}px`; + + const newDim = new Dimension(widthInPixel, heightInPixel); + if (!Dimension.equals(this._dim, newDim)) { + this._dim = newDim; + this._previewEditor.layout(this._dim.with(undefined, this._dim.height - InlineChatFileCreatePreviewWidget.TitleHeight)); + } + } +} + + +class ButtonBarWidget { + + private readonly _domNode = h('div.buttonbar-widget'); + private readonly _buttonBar: ButtonBar; + private readonly _store = new DisposableStore(); + + constructor( + @IContextMenuService private _contextMenuService: IContextMenuService, + ) { + this._buttonBar = new ButtonBar(this.domNode); + + } + + update(allActions: IAction[][]): void { + this._buttonBar.clear(); + let secondary = false; + for (const actions of allActions) { + let btn: IButton; + const [first, ...rest] = actions; + if (!first) { + continue; + } else if (rest.length === 0) { + // single action + btn = this._buttonBar.addButton({ ...defaultButtonStyles, secondary }); + } else { + btn = this._buttonBar.addButtonWithDropdown({ + ...defaultButtonStyles, + addPrimaryActionToDropdown: false, + actions: rest, + contextMenuProvider: this._contextMenuService + }); + } + btn.label = first.label; + this._store.add(btn.onDidClick(() => first.run())); + secondary = true; + } + } + + dispose(): void { + this._buttonBar.dispose(); + this._store.dispose(); + } + + get domNode() { + return this._domNode.root; + } +} diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts deleted file mode 100644 index 85d9762dc22..00000000000 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts +++ /dev/null @@ -1,532 +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 { Dimension, getWindow, h, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; -import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; -import { assertType } from 'vs/base/common/types'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget'; -import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; -import { EditorOption, IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { Range } from 'vs/editor/common/core/range'; -import { IModelDecorationOptions, ITextModel } from 'vs/editor/common/model'; -import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; -import * as editorColorRegistry from 'vs/editor/common/core/editorColorRegistry'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { INLINE_CHAT_ID, inlineChatDiffInserted, inlineChatDiffRemoved, inlineChatRegionHighlight } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; -import { LineRange } from 'vs/editor/common/core/lineRange'; -import { Position } from 'vs/editor/common/core/position'; -import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; -import { IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; -import { ILogService } from 'vs/platform/log/common/log'; -import { invertLineRange, asRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; -import { ResourceLabel } from 'vs/workbench/browser/labels'; -import { FileKind } from 'vs/platform/files/common/files'; -import { HunkInformation, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; -import { FoldingController } from 'vs/editor/contrib/folding/browser/folding'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { generateUuid } from 'vs/base/common/uuid'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { ButtonBar, IButton } from 'vs/base/browser/ui/button/button'; -import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles'; -import { SaveReason, SideBySideEditor } from 'vs/workbench/common/editor'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IAction, toAction } from 'vs/base/common/actions'; -import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; -import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; -import { Codicon } from 'vs/base/common/codicons'; -import { TAB_ACTIVE_MODIFIED_BORDER } from 'vs/workbench/common/theme'; -import { localize } from 'vs/nls'; -import { Event } from 'vs/base/common/event'; - -export class InlineChatLivePreviewWidget extends ZoneWidget { - - private readonly _hideId = `overlayDiff:${generateUuid()}`; - - private readonly _elements = h('div.inline-chat-diff-widget@domNode'); - - private readonly _decorationCollection: IEditorDecorationsCollection; - private readonly _diffEditor: DiffEditorWidget; - - private _dim: Dimension | undefined; - private _isVisible: boolean = false; - - constructor( - editor: ICodeEditor, - private readonly _session: Session, - options: IDiffEditorOptions, - onDidChangeDiff: (() => void) | undefined, - @IInstantiationService instantiationService: IInstantiationService, - @IThemeService themeService: IThemeService, - @ILogService private readonly _logService: ILogService, - @IAccessibilityService private readonly accessibilityService: IAccessibilityService, - ) { - super(editor, { showArrow: false, showFrame: false, isResizeable: false, isAccessible: true, allowUnlimitedHeight: true, showInHiddenAreas: true, keepEditorSelection: true, ordinal: 10000 + 1 }); - super.create(); - assertType(editor.hasModel()); - - this._decorationCollection = editor.createDecorationsCollection(); - - const diffContributions = EditorExtensionsRegistry - .getEditorContributions() - .filter(c => c.id !== INLINE_CHAT_ID && c.id !== FoldingController.ID); - - this._diffEditor = instantiationService.createInstance(EmbeddedDiffEditorWidget, this._elements.domNode, { - scrollbar: { useShadows: false, alwaysConsumeMouseWheel: false, ignoreHorizontalScrollbarInContentHeight: true, }, - scrollBeyondLastLine: false, - renderMarginRevertIcon: true, - renderOverviewRuler: false, - rulers: undefined, - overviewRulerBorder: undefined, - overviewRulerLanes: 0, - diffAlgorithm: 'advanced', - splitViewDefaultRatio: 0.35, - padding: { top: 0, bottom: 0 }, - folding: false, - diffCodeLens: false, - stickyScroll: { enabled: false }, - minimap: { enabled: false }, - isInEmbeddedEditor: true, - useInlineViewWhenSpaceIsLimited: false, - overflowWidgetsDomNode: editor.getOverflowWidgetsDomNode(), - onlyShowAccessibleDiffViewer: this.accessibilityService.isScreenReaderOptimized(), - ...options - }, { - originalEditor: { contributions: diffContributions }, - modifiedEditor: { contributions: diffContributions } - }, editor); - - this._disposables.add(this._diffEditor); - this._diffEditor.setModel({ original: this._session.textModel0, modified: editor.getModel() }); - this._diffEditor.updateOptions({ - lineDecorationsWidth: editor.getLayoutInfo().decorationsWidth - }); - - if (onDidChangeDiff) { - this._disposables.add(this._diffEditor.onDidUpdateDiff(() => { onDidChangeDiff(); })); - - const render = this._disposables.add(new MutableDisposable()); - this._disposables.add(this._diffEditor.onDidContentSizeChange(e => { - if (!this._isVisible || !e.contentHeightChanged) { - return; - } - render.value = runAtThisOrScheduleAtNextAnimationFrame(getWindow(this._diffEditor.getContainerDomNode()), () => { - const lineHeight = this.editor.getOption(EditorOption.lineHeight); - const heightInLines = e.contentHeight / lineHeight; - this._logService.debug(`[IE] relaying with ${heightInLines} lines height`); - this._relayout(heightInLines); - }); - })); - } - - - const doStyle = () => { - const theme = themeService.getColorTheme(); - const overrides: [target: string, source: string][] = [ - [colorRegistry.editorBackground, inlineChatRegionHighlight], - [editorColorRegistry.editorGutter, inlineChatRegionHighlight], - [colorRegistry.diffInsertedLine, inlineChatDiffInserted], - [colorRegistry.diffInserted, inlineChatDiffInserted], - [colorRegistry.diffRemovedLine, inlineChatDiffRemoved], - [colorRegistry.diffRemoved, inlineChatDiffRemoved], - ]; - - for (const [target, source] of overrides) { - const value = theme.getColor(source); - if (value) { - this._elements.domNode.style.setProperty(colorRegistry.asCssVariableName(target), String(value)); - } - } - }; - doStyle(); - this._disposables.add(themeService.onDidColorThemeChange(doStyle)); - } - - - protected override _fillContainer(container: HTMLElement): void { - container.appendChild(this._elements.domNode); - } - - // --- show / hide -------------------- - - get isVisible(): boolean { - return this._isVisible; - } - - override hide(): void { - this._decorationCollection.clear(); - this._cleanupFullDiff(); - super.hide(); - this._isVisible = false; - } - - override show(): void { - throw new Error('use showForChanges'); - } - - showForChanges(hunk: HunkInformation): void { - const hasFocus = this._diffEditor.hasTextFocus(); - this._isVisible = true; - - const onlyInserts = hunk.isInsertion(); - - if (onlyInserts || this._session.textModel0.getValueLength() === 0) { - // no change or changes to an empty file - this._logService.debug('[IE] livePreview-mode: no diff'); - this._cleanupFullDiff(); - this._renderInsertWithHighlight(hunk); - } else { - // complex changes - this._logService.debug('[IE] livePreview-mode: full diff'); - this._decorationCollection.clear(); - this._renderChangesWithFullDiff(hunk); - } - - // TODO@jrieken find a better fix for this. this is the challenge: - // the `_updateFromChanges` method invokes show of the zone widget which removes and adds the - // zone and overlay parts. this dettaches and reattaches the dom nodes which means they lose - // focus - if (hasFocus) { - this._diffEditor.focus(); - } - } - - private _renderInsertWithHighlight(hunk: HunkInformation) { - assertType(this.editor.hasModel()); - - const options: IModelDecorationOptions = { - description: 'inline-chat-insert', - showIfCollapsed: false, - isWholeLine: true, - className: 'inline-chat-lines-inserted-range', - }; - - this._decorationCollection.set([{ - range: hunk.getRangesN()[0], - options - }]); - } - - // --- full diff - - private _renderChangesWithFullDiff(hunk: HunkInformation) { - assertType(this.editor.hasModel()); - - const ranges = this._computeHiddenRanges(this._session.textModelN, hunk); - - this._hideEditorRanges(this.editor, [ranges.modifiedHidden]); - this._hideEditorRanges(this._diffEditor.getOriginalEditor(), ranges.originalDiffHidden); - this._hideEditorRanges(this._diffEditor.getModifiedEditor(), ranges.modifiedDiffHidden); - - // this._diffEditor.revealLine(ranges.modifiedHidden.startLineNumber, ScrollType.Immediate); - - const lineCountModified = ranges.modifiedHidden.length; - const lineCountOriginal = ranges.originalHidden.length; - - const heightInLines = Math.max(lineCountModified, lineCountOriginal); - - super.show(ranges.anchor, heightInLines); - this._logService.debug(`[IE] diff SHOWING at ${ranges.anchor} with ${heightInLines} (approx) lines height`); - } - - private _cleanupFullDiff() { - this.editor.setHiddenAreas([], this._hideId); - this._diffEditor.getOriginalEditor().setHiddenAreas([], this._hideId); - this._diffEditor.getModifiedEditor().setHiddenAreas([], this._hideId); - super.hide(); - this._isVisible = false; - } - - private _computeHiddenRanges(model: ITextModel, hunk: HunkInformation) { - - - const modifiedLineRange = LineRange.fromRangeInclusive(hunk.getRangesN()[0]); - let originalLineRange = LineRange.fromRangeInclusive(hunk.getRanges0()[0]); - if (originalLineRange.isEmpty) { - originalLineRange = new LineRange(originalLineRange.startLineNumber, originalLineRange.endLineNumberExclusive + 1); - } - - const originalDiffHidden = invertLineRange(originalLineRange, this._session.textModel0); - const modifiedDiffHidden = invertLineRange(modifiedLineRange, model); - - return { - originalHidden: originalLineRange, - originalDiffHidden, - modifiedHidden: modifiedLineRange, - modifiedDiffHidden, - anchor: new Position(modifiedLineRange.startLineNumber - 1, 1) - }; - } - - private _hideEditorRanges(editor: ICodeEditor, lineRanges: LineRange[]): void { - assertType(editor.hasModel()); - - lineRanges = lineRanges.filter(range => !range.isEmpty); - if (lineRanges.length === 0) { - // todo? - this._logService.debug(`[IE] diff NOTHING to hide for ${editor.getId()} with ${String(editor.getModel()?.uri)}`); - return; - } - - let hiddenRanges: Range[]; - const hiddenLinesCount = lineRanges.reduce((p, c) => p + c.length, 0); // assumes no overlap - if (hiddenLinesCount >= editor.getModel().getLineCount()) { - // TODO: not every line can be hidden, keep the first line around - hiddenRanges = [editor.getModel().getFullModelRange().delta(1)]; - } else { - hiddenRanges = lineRanges.map(lr => asRange(lr, editor.getModel())); - } - editor.setHiddenAreas(hiddenRanges, this._hideId); - this._logService.debug(`[IE] diff HIDING ${hiddenRanges} for ${editor.getId()} with ${String(editor.getModel()?.uri)}`); - } - - protected override revealRange(range: Range, isLastLine: boolean): void { - // ignore - } - - // --- layout ------------------------- - - protected override _onWidth(widthInPixel: number): void { - if (this._dim) { - this._doLayout(this._dim.height, widthInPixel); - } - } - - protected override _doLayout(heightInPixel: number, widthInPixel: number): void { - const newDim = new Dimension(widthInPixel, heightInPixel); - if (!Dimension.equals(this._dim, newDim)) { - this._dim = newDim; - this._diffEditor.layout(this._dim.with(undefined, this._dim.height)); - this._logService.debug('[IE] diff LAYOUT', this._dim); - } - } -} - - -export class InlineChatFileCreatePreviewWidget extends ZoneWidget { - - private static TitleHeight = 35; - - private readonly _elements = h('div.inline-chat-newfile-widget@domNode', [ - h('div.title@title', [ - h('span.name.show-file-icons@name'), - h('span.detail@detail'), - ]), - h('div.editor@editor'), - ]); - - private readonly _name: ResourceLabel; - private readonly _previewEditor: ICodeEditor; - private readonly _previewStore = new MutableDisposable(); - private readonly _buttonBar: ButtonBarWidget; - private _dim: Dimension | undefined; - - constructor( - parentEditor: ICodeEditor, - @IInstantiationService instaService: IInstantiationService, - @IThemeService themeService: IThemeService, - @ITextModelService private readonly _textModelResolverService: ITextModelService, - @IEditorService private readonly _editorService: IEditorService, - ) { - super(parentEditor, { - showArrow: false, - showFrame: true, - frameColor: colorRegistry.asCssVariable(TAB_ACTIVE_MODIFIED_BORDER), - frameWidth: 1, - isResizeable: true, - isAccessible: true, - showInHiddenAreas: true, - ordinal: 10000 + 2 - }); - super.create(); - - this._name = instaService.createInstance(ResourceLabel, this._elements.name, { supportIcons: true }); - this._elements.detail.appendChild(renderIcon(Codicon.circleFilled)); - - const contributions = EditorExtensionsRegistry - .getEditorContributions() - .filter(c => c.id !== INLINE_CHAT_ID); - - this._previewEditor = instaService.createInstance(EmbeddedCodeEditorWidget, this._elements.editor, { - scrollBeyondLastLine: false, - stickyScroll: { enabled: false }, - minimap: { enabled: false }, - scrollbar: { alwaysConsumeMouseWheel: false, useShadows: true, ignoreHorizontalScrollbarInContentHeight: true, }, - }, { isSimpleWidget: true, contributions }, parentEditor); - - const doStyle = () => { - const theme = themeService.getColorTheme(); - const overrides: [target: string, source: string][] = [ - [colorRegistry.editorBackground, inlineChatRegionHighlight], - [editorColorRegistry.editorGutter, inlineChatRegionHighlight], - ]; - - for (const [target, source] of overrides) { - const value = theme.getColor(source); - if (value) { - this._elements.domNode.style.setProperty(colorRegistry.asCssVariableName(target), String(value)); - } - } - }; - doStyle(); - this._disposables.add(themeService.onDidColorThemeChange(doStyle)); - - this._buttonBar = instaService.createInstance(ButtonBarWidget); - this._elements.title.appendChild(this._buttonBar.domNode); - } - - override dispose(): void { - this._name.dispose(); - this._buttonBar.dispose(); - this._previewEditor.dispose(); - this._previewStore.dispose(); - super.dispose(); - } - - protected override _fillContainer(container: HTMLElement): void { - container.appendChild(this._elements.domNode); - } - - override show(): void { - throw new Error('Use showFileCreation'); - } - - async showCreation(where: Position, untitledTextModel: IUntitledTextEditorModel): Promise { - - const store = new DisposableStore(); - this._previewStore.value = store; - - this._name.element.setFile(untitledTextModel.resource, { - fileKind: FileKind.FILE, - fileDecorations: { badges: true, colors: true } - }); - - const actionSave = toAction({ - id: '1', - label: localize('save', "Create"), - run: () => untitledTextModel.save({ reason: SaveReason.EXPLICIT }) - }); - const actionSaveAs = toAction({ - id: '2', - label: localize('saveAs', "Create As"), - run: async () => { - const ids = this._editorService.findEditors(untitledTextModel.resource, { supportSideBySide: SideBySideEditor.ANY }); - await this._editorService.save(ids.slice(), { saveAs: true, reason: SaveReason.EXPLICIT }); - } - }); - - this._buttonBar.update([ - [actionSave, actionSaveAs], - [(toAction({ id: '3', label: localize('discard', "Discard"), run: () => untitledTextModel.revert() }))] - ]); - - store.add(Event.any( - untitledTextModel.onDidRevert, - untitledTextModel.onDidSave, - untitledTextModel.onDidChangeDirty, - untitledTextModel.onWillDispose - )(() => this.hide())); - - await untitledTextModel.resolve(); - - const ref = await this._textModelResolverService.createModelReference(untitledTextModel.resource); - store.add(ref); - - const model = ref.object.textEditorModel; - this._previewEditor.setModel(model); - - const lineHeight = this.editor.getOption(EditorOption.lineHeight); - - this._elements.title.style.height = `${InlineChatFileCreatePreviewWidget.TitleHeight}px`; - const titleHightInLines = InlineChatFileCreatePreviewWidget.TitleHeight / lineHeight; - - const maxLines = Math.max(4, Math.floor((this.editor.getLayoutInfo().height / lineHeight) * .33)); - const lines = Math.min(maxLines, model.getLineCount()); - - super.show(where, titleHightInLines + lines); - } - - override hide(): void { - this._previewStore.clear(); - super.hide(); - } - - // --- layout - - protected override revealRange(range: Range, isLastLine: boolean): void { - // ignore - } - - protected override _onWidth(widthInPixel: number): void { - if (this._dim) { - this._doLayout(this._dim.height, widthInPixel); - } - } - - protected override _doLayout(heightInPixel: number, widthInPixel: number): void { - - const { lineNumbersLeft } = this.editor.getLayoutInfo(); - this._elements.title.style.marginLeft = `${lineNumbersLeft}px`; - - const newDim = new Dimension(widthInPixel, heightInPixel); - if (!Dimension.equals(this._dim, newDim)) { - this._dim = newDim; - this._previewEditor.layout(this._dim.with(undefined, this._dim.height - InlineChatFileCreatePreviewWidget.TitleHeight)); - } - } -} - - -class ButtonBarWidget { - - private readonly _domNode = h('div.buttonbar-widget'); - private readonly _buttonBar: ButtonBar; - private readonly _store = new DisposableStore(); - - constructor( - @IContextMenuService private _contextMenuService: IContextMenuService, - ) { - this._buttonBar = new ButtonBar(this.domNode); - - } - - update(allActions: IAction[][]): void { - this._buttonBar.clear(); - let secondary = false; - for (const actions of allActions) { - let btn: IButton; - const [first, ...rest] = actions; - if (!first) { - continue; - } else if (rest.length === 0) { - // single action - btn = this._buttonBar.addButton({ ...defaultButtonStyles, secondary }); - } else { - btn = this._buttonBar.addButtonWithDropdown({ - ...defaultButtonStyles, - addPrimaryActionToDropdown: false, - actions: rest, - contextMenuProvider: this._contextMenuService - }); - } - btn.label = first.label; - this._store.add(btn.onDidClick(() => first.run())); - secondary = true; - } - } - - dispose(): void { - this._buttonBar.dispose(); - this._store.dispose(); - } - - get domNode() { - return this._domNode.root; - } -} diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 63088a27ed5..30aa3f46b10 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -28,7 +28,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { Progress } from 'vs/platform/progress/common/progress'; import { SaveReason } from 'vs/workbench/common/editor'; import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; -import { InlineChatFileCreatePreviewWidget, InlineChatLivePreviewWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget'; +import { InlineChatFileCreatePreviewWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatFileCreationWidget'; import { HunkInformation, ReplyResponse, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { InlineChatZoneWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_DOCUMENT_CHANGED, InlineChatConfigKeys, overviewRulerInlineChatDiffInserted } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; @@ -231,166 +231,7 @@ export interface ProgressingEditsOptions { token: CancellationToken; } -export class LivePreviewStrategy extends EditModeStrategy { - private readonly _previewZone: Lazy; - private readonly _diffZonePool: InlineChatLivePreviewWidget[] = []; - - constructor( - session: Session, - editor: ICodeEditor, - zone: InlineChatZoneWidget, - @IInstantiationService private readonly _instaService: IInstantiationService, - ) { - super(session, editor, zone); - - this._previewZone = new Lazy(() => _instaService.createInstance(InlineChatFileCreatePreviewWidget, editor)); - } - - override dispose(): void { - for (const zone of this._diffZonePool) { - zone.hide(); - zone.dispose(); - } - this._previewZone.rawValue?.hide(); - this._previewZone.rawValue?.dispose(); - super.dispose(); - } - - async apply() { - if (this._editCount > 0) { - this._editor.pushUndoStop(); - } - if (!(this._session.lastExchange?.response instanceof ReplyResponse)) { - return; - } - const { untitledTextModel } = this._session.lastExchange.response; - if (untitledTextModel && !untitledTextModel.isDisposed() && untitledTextModel.isDirty()) { - await untitledTextModel.save({ reason: SaveReason.EXPLICIT }); - } - } - - override async undoChanges(altVersionId: number): Promise { - const { textModelN } = this._session; - await undoModelUntil(textModelN, altVersionId); - this._updateDiffZones(); - } - - override async makeChanges(edits: ISingleEditOperation[], obs: IEditObserver): Promise { - return this._makeChanges(edits, obs, undefined, undefined); - } - - override async makeProgressiveChanges(edits: ISingleEditOperation[], obs: IEditObserver, opts: ProgressingEditsOptions): Promise { - await this._makeChanges(edits, obs, opts, new Progress(() => { - this._updateDiffZones(); - })); - } - - override async renderChanges(response: ReplyResponse): Promise { - - if (response.untitledTextModel && !response.untitledTextModel.isDisposed()) { - this._previewZone.value.showCreation(this._session.wholeRange.value.getStartPosition().delta(-1), response.untitledTextModel); - } else { - this._previewZone.rawValue?.hide(); - } - - return this._updateDiffZones(); - } - - - protected _updateSummaryMessage(hunkCount: number) { - let message: string; - if (hunkCount === 0) { - message = localize('change.0', "Nothing changed"); - } else if (hunkCount === 1) { - message = localize('change.1', "1 change"); - } else { - message = localize('lines.NM', "{0} changes", hunkCount); - } - this._zone.widget.updateStatus(message); - } - - - private _updateDiffZones(): Position | undefined { - - const { hunkData } = this._session; - const hunks = hunkData.getInfo().filter(hunk => hunk.getState() === HunkState.Pending); - - if (hunks.length === 0) { - for (const zone of this._diffZonePool) { - zone.hide(); - } - - if (hunkData.getInfo().find(hunk => hunk.getState() === HunkState.Accepted)) { - this._onDidAccept.fire(); - } else { - this._onDidDiscard.fire(); - } - - return; - } - - this._updateSummaryMessage(hunks.length); - - // create enough zones - const handleDiff = () => this._updateDiffZones(); - - type Data = { position: Position; distance: number; accept: Function; discard: Function }; - let nearest: Data | undefined; - - // create enough zones - while (hunks.length > this._diffZonePool.length) { - this._diffZonePool.push(this._instaService.createInstance(InlineChatLivePreviewWidget, this._editor, this._session, {}, this._diffZonePool.length === 0 ? handleDiff : undefined)); - } - - for (let i = 0; i < hunks.length; i++) { - const hunk = hunks[i]; - this._diffZonePool[i].showForChanges(hunk); - - const modifiedRange = hunk.getRangesN()[0]; - const zoneLineNumber = this._zone.position!.lineNumber; - const distance = zoneLineNumber <= modifiedRange.startLineNumber - ? modifiedRange.startLineNumber - zoneLineNumber - : zoneLineNumber - modifiedRange.endLineNumber; - - if (!nearest || nearest.distance > distance) { - nearest = { - position: modifiedRange.getStartPosition().delta(-1), - distance, - accept: () => { - hunk.acceptChanges(); - handleDiff(); - }, - discard: () => { - hunk.discardChanges(); - handleDiff(); - } - }; - } - - } - // hide unused zones - for (let i = hunks.length; i < this._diffZonePool.length; i++) { - this._diffZonePool[i].hide(); - } - - this.acceptHunk = async () => nearest?.accept(); - this.discardHunk = async () => nearest?.discard(); - - if (nearest) { - this._zone.updatePositionAndHeight(nearest.position); - this._editor.revealPositionInCenterIfOutsideViewport(nearest.position); - } - - return nearest?.position; - } - - override hasFocus(): boolean { - return this._zone.widget.hasFocus() - || Boolean(this._previewZone.rawValue?.hasFocus()) - || this._diffZonePool.some(zone => zone.isVisible && zone.hasFocus()); - } -} type HunkDisplayData = { diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index dbf95c3bb0b..10e6097adea 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -209,9 +209,7 @@ export const overviewRulerInlineChatDiffRemoved = registerColor('editorOverviewR export const enum EditMode { Live = 'live', - Preview = 'preview', - /** @deprecated */ - LivePreview = 'livePreview', + Preview = 'preview' } Registry.as(ExtensionsMigration.ConfigurationMigration).registerConfigurationMigrations( diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index f998c0969d7..6fd5722bc72 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -512,18 +512,15 @@ suite('InteractiveChatController', function () { configurationService.setUserConfiguration('inlineChat', { mode: EditMode.Live }); await makeRequest(); - configurationService.setUserConfiguration('inlineChat', { mode: EditMode.LivePreview }); - await makeRequest(); configurationService.setUserConfiguration('inlineChat', { mode: EditMode.Preview }); await makeRequest(); - assert.strictEqual(requests.length, 3); + assert.strictEqual(requests.length, 2); assert.strictEqual(requests[0].previewDocument.toString(), model.uri.toString()); // live - assert.strictEqual(requests[1].previewDocument.toString(), model.uri.toString()); // live preview - assert.strictEqual(requests[2].previewDocument.scheme, Schemas.vscode); // preview - assert.strictEqual(requests[2].previewDocument.authority, 'inline-chat'); + assert.strictEqual(requests[1].previewDocument.scheme, Schemas.vscode); // preview + assert.strictEqual(requests[1].previewDocument.authority, 'inline-chat'); }); test('start with existing exchange', async function () { From 10ddce6696c7a66b8f6d9965529ff00c97c967db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Cie=C5=9Blak?= Date: Fri, 23 Feb 2024 16:24:50 +0100 Subject: [PATCH 0140/1175] Fix off-by-one error in rendering removals in inline edits (#205890) --- src/vs/editor/contrib/inlineEdit/browser/ghostTextWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/inlineEdit/browser/ghostTextWidget.ts b/src/vs/editor/contrib/inlineEdit/browser/ghostTextWidget.ts index da0fd80b543..298fcd4452e 100644 --- a/src/vs/editor/contrib/inlineEdit/browser/ghostTextWidget.ts +++ b/src/vs/editor/contrib/inlineEdit/browser/ghostTextWidget.ts @@ -176,7 +176,7 @@ export class GhostTextWidget extends Disposable { } else { const lines = uiState.range.endLineNumber - uiState.range.startLineNumber; - for (let i = 0; i <= lines; i++) { + for (let i = 0; i < lines; i++) { const line = uiState.range.startLineNumber + i; const firstNonWhitespace = uiState.targetTextModel.getLineFirstNonWhitespaceColumn(line); const lastNonWhitespace = uiState.targetTextModel.getLineLastNonWhitespaceColumn(line); From 18e68638bdb98413760e1b7a04af30e66a0d66ea Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 23 Feb 2024 16:47:24 +0100 Subject: [PATCH 0141/1175] Fixes #204948 --- .../heuristicSequenceOptimizations.ts | 2 +- .../node/diffing/fixtures/issue-204948/1.txt | 9 ++++++++ .../node/diffing/fixtures/issue-204948/2.txt | 9 ++++++++ .../issue-204948/advanced.expected.diff.json | 22 +++++++++++++++++++ .../issue-204948/legacy.expected.diff.json | 22 +++++++++++++++++++ 5 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 src/vs/editor/test/node/diffing/fixtures/issue-204948/1.txt create mode 100644 src/vs/editor/test/node/diffing/fixtures/issue-204948/2.txt create mode 100644 src/vs/editor/test/node/diffing/fixtures/issue-204948/advanced.expected.diff.json create mode 100644 src/vs/editor/test/node/diffing/fixtures/issue-204948/legacy.expected.diff.json diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts index 08efd578136..fddfb1e0c61 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts @@ -247,7 +247,7 @@ export function extendDiffsToEntireWordIfAppropriate(sequence1: LinesSliceCharSe while (equalMappings.length > 0) { const next = equalMappings[0]; - const intersects = next.seq1Range.intersects(w1) || next.seq2Range.intersects(w2); + const intersects = next.seq1Range.intersects(w.seq1Range) || next.seq2Range.intersects(w.seq2Range); if (!intersects) { break; } diff --git a/src/vs/editor/test/node/diffing/fixtures/issue-204948/1.txt b/src/vs/editor/test/node/diffing/fixtures/issue-204948/1.txt new file mode 100644 index 00000000000..42f5b92add2 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/issue-204948/1.txt @@ -0,0 +1,9 @@ + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" + integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/issue-204948/2.txt b/src/vs/editor/test/node/diffing/fixtures/issue-204948/2.txt new file mode 100644 index 00000000000..2b5867f0165 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/issue-204948/2.txt @@ -0,0 +1,9 @@ + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" + integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/issue-204948/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/issue-204948/advanced.expected.diff.json new file mode 100644 index 00000000000..4dd454f4a45 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/issue-204948/advanced.expected.diff.json @@ -0,0 +1,22 @@ +{ + "original": { + "content": " \"@babel/types\" \"^7.22.15\"\n\n\"@babel/traverse@^7.23.9\":\n version \"7.23.9\"\n resolved \"https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305\"\n integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==\n dependencies:\n \"@babel/code-frame\" \"^7.23.5\"\n \"@babel/generator\" \"^7.23.6\"", + "fileName": "./1.txt" + }, + "modified": { + "content": " \"@babel/types\" \"^7.22.15\"\n\n\"@babel/traverse@^7.23.9\":\n version \"7.23.9\"\n resolved \"https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305\"\n integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==\n dependencies:\n \"@babel/code-frame\" \"^7.23.5\"\n \"@babel/generator\" \"^7.23.6\"", + "fileName": "./2.txt" + }, + "diffs": [ + { + "originalRange": "[6,7)", + "modifiedRange": "[6,7)", + "innerChanges": [ + { + "originalRange": "[6,20 -> 7,1]", + "modifiedRange": "[6,20 -> 7,1]" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/issue-204948/legacy.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/issue-204948/legacy.expected.diff.json new file mode 100644 index 00000000000..97e5b8d4e12 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/issue-204948/legacy.expected.diff.json @@ -0,0 +1,22 @@ +{ + "original": { + "content": " \"@babel/types\" \"^7.22.15\"\n\n\"@babel/traverse@^7.23.9\":\n version \"7.23.9\"\n resolved \"https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305\"\n integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==\n dependencies:\n \"@babel/code-frame\" \"^7.23.5\"\n \"@babel/generator\" \"^7.23.6\"", + "fileName": "./1.txt" + }, + "modified": { + "content": " \"@babel/types\" \"^7.22.15\"\n\n\"@babel/traverse@^7.23.9\":\n version \"7.23.9\"\n resolved \"https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305\"\n integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==\n dependencies:\n \"@babel/code-frame\" \"^7.23.5\"\n \"@babel/generator\" \"^7.23.6\"", + "fileName": "./2.txt" + }, + "diffs": [ + { + "originalRange": "[6,7)", + "modifiedRange": "[6,7)", + "innerChanges": [ + { + "originalRange": "[6,20 -> 6,105]", + "modifiedRange": "[6,20 -> 6,105]" + } + ] + } + ] +} \ No newline at end of file From c6eed5d1b50413f7e6de8c5837e7095321d27050 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 23 Feb 2024 16:53:38 +0100 Subject: [PATCH 0142/1175] Stricter assert --- .../defaultLinesDiffComputer.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts index e2de212aa40..b7c34e07604 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts @@ -13,11 +13,11 @@ import { DateTimeout, ITimeout, InfiniteTimeout, SequenceDiff } from 'vs/editor/ import { DynamicProgrammingDiffing } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing'; import { MyersDiffAlgorithm } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm'; import { computeMovedLines } from 'vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines'; -import { extendDiffsToEntireWordIfAppropriate, optimizeSequenceDiffs, removeVeryShortMatchingLinesBetweenDiffs, removeVeryShortMatchingTextBetweenLongDiffs, removeShortMatches } from 'vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations'; +import { extendDiffsToEntireWordIfAppropriate, optimizeSequenceDiffs, removeShortMatches, removeVeryShortMatchingLinesBetweenDiffs, removeVeryShortMatchingTextBetweenLongDiffs } from 'vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations'; +import { LineSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/lineSequence'; +import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence'; import { ILinesDiffComputer, ILinesDiffComputerOptions, LinesDiff, MovedText } from 'vs/editor/common/diff/linesDiffComputer'; import { DetailedLineRangeMapping, RangeMapping } from '../rangeMapping'; -import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence'; -import { LineSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/lineSequence'; export class DefaultLinesDiffComputer implements ILinesDiffComputer { private readonly dynamicProgrammingDiffing = new DynamicProgrammingDiffing(); @@ -256,8 +256,11 @@ export function lineRangeMappingFromRangeMappings(alignments: RangeMapping[], or } assertFn(() => { - if (!dontAssertStartLine) { - if (changes.length > 0 && changes[0].original.startLineNumber !== changes[0].modified.startLineNumber) { + if (!dontAssertStartLine && changes.length > 0) { + if (changes[0].modified.startLineNumber !== changes[0].original.startLineNumber) { + return false; + } + if (modifiedLines.length - changes[changes.length - 1].modified.endLineNumberExclusive !== originalLines.length - changes[changes.length - 1].original.endLineNumberExclusive) { return false; } } From cdb1a962ccd40d6d03132e7974a835c98f6e82e6 Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Fri, 23 Feb 2024 16:46:47 +0100 Subject: [PATCH 0143/1175] rename controller: don't throw on cancellation & more type-safety to avoid `MessageController#showMessage` throwing --- src/vs/editor/contrib/rename/browser/rename.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/rename/browser/rename.ts b/src/vs/editor/contrib/rename/browser/rename.ts index 8b7bc9b51b8..cb4f58e1073 100644 --- a/src/vs/editor/contrib/rename/browser/rename.ts +++ b/src/vs/editor/contrib/rename/browser/rename.ts @@ -6,7 +6,8 @@ import { alert } from 'vs/base/browser/ui/aria/aria'; import { raceCancellation } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { onUnexpectedError } from 'vs/base/common/errors'; +import { CancellationError, onUnexpectedError } from 'vs/base/common/errors'; +import { isMarkdownString } from 'vs/base/common/htmlContent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { assertType } from 'vs/base/common/types'; @@ -192,9 +193,15 @@ class RenameController implements IEditorContribution { this._progressService.showWhile(resolveLocationOperation, 250); loc = await resolveLocationOperation; trace('resolved rename location'); - } catch (e) { - trace('resolve rename location failed', JSON.stringify(e, null, '\t')); - MessageController.get(this.editor)?.showMessage(e || nls.localize('resolveRenameLocationFailed', "An unknown error occurred while resolving rename location"), position); + } catch (e: unknown) { + if (e instanceof CancellationError) { + trace('resolve rename location cancelled', JSON.stringify(e, null, '\t')); + } else { + trace('resolve rename location failed', e instanceof Error ? e : JSON.stringify(e, null, '\t')); + if (typeof e === 'string' || isMarkdownString(e)) { + MessageController.get(this.editor)?.showMessage(e || nls.localize('resolveRenameLocationFailed', "An unknown error occurred while resolving rename location"), position); + } + } return undefined; } finally { From 12997e68fd1620ff0cb2cbfae3f7390a0c1e9e13 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 23 Feb 2024 18:14:21 +0100 Subject: [PATCH 0144/1175] Fixes #196084 (#206100) --- extensions/git/src/commands.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 7f7f769fec5..01a8434c448 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1194,7 +1194,7 @@ export class CommandCenter { const activeTextEditor = window.activeTextEditor; // Must extract these now because opening a new document will change the activeTextEditor reference - const previousVisibleRange = activeTextEditor?.visibleRanges[0]; + const previousVisibleRanges = activeTextEditor?.visibleRanges; const previousURI = activeTextEditor?.document.uri; const previousSelection = activeTextEditor?.selection; @@ -1225,8 +1225,13 @@ export class CommandCenter { opts.selection = previousSelection; const editor = await window.showTextDocument(document, opts); // This should always be defined but just in case - if (previousVisibleRange) { - editor.revealRange(previousVisibleRange); + if (previousVisibleRanges && previousVisibleRanges.length > 0) { + let rangeToReveal = previousVisibleRanges[0]; + if (previousSelection && previousVisibleRanges.length > 1) { + // In case of multiple visible ranges, find the one that intersects with the selection + rangeToReveal = previousVisibleRanges.find(r => r.intersection(previousSelection)) ?? rangeToReveal; + } + editor.revealRange(rangeToReveal); } } } From bd4ba72a33caa9f52882bb695880790796e44f51 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 23 Feb 2024 18:52:11 +0100 Subject: [PATCH 0145/1175] Fixes #199290 (#206112) --- .../features/hideUnchangedRegionsFeature.ts | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts index 6767a375a3c..248f2b46d81 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts @@ -59,27 +59,25 @@ export class HideUnchangedRegionsFeature extends Disposable { super(); this._register(this._editors.original.onDidChangeCursorPosition(e => { - if (e.reason === CursorChangeReason.Explicit) { - const m = this._diffModel.get(); - transaction(tx => { - for (const s of this._editors.original.getSelections() || []) { - m?.ensureOriginalLineIsVisible(s.getStartPosition().lineNumber, RevealPreference.FromCloserSide, tx); - m?.ensureOriginalLineIsVisible(s.getEndPosition().lineNumber, RevealPreference.FromCloserSide, tx); - } - }); - } + if (e.reason === CursorChangeReason.ContentFlush) { return; } + const m = this._diffModel.get(); + transaction(tx => { + for (const s of this._editors.original.getSelections() || []) { + m?.ensureOriginalLineIsVisible(s.getStartPosition().lineNumber, RevealPreference.FromCloserSide, tx); + m?.ensureOriginalLineIsVisible(s.getEndPosition().lineNumber, RevealPreference.FromCloserSide, tx); + } + }); })); this._register(this._editors.modified.onDidChangeCursorPosition(e => { - if (e.reason === CursorChangeReason.Explicit) { - const m = this._diffModel.get(); - transaction(tx => { - for (const s of this._editors.modified.getSelections() || []) { - m?.ensureModifiedLineIsVisible(s.getStartPosition().lineNumber, RevealPreference.FromCloserSide, tx); - m?.ensureModifiedLineIsVisible(s.getEndPosition().lineNumber, RevealPreference.FromCloserSide, tx); - } - }); - } + if (e.reason === CursorChangeReason.ContentFlush) { return; } + const m = this._diffModel.get(); + transaction(tx => { + for (const s of this._editors.modified.getSelections() || []) { + m?.ensureModifiedLineIsVisible(s.getStartPosition().lineNumber, RevealPreference.FromCloserSide, tx); + m?.ensureModifiedLineIsVisible(s.getEndPosition().lineNumber, RevealPreference.FromCloserSide, tx); + } + }); })); const unchangedRegions = this._diffModel.map((m, reader) => { From 436af204d46a337b78b94423ba66fccefd93c7f3 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 23 Feb 2024 11:00:23 -0700 Subject: [PATCH 0146/1175] Bump tas client (#206106) --- extensions/github-authentication/package.json | 2 +- extensions/github-authentication/yarn.lock | 48 +++--------- .../typescript-language-features/package.json | 2 +- .../typescript-language-features/yarn.lock | 77 +++---------------- 4 files changed, 20 insertions(+), 109 deletions(-) diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index dc26d5c07e8..d55e8dcfd03 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -61,7 +61,7 @@ "dependencies": { "node-fetch": "2.6.7", "@vscode/extension-telemetry": "^0.9.0", - "vscode-tas-client": "^0.1.47" + "vscode-tas-client": "^0.1.84" }, "devDependencies": { "@types/mocha": "^9.1.1", diff --git a/extensions/github-authentication/yarn.lock b/extensions/github-authentication/yarn.lock index a1c68b8d5a6..724b304c53e 100644 --- a/extensions/github-authentication/yarn.lock +++ b/extensions/github-authentication/yarn.lock @@ -132,15 +132,6 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -axios@^1.6.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" - integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== - dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -153,11 +144,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -follow-redirects@^1.15.0: - version "1.15.4" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" - integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== - form-data@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" @@ -167,15 +153,6 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - mime-db@1.44.0: version "1.44.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" @@ -195,29 +172,22 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - -tas-client@0.1.73: - version "0.1.73" - resolved "https://registry.yarnpkg.com/tas-client/-/tas-client-0.1.73.tgz#2dacf68547a37989ef1554c6510dc108a1ea7a71" - integrity sha512-UDdUF9kV2hYdlv+7AgqP2kXarVSUhjK7tg1BUflIRGEgND0/QoNpN64rcEuhEcM8AIbW65yrCopJWqRhLZ3m8w== - dependencies: - axios "^1.6.1" +tas-client@0.2.33: + version "0.2.33" + resolved "https://registry.yarnpkg.com/tas-client/-/tas-client-0.2.33.tgz#451bf114a8a64748030ce4068ab7d079958402e6" + integrity sha512-V+uqV66BOQnWxvI6HjDnE4VkInmYZUQ4dgB7gzaDyFyFSK1i1nF/j7DpS9UbQAgV9NaF1XpcyuavnM1qOeiEIg== tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= -vscode-tas-client@^0.1.47: - version "0.1.75" - resolved "https://registry.yarnpkg.com/vscode-tas-client/-/vscode-tas-client-0.1.75.tgz#771780a9a178163028299f52d41973300060dd38" - integrity sha512-/+ALFWPI4U3obeRvLFSt39guT7P9bZQrkmcLoiS+2HtzJ/7iPKNt5Sj+XTiitGlPYVFGFc0plxX8AAp6Uxs0xQ== +vscode-tas-client@^0.1.84: + version "0.1.84" + resolved "https://registry.yarnpkg.com/vscode-tas-client/-/vscode-tas-client-0.1.84.tgz#906bdcfd8c9e1dc04321d6bc0335184f9119968e" + integrity sha512-rUTrUopV+70hvx1hW5ebdw1nd6djxubkLvVxjGdyD/r5v/wcVF41LIfiAtbm5qLZDtQdsMH1IaCuDoluoIa88w== dependencies: - tas-client "0.1.73" + tas-client "0.2.33" webidl-conversions@^3.0.0: version "3.0.1" diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 9a5f8ff1f86..34dad264553 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -45,7 +45,7 @@ "@vscode/ts-package-manager": "^0.0.2", "jsonc-parser": "^3.2.0", "semver": "7.5.2", - "vscode-tas-client": "^0.1.63", + "vscode-tas-client": "^0.1.84", "vscode-uri": "^3.0.3" }, "devDependencies": { diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index 482239a3cbb..bc72fe4cb8b 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -140,46 +140,6 @@ resolved "https://registry.yarnpkg.com/@vscode/ts-package-manager/-/ts-package-manager-0.0.2.tgz#d1cade5ff0d01da8c5b5b00bf79d80e7156771cf" integrity sha512-cXPxGbPVTkEQI8mUiWYUwB6j3ga6M9i7yubUOCrjgZ01GeZPMSnaWRprfJ09uuy81wJjY2gfHgLsOgwrGvUBTw== -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -axios@^1.6.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" - integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== - dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - -follow-redirects@^1.15.0: - version "1.15.4" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" - integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== - -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - jsonc-parser@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" @@ -192,23 +152,6 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - semver@7.5.2: version "7.5.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" @@ -216,19 +159,17 @@ semver@7.5.2: dependencies: lru-cache "^6.0.0" -tas-client@0.1.73: - version "0.1.73" - resolved "https://registry.yarnpkg.com/tas-client/-/tas-client-0.1.73.tgz#2dacf68547a37989ef1554c6510dc108a1ea7a71" - integrity sha512-UDdUF9kV2hYdlv+7AgqP2kXarVSUhjK7tg1BUflIRGEgND0/QoNpN64rcEuhEcM8AIbW65yrCopJWqRhLZ3m8w== - dependencies: - axios "^1.6.1" +tas-client@0.2.33: + version "0.2.33" + resolved "https://registry.yarnpkg.com/tas-client/-/tas-client-0.2.33.tgz#451bf114a8a64748030ce4068ab7d079958402e6" + integrity sha512-V+uqV66BOQnWxvI6HjDnE4VkInmYZUQ4dgB7gzaDyFyFSK1i1nF/j7DpS9UbQAgV9NaF1XpcyuavnM1qOeiEIg== -vscode-tas-client@^0.1.63: - version "0.1.75" - resolved "https://registry.yarnpkg.com/vscode-tas-client/-/vscode-tas-client-0.1.75.tgz#771780a9a178163028299f52d41973300060dd38" - integrity sha512-/+ALFWPI4U3obeRvLFSt39guT7P9bZQrkmcLoiS+2HtzJ/7iPKNt5Sj+XTiitGlPYVFGFc0plxX8AAp6Uxs0xQ== +vscode-tas-client@^0.1.84: + version "0.1.84" + resolved "https://registry.yarnpkg.com/vscode-tas-client/-/vscode-tas-client-0.1.84.tgz#906bdcfd8c9e1dc04321d6bc0335184f9119968e" + integrity sha512-rUTrUopV+70hvx1hW5ebdw1nd6djxubkLvVxjGdyD/r5v/wcVF41LIfiAtbm5qLZDtQdsMH1IaCuDoluoIa88w== dependencies: - tas-client "0.1.73" + tas-client "0.2.33" vscode-uri@3.0.3: version "3.0.3" From 3bee3f48a5880e65c7525be0ff77042b7d295bdf Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 23 Feb 2024 10:30:11 -0800 Subject: [PATCH 0147/1175] fix: cwd not escaping when running wt.exe externally (#206113) We actually already set the cwd when spawning wt.exe, so we can use just `.` to reference it and avoid dealing with escaping entirely. --- .../platform/externalTerminal/node/externalTerminalService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/externalTerminal/node/externalTerminalService.ts b/src/vs/platform/externalTerminal/node/externalTerminalService.ts index 9fd38929726..a8df823266a 100644 --- a/src/vs/platform/externalTerminal/node/externalTerminalService.ts +++ b/src/vs/platform/externalTerminal/node/externalTerminalService.ts @@ -107,7 +107,7 @@ export class WindowsExternalTerminalService extends ExternalTerminalService impl // prefer to use the window terminal to spawn if it's available instead // of start, since that allows ctrl+c handling (#81322) spawnExec = wt; - cmdArgs = ['-d', dir || '.', exec, '/c', command]; // default dir fixes #204039 + cmdArgs = ['-d', '.', exec, '/c', command]; } else { spawnExec = WindowsExternalTerminalService.CMD; cmdArgs = ['/c', 'start', title, '/wait', exec, '/c', command]; From 25bc3dfec4e6d38d4899b98135e0bb369b6db51f Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 23 Feb 2024 10:37:02 -0800 Subject: [PATCH 0148/1175] fix: use `CancellationToken.None` (#206115) --- src/vs/workbench/api/browser/mainThreadShare.ts | 4 ++-- .../contrib/editSessions/test/browser/editSessions.test.ts | 4 ++-- src/vs/workbench/contrib/share/browser/share.contribution.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadShare.ts b/src/vs/workbench/api/browser/mainThreadShare.ts index 1974180b331..d517c23c906 100644 --- a/src/vs/workbench/api/browser/mainThreadShare.ts +++ b/src/vs/workbench/api/browser/mainThreadShare.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { ExtHostContext, ExtHostShareShape, IDocumentFilterDto, MainContext, MainThreadShareShape } from 'vs/workbench/api/common/extHost.protocol'; @@ -31,7 +31,7 @@ export class MainThreadShare implements MainThreadShareShape { selector, priority, provideShare: async (item: IShareableItem) => { - const result = await this.proxy.$provideShare(handle, item, new CancellationTokenSource().token); + const result = await this.proxy.$provideShare(handle, item, CancellationToken.None); return typeof result === 'string' ? result : URI.revive(result); } }; diff --git a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts index 4ad6b2fe900..c6be96b073d 100644 --- a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts +++ b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts @@ -37,7 +37,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IDialogService, IPrompt } from 'vs/platform/dialogs/common/dialogs'; import { IEditorService, ISaveAllEditorsOptions } from 'vs/workbench/services/editor/common/editorService'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @@ -213,7 +213,7 @@ suite('Edit session sync', () => { // Create root folder await fileService.createFolder(folderUri); - await editSessionsContribution.storeEditSession(true, new CancellationTokenSource().token); + await editSessionsContribution.storeEditSession(true, CancellationToken.None); // Verify that we did not attempt to write the edit session assert.equal(writeStub.called, false); diff --git a/src/vs/workbench/contrib/share/browser/share.contribution.ts b/src/vs/workbench/contrib/share/browser/share.contribution.ts index ae071141610..5bdf93d7236 100644 --- a/src/vs/workbench/contrib/share/browser/share.contribution.ts +++ b/src/vs/workbench/contrib/share/browser/share.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./share'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; @@ -110,7 +110,7 @@ class ShareWorkbenchContribution { const result = await progressService.withProgress({ location: ProgressLocation.Window, detail: localize('generating link', 'Generating link...') - }, async () => shareService.provideShare({ resourceUri, selection }, new CancellationTokenSource().token)); + }, async () => shareService.provideShare({ resourceUri, selection }, CancellationToken.None)); if (result) { const uriText = result.toString(); From ec72162ee60247944ec2faa16a08ce0ac580de76 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 23 Feb 2024 11:45:43 -0700 Subject: [PATCH 0149/1175] Deregister before registering to prevent view collisions (#206117) --- src/vs/workbench/contrib/files/browser/explorerViewlet.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index 9785e04a5b1..0a79bee3fc6 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -94,12 +94,12 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor } } - if (viewDescriptorsToRegister.length) { - viewsRegistry.registerViews(viewDescriptorsToRegister, VIEW_CONTAINER); - } if (viewDescriptorsToDeregister.length) { viewsRegistry.deregisterViews(viewDescriptorsToDeregister, VIEW_CONTAINER); } + if (viewDescriptorsToRegister.length) { + viewsRegistry.registerViews(viewDescriptorsToRegister, VIEW_CONTAINER); + } mark('code/didRegisterExplorerViews'); } From ef1836b20c47096ef173f82783cfb01568e82f77 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 23 Feb 2024 11:37:30 -0800 Subject: [PATCH 0150/1175] Exit early when object is disposed Fixes #206116 --- .../stickyScroll/browser/terminalStickyScrollOverlay.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts index 8868f63beef..4dc0908e54e 100644 --- a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts @@ -95,6 +95,9 @@ export class TerminalStickyScrollOverlay extends Disposable { // Eagerly create the overlay xtermCtor.then(ctor => { + if (this._store.isDisposed) { + return; + } this._stickyScrollOverlay = this._register(new ctor({ rows: 1, cols: this._xterm.raw.cols, From 45e763b40ca1e5d45e92b66ad04d02f320f0a64b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 23 Feb 2024 21:52:10 +0100 Subject: [PATCH 0151/1175] Configure web server base path (#202491) * add support for custom root URL path through command line option `--route-base-url-path` * fix CI/CD errors * refine path joining in `RemoteAuthoritiesImpl` * revert changes to `.gitignore` * avoid RemotePaths global * polish. Option is now called `server-base-path` * revert uri changes * remove unnecessary file * remove unnecessary new line * revert address port change --------- Co-authored-by: sysadmin <> Co-authored-by: Jared C <28533997+oakaigh@users.noreply.github.com> Co-authored-by: sysadmin --- src/vs/base/common/network.ts | 19 ++++++++++++++++--- .../common/extensionResourceLoader.ts | 9 ++++----- .../browser/remoteAuthorityResolverService.ts | 5 +++-- .../remote/common/remoteAgentConnection.ts | 4 ++-- src/vs/platform/remote/common/remoteHosts.ts | 9 --------- .../remoteAuthorityResolverService.ts | 3 +-- .../node/remoteExtensionHostAgentServer.ts | 18 ++++++++++++------ .../server/node/serverEnvironmentService.ts | 7 +++++++ src/vs/server/node/webClientServer.ts | 10 ++++++---- src/vs/workbench/browser/web.api.ts | 7 +++++++ src/vs/workbench/browser/web.main.ts | 4 ++-- .../test/browser/configurationService.test.ts | 6 +++--- .../test/browser/extensionService.test.ts | 2 +- 13 files changed, 64 insertions(+), 39 deletions(-) diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 974d0c21743..5cbdad174b7 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -7,6 +7,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'; +import * as paths from 'vs/base/common/path'; export namespace Schemas { @@ -144,7 +145,7 @@ class RemoteAuthoritiesImpl { private readonly _connectionTokens: { [authority: string]: string | undefined } = Object.create(null); private _preferredWebSchema: 'http' | 'https' = 'http'; private _delegate: ((uri: URI) => URI) | null = null; - private _remoteResourcesPath: string = `/${Schemas.vscodeRemoteResource}`; + private _serverRootPath: string = '/'; setPreferredWebSchema(schema: 'http' | 'https') { this._preferredWebSchema = schema; @@ -154,8 +155,16 @@ class RemoteAuthoritiesImpl { this._delegate = delegate; } - setServerRootPath(serverRootPath: string): void { - this._remoteResourcesPath = `${serverRootPath}/${Schemas.vscodeRemoteResource}`; + setServerRootPath(product: { quality?: string; commit?: string }, serverBasePath: string | undefined): void { + this._serverRootPath = getServerRootPath(product, serverBasePath); + } + + getServerRootPath(): string { + return this._serverRootPath; + } + + private get _remoteResourcesPath(): string { + return paths.posix.join(this._serverRootPath, Schemas.vscodeRemoteResource); } set(authority: string, host: string, port: number): void { @@ -202,6 +211,10 @@ class RemoteAuthoritiesImpl { export const RemoteAuthorities = new RemoteAuthoritiesImpl(); +export function getServerRootPath(product: { quality?: string; commit?: string }, basePath: string | undefined): string { + return paths.posix.join(basePath ?? '/', `${product.quality ?? 'oss'}-${product.commit ?? 'dev'}`); +} + /** * A string pointing to a path inside the app. It should not begin with ./ or ../ */ diff --git a/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts b/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts index e63c48d0c2f..f1660961c58 100644 --- a/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts +++ b/src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts @@ -17,10 +17,9 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { RemoteAuthorities } from 'vs/base/common/network'; -import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; import { TargetPlatform } from 'vs/platform/extensions/common/extensions'; -const WEB_EXTENSION_RESOURCE_END_POINT = 'web-extension-resource'; +const WEB_EXTENSION_RESOURCE_END_POINT_SEGMENT = '/web-extension-resource/'; export const IExtensionResourceLoaderService = createDecorator('extensionResourceLoaderService'); @@ -67,7 +66,6 @@ export abstract class AbstractExtensionResourceLoaderService implements IExtensi readonly _serviceBrand: undefined; - private readonly _webExtensionResourceEndPoint: string; private readonly _extensionGalleryResourceUrlTemplate: string | undefined; private readonly _extensionGalleryAuthority: string | undefined; @@ -78,7 +76,6 @@ export abstract class AbstractExtensionResourceLoaderService implements IExtensi private readonly _environmentService: IEnvironmentService, private readonly _configurationService: IConfigurationService, ) { - this._webExtensionResourceEndPoint = `${getRemoteServerRootPath(_productService)}/${WEB_EXTENSION_RESOURCE_END_POINT}/`; if (_productService.extensionsGallery) { this._extensionGalleryResourceUrlTemplate = _productService.extensionsGallery.resourceUrlTemplate; this._extensionGalleryAuthority = this._extensionGalleryResourceUrlTemplate ? this._getExtensionGalleryAuthority(URI.parse(this._extensionGalleryResourceUrlTemplate)) : undefined; @@ -144,7 +141,9 @@ export abstract class AbstractExtensionResourceLoaderService implements IExtensi } protected _isWebExtensionResourceEndPoint(uri: URI): boolean { - return uri.path.startsWith(this._webExtensionResourceEndPoint); + const uriPath = uri.path, serverRootPath = RemoteAuthorities.getServerRootPath(); + // test if the path starts with the server root path followed by the web extension resource end point segment + return uriPath.startsWith(serverRootPath) && uriPath.startsWith(WEB_EXTENSION_RESOURCE_END_POINT_SEGMENT, serverRootPath.length); } } diff --git a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts index 529d9d74999..8b85c237151 100644 --- a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts @@ -15,7 +15,7 @@ import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRemoteAuthorityResolverService, IRemoteConnectionData, RemoteConnectionType, ResolvedAuthority, ResolvedOptions, ResolverResult, WebSocketRemoteConnection, getRemoteAuthorityPrefix } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { getRemoteServerRootPath, parseAuthorityWithOptionalPort } from 'vs/platform/remote/common/remoteHosts'; +import { parseAuthorityWithOptionalPort } from 'vs/platform/remote/common/remoteHosts'; export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService { @@ -34,6 +34,7 @@ export class RemoteAuthorityResolverService extends Disposable implements IRemot isWorkbenchOptionsBasedResolution: boolean, connectionToken: Promise | string | undefined, resourceUriProvider: ((uri: URI) => URI) | undefined, + serverBasePath: string | undefined, @IProductService productService: IProductService, @ILogService private readonly _logService: ILogService, ) { @@ -44,7 +45,7 @@ export class RemoteAuthorityResolverService extends Disposable implements IRemot if (resourceUriProvider) { RemoteAuthorities.setDelegate(resourceUriProvider); } - RemoteAuthorities.setServerRootPath(getRemoteServerRootPath(productService)); + RemoteAuthorities.setServerRootPath(productService, serverBasePath); } async resolveAuthority(authority: string): Promise { diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts index 9539650dec0..45ebbe8df04 100644 --- a/src/vs/platform/remote/common/remoteAgentConnection.ts +++ b/src/vs/platform/remote/common/remoteAgentConnection.ts @@ -9,6 +9,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { isCancellationError, onUnexpectedError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { RemoteAuthorities } from 'vs/base/common/network'; import * as performance from 'vs/base/common/performance'; import { StopWatch } from 'vs/base/common/stopwatch'; import { generateUuid } from 'vs/base/common/uuid'; @@ -17,7 +18,6 @@ import { Client, ISocket, PersistentProtocol, SocketCloseEventType } from 'vs/ba import { ILogService } from 'vs/platform/log/common/log'; import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { RemoteAuthorityResolverError, RemoteConnection } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; import { IRemoteSocketFactoryService } from 'vs/platform/remote/common/remoteSocketFactoryService'; import { ISignService } from 'vs/platform/sign/common/sign'; @@ -232,7 +232,7 @@ async function connectToRemoteExtensionHostAgent(opt let socket: ISocket; try { - socket = await createSocket(options.logService, options.remoteSocketFactoryService, options.connectTo, getRemoteServerRootPath(options), `reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, connectionTypeToString(connectionType), `renderer-${connectionTypeToString(connectionType)}-${options.reconnectionToken}`, timeoutCancellationToken); + socket = await createSocket(options.logService, options.remoteSocketFactoryService, options.connectTo, RemoteAuthorities.getServerRootPath(), `reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, connectionTypeToString(connectionType), `renderer-${connectionTypeToString(connectionType)}-${options.reconnectionToken}`, timeoutCancellationToken); } catch (error) { options.logService.error(`${logPrefix} socketFactory.connect() failed or timed out. Error:`); options.logService.error(error); diff --git a/src/vs/platform/remote/common/remoteHosts.ts b/src/vs/platform/remote/common/remoteHosts.ts index ccc99953c8d..ccf58f9accb 100644 --- a/src/vs/platform/remote/common/remoteHosts.ts +++ b/src/vs/platform/remote/common/remoteHosts.ts @@ -25,15 +25,6 @@ export function getRemoteName(authority: string | undefined): string | undefined return authority.substr(0, pos); } -/** - * The root path to use when accessing the remote server. The path contains the quality and commit of the current build. - * @param product - * @returns - */ -export function getRemoteServerRootPath(product: { quality?: string; commit?: string }): string { - return `/${product.quality ?? 'oss'}-${product.commit ?? 'dev'}`; -} - export function parseAuthorityWithPort(authority: string): { host: string; port: number } { const { host, port } = parseAuthority(authority); if (typeof port === 'undefined') { diff --git a/src/vs/platform/remote/electron-sandbox/remoteAuthorityResolverService.ts b/src/vs/platform/remote/electron-sandbox/remoteAuthorityResolverService.ts index debbe333ae8..9948495f898 100644 --- a/src/vs/platform/remote/electron-sandbox/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/electron-sandbox/remoteAuthorityResolverService.ts @@ -11,7 +11,6 @@ import { RemoteAuthorities } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { IProductService } from 'vs/platform/product/common/productService'; import { IRemoteAuthorityResolverService, IRemoteConnectionData, RemoteConnectionType, ResolvedAuthority, ResolvedOptions, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; import { ElectronRemoteResourceLoader } from 'vs/platform/remote/electron-sandbox/electronRemoteResourceLoader'; export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService { @@ -33,7 +32,7 @@ export class RemoteAuthorityResolverService extends Disposable implements IRemot this._canonicalURIRequests = new Map(); this._canonicalURIProvider = null; - RemoteAuthorities.setServerRootPath(getRemoteServerRootPath(productService)); + RemoteAuthorities.setServerRootPath(productService, undefined); // on the desktop we don't support custom server base paths } resolveAuthority(authority: string): Promise { diff --git a/src/vs/server/node/remoteExtensionHostAgentServer.ts b/src/vs/server/node/remoteExtensionHostAgentServer.ts index a90d28e82cf..84664bbb39a 100644 --- a/src/vs/server/node/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/node/remoteExtensionHostAgentServer.ts @@ -15,7 +15,7 @@ import { CharCode } from 'vs/base/common/charCode'; import { isSigPipeError, onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { isEqualOrParent } from 'vs/base/common/extpath'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { connectionTokenQueryName, FileAccess, Schemas } from 'vs/base/common/network'; +import { connectionTokenQueryName, FileAccess, getServerRootPath, Schemas } from 'vs/base/common/network'; import { dirname, join } from 'vs/base/common/path'; import * as perf from 'vs/base/common/performance'; import * as platform from 'vs/base/common/platform'; @@ -33,7 +33,6 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { ConnectionType, ConnectionTypeRequest, ErrorMessage, HandshakeMessage, IRemoteExtensionHostStartParams, ITunnelConnectionStartParams, SignRequest } from 'vs/platform/remote/common/remoteAgentConnection'; import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; -import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExtensionHostConnection } from 'vs/server/node/extensionHostConnection'; import { ManagementConnection } from 'vs/server/node/remoteExtensionManagement'; @@ -75,6 +74,7 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { private readonly _connectionToken: ServerConnectionToken, private readonly _vsdaMod: typeof vsda | null, hasWebClient: boolean, + serverBasePath: string | undefined, @IServerEnvironmentService private readonly _environmentService: IServerEnvironmentService, @IProductService private readonly _productService: IProductService, @ILogService private readonly _logService: ILogService, @@ -82,13 +82,13 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { ) { super(); - this._serverRootPath = getRemoteServerRootPath(_productService); + this._serverRootPath = getServerRootPath(_productService, serverBasePath); this._extHostConnections = Object.create(null); this._managementConnections = Object.create(null); this._allReconnectionTokens = new Set(); this._webClientServer = ( hasWebClient - ? this._instantiationService.createInstance(WebClientServer, this._connectionToken) + ? this._instantiationService.createInstance(WebClientServer, this._connectionToken, serverBasePath ?? '/', this._serverRootPath) : null ); this._logService.info(`Extension host agent started.`); @@ -665,6 +665,7 @@ export interface IServerAPI { } export async function createServer(address: string | net.AddressInfo | null, args: ServerParsedArgs, REMOTE_DATA_FOLDER: string): Promise { + const connectionToken = await determineServerConnectionToken(args); if (connectionToken instanceof ServerConnectionTokenParseError) { console.warn(connectionToken.message); @@ -774,15 +775,20 @@ export async function createServer(address: string | net.AddressInfo | null, arg return null; }); + let serverBasePath = args['server-base-path']; + if (serverBasePath && !serverBasePath.startsWith('/')) { + serverBasePath = `/${serverBasePath}`; + } + const hasWebClient = fs.existsSync(FileAccess.asFileUri('vs/code/browser/workbench/workbench.html').fsPath); if (hasWebClient && address && typeof address !== 'string') { // ships the web ui! const queryPart = (connectionToken.type !== ServerConnectionTokenType.None ? `?${connectionTokenQueryName}=${connectionToken.value}` : ''); - console.log(`Web UI available at http://localhost${address.port === 80 ? '' : `:${address.port}`}/${queryPart}`); + console.log(`Web UI available at http://localhost${address.port === 80 ? '' : `:${address.port}`}${serverBasePath ?? ''}${queryPart}`); } - const remoteExtensionHostAgentServer = instantiationService.createInstance(RemoteExtensionHostAgentServer, socketServer, connectionToken, vsdaMod, hasWebClient); + const remoteExtensionHostAgentServer = instantiationService.createInstance(RemoteExtensionHostAgentServer, socketServer, connectionToken, vsdaMod, hasWebClient, serverBasePath); perf.mark('code/server/ready'); const currentTime = performance.now(); diff --git a/src/vs/server/node/serverEnvironmentService.ts b/src/vs/server/node/serverEnvironmentService.ts index 900815a06d2..fce1842f1bd 100644 --- a/src/vs/server/node/serverEnvironmentService.ts +++ b/src/vs/server/node/serverEnvironmentService.ts @@ -19,6 +19,7 @@ export const serverOptions: OptionDescriptions> = { 'host': { type: 'string', cat: 'o', args: 'ip-address', description: nls.localize('host', "The host name or IP address the server should listen to. If not set, defaults to 'localhost'.") }, 'port': { type: 'string', cat: 'o', args: 'port | port range', description: nls.localize('port', "The port the server should listen to. If 0 is passed a random free port is picked. If a range in the format num-num is passed, a free port from the range (end inclusive) is selected.") }, 'socket-path': { type: 'string', cat: 'o', args: 'path', description: nls.localize('socket-path', "The path to a socket file for the server to listen to.") }, + 'server-base-path': { type: 'string', cat: 'o', args: 'path', description: nls.localize('server-base-path', "The path under which the web UI and the code server is provided. Defaults to '/'.`") }, 'connection-token': { type: 'string', cat: 'o', args: 'token', deprecates: ['connectionToken'], description: nls.localize('connection-token', "A secret that must be included with all requests.") }, 'connection-token-file': { type: 'string', cat: 'o', args: 'path', deprecates: ['connection-secret', 'connectionTokenFile'], description: nls.localize('connection-token-file', "Path to a file that contains the connection token.") }, 'without-connection-token': { type: 'boolean', cat: 'o', description: nls.localize('without-connection-token', "Run without a connection token. Only use this if the connection is secured by other means.") }, @@ -102,6 +103,12 @@ export interface ServerParsedArgs { port?: string; 'socket-path'?: string; + /** + * The path under which the web UI and the code server is provided. + * By defaults it is '/'.` + */ + 'server-base-path'?: string; + /** * A secret token that must be provided by the web client with all requests. * Use only `[0-9A-Za-z\-]`. diff --git a/src/vs/server/node/webClientServer.ts b/src/vs/server/node/webClientServer.ts index a46d6748d0b..feed129fc94 100644 --- a/src/vs/server/node/webClientServer.ts +++ b/src/vs/server/node/webClientServer.ts @@ -28,7 +28,6 @@ import { streamToBuffer } from 'vs/base/common/buffer'; import { IProductConfiguration } from 'vs/base/common/product'; import { isString } from 'vs/base/common/types'; import { CharCode } from 'vs/base/common/charCode'; -import { getRemoteServerRootPath } from 'vs/platform/remote/common/remoteHosts'; import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; const textMimeType = { @@ -104,13 +103,15 @@ export class WebClientServer { constructor( private readonly _connectionToken: ServerConnectionToken, + private readonly _basePath: string, + readonly serverRootPath: string, @IServerEnvironmentService private readonly _environmentService: IServerEnvironmentService, @ILogService private readonly _logService: ILogService, @IRequestService private readonly _requestService: IRequestService, @IProductService private readonly _productService: IProductService, ) { this._webExtensionResourceUrlTemplate = this._productService.extensionsGallery?.resourceUrlTemplate ? URI.parse(this._productService.extensionsGallery.resourceUrlTemplate) : undefined; - const serverRootPath = getRemoteServerRootPath(_productService); + this._staticRoute = `${serverRootPath}/static`; this._callbackRoute = `${serverRootPath}/callback`; this._webExtensionRoute = `${serverRootPath}/web-extension-resource`; @@ -128,7 +129,7 @@ export class WebClientServer { if (pathname.startsWith(this._staticRoute) && pathname.charCodeAt(this._staticRoute.length) === CharCode.Slash) { return this._handleStatic(req, res, parsedUrl); } - if (pathname === '/') { + if (pathname === this._basePath) { return this._handleRoot(req, res, parsedUrl); } if (pathname === this._callbackRoute) { @@ -262,7 +263,7 @@ export class WebClientServer { newQuery[key] = parsedUrl.query[key]; } } - const newLocation = url.format({ pathname: '/', query: newQuery }); + const newLocation = url.format({ pathname: parsedUrl.pathname, query: newQuery }); responseHeaders['Location'] = newLocation; res.writeHead(302, responseHeaders); @@ -326,6 +327,7 @@ export class WebClientServer { const workbenchWebConfiguration = { remoteAuthority, + remoteBaseUrl: this._basePath, _wrapWebWorkerExtHostInIframe, developmentOptions: { enableSmokeTestDriver: this._environmentService.args['enable-smoke-test-driver'] ? true : undefined, logLevel: this._logService.getLevel() }, settingsSyncOptions: !this._environmentService.isBuilt && this._environmentService.args['enable-sync'] ? { enabled: true } : undefined, diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index 50d76a2213c..196edf88796 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -142,6 +142,13 @@ export interface IWorkbenchConstructionOptions { */ readonly remoteAuthority?: string; + /** + * The server base path is the path where the workbench is served from. + * The path must be absolute (start with a slash). + * Corresponds to option `server-base-path` on the server side. + */ + readonly serverBasePath?: string; + /** * The connection token to send to the server. */ diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 9061af27859..b36400ec9f1 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -284,11 +284,11 @@ export class BrowserMain extends Disposable { // Register them early because they are needed for the profiles initialization await this.registerIndexedDBFileSystemProviders(environmentService, fileService, logService, loggerService, logsPath); - // Remote + const connectionToken = environmentService.options.connectionToken || getCookieValue(connectionTokenCookieName); const remoteResourceLoader = this.configuration.remoteResourceProvider ? new BrowserRemoteResourceLoader(fileService, this.configuration.remoteResourceProvider) : undefined; const resourceUriProvider = this.configuration.resourceUriProvider ?? remoteResourceLoader?.getResourceUriProvider(); - const remoteAuthorityResolverService = new RemoteAuthorityResolverService(!environmentService.expectsResolverExtension, connectionToken, resourceUriProvider, productService, logService); + const remoteAuthorityResolverService = new RemoteAuthorityResolverService(!environmentService.expectsResolverExtension, connectionToken, resourceUriProvider, this.configuration.serverBasePath, productService, logService); serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService); // Signing diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index 0ca8625187e..a7ef7154baa 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -101,7 +101,7 @@ suite('WorkspaceContextService - Folder', () => { userDataProfileService, environmentService, TestProductService, - disposables.add(new RemoteAuthorityResolverService(false, undefined, undefined, TestProductService, logService)), + disposables.add(new RemoteAuthorityResolverService(false, undefined, undefined, undefined, TestProductService, logService)), new SignService(TestProductService), new NullLogService())), uriIdentityService, new NullLogService(), @@ -152,7 +152,7 @@ suite('WorkspaceContextService - Folder', () => { userDataProfileService, userDataProfilesService, fileService, - disposables.add(new RemoteAgentService(new RemoteSocketFactoryService(), userDataProfileService, environmentService, TestProductService, disposables.add(new RemoteAuthorityResolverService(false, undefined, undefined, TestProductService, logService)), new SignService(TestProductService), new NullLogService())), + disposables.add(new RemoteAgentService(new RemoteSocketFactoryService(), userDataProfileService, environmentService, TestProductService, disposables.add(new RemoteAuthorityResolverService(false, undefined, undefined, undefined, TestProductService, logService)), new SignService(TestProductService), new NullLogService())), uriIdentityService, new NullLogService(), new NullPolicyService())); @@ -184,7 +184,7 @@ suite('WorkspaceContextService - Folder', () => { userDataProfileService, userDataProfilesService, fileService, - disposables.add(new RemoteAgentService(new RemoteSocketFactoryService(), userDataProfileService, environmentService, TestProductService, disposables.add(new RemoteAuthorityResolverService(false, undefined, undefined, TestProductService, logService)), new SignService(TestProductService), new NullLogService())), + disposables.add(new RemoteAgentService(new RemoteSocketFactoryService(), userDataProfileService, environmentService, TestProductService, disposables.add(new RemoteAuthorityResolverService(false, undefined, undefined, undefined, TestProductService, logService)), new SignService(TestProductService), new NullLogService())), uriIdentityService, new NullLogService(), new NullPolicyService())); diff --git a/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts b/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts index 84682289313..3ced43a5f42 100644 --- a/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts +++ b/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts @@ -250,7 +250,7 @@ suite('ExtensionService', () => { [IUserDataProfileService, TestUserDataProfileService], [IUriIdentityService, UriIdentityService], [IRemoteExtensionsScannerService, TestRemoteExtensionsScannerService], - [IRemoteAuthorityResolverService, new RemoteAuthorityResolverService(false, undefined, undefined, testProductService, new NullLogService())] + [IRemoteAuthorityResolverService, new RemoteAuthorityResolverService(false, undefined, undefined, undefined, testProductService, new NullLogService())] ])); extService = instantiationService.get(IExtensionService); }); From 9854e101dd46a652155d0af52224f377f5fa5c0a Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 23 Feb 2024 13:29:43 -0800 Subject: [PATCH 0152/1175] fix #204395 --- .../contrib/accessibility/browser/accessibleView.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index cfbfa70a242..3e90ba4b1a8 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -13,7 +13,7 @@ import { Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { marked } from 'vs/base/common/marked/marked'; -import { isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { ThemeIcon } from 'vs/base/common/themables'; import { URI } from 'vs/base/common/uri'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; @@ -458,7 +458,7 @@ export class AccessibleView extends Disposable { const exitThisDialogHint = verbose && !provider.options.position ? localize('exit', '\n\nExit this dialog (Escape).') : ''; this._currentContent = message + provider.provideContent() + readMoreLink + disableHelpHint + exitThisDialogHint; this._updateContextKeys(provider, true); - + const widgetIsFocused = this._editorWidget.hasTextFocus() || this._editorWidget.hasWidgetFocus(); this._getTextModel(URI.from({ path: `accessible-view-${provider.verbositySettingKey}`, scheme: 'accessible-view', fragment: this._currentContent })).then((model) => { if (!model) { return; @@ -483,6 +483,11 @@ export class AccessibleView extends Disposable { } else if (actionsHint) { ariaLabel = localize('accessibility-help-hint', "Accessibility Help, {0}", actionsHint); } + if (isWindows && widgetIsFocused) { + // prevent the screen reader on windows from reading + // the aria label again when it's refocused + ariaLabel = ''; + } this._editorWidget.updateOptions({ ariaLabel }); this._editorWidget.focus(); if (this._currentProvider?.options.position) { From e6132eaeff5fbf19b01f82cda7601c337e8d17ed Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 23 Feb 2024 13:48:39 -0800 Subject: [PATCH 0153/1175] Preview selected detected link Fixes #206126 --- .../links/browser/terminalLink.ts | 4 ++ .../browser/terminalLinkDetectorAdapter.ts | 6 +- .../links/browser/terminalLinkQuickpick.ts | 69 ++++++++++++++++++- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLink.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLink.ts index 4654d703f1d..84d9ca4fdd2 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLink.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLink.ts @@ -13,6 +13,8 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TerminalLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { IHoverAction } from 'vs/platform/hover/browser/hover'; +import type { URI } from 'vs/base/common/uri'; +import type { IParsedLink } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing'; export class TerminalLink extends DisposableStore implements ILink { decorations: ILinkDecorations; @@ -30,6 +32,8 @@ export class TerminalLink extends DisposableStore implements ILink { private readonly _xterm: Terminal, readonly range: IBufferRange, readonly text: string, + readonly uri: URI | undefined, + readonly parsedLink: IParsedLink | undefined, readonly actions: IHoverAction[] | undefined, private readonly _viewportY: number, private readonly _activateCallback: (event: MouseEvent | undefined, uri: string) => Promise, diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts index 8226841a9a0..26c047ea42d 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts @@ -92,9 +92,7 @@ export class TerminalLinkDetectorAdapter extends Disposable implements ILinkProv const detectedLinks = await this._detector.detect(lines, startLine, endLine); for (const link of detectedLinks) { - links.push(this._createTerminalLink(link, async (event) => { - this._onDidActivateLink.fire({ link, event }); - })); + links.push(this._createTerminalLink(link, async (event) => this._onDidActivateLink.fire({ link, event }))); } return links; @@ -110,6 +108,8 @@ export class TerminalLinkDetectorAdapter extends Disposable implements ILinkProv this._detector.xterm, l.bufferRange, l.text, + l.uri, + l.parsedLink, l.actions, this._detector.xterm.buffer.active.viewportY, activateCallback, diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts index e954be537df..aafd2e45711 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts @@ -6,24 +6,37 @@ import { EventType } from 'vs/base/browser/dom'; import { Emitter, Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; -import { QuickPickItem, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { QuickPickItem, IQuickInputService, IQuickPickItem, QuickInputHideReason } from 'vs/platform/quickinput/common/quickInput'; import { IDetectedLinks } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager'; import { TerminalLinkQuickPickEvent } from 'vs/workbench/contrib/terminal/browser/terminal'; import type { ILink } from '@xterm/xterm'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import type { TerminalLink } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLink'; +import { Sequencer } from 'vs/base/common/async'; +import { EditorViewState } from 'vs/workbench/browser/quickaccess'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { getLinkSuffix } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing'; +import type { ITextEditorSelection } from 'vs/platform/editor/common/editor'; export class TerminalLinkQuickpick extends DisposableStore { + private readonly _editorSequencer = new Sequencer(); + private readonly _editorViewState: EditorViewState; + private readonly _onDidRequestMoreLinks = this.add(new Emitter()); readonly onDidRequestMoreLinks = this._onDidRequestMoreLinks.event; constructor( + @IEditorService private readonly _editorService: IEditorService, + @IHistoryService private readonly _historyService: IHistoryService, @IQuickInputService private readonly _quickInputService: IQuickInputService, @IAccessibleViewService private readonly _accessibleViewService: IAccessibleViewService ) { super(); + this._editorViewState = new EditorViewState(_editorService); } async show(links: { viewport: IDetectedLinks; all: Promise }): Promise { @@ -57,6 +70,9 @@ export class TerminalLinkQuickpick extends DisposableStore { pick.placeholder = localize('terminal.integrated.openDetectedLink', "Select the link to open, type to filter all links"); pick.sortByLabel = false; pick.show(); + if (pick.activeItems.length > 0) { + this._previewItem(pick.activeItems[0]); + } // Show all results only when filtering begins, this is done so the quick pick will show up // ASAP with only the viewport entries. @@ -93,8 +109,20 @@ export class TerminalLinkQuickpick extends DisposableStore { pick.items = picks; })); + disposables.add(pick.onDidChangeActive(async () => { + const [item] = pick.activeItems; + this._previewItem(item); + })); + return new Promise(r => { - disposables.add(pick.onDidHide(() => { + disposables.add(pick.onDidHide(({ reason }) => { + // Restore view state upon cancellation if we changed it + // but only when the picker was closed via explicit user + // gesture and not e.g. when focus was lost because that + // could mean the user clicked into the editor directly. + if (reason === QuickInputHideReason.Gesture) { + this._editorViewState.restore(true); + } disposables.dispose(); if (pick.selectedItems.length === 0) { this._accessibleViewService.showLastProvider(AccessibleViewProviderId.Terminal); @@ -132,10 +160,45 @@ export class TerminalLinkQuickpick extends DisposableStore { } return picks.length > 0 ? picks : undefined; } + + private _previewItem(item: ITerminalLinkQuickPickItem | IQuickPickItem) { + if (item && 'link' in item && item.link && 'uri' in item.link && item.link.uri) { + this._editorViewState.set(); + const link = item.link; + const uri = link.uri; + + + + const linkSuffix = link.parsedLink ? link.parsedLink.suffix : getLinkSuffix(link.text); + let selection: ITextEditorSelection | undefined;// = link.selection; + if (!selection) { + selection = linkSuffix?.row === undefined ? undefined : { + startLineNumber: linkSuffix.row ?? 1, + startColumn: linkSuffix.col ?? 1, + endLineNumber: linkSuffix.rowEnd, + endColumn: linkSuffix.colEnd + }; + } + + + this._editorSequencer.queue(async () => { + // disable and re-enable history service so that we can ignore this history entry + const disposable = this._historyService.suspendTracking(); + try { + await this._editorService.openEditor({ + resource: uri, + options: { preserveFocus: true, revealIfOpened: true, ignoreError: true, selection } + }); + } finally { + disposable.dispose(); + } + }); + } + } } export interface ITerminalLinkQuickPickItem extends IQuickPickItem { - link: ILink; + link: ILink | TerminalLink; } type LinkQuickPickItem = ITerminalLinkQuickPickItem | QuickPickItem; From 228a35f333e8ca1f3fe78b8d5a5ca558eab870f7 Mon Sep 17 00:00:00 2001 From: Vinicius Stock Date: Fri, 23 Feb 2024 17:07:53 -0500 Subject: [PATCH 0154/1175] Fix accidental dedent for `in` and `when` dedent in Ruby comments --- extensions/ruby/language-configuration.json | 2 +- .../contrib/indentation/test/browser/indentation.test.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/extensions/ruby/language-configuration.json b/extensions/ruby/language-configuration.json index e61f3ac410f..e1125e0bf2b 100644 --- a/extensions/ruby/language-configuration.json +++ b/extensions/ruby/language-configuration.json @@ -26,6 +26,6 @@ ], "indentationRules": { "increaseIndentPattern": "^\\s*((begin|class|(private|protected)\\s+def|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|in|while|case)|([^#]*\\sdo\\b)|([^#]*=\\s*(case|if|unless)))\\b([^#\\{;]|(\"|'|\/).*\\4)*(#.*)?$", - "decreaseIndentPattern": "^\\s*([}\\]]([,)]?\\s*(#|$)|\\.[a-zA-Z_]\\w*\\b)|(end|rescue|ensure|else|elsif)\\b)|((in|when)\\s)" + "decreaseIndentPattern": "^\\s*([}\\]]([,)]?\\s*(#|$)|\\.[a-zA-Z_]\\w*\\b)|(end|rescue|ensure|else|elsif)\\b|(in|when)\\s)" } } diff --git a/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts b/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts index 91666a05d19..516966c9477 100644 --- a/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts +++ b/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts @@ -373,16 +373,23 @@ suite('Editor Contrib - Auto Dedent On Type', () => { ['(', ')'] ], indentationRules: { - decreaseIndentPattern: /\s*([}\]]([,)]?\s*(#|$)|\.[a-zA-Z_]\w*\b)|(end|rescue|ensure|else|elsif)\b)|((in|when)\s)/, + decreaseIndentPattern: /^\s*([}\]]([,)]?\s*(#|$)|\.[a-zA-Z_]\w*\b)|(end|rescue|ensure|else|elsif)\b|(in|when)\s)/, increaseIndentPattern: /^\s*((begin|class|(private|protected)\s+def|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|in|while|case)|([^#]*\sdo\b)|([^#]*=\s*(case|if|unless)))\b([^#\{;]|(\"|'|\/).*\4)*(#.*)?$/, }, }); + viewModel.model.setValue(""); viewModel.type("def foo\n i"); viewModel.type("n", 'keyboard'); assert.strictEqual(model.getValue(), "def foo\n in"); viewModel.type(" ", 'keyboard'); assert.strictEqual(model.getValue(), "def foo\nin "); + + viewModel.model.setValue(""); + viewModel.type(" # in"); + assert.strictEqual(model.getValue(), " # in"); + viewModel.type(" ", 'keyboard'); + assert.strictEqual(model.getValue(), " # in "); improvedLanguageModel.dispose(); }); }); From c43cd02a59395b830e90ccaac720c0433c74371b Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 23 Feb 2024 14:27:53 -0800 Subject: [PATCH 0155/1175] fix #203722 --- .../contrib/accessibility/browser/accessibleView.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index cfbfa70a242..1e7899de2d4 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -29,6 +29,7 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewDelegate, IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -157,7 +158,8 @@ export class AccessibleView extends Disposable { @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @ILayoutService private readonly _layoutService: ILayoutService, - @IMenuService private readonly _menuService: IMenuService + @IMenuService private readonly _menuService: IMenuService, + @ICommandService private readonly _commandService: ICommandService ) { super(); @@ -252,6 +254,10 @@ export class AccessibleView extends Disposable { } } + activateLink(): void { + this._commandService.executeCommand('editor.action.openLink'); + } + showLastProvider(id: AccessibleViewProviderId): void { if (!this._lastProvider || this._lastProvider.options.id !== id) { return; @@ -509,7 +515,9 @@ export class AccessibleView extends Disposable { }; const disposableStore = new DisposableStore(); disposableStore.add(this._editorWidget.onKeyDown((e) => { - if (e.keyCode === KeyCode.Escape || shouldHide(e.browserEvent, this._keybindingService, this._configurationService)) { + if (e.keyCode === KeyCode.Enter) { + this.activateLink(); + } else if (e.keyCode === KeyCode.Escape || shouldHide(e.browserEvent, this._keybindingService, this._configurationService)) { hide(e); } else if (e.keyCode === KeyCode.KeyH && provider.options.readMoreUrl) { const url: string = provider.options.readMoreUrl; From db5fbe6fd930a7fe0a7fe03f432eb1aa71ca14bd Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 23 Feb 2024 14:31:18 -0800 Subject: [PATCH 0156/1175] simplify --- .../contrib/accessibility/browser/accessibleView.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 1e7899de2d4..130efbcc20c 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -254,9 +254,6 @@ export class AccessibleView extends Disposable { } } - activateLink(): void { - this._commandService.executeCommand('editor.action.openLink'); - } showLastProvider(id: AccessibleViewProviderId): void { if (!this._lastProvider || this._lastProvider.options.id !== id) { @@ -516,7 +513,7 @@ export class AccessibleView extends Disposable { const disposableStore = new DisposableStore(); disposableStore.add(this._editorWidget.onKeyDown((e) => { if (e.keyCode === KeyCode.Enter) { - this.activateLink(); + this._commandService.executeCommand('editor.action.openLink'); } else if (e.keyCode === KeyCode.Escape || shouldHide(e.browserEvent, this._keybindingService, this._configurationService)) { hide(e); } else if (e.keyCode === KeyCode.KeyH && provider.options.readMoreUrl) { From 9a07ceb9f7f1dc1803913105c816009cb4bfdec1 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 23 Feb 2024 15:02:22 -0800 Subject: [PATCH 0157/1175] cli: ensure the canonical snap exe is used for the CLI (#206133) Fixes #204907 --- cli/src/commands/tunnels.rs | 7 +--- cli/src/util/machine.rs | 81 ++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/cli/src/commands/tunnels.rs b/cli/src/commands/tunnels.rs index f9ae6883075..02a697c1793 100644 --- a/cli/src/commands/tunnels.rs +++ b/cli/src/commands/tunnels.rs @@ -50,10 +50,7 @@ use crate::{ AuthRequired, Next, ServeStreamParams, ServiceContainer, ServiceManager, }, util::{ - app_lock::AppMutex, - command::new_std_command, - errors::{wrap, AnyError, CodeError}, - prereqs::PreReqChecker, + app_lock::AppMutex, command::new_std_command, errors::{wrap, AnyError, CodeError}, machine::canonical_exe, prereqs::PreReqChecker }, }; use crate::{ @@ -231,7 +228,7 @@ pub async fn service( legal::require_consent(&ctx.paths, args.accept_server_license_terms)?; let current_exe = - std::env::current_exe().map_err(|e| wrap(e, "could not get current exe"))?; + canonical_exe().map_err(|e| wrap(e, "could not get current exe"))?; manager .register( diff --git a/cli/src/util/machine.rs b/cli/src/util/machine.rs index 4c7b6729e43..a573231c2b9 100644 --- a/cli/src/util/machine.rs +++ b/cli/src/util/machine.rs @@ -3,7 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -use std::{path::Path, time::Duration}; + use std::{ + ffi::OsString, + path::{Path, PathBuf}, + time::Duration, +}; use sysinfo::{Pid, PidExt, ProcessExt, System, SystemExt}; pub fn process_at_path_exists(pid: u32, name: &Path) -> bool { @@ -71,3 +75,78 @@ pub async fn wait_until_exe_deleted(current_exe: &Path, poll_ms: u64) { tokio::time::sleep(duration).await; } } + +/// Gets the canonical current exe location, referring to the "current" symlink +/// if running inside snap. +pub fn canonical_exe() -> std::io::Result { + canonical_exe_inner( + std::env::current_exe(), + std::env::var_os("SNAP"), + std::env::var_os("SNAP_REVISION"), + ) +} + +#[inline(always)] +#[allow(unused_variables)] +fn canonical_exe_inner( + exe: std::io::Result, + snap: Option, + rev: Option, +) -> std::io::Result { + let exe = exe?; + + #[cfg(target_os = "linux")] + if let (Some(snap), Some(rev)) = (snap, rev) { + if !exe.starts_with(snap) { + return Ok(exe); + } + + let mut out = PathBuf::new(); + for part in exe.iter() { + if part == rev { + out.push("current") + } else { + out.push(part) + } + } + + return Ok(out); + } + + Ok(exe) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::path::PathBuf; + + #[test] + #[cfg(target_os = "linux")] + fn test_canonical_exe_in_snap() { + let exe = canonical_exe_inner( + Ok(PathBuf::from("/snap/my-snap/1234/some/exe")), + Some("/snap/my-snap/1234".into()), + Some("1234".into()), + ) + .unwrap(); + assert_eq!(exe, PathBuf::from("/snap/my-snap/current/some/exe")); + } + + #[test] + fn test_canonical_exe_not_in_snap() { + let exe = canonical_exe_inner( + Ok(PathBuf::from("/not-in-snap")), + Some("/snap/my-snap/1234".into()), + Some("1234".into()), + ) + .unwrap(); + assert_eq!(exe, PathBuf::from("/not-in-snap")); + } + + #[test] + fn test_canonical_exe_not_in_snap2() { + let exe = canonical_exe_inner(Ok(PathBuf::from("/not-in-snap")), None, None).unwrap(); + assert_eq!(exe, PathBuf::from("/not-in-snap")); + } +} From 47002157e96daf36dfebb911ae5724a6e7710e60 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 23 Feb 2024 15:18:47 -0800 Subject: [PATCH 0158/1175] debug: fix loading state in triggered bp widget (#206140) Fixes #204699 --- .../contrib/debug/browser/breakpointWidget.ts | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts index 7289d25ba29..59a3a4dd4bf 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts @@ -265,30 +265,28 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi private createTriggerBreakpointInput(container: HTMLElement) { const breakpoints = this.debugService.getModel().getBreakpoints().filter(bp => bp !== this.breakpoint); + const breakpointOptions: ISelectOptionItem[] = [ + { text: nls.localize('noTriggerByBreakpoint', 'None'), isDisabled: true }, + ...breakpoints.map(bp => ({ + text: `${this.labelService.getUriLabel(bp.uri, { relative: true })}: ${bp.lineNumber}`, + description: nls.localize('triggerByLoading', 'Loading...') + })), + ]; const index = breakpoints.findIndex((bp) => this.breakpoint?.triggeredBy === bp.getId()); - let select = 0; - if (index > -1) { - select = index + 1; - } - - Promise.all(breakpoints.map(async (bp): Promise => ({ - text: `${this.labelService.getUriLabel(bp.uri, { relative: true })}: ${bp.lineNumber}`, - description: await this.textModelService.createModelReference(bp.uri).then(ref => { + for (const [i, bp] of breakpoints.entries()) { + this.textModelService.createModelReference(bp.uri).then(ref => { try { - return ref.object.textEditorModel.getLineContent(bp.lineNumber).trim(); + breakpointOptions[i + 1].description = ref.object.textEditorModel.getLineContent(bp.lineNumber).trim(); } finally { ref.dispose(); } - }, () => undefined), - }))).then(breakpoints => { - selectBreakpointBox.setOptions([ - { text: nls.localize('noTriggerByBreakpoint', 'None') }, - ...breakpoints - ], select); - }); + }).catch(() => { + breakpointOptions[i + 1].description = nls.localize('noBpSource', 'Could not load source.'); + }); + } - const selectBreakpointBox = this.selectBreakpointBox = new SelectBox([{ text: nls.localize('triggerByLoading', 'Loading...'), isDisabled: true }], 0, this.contextViewService, defaultSelectBoxStyles, { ariaLabel: nls.localize('selectBreakpoint', 'Select breakpoint') }); + const selectBreakpointBox = this.selectBreakpointBox = new SelectBox(breakpointOptions, index + 1, this.contextViewService, defaultSelectBoxStyles, { ariaLabel: nls.localize('selectBreakpoint', 'Select breakpoint') }); selectBreakpointBox.onDidSelect(e => { if (e.index === 0) { this.triggeredByBreakpointInput = undefined; From d63202a5382aa104f5515ea09053a2a21a2587c6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 23 Feb 2024 16:05:26 -0800 Subject: [PATCH 0159/1175] Fix sharing copy paste data across editor groups (#206142) Fix sharing copy paste data across editor groups --- .../browser/copyPasteController.ts | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts index 10654d61a52..adc0684cfca 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts @@ -56,13 +56,20 @@ export class CopyPasteController extends Disposable implements IEditorContributi return editor.getContribution(CopyPasteController.ID); } - private readonly _editor: ICodeEditor; - - private _currentCopyOperation?: { + /** + * Global tracking the last copy operation. + * + * This is shared across all editors so that you can copy and paste between groups. + * + * TODO: figure out how to make this work with multiple windows + */ + private static _currentCopyOperation?: { readonly handle: string; readonly dataTransferPromise: CancelablePromise; }; + private readonly _editor: ICodeEditor; + private _currentPasteOperation?: CancelablePromise; private _pasteAsActionContext?: { readonly preferredId: string | undefined }; @@ -204,8 +211,8 @@ export class CopyPasteController extends Disposable implements IEditorContributi return dataTransfer; }); - this._currentCopyOperation?.dataTransferPromise.cancel(); - this._currentCopyOperation = { handle: handle, dataTransferPromise: promise }; + CopyPasteController._currentCopyOperation?.dataTransferPromise.cancel(); + CopyPasteController._currentCopyOperation = { handle: handle, dataTransferPromise: promise }; } private async handlePaste(e: ClipboardEvent) { @@ -436,8 +443,8 @@ export class CopyPasteController extends Disposable implements IEditorContributi } private async mergeInDataFromCopy(dataTransfer: VSDataTransfer, metadata: CopyMetadata | undefined, token: CancellationToken): Promise { - if (metadata?.id && this._currentCopyOperation?.handle === metadata.id) { - const toMergeDataTransfer = await this._currentCopyOperation.dataTransferPromise; + if (metadata?.id && CopyPasteController._currentCopyOperation?.handle === metadata.id) { + const toMergeDataTransfer = await CopyPasteController._currentCopyOperation.dataTransferPromise; if (token.isCancellationRequested) { return; } From 660264a263fcfeb5e15604ecd8eaeb71ca98cd5f Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Sat, 24 Feb 2024 16:21:20 +0100 Subject: [PATCH 0160/1175] Adopt custom hover for extension and runtime view (#206154) adopt custom hover for extension and runtime view --- .../abstractRuntimeExtensionsEditor.ts | 12 ++++++--- .../extensions/browser/extensionEditor.ts | 24 ++++++++++++----- .../extensions/browser/extensionsWidgets.ts | 26 ++++++++++++++----- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts index 991a3df035c..cb2c01bdf75 100644 --- a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts @@ -5,6 +5,8 @@ import { $, Dimension, addDisposableListener, append, clearNode } from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; @@ -364,13 +366,15 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { } else { title = nls.localize('extensionActivating', "Extension is activating..."); } - data.activationTime.title = title; + data.elementDisposables.push(setupCustomHover(getDefaultHoverDelegate('mouse'), data.activationTime, title)); clearNode(data.msgContainer); if (this._getUnresponsiveProfile(element.description.identifier)) { const el = $('span', undefined, ...renderLabelWithIcons(` $(alert) Unresponsive`)); - el.title = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze."); + const extensionHostFreezTitle = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze."); + data.elementDisposables.push(setupCustomHover(getDefaultHoverDelegate('mouse'), el, extensionHostFreezTitle)); + data.msgContainer.appendChild(el); } @@ -416,7 +420,9 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { } if (accessData?.current) { const element = $('span', undefined, nls.localize('requests count', "{0} Requests: {1} (Session)", feature.label, accessData.current.count)); - element.title = nls.localize('requests count title', "Last request was {0}. Overall Requests: {1}", fromNow(accessData.current.lastAccessed, true, true), accessData.totalCount); + const title = nls.localize('requests count title', "Last request was {0}. Overall Requests: {1}", fromNow(accessData.current.lastAccessed, true, true), accessData.totalCount); + data.elementDisposables.push(setupCustomHover(getDefaultHoverDelegate('mouse'), element, title)); + data.msgContainer.appendChild(element); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 21986a4b709..097f4a3c634 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -5,6 +5,8 @@ import { $, Dimension, addDisposableListener, append, setParentFlowTo } from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { CheckboxActionViewItem } from 'vs/base/browser/ui/toggle/toggle'; import { Action, IAction } from 'vs/base/common/actions'; @@ -188,7 +190,8 @@ class VersionWidget extends ExtensionWithDifferentGalleryVersionWidget { private readonly element: HTMLElement; constructor(container: HTMLElement) { super(); - this.element = append(container, $('code.version', { title: localize('extension version', "Extension Version") })); + this.element = append(container, $('code.version')); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.element, localize('extension version', "Extension Version"))); this.render(); } render(): void { @@ -268,25 +271,30 @@ export class ExtensionEditor extends EditorPane { const details = append(header, $('.details')); const title = append(details, $('.title')); - const name = append(title, $('span.name.clickable', { title: localize('name', "Extension name"), role: 'heading', tabIndex: 0 })); + const name = append(title, $('span.name.clickable', { role: 'heading', tabIndex: 0 })); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), name, localize('name', "Extension name"))); const versionWidget = new VersionWidget(title); - const preview = append(title, $('span.preview', { title: localize('preview', "Preview") })); + const preview = append(title, $('span.preview')); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), preview, localize('preview', "Preview"))); preview.textContent = localize('preview', "Preview"); const builtin = append(title, $('span.builtin')); builtin.textContent = localize('builtin', "Built-in"); const subtitle = append(details, $('.subtitle')); - const publisher = append(append(subtitle, $('.subtitle-entry')), $('.publisher.clickable', { title: localize('publisher', "Publisher"), tabIndex: 0 })); + const publisher = append(append(subtitle, $('.subtitle-entry')), $('.publisher.clickable', { tabIndex: 0 })); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), publisher, localize('publisher', "Publisher"))); publisher.setAttribute('role', 'button'); const publisherDisplayName = append(publisher, $('.publisher-name')); const verifiedPublisherWidget = this.instantiationService.createInstance(VerifiedPublisherWidget, append(publisher, $('.verified-publisher')), false); - const installCount = append(append(subtitle, $('.subtitle-entry')), $('span.install', { title: localize('install count', "Install count"), tabIndex: 0 })); + const installCount = append(append(subtitle, $('.subtitle-entry')), $('span.install', { tabIndex: 0 })); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), installCount, localize('install count', "Install count"))); const installCountWidget = this.instantiationService.createInstance(InstallCountWidget, installCount, false); - const rating = append(append(subtitle, $('.subtitle-entry')), $('span.rating.clickable', { title: localize('rating', "Rating"), tabIndex: 0 })); + const rating = append(append(subtitle, $('.subtitle-entry')), $('span.rating.clickable', { tabIndex: 0 })); + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), rating, localize('rating', "Rating"))); rating.setAttribute('role', 'link'); // #132645 const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, rating, false); @@ -914,7 +922,9 @@ export class ExtensionEditor extends EditorPane { append(extensionResourcesContainer, $('.additional-details-title', undefined, localize('resources', "Resources"))); const resourcesElement = append(extensionResourcesContainer, $('.resources')); for (const [label, uri] of resources) { - this.transientDisposables.add(onClick(append(resourcesElement, $('a.resource', { title: uri.toString(), tabindex: '0' }, label)), () => this.openerService.open(uri))); + const resource = append(resourcesElement, $('a.resource', { tabindex: '0' }, label)); + this.transientDisposables.add(onClick(resource, () => this.openerService.open(uri))); + this.transientDisposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), resource, uri.toString())); } } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index a4a887e596a..4ad3680b7d7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -31,7 +31,7 @@ import { URI } from 'vs/base/common/uri'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import Severity from 'vs/base/common/severity'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { Color } from 'vs/base/common/color'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -41,6 +41,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { defaultCountBadgeStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { private _extension: IExtension | null = null; @@ -124,6 +125,8 @@ export class InstallCountWidget extends ExtensionWidget { export class RatingsWidget extends ExtensionWidget { + private readonly containerHover: ICustomHover; + constructor( private container: HTMLElement, private small: boolean @@ -135,12 +138,13 @@ export class RatingsWidget extends ExtensionWidget { container.classList.add('small'); } + this.containerHover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), container, '')); + this.render(); } render(): void { this.container.innerText = ''; - this.container.title = ''; if (!this.extension) { return; @@ -159,7 +163,7 @@ export class RatingsWidget extends ExtensionWidget { } const rating = Math.round(this.extension.rating * 2) / 2; - this.container.title = localize('ratedLabel', "Average rating: {0} out of 5", rating); + this.containerHover.update(localize('ratedLabel', "Average rating: {0} out of 5", rating)); if (this.small) { append(this.container, $('span' + ThemeIcon.asCSSSelector(starFullIcon))); @@ -186,6 +190,7 @@ export class RatingsWidget extends ExtensionWidget { export class VerifiedPublisherWidget extends ExtensionWidget { private disposables = this._register(new DisposableStore()); + private readonly containerHover: ICustomHover; constructor( private container: HTMLElement, @@ -193,6 +198,7 @@ export class VerifiedPublisherWidget extends ExtensionWidget { @IOpenerService private readonly openerService: IOpenerService, ) { super(); + this.containerHover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), container, '')); this.render(); } @@ -209,7 +215,7 @@ export class VerifiedPublisherWidget extends ExtensionWidget { if (!this.small) { verifiedPublisher.tabIndex = 0; - verifiedPublisher.title = `Verified Domain: ${this.extension.publisherDomain.link}`; + this.containerHover.update(`Verified Domain: ${this.extension.publisherDomain.link}`); verifiedPublisher.setAttribute('role', 'link'); append(verifiedPublisher, $('span.extension-verified-publisher-domain', undefined, publisherDomainLink.authority.startsWith('www.') ? publisherDomainLink.authority.substring(4) : publisherDomainLink.authority)); @@ -239,7 +245,8 @@ export class SponsorWidget extends ExtensionWidget { return; } - const sponsor = append(this.container, $('span.sponsor.clickable', { tabIndex: 0, title: this.extension?.publisherSponsorLink })); + const sponsor = append(this.container, $('span.sponsor.clickable', { tabIndex: 0 })); + this.disposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), sponsor, this.extension?.publisherSponsorLink.toString() ?? '')); sponsor.setAttribute('role', 'link'); // #132645 const sponsorIconElement = renderIcon(sponsorIcon); const label = $('span', undefined, localize('sponsor', "Sponsor")); @@ -367,6 +374,7 @@ export class RemoteBadgeWidget extends ExtensionWidget { class RemoteBadge extends Disposable { readonly element: HTMLElement; + readonly elementHover: ICustomHover; constructor( private readonly tooltip: boolean, @@ -376,6 +384,7 @@ class RemoteBadge extends Disposable { ) { super(); this.element = $('div.extension-badge.extension-remote-badge'); + this.elementHover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.element, '')); this.render(); } @@ -397,7 +406,7 @@ class RemoteBadge extends Disposable { if (this.tooltip) { const updateTitle = () => { if (this.element && this.extensionManagementServerService.remoteExtensionManagementServer) { - this.element.title = localize('remote extension title', "Extension in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label); + this.elementHover.update(localize('remote extension title', "Extension in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label)); } }; this._register(this.labelService.onDidChangeFormatters(() => updateTitle())); @@ -435,6 +444,8 @@ export class ExtensionPackCountWidget extends ExtensionWidget { export class SyncIgnoredWidget extends ExtensionWidget { + private readonly disposables = this._register(new DisposableStore()); + constructor( private readonly container: HTMLElement, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -448,11 +459,12 @@ export class SyncIgnoredWidget extends ExtensionWidget { } render(): void { + this.disposables.clear(); this.container.innerText = ''; if (this.extension && this.extension.state === ExtensionState.Installed && this.userDataSyncEnablementService.isEnabled() && this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension)) { const element = append(this.container, $('span.extension-sync-ignored' + ThemeIcon.asCSSSelector(syncIgnoredIcon))); - element.title = localize('syncingore.label', "This extension is ignored during sync."); + this.disposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), element, localize('syncingore.label', "This extension is ignored during sync."))); element.classList.add(...ThemeIcon.asClassNameArray(syncIgnoredIcon)); } } From f10dd6906c031e1bfacfe6b1976883947688a636 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 24 Feb 2024 07:35:39 -0800 Subject: [PATCH 0161/1175] Only preview detected links for files and when preview is on Part of #206126 --- .../links/browser/terminalLinkQuickpick.ts | 69 +++++++++++-------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts index aafd2e45711..6ceb6c3db32 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts @@ -20,6 +20,9 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { getLinkSuffix } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing'; import type { ITextEditorSelection } from 'vs/platform/editor/common/editor'; +import { TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import type { IFilesConfiguration } from 'vs/workbench/contrib/files/common/files'; export class TerminalLinkQuickpick extends DisposableStore { @@ -30,6 +33,7 @@ export class TerminalLinkQuickpick extends DisposableStore { readonly onDidRequestMoreLinks = this._onDidRequestMoreLinks.event; constructor( + @IConfigurationService private readonly _configurationService: IConfigurationService, @IEditorService private readonly _editorService: IEditorService, @IHistoryService private readonly _historyService: IHistoryService, @IQuickInputService private readonly _quickInputService: IQuickInputService, @@ -162,38 +166,45 @@ export class TerminalLinkQuickpick extends DisposableStore { } private _previewItem(item: ITerminalLinkQuickPickItem | IQuickPickItem) { - if (item && 'link' in item && item.link && 'uri' in item.link && item.link.uri) { - this._editorViewState.set(); - const link = item.link; - const uri = link.uri; - - - - const linkSuffix = link.parsedLink ? link.parsedLink.suffix : getLinkSuffix(link.text); - let selection: ITextEditorSelection | undefined;// = link.selection; - if (!selection) { - selection = linkSuffix?.row === undefined ? undefined : { - startLineNumber: linkSuffix.row ?? 1, - startColumn: linkSuffix.col ?? 1, - endLineNumber: linkSuffix.rowEnd, - endColumn: linkSuffix.colEnd - }; - } + if (!item || !('link' in item) || !item.link || !('uri' in item.link) || !item.link.uri) { + return; + } + const link = item.link; + if (link.type !== TerminalBuiltinLinkType.LocalFile) { + return; + } - this._editorSequencer.queue(async () => { - // disable and re-enable history service so that we can ignore this history entry - const disposable = this._historyService.suspendTracking(); - try { - await this._editorService.openEditor({ - resource: uri, - options: { preserveFocus: true, revealIfOpened: true, ignoreError: true, selection } - }); - } finally { - disposable.dispose(); - } - }); + // Don't open if preview editors are disabled as it may open many editor + const config = this._configurationService.getValue(); + if (!config.workbench?.editor?.enablePreview) { + return; + } + + const linkSuffix = link.parsedLink ? link.parsedLink.suffix : getLinkSuffix(link.text); + let selection: ITextEditorSelection | undefined;// = link.selection; + if (!selection) { + selection = linkSuffix?.row === undefined ? undefined : { + startLineNumber: linkSuffix.row ?? 1, + startColumn: linkSuffix.col ?? 1, + endLineNumber: linkSuffix.rowEnd, + endColumn: linkSuffix.colEnd + }; } + + this._editorViewState.set(); + this._editorSequencer.queue(async () => { + // disable and re-enable history service so that we can ignore this history entry + const disposable = this._historyService.suspendTracking(); + try { + await this._editorService.openEditor({ + resource: link.uri, + options: { preserveFocus: true, revealIfOpened: true, ignoreError: true, selection, } + }); + } finally { + disposable.dispose(); + } + }); } } From 523dd898669fd40d2587197655c33ee066f9f62a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 24 Feb 2024 07:38:26 -0800 Subject: [PATCH 0162/1175] Simplify setting selection --- .../links/browser/terminalLinkQuickpick.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts index 6ceb6c3db32..0cea7b3f514 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts @@ -19,7 +19,6 @@ import { EditorViewState } from 'vs/workbench/browser/quickaccess'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { getLinkSuffix } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing'; -import type { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import type { IFilesConfiguration } from 'vs/workbench/contrib/files/common/files'; @@ -182,15 +181,12 @@ export class TerminalLinkQuickpick extends DisposableStore { } const linkSuffix = link.parsedLink ? link.parsedLink.suffix : getLinkSuffix(link.text); - let selection: ITextEditorSelection | undefined;// = link.selection; - if (!selection) { - selection = linkSuffix?.row === undefined ? undefined : { - startLineNumber: linkSuffix.row ?? 1, - startColumn: linkSuffix.col ?? 1, - endLineNumber: linkSuffix.rowEnd, - endColumn: linkSuffix.colEnd - }; - } + const selection = linkSuffix?.row === undefined ? undefined : { + startLineNumber: linkSuffix.row ?? 1, + startColumn: linkSuffix.col ?? 1, + endLineNumber: linkSuffix.rowEnd, + endColumn: linkSuffix.colEnd + }; this._editorViewState.set(); this._editorSequencer.queue(async () => { From d2f340649b56500692ae84c82b8fac41f4bf8832 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Sun, 25 Feb 2024 18:38:20 +0100 Subject: [PATCH 0163/1175] Adopt custom hover for inline chat widget (#206170) inline chat custom hover adoption --- src/vs/platform/actions/browser/buttonbar.ts | 19 ++++++++++++++----- .../inlineChat/browser/inlineChatWidget.ts | 10 ++++++++-- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/actions/browser/buttonbar.ts b/src/vs/platform/actions/browser/buttonbar.ts index 21d4c4c5fc6..94720d5b557 100644 --- a/src/vs/platform/actions/browser/buttonbar.ts +++ b/src/vs/platform/actions/browser/buttonbar.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { ButtonBar, IButton } from 'vs/base/browser/ui/button/button'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { ActionRunner, IAction, IActionRunner, SubmenuAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -29,6 +31,7 @@ export interface IWorkbenchButtonBarOptions { export class WorkbenchButtonBar extends ButtonBar { protected readonly _store = new DisposableStore(); + protected readonly _updateStore = new DisposableStore(); private readonly _actionRunner: IActionRunner; private readonly _onDidChange = new Emitter(); @@ -57,6 +60,7 @@ export class WorkbenchButtonBar extends ButtonBar { override dispose() { this._onDidChange.dispose(); + this._updateStore.dispose(); this._store.dispose(); super.dispose(); } @@ -65,8 +69,12 @@ export class WorkbenchButtonBar extends ButtonBar { const conifgProvider: IButtonConfigProvider = this._options?.buttonConfigProvider ?? (() => ({ showLabel: true })); + this._updateStore.clear(); this.clear(); + // Support instamt hover between buttons + const hoverDelegate = this._updateStore.add(getDefaultHoverDelegate('element', true)); + for (let i = 0; i < actions.length; i++) { const secondary = i > 0; @@ -107,15 +115,16 @@ export class WorkbenchButtonBar extends ButtonBar { } } const kb = this._keybindingService.lookupKeybinding(action.id); + let tooltip: string; if (kb) { - btn.element.title = localize('labelWithKeybinding', "{0} ({1})", action.label, kb.getLabel()); + tooltip = localize('labelWithKeybinding', "{0} ({1})", action.label, kb.getLabel()); } else { - btn.element.title = action.label; - + tooltip = action.label; } - btn.onDidClick(async () => { + this._updateStore.add(setupCustomHover(hoverDelegate, btn.element, tooltip)); + this._updateStore.add(btn.onDidClick(async () => { this._actionRunner.run(action); - }); + })); } this._onDidChange.fire(this); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 50bba01479e..084e20c5898 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -66,6 +66,7 @@ import { ExpansionState, HunkData, HunkInformation, Session } from 'vs/workbench import { asRange, invertLineRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; import { ACTION_ACCEPT_CHANGES, ACTION_REGENERATE_RESPONSE, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_END, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_INNER_CURSOR_START, CTX_INLINE_CHAT_MESSAGE_CROP_STATE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_VISIBLE, IInlineChatFollowup, IInlineChatSlashCommand, MENU_INLINE_CHAT_INPUT, MENU_INLINE_CHAT_WIDGET, MENU_INLINE_CHAT_WIDGET_FEEDBACK, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, MENU_INLINE_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; const defaultAriaLabel = localize('aria-label', "Inline Chat Input"); @@ -373,12 +374,16 @@ export class InlineChatWidget { this._slashCommandContentWidget = new SlashCommandContentWidget(this._inputEditor); this._store.add(this._slashCommandContentWidget); + // Share hover delegates between toolbars to support instant hover between both + const hoverDelegate = this._store.add(getDefaultHoverDelegate('element', true)); + // toolbars this._store.add(this._instantiationService.createInstance(MenuWorkbenchToolBar, this._elements.editorToolbar, _options.menuId, { telemetrySource: 'interactiveEditorWidget-toolbar', toolbarOptions: { primaryGroup: 'main' }, - hiddenItemStrategy: HiddenItemStrategy.Ignore // keep it lean when hiding items and avoid a "..." overflow menu + hiddenItemStrategy: HiddenItemStrategy.Ignore, // keep it lean when hiding items and avoid a "..." overflow menu + hoverDelegate })); this._progressBar = new ProgressBar(this._elements.progress); @@ -387,7 +392,8 @@ export class InlineChatWidget { this._store.add(this._instantiationService.createInstance(MenuWorkbenchToolBar, this._elements.widgetToolbar, _options.widgetMenuId, { telemetrySource: 'interactiveEditorWidget-toolbar', - toolbarOptions: { primaryGroup: 'main' } + toolbarOptions: { primaryGroup: 'main' }, + hoverDelegate })); const workbenchMenubarOptions: IWorkbenchButtonBarOptions = { From 03f6a7894e4f3b21a2f4936eeb1dc762606a5bd4 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 26 Feb 2024 02:20:16 +0100 Subject: [PATCH 0164/1175] Updated actionViewItemProvider to include options parameter (#206188) Updated actionViewItemProvider to include options parameter in various components --- .../workbench/browser/parts/compositeBar.ts | 4 +-- .../browser/parts/globalCompositeBar.ts | 5 ++-- .../notifications/notificationsCenter.ts | 3 ++- .../parts/titlebar/commandCenterControl.ts | 11 +++++--- .../browser/parts/titlebar/titlebarPart.ts | 7 ++--- .../browser/parts/views/viewFilter.ts | 5 ++-- .../contrib/comments/browser/commentNode.ts | 26 +++++++++---------- .../extensions/browser/extensionEditor.ts | 8 +++--- .../extensions/browser/extensionsActions.ts | 10 ++++--- .../extensions/browser/extensionsList.ts | 7 ++--- .../contrib/markers/browser/markersTable.ts | 2 +- .../markers/browser/markersTreeViewer.ts | 6 ++--- .../markers/browser/markersViewActions.ts | 8 +++--- .../contrib/find/notebookFindReplaceWidget.ts | 8 +++--- .../notebook/browser/diff/diffComponents.ts | 4 +-- .../notebook/browser/diff/notebookDiffList.ts | 4 +-- .../browser/view/cellParts/cellToolbars.ts | 6 ++--- .../view/cellParts/codeCellRunToolbar.ts | 3 ++- .../viewParts/notebookTopCellToolbar.ts | 4 +-- .../preferences/browser/keybindingsEditor.ts | 5 ++-- .../preferences/browser/preferencesWidgets.ts | 4 +-- .../preferences/browser/settingsEditor2.ts | 4 +-- .../preferences/browser/settingsSearchMenu.ts | 3 +++ .../terminal/browser/terminalTabsList.ts | 4 +-- .../testing/browser/testingExplorerFilter.ts | 7 ++--- .../testing/browser/testingOutputPeek.ts | 4 +-- 26 files changed, 91 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 76d6c0c1ee4..27d0b3738ea 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -201,14 +201,14 @@ export class CompositeBar extends Widget implements ICompositeBar { create(parent: HTMLElement): HTMLElement { const actionBarDiv = parent.appendChild($('.composite-bar')); this.compositeSwitcherBar = this._register(new ActionBar(actionBarDiv, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action instanceof CompositeOverflowActivityAction) { return this.compositeOverflowActionViewItem; } const item = this.model.findItem(action.id); return item && this.instantiationService.createInstance( CompositeActionViewItem, - { draggable: true, colors: this.options.colors, icon: this.options.icon, hoverOptions: this.options.activityHoverOptions, compact: this.options.compact }, + { ...options, draggable: true, colors: this.options.colors, icon: this.options.icon, hoverOptions: this.options.activityHoverOptions, compact: this.options.compact }, action as CompositeBarAction, item.pinnedAction, item.toggleBadgeAction, diff --git a/src/vs/workbench/browser/parts/globalCompositeBar.ts b/src/vs/workbench/browser/parts/globalCompositeBar.ts index db3471fcadc..3490432e312 100644 --- a/src/vs/workbench/browser/parts/globalCompositeBar.ts +++ b/src/vs/workbench/browser/parts/globalCompositeBar.ts @@ -72,15 +72,16 @@ export class GlobalCompositeBar extends Disposable { anchorAxisAlignment: AnchorAxisAlignment.HORIZONTAL }); this.globalActivityActionBar = this._register(new ActionBar(this.element, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === GLOBAL_ACTIVITY_ID) { - return this.instantiationService.createInstance(GlobalActivityActionViewItem, this.contextMenuActionsProvider, { colors: this.colors, hoverOptions: this.activityHoverOptions }, contextMenuAlignmentOptions); + return this.instantiationService.createInstance(GlobalActivityActionViewItem, this.contextMenuActionsProvider, { ...options, colors: this.colors, hoverOptions: this.activityHoverOptions }, contextMenuAlignmentOptions); } if (action.id === ACCOUNTS_ACTIVITY_ID) { return this.instantiationService.createInstance(AccountsActivityActionViewItem, this.contextMenuActionsProvider, { + ...options, colors: this.colors, hoverOptions: this.activityHoverOptions }, diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index e4cbaff2272..40bcde5fdb4 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -173,7 +173,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente const notificationsToolBar = this._register(new ActionBar(toolbarContainer, { ariaLabel: localize('notificationsToolbar', "Notification Center Actions"), actionRunner, - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === ConfigureDoNotDisturbAction.ID) { return this._register(this.instantiationService.createInstance(DropdownMenuActionViewItem, action, { getActions() { @@ -208,6 +208,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente return actions; }, }, this.contextMenuService, { + ...options, actionRunner, classNames: action.class, keybindingProvider: action => this.keybindingService.lookupKeybinding(action.id) diff --git a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts index 3843068e941..72eeea0b590 100644 --- a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts @@ -5,6 +5,7 @@ import { isActiveDocument, reset } from 'vs/base/browser/dom'; import { BaseActionViewItem, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; @@ -46,11 +47,11 @@ export class CommandCenterControl { primaryGroup: () => true, }, telemetrySource: 'commandCenter', - actionViewItemProvider: (action) => { + actionViewItemProvider: (action, options) => { if (action instanceof SubmenuItemAction && action.item.submenu === MenuId.CommandCenterCenter) { - return instantiationService.createInstance(CommandCenterCenterViewItem, action, windowTitle, hoverDelegate, {}); + return instantiationService.createInstance(CommandCenterCenterViewItem, action, windowTitle, { ...options, hoverDelegate }); } else { - return createActionViewItem(instantiationService, action, { hoverDelegate }); + return createActionViewItem(instantiationService, action, { ...options, hoverDelegate }); } } }); @@ -75,16 +76,18 @@ class CommandCenterCenterViewItem extends BaseActionViewItem { private static readonly _quickOpenCommandId = 'workbench.action.quickOpenWithModes'; + private readonly _hoverDelegate: IHoverDelegate; + constructor( private readonly _submenu: SubmenuItemAction, private readonly _windowTitle: WindowTitle, - private readonly _hoverDelegate: IHoverDelegate, options: IBaseActionViewItemOptions, @IKeybindingService private _keybindingService: IKeybindingService, @IInstantiationService private _instaService: IInstantiationService, @IEditorGroupsService private _editorGroupService: IEditorGroupsService, ) { super(undefined, _submenu.actions.find(action => action.id === 'workbench.action.quickOpenWithModes') ?? _submenu.actions[0], options); + this._hoverDelegate = options.hoverDelegate ?? getDefaultHoverDelegate('mouse'); } override render(container: HTMLElement): void { diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 05ad43933f8..176ac265172 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -532,7 +532,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { // --- Editor Actions const activeEditorPane = this.editorGroupsContainer.activeGroup?.activeEditorPane; if (activeEditorPane && activeEditorPane instanceof EditorPane) { - const result = activeEditorPane.getActionViewItem(action, { hoverDelegate: this.hoverDelegate }); + const result = activeEditorPane.getActionViewItem(action, options); if (result) { return result; @@ -540,7 +540,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { } // Check extensions - return createActionViewItem(this.instantiationService, action, { ...options, hoverDelegate: this.hoverDelegate, menuAsChild: false }); + return createActionViewItem(this.instantiationService, action, { ...options, menuAsChild: false }); } private getKeybinding(action: IAction): ResolvedKeybinding | undefined { @@ -565,7 +565,8 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { anchorAlignmentProvider: () => AnchorAlignment.RIGHT, telemetrySource: 'titlePart', highlightToggledItems: this.editorActionsEnabled, // Only show toggled state for editor actions (Layout actions are not shown as toggled) - actionViewItemProvider: (action, options) => this.actionViewItemProvider(action, options) + actionViewItemProvider: (action, options) => this.actionViewItemProvider(action, options), + hoverDelegate: this.hoverDelegate })); if (this.editorActionsEnabled) { diff --git a/src/vs/workbench/browser/parts/views/viewFilter.ts b/src/vs/workbench/browser/parts/views/viewFilter.ts index 3e5763a2203..b6285e45c71 100644 --- a/src/vs/workbench/browser/parts/views/viewFilter.ts +++ b/src/vs/workbench/browser/parts/views/viewFilter.ts @@ -25,6 +25,7 @@ import { SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntr import { Widget } from 'vs/base/browser/ui/widget'; import { Emitter } from 'vs/base/common/event'; import { defaultInputBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; const viewFilterMenu = new MenuId('menu.view.filter'); export const viewFilterSubmenu = new MenuId('submenu.view.filter'); @@ -196,9 +197,9 @@ export class FilterWidget extends Widget { return this.instantiationService.createInstance(MenuWorkbenchToolBar, container, viewFilterMenu, { hiddenItemStrategy: HiddenItemStrategy.NoHide, - actionViewItemProvider: (action: IAction) => { + actionViewItemProvider: (action: IAction, options: IActionViewItemOptions) => { if (action instanceof SubmenuItemAction && action.item.submenu.id === viewFilterSubmenu.id) { - this.moreFiltersActionViewItem = this.instantiationService.createInstance(MoreFiltersActionViewItem, action, undefined); + this.moreFiltersActionViewItem = this.instantiationService.createInstance(MoreFiltersActionViewItem, action, options); this.moreFiltersActionViewItem.checked = this.isMoreFiltersChecked; return this.moreFiltersActionViewItem; } diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 7085518f94a..58e376f9561 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -30,7 +30,7 @@ import { MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; -import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { ActionViewItem, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; @@ -301,21 +301,22 @@ export class CommentNode extends Disposable { private createToolbar() { this.toolbar = new ToolBar(this._actionsToolbarContainer, this.contextMenuService, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === ToggleReactionsAction.ID) { return new DropdownMenuActionViewItem( action, (action).menuActions, this.contextMenuService, { - actionViewItemProvider: action => this.actionViewItemProvider(action as Action), + ...options, + actionViewItemProvider: (action, options) => this.actionViewItemProvider(action as Action, options), actionRunner: this.actionRunner, classNames: ['toolbar-toggle-pickReactions', ...ThemeIcon.asClassNameArray(Codicon.reactions)], anchorAlignmentProvider: () => AnchorAlignment.RIGHT } ); } - return this.actionViewItemProvider(action as Action); + return this.actionViewItemProvider(action as Action, options); }, orientation: ActionsOrientation.HORIZONTAL }); @@ -357,8 +358,7 @@ export class CommentNode extends Disposable { } } - actionViewItemProvider(action: Action) { - let options = {}; + actionViewItemProvider(action: Action, options: IActionViewItemOptions) { if (action.id === ToggleReactionsAction.ID) { options = { label: false, icon: true }; } else { @@ -369,9 +369,9 @@ export class CommentNode extends Disposable { const item = new ReactionActionViewItem(action); return item; } else if (action instanceof MenuItemAction) { - return this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined); + return this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }); } else if (action instanceof SubmenuItemAction) { - return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action, undefined); + return this.instantiationService.createInstance(SubmenuEntryActionViewItem, action, options); } else { const item = new ActionViewItem({}, action, options); return item; @@ -413,11 +413,11 @@ export class CommentNode extends Disposable { (toggleReactionAction).menuActions, this.contextMenuService, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === ToggleReactionsAction.ID) { return toggleReactionActionViewItem; } - return this.actionViewItemProvider(action as Action); + return this.actionViewItemProvider(action as Action, options); }, actionRunner: this.actionRunner, classNames: 'toolbar-toggle-pickReactions', @@ -431,21 +431,21 @@ export class CommentNode extends Disposable { private createReactionsContainer(commentDetailsContainer: HTMLElement): void { this._reactionActionsContainer = dom.append(commentDetailsContainer, dom.$('div.comment-reactions')); this._reactionsActionBar = new ActionBar(this._reactionActionsContainer, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === ToggleReactionsAction.ID) { return new DropdownMenuActionViewItem( action, (action).menuActions, this.contextMenuService, { - actionViewItemProvider: action => this.actionViewItemProvider(action as Action), + actionViewItemProvider: (action, options) => this.actionViewItemProvider(action as Action, options), actionRunner: this.actionRunner, classNames: ['toolbar-toggle-pickReactions', ...ThemeIcon.asClassNameArray(Codicon.reactions)], anchorAlignmentProvider: () => AnchorAlignment.RIGHT } ); } - return this.actionViewItemProvider(action as Action); + return this.actionViewItemProvider(action as Action, options); } }); this._register(this._reactionsActionBar); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 097f4a3c634..11ee0d9a2b9 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -344,15 +344,15 @@ export class ExtensionEditor extends EditorPane { const actionsAndStatusContainer = append(details, $('.actions-status-container')); const extensionActionBar = this._register(new ActionBar(actionsAndStatusContainer, { - actionViewItemProvider: (action: IAction) => { + actionViewItemProvider: (action: IAction, options) => { if (action instanceof ExtensionDropDownAction) { - return action.createActionViewItem(); + return action.createActionViewItem(options); } if (action instanceof ActionWithDropDownAction) { - return new ExtensionActionWithDropdownActionViewItem(action, { icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService); + return new ExtensionActionWithDropdownActionViewItem(action, { ...options, icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService); } if (action instanceof ToggleAutoUpdateForExtensionAction) { - return new CheckboxActionViewItem(undefined, action, { icon: true, label: true, checkboxStyles: defaultCheckboxStyles }); + return new CheckboxActionViewItem(undefined, action, { ...options, icon: true, label: true, checkboxStyles: defaultCheckboxStyles }); } return undefined; }, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 1fc58a9e0f5..6e0155f3249 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1021,8 +1021,8 @@ export abstract class ExtensionDropDownAction extends ExtensionAction { } private _actionViewItem: DropDownMenuActionViewItem | null = null; - createActionViewItem(): DropDownMenuActionViewItem { - this._actionViewItem = this.instantiationService.createInstance(DropDownMenuActionViewItem, this); + createActionViewItem(options: IActionViewItemOptions): DropDownMenuActionViewItem { + this._actionViewItem = this.instantiationService.createInstance(DropDownMenuActionViewItem, this, options); return this._actionViewItem; } @@ -1034,10 +1034,12 @@ export abstract class ExtensionDropDownAction extends ExtensionAction { export class DropDownMenuActionViewItem extends ActionViewItem { - constructor(action: ExtensionDropDownAction, + constructor( + action: ExtensionDropDownAction, + options: IActionViewItemOptions, @IContextMenuService private readonly contextMenuService: IContextMenuService ) { - super(null, action, { icon: true, label: true }); + super(null, action, { ...options, icon: true, label: true }); } public showMenu(menuActionGroups: IAction[][], disposeActionsOnHide: boolean): void { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 1bf769f4c66..d5058c51b43 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -26,6 +26,7 @@ import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; import { verifiedPublisherIcon as verifiedPublisherThemeIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; const EXTENSION_LIST_ELEMENT_HEIGHT = 72; @@ -98,12 +99,12 @@ export class Renderer implements IPagedRenderer { const verifiedPublisherWidget = this.instantiationService.createInstance(VerifiedPublisherWidget, append(publisher, $(`.verified-publisher`)), true); const publisherDisplayName = append(publisher, $('.publisher-name.ellipsis')); const actionbar = new ActionBar(footer, { - actionViewItemProvider: (action: IAction) => { + actionViewItemProvider: (action: IAction, options: IActionViewItemOptions) => { if (action instanceof ActionWithDropDownAction) { - return new ExtensionActionWithDropdownActionViewItem(action, { icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService); + return new ExtensionActionWithDropdownActionViewItem(action, { ...options, icon: true, label: true, menuActionsOrProvider: { getActions: () => action.menuActions }, menuActionClassNames: (action.class || '').split(' ') }, this.contextMenuService); } if (action instanceof ExtensionDropDownAction) { - return action.createActionViewItem(); + return action.createActionViewItem(options); } return undefined; }, diff --git a/src/vs/workbench/contrib/markers/browser/markersTable.ts b/src/vs/workbench/contrib/markers/browser/markersTable.ts index dc454d054ed..bb63d92e9d2 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTable.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTable.ts @@ -74,7 +74,7 @@ class MarkerSeverityColumnRenderer implements ITableRenderer action.id === QuickFixAction.ID ? this.instantiationService.createInstance(QuickFixActionViewItem, action) : undefined + actionViewItemProvider: (action: IAction, options) => action.id === QuickFixAction.ID ? this.instantiationService.createInstance(QuickFixActionViewItem, action, options) : undefined }); return { actionBar, icon }; diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 4b7a07b3b17..180a378ac81 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -295,7 +295,7 @@ class MarkerWidget extends Disposable { ) { super(); this.actionBar = this._register(new ActionBar(dom.append(parent, dom.$('.actions')), { - actionViewItemProvider: (action: IAction) => action.id === QuickFixAction.ID ? _instantiationService.createInstance(QuickFixActionViewItem, action) : undefined + actionViewItemProvider: (action: IAction, options) => action.id === QuickFixAction.ID ? _instantiationService.createInstance(QuickFixActionViewItem, action, options) : undefined })); // wrap the icon in a container that get the icon color as foreground color. That way, if the @@ -342,9 +342,9 @@ class MarkerWidget extends Disposable { private renderMultilineActionbar(marker: Marker, parent: HTMLElement): void { const multilineActionbar = this.disposables.add(new ActionBar(dom.append(parent, dom.$('.multiline-actions')), { - actionViewItemProvider: (action) => { + actionViewItemProvider: (action, options) => { if (action.id === toggleMultilineAction) { - return new ToggleMultilineActionViewItem(undefined, action, { icon: true }); + return new ToggleMultilineActionViewItem(undefined, action, { ...options, icon: true }); } return undefined; } diff --git a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts index 818f2f56fe6..74fa00a81ae 100644 --- a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts @@ -13,7 +13,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Event, Emitter } from 'vs/base/common/event'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; -import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { ActionViewItem, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { MarkersContextKeys } from 'vs/workbench/contrib/markers/common/markers'; import 'vs/css!./markersViewActions'; @@ -145,10 +145,12 @@ export class QuickFixAction extends Action { export class QuickFixActionViewItem extends ActionViewItem { - constructor(action: QuickFixAction, + constructor( + action: QuickFixAction, + options: IActionViewItemOptions, @IContextMenuService private readonly contextMenuService: IContextMenuService, ) { - super(null, action, { icon: true, label: false }); + super(null, action, { ...options, icon: true, label: false }); } public override onClick(event: DOM.EventLike): void { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts index 480033afb33..fd9a432d6c3 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -40,6 +40,7 @@ import { defaultInputBoxStyles, defaultProgressBarStyles, defaultToggleStyles } import { IToggleStyles } from 'vs/base/browser/ui/toggle/toggle'; import { Disposable } from 'vs/base/common/lifecycle'; import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); @@ -63,11 +64,12 @@ const NOTEBOOK_FIND_IN_CODE_OUTPUT = nls.localize('notebook.find.filter.findInCo const NOTEBOOK_FIND_WIDGET_INITIAL_WIDTH = 318; const NOTEBOOK_FIND_WIDGET_INITIAL_HORIZONTAL_PADDING = 4; class NotebookFindFilterActionViewItem extends DropdownMenuActionViewItem { - constructor(readonly filters: NotebookFindFilters, action: IAction, actionRunner: IActionRunner, @IContextMenuService contextMenuService: IContextMenuService) { + constructor(readonly filters: NotebookFindFilters, action: IAction, options: IActionViewItemOptions, actionRunner: IActionRunner, @IContextMenuService contextMenuService: IContextMenuService) { super(action, { getActions: () => this.getActions() }, contextMenuService, { + ...options, actionRunner, classNames: action.class, anchorAlignmentProvider: () => AnchorAlignment.RIGHT @@ -196,9 +198,9 @@ export class NotebookFindInputFilterButton extends Disposable { private createFilters(container: HTMLElement): void { this._actionbar = this._register(new ActionBar(container, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === this._filtersAction.id) { - return this.instantiationService.createInstance(NotebookFindFilterActionViewItem, this.filters, action, new ActionRunner()); + return this.instantiationService.createInstance(NotebookFindFilterActionViewItem, this.filters, action, options, new ActionRunner()); } return undefined; } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index c1218b15ce8..43b387b9689 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -117,9 +117,9 @@ class PropertyHeader extends Disposable { const cellToolbarContainer = DOM.append(this.propertyHeaderContainer, DOM.$('div.property-toolbar')); this._toolbar = new WorkbenchToolBar(cellToolbarContainer, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action instanceof MenuItemAction) { - const item = new CodiconActionViewItem(action, undefined, this.keybindingService, this.notificationService, this.contextKeyService, this.themeService, this.contextMenuService, this.accessibilityService); + const item = new CodiconActionViewItem(action, { hoverDelegate: options.hoverDelegate }, this.keybindingService, this.notificationService, this.contextKeyService, this.themeService, this.contextMenuService, this.accessibilityService); return item; } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts index 9248d1cee6e..4e92fa8a42f 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts @@ -189,9 +189,9 @@ export class CellDiffSideBySideRenderer implements IListRenderer { + actionViewItemProvider: (action, options) => { if (action instanceof MenuItemAction) { - const item = new CodiconActionViewItem(action, undefined, this.keybindingService, this.notificationService, this.contextKeyService, this.themeService, this.contextMenuService, this.accessibilityService); + const item = new CodiconActionViewItem(action, { hoverDelegate: options.hoverDelegate }, this.keybindingService, this.notificationService, this.contextKeyService, this.themeService, this.contextMenuService, this.accessibilityService); return item; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index 12b86db201e..f10530616f1 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -46,12 +46,12 @@ export class BetweenCellToolbar extends CellOverlayPart { } const betweenCellToolbar = this._register(new ToolBar(this._bottomCellToolbarContainer, this.contextMenuService, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action instanceof MenuItemAction) { if (this._notebookEditor.notebookOptions.getDisplayOptions().insertToolbarAlignment === 'center') { - return this.instantiationService.createInstance(CodiconActionViewItem, action, undefined); + return this.instantiationService.createInstance(CodiconActionViewItem, action, { hoverDelegate: options.hoverDelegate }); } else { - return this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined); + return this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }); } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts index 48974ad10a5..de2c0e912bf 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts @@ -84,7 +84,7 @@ export class RunToolbar extends CellContentPart { const executionContextKeyService = this._register(getCodeCellExecutionContextKeyService(contextKeyService)); this.toolbar = this._register(new ToolBar(container, this.contextMenuService, { getKeyBinding: keybindingProvider, - actionViewItemProvider: _action => { + actionViewItemProvider: (_action, _options) => { actionViewItemDisposables.clear(); const primary = this.getCellToolbarActions(this.primaryMenu).primary[0]; @@ -104,6 +104,7 @@ export class RunToolbar extends CellContentPart { 'notebook-cell-run-toolbar', this.contextMenuService, { + ..._options, getKeyBinding: keybindingProvider }); actionViewItemDisposables.add(item.onDidChangeDropdownVisibility(visible => { diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookTopCellToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookTopCellToolbar.ts index 9a558d0dd28..f606649ca03 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookTopCellToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookTopCellToolbar.ts @@ -96,9 +96,9 @@ export class ListTopCellToolbar extends Disposable { DOM.clearNode(this.topCellToolbar); const toolbar = this.instantiationService.createInstance(MenuWorkbenchToolBar, this.topCellToolbar, this.notebookEditor.creationOptions.menuIds.cellTopInsertToolbar, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action instanceof MenuItemAction) { - const item = this.instantiationService.createInstance(CodiconActionViewItem, action, undefined); + const item = this.instantiationService.createInstance(CodiconActionViewItem, action, { hoverDelegate: options.hoverDelegate }); return item; } diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index b0026da674c..eec7a8ce974 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -57,6 +57,7 @@ import { settingsTextInputBorder } from 'vs/workbench/contrib/preferences/common import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { registerNavigableContainer } from 'vs/workbench/browser/actions/widgetNavigationCommands'; +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; const $ = DOM.$; @@ -397,9 +398,9 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP const actions = [this.recordKeysAction, this.sortByPrecedenceAction, clearInputAction]; const toolBar = this._register(new ToolBar(this.actionsContainer, this.contextMenuService, { - actionViewItemProvider: (action: IAction) => { + actionViewItemProvider: (action: IAction, options: IActionViewItemOptions) => { if (action.id === this.sortByPrecedenceAction.id || action.id === this.recordKeysAction.id) { - return new ToggleActionViewItem(null, action, { keybinding: this.keybindingsService.lookupKeybinding(action.id)?.getLabel(), toggleStyles: defaultToggleStyles }); + return new ToggleActionViewItem(null, action, { ...options, keybinding: this.keybindingsService.lookupKeybinding(action.id)?.getLabel(), toggleStyles: defaultToggleStyles }); } return undefined; }, diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 1be32db1a39..30542844cc9 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -6,7 +6,7 @@ import * as DOM from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; -import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { BaseActionViewItem, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { HistoryInputBox, IHistoryInputOptions } from 'vs/base/browser/ui/inputbox/inputBox'; import { Widget } from 'vs/base/browser/ui/widget'; import { Action, IAction } from 'vs/base/common/actions'; @@ -252,7 +252,7 @@ export class SettingsTargetsWidget extends Widget { orientation: ActionsOrientation.HORIZONTAL, focusOnlyEnabledItems: true, ariaLabel: localize('settingsSwitcherBarAriaLabel', "Settings Switcher"), - actionViewItemProvider: (action: IAction) => action.id === 'folderSettings' ? this.folderSettings : undefined + actionViewItemProvider: (action: IAction, options: IActionViewItemOptions) => action.id === 'folderSettings' ? this.folderSettings : undefined })); this.userLocalSettings = new Action('userSettings', '', '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_LOCAL)); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index c0632e494bc..33c9e99303b 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -655,9 +655,9 @@ export class SettingsEditor2 extends EditorPane { this.controlsElement = DOM.append(searchContainer, DOM.$('.settings-clear-widget')); const actionBar = this._register(new ActionBar(this.controlsElement, { - actionViewItemProvider: (action) => { + actionViewItemProvider: (action, options) => { if (action.id === filterAction.id) { - return this.instantiationService.createInstance(SettingsSearchFilterDropdownMenuActionViewItem, action, this.actionRunner, this.searchWidget); + return this.instantiationService.createInstance(SettingsSearchFilterDropdownMenuActionViewItem, action, options, this.actionRunner, this.searchWidget); } return undefined; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts index 93e13c0234d..d119cd97f69 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { IAction, IActionRunner } from 'vs/base/common/actions'; @@ -17,6 +18,7 @@ export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenu constructor( action: IAction, + options: IActionViewItemOptions, actionRunner: IActionRunner | undefined, private readonly searchWidget: SuggestEnabledInput, @IContextMenuService contextMenuService: IContextMenuService @@ -25,6 +27,7 @@ export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenu { getActions: () => this.getActions() }, contextMenuService, { + ...options, actionRunner, classNames: action.class, anchorAlignmentProvider: () => AnchorAlignment.RIGHT, diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index e9bf671e021..e2fd9619a69 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -279,9 +279,9 @@ class TerminalTabsRenderer implements IListRenderer + actionViewItemProvider: (action, options) => action instanceof MenuItemAction - ? this._instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) + ? this._instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }) : undefined }); diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts index a7490c873cf..16b2f5f1e92 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts @@ -5,7 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { BaseActionViewItem, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { BaseActionViewItem, IActionViewItemOptions, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { Action, IAction, IActionRunner, Separator } from 'vs/base/common/actions'; @@ -121,9 +121,9 @@ export class TestingExplorerFilter extends BaseActionViewItem { }))); const actionbar = this._register(new ActionBar(container, { - actionViewItemProvider: action => { + actionViewItemProvider: (action, options) => { if (action.id === this.filtersAction.id) { - return this.instantiationService.createInstance(FiltersDropdownMenuActionViewItem, action, this.state, this.actionRunner); + return this.instantiationService.createInstance(FiltersDropdownMenuActionViewItem, action, options, this.state, this.actionRunner); } return undefined; }, @@ -176,6 +176,7 @@ class FiltersDropdownMenuActionViewItem extends DropdownMenuActionViewItem { constructor( action: IAction, + options: IActionViewItemOptions, private readonly filters: ITestExplorerFilterState, actionRunner: IActionRunner, @IContextMenuService contextMenuService: IContextMenuService, diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 7f5dcfb7920..901d0a5d5da 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -2227,9 +2227,9 @@ class TestRunElementRenderer implements ICompressibleTreeRenderer + actionViewItemProvider: (action, options) => action instanceof MenuItemAction - ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) + ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }) : undefined }); From 13a2ba89a6159246bdf9b9d220c30a57eecfbd79 Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Sun, 25 Feb 2024 20:49:24 +0100 Subject: [PATCH 0165/1175] rename suggestions: fix width overflow --- src/vs/editor/contrib/rename/browser/renameInputField.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/editor/contrib/rename/browser/renameInputField.ts b/src/vs/editor/contrib/rename/browser/renameInputField.ts index cf76d531a2a..1b3728c30c6 100644 --- a/src/vs/editor/contrib/rename/browser/renameInputField.ts +++ b/src/vs/editor/contrib/rename/browser/renameInputField.ts @@ -515,7 +515,6 @@ class CandidatesView { public layout({ height, width }: { height: number; width: number }): void { this._availableHeight = height; this._minimumWidth = width; - this._listContainer.style.width = `${this._minimumWidth}px`; } public setCandidates(candidates: NewSymbolName[]): void { From a389ff1c805a8cb4b8044a5d88a24323ae268ecd Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 26 Feb 2024 10:28:28 +0100 Subject: [PATCH 0166/1175] Use compact hover by default for workbench (#206224) Use a compact hover by default for workbench --- src/vs/platform/hover/browser/hover.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/platform/hover/browser/hover.ts b/src/vs/platform/hover/browser/hover.ts index 82d9574ca06..fea45187b43 100644 --- a/src/vs/platform/hover/browser/hover.ts +++ b/src/vs/platform/hover/browser/hover.ts @@ -285,6 +285,9 @@ export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate hideOnHover: true, hideOnKeyDown: true, }, + appearance: { + compact: true, + }, ...overrideOptions }, focus); } From 2c045aee0a449ff5f400f893e8e97fa9522a09f9 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 26 Feb 2024 11:33:40 +0100 Subject: [PATCH 0167/1175] Hoverservice has it's own contextview instance (#206151) Hoverservice with it's own context view --- .../services/hoverService/hoverService.ts | 20 +++++++++++-------- .../contextview/browser/contextViewService.ts | 20 +++++++++++-------- .../parts/editor/breadcrumbsControl.ts | 4 ++-- .../browser/parts/editor/breadcrumbsPicker.ts | 7 ++----- .../browser/outline/documentSymbolsTree.ts | 11 ++-------- 5 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/vs/editor/browser/services/hoverService/hoverService.ts b/src/vs/editor/browser/services/hoverService/hoverService.ts index f7338ae7b52..47fddf11c86 100644 --- a/src/vs/editor/browser/services/hoverService/hoverService.ts +++ b/src/vs/editor/browser/services/hoverService/hoverService.ts @@ -7,11 +7,11 @@ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/ import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { editorHoverBorder } from 'vs/platform/theme/common/colorRegistry'; import { IHoverService, IHoverOptions } from 'vs/platform/hover/browser/hover'; -import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { HoverWidget } from 'vs/editor/browser/services/hoverService/hoverWidget'; import { IContextViewProvider, IDelegate } from 'vs/base/browser/ui/contextview/contextview'; -import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { addDisposableListener, EventType, getActiveElement, isAncestorOfActiveElement, isAncestor, getWindow } from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -20,10 +20,12 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { mainWindow } from 'vs/base/browser/window'; import { IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { ContextViewHandler } from 'vs/platform/contextview/browser/contextViewService'; -export class HoverService implements IHoverService { +export class HoverService extends Disposable implements IHoverService { declare readonly _serviceBrand: undefined; + private _contextViewHandler: IContextViewProvider; private _currentHoverOptions: IHoverOptions | undefined; private _currentHover: HoverWidget | undefined; private _lastHoverOptions: IHoverOptions | undefined; @@ -32,13 +34,15 @@ export class HoverService implements IHoverService { constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IContextViewService private readonly _contextViewService: IContextViewService, @IContextMenuService contextMenuService: IContextMenuService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @ILayoutService private readonly _layoutService: ILayoutService, @IAccessibilityService private readonly _accessibilityService: IAccessibilityService ) { + super(); + contextMenuService.onDidShowContextMenu(() => this.hideHover()); + this._contextViewHandler = this._register(new ContextViewHandler(this._layoutService)); } showHover(options: IHoverOptions, focus?: boolean, skipLastFocusedUpdate?: boolean): IHoverWidget | undefined { @@ -84,12 +88,12 @@ export class HoverService implements IHoverService { const targetElement = options.target instanceof HTMLElement ? options.target : options.target.targetElements[0]; options.container = this._layoutService.getContainer(getWindow(targetElement)); } - const provider = this._contextViewService as IContextViewProvider; - provider.showContextView( + + this._contextViewHandler.showContextView( new HoverContextViewDelegate(hover, focus), options.container ); - hover.onRequestLayout(() => provider.layout()); + hover.onRequestLayout(() => this._contextViewHandler.layout()); if (options.persistence?.sticky) { hoverDisposables.add(addDisposableListener(getWindow(options.container).document, EventType.MOUSE_DOWN, e => { if (!isAncestor(e.target as HTMLElement, hover.domNode)) { @@ -136,7 +140,7 @@ export class HoverService implements IHoverService { private doHideHover(): void { this._currentHover = undefined; this._currentHoverOptions = undefined; - this._contextViewService.hideContextView(); + this._contextViewHandler.hideContextView(); } private _intersectionChange(entries: IntersectionObserverEntry[], hover: IDisposable): void { diff --git a/src/vs/platform/contextview/browser/contextViewService.ts b/src/vs/platform/contextview/browser/contextViewService.ts index f47285746fe..929cb32d5a8 100644 --- a/src/vs/platform/contextview/browser/contextViewService.ts +++ b/src/vs/platform/contextview/browser/contextViewService.ts @@ -3,18 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ContextView, ContextViewDOMPosition } from 'vs/base/browser/ui/contextview/contextview'; +import { ContextView, ContextViewDOMPosition, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IContextViewDelegate, IContextViewService } from './contextView'; import { getWindow } from 'vs/base/browser/dom'; -export class ContextViewService extends Disposable implements IContextViewService { - declare readonly _serviceBrand: undefined; +export class ContextViewHandler extends Disposable implements IContextViewProvider { private currentViewDisposable: IDisposable = Disposable.None; - private readonly contextView = this._register(new ContextView(this.layoutService.mainContainer, ContextViewDOMPosition.ABSOLUTE)); + protected readonly contextView = this._register(new ContextView(this.layoutService.mainContainer, ContextViewDOMPosition.ABSOLUTE)); constructor( @ILayoutService private readonly layoutService: ILayoutService @@ -55,10 +54,6 @@ export class ContextViewService extends Disposable implements IContextViewServic return disposable; } - getContextViewElement(): HTMLElement { - return this.contextView.getViewElement(); - } - layout(): void { this.contextView.layout(); } @@ -74,3 +69,12 @@ export class ContextViewService extends Disposable implements IContextViewServic this.currentViewDisposable = Disposable.None; } } + +export class ContextViewService extends ContextViewHandler implements IContextViewService { + + declare readonly _serviceBrand: undefined; + + getContextViewElement(): HTMLElement { + return this.contextView.getViewElement(); + } +} diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 2a9d829b3ff..bf46167248f 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -41,7 +41,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { defaultBreadcrumbsWidgetStyles } from 'vs/platform/theme/browser/defaultStyles'; import { Emitter } from 'vs/base/common/event'; import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { nativeHoverDelegate } from 'vs/platform/hover/browser/hover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; class OutlineItem extends BreadcrumbsItem { @@ -229,7 +229,7 @@ export class BreadcrumbsControl { this._ckBreadcrumbsVisible = BreadcrumbsControl.CK_BreadcrumbsVisible.bindTo(this._contextKeyService); this._ckBreadcrumbsActive = BreadcrumbsControl.CK_BreadcrumbsActive.bindTo(this._contextKeyService); - this._hoverDelegate = nativeHoverDelegate; + this._hoverDelegate = getDefaultHoverDelegate('mouse'); this._disposables.add(breadcrumbsService.register(this._editorGroup.id, this._widget)); this.hide(); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 26f77202d53..9ca0b4310a5 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -31,8 +31,6 @@ import { IOutline, IOutlineComparator } from 'vs/workbench/services/outline/brow import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { nativeHoverDelegate } from 'vs/platform/hover/browser/hover'; interface ILayoutInfo { maxHeight: number; @@ -215,13 +213,12 @@ class FileRenderer implements ITreeRenderer, index: number, templateData: IResourceLabel): void { @@ -377,7 +374,7 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { 'BreadcrumbsFilePicker', container, new FileVirtualDelegate(), - [this._instantiationService.createInstance(FileRenderer, labels, nativeHoverDelegate)], + [this._instantiationService.createInstance(FileRenderer, labels)], this._instantiationService.createInstance(FileDataSource), { multipleSelectionSupport: false, diff --git a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts index 4e8761ffb1d..c4f2a2def5a 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts @@ -24,9 +24,6 @@ import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IOutlineComparator, OutlineConfigKeys, OutlineTarget } from 'vs/workbench/services/outline/browser/outline'; import { ThemeIcon } from 'vs/base/common/themables'; import { mainWindow } from 'vs/base/browser/window'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { nativeHoverDelegate } from 'vs/platform/hover/browser/hover'; export type DocumentSymbolItem = OutlineGroup | OutlineElement; @@ -118,20 +115,16 @@ export class DocumentSymbolRenderer implements ITreeRenderer Date: Mon, 26 Feb 2024 20:11:47 +0900 Subject: [PATCH 0168/1175] chore: update to electron 28 (#203956) * chore: update electron@28.1.4 * ci: use latest Node.js v18 release 18.18.2 has npm version that has removed the node-gyp script which will cause native modules fail to build that rely on something like `install: node-gyp rebuild` Refs https://github.com/npm/cli/commit/c93edb55f52532e666a9ba2719bee0da725fe6f0 * chore: update rpm dependencies * chore: bump electron@28.2.1 * chore: bump nodejs@18.18.2 * chore: bump electron@28.2.2 * chore: bump distro --- .nvmrc | 2 +- .yarnrc | 4 +- build/azure-pipelines/linux/install.sh | 8 +- build/checksums/electron.txt | 150 ++++++++++++------------- build/checksums/nodejs.txt | 12 +- build/linux/dependencies-generator.js | 2 +- build/linux/dependencies-generator.ts | 2 +- build/linux/rpm/dep-lists.js | 3 - build/linux/rpm/dep-lists.ts | 3 - cgmanifest.json | 14 +-- package.json | 4 +- remote/.yarnrc | 4 +- yarn.lock | 8 +- 13 files changed, 105 insertions(+), 111 deletions(-) diff --git a/.nvmrc b/.nvmrc index 4a1f488b6c3..a9d087399d7 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.17.1 +18.19.0 diff --git a/.yarnrc b/.yarnrc index 19c5cb1eb8f..8675f7ab83c 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,5 +1,5 @@ disturl "https://electronjs.org/headers" -target "27.3.2" -ms_build_id "26836302" +target "28.2.2" +ms_build_id "26836304" runtime "electron" build_from_source "true" diff --git a/build/azure-pipelines/linux/install.sh b/build/azure-pipelines/linux/install.sh index 57f58763cca..c75100cac49 100755 --- a/build/azure-pipelines/linux/install.sh +++ b/build/azure-pipelines/linux/install.sh @@ -15,7 +15,7 @@ SYSROOT_ARCH="$SYSROOT_ARCH" node -e '(async () => { const { getVSCodeSysroot } if [ "$npm_config_arch" == "x64" ]; then # Download clang based on chromium revision used by vscode - curl -s https://raw.githubusercontent.com/chromium/chromium/118.0.5993.159/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux + curl -s https://raw.githubusercontent.com/chromium/chromium/120.0.6099.268/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux # Download libcxx headers and objects from upstream electron releases DEBUG=libcxx-fetcher \ @@ -27,9 +27,9 @@ if [ "$npm_config_arch" == "x64" ]; then # Set compiler toolchain # Flags for the client build are based on - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/118.0.5993.159:build/config/arm.gni - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/118.0.5993.159:build/config/compiler/BUILD.gn - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/118.0.5993.159:build/config/c++/BUILD.gn + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/120.0.6099.268:build/config/arm.gni + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/120.0.6099.268:build/config/compiler/BUILD.gn + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/120.0.6099.268:build/config/c++/BUILD.gn export CC="$PWD/.build/CR_Clang/bin/clang --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" export CXX="$PWD/.build/CR_Clang/bin/clang++ --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" export CXXFLAGS="-nostdinc++ -D__NO_INLINE__ -I$PWD/.build/libcxx_headers -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit -D_LIBCPP_ABI_NAMESPACE=Cr --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot" diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index a774dffc830..35feee9b876 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -1,75 +1,75 @@ -032e54843700736bf3566518ff88717b2dc70be41bdc43840993fcb4cd9c82e8 *chromedriver-v27.3.2-darwin-arm64.zip -7d693267bacc510b724b97db23e21e22983e9f500605a132ab519303ec2e4d94 *chromedriver-v27.3.2-darwin-x64.zip -5f3f417986667e4c82c492b30c14892b0fef3a6dcf07860e74f7d7ba29f0ca41 *chromedriver-v27.3.2-linux-arm64.zip -84364d9c1fc53ce6f29e41d08d12351a2a4a208646acf02551c6f9aa6029c163 *chromedriver-v27.3.2-linux-armv7l.zip -7d3965a5ca3217e16739153d2817fc292e7cb16f55034fde76f26bdc916e60d1 *chromedriver-v27.3.2-linux-x64.zip -068adc1ea9e1d21dcfef1468b2b789714c93465c1874dbd3bf2872a695a1279f *chromedriver-v27.3.2-mas-arm64.zip -0d4d4bb8971260cbc0058cab2a7972e556b83a19d6ea062ea226e7a8555bc369 *chromedriver-v27.3.2-mas-x64.zip -83ffc61b6b524ee0caa0e5cd02dcd00adcd166ba1e03e7fc50206a299a6fca11 *chromedriver-v27.3.2-win32-arm64.zip -df4e9f20681b3e7b65c41dd1df3aa8cb9bc0a061a24ddcffbe44a9191aa01e0c *chromedriver-v27.3.2-win32-ia32.zip -1ef67b7c06061e691176df5e3463f4d5f5f258946dac24ae62e3cc250b8b95d1 *chromedriver-v27.3.2-win32-x64.zip -f3c52d205572da71a23f436b4708dc89c721a74f0e0c5c51093e3e331b1dff67 *electron-api.json -1489dca88c89f6fef05bdc2c08b9623bb46eb8d0f43020985776daef08642061 *electron-v27.3.2-darwin-arm64-dsym-snapshot.zip -7ee895e81d695c1ed65378ff4514d4fc9c4015a1c3c67691765f92c08c8e0855 *electron-v27.3.2-darwin-arm64-dsym.zip -cbc1c9973b2a895aa2ebecdbd92b3fe8964590b12141a658a6d03ed97339fae6 *electron-v27.3.2-darwin-arm64-symbols.zip -0d4efeff14ac16744eef3d461b95fb59abd2c3affbf638af169698135db73e1f *electron-v27.3.2-darwin-arm64.zip -a77b52509213e67ae1e24172256479831ecbff55d1f49dc0e8bfd4818a5f393e *electron-v27.3.2-darwin-x64-dsym-snapshot.zip -9006386321c50aa7e0e02cd9bd9daef4b8c3ec0e9735912524802f31d02399ef *electron-v27.3.2-darwin-x64-dsym.zip -14fa8e76e519e1fb9e166e134d03f3df1ae1951c14dfd76db8a033a9627c0f13 *electron-v27.3.2-darwin-x64-symbols.zip -5105acce7d832a606fd11b0551d1ef00e0c49fc8b4cff4b53712c9efdddc27a2 *electron-v27.3.2-darwin-x64.zip -3bc20fb4f1d5effb2d882e7b587a337f910026aa50c22e7bc92522daa13f389c *electron-v27.3.2-linux-arm64-debug.zip -0d5d97a93938fa62d2659e2053dcc8d1cabc967878992b248bfec4dcc7763b8c *electron-v27.3.2-linux-arm64-symbols.zip -db9320d9ec6309145347fbba369ab7634139e80f15fff452be9b0171b2bd1823 *electron-v27.3.2-linux-arm64.zip -3bc20fb4f1d5effb2d882e7b587a337f910026aa50c22e7bc92522daa13f389c *electron-v27.3.2-linux-armv7l-debug.zip -6b9117419568c72542ab671301df05d46a662deab0bc37787b3dc9a907e68f8c *electron-v27.3.2-linux-armv7l-symbols.zip -72fd10c666dd810e9f961c2727ae44f5f6cf964cedb6860c1f09da7152e29a29 *electron-v27.3.2-linux-armv7l.zip -354209d48be01785d286eb80d691cdff476479db2d8cdbc6b6bd30652f5539fa *electron-v27.3.2-linux-x64-debug.zip -5f45a4b42f3b35ecea8a623338a6add35bb5220cb0ed02e3489b6d77fbe102ef *electron-v27.3.2-linux-x64-symbols.zip -2261aa0a5a293cf963487c050e9f6d05124da1f946f99bd1115f616f8730f286 *electron-v27.3.2-linux-x64.zip -54a4ad6e75e5a0001c32de18dbfec17f5edc17693663078076456ded525d65da *electron-v27.3.2-mas-arm64-dsym-snapshot.zip -5a5c85833ad7a6ef04337ed8acd131e5cf383a49638789dfd84e07c855b33ccc *electron-v27.3.2-mas-arm64-dsym.zip -16da4cc5a19a953c839093698f0532854e4d3fc839496a5c2b2405fd63c707f4 *electron-v27.3.2-mas-arm64-symbols.zip -8455b79826fe195124bee3f0661e08c14ca50858d376b09d03c79aace0082ea5 *electron-v27.3.2-mas-arm64.zip -00731db08a1bb66e51af0d26d03f8510221f4f6f92282c7baa0cd1c130e0cce6 *electron-v27.3.2-mas-x64-dsym-snapshot.zip -446f98f2d957e4ae487a6307b18be7b11edff35187b71143def4d00325943e42 *electron-v27.3.2-mas-x64-dsym.zip -d3455394eff02d463fdf89aabeee9c05d4980207ecf75a5eac27b35fb2aef874 *electron-v27.3.2-mas-x64-symbols.zip -dae434f52ff9b1055703aaf74b17ff3d93351646e9271a3b10e14b49969d4218 *electron-v27.3.2-mas-x64.zip -a598fcd1e20dcef9e7dccf7676ba276cd95ec7ff6799834fd090800fb15a6507 *electron-v27.3.2-win32-arm64-pdb.zip -7ba64940321ddff307910cc49077aa36c430d4b0797097975cb797cc0ab2b39d *electron-v27.3.2-win32-arm64-symbols.zip -c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v27.3.2-win32-arm64-toolchain-profile.zip -692f264e9d13478ad9a42d06e2eead0ed67ab1b52fc3693ba536a6a441fd9010 *electron-v27.3.2-win32-arm64.zip -a74eee739ff26681f6696f7959ab8e8603bb57f8fcb7ddab305220f71d2c69f3 *electron-v27.3.2-win32-ia32-pdb.zip -c10b90b51d0292129dc5bba5e012c7e07c78d6c70b0980c36676d6abf8eef12f *electron-v27.3.2-win32-ia32-symbols.zip -c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v27.3.2-win32-ia32-toolchain-profile.zip -63e477332608d31afb965a4054b5d78165df1da65d57477ac1dbddf8ede0f1b9 *electron-v27.3.2-win32-ia32.zip -3d795150c0afd48f585c7d32685f726618825262cb76f4014567be9e3de88732 *electron-v27.3.2-win32-x64-pdb.zip -d5463f797d1eb9a57ac9b20caa6419c15c5f3b378a3cb2b45d338040d7124411 *electron-v27.3.2-win32-x64-symbols.zip -c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v27.3.2-win32-x64-toolchain-profile.zip -e701b3023d4929f86736ae8a7ff6409134455da99b3fbdcea8d58555acbd9d46 *electron-v27.3.2-win32-x64.zip -3383cd44951cf763ddd36ba3ec91c930c9e8d33a175adfcb6dce4f667d60bc34 *electron.d.ts -db6df7bd0264c859009247276b35eda4ef20f22a7b2f41c2335a4609f5653cb7 *ffmpeg-v27.3.2-darwin-arm64.zip -3c0bb9740d6b95ff476ff7a5d4b442ccef7ec98e0fa3f2bad8d0e6a51329b511 *ffmpeg-v27.3.2-darwin-x64.zip -6fea38ce22bae4d23fb6b143e946c1c3d214ccecabf841883a2cb1b621161113 *ffmpeg-v27.3.2-linux-arm64.zip -926d0da25ffcea3d05a6cbcae15e5d7729d93bc43394ae4439747669d2210e1d *ffmpeg-v27.3.2-linux-armv7l.zip -6f9c0ef52af14828ad547a80b17f8c63cac51a18b8d5769a2f33e4fa6cccfc7e *ffmpeg-v27.3.2-linux-x64.zip -c75f62fc08d6c5e49fd1a805ca00b4191d5f04d26469448e3d4af48fb409b3a7 *ffmpeg-v27.3.2-mas-arm64.zip -acb8154c113ecbafb91aef5a294dc2c2bce61cbc4a261696681b723d292a5cb3 *ffmpeg-v27.3.2-mas-x64.zip -1665bdac6aa7264a6eb5f00a93110b718c7231010389bdda5ec7bf8275aab953 *ffmpeg-v27.3.2-win32-arm64.zip -3972d89c60a77f7955d7e8520adeae0c9f449a5ae3730cacf202f2baf2bae079 *ffmpeg-v27.3.2-win32-ia32.zip -37d2da723c2f2148c1c8f2ccf354b6dd933148c49dfc7f32aa57ecbd7063ffaf *ffmpeg-v27.3.2-win32-x64.zip -8828099c931c56981865fb9ff6fca85012dd05702a125858d6377c793760db1f *hunspell_dictionaries.zip -9e2126db472f66d3dde2d77eec63364e7071358f5591fc3c4dfb53d191ab5da8 *libcxx-objects-v27.3.2-linux-arm64.zip -530c3a92c4cd721e49e62d4fd97090c4e4d1b00c3ba821fd4f42c5f9186c98e7 *libcxx-objects-v27.3.2-linux-armv7l.zip -5b67f5e2a268bd1980a13b794013d4ac96e7ee40c4878d96f7c27da2c3f94923 *libcxx-objects-v27.3.2-linux-x64.zip -0d3086ccf9a050a88251a4382349f436f99d3d2b1842d87d854ea80667f6c423 *libcxx_headers.zip -ac02262548cb396051c683ad35fcbbed61b9a6f935c2a2bd3d568b209ce9e5a4 *libcxxabi_headers.zip -ba3b63a297b8be954a0ca1b8b83c3c856abaae85d17e6337d2b34e1c14f0d4b2 *mksnapshot-v27.3.2-darwin-arm64.zip -cb09a9e9e1fee567bf9e697eef30d143bd30627c0b189d0271cf84a72a03042e *mksnapshot-v27.3.2-darwin-x64.zip -014c5b621bbbc497bdc40dac47fac20143013fa1e905c0570b5cf92a51826354 *mksnapshot-v27.3.2-linux-arm64-x64.zip -f71407b9cc5c727de243a9e9e7fb56d2a0880e02187fa79982478853432ed5b7 *mksnapshot-v27.3.2-linux-armv7l-x64.zip -e5caa81f467d071756a4209f05f360055be7625a71a0dd9b2a8c95296c8415b5 *mksnapshot-v27.3.2-linux-x64.zip -fc33ec02a17fb58d48625c7b68517705dcd95b5a12e731d0072711a084dc65bd *mksnapshot-v27.3.2-mas-arm64.zip -961af5fc0ef80243d0e94036fb31b90f7e8458e392dd0e49613c11be89cb723f *mksnapshot-v27.3.2-mas-x64.zip -844a70ccef160921e0baeaefe9038d564db9a9476d98fab1eebb5c122ba9c22c *mksnapshot-v27.3.2-win32-arm64-x64.zip -3e723ca42794d43e16656599fbfec73880b964264f5057e38b865688c83ac905 *mksnapshot-v27.3.2-win32-ia32.zip -3e6fc056fa8cfb9940b26c4f066a9c9343056f053bcc53e1eada464bf5bc0d42 *mksnapshot-v27.3.2-win32-x64.zip +b1478a79a80e453e9ee39ad4a0b0e0d871f3e369ec519e3ac5a1da20bb7bdbf3 *chromedriver-v28.2.2-darwin-arm64.zip +4e7c4651d610c70de883b9ceef633f1a2bf90e0f3a732eae7a6d7bcad11bb2df *chromedriver-v28.2.2-darwin-x64.zip +7cee31da7d90c2a24338a10046386517bb93c69c79bd44cfcc9372a551fc7d01 *chromedriver-v28.2.2-linux-arm64.zip +2056e41f713d1a6c83d1f0260c0f2b8addc3c49887ae85ca7e92267eb53951e8 *chromedriver-v28.2.2-linux-armv7l.zip +19503257092605e21bd3798f5ffd0049d8420a504ececef7b1e95d3733846874 *chromedriver-v28.2.2-linux-x64.zip +ec09eeb8a7040c7402a8a5f54491b33e5dc95ea0535b55381a3ec405014f08db *chromedriver-v28.2.2-mas-arm64.zip +1dd5cb2a113c74ae84f2ac98f6f40da2c367014381a547788fea9ae220e6fc9f *chromedriver-v28.2.2-mas-x64.zip +fd505e1f1c2f72266c48914690a48918fc7920877215a508ea5325cf0353f72c *chromedriver-v28.2.2-win32-arm64.zip +c0226c0fb260d6812185eeea718c8c0054d0fcac995bb1ccb333f852206372c8 *chromedriver-v28.2.2-win32-ia32.zip +b6daccad5bcd3046d0678c927f6b97ed91f2242f716deb0de95a0ee2303af818 *chromedriver-v28.2.2-win32-x64.zip +76a88da92b950c882d90c3dcb26e0c2ca5e5a52ad7a066ec0b3cbf9cc4d04563 *electron-api.json +6ad08d733c95de3c30560e8289d0e657ed5ee03bc8ba9d1f11d528851e5b7fba *electron-v28.2.2-darwin-arm64-dsym-snapshot.zip +8f0d450f3d2392cbe7a6cb274ec0f3bf63da66c98fa0baaa2355e69f1c93b151 *electron-v28.2.2-darwin-arm64-dsym.zip +262036eb86b767db0d199df022b8b432aa3714e451b9ac656af7ef031581b44a *electron-v28.2.2-darwin-arm64-symbols.zip +23119b333c47a5ea9e36e04cdc3b8c5955cfccfeb90994f1fecea4722bfb8dcc *electron-v28.2.2-darwin-arm64.zip +384015a3e49a6846ebefc78f9f01ce6d47c2ec109e6223907298aa6382b0d072 *electron-v28.2.2-darwin-x64-dsym-snapshot.zip +434838821de746ff71baafdf9e0df07cb3766dd73eb7fcd253aee0571bd0cd59 *electron-v28.2.2-darwin-x64-dsym.zip +470087b5d631dc0032611048d5fc23faed9a71ec2c36a528c5a50c2e357d1716 *electron-v28.2.2-darwin-x64-symbols.zip +48f3424b3cbdf602a13f451361ade2f7f2896a354a51f78da4239dbdf2d1218b *electron-v28.2.2-darwin-x64.zip +d5bf835ba4b2eaa4435946f97ad7ac3e7243564037423cfaadaf5cb03af4ddbc *electron-v28.2.2-linux-arm64-debug.zip +90550f29b1f032ebcf467dc81f4915c322f93855a4658cf74261f68a3ccdc21e *electron-v28.2.2-linux-arm64-symbols.zip +746284eb1d8029b0f6b02281543ab2ecf45f071da21407f45b2b32d1ff268310 *electron-v28.2.2-linux-arm64.zip +d5bf835ba4b2eaa4435946f97ad7ac3e7243564037423cfaadaf5cb03af4ddbc *electron-v28.2.2-linux-armv7l-debug.zip +80cc8d9333156caaee59c7ddf3bd77712be8379b51f4218063e6c176a4ec2c26 *electron-v28.2.2-linux-armv7l-symbols.zip +f4580e8877481c0526110feaa78372ed3045bfbf5a6ba4b14e8cd155b9965f5e *electron-v28.2.2-linux-armv7l.zip +da33d92871768e4cf95b143c6022830d97b0ec2d4120463ab71b48597f940f07 *electron-v28.2.2-linux-x64-debug.zip +18dce5283513abd94b79a1636d25e3453f5c33d335425a234b9967dd4e5ce942 *electron-v28.2.2-linux-x64-symbols.zip +1eeb6ebc3b0699cae1fb171bbf7c9105e716db833f6e73a90f4ca161f17ffb15 *electron-v28.2.2-linux-x64.zip +e74da15d90e52cddf0f0f14663f6313df585b486b002966f6016c1b148cdd70d *electron-v28.2.2-mas-arm64-dsym-snapshot.zip +498357eb2e784bff54c5ac59fd3eada814d130f12a5e77d47c468f2305377717 *electron-v28.2.2-mas-arm64-dsym.zip +849fa891d072d06b1e929eb1acfbe7ac83f0238483039f8e1102e01e5223c3f5 *electron-v28.2.2-mas-arm64-symbols.zip +621fd91d70cb33ec58543fc57762e692dfa0e272a53f3316fd215ffa88bd075b *electron-v28.2.2-mas-arm64.zip +0f9e2ab79bca99f44c1e9a140929fad6d2cd37def60303974f5a82ca95dd9a69 *electron-v28.2.2-mas-x64-dsym-snapshot.zip +51c29e047ba7d8669030cc9615f70ecaa5c9519cd04ab5e62822c0d4f21f5fbb *electron-v28.2.2-mas-x64-dsym.zip +25da93f45b095a3669475416832647a01f2a02a95dcc064dfabdf9c621045106 *electron-v28.2.2-mas-x64-symbols.zip +3b6931362f1b7f377624ea7c6ccf069f291e4e675a28f12a56e3e75355c13fbd *electron-v28.2.2-mas-x64.zip +cece93c232c65bf4e1b918b9645f5a2e247bd3f8bb2dd9e6e889a402060a103b *electron-v28.2.2-win32-arm64-pdb.zip +4a46e1ead0de7b6f757c1194add6467b3375a8dcfb02d903e481c0d8db5c7e5d *electron-v28.2.2-win32-arm64-symbols.zip +c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v28.2.2-win32-arm64-toolchain-profile.zip +083f95abbce97cab70e77b86e39cff01ff1df121f36b9da581ead960ae329f69 *electron-v28.2.2-win32-arm64.zip +f9b4633bc03fe7c77db4b335121e7e3e05f6788c6752ccb3f68267e664d4323a *electron-v28.2.2-win32-ia32-pdb.zip +d20f70ea8dc86477f723d104d42fe78e2508577ef3b1eb6ec812366f18ad80d8 *electron-v28.2.2-win32-ia32-symbols.zip +c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v28.2.2-win32-ia32-toolchain-profile.zip +c57691b73592632829cef136be6dd356a82331450920fd024ac3589654d23550 *electron-v28.2.2-win32-ia32.zip +b8a14fc75b9205a4b82aa008e23a020e9fac694399b47390a163c3100ac7946d *electron-v28.2.2-win32-x64-pdb.zip +780089dde95ce1ab5da176ad53d9c7cd122085809622852a3132b80b93faac9b *electron-v28.2.2-win32-x64-symbols.zip +c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v28.2.2-win32-x64-toolchain-profile.zip +5fbc76585891b0d7b09c938f7be25b7ab36b3768491021b053dc99bc70a8aa29 *electron-v28.2.2-win32-x64.zip +225268475350fa71d9fdea966160fc91379ced2f2353a902addf65d5f9b0dbf1 *electron.d.ts +59a8d6b81d93bc99ecf099fac6492eb67ba601386cce07261a009a5b99e75479 *ffmpeg-v28.2.2-darwin-arm64.zip +15386f238dce9ba40714336265422cc41a1ef0608041f562a8fd42e3813ddc64 *ffmpeg-v28.2.2-darwin-x64.zip +8e108e533811febcc51f377ac8604d506663453e41c02dc818517e1ea9a4e8d5 *ffmpeg-v28.2.2-linux-arm64.zip +51ecd03435f56a2ced31b1c9dbf281955ba82a814ca0214a4292bdc711e5a45c *ffmpeg-v28.2.2-linux-armv7l.zip +acc9dc3765f68b7563045e2d0df11bbef6b41be0a1c34bbf9fa778f36eefb42f *ffmpeg-v28.2.2-linux-x64.zip +e71aac5c02f67bd5ba5d650160ff4edb122f697ab6bd8e686eae78426c439733 *ffmpeg-v28.2.2-mas-arm64.zip +3d0bb26cc9b751dad883750972fddec72aa936ecaa0d9bd198ba9b47203410e8 *ffmpeg-v28.2.2-mas-x64.zip +035b24a44f09587092e7db4e28400139901cec6378b3c828ce9f90a60f4f3a9a *ffmpeg-v28.2.2-win32-arm64.zip +38b25e225fd028f1f3f2c551f3b42d62d9e5c4ef388e0b0e019e9c8d93a85b07 *ffmpeg-v28.2.2-win32-ia32.zip +41849e779371dc0c35899341ae658b883ef0124296787ad96b7d5e4d9b69f1b9 *ffmpeg-v28.2.2-win32-x64.zip +8830364f8050164b1736246c30e96ae7ac876bcec5af1bf6344edbd66ed45353 *hunspell_dictionaries.zip +fc417873289fa9c947598ed73a27a28c4b5a07ce90ef998bb56550c4e10a034b *libcxx-objects-v28.2.2-linux-arm64.zip +06e9cdb2e8785a0835f66d34e9518c47ef220e32646e5b43e599339836e9e7b1 *libcxx-objects-v28.2.2-linux-armv7l.zip +ac098a006a8f84d0bb19088b2dec3ee3068b19208c5611194e831b1e5878fb2d *libcxx-objects-v28.2.2-linux-x64.zip +56414a1e809874949c1a1111b8e68b8d4f40d55cb481ad4869e920e47fe1b71b *libcxx_headers.zip +36e46cbed397cc1fe34d8dc477d3a87613acb9936f811535c1300e138e1a7008 *libcxxabi_headers.zip +94b01f4dd6bd56dec39a0be9ac14bb8c9a73db22cb579d6093f4f4c95a4a8896 *mksnapshot-v28.2.2-darwin-arm64.zip +ea768087b4fedf09c38eb093beb744c1a3b5b2a54025a83f1e2301ea03539500 *mksnapshot-v28.2.2-darwin-x64.zip +b9a01ba90abb69877838515d8273532e4aeea6d66c49b8aac3267e26546fc8b3 *mksnapshot-v28.2.2-linux-arm64-x64.zip +60005160b5e9db4a3847c63893f44e18ca86657a3ec97b6c13a90e43291bdb65 *mksnapshot-v28.2.2-linux-armv7l-x64.zip +81bf5ec59e7c33c642b79582fc5b775ec635ce0c52f3f5c30315cb45fdbffd12 *mksnapshot-v28.2.2-linux-x64.zip +7bfbe3cf02713b1a09aa19b75b876e158ed167b0d4345ec3b429061b53fc4b8f *mksnapshot-v28.2.2-mas-arm64.zip +91f7d34a05fa9c7cda4f36a44309f51e7defea2134d5bcc818a3eb4537979870 *mksnapshot-v28.2.2-mas-x64.zip +3f7163a34aae864cd44ebec086d4fab30132924680f20136cf19348811bace50 *mksnapshot-v28.2.2-win32-arm64-x64.zip +ac64fbfb78a1f6f389dac96ad7c655e2ea6fb2289e38a8fd516dbbda6bea42a3 *mksnapshot-v28.2.2-win32-ia32.zip +1bcd03747ce3eee6dd94b0608a0812268dacf77bac5541c581c22b92f700b303 *mksnapshot-v28.2.2-win32-x64.zip diff --git a/build/checksums/nodejs.txt b/build/checksums/nodejs.txt index 9ed8af5842a..13aa4c7e87b 100644 --- a/build/checksums/nodejs.txt +++ b/build/checksums/nodejs.txt @@ -1,6 +1,6 @@ -18ca716ea57522b90473777cb9f878467f77fdf826d37beb15a0889fdd74533e node-v18.17.1-darwin-arm64.tar.gz -b3e083d2715f07ec3f00438401fb58faa1e0bdf3c7bde9f38b75ed17809d92fa node-v18.17.1-darwin-x64.tar.gz -8f5203f5c6dc44ea50ac918b7ecbdb1c418e4f3d9376d8232a1ef9ff38f9c480 node-v18.17.1-linux-arm64.tar.gz -1ab79868859b2d37148c6d8ecee3abb5ee55b88731ab5df01928ed4f6f9bfbad node-v18.17.1-linux-armv7l.tar.gz -2cb75f2bc04b0a3498733fbee779b2f76fe3f655188b4ac69ef2887b6721da2d node-v18.17.1-linux-x64.tar.gz -afb45186ad4f4217c2fc1dfc2239ff5ab016ef0ba5fc329bc6aa8fd10c7ecc88 win-x64/node.exe +9f982cc91b28778dd8638e4f94563b0c2a1da7aba62beb72bd427721035ab553 node-v18.18.2-darwin-arm64.tar.gz +5bb8da908ed590e256a69bf2862238c8a67bc4600119f2f7721ca18a7c810c0f node-v18.18.2-darwin-x64.tar.gz +0c9a6502b66310cb26e12615b57304e91d92ac03d4adcb91c1906351d7928f0d node-v18.18.2-linux-arm64.tar.gz +7a3b34a6fdb9514bc2374114ec6df3c36113dc5075c38b22763aa8f106783737 node-v18.18.2-linux-armv7l.tar.gz +a44c3e7f8bf91e852c928e5d8bd67ca316b35e27eec1d8acbe3b9dbe03688dab node-v18.18.2-linux-x64.tar.gz +54884183ff5108874c091746465e8156ae0acc68af589cc10bc41b3927db0f4a win-x64/node.exe diff --git a/build/linux/dependencies-generator.js b/build/linux/dependencies-generator.js index e40ed70901c..58db0f4af51 100644 --- a/build/linux/dependencies-generator.js +++ b/build/linux/dependencies-generator.js @@ -23,7 +23,7 @@ const product = require("../../product.json"); // The reference dependencies, which one has to update when the new dependencies // are valid, are in dep-lists.ts const FAIL_BUILD_FOR_NEW_DEPENDENCIES = true; -// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/118.0.5993.159:chrome/installer/linux/BUILD.gn;l=64-80 +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/120.0.6099.268:chrome/installer/linux/BUILD.gn;l=64-80 // and the Linux Archive build // Shared library dependencies that we already bundle. const bundledDeps = [ diff --git a/build/linux/dependencies-generator.ts b/build/linux/dependencies-generator.ts index 12bc3c08a64..9f1a068b8d7 100644 --- a/build/linux/dependencies-generator.ts +++ b/build/linux/dependencies-generator.ts @@ -25,7 +25,7 @@ import product = require('../../product.json'); // are valid, are in dep-lists.ts const FAIL_BUILD_FOR_NEW_DEPENDENCIES: boolean = true; -// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/118.0.5993.159:chrome/installer/linux/BUILD.gn;l=64-80 +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/120.0.6099.268:chrome/installer/linux/BUILD.gn;l=64-80 // and the Linux Archive build // Shared library dependencies that we already bundle. const bundledDeps = [ diff --git a/build/linux/rpm/dep-lists.js b/build/linux/rpm/dep-lists.js index b9a6e80d5f3..bd84fc146dc 100644 --- a/build/linux/rpm/dep-lists.js +++ b/build/linux/rpm/dep-lists.js @@ -81,7 +81,6 @@ exports.referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.11)(64bit)', 'libnss3.so(NSS_3.12)(64bit)', 'libnss3.so(NSS_3.12.1)(64bit)', - 'libnss3.so(NSS_3.13)(64bit)', 'libnss3.so(NSS_3.2)(64bit)', 'libnss3.so(NSS_3.22)(64bit)', 'libnss3.so(NSS_3.3)(64bit)', @@ -173,7 +172,6 @@ exports.referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.11)', 'libnss3.so(NSS_3.12)', 'libnss3.so(NSS_3.12.1)', - 'libnss3.so(NSS_3.13)', 'libnss3.so(NSS_3.2)', 'libnss3.so(NSS_3.22)', 'libnss3.so(NSS_3.22)(64bit)', @@ -269,7 +267,6 @@ exports.referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.11)(64bit)', 'libnss3.so(NSS_3.12)(64bit)', 'libnss3.so(NSS_3.12.1)(64bit)', - 'libnss3.so(NSS_3.13)(64bit)', 'libnss3.so(NSS_3.2)(64bit)', 'libnss3.so(NSS_3.22)(64bit)', 'libnss3.so(NSS_3.3)(64bit)', diff --git a/build/linux/rpm/dep-lists.ts b/build/linux/rpm/dep-lists.ts index 275d88b95a8..82a4fe7698d 100644 --- a/build/linux/rpm/dep-lists.ts +++ b/build/linux/rpm/dep-lists.ts @@ -80,7 +80,6 @@ export const referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.11)(64bit)', 'libnss3.so(NSS_3.12)(64bit)', 'libnss3.so(NSS_3.12.1)(64bit)', - 'libnss3.so(NSS_3.13)(64bit)', 'libnss3.so(NSS_3.2)(64bit)', 'libnss3.so(NSS_3.22)(64bit)', 'libnss3.so(NSS_3.3)(64bit)', @@ -172,7 +171,6 @@ export const referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.11)', 'libnss3.so(NSS_3.12)', 'libnss3.so(NSS_3.12.1)', - 'libnss3.so(NSS_3.13)', 'libnss3.so(NSS_3.2)', 'libnss3.so(NSS_3.22)', 'libnss3.so(NSS_3.22)(64bit)', @@ -268,7 +266,6 @@ export const referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.11)(64bit)', 'libnss3.so(NSS_3.12)(64bit)', 'libnss3.so(NSS_3.12.1)(64bit)', - 'libnss3.so(NSS_3.13)(64bit)', 'libnss3.so(NSS_3.2)(64bit)', 'libnss3.so(NSS_3.22)(64bit)', 'libnss3.so(NSS_3.3)(64bit)', diff --git a/cgmanifest.json b/cgmanifest.json index 2673931fdb6..4b4d49573e4 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "b1f5594cf472956192e71c38ebfc22472d44a03d" + "commitHash": "01303e423c41f9fefe7ff777744a4c549c0c6d8c" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "118.0.5993.159" + "version": "120.0.6099.276" }, { "component": { @@ -48,7 +48,7 @@ "git": { "name": "ffmpeg", "repositoryUrl": "https://chromium.googlesource.com/chromium/third_party/ffmpeg", - "commitHash": "0ba37733400593b162e5ae9ff26b384cff49c250" + "commitHash": "e1ca3f06adec15150a171bc38f550058b4bbb23b" } }, "isOnlyProductionDependency": true, @@ -516,11 +516,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "2e414d5d1082233c3516fca923fe351d5186c80e" + "commitHash": "8a01b3dcb7d08a48bfd3e6bf85ef49faa1454839" } }, "isOnlyProductionDependency": true, - "version": "18.17.1" + "version": "18.18.2" }, { "component": { @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "077c4addd5faa3ad1d1c9e598284368394a97fdd" + "commitHash": "16adf2a26358e3fc2297832e867c942b6df35844" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "27.3.2" + "version": "28.2.2" }, { "component": { diff --git a/package.json b/package.json index 1c3d3fc095f..2f127741d55 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.88.0", - "distro": "b314654a31bdba8cd2b0c7548e931916d03416bf", + "distro": "de07f23454d5352cc3711ca34d51278767e6eb0a", "author": { "name": "Microsoft Corporation" }, @@ -149,7 +149,7 @@ "cssnano": "^6.0.3", "debounce": "^1.0.0", "deemon": "^1.8.0", - "electron": "27.3.2", + "electron": "28.2.2", "eslint": "8.36.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^46.5.0", diff --git a/remote/.yarnrc b/remote/.yarnrc index cac528fd2dd..4e7208cdf69 100644 --- a/remote/.yarnrc +++ b/remote/.yarnrc @@ -1,5 +1,5 @@ disturl "https://nodejs.org/dist" -target "18.17.1" -ms_build_id "255375" +target "18.18.2" +ms_build_id "256117" runtime "node" build_from_source "true" diff --git a/yarn.lock b/yarn.lock index 7dcbe6fe030..98368474549 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3556,10 +3556,10 @@ electron-to-chromium@^1.4.648: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.648.tgz#c7b46c9010752c37bb4322739d6d2dd82354fbe4" integrity sha512-EmFMarXeqJp9cUKu/QEciEApn0S/xRcpZWuAm32U7NgoZCimjsilKXHRO9saeEW55eHZagIDg6XTUOv32w9pjg== -electron@27.3.2: - version "27.3.2" - resolved "https://registry.yarnpkg.com/electron/-/electron-27.3.2.tgz#ff2caa6d09e013ec32bae3185c790d712cd02f54" - integrity sha512-FoLdHj2ON0jE8S0YntgNT4ABaHgK4oR4dqXixPQXnTK0JvXgrrrW5W7ls+c7oiFBGN/f9bm0Mabq8iKH+7wMYQ== +electron@28.2.2: + version "28.2.2" + resolved "https://registry.yarnpkg.com/electron/-/electron-28.2.2.tgz#d5aa4a33c00927d83ca893f8726f7c62aad98c41" + integrity sha512-8UcvIGFcjplHdjPFNAHVFg5bS0atDyT3Zx21WwuE4iLfxcAMsyMEOgrQX3im5LibA8srwsUZs7Cx0JAUfcQRpw== dependencies: "@electron/get" "^2.0.0" "@types/node" "^18.11.18" From 51772af23ced693d721de52b0b3ed90ed0635bc6 Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Mon, 26 Feb 2024 11:41:18 +0100 Subject: [PATCH 0169/1175] rename suggestions: increase approximate font width --- src/vs/editor/contrib/rename/browser/renameInputField.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/rename/browser/renameInputField.ts b/src/vs/editor/contrib/rename/browser/renameInputField.ts index 1b3728c30c6..6cb9f8a8c5a 100644 --- a/src/vs/editor/contrib/rename/browser/renameInputField.ts +++ b/src/vs/editor/contrib/rename/browser/renameInputField.ts @@ -612,7 +612,7 @@ class CandidatesView { } private _pickListWidth(candidates: NewSymbolName[]): number { - const APPROXIMATE_CHAR_WIDTH = 7.2; // approximate # of pixes taken by a single character + const APPROXIMATE_CHAR_WIDTH = 8; // approximate # of pixes taken by a single character const longestCandidateWidth = Math.ceil(Math.max(...candidates.map(c => c.newSymbolName.length)) * APPROXIMATE_CHAR_WIDTH); // TODO@ulugbekna: use editor#typicalCharacterWidth or something const width = Math.max( this._minimumWidth, From aa8ec9703cab7b592e0adce2f4d2bc6927b84127 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 26 Feb 2024 12:32:17 +0100 Subject: [PATCH 0170/1175] Fix some bogus uses of new CancellationTokenSource().token (#205994) Part of #205966 --- src/vs/workbench/api/browser/mainThreadQuickDiff.ts | 4 ++-- src/vs/workbench/api/common/extHostTunnelService.ts | 4 ++-- src/vs/workbench/browser/parts/views/treeView.ts | 2 +- src/vs/workbench/contrib/remote/browser/tunnelView.ts | 6 +++--- src/vs/workbench/services/remote/common/tunnelModel.ts | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadQuickDiff.ts b/src/vs/workbench/api/browser/mainThreadQuickDiff.ts index 48f53faea39..d5312097ee9 100644 --- a/src/vs/workbench/api/browser/mainThreadQuickDiff.ts +++ b/src/vs/workbench/api/browser/mainThreadQuickDiff.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { DisposableMap, IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ExtHostContext, ExtHostQuickDiffShape, IDocumentFilterDto, MainContext, MainThreadQuickDiffShape } from 'vs/workbench/api/common/extHost.protocol'; @@ -30,7 +30,7 @@ export class MainThreadQuickDiff implements MainThreadQuickDiffShape { selector, isSCM: false, getOriginalResource: async (uri: URI) => { - return URI.revive(await this.proxy.$provideOriginalResource(handle, uri, new CancellationTokenSource().token)); + return URI.revive(await this.proxy.$provideOriginalResource(handle, uri, CancellationToken.None)); } }; const disposable = this.quickDiffService.addQuickDiffProvider(provider); diff --git a/src/vs/workbench/api/common/extHostTunnelService.ts b/src/vs/workbench/api/common/extHostTunnelService.ts index ccf5700c83b..7200372acca 100644 --- a/src/vs/workbench/api/common/extHostTunnelService.ts +++ b/src/vs/workbench/api/common/extHostTunnelService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import * as nls from 'vs/nls'; @@ -149,7 +149,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe throw new Error('A tunnel provider has already been registered. Only the first tunnel provider to be registered will be used.'); } this._forwardPortProvider = async (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => { - const result = await provider.provideTunnel(tunnelOptions, tunnelCreationOptions, new CancellationTokenSource().token); + const result = await provider.provideTunnel(tunnelOptions, tunnelCreationOptions, CancellationToken.None); return result ?? undefined; }; diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 0eb478a3544..3b40a248a1d 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -767,7 +767,7 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { let command = element?.command; if (element && !command) { if ((element instanceof ResolvableTreeItem) && element.hasResolve) { - await element.resolve(new CancellationTokenSource().token); + await element.resolve(CancellationToken.None); command = element.command; } } diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 8b0318487b6..35ba3ee8c5d 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -44,7 +44,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { copyAddressIcon, forwardedPortWithoutProcessIcon, forwardedPortWithProcessIcon, forwardPortIcon, labelPortIcon, openBrowserIcon, openPreviewIcon, portsViewIcon, privatePortIcon, stopForwardIcon } from 'vs/workbench/contrib/remote/browser/remoteIcons'; import { IExternalUriOpenerService } from 'vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { isMacintosh } from 'vs/base/common/platform'; import { ITableColumn, ITableContextMenuEvent, ITableEvent, ITableMouseEvent, ITableRenderer, ITableVirtualDelegate } from 'vs/base/browser/ui/table/table'; import { WorkbenchTable } from 'vs/platform/list/browser/listService'; @@ -1372,9 +1372,9 @@ export namespace OpenPortInPreviewAction { if (tunnel) { const remoteHost = tunnel.remoteHost.includes(':') ? `[${tunnel.remoteHost}]` : tunnel.remoteHost; const sourceUri = URI.parse(`http://${remoteHost}:${tunnel.remotePort}`); - const opener = await externalOpenerService.getOpener(tunnel.localUri, { sourceUri }, new CancellationTokenSource().token); + const opener = await externalOpenerService.getOpener(tunnel.localUri, { sourceUri }, CancellationToken.None); if (opener) { - return opener.openExternalUri(tunnel.localUri, { sourceUri }, new CancellationTokenSource().token); + return opener.openExternalUri(tunnel.localUri, { sourceUri }, CancellationToken.None); } return openerService.open(tunnel.localUri); } diff --git a/src/vs/workbench/services/remote/common/tunnelModel.ts b/src/vs/workbench/services/remote/common/tunnelModel.ts index 5394416d717..f0ceaf1950c 100644 --- a/src/vs/workbench/services/remote/common/tunnelModel.ts +++ b/src/vs/workbench/services/remote/common/tunnelModel.ts @@ -20,7 +20,7 @@ import { RemoteTunnel, ITunnelService, TunnelProtocol, TunnelPrivacyId, LOCALHOS import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { isNumber, isObject, isString } from 'vs/base/common/types'; import { deepClone } from 'vs/base/common/objects'; import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -993,7 +993,7 @@ export class TunnelModel extends Disposable { const portGroup = entry[1]; const matchingCandidate = matchingCandidates.get(portGroup[0]); return provider.providePortAttributes(portGroup, - matchingCandidate?.pid, matchingCandidate?.detail, new CancellationTokenSource().token); + matchingCandidate?.pid, matchingCandidate?.detail, CancellationToken.None); }); }))); const providedAttributes: Map = new Map(); From 90ab3213c32e5dfc9eff78af7b7b4783f78d855b Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 23 Feb 2024 14:44:32 +0100 Subject: [PATCH 0171/1175] Move diff editor internal commands to their own file --- .../browser/widget/diffEditor/commands.ts | 242 ++++++++++++++++++ .../diffEditor/diffEditor.contribution.ts | 240 +---------------- 2 files changed, 245 insertions(+), 237 deletions(-) create mode 100644 src/vs/editor/browser/widget/diffEditor/commands.ts diff --git a/src/vs/editor/browser/widget/diffEditor/commands.ts b/src/vs/editor/browser/widget/diffEditor/commands.ts new file mode 100644 index 00000000000..5297e77c086 --- /dev/null +++ b/src/vs/editor/browser/widget/diffEditor/commands.ts @@ -0,0 +1,242 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getActiveElement } from 'vs/base/browser/dom'; +import { Codicon } from 'vs/base/common/codicons'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction2, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { localize2 } from 'vs/nls'; +import { ILocalizedString } from 'vs/platform/action/common/action'; +import { Action2, MenuId } from 'vs/platform/actions/common/actions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import './registrations.contribution'; + +export class ToggleCollapseUnchangedRegions extends Action2 { + constructor() { + super({ + id: 'diffEditor.toggleCollapseUnchangedRegions', + title: localize2('toggleCollapseUnchangedRegions', 'Toggle Collapse Unchanged Regions'), + icon: Codicon.map, + toggled: ContextKeyExpr.has('config.diffEditor.hideUnchangedRegions.enabled'), + precondition: ContextKeyExpr.has('isInDiffEditor'), + menu: { + when: ContextKeyExpr.has('isInDiffEditor'), + id: MenuId.EditorTitle, + order: 22, + group: 'navigation', + }, + }); + } + + run(accessor: ServicesAccessor, ...args: unknown[]): void { + const configurationService = accessor.get(IConfigurationService); + const newValue = !configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'); + configurationService.updateValue('diffEditor.hideUnchangedRegions.enabled', newValue); + } +} + +export class ToggleShowMovedCodeBlocks extends Action2 { + constructor() { + super({ + id: 'diffEditor.toggleShowMovedCodeBlocks', + title: localize2('toggleShowMovedCodeBlocks', 'Toggle Show Moved Code Blocks'), + precondition: ContextKeyExpr.has('isInDiffEditor'), + }); + } + + run(accessor: ServicesAccessor, ...args: unknown[]): void { + const configurationService = accessor.get(IConfigurationService); + const newValue = !configurationService.getValue('diffEditor.experimental.showMoves'); + configurationService.updateValue('diffEditor.experimental.showMoves', newValue); + } +} + +export class ToggleUseInlineViewWhenSpaceIsLimited extends Action2 { + constructor() { + super({ + id: 'diffEditor.toggleUseInlineViewWhenSpaceIsLimited', + title: localize2('toggleUseInlineViewWhenSpaceIsLimited', 'Toggle Use Inline View When Space Is Limited'), + precondition: ContextKeyExpr.has('isInDiffEditor'), + }); + } + + run(accessor: ServicesAccessor, ...args: unknown[]): void { + const configurationService = accessor.get(IConfigurationService); + const newValue = !configurationService.getValue('diffEditor.useInlineViewWhenSpaceIsLimited'); + configurationService.updateValue('diffEditor.useInlineViewWhenSpaceIsLimited', newValue); + } +} + +const diffEditorCategory: ILocalizedString = localize2('diffEditor', "Diff Editor"); + +export class SwitchSide extends EditorAction2 { + constructor() { + super({ + id: 'diffEditor.switchSide', + title: localize2('switchSide', 'Switch Side'), + icon: Codicon.arrowSwap, + precondition: ContextKeyExpr.has('isInDiffEditor'), + f1: true, + category: diffEditorCategory, + }); + } + + runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, arg?: { dryRun: boolean }): unknown { + const diffEditor = findFocusedDiffEditor(accessor); + if (diffEditor instanceof DiffEditorWidget) { + if (arg && arg.dryRun) { + return { destinationSelection: diffEditor.mapToOtherSide().destinationSelection }; + } else { + diffEditor.switchSide(); + } + } + return undefined; + } +} +export class ExitCompareMove extends EditorAction2 { + constructor() { + super({ + id: 'diffEditor.exitCompareMove', + title: localize2('exitCompareMove', 'Exit Compare Move'), + icon: Codicon.close, + precondition: EditorContextKeys.comparingMovedCode, + f1: false, + category: diffEditorCategory, + keybinding: { + weight: 10000, + primary: KeyCode.Escape, + } + }); + } + + runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { + const diffEditor = findFocusedDiffEditor(accessor); + if (diffEditor instanceof DiffEditorWidget) { + diffEditor.exitCompareMove(); + } + } +} + +export class CollapseAllUnchangedRegions extends EditorAction2 { + constructor() { + super({ + id: 'diffEditor.collapseAllUnchangedRegions', + title: localize2('collapseAllUnchangedRegions', 'Collapse All Unchanged Regions'), + icon: Codicon.fold, + precondition: ContextKeyExpr.has('isInDiffEditor'), + f1: true, + category: diffEditorCategory, + }); + } + + runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { + const diffEditor = findFocusedDiffEditor(accessor); + if (diffEditor instanceof DiffEditorWidget) { + diffEditor.collapseAllUnchangedRegions(); + } + } +} + +export class ShowAllUnchangedRegions extends EditorAction2 { + constructor() { + super({ + id: 'diffEditor.showAllUnchangedRegions', + title: localize2('showAllUnchangedRegions', 'Show All Unchanged Regions'), + icon: Codicon.unfold, + precondition: ContextKeyExpr.has('isInDiffEditor'), + f1: true, + category: diffEditorCategory, + }); + } + + runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { + const diffEditor = findFocusedDiffEditor(accessor); + if (diffEditor instanceof DiffEditorWidget) { + diffEditor.showAllUnchangedRegions(); + } + } +} + +const accessibleDiffViewerCategory: ILocalizedString = localize2('accessibleDiffViewer', "Accessible Diff Viewer"); + +export class AccessibleDiffViewerNext extends Action2 { + public static id = 'editor.action.accessibleDiffViewer.next'; + + constructor() { + super({ + id: AccessibleDiffViewerNext.id, + title: localize2('editor.action.accessibleDiffViewer.next', 'Go to Next Difference'), + category: accessibleDiffViewerCategory, + precondition: ContextKeyExpr.has('isInDiffEditor'), + keybinding: { + primary: KeyCode.F7, + weight: KeybindingWeight.EditorContrib + }, + f1: true, + }); + } + + public override run(accessor: ServicesAccessor): void { + const diffEditor = findFocusedDiffEditor(accessor); + diffEditor?.accessibleDiffViewerNext(); + } +} + +export class AccessibleDiffViewerPrev extends Action2 { + public static id = 'editor.action.accessibleDiffViewer.prev'; + + constructor() { + super({ + id: AccessibleDiffViewerPrev.id, + title: localize2('editor.action.accessibleDiffViewer.prev', 'Go to Previous Difference'), + category: accessibleDiffViewerCategory, + precondition: ContextKeyExpr.has('isInDiffEditor'), + keybinding: { + primary: KeyMod.Shift | KeyCode.F7, + weight: KeybindingWeight.EditorContrib + }, + f1: true, + }); + } + + public override run(accessor: ServicesAccessor): void { + const diffEditor = findFocusedDiffEditor(accessor); + diffEditor?.accessibleDiffViewerPrev(); + } +} + +export function findFocusedDiffEditor(accessor: ServicesAccessor): IDiffEditor | null { + const codeEditorService = accessor.get(ICodeEditorService); + const diffEditors = codeEditorService.listDiffEditors(); + + const activeElement = getActiveElement(); + if (activeElement) { + for (const d of diffEditors) { + const container = d.getContainerDomNode(); + if (isElementOrParentOf(container, activeElement)) { + return d; + } + } + } + + return null; +} + +function isElementOrParentOf(elementOrParent: Element, element: Element): boolean { + let e: Element | null = element; + while (e) { + if (e === elementOrParent) { + return true; + } + e = e.parentElement; + } + return false; +} diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditor.contribution.ts b/src/vs/editor/browser/widget/diffEditor/diffEditor.contribution.ts index 2cc7ffacdc4..bb8ab9ccca7 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditor.contribution.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditor.contribution.ts @@ -3,83 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getActiveElement } from 'vs/base/browser/dom'; import { Codicon } from 'vs/base/common/codicons'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorAction2, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; +import { AccessibleDiffViewerNext, AccessibleDiffViewerPrev, CollapseAllUnchangedRegions, ExitCompareMove, ShowAllUnchangedRegions, SwitchSide, ToggleCollapseUnchangedRegions, ToggleShowMovedCodeBlocks, ToggleUseInlineViewWhenSpaceIsLimited } from 'vs/editor/browser/widget/diffEditor/commands'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { localize, localize2 } from 'vs/nls'; -import { ILocalizedString } from 'vs/platform/action/common/action'; -import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; +import { localize } from 'vs/nls'; +import { MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import './registrations.contribution'; -export class ToggleCollapseUnchangedRegions extends Action2 { - constructor() { - super({ - id: 'diffEditor.toggleCollapseUnchangedRegions', - title: localize2('toggleCollapseUnchangedRegions', 'Toggle Collapse Unchanged Regions'), - icon: Codicon.map, - toggled: ContextKeyExpr.has('config.diffEditor.hideUnchangedRegions.enabled'), - precondition: ContextKeyExpr.has('isInDiffEditor'), - menu: { - when: ContextKeyExpr.has('isInDiffEditor'), - id: MenuId.EditorTitle, - order: 22, - group: 'navigation', - }, - }); - } - - run(accessor: ServicesAccessor, ...args: unknown[]): void { - const configurationService = accessor.get(IConfigurationService); - const newValue = !configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'); - configurationService.updateValue('diffEditor.hideUnchangedRegions.enabled', newValue); - } -} - registerAction2(ToggleCollapseUnchangedRegions); - -export class ToggleShowMovedCodeBlocks extends Action2 { - constructor() { - super({ - id: 'diffEditor.toggleShowMovedCodeBlocks', - title: localize2('toggleShowMovedCodeBlocks', 'Toggle Show Moved Code Blocks'), - precondition: ContextKeyExpr.has('isInDiffEditor'), - }); - } - - run(accessor: ServicesAccessor, ...args: unknown[]): void { - const configurationService = accessor.get(IConfigurationService); - const newValue = !configurationService.getValue('diffEditor.experimental.showMoves'); - configurationService.updateValue('diffEditor.experimental.showMoves', newValue); - } -} - registerAction2(ToggleShowMovedCodeBlocks); - -export class ToggleUseInlineViewWhenSpaceIsLimited extends Action2 { - constructor() { - super({ - id: 'diffEditor.toggleUseInlineViewWhenSpaceIsLimited', - title: localize2('toggleUseInlineViewWhenSpaceIsLimited', 'Toggle Use Inline View When Space Is Limited'), - precondition: ContextKeyExpr.has('isInDiffEditor'), - }); - } - - run(accessor: ServicesAccessor, ...args: unknown[]): void { - const configurationService = accessor.get(IConfigurationService); - const newValue = !configurationService.getValue('diffEditor.useInlineViewWhenSpaceIsLimited'); - configurationService.updateValue('diffEditor.useInlineViewWhenSpaceIsLimited', newValue); - } -} - registerAction2(ToggleUseInlineViewWhenSpaceIsLimited); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { @@ -110,130 +44,12 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { when: ContextKeyExpr.has('isInDiffEditor'), }); -const diffEditorCategory: ILocalizedString = localize2('diffEditor', "Diff Editor"); - -export class SwitchSide extends EditorAction2 { - constructor() { - super({ - id: 'diffEditor.switchSide', - title: localize2('switchSide', 'Switch Side'), - icon: Codicon.arrowSwap, - precondition: ContextKeyExpr.has('isInDiffEditor'), - f1: true, - category: diffEditorCategory, - }); - } - - runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, arg?: { dryRun: boolean }): unknown { - const diffEditor = findFocusedDiffEditor(accessor); - if (diffEditor instanceof DiffEditorWidget) { - if (arg && arg.dryRun) { - return { destinationSelection: diffEditor.mapToOtherSide().destinationSelection }; - } else { - diffEditor.switchSide(); - } - } - return undefined; - } -} registerAction2(SwitchSide); - -export class ExitCompareMove extends EditorAction2 { - constructor() { - super({ - id: 'diffEditor.exitCompareMove', - title: localize2('exitCompareMove', 'Exit Compare Move'), - icon: Codicon.close, - precondition: EditorContextKeys.comparingMovedCode, - f1: false, - category: diffEditorCategory, - keybinding: { - weight: 10000, - primary: KeyCode.Escape, - } - }); - } - - runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { - const diffEditor = findFocusedDiffEditor(accessor); - if (diffEditor instanceof DiffEditorWidget) { - diffEditor.exitCompareMove(); - } - } -} - registerAction2(ExitCompareMove); - -export class CollapseAllUnchangedRegions extends EditorAction2 { - constructor() { - super({ - id: 'diffEditor.collapseAllUnchangedRegions', - title: localize2('collapseAllUnchangedRegions', 'Collapse All Unchanged Regions'), - icon: Codicon.fold, - precondition: ContextKeyExpr.has('isInDiffEditor'), - f1: true, - category: diffEditorCategory, - }); - } - - runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { - const diffEditor = findFocusedDiffEditor(accessor); - if (diffEditor instanceof DiffEditorWidget) { - diffEditor.collapseAllUnchangedRegions(); - } - } -} - registerAction2(CollapseAllUnchangedRegions); - -export class ShowAllUnchangedRegions extends EditorAction2 { - constructor() { - super({ - id: 'diffEditor.showAllUnchangedRegions', - title: localize2('showAllUnchangedRegions', 'Show All Unchanged Regions'), - icon: Codicon.unfold, - precondition: ContextKeyExpr.has('isInDiffEditor'), - f1: true, - category: diffEditorCategory, - }); - } - - runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { - const diffEditor = findFocusedDiffEditor(accessor); - if (diffEditor instanceof DiffEditorWidget) { - diffEditor.showAllUnchangedRegions(); - } - } -} - registerAction2(ShowAllUnchangedRegions); -const accessibleDiffViewerCategory: ILocalizedString = localize2('accessibleDiffViewer', "Accessible Diff Viewer"); - -export class AccessibleDiffViewerNext extends Action2 { - public static id = 'editor.action.accessibleDiffViewer.next'; - - constructor() { - super({ - id: AccessibleDiffViewerNext.id, - title: localize2('editor.action.accessibleDiffViewer.next', 'Go to Next Difference'), - category: accessibleDiffViewerCategory, - precondition: ContextKeyExpr.has('isInDiffEditor'), - keybinding: { - primary: KeyCode.F7, - weight: KeybindingWeight.EditorContrib - }, - f1: true, - }); - } - - public override run(accessor: ServicesAccessor): void { - const diffEditor = findFocusedDiffEditor(accessor); - diffEditor?.accessibleDiffViewerNext(); - } -} - MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: AccessibleDiffViewerNext.id, @@ -248,56 +64,6 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { ), }); -export class AccessibleDiffViewerPrev extends Action2 { - public static id = 'editor.action.accessibleDiffViewer.prev'; - - constructor() { - super({ - id: AccessibleDiffViewerPrev.id, - title: localize2('editor.action.accessibleDiffViewer.prev', 'Go to Previous Difference'), - category: accessibleDiffViewerCategory, - precondition: ContextKeyExpr.has('isInDiffEditor'), - keybinding: { - primary: KeyMod.Shift | KeyCode.F7, - weight: KeybindingWeight.EditorContrib - }, - f1: true, - }); - } - - public override run(accessor: ServicesAccessor): void { - const diffEditor = findFocusedDiffEditor(accessor); - diffEditor?.accessibleDiffViewerPrev(); - } -} - -export function findFocusedDiffEditor(accessor: ServicesAccessor): IDiffEditor | null { - const codeEditorService = accessor.get(ICodeEditorService); - const diffEditors = codeEditorService.listDiffEditors(); - - const activeElement = getActiveElement(); - if (activeElement) { - for (const d of diffEditors) { - const container = d.getContainerDomNode(); - if (isElementOrParentOf(container, activeElement)) { - return d; - } - } - } - - return null; -} - -function isElementOrParentOf(elementOrParent: Element, element: Element): boolean { - let e: Element | null = element; - while (e) { - if (e === elementOrParent) { - return true; - } - e = e.parentElement; - } - return false; -} CommandsRegistry.registerCommandAlias('editor.action.diffReview.next', AccessibleDiffViewerNext.id); registerAction2(AccessibleDiffViewerNext); From f63ffa61419924642f0d5722452f16eb14984fa9 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 23 Feb 2024 14:57:23 +0100 Subject: [PATCH 0172/1175] Move diff editor commands into their own files --- .../parts/editor/diffEditorCommands.ts | 231 ++++++++++++++++++ .../parts/editor/editor.contribution.ts | 14 +- .../browser/parts/editor/editorCommands.ts | 221 +---------------- 3 files changed, 241 insertions(+), 225 deletions(-) create mode 100644 src/vs/workbench/browser/parts/editor/diffEditorCommands.ts diff --git a/src/vs/workbench/browser/parts/editor/diffEditorCommands.ts b/src/vs/workbench/browser/parts/editor/diffEditorCommands.ts new file mode 100644 index 00000000000..550ed81628f --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/diffEditorCommands.ts @@ -0,0 +1,231 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { localize2, localize } from 'vs/nls'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor'; +import { TextCompareEditorVisibleContext, TextCompareEditorActiveContext } from 'vs/workbench/common/contextkeys'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + +export const TOGGLE_DIFF_SIDE_BY_SIDE = 'toggle.diff.renderSideBySide'; +export const GOTO_NEXT_CHANGE = 'workbench.action.compareEditor.nextChange'; +export const GOTO_PREVIOUS_CHANGE = 'workbench.action.compareEditor.previousChange'; +export const DIFF_FOCUS_PRIMARY_SIDE = 'workbench.action.compareEditor.focusPrimarySide'; +export const DIFF_FOCUS_SECONDARY_SIDE = 'workbench.action.compareEditor.focusSecondarySide'; +export const DIFF_FOCUS_OTHER_SIDE = 'workbench.action.compareEditor.focusOtherSide'; +export const DIFF_OPEN_SIDE = 'workbench.action.compareEditor.openSide'; +export const TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE = 'toggle.diff.ignoreTrimWhitespace'; +export const DIFF_SWAP_SIDES = 'workbench.action.compareEditor.swapSides'; + +export function registerDiffEditorCommands(): void { + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: GOTO_NEXT_CHANGE, + weight: KeybindingWeight.WorkbenchContrib, + when: TextCompareEditorVisibleContext, + primary: KeyMod.Alt | KeyCode.F5, + handler: accessor => navigateInDiffEditor(accessor, true) + }); + + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: GOTO_NEXT_CHANGE, + title: localize2('compare.nextChange', 'Go to Next Change'), + } + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: GOTO_PREVIOUS_CHANGE, + weight: KeybindingWeight.WorkbenchContrib, + when: TextCompareEditorVisibleContext, + primary: KeyMod.Alt | KeyMod.Shift | KeyCode.F5, + handler: accessor => navigateInDiffEditor(accessor, false) + }); + + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: GOTO_PREVIOUS_CHANGE, + title: localize2('compare.previousChange', 'Go to Previous Change'), + } + }); + + function getActiveTextDiffEditor(accessor: ServicesAccessor): TextDiffEditor | undefined { + const editorService = accessor.get(IEditorService); + + for (const editor of [editorService.activeEditorPane, ...editorService.visibleEditorPanes]) { + if (editor instanceof TextDiffEditor) { + return editor; + } + } + + return undefined; + } + + function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void { + const activeTextDiffEditor = getActiveTextDiffEditor(accessor); + + if (activeTextDiffEditor) { + activeTextDiffEditor.getControl()?.goToDiff(next ? 'next' : 'previous'); + } + } + + enum FocusTextDiffEditorMode { + Original, + Modified, + Toggle + } + + function focusInDiffEditor(accessor: ServicesAccessor, mode: FocusTextDiffEditorMode): void { + const activeTextDiffEditor = getActiveTextDiffEditor(accessor); + + if (activeTextDiffEditor) { + switch (mode) { + case FocusTextDiffEditorMode.Original: + activeTextDiffEditor.getControl()?.getOriginalEditor().focus(); + break; + case FocusTextDiffEditorMode.Modified: + activeTextDiffEditor.getControl()?.getModifiedEditor().focus(); + break; + case FocusTextDiffEditorMode.Toggle: + if (activeTextDiffEditor.getControl()?.getModifiedEditor().hasWidgetFocus()) { + return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original); + } else { + return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified); + } + } + } + } + + function toggleDiffSideBySide(accessor: ServicesAccessor): void { + const configurationService = accessor.get(IConfigurationService); + + const newValue = !configurationService.getValue('diffEditor.renderSideBySide'); + configurationService.updateValue('diffEditor.renderSideBySide', newValue); + } + + function toggleDiffIgnoreTrimWhitespace(accessor: ServicesAccessor): void { + const configurationService = accessor.get(IConfigurationService); + + const newValue = !configurationService.getValue('diffEditor.ignoreTrimWhitespace'); + configurationService.updateValue('diffEditor.ignoreTrimWhitespace', newValue); + } + + async function swapDiffSides(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + + const diffEditor = getActiveTextDiffEditor(accessor); + const activeGroup = diffEditor?.group; + const diffInput = diffEditor?.input; + if (!diffEditor || typeof activeGroup === 'undefined' || !(diffInput instanceof DiffEditorInput) || !diffInput.modified.resource) { + return; + } + + const untypedDiffInput = diffInput.toUntyped({ preserveViewState: activeGroup.id, preserveResource: true }); + if (!untypedDiffInput) { + return; + } + + // Since we are about to replace the diff editor, make + // sure to first open the modified side if it is not + // yet opened. This ensures that the swapping is not + // bringing up a confirmation dialog to save. + if (diffInput.modified.isModified() && editorService.findEditors({ resource: diffInput.modified.resource, typeId: diffInput.modified.typeId, editorId: diffInput.modified.editorId }).length === 0) { + await editorService.openEditor({ + ...untypedDiffInput.modified, + options: { + ...untypedDiffInput.modified.options, + pinned: true, + inactive: true + } + }, activeGroup); + } + + // Replace the input with the swapped variant + await editorService.replaceEditors([ + { + editor: diffInput, + replacement: { + ...untypedDiffInput, + original: untypedDiffInput.modified, + modified: untypedDiffInput.original, + options: { + ...untypedDiffInput.options, + pinned: true + } + } + } + ], activeGroup); + } + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: TOGGLE_DIFF_SIDE_BY_SIDE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => toggleDiffSideBySide(accessor) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: DIFF_FOCUS_PRIMARY_SIDE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: DIFF_FOCUS_SECONDARY_SIDE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: DIFF_FOCUS_OTHER_SIDE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Toggle) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => toggleDiffIgnoreTrimWhitespace(accessor) + }); + + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: DIFF_SWAP_SIDES, + weight: KeybindingWeight.WorkbenchContrib, + when: undefined, + primary: undefined, + handler: accessor => swapDiffSides(accessor) + }); + + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: TOGGLE_DIFF_SIDE_BY_SIDE, + title: localize2('toggleInlineView', "Toggle Inline View"), + category: localize('compare', "Compare") + }, + when: TextCompareEditorActiveContext + }); + + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: DIFF_SWAP_SIDES, + title: localize2('swapDiffSides', "Swap Left and Right Editor Side"), + category: localize('compare', "Compare") + }, + when: TextCompareEditorActiveContext + }); +} diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index ca21ad13864..a60e8e59d62 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -47,12 +47,13 @@ import { } 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, - CLOSE_PINNED_EDITOR_COMMAND_ID, CLOSE_SAVED_EDITORS_COMMAND_ID, GOTO_NEXT_CHANGE, GOTO_PREVIOUS_CHANGE, KEEP_EDITOR_COMMAND_ID, PIN_EDITOR_COMMAND_ID, SHOW_EDITORS_IN_GROUP, SPLIT_EDITOR_DOWN, SPLIT_EDITOR_LEFT, - SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, TOGGLE_DIFF_SIDE_BY_SIDE, TOGGLE_KEEP_EDITORS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, setup as registerEditorCommands, REOPEN_WITH_COMMAND_ID, + CLOSE_PINNED_EDITOR_COMMAND_ID, CLOSE_SAVED_EDITORS_COMMAND_ID, KEEP_EDITOR_COMMAND_ID, PIN_EDITOR_COMMAND_ID, SHOW_EDITORS_IN_GROUP, SPLIT_EDITOR_DOWN, SPLIT_EDITOR_LEFT, + SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, TOGGLE_KEEP_EDITORS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, setup as registerEditorCommands, REOPEN_WITH_COMMAND_ID, TOGGLE_LOCK_GROUP_COMMAND_ID, UNLOCK_GROUP_COMMAND_ID, SPLIT_EDITOR_IN_GROUP, JOIN_EDITOR_IN_GROUP, FOCUS_FIRST_SIDE_EDITOR, FOCUS_SECOND_SIDE_EDITOR, TOGGLE_SPLIT_EDITOR_IN_GROUP_LAYOUT, LOCK_GROUP_COMMAND_ID, SPLIT_EDITOR, TOGGLE_MAXIMIZE_EDITOR_GROUP, MOVE_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, MOVE_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, - NEW_EMPTY_EDITOR_WINDOW_COMMAND_ID, DIFF_SWAP_SIDES + NEW_EMPTY_EDITOR_WINDOW_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; +import { GOTO_NEXT_CHANGE, GOTO_PREVIOUS_CHANGE, TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, TOGGLE_DIFF_SIDE_BY_SIDE, DIFF_SWAP_SIDES } from './diffEditorCommands'; import { inQuickPickContext, getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; @@ -570,11 +571,8 @@ appendEditorToolItem( CLOSE_ORDER - 1, // immediately to the left of close action ); -const previousChangeIcon = registerIcon('diff-editor-previous-change', Codicon.arrowUp, localize('previousChangeIcon', 'Icon for the previous change action in the diff editor.')); -const nextChangeIcon = registerIcon('diff-editor-next-change', Codicon.arrowDown, localize('nextChangeIcon', 'Icon for the next change action in the diff editor.')); -const toggleWhitespace = registerIcon('diff-editor-toggle-whitespace', Codicon.whitespace, localize('toggleWhitespace', 'Icon for the toggle whitespace action in the diff editor.')); - // Diff Editor Title Menu: Previous Change +const previousChangeIcon = registerIcon('diff-editor-previous-change', Codicon.arrowUp, localize('previousChangeIcon', 'Icon for the previous change action in the diff editor.')); appendEditorToolItem( { id: GOTO_PREVIOUS_CHANGE, @@ -588,6 +586,7 @@ appendEditorToolItem( ); // Diff Editor Title Menu: Next Change +const nextChangeIcon = registerIcon('diff-editor-next-change', Codicon.arrowDown, localize('nextChangeIcon', 'Icon for the next change action in the diff editor.')); appendEditorToolItem( { id: GOTO_NEXT_CHANGE, @@ -613,6 +612,7 @@ appendEditorToolItem( undefined ); +const toggleWhitespace = registerIcon('diff-editor-toggle-whitespace', Codicon.whitespace, localize('toggleWhitespace', 'Icon for the toggle whitespace action in the diff editor.')); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 68cd3a6b328..89795c71057 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -16,7 +16,7 @@ import { isDiffEditor } from 'vs/editor/browser/editorBrowser'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { localize, localize2 } from 'vs/nls'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; -import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; +import { Action2, 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'; @@ -30,7 +30,7 @@ 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 { ActiveEditorCanSplitInGroupContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupLockedContext, ActiveEditorStickyContext, MultipleEditorGroupsContext, SideBySideEditorActiveContext, TextCompareEditorActiveContext } 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'; @@ -41,6 +41,7 @@ import { IEditorResolverService } from 'vs/workbench/services/editor/common/edit 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'; +import { DIFF_FOCUS_OTHER_SIDE, DIFF_FOCUS_PRIMARY_SIDE, DIFF_FOCUS_SECONDARY_SIDE, DIFF_OPEN_SIDE, registerDiffEditorCommands } from './diffEditorCommands'; export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors'; export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup'; @@ -65,16 +66,6 @@ export const REOPEN_WITH_COMMAND_ID = 'workbench.action.reopenWithEditor'; export const PIN_EDITOR_COMMAND_ID = 'workbench.action.pinEditor'; export const UNPIN_EDITOR_COMMAND_ID = 'workbench.action.unpinEditor'; -export const TOGGLE_DIFF_SIDE_BY_SIDE = 'toggle.diff.renderSideBySide'; -export const GOTO_NEXT_CHANGE = 'workbench.action.compareEditor.nextChange'; -export const GOTO_PREVIOUS_CHANGE = 'workbench.action.compareEditor.previousChange'; -export const DIFF_FOCUS_PRIMARY_SIDE = 'workbench.action.compareEditor.focusPrimarySide'; -export const DIFF_FOCUS_SECONDARY_SIDE = 'workbench.action.compareEditor.focusSecondarySide'; -export const DIFF_FOCUS_OTHER_SIDE = 'workbench.action.compareEditor.focusOtherSide'; -export const DIFF_OPEN_SIDE = 'workbench.action.compareEditor.openSide'; -export const TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE = 'toggle.diff.ignoreTrimWhitespace'; -export const DIFF_SWAP_SIDES = 'workbench.action.compareEditor.swapSides'; - export const SPLIT_EDITOR = 'workbench.action.splitEditor'; export const SPLIT_EDITOR_UP = 'workbench.action.splitEditorUp'; export const SPLIT_EDITOR_DOWN = 'workbench.action.splitEditorDown'; @@ -373,212 +364,6 @@ function registerEditorGroupsLayoutCommands(): void { }); } -function registerDiffEditorCommands(): void { - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: GOTO_NEXT_CHANGE, - weight: KeybindingWeight.WorkbenchContrib, - when: TextCompareEditorVisibleContext, - primary: KeyMod.Alt | KeyCode.F5, - handler: accessor => navigateInDiffEditor(accessor, true) - }); - - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: GOTO_NEXT_CHANGE, - title: localize2('compare.nextChange', 'Go to Next Change'), - } - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: GOTO_PREVIOUS_CHANGE, - weight: KeybindingWeight.WorkbenchContrib, - when: TextCompareEditorVisibleContext, - primary: KeyMod.Alt | KeyMod.Shift | KeyCode.F5, - handler: accessor => navigateInDiffEditor(accessor, false) - }); - - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: GOTO_PREVIOUS_CHANGE, - title: localize2('compare.previousChange', 'Go to Previous Change'), - } - }); - - function getActiveTextDiffEditor(accessor: ServicesAccessor): TextDiffEditor | undefined { - const editorService = accessor.get(IEditorService); - - for (const editor of [editorService.activeEditorPane, ...editorService.visibleEditorPanes]) { - if (editor instanceof TextDiffEditor) { - return editor; - } - } - - return undefined; - } - - function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void { - const activeTextDiffEditor = getActiveTextDiffEditor(accessor); - - if (activeTextDiffEditor) { - activeTextDiffEditor.getControl()?.goToDiff(next ? 'next' : 'previous'); - } - } - - enum FocusTextDiffEditorMode { - Original, - Modified, - Toggle - } - - function focusInDiffEditor(accessor: ServicesAccessor, mode: FocusTextDiffEditorMode): void { - const activeTextDiffEditor = getActiveTextDiffEditor(accessor); - - if (activeTextDiffEditor) { - switch (mode) { - case FocusTextDiffEditorMode.Original: - activeTextDiffEditor.getControl()?.getOriginalEditor().focus(); - break; - case FocusTextDiffEditorMode.Modified: - activeTextDiffEditor.getControl()?.getModifiedEditor().focus(); - break; - case FocusTextDiffEditorMode.Toggle: - if (activeTextDiffEditor.getControl()?.getModifiedEditor().hasWidgetFocus()) { - return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original); - } else { - return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified); - } - } - } - } - - function toggleDiffSideBySide(accessor: ServicesAccessor): void { - const configurationService = accessor.get(IConfigurationService); - - const newValue = !configurationService.getValue('diffEditor.renderSideBySide'); - configurationService.updateValue('diffEditor.renderSideBySide', newValue); - } - - function toggleDiffIgnoreTrimWhitespace(accessor: ServicesAccessor): void { - const configurationService = accessor.get(IConfigurationService); - - const newValue = !configurationService.getValue('diffEditor.ignoreTrimWhitespace'); - configurationService.updateValue('diffEditor.ignoreTrimWhitespace', newValue); - } - - async function swapDiffSides(accessor: ServicesAccessor): Promise { - const editorService = accessor.get(IEditorService); - - const diffEditor = getActiveTextDiffEditor(accessor); - const activeGroup = diffEditor?.group; - const diffInput = diffEditor?.input; - if (!diffEditor || typeof activeGroup === 'undefined' || !(diffInput instanceof DiffEditorInput) || !diffInput.modified.resource) { - return; - } - - const untypedDiffInput = diffInput.toUntyped({ preserveViewState: activeGroup.id, preserveResource: true }); - if (!untypedDiffInput) { - return; - } - - // Since we are about to replace the diff editor, make - // sure to first open the modified side if it is not - // yet opened. This ensures that the swapping is not - // bringing up a confirmation dialog to save. - if (diffInput.modified.isModified() && editorService.findEditors({ resource: diffInput.modified.resource, typeId: diffInput.modified.typeId, editorId: diffInput.modified.editorId }).length === 0) { - await editorService.openEditor({ - ...untypedDiffInput.modified, - options: { - ...untypedDiffInput.modified.options, - pinned: true, - inactive: true - } - }, activeGroup); - } - - // Replace the input with the swapped variant - await editorService.replaceEditors([ - { - editor: diffInput, - replacement: { - ...untypedDiffInput, - original: untypedDiffInput.modified, - modified: untypedDiffInput.original, - options: { - ...untypedDiffInput.options, - pinned: true - } - } - } - ], activeGroup); - } - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: TOGGLE_DIFF_SIDE_BY_SIDE, - weight: KeybindingWeight.WorkbenchContrib, - when: undefined, - primary: undefined, - handler: accessor => toggleDiffSideBySide(accessor) - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: DIFF_FOCUS_PRIMARY_SIDE, - weight: KeybindingWeight.WorkbenchContrib, - when: undefined, - primary: undefined, - handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified) - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: DIFF_FOCUS_SECONDARY_SIDE, - weight: KeybindingWeight.WorkbenchContrib, - when: undefined, - primary: undefined, - handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original) - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: DIFF_FOCUS_OTHER_SIDE, - weight: KeybindingWeight.WorkbenchContrib, - when: undefined, - primary: undefined, - handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Toggle) - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE, - weight: KeybindingWeight.WorkbenchContrib, - when: undefined, - primary: undefined, - handler: accessor => toggleDiffIgnoreTrimWhitespace(accessor) - }); - - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: DIFF_SWAP_SIDES, - weight: KeybindingWeight.WorkbenchContrib, - when: undefined, - primary: undefined, - handler: accessor => swapDiffSides(accessor) - }); - - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: TOGGLE_DIFF_SIDE_BY_SIDE, - title: localize2('toggleInlineView', "Toggle Inline View"), - category: localize('compare', "Compare") - }, - when: TextCompareEditorActiveContext - }); - - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: { - id: DIFF_SWAP_SIDES, - title: localize2('swapDiffSides', "Swap Left and Right Editor Side"), - category: localize('compare', "Compare") - }, - when: TextCompareEditorActiveContext - }); -} - function registerOpenEditorAPICommands(): void { function mixinContext(context: IOpenEvent | undefined, options: ITextEditorOptions | undefined, column: EditorGroupColumn | undefined): [ITextEditorOptions | undefined, EditorGroupColumn | undefined] { From 6176d9642094a0140f4318659a683bb3baf0915c Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 23 Feb 2024 19:22:57 +0100 Subject: [PATCH 0173/1175] Fixes CI --- .../contrib/chat/browser/actions/chatAccessibilityHelp.ts | 2 +- src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts index 9fd67d69b88..92b430ff162 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts @@ -12,7 +12,7 @@ import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; -import { AccessibleDiffViewerNext } from 'vs/editor/browser/widget/diffEditor/diffEditor.contribution'; +import { AccessibleDiffViewerNext } from 'vs/editor/browser/widget/diffEditor/commands'; export function getAccessibilityHelpText(accessor: ServicesAccessor, type: 'panelChat' | 'inlineChat'): string { const keybindingService = accessor.get(IKeybindingService); diff --git a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts index a19d88cca33..902b335ba4f 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts @@ -8,7 +8,7 @@ import { autorunWithStore, observableFromEvent } from 'vs/base/common/observable import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { registerDiffEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { AccessibleDiffViewerNext, AccessibleDiffViewerPrev } from 'vs/editor/browser/widget/diffEditor/diffEditor.contribution'; +import { AccessibleDiffViewerNext, AccessibleDiffViewerPrev } from 'vs/editor/browser/widget/diffEditor/commands'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget'; import { IDiffEditorContribution } from 'vs/editor/common/editorCommon'; From 9e5982f512e32739e6ffe92e8c9b123531430b5a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 26 Feb 2024 12:51:14 +0100 Subject: [PATCH 0174/1175] Some :lipstick: around the code (#206222) --- .../browser/parts/editor/editorStatus.ts | 43 +++++++++--------- src/vs/workbench/common/contributions.ts | 2 +- src/vs/workbench/common/editor.ts | 6 +-- .../common/editor/sideBySideEditorInput.ts | 6 +-- .../browser/multiDiffEditorInput.ts | 4 +- .../contrib/remote/browser/remoteIndicator.ts | 45 +++++++++++-------- .../editor/browser/editorResolverService.ts | 4 +- .../editor/common/editorResolverService.ts | 4 +- 8 files changed, 62 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index e1ee2bc0d94..d34a58122cd 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -293,8 +293,6 @@ class TabFocusMode extends Disposable { const tabFocusModeConfig = configurationService.getValue('editor.tabFocusMode') === true ? true : false; TabFocus.setTabFocusMode(tabFocusModeConfig); - - this._onDidChange.fire(tabFocusModeConfig); } private registerListeners(): void { @@ -328,14 +326,16 @@ class EditorStatus extends Disposable { private readonly eolElement = this._register(new MutableDisposable()); private readonly languageElement = this._register(new MutableDisposable()); private readonly metadataElement = this._register(new MutableDisposable()); - private readonly currentProblemStatus = this._register(this.instantiationService.createInstance(ShowCurrentMarkerInStatusbarContribution)); - private readonly state = new State(); - private readonly activeEditorListeners = this._register(new DisposableStore()); - private readonly delayedRender = this._register(new MutableDisposable()); + + private readonly currentMarkerStatus = this._register(this.instantiationService.createInstance(ShowCurrentMarkerInStatusbarContribution)); private readonly tabFocusMode = this.instantiationService.createInstance(TabFocusMode); + private readonly state = new State(); private toRender: StateChange | undefined = undefined; + private readonly activeEditorListeners = this._register(new DisposableStore()); + private readonly delayedRender = this._register(new MutableDisposable()); + constructor( private readonly targetWindowId: number, @IEditorService private readonly editorService: IEditorService, @@ -634,7 +634,7 @@ class EditorStatus extends Disposable { this.onEncodingChange(activeEditorPane, activeCodeEditor); this.onIndentationChange(activeCodeEditor); this.onMetadataChange(activeEditorPane); - this.currentProblemStatus.update(activeCodeEditor); + this.currentMarkerStatus.update(activeCodeEditor); // Dispose old active editor listeners this.activeEditorListeners.clear(); @@ -662,7 +662,7 @@ class EditorStatus extends Disposable { // Hook Listener for Selection changes this.activeEditorListeners.add(Event.defer(activeCodeEditor.onDidChangeCursorPosition)(() => { this.onSelectionChange(activeCodeEditor); - this.currentProblemStatus.update(activeCodeEditor); + this.currentMarkerStatus.update(activeCodeEditor); })); // Hook Listener for language changes @@ -673,7 +673,7 @@ class EditorStatus extends Disposable { // Hook Listener for content changes this.activeEditorListeners.add(Event.accumulate(activeCodeEditor.onDidChangeModelContent)(e => { this.onEOLChange(activeCodeEditor); - this.currentProblemStatus.update(activeCodeEditor); + this.currentMarkerStatus.update(activeCodeEditor); const selections = activeCodeEditor.getSelections(); if (selections) { @@ -918,13 +918,16 @@ class ShowCurrentMarkerInStatusbarContribution extends Disposable { @IConfigurationService private readonly configurationService: IConfigurationService, ) { super(); + this.statusBarEntryAccessor = this._register(new MutableDisposable()); + this._register(markerService.onMarkerChanged(changedResources => this.onMarkerChanged(changedResources))); this._register(Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('problems.showCurrentInStatus'))(() => this.updateStatus())); } update(editor: ICodeEditor | undefined): void { this.editor = editor; + this.updateMarkers(); this.updateStatus(); } @@ -1022,26 +1025,26 @@ class ShowCurrentMarkerInStatusbarContribution extends Disposable { resource: model.uri, severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }); - this.markers.sort(compareMarker); + this.markers.sort(this.compareMarker); } else { this.markers = []; } this.updateStatus(); } -} -function compareMarker(a: IMarker, b: IMarker): number { - let res = compare(a.resource.toString(), b.resource.toString()); - if (res === 0) { - res = MarkerSeverity.compare(a.severity, b.severity); - } + private compareMarker(a: IMarker, b: IMarker): number { + let res = compare(a.resource.toString(), b.resource.toString()); + if (res === 0) { + res = MarkerSeverity.compare(a.severity, b.severity); + } - if (res === 0) { - res = Range.compareRangesUsingStarts(a, b); - } + if (res === 0) { + res = Range.compareRangesUsingStarts(a, b); + } - return res; + return res; + } } export class ShowLanguageExtensionsAction extends Action { diff --git a/src/vs/workbench/common/contributions.ts b/src/vs/workbench/common/contributions.ts index b96df678196..aaf1452c25a 100644 --- a/src/vs/workbench/common/contributions.ts +++ b/src/vs/workbench/common/contributions.ts @@ -385,7 +385,7 @@ export class WorkbenchContributionsRegistry extends Disposable implements IWorkb } } - if (typeof contribution.id === 'string' || !environmentService.isBuilt /* only log out of sources where we have good ctor names (TODO@bpasero remove when adopted IDs) */) { + if (typeof contribution.id === 'string' || !environmentService.isBuilt /* only log out of sources where we have good ctor names */) { const time = Date.now() - now; if (time > (phase < LifecyclePhase.Restored ? WorkbenchContributionsRegistry.BLOCK_BEFORE_RESTORE_WARN_THRESHOLD : WorkbenchContributionsRegistry.BLOCK_AFTER_RESTORE_WARN_THRESHOLD)) { logService.warn(`Creation of workbench contribution '${contribution.id ?? contribution.ctor.name}' took ${time}ms.`); diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index c6986bd0f57..9652c2dfe00 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -564,7 +564,7 @@ export function isResourceDiffEditorInput(editor: unknown): editor is IResourceD return candidate?.original !== undefined && candidate.modified !== undefined; } -export function isResourceDiffListEditorInput(editor: unknown): editor is IResourceMultiDiffEditorInput { +export function isResourceMultiDiffEditorInput(editor: unknown): editor is IResourceMultiDiffEditorInput { if (isEditorInput(editor)) { return false; // make sure to not accidentally match on typed editor inputs } @@ -1310,7 +1310,7 @@ class EditorResourceAccessorImpl { } } - if (isResourceDiffEditorInput(editor) || isResourceDiffListEditorInput(editor) || isResourceSideBySideEditorInput(editor) || isResourceMergeEditorInput(editor)) { + if (isResourceDiffEditorInput(editor) || isResourceMultiDiffEditorInput(editor) || isResourceSideBySideEditorInput(editor) || isResourceMergeEditorInput(editor)) { return undefined; } @@ -1379,7 +1379,7 @@ class EditorResourceAccessorImpl { } } - if (isResourceDiffEditorInput(editor) || isResourceDiffListEditorInput(editor) || isResourceSideBySideEditorInput(editor) || isResourceMergeEditorInput(editor)) { + if (isResourceDiffEditorInput(editor) || isResourceMultiDiffEditorInput(editor) || isResourceSideBySideEditorInput(editor) || isResourceMergeEditorInput(editor)) { return undefined; } diff --git a/src/vs/workbench/common/editor/sideBySideEditorInput.ts b/src/vs/workbench/common/editor/sideBySideEditorInput.ts index 7228b47ae71..0c6e63d43ce 100644 --- a/src/vs/workbench/common/editor/sideBySideEditorInput.ts +++ b/src/vs/workbench/common/editor/sideBySideEditorInput.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; -import { EditorInputCapabilities, GroupIdentifier, ISaveOptions, IRevertOptions, EditorExtensions, IEditorFactoryRegistry, IEditorSerializer, ISideBySideEditorInput, IUntypedEditorInput, isResourceSideBySideEditorInput, isDiffEditorInput, isResourceDiffEditorInput, IResourceSideBySideEditorInput, findViewStateForEditor, IMoveResult, isEditorInput, isResourceEditorInput, Verbosity, isResourceMergeEditorInput, isResourceDiffListEditorInput } from 'vs/workbench/common/editor'; +import { EditorInputCapabilities, GroupIdentifier, ISaveOptions, IRevertOptions, EditorExtensions, IEditorFactoryRegistry, IEditorSerializer, ISideBySideEditorInput, IUntypedEditorInput, isResourceSideBySideEditorInput, isDiffEditorInput, isResourceDiffEditorInput, IResourceSideBySideEditorInput, findViewStateForEditor, IMoveResult, isEditorInput, isResourceEditorInput, Verbosity, isResourceMergeEditorInput, isResourceMultiDiffEditorInput } from 'vs/workbench/common/editor'; import { EditorInput, IUntypedEditorOptions } from 'vs/workbench/common/editor/editorInput'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -210,7 +210,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi return new SideBySideEditorInput(this.preferredName, this.preferredDescription, primarySaveResult, primarySaveResult, this.editorService); } - if (!isResourceDiffEditorInput(primarySaveResult) && !isResourceDiffListEditorInput(primarySaveResult) && !isResourceSideBySideEditorInput(primarySaveResult) && !isResourceMergeEditorInput(primarySaveResult)) { + if (!isResourceDiffEditorInput(primarySaveResult) && !isResourceMultiDiffEditorInput(primarySaveResult) && !isResourceSideBySideEditorInput(primarySaveResult) && !isResourceMergeEditorInput(primarySaveResult)) { return { primary: primarySaveResult, secondary: primarySaveResult, @@ -279,7 +279,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi if ( primaryResourceEditorInput && secondaryResourceEditorInput && !isResourceDiffEditorInput(primaryResourceEditorInput) && !isResourceDiffEditorInput(secondaryResourceEditorInput) && - !isResourceDiffListEditorInput(primaryResourceEditorInput) && !isResourceDiffListEditorInput(secondaryResourceEditorInput) && + !isResourceMultiDiffEditorInput(primaryResourceEditorInput) && !isResourceMultiDiffEditorInput(secondaryResourceEditorInput) && !isResourceSideBySideEditorInput(primaryResourceEditorInput) && !isResourceSideBySideEditorInput(secondaryResourceEditorInput) && !isResourceMergeEditorInput(primaryResourceEditorInput) && !isResourceMergeEditorInput(secondaryResourceEditorInput) ) { diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts index 4344d015873..ab81ed790df 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts @@ -343,9 +343,9 @@ export class MultiDiffEditorResolverContribution extends Disposable { }, {}, { - createMultiDiffEditorInput: (diffListEditor: IResourceMultiDiffEditorInput): EditorInputWithOptions => { + createMultiDiffEditorInput: (multiDiffEditor: IResourceMultiDiffEditorInput): EditorInputWithOptions => { return { - editor: MultiDiffEditorInput.fromResourceMultiDiffEditorInput(diffListEditor, instantiationService), + editor: MultiDiffEditorInput.fromResourceMultiDiffEditorInput(multiDiffEditor, instantiationService), }; }, } diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index f362b61aa1b..f5d98f3fd90 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -97,7 +97,32 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr private measureNetworkConnectionLatencyScheduler: RunOnceScheduler | undefined = undefined; private loggedInvalidGroupNames: { [group: string]: boolean } = Object.create(null); - private readonly remoteExtensionMetadata: RemoteExtensionMetadata[]; + + private _remoteExtensionMetadata: RemoteExtensionMetadata[] | undefined = undefined; + private get remoteExtensionMetadata(): RemoteExtensionMetadata[] { + if (!this._remoteExtensionMetadata) { + const remoteExtensionTips = { ...this.productService.remoteExtensionTips, ...this.productService.virtualWorkspaceExtensionTips }; + this._remoteExtensionMetadata = Object.values(remoteExtensionTips).filter(value => value.startEntry !== undefined).map(value => { + return { + id: value.extensionId, + installed: false, + friendlyName: value.friendlyName, + isPlatformCompatible: false, + dependencies: [], + helpLink: value.startEntry?.helpLink ?? '', + startConnectLabel: value.startEntry?.startConnectLabel ?? '', + startCommand: value.startEntry?.startCommand ?? '', + priority: value.startEntry?.priority ?? 10, + supportedPlatforms: value.supportedPlatforms + }; + }); + + this.remoteExtensionMetadata.sort((ext1, ext2) => ext1.priority - ext2.priority); + } + + return this._remoteExtensionMetadata; + } + private remoteMetadataInitialized: boolean = false; private readonly _onDidChangeEntries = this._register(new Emitter()); private readonly onDidChangeEntries: Event = this._onDidChangeEntries.event; @@ -124,24 +149,6 @@ export class RemoteStatusIndicator extends Disposable implements IWorkbenchContr ) { super(); - const remoteExtensionTips = { ...this.productService.remoteExtensionTips, ...this.productService.virtualWorkspaceExtensionTips }; - this.remoteExtensionMetadata = Object.values(remoteExtensionTips).filter(value => value.startEntry !== undefined).map(value => { - return { - id: value.extensionId, - installed: false, - friendlyName: value.friendlyName, - isPlatformCompatible: false, - dependencies: [], - helpLink: value.startEntry?.helpLink ?? '', - startConnectLabel: value.startEntry?.startConnectLabel ?? '', - startCommand: value.startEntry?.startCommand ?? '', - priority: value.startEntry?.priority ?? 10, - supportedPlatforms: value.supportedPlatforms - }; - }); - - this.remoteExtensionMetadata.sort((ext1, ext2) => ext1.priority - ext2.priority); - // Set initial connection state if (this.remoteAuthority) { this.connectionState = 'initializing'; diff --git a/src/vs/workbench/services/editor/browser/editorResolverService.ts b/src/vs/workbench/services/editor/browser/editorResolverService.ts index 91d1b7f6da9..d6c3375262a 100644 --- a/src/vs/workbench/services/editor/browser/editorResolverService.ts +++ b/src/vs/workbench/services/editor/browser/editorResolverService.ts @@ -10,7 +10,7 @@ import { basename, extname, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { EditorActivation, EditorResolution, IEditorOptions } from 'vs/platform/editor/common/editor'; -import { DEFAULT_EDITOR_ASSOCIATION, EditorResourceAccessor, EditorInputWithOptions, IResourceSideBySideEditorInput, isEditorInputWithOptions, isEditorInputWithOptionsAndGroup, isResourceDiffEditorInput, isResourceSideBySideEditorInput, isUntitledResourceEditorInput, isResourceMergeEditorInput, IUntypedEditorInput, SideBySideEditor, isResourceDiffListEditorInput } from 'vs/workbench/common/editor'; +import { DEFAULT_EDITOR_ASSOCIATION, EditorResourceAccessor, EditorInputWithOptions, IResourceSideBySideEditorInput, isEditorInputWithOptions, isEditorInputWithOptionsAndGroup, isResourceDiffEditorInput, isResourceSideBySideEditorInput, isUntitledResourceEditorInput, isResourceMergeEditorInput, IUntypedEditorInput, SideBySideEditor, isResourceMultiDiffEditorInput } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { Schemas } from 'vs/base/common/network'; @@ -476,7 +476,7 @@ export class EditorResolverService extends Disposable implements IEditorResolver } // If it's a diff list editor we trigger the create diff list editor input - if (isResourceDiffListEditorInput(editor)) { + if (isResourceMultiDiffEditorInput(editor)) { if (!selectedEditor.editorFactoryObject.createMultiDiffEditorInput) { return; } diff --git a/src/vs/workbench/services/editor/common/editorResolverService.ts b/src/vs/workbench/services/editor/common/editorResolverService.ts index cd571b4d9e2..bc3a26d311b 100644 --- a/src/vs/workbench/services/editor/common/editorResolverService.ts +++ b/src/vs/workbench/services/editor/common/editorResolverService.ts @@ -108,7 +108,7 @@ export type UntitledEditorInputFactoryFunction = (untitledEditorInput: IUntitled export type DiffEditorInputFactoryFunction = (diffEditorInput: IResourceDiffEditorInput, group: IEditorGroup) => EditorInputFactoryResult; -export type DiffListEditorInputFactoryFunction = (diffEditorInput: IResourceMultiDiffEditorInput, group: IEditorGroup) => EditorInputFactoryResult; +export type MultiDiffEditorInputFactoryFunction = (multiDiffEditorInput: IResourceMultiDiffEditorInput, group: IEditorGroup) => EditorInputFactoryResult; export type MergeEditorInputFactoryFunction = (mergeEditorInput: IResourceMergeEditorInput, group: IEditorGroup) => EditorInputFactoryResult; @@ -116,7 +116,7 @@ type EditorInputFactories = { createEditorInput?: EditorInputFactoryFunction; createUntitledEditorInput?: UntitledEditorInputFactoryFunction; createDiffEditorInput?: DiffEditorInputFactoryFunction; - createMultiDiffEditorInput?: DiffListEditorInputFactoryFunction; + createMultiDiffEditorInput?: MultiDiffEditorInputFactoryFunction; createMergeEditorInput?: MergeEditorInputFactoryFunction; }; From 9f4bcdde684bc54e311ea49cb469771796094e7d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 26 Feb 2024 13:08:31 +0100 Subject: [PATCH 0175/1175] voice - tweak mic animation further (#206064) --- .../electron-sandbox/actions/voiceChatActions.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index b508d24ff6b..36ff7b6c4b0 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -788,7 +788,18 @@ registerThemingParticipant((theme, collector) => { .monaco-workbench:not(.reduce-motion) .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled) { border-radius: 50%; outline: 2px solid ${activeRecordingColor}; - animation: pulseAnimation 1500ms ease-in-out infinite !important; + outline-offset: -1px; + animation: pulseAnimation 1500ms cubic-bezier(0.75, 0, 0.25, 1) infinite; + } + + .monaco-workbench:not(.reduce-motion) .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before, + .monaco-workbench:not(.reduce-motion) .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled)::before { + position: absolute; + outline: 1px solid ${activeRecordingColor}; + outline-offset: 2px; + border-radius: 50%; + width: 16px; + height: 16px; } @keyframes pulseAnimation { From 10e94ec363bf41ca09098a44186bc915b710cb61 Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Mon, 26 Feb 2024 14:03:25 +0100 Subject: [PATCH 0176/1175] rename suggestions: use editor#typicalHalfwidthCharacterWidth --- src/vs/editor/contrib/rename/browser/renameInputField.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/rename/browser/renameInputField.ts b/src/vs/editor/contrib/rename/browser/renameInputField.ts index 6cb9f8a8c5a..3bb00ecdb05 100644 --- a/src/vs/editor/contrib/rename/browser/renameInputField.ts +++ b/src/vs/editor/contrib/rename/browser/renameInputField.ts @@ -435,6 +435,7 @@ class CandidatesView { private _lineHeight: number; private _availableHeight: number; private _minimumWidth: number; + private _typicalHalfwidthCharacterWidth: number; private _disposables: DisposableStore; @@ -446,6 +447,7 @@ class CandidatesView { this._minimumWidth = 0; this._lineHeight = opts.fontInfo.lineHeight; + this._typicalHalfwidthCharacterWidth = opts.fontInfo.typicalHalfwidthCharacterWidth; this._listContainer = document.createElement('div'); this._listContainer.style.fontFamily = opts.fontInfo.fontFamily; @@ -612,8 +614,7 @@ class CandidatesView { } private _pickListWidth(candidates: NewSymbolName[]): number { - const APPROXIMATE_CHAR_WIDTH = 8; // approximate # of pixes taken by a single character - const longestCandidateWidth = Math.ceil(Math.max(...candidates.map(c => c.newSymbolName.length)) * APPROXIMATE_CHAR_WIDTH); // TODO@ulugbekna: use editor#typicalCharacterWidth or something + const longestCandidateWidth = Math.ceil(Math.max(...candidates.map(c => c.newSymbolName.length)) * this._typicalHalfwidthCharacterWidth); const width = Math.max( this._minimumWidth, 4 /* padding */ + 16 /* sparkle icon */ + 5 /* margin-left */ + longestCandidateWidth + 10 /* (possibly visible) scrollbar width */ // TODO@ulugbekna: approximate calc - clean this up From 20f1afb29131d150028318dfe8afe33efc288693 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 26 Feb 2024 14:40:58 +0100 Subject: [PATCH 0177/1175] api - update todos, rename makeChatRequest (#206240) --- src/vs/workbench/api/common/extHost.api.impl.ts | 2 +- src/vscode-dts/vscode.proposed.languageModels.d.ts | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 3bf78d8ed24..65b718f3f05 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1437,7 +1437,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'languageModels'); return extHostChatProvider.onDidChangeProviders(listener, thisArgs, disposables); }, - makeChatRequest(languageModel: string, messages: vscode.LanguageModelMessage[], optionsOrToken: { [name: string]: any } | vscode.CancellationToken, token?: vscode.CancellationToken) { + chatRequest(languageModel: string, messages: vscode.LanguageModelMessage[], optionsOrToken: { [name: string]: any } | vscode.CancellationToken, token?: vscode.CancellationToken) { checkProposedApiEnabled(extension, 'languageModels'); let options: Record; if (CancellationToken.isCancellationToken(optionsOrToken)) { diff --git a/src/vscode-dts/vscode.proposed.languageModels.d.ts b/src/vscode-dts/vscode.proposed.languageModels.d.ts index bde1bd4aad4..258a5bf18e9 100644 --- a/src/vscode-dts/vscode.proposed.languageModels.d.ts +++ b/src/vscode-dts/vscode.proposed.languageModels.d.ts @@ -190,12 +190,19 @@ declare module 'vscode' { */ // TODO@API refine doc // TODO@API define specific error types? - export function makeChatRequest(languageModel: string, messages: LanguageModelMessage[], options: { [name: string]: any }, token: CancellationToken): Thenable; + // TODO@API NAME: chatRequest + // TODO@API NAME: ChatRequestXYZMessage + // TODO@API NAME: ChatRequestResponse + export function chatRequest(languageModel: string, messages: LanguageModelMessage[], options: { [name: string]: any }, token: CancellationToken): Thenable; /** - * @see {@link makeChatRequest} + * @see {@link chatRequest} */ - export function makeChatRequest(languageModel: string, messages: LanguageModelMessage[], token: CancellationToken): Thenable; + export function chatRequest(languageModel: string, messages: LanguageModelMessage[], token: CancellationToken): Thenable; + + // TODO@API probe on having access + // TODO@API, BETTER?: ExtensionContext.permissions.languageModels: Record; + // export function canMakeChatRequest(languageModel: string): Thenable; /** * The identifiers of all language models that are currently available. From b1a49d4cdb0ae53e19a93a27cb276061e34d9d8b Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:19:48 +0100 Subject: [PATCH 0178/1175] Fix hover pointer and override options (#206246) fix hover pointer and override options --- src/vs/platform/hover/browser/hover.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/hover/browser/hover.ts b/src/vs/platform/hover/browser/hover.ts index fea45187b43..0a593c04ec8 100644 --- a/src/vs/platform/hover/browser/hover.ts +++ b/src/vs/platform/hover/browser/hover.ts @@ -281,14 +281,17 @@ export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate return this.hoverService.showHover({ ...options, + ...overrideOptions, persistence: { hideOnHover: true, hideOnKeyDown: true, + ...overrideOptions.persistence }, appearance: { + ...options.appearance, compact: true, - }, - ...overrideOptions + ...overrideOptions.appearance + } }, focus); } From 845c8ed65c5daecf5395d6216db63cf2bb0eaa9b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 26 Feb 2024 15:35:06 +0100 Subject: [PATCH 0179/1175] Indentation doesn't work in .yml files (#205979) --- extensions/docker/language-configuration.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/extensions/docker/language-configuration.json b/extensions/docker/language-configuration.json index cab4f6602ff..08a483ad5ce 100644 --- a/extensions/docker/language-configuration.json +++ b/extensions/docker/language-configuration.json @@ -20,5 +20,9 @@ ["(", ")"], ["\"", "\""], ["'", "'"] - ] -} \ No newline at end of file + ], + "indentationRules": { + "increaseIndentPattern": "^\\s*.*(:|-) ?(&\\w+)?(\\{[^}\"']*|\\([^)\"']*)?$", + "decreaseIndentPattern": "^\\s+\\}$" + } +} From 69db64290797e02b48dc481e4be0928b5cf02d39 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 26 Feb 2024 07:06:57 -0800 Subject: [PATCH 0180/1175] Add warning to suggestEnabled via deprecation message I get the occasional report of people bricking their terminal because of this setting. I'd prefer not to remove it, the deprecation message gives the best of both worlds (still in code, loud warning when used). Fixes #206172 --- .../workbench/contrib/terminal/common/terminalConfiguration.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 0df9dc1171c..3b3cafe11ab 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -601,7 +601,8 @@ const terminalConfiguration: IConfigurationNode = { restricted: true, markdownDescription: localize('terminal.integrated.shellIntegration.suggestEnabled', "Enables experimental terminal Intellisense suggestions for supported shells when {0} is set to {1}. If shell integration is installed manually, {2} needs to be set to {3} before calling the script.", '`#terminal.integrated.shellIntegration.enabled#`', '`true`', '`VSCODE_SUGGEST`', '`1`'), type: 'boolean', - default: false + default: false, + markdownDeprecationMessage: localize('suggestEnabled.deprecated', 'This is an experimental setting and may break the terminal! Use at your own risk.') }, [TerminalSettingId.SmoothScrolling]: { markdownDescription: localize('terminal.integrated.smoothScrolling', "Controls whether the terminal will scroll using an animation."), From 5443fa0bd8d08629f69e3ed075cb358537867bb7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 26 Feb 2024 07:55:29 -0800 Subject: [PATCH 0181/1175] De-dupe links and add URI-based presentation Fixes #206260 Fixes #206124 --- .../links/browser/terminalLinkManager.ts | 2 +- .../links/browser/terminalLinkQuickpick.ts | 55 +++++++++++++++++-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts index 8d981fb62d2..5d8f803de51 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts @@ -441,6 +441,6 @@ export interface ILineColumnInfo { export interface IDetectedLinks { wordLinks?: ILink[]; webLinks?: ILink[]; - fileLinks?: ILink[]; + fileLinks?: (ILink | TerminalLink)[]; folderLinks?: ILink[]; } diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts index 0cea7b3f514..2c042949a9d 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts @@ -22,6 +22,8 @@ import { getLinkSuffix } from 'vs/workbench/contrib/terminalContrib/links/browse import { TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import type { IFilesConfiguration } from 'vs/workbench/contrib/files/common/files'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; export class TerminalLinkQuickpick extends DisposableStore { @@ -35,6 +37,7 @@ export class TerminalLinkQuickpick extends DisposableStore { @IConfigurationService private readonly _configurationService: IConfigurationService, @IEditorService private readonly _editorService: IEditorService, @IHistoryService private readonly _historyService: IHistoryService, + @ILabelService private readonly _labelService: ILabelService, @IQuickInputService private readonly _quickInputService: IQuickInputService, @IAccessibleViewService private readonly _accessibleViewService: IAccessibleViewService ) { @@ -148,17 +151,57 @@ export class TerminalLinkQuickpick extends DisposableStore { /** * @param ignoreLinks Links with labels to not include in the picks. */ - private async _generatePicks(links: ILink[], ignoreLinks?: ILink[]): Promise { + private async _generatePicks(links: (ILink | TerminalLink)[], ignoreLinks?: ILink[]): Promise { if (!links) { return; } - const linkKeys: Set = new Set(); + const linkTextKeys: Set = new Set(); + const linkUriKeys: Set = new Set(); const picks: ITerminalLinkQuickPickItem[] = []; for (const link of links) { - const label = link.text; - if (!linkKeys.has(label) && (!ignoreLinks || !ignoreLinks.some(e => e.text === label))) { - linkKeys.add(label); - picks.push({ label, link }); + let label = link.text; + if (!linkTextKeys.has(label) && (!ignoreLinks || !ignoreLinks.some(e => e.text === label))) { + linkTextKeys.add(label); + + // Add a consistently formatted resolved URI label to the description if applicable + let description: string | undefined; + if ('uri' in link && link.uri) { + // For local files and folders, mimic the presentation of go to file + if ( + link.type === TerminalBuiltinLinkType.LocalFile || + link.type === TerminalBuiltinLinkType.LocalFolderInWorkspace || + link.type === TerminalBuiltinLinkType.LocalFolderOutsideWorkspace + ) { + label = basenameOrAuthority(link.uri); + description = this._labelService.getUriLabel(dirname(link.uri), { relative: true }); + } + + // Add line and column numbers to the label if applicable + if (link.type === TerminalBuiltinLinkType.LocalFile) { + if (link.parsedLink?.suffix?.row !== undefined) { + label += `:${link.parsedLink.suffix.row}`; + if (link.parsedLink?.suffix?.rowEnd !== undefined) { + label += `-${link.parsedLink.suffix.rowEnd}`; + } + if (link.parsedLink?.suffix?.col !== undefined) { + label += `:${link.parsedLink.suffix.col}`; + if (link.parsedLink?.suffix?.colEnd !== undefined) { + label += `-${link.parsedLink.suffix.colEnd}`; + } + } + } + } + + // Skip the link if it's a duplicate URI + line/col + if (description) { + if (linkUriKeys.has(label + '|' + description)) { + continue; + } + linkUriKeys.add(label + '|' + description); + } + } + + picks.push({ label, link, description }); } } return picks.length > 0 ? picks : undefined; From a984153d6a98fba04f0a39a5ab9918c7b6e47511 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:02:42 +0100 Subject: [PATCH 0182/1175] Hover debt. Mainly import changes (#206247) * hover debt * hover import * less hover imports --- .../browser/ui/actionbar/actionViewItems.ts | 13 ++- src/vs/base/browser/ui/actionbar/actionbar.ts | 6 +- src/vs/base/browser/ui/button/button.ts | 10 +-- src/vs/base/browser/ui/dropdown/dropdown.ts | 8 +- .../ui/dropdown/dropdownActionViewItem.ts | 4 +- src/vs/base/browser/ui/findinput/findInput.ts | 4 +- .../browser/ui/findinput/findInputToggles.ts | 4 +- .../base/browser/ui/findinput/replaceInput.ts | 2 +- src/vs/base/browser/ui/hover/hoverDelegate.ts | 83 +++++++++++++------ .../browser/ui/hover/hoverDelegateFactory.ts | 35 ++++++++ .../ui/hover/{hover.css => hoverWidget.css} | 0 src/vs/base/browser/ui/hover/hoverWidget.ts | 2 +- .../updatableHoverWidget.ts} | 5 +- .../browser/ui/iconLabel/iconHoverDelegate.ts | 72 ---------------- src/vs/base/browser/ui/iconLabel/iconLabel.ts | 6 +- .../browser/ui/selectBox/selectBoxCustom.ts | 24 ++++-- src/vs/base/browser/ui/table/tableWidget.ts | 9 +- src/vs/base/browser/ui/toggle/toggle.ts | 6 +- src/vs/base/browser/ui/toolbar/toolbar.ts | 6 +- src/vs/base/browser/ui/tree/abstractTree.ts | 6 +- .../services/hoverService/hoverService.ts | 2 +- .../services/hoverService/hoverWidget.ts | 2 +- .../contrib/find/browser/findOptionsWidget.ts | 4 +- .../editor/contrib/find/browser/findWidget.ts | 10 +-- .../browser/standaloneCodeEditor.ts | 2 +- src/vs/platform/actions/browser/buttonbar.ts | 6 +- .../dropdownWithPrimaryActionViewItem.ts | 2 +- .../browser/menuEntryActionViewItem.ts | 2 +- src/vs/platform/hover/browser/hover.ts | 3 +- .../platform/quickinput/browser/quickInput.ts | 2 +- .../quickinput/browser/quickInputList.ts | 4 +- .../browser/parts/compositeBarActions.ts | 2 +- .../workbench/browser/parts/compositePart.ts | 8 +- .../parts/editor/breadcrumbsControl.ts | 4 +- .../browser/parts/editor/editorTabsControl.ts | 4 +- .../notifications/notificationsViewer.ts | 4 +- .../browser/parts/statusbar/statusbarItem.ts | 4 +- .../parts/titlebar/commandCenterControl.ts | 6 +- .../browser/parts/titlebar/titlebarPart.ts | 6 +- .../workbench/browser/parts/views/checkbox.ts | 4 +- .../workbench/browser/parts/views/treeView.ts | 4 +- .../workbench/browser/parts/views/viewPane.ts | 4 +- src/vs/workbench/browser/workbench.ts | 2 +- .../contrib/comments/browser/commentReply.ts | 4 +- .../comments/browser/commentsTreeViewer.ts | 4 +- .../contrib/comments/browser/timestamp.ts | 4 +- .../contrib/debug/browser/baseDebugView.ts | 4 +- .../contrib/debug/browser/breakpointsView.ts | 4 +- .../contrib/debug/browser/callStackView.ts | 8 +- .../debug/browser/debugActionViewItems.ts | 4 +- .../contrib/debug/browser/replViewer.ts | 4 +- .../abstractRuntimeExtensionsEditor.ts | 4 +- .../extensions/browser/extensionEditor.ts | 4 +- .../extensions/browser/extensionsWidgets.ts | 4 +- .../files/browser/views/explorerViewer.ts | 3 +- .../inlineChat/browser/inlineChatWidget.ts | 4 +- .../browser/view/cellParts/cellActionView.ts | 4 +- .../browser/view/cellParts/cellStatusPart.ts | 4 +- .../browser/view/cellParts/cellToolbars.ts | 6 +- .../settingsEditorSettingIndicators.ts | 2 +- .../contrib/remote/browser/tunnelView.ts | 4 +- .../search/browser/patternInputWidget.ts | 2 +- .../search/browser/searchResultsView.ts | 4 +- .../contrib/search/browser/searchView.ts | 4 +- .../contrib/search/browser/searchWidget.ts | 2 +- .../searchEditor/browser/searchEditor.ts | 4 +- .../contrib/terminal/browser/terminalView.ts | 2 +- .../testing/browser/testCoverageBars.ts | 4 +- .../testing/browser/testingExplorerView.ts | 4 +- .../contrib/timeline/browser/timelinePane.ts | 4 +- .../userDataProfileImportExportService.ts | 2 +- 71 files changed, 256 insertions(+), 247 deletions(-) create mode 100644 src/vs/base/browser/ui/hover/hoverDelegateFactory.ts rename src/vs/base/browser/ui/hover/{hover.css => hoverWidget.css} (100%) rename src/vs/base/browser/ui/{iconLabel/iconLabelHover.ts => hover/updatableHoverWidget.ts} (98%) delete mode 100644 src/vs/base/browser/ui/iconLabel/iconHoverDelegate.ts diff --git a/src/vs/base/browser/ui/actionbar/actionViewItems.ts b/src/vs/base/browser/ui/actionbar/actionViewItems.ts index fd7424b0f72..698d711f4dc 100644 --- a/src/vs/base/browser/ui/actionbar/actionViewItems.ts +++ b/src/vs/base/browser/ui/actionbar/actionViewItems.ts @@ -9,9 +9,9 @@ import { addDisposableListener, EventHelper, EventLike, EventType } from 'vs/bas import { EventType as TouchEventType, Gesture } from 'vs/base/browser/touch'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { ISelectBoxOptions, ISelectBoxStyles, ISelectOptionItem, SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import { IToggleStyles } from 'vs/base/browser/ui/toggle/toggle'; import { Action, ActionRunner, IAction, IActionChangeEvent, IActionRunner, Separator } from 'vs/base/common/actions'; @@ -230,11 +230,10 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem { /* While custom hover is not supported with context view */ this.element.title = title; } else { - if (!this.customHover) { + if (!this.customHover && title !== '') { const hoverDelegate = this.options.hoverDelegate ?? getDefaultHoverDelegate('element'); - this.customHover = setupCustomHover(hoverDelegate, this.element, title); - this._store.add(this.customHover); - } else { + this.customHover = this._store.add(setupCustomHover(hoverDelegate, this.element, title)); + } else if (this.customHover) { this.customHover.update(title); } } diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 5f5ad352842..05505b768a5 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -6,8 +6,8 @@ import * as DOM from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionViewItem, BaseActionViewItem, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { createInstantHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; import { ActionRunner, IAction, IActionRunner, IRunEvent, Separator } from 'vs/base/common/actions'; import { Emitter } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; @@ -118,7 +118,7 @@ export class ActionBar extends Disposable implements IActionRunner { keys: this.options.triggerKeys?.keys ?? [KeyCode.Enter, KeyCode.Space] }; - this._hoverDelegate = options.hoverDelegate ?? this._register(getDefaultHoverDelegate('element', true)); + this._hoverDelegate = options.hoverDelegate ?? this._register(createInstantHoverDelegate()); if (this.options.actionRunner) { this._actionRunner = this.options.actionRunner; diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index e7ca41dc8b3..1d2a3364d0b 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -9,9 +9,9 @@ import { sanitize } from 'vs/base/browser/dompurify/dompurify'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { renderMarkdown, renderStringAsPlaintext } from 'vs/base/browser/markdownRenderer'; import { Gesture, EventType as TouchEventType } from 'vs/base/browser/touch'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { Action, IAction, IActionRunner } from 'vs/base/common/actions'; import { Codicon } from 'vs/base/common/codicons'; @@ -303,9 +303,9 @@ export class Button extends Disposable implements IButton { } private setTitle(title: string) { - if (!this._hover) { + if (!this._hover && title !== '') { this._hover = this._register(setupCustomHover(this.options.hoverDelegate ?? getDefaultHoverDelegate('mouse'), this._element, title)); - } else { + } else if (this._hover) { this._hover.update(title); } } diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index 88dfaf2c5b1..dfc2329510f 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -8,8 +8,8 @@ import { $, addDisposableListener, append, EventHelper, EventType, isMouseEvent import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { EventType as GestureEventType, Gesture } from 'vs/base/browser/touch'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { IMenuOptions } from 'vs/base/browser/ui/menu/menu'; import { ActionRunner, IAction } from 'vs/base/common/actions'; import { Emitter } from 'vs/base/common/event'; @@ -105,9 +105,9 @@ class BaseDropdown extends ActionRunner { set tooltip(tooltip: string) { if (this._label) { - if (!this.hover) { + if (!this.hover && tooltip !== '') { this.hover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this._label, tooltip)); - } else { + } else if (this.hover) { this.hover.update(tooltip); } } diff --git a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts index 995b8f40817..75333985372 100644 --- a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -19,8 +19,8 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { ResolvedKeybinding } from 'vs/base/common/keybindings'; import { IDisposable } from 'vs/base/common/lifecycle'; import 'vs/css!./dropdown'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export interface IKeybindingProvider { (action: IAction): ResolvedKeybinding | undefined; diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index c119ad43779..9ecfcbce827 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -16,7 +16,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import 'vs/css!./findInput'; import * as nls from 'vs/nls'; import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { createInstantHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export interface IFindInputOptions { @@ -114,7 +114,7 @@ export class FindInput extends Widget { inputBoxStyles: options.inputBoxStyles, })); - const hoverDelegate = this._register(getDefaultHoverDelegate('element', true)); + const hoverDelegate = this._register(createInstantHoverDelegate()); if (this.showCommonFindToggles) { this.regex = this._register(new RegexToggle({ diff --git a/src/vs/base/browser/ui/findinput/findInputToggles.ts b/src/vs/base/browser/ui/findinput/findInputToggles.ts index 8b3ea12580f..adce009430b 100644 --- a/src/vs/base/browser/ui/findinput/findInputToggles.ts +++ b/src/vs/base/browser/ui/findinput/findInputToggles.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { Codicon } from 'vs/base/common/codicons'; import * as nls from 'vs/nls'; diff --git a/src/vs/base/browser/ui/findinput/replaceInput.ts b/src/vs/base/browser/ui/findinput/replaceInput.ts index 8e20025d757..4dfdf549a3b 100644 --- a/src/vs/base/browser/ui/findinput/replaceInput.ts +++ b/src/vs/base/browser/ui/findinput/replaceInput.ts @@ -16,7 +16,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import 'vs/css!./findInput'; import * as nls from 'vs/nls'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export interface IReplaceInputOptions { diff --git a/src/vs/base/browser/ui/hover/hoverDelegate.ts b/src/vs/base/browser/ui/hover/hoverDelegate.ts index 6d2dfef371a..57f6962ac0e 100644 --- a/src/vs/base/browser/ui/hover/hoverDelegate.ts +++ b/src/vs/base/browser/ui/hover/hoverDelegate.ts @@ -3,33 +3,66 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IHoverDelegate, IScopedHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { Lazy } from 'vs/base/common/lazy'; +import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; +import { IHoverWidget, IUpdatableHoverOptions } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { IDisposable } from 'vs/base/common/lifecycle'; -const nullHoverDelegateFactory = () => ({ - get delay(): number { return -1; }, - dispose: () => { }, - showHover: () => { return undefined; }, -}); - -let hoverDelegateFactory: (placement: 'mouse' | 'element', enableInstantHover: boolean) => IScopedHoverDelegate = nullHoverDelegateFactory; -const defaultHoverDelegateMouse = new Lazy(() => hoverDelegateFactory('mouse', false)); -const defaultHoverDelegateElement = new Lazy(() => hoverDelegateFactory('element', false)); - -export function setHoverDelegateFactory(hoverDelegateProvider: ((placement: 'mouse' | 'element', enableInstantHover: boolean) => IScopedHoverDelegate)): void { - hoverDelegateFactory = hoverDelegateProvider; +export interface IHoverDelegateTarget extends IDisposable { + readonly targetElements: readonly HTMLElement[]; + x?: number; } -export function getDefaultHoverDelegate(placement: 'mouse' | 'element'): IHoverDelegate; -export function getDefaultHoverDelegate(placement: 'element', enableInstantHover: true): IScopedHoverDelegate; -export function getDefaultHoverDelegate(placement: 'mouse' | 'element', enableInstantHover?: boolean): IHoverDelegate | IScopedHoverDelegate { - if (enableInstantHover) { - // If instant hover is enabled, the consumer is responsible for disposing the hover delegate - return hoverDelegateFactory(placement, true); - } +export interface IHoverDelegateOptions extends IUpdatableHoverOptions { + /** + * The content to display in the primary section of the hover. The type of text determines the + * default `hideOnHover` behavior. + */ + content: IMarkdownString | string | HTMLElement; + /** + * The target for the hover. This determines the position of the hover and it will only be + * hidden when the mouse leaves both the hover and the target. A HTMLElement can be used for + * simple cases and a IHoverDelegateTarget for more complex cases where multiple elements and/or a + * dispose method is required. + */ + target: IHoverDelegateTarget | HTMLElement; + /** + * The container to pass to {@link IContextViewProvider.showContextView} which renders the hover + * in. This is particularly useful for more natural tab focusing behavior, where the hover is + * created as the next tab index after the element being hovered and/or to workaround the + * element's container hiding on `focusout`. + */ + container?: HTMLElement; + /** + * Options that defines where the hover is positioned. + */ + position?: { + /** + * Position of the hover. The default is to show above the target. This option will be ignored + * if there is not enough room to layout the hover in the specified position, unless the + * forcePosition option is set. + */ + hoverPosition?: HoverPosition; + }; + appearance?: { + /** + * Whether to show the hover pointer + */ + showPointer?: boolean; + /** + * Whether to skip the fade in animation, this should be used when hovering from one hover to + * another in the same group so it looks like the hover is moving from one element to the other. + */ + skipFadeInAnimation?: boolean; + }; +} - if (placement === 'element') { - return defaultHoverDelegateElement.value; - } - return defaultHoverDelegateMouse.value; +export interface IHoverDelegate { + showHover(options: IHoverDelegateOptions, focus?: boolean): IHoverWidget | undefined; + onDidHideHover?: () => void; + delay: number; + placement?: 'mouse' | 'element'; + showNativeHover?: boolean; // TODO@benibenj remove this, only temp fix for contextviews } + +export interface IScopedHoverDelegate extends IHoverDelegate, IDisposable { } diff --git a/src/vs/base/browser/ui/hover/hoverDelegateFactory.ts b/src/vs/base/browser/ui/hover/hoverDelegateFactory.ts new file mode 100644 index 00000000000..44628261333 --- /dev/null +++ b/src/vs/base/browser/ui/hover/hoverDelegateFactory.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IHoverDelegate, IScopedHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { Lazy } from 'vs/base/common/lazy'; + +const nullHoverDelegateFactory = () => ({ + get delay(): number { return -1; }, + dispose: () => { }, + showHover: () => { return undefined; }, +}); + +let hoverDelegateFactory: (placement: 'mouse' | 'element', enableInstantHover: boolean) => IScopedHoverDelegate = nullHoverDelegateFactory; +const defaultHoverDelegateMouse = new Lazy(() => hoverDelegateFactory('mouse', false)); +const defaultHoverDelegateElement = new Lazy(() => hoverDelegateFactory('element', false)); + +export function setHoverDelegateFactory(hoverDelegateProvider: ((placement: 'mouse' | 'element', enableInstantHover: boolean) => IScopedHoverDelegate)): void { + hoverDelegateFactory = hoverDelegateProvider; +} + +export function getDefaultHoverDelegate(placement: 'mouse' | 'element'): IHoverDelegate { + if (placement === 'element') { + return defaultHoverDelegateElement.value; + } + return defaultHoverDelegateMouse.value; +} + +export function createInstantHoverDelegate(): IScopedHoverDelegate { + // Creates a hover delegate with instant hover enabled. + // This hover belongs to the consumer and requires the them to dispose it. + // Instant hover only makes sense for 'element' placement. + return hoverDelegateFactory('element', true); +} diff --git a/src/vs/base/browser/ui/hover/hover.css b/src/vs/base/browser/ui/hover/hoverWidget.css similarity index 100% rename from src/vs/base/browser/ui/hover/hover.css rename to src/vs/base/browser/ui/hover/hoverWidget.css diff --git a/src/vs/base/browser/ui/hover/hoverWidget.ts b/src/vs/base/browser/ui/hover/hoverWidget.ts index dc0af66ff5a..bff397303be 100644 --- a/src/vs/base/browser/ui/hover/hoverWidget.ts +++ b/src/vs/base/browser/ui/hover/hoverWidget.ts @@ -8,7 +8,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; -import 'vs/css!./hover'; +import 'vs/css!./hoverWidget'; import { localize } from 'vs/nls'; const $ = dom.$; diff --git a/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts b/src/vs/base/browser/ui/hover/updatableHoverWidget.ts similarity index 98% rename from src/vs/base/browser/ui/iconLabel/iconLabelHover.ts rename to src/vs/base/browser/ui/hover/updatableHoverWidget.ts index bdcdfa7c7da..c3c505cef39 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabelHover.ts +++ b/src/vs/base/browser/ui/hover/updatableHoverWidget.ts @@ -5,7 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; -import { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget, IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget } from 'vs/base/browser/ui/hover/hoverDelegate'; import { TimeoutTimer } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IMarkdownString, isMarkdownString } from 'vs/base/common/htmlContent'; @@ -68,6 +68,9 @@ export interface ICustomHover extends IDisposable { update(tooltip: IHoverContent, options?: IUpdatableHoverOptions): void; } +export interface IHoverWidget extends IDisposable { + readonly isDisposed: boolean; +} class UpdatableHoverWidget implements IDisposable { diff --git a/src/vs/base/browser/ui/iconLabel/iconHoverDelegate.ts b/src/vs/base/browser/ui/iconLabel/iconHoverDelegate.ts deleted file mode 100644 index f0209856dc3..00000000000 --- a/src/vs/base/browser/ui/iconLabel/iconHoverDelegate.ts +++ /dev/null @@ -1,72 +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 { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; -import { IUpdatableHoverOptions } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { IMarkdownString } from 'vs/base/common/htmlContent'; -import { IDisposable } from 'vs/base/common/lifecycle'; - -export interface IHoverDelegateTarget extends IDisposable { - readonly targetElements: readonly HTMLElement[]; - x?: number; -} - -export interface IHoverDelegateOptions extends IUpdatableHoverOptions { - /** - * The content to display in the primary section of the hover. The type of text determines the - * default `hideOnHover` behavior. - */ - content: IMarkdownString | string | HTMLElement; - /** - * The target for the hover. This determines the position of the hover and it will only be - * hidden when the mouse leaves both the hover and the target. A HTMLElement can be used for - * simple cases and a IHoverDelegateTarget for more complex cases where multiple elements and/or a - * dispose method is required. - */ - target: IHoverDelegateTarget | HTMLElement; - /** - * The container to pass to {@link IContextViewProvider.showContextView} which renders the hover - * in. This is particularly useful for more natural tab focusing behavior, where the hover is - * created as the next tab index after the element being hovered and/or to workaround the - * element's container hiding on `focusout`. - */ - container?: HTMLElement; - /** - * Options that defines where the hover is positioned. - */ - position?: { - /** - * Position of the hover. The default is to show above the target. This option will be ignored - * if there is not enough room to layout the hover in the specified position, unless the - * forcePosition option is set. - */ - hoverPosition?: HoverPosition; - }; - appearance?: { - /** - * Whether to show the hover pointer - */ - showPointer?: boolean; - /** - * Whether to skip the fade in animation, this should be used when hovering from one hover to - * another in the same group so it looks like the hover is moving from one element to the other. - */ - skipFadeInAnimation?: boolean; - }; -} - -export interface IHoverDelegate { - showHover(options: IHoverDelegateOptions, focus?: boolean): IHoverWidget | undefined; - onDidHideHover?: () => void; - delay: number; - placement?: 'mouse' | 'element'; - showNativeHover?: boolean; // TODO@benibenj remove this, only temp fix for contextviews -} - -export interface IScopedHoverDelegate extends IHoverDelegate, IDisposable { } - -export interface IHoverWidget extends IDisposable { - readonly isDisposed: boolean; -} diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index c9d62a9bc4e..5bf35b8ffc2 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -6,13 +6,13 @@ import 'vs/css!./iconlabel'; import * as dom from 'vs/base/browser/dom'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { ITooltipMarkdownString, setupCustomHover, setupNativeHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { ITooltipMarkdownString, setupCustomHover, setupNativeHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { IMatch } from 'vs/base/common/filters'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { equals } from 'vs/base/common/objects'; import { Range } from 'vs/base/common/range'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export interface IIconLabelCreationOptions { readonly supportHighlights?: boolean; diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index ca6aaa33505..c813532f496 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -9,8 +9,8 @@ import { IContentActionHandler } from 'vs/base/browser/formattedTextRenderer'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; import { AnchorPosition, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { IListEvent, IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { ISelectBoxDelegate, ISelectBoxOptions, ISelectBoxStyles, ISelectData, ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; @@ -103,7 +103,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi private selectionDetailsPane!: HTMLElement; private _skipLayout: boolean = false; private _cachedMaxDetailsHeight?: number; - private _hover: ICustomHover; + private _hover?: ICustomHover; private _sticky: boolean = false; // for dev purposes only @@ -134,8 +134,6 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.selectElement.setAttribute('aria-description', this.selectBoxOptions.ariaDescription); } - this._hover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.selectElement, '')); - this._onDidSelect = new Emitter(); this._register(this._onDidSelect); @@ -152,6 +150,14 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } + private setTitle(title: string): void { + if (!this._hover && title) { + this._hover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.selectElement, title)); + } else if (this._hover) { + this._hover.update(title); + } + } + // IDelegate - List renderer getHeight(): number { @@ -204,7 +210,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi selected: e.target.value }); if (!!this.options[this.selected] && !!this.options[this.selected].text) { - this._hover.update(this.options[this.selected].text); + this.setTitle(this.options[this.selected].text); } })); @@ -314,7 +320,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.selectElement.selectedIndex = this.selected; if (!!this.options[this.selected] && !!this.options[this.selected].text) { - this._hover.update(this.options[this.selected].text); + this.setTitle(this.options[this.selected].text); } } @@ -842,7 +848,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi }); if (!!this.options[this.selected] && !!this.options[this.selected].text) { - this._hover.update(this.options[this.selected].text); + this.setTitle(this.options[this.selected].text); } } @@ -941,7 +947,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi selected: this.options[this.selected].text }); if (!!this.options[this.selected] && !!this.options[this.selected].text) { - this._hover.update(this.options[this.selected].text); + this.setTitle(this.options[this.selected].text); } } diff --git a/src/vs/base/browser/ui/table/tableWidget.ts b/src/vs/base/browser/ui/table/tableWidget.ts index 536fb25608e..38192d39413 100644 --- a/src/vs/base/browser/ui/table/tableWidget.ts +++ b/src/vs/base/browser/ui/table/tableWidget.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { $, append, clearNode, createStyleSheet, getContentHeight, getContentWidth } from 'vs/base/browser/dom'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IListOptions, IListOptionsUpdate, IListStyles, List, unthemedListStyles } from 'vs/base/browser/ui/list/listWidget'; import { ISplitViewDescriptor, IView, Orientation, SplitView } from 'vs/base/browser/ui/splitview/splitview'; @@ -132,7 +132,10 @@ class ColumnHeader extends Disposable implements IView { super(); this.element = $('.monaco-table-th', { 'data-col-index': index }, column.label); - this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.element, column.tooltip)); + + if (column.tooltip) { + this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.element, column.tooltip)); + } } layout(size: number): void { diff --git a/src/vs/base/browser/ui/toggle/toggle.ts b/src/vs/base/browser/ui/toggle/toggle.ts index 540bb17db2f..53e0e2b9a30 100644 --- a/src/vs/base/browser/ui/toggle/toggle.ts +++ b/src/vs/base/browser/ui/toggle/toggle.ts @@ -13,9 +13,9 @@ import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import 'vs/css!./toggle'; import { isActiveElement, $, addDisposableListener, EventType } from 'vs/base/browser/dom'; -import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export interface IToggleOpts extends IToggleStyles { readonly actionClassName?: string; diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index f92369cddfb..ebb6bc264d1 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -15,8 +15,8 @@ import { ResolvedKeybinding } from 'vs/base/common/keybindings'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import 'vs/css!./toolbar'; import * as nls from 'vs/nls'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { createInstantHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; @@ -60,7 +60,7 @@ export class ToolBar extends Disposable { constructor(container: HTMLElement, contextMenuProvider: IContextMenuProvider, options: IToolBarOptions = { orientation: ActionsOrientation.HORIZONTAL }) { super(); - options.hoverDelegate = options.hoverDelegate ?? this._register(getDefaultHoverDelegate('element', true)); + options.hoverDelegate = options.hoverDelegate ?? this._register(createInstantHoverDelegate()); this.options = options; this.lookupKeybindings = typeof this.options.getKeyBinding === 'function'; diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index c42cd0ba336..87e8f52fbc6 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -33,8 +33,8 @@ import { ISpliceable } from 'vs/base/common/sequence'; import { isNumber } from 'vs/base/common/types'; import 'vs/css!./media/tree'; import { localize } from 'vs/nls'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { createInstantHoverDelegate, getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; class TreeElementsDragAndDropData extends ElementsDragAndDropData { @@ -807,7 +807,7 @@ class FindWidget extends Disposable { this.elements.root.style.boxShadow = `0 0 8px 2px ${styles.listFilterWidgetShadow}`; } - const toggleHoverDelegate = this._register(getDefaultHoverDelegate('element', true)); + const toggleHoverDelegate = this._register(createInstantHoverDelegate()); this.modeToggle = this._register(new ModeToggle({ ...styles.toggleStyles, isChecked: mode === TreeFindMode.Filter, hoverDelegate: toggleHoverDelegate })); this.matchTypeToggle = this._register(new FuzzyToggle({ ...styles.toggleStyles, isChecked: matchType === TreeFindMatchType.Fuzzy, hoverDelegate: toggleHoverDelegate })); this.onDidChangeMode = Event.map(this.modeToggle.onChange, () => this.modeToggle.checked ? TreeFindMode.Filter : TreeFindMode.Highlight, this._store); diff --git a/src/vs/editor/browser/services/hoverService/hoverService.ts b/src/vs/editor/browser/services/hoverService/hoverService.ts index 47fddf11c86..f3d6b5c9f16 100644 --- a/src/vs/editor/browser/services/hoverService/hoverService.ts +++ b/src/vs/editor/browser/services/hoverService/hoverService.ts @@ -19,7 +19,7 @@ import { ResultKind } from 'vs/platform/keybinding/common/keybindingResolver'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { mainWindow } from 'vs/base/browser/window'; -import { IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverWidget } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { ContextViewHandler } from 'vs/platform/contextview/browser/contextViewService'; export class HoverService extends Disposable implements IHoverService { diff --git a/src/vs/editor/browser/services/hoverService/hoverWidget.ts b/src/vs/editor/browser/services/hoverService/hoverWidget.ts index 8b4814a92c9..4ec59ed09bd 100644 --- a/src/vs/editor/browser/services/hoverService/hoverWidget.ts +++ b/src/vs/editor/browser/services/hoverService/hoverWidget.ts @@ -23,7 +23,7 @@ import { localize } from 'vs/nls'; import { isMacintosh } from 'vs/base/common/platform'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { status } from 'vs/base/browser/ui/aria/aria'; -import { IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverWidget } from 'vs/base/browser/ui/hover/updatableHoverWidget'; const $ = dom.$; type TargetRect = { diff --git a/src/vs/editor/contrib/find/browser/findOptionsWidget.ts b/src/vs/editor/contrib/find/browser/findOptionsWidget.ts index 7b693a1f28c..007723f6986 100644 --- a/src/vs/editor/contrib/find/browser/findOptionsWidget.ts +++ b/src/vs/editor/contrib/find/browser/findOptionsWidget.ts @@ -13,7 +13,7 @@ import { FIND_IDS } from 'vs/editor/contrib/find/browser/findModel'; import { FindReplaceState } from 'vs/editor/contrib/find/browser/findState'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { asCssVariable, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { createInstantHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export class FindOptionsWidget extends Widget implements IOverlayWidget { @@ -53,7 +53,7 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget { inputActiveOptionBackground: asCssVariable(inputActiveOptionBackground), }; - const hoverDelegate = this._register(getDefaultHoverDelegate('element', true)); + const hoverDelegate = this._register(createInstantHoverDelegate()); this.caseSensitive = this._register(new CaseSensitiveToggle({ appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleCaseSensitiveCommand), diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts index 76189e64741..424375e8cae 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.ts +++ b/src/vs/editor/contrib/find/browser/findWidget.ts @@ -43,9 +43,9 @@ import { isHighContrast } from 'vs/platform/theme/common/theme'; import { assertIsDefined } from 'vs/base/common/types'; import { defaultInputBoxStyles, defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; import { Selection } from 'vs/editor/common/core/selection'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { createInstantHoverDelegate, getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; const findSelectionIcon = registerIcon('find-selection', Codicon.selection, nls.localize('findSelectionIcon', 'Icon for \'Find in Selection\' in the editor find widget.')); const findCollapsedIcon = registerIcon('find-collapsed', Codicon.chevronRight, nls.localize('findCollapsedIcon', 'Icon to indicate that the editor find widget is collapsed.')); @@ -1014,7 +1014,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL this._updateMatchesCount(); // Create a scoped hover delegate for all find related buttons - const hoverDelegate = getDefaultHoverDelegate('element', true); + const hoverDelegate = this._register(createInstantHoverDelegate()); // Previous button this._prevBtn = this._register(new SimpleButton({ @@ -1148,7 +1148,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL })); // Create scoped hover delegate for replace actions - const replaceHoverDelegate = this._register(getDefaultHoverDelegate('element', true)); + const replaceHoverDelegate = this._register(createInstantHoverDelegate()); // Replace one button this._replaceBtn = this._register(new SimpleButton({ diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 3e903d5bab9..05305694e57 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -39,7 +39,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { IAccessibilitySignalService } from 'vs/platform/accessibilitySignal/browser/accessibilitySignalService'; import { mainWindow } from 'vs/base/browser/window'; -import { setHoverDelegateFactory } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setHoverDelegateFactory } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; import { WorkbenchHoverDelegate } from 'vs/platform/hover/browser/hover'; /** diff --git a/src/vs/platform/actions/browser/buttonbar.ts b/src/vs/platform/actions/browser/buttonbar.ts index 94720d5b557..79cbe6faa37 100644 --- a/src/vs/platform/actions/browser/buttonbar.ts +++ b/src/vs/platform/actions/browser/buttonbar.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { ButtonBar, IButton } from 'vs/base/browser/ui/button/button'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { createInstantHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { ActionRunner, IAction, IActionRunner, SubmenuAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -73,7 +73,7 @@ export class WorkbenchButtonBar extends ButtonBar { this.clear(); // Support instamt hover between buttons - const hoverDelegate = this._updateStore.add(getDefaultHoverDelegate('element', true)); + const hoverDelegate = this._updateStore.add(createInstantHoverDelegate()); for (let i = 0; i < actions.length; i++) { diff --git a/src/vs/platform/actions/browser/dropdownWithPrimaryActionViewItem.ts b/src/vs/platform/actions/browser/dropdownWithPrimaryActionViewItem.ts index cfcc0e2c73b..97aac13854a 100644 --- a/src/vs/platform/actions/browser/dropdownWithPrimaryActionViewItem.ts +++ b/src/vs/platform/actions/browser/dropdownWithPrimaryActionViewItem.ts @@ -19,7 +19,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export interface IDropdownWithPrimaryActionViewItemOptions { actionRunner?: IActionRunner; diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index c1edd287bea..da596a8fc6c 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -26,7 +26,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ThemeIcon } from 'vs/base/common/themables'; import { isDark } from 'vs/platform/theme/common/theme'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; import { assertType } from 'vs/base/common/types'; import { asCssVariable, selectBorder } from 'vs/platform/theme/common/colorRegistry'; import { defaultSelectBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; diff --git a/src/vs/platform/hover/browser/hover.ts b/src/vs/platform/hover/browser/hover.ts index 0a593c04ec8..3a44ead957b 100644 --- a/src/vs/platform/hover/browser/hover.ts +++ b/src/vs/platform/hover/browser/hover.ts @@ -7,10 +7,11 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; -import { IHoverDelegate, IHoverDelegateOptions, IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/hover/hoverDelegate'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { addStandardDisposableListener } from 'vs/base/browser/dom'; import { KeyCode } from 'vs/base/common/keyCodes'; +import { IHoverWidget } from 'vs/base/browser/ui/hover/updatableHoverWidget'; export const IHoverService = createDecorator('hoverService'); diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index 96dfeee7ea8..73c3a23a305 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -8,7 +8,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Button, IButtonStyles } from 'vs/base/browser/ui/button/button'; import { CountBadge, ICountBadgeStyles } from 'vs/base/browser/ui/countBadge/countBadge'; -import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/hover/hoverDelegate'; import { IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox'; import { IKeybindingLabelStyles } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; diff --git a/src/vs/platform/quickinput/browser/quickInputList.ts b/src/vs/platform/quickinput/browser/quickInputList.ts index de219a800bb..26e32890647 100644 --- a/src/vs/platform/quickinput/browser/quickInputList.ts +++ b/src/vs/platform/quickinput/browser/quickInputList.ts @@ -8,7 +8,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { AriaRole } from 'vs/base/browser/ui/aria/aria'; import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; -import { IHoverDelegate, IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; @@ -35,7 +35,7 @@ import { Lazy } from 'vs/base/common/lazy'; import { URI } from 'vs/base/common/uri'; import { isDark } from 'vs/platform/theme/common/theme'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ITooltipMarkdownString } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { IHoverWidget, ITooltipMarkdownString } from 'vs/base/browser/ui/hover/updatableHoverWidget'; const $ = dom.$; diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index cfa2d348aa0..6ae11c51361 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -26,7 +26,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; import { URI } from 'vs/base/common/uri'; import { badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; -import { IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverWidget } from 'vs/base/browser/ui/hover/updatableHoverWidget'; export interface ICompositeBar { diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 81a034ffa91..f4a0bb4fe28 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -33,9 +33,9 @@ import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { defaultProgressBarStyles } from 'vs/platform/theme/browser/defaultStyles'; import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash'; import { IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { createInstantHoverDelegate, getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; export interface ICompositeTitleLabel { @@ -97,7 +97,7 @@ export abstract class CompositePart extends Part { super(id, options, themeService, storageService, layoutService); this.lastActiveCompositeId = storageService.get(activeCompositeSettingsKey, StorageScope.WORKSPACE, this.defaultCompositeId); - this.toolbarHoverDelegate = this._register(getDefaultHoverDelegate('element', true)); + this.toolbarHoverDelegate = this._register(createInstantHoverDelegate()); } protected openComposite(id: string, focus?: boolean): Composite | undefined { diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index bf46167248f..00e7153c46f 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -40,8 +40,8 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { Codicon } from 'vs/base/common/codicons'; import { defaultBreadcrumbsWidgetStyles } from 'vs/platform/theme/browser/defaultStyles'; import { Emitter } from 'vs/base/common/event'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; class OutlineItem extends BreadcrumbsItem { diff --git a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts index 218c8808205..d7a82ed5c16 100644 --- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts @@ -44,8 +44,8 @@ import { IAuxiliaryEditorPart, MergeGroupMode } from 'vs/workbench/services/edit import { isMacintosh } from 'vs/base/common/platform'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; import { IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; export class EditorCommandsContextActionRunner extends ActionRunner { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index e66dc53ad61..246532ddd02 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -29,8 +29,8 @@ import { Event } from 'vs/base/common/event'; import { defaultButtonStyles, defaultProgressBarStyles } from 'vs/platform/theme/browser/defaultStyles'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export class NotificationsListDelegate implements IListVirtualDelegate { diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts b/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts index e18622523c5..13b5fde792b 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts @@ -21,9 +21,9 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { renderIcon, renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { spinningLoading, syncing } from 'vs/platform/theme/common/iconRegistry'; -import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { isMarkdownString, markdownStringEqual } from 'vs/base/common/htmlContent'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; import { Gesture, EventType as TouchEventType } from 'vs/base/browser/touch'; export class StatusbarEntryItem extends Disposable { diff --git a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts index 72eeea0b590..54eded7aab3 100644 --- a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts @@ -5,9 +5,9 @@ import { isActiveDocument, reset } from 'vs/base/browser/dom'; import { BaseActionViewItem, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { IAction, SubmenuAction } from 'vs/base/common/actions'; import { Codicon } from 'vs/base/common/codicons'; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 176ac265172..548f4d36caf 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -52,9 +52,9 @@ import { IEditorCommandsContext, IEditorPartOptionsChangeEvent, IToolbarActions import { mainWindow } from 'vs/base/browser/window'; import { ACCOUNTS_ACTIVITY_TILE_ACTION, GLOBAL_ACTIVITY_TITLE_ACTION } from 'vs/workbench/browser/parts/titlebar/titlebarActions'; import { IView } from 'vs/base/browser/ui/grid/grid'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { createInstantHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; import { IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export interface ITitleVariable { readonly name: string; @@ -282,7 +282,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { this.windowTitle = this._register(instantiationService.createInstance(WindowTitle, targetWindow, editorGroupsContainer)); - this.hoverDelegate = this._register(getDefaultHoverDelegate('element', true)); + this.hoverDelegate = this._register(createInstantHoverDelegate()); this.registerListeners(getWindowId(targetWindow)); } diff --git a/src/vs/workbench/browser/parts/views/checkbox.ts b/src/vs/workbench/browser/parts/views/checkbox.ts index 849966bbe33..d79f2ac7930 100644 --- a/src/vs/workbench/browser/parts/views/checkbox.ts +++ b/src/vs/workbench/browser/parts/views/checkbox.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 3b40a248a1d..2af2a8be2e5 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -8,8 +8,8 @@ import * as DOM from 'vs/base/browser/dom'; import { renderMarkdownAsPlaintext } from 'vs/base/browser/markdownRenderer'; import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; -import { ITooltipMarkdownString } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { ITooltipMarkdownString } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ElementsDragAndDropData, ListViewTargetSector } from 'vs/base/browser/ui/list/listView'; import { IAsyncDataSource, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeDragOverReaction, ITreeNode, ITreeRenderer, TreeDragOverBubble } from 'vs/base/browser/ui/tree/tree'; diff --git a/src/vs/workbench/browser/parts/views/viewPane.ts b/src/vs/workbench/browser/parts/views/viewPane.ts index 9cc1b35c354..06b591f477a 100644 --- a/src/vs/workbench/browser/parts/views/viewPane.ts +++ b/src/vs/workbench/browser/parts/views/viewPane.ts @@ -47,8 +47,8 @@ import { FilterWidget, IFilterWidgetOptions } from 'vs/workbench/browser/parts/v import { BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { defaultButtonStyles, defaultProgressBarStyles } from 'vs/platform/theme/browser/defaultStyles'; -import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export enum ViewPaneShowActions { /** Show the actions when the view is hovered. This is the default behavior. */ diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 8a311d4bb0e..0315e95a2d4 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -44,7 +44,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { mainWindow } from 'vs/base/browser/window'; import { PixelRatio } from 'vs/base/browser/pixelRatio'; import { WorkbenchHoverDelegate } from 'vs/platform/hover/browser/hover'; -import { setHoverDelegateFactory } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setHoverDelegateFactory } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export interface IWorkbenchOptions { diff --git a/src/vs/workbench/contrib/comments/browser/commentReply.ts b/src/vs/workbench/contrib/comments/browser/commentReply.ts index 4d2a9e4b887..55ea0d4e8f9 100644 --- a/src/vs/workbench/contrib/comments/browser/commentReply.ts +++ b/src/vs/workbench/contrib/comments/browser/commentReply.ts @@ -30,8 +30,8 @@ import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/comme import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { LayoutableEditor, MIN_EDITOR_HEIGHT, SimpleCommentEditor, calculateEditorHeight } from './simpleCommentEditor'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; const COMMENT_SCHEME = 'comment'; let INMEM_MODEL_ID = 0; diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index 585d253442c..451bc210789 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -32,8 +32,8 @@ import { IStyleOverride } from 'vs/platform/theme/browser/defaultStyles'; import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { ILocalizedString } from 'vs/platform/action/common/action'; import { CommentsModel } from 'vs/workbench/contrib/comments/browser/commentsModel'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export const COMMENTS_VIEW_ID = 'workbench.panel.comments'; export const COMMENTS_VIEW_STORAGE_ID = 'Comments'; diff --git a/src/vs/workbench/contrib/comments/browser/timestamp.ts b/src/vs/workbench/contrib/comments/browser/timestamp.ts index 2d1fcf15b48..16b3969d2c3 100644 --- a/src/vs/workbench/contrib/comments/browser/timestamp.ts +++ b/src/vs/workbench/contrib/comments/browser/timestamp.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { fromNow } from 'vs/base/common/date'; import { Disposable } from 'vs/base/common/lifecycle'; import { language } from 'vs/base/common/platform'; diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index 90aba19a7f2..5a365a2cb9f 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -7,8 +7,8 @@ import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { IInputValidationOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { IAsyncDataSource, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { Codicon } from 'vs/base/common/codicons'; diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 5de52259df4..13b72018035 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -8,9 +8,9 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Gesture } from 'vs/base/browser/touch'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { AriaRole } from 'vs/base/browser/ui/aria/aria'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { IListContextMenuEvent, IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 43b69137af3..139928ec9fe 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -48,8 +48,8 @@ import { createDisconnectMenuItemAction } from 'vs/workbench/contrib/debug/brows import { CALLSTACK_VIEW_ID, CONTEXT_CALLSTACK_ITEM_STOPPED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_CALLSTACK_SESSION_HAS_ONE_THREAD, CONTEXT_CALLSTACK_SESSION_IS_ATTACH, CONTEXT_DEBUG_STATE, CONTEXT_FOCUSED_SESSION_IS_NO_DEBUG, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, getStateLabel, IDebugModel, IDebugService, IDebugSession, IRawStoppedDetails, IStackFrame, IThread, State } from 'vs/workbench/contrib/debug/common/debug'; import { StackFrame, Thread, ThreadAndSessionIds } from 'vs/workbench/contrib/debug/common/debugModel'; import { isSessionAttach } from 'vs/workbench/contrib/debug/common/debugUtils'; -import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; const $ = dom.$; @@ -556,9 +556,9 @@ class SessionsRenderer implements ICompressibleTreeRenderer { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 084e20c5898..04d8fe1ff4f 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -66,7 +66,7 @@ import { ExpansionState, HunkData, HunkInformation, Session } from 'vs/workbench import { asRange, invertLineRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; import { ACTION_ACCEPT_CHANGES, ACTION_REGENERATE_RESPONSE, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_END, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_INNER_CURSOR_START, CTX_INLINE_CHAT_MESSAGE_CROP_STATE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_VISIBLE, IInlineChatFollowup, IInlineChatSlashCommand, MENU_INLINE_CHAT_INPUT, MENU_INLINE_CHAT_WIDGET, MENU_INLINE_CHAT_WIDGET_FEEDBACK, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, MENU_INLINE_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { createInstantHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; const defaultAriaLabel = localize('aria-label', "Inline Chat Input"); @@ -375,7 +375,7 @@ export class InlineChatWidget { this._store.add(this._slashCommandContentWidget); // Share hover delegates between toolbars to support instant hover between both - const hoverDelegate = this._store.add(getDefaultHoverDelegate('element', true)); + const hoverDelegate = this._store.add(createInstantHoverDelegate()); // toolbars diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts index 35007bedeb8..bcfd1900226 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts @@ -13,8 +13,8 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ThemeIcon } from 'vs/base/common/themables'; -import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export class CodiconActionViewItem extends MenuEntryActionViewItem { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellStatusPart.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellStatusPart.ts index 52e87ad8086..b5c306d0b41 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellStatusPart.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellStatusPart.ts @@ -27,8 +27,8 @@ import { CellContentPart } from 'vs/workbench/contrib/notebook/browser/view/cell import { ClickTargetType, IClickTarget } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { CellStatusbarAlignment, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { ITooltipMarkdownString, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { ITooltipMarkdownString, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/hover/hoverDelegate'; import { IHoverService } from 'vs/platform/hover/browser/hover'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index f10530616f1..250cb9824ec 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -22,8 +22,8 @@ import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/vie import { CellOverlayPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; import { registerCellToolbarStickyScroll } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll'; import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { createInstantHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; export class BetweenCellToolbar extends CellOverlayPart { private _betweenCellToolbar: ToolBar | undefined; @@ -167,7 +167,7 @@ export class CellTitleToolbarPart extends CellOverlayPart { if (this._view) { return this._view; } - const hoverDelegate = this._register(getDefaultHoverDelegate('element', true)); + const hoverDelegate = this._register(createInstantHoverDelegate()); const toolbar = this._register(this.instantiationService.createInstance(WorkbenchToolBar, this.toolbarContainer, { actionViewItemProvider: (action, options) => { return createActionViewItem(this.instantiationService, action, options); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts index 2f32b9c5932..cf98a39a26e 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts @@ -22,7 +22,7 @@ import { SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/bro import { POLICY_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IHoverOptions, IHoverService } from 'vs/platform/hover/browser/hover'; -import { IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverWidget } from 'vs/base/browser/ui/hover/updatableHoverWidget'; const $ = DOM.$; diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 35ba3ee8c5d..49a702fbbfe 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -51,12 +51,12 @@ import { WorkbenchTable } from 'vs/platform/list/browser/listService'; import { Button } from 'vs/base/browser/ui/button/button'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; import { STATUS_BAR_REMOTE_ITEM_BACKGROUND } from 'vs/workbench/common/theme'; import { Codicon } from 'vs/base/common/codicons'; import { defaultButtonStyles, defaultInputBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; import { Attributes, CandidatePort, Tunnel, TunnelCloseReason, TunnelModel, TunnelSource, forwardedPortsViewEnabled, makeAddress, mapHasAddressLocalhostOrAllInterfaces, parseAddress } from 'vs/workbench/services/remote/common/tunnelModel'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export const openPreviewEnabledContext = new RawContextKey('openPreviewEnabled', false); diff --git a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts index c7dfc7eae60..d12c8269212 100644 --- a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts @@ -19,7 +19,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export interface IOptions { placeholder?: string; diff --git a/src/vs/workbench/contrib/search/browser/searchResultsView.ts b/src/vs/workbench/contrib/search/browser/searchResultsView.ts index 38a4f536403..e22115c2a8f 100644 --- a/src/vs/workbench/contrib/search/browser/searchResultsView.ts +++ b/src/vs/workbench/contrib/search/browser/searchResultsView.ts @@ -30,8 +30,8 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { defaultCountBadgeStyles } from 'vs/platform/theme/browser/defaultStyles'; import { SearchContext } from 'vs/workbench/contrib/search/common/constants'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; interface IFolderMatchTemplate { label: IResourceLabel; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 4a0a156fcf6..6ea0cb4542e 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -81,8 +81,8 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { ILogService } from 'vs/platform/log/common/log'; import { AccessibilitySignal, IAccessibilitySignalService } from 'vs/platform/accessibilitySignal/browser/accessibilitySignalService'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; const $ = dom.$; diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 731653a4828..332c290246a 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -42,7 +42,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { GroupModelChangeKind } from 'vs/workbench/common/editor'; import { SearchFindInput } from 'vs/workbench/contrib/search/browser/searchFindInput'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; /** Specified in searchview.css */ const SingleLineInputHeight = 26; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 02b833d6737..449b0d60787 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -62,8 +62,8 @@ import { UnusualLineTerminatorsDetector } from 'vs/editor/contrib/unusualLineTer import { defaultToggleStyles, getInputBoxStyle } from 'vs/platform/theme/browser/defaultStyles'; import { ILogService } from 'vs/platform/log/common/log'; import { SearchContext } from 'vs/workbench/contrib/search/common/constants'; -import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; const RESULT_LINE_REGEX = /^(\s+)(\d+)(: | )(\s*)(.*)$/; const FILE_LINE_REGEX = /^(\S.*):$/; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 31ed968488e..7bf4f427bd0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -44,7 +44,7 @@ import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { defaultSelectBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; import { Event } from 'vs/base/common/event'; -import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/hover/hoverDelegate'; import { IHoverService } from 'vs/platform/hover/browser/hover'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { InstanceContext, TerminalContextActionRunner } from 'vs/workbench/contrib/terminal/browser/terminalContextMenu'; diff --git a/src/vs/workbench/contrib/testing/browser/testCoverageBars.ts b/src/vs/workbench/contrib/testing/browser/testCoverageBars.ts index 7261e5e1021..56d486699a6 100644 --- a/src/vs/workbench/contrib/testing/browser/testCoverageBars.ts +++ b/src/vs/workbench/contrib/testing/browser/testCoverageBars.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { h } from 'vs/base/browser/dom'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { ICustomHover, ITooltipMarkdownString, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { ICustomHover, ITooltipMarkdownString, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { assertNever } from 'vs/base/common/assert'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { Lazy } from 'vs/base/common/lazy'; diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 8eb58e118a3..e5e8e34abfa 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -8,8 +8,8 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { ActionBar, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { Button } from 'vs/base/browser/ui/button/button'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; -import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { DefaultKeyboardNavigationDelegate, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 06476f564c8..87503bb990c 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -49,13 +49,13 @@ import { API_OPEN_DIFF_EDITOR_COMMAND_ID, API_OPEN_EDITOR_COMMAND_ID } from 'vs/ import { MarshalledId } from 'vs/base/common/marshallingIds'; import { isString } from 'vs/base/common/types'; import { renderMarkdownAsPlaintext } from 'vs/base/browser/markdownRenderer'; -import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { AriaRole } from 'vs/base/browser/ui/aria/aria'; import { ILocalizedString } from 'vs/platform/action/common/action'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; const ItemHeight = 22; diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts index 45ea476ec9a..987d9f119b9 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts @@ -77,7 +77,7 @@ import { IHoverService } from 'vs/platform/hover/browser/hover'; import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; import { DEFAULT_ICON, ICONS } from 'vs/workbench/services/userDataProfile/common/userDataProfileIcons'; import { WorkbenchIconSelectBox } from 'vs/workbench/services/userDataProfile/browser/iconSelectBox'; -import { IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IHoverWidget } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; From 0bdc2f09bb775f72e72f5ca178a9f918e9dc575a Mon Sep 17 00:00:00 2001 From: Mahmoud Salah Date: Mon, 26 Feb 2024 18:09:44 +0200 Subject: [PATCH 0183/1175] =?UTF-8?q?for=20diff=20editors,=20resolve=20the?= =?UTF-8?q?=20modified=20editor=20to=20allow=20run=20tests=20in=20c?= =?UTF-8?q?=E2=80=A6=20(#206026)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * for diff editors, resolve the modified editor to allow run tests in current file or run test at cursor to work * Check for active code editor when finding tests. * Handle compilation issues with possible null. * update import path. * Missing null check. * Remove cast. --------- Co-authored-by: Mahmoud Khalil --- .../testing/browser/testExplorerActions.ts | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts index f6f451bcd0d..103efb04a9c 100644 --- a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts +++ b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts @@ -9,7 +9,8 @@ import { Iterable } from 'vs/base/common/iterator'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { isDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; -import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; @@ -981,15 +982,20 @@ abstract class ExecuteTestAtCursor extends Action2 { * @override */ public async run(accessor: ServicesAccessor) { + const codeEditorService = accessor.get(ICodeEditorService); const editorService = accessor.get(IEditorService); const activeEditorPane = editorService.activeEditorPane; - const activeControl = editorService.activeTextEditorControl; - if (!activeEditorPane || !activeControl) { + let editor = codeEditorService.getActiveCodeEditor(); + if (!activeEditorPane || !editor) { return; } - const position = activeControl?.getPosition(); - const model = activeControl?.getModel(); + if (editor instanceof EmbeddedCodeEditorWidget) { + editor = editor.getParentEditor(); + } + + const position = editor?.getPosition(); + const model = editor?.getModel(); if (!position || !model || !('uri' in model)) { return; } @@ -1053,8 +1059,8 @@ abstract class ExecuteTestAtCursor extends Action2 { group: this.group, tests: bestNodes.length ? bestNodes : bestNodesBefore, }); - } else if (isCodeEditor(activeControl)) { - MessageController.get(activeControl)?.showMessage(localize('noTestsAtCursor', "No tests found here"), position); + } else if (editor) { + MessageController.get(editor)?.showMessage(localize('noTestsAtCursor', "No tests found here"), position); } } } @@ -1186,9 +1192,15 @@ abstract class ExecuteTestsInCurrentFile extends Action2 { * @override */ public run(accessor: ServicesAccessor) { - const control = accessor.get(IEditorService).activeTextEditorControl; - const position = control?.getPosition(); - const model = control?.getModel(); + let editor = accessor.get(ICodeEditorService).getActiveCodeEditor(); + if (!editor) { + return; + } + if (editor instanceof EmbeddedCodeEditorWidget) { + editor = editor.getParentEditor(); + } + const position = editor?.getPosition(); + const model = editor?.getModel(); if (!position || !model || !('uri' in model)) { return; } @@ -1218,8 +1230,8 @@ abstract class ExecuteTestsInCurrentFile extends Action2 { }); } - if (isCodeEditor(control)) { - MessageController.get(control)?.showMessage(localize('noTestsInFile', "No tests found in this file"), position); + if (editor) { + MessageController.get(editor)?.showMessage(localize('noTestsInFile', "No tests found in this file"), position); } return undefined; From 2ba3fae68d15d0a83a00cc62e2d84962180c359a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 26 Feb 2024 08:45:12 -0800 Subject: [PATCH 0184/1175] Pick up TS 5.4 rc for bundling (#206263) --- 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 4365c20acc1..7066412c3f8 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.2" + "typescript": "5.4.1-rc" }, "scripts": { "postinstall": "node ./postinstall.mjs" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index f4543f6a1c9..4b22ef50a82 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -234,10 +234,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -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== +typescript@5.4.1-rc: + version "5.4.1-rc" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.1-rc.tgz#1ecdd897df1d9ef5bd1f844bad64691ecc23314d" + integrity sha512-gInURzaO0bbfzfQAc3mfcHxh8qev+No4QOFUZHajo9vBgOLaljELJ3wuzyoGo/zHIzMSezdhtrsRdqL6E9SvNA== vscode-grammar-updater@^1.1.0: version "1.1.0" From 3d880720c822671cd4aa38fa9529567388b3b191 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 26 Feb 2024 09:01:43 -0800 Subject: [PATCH 0185/1175] fix #205066 --- src/vs/workbench/contrib/terminal/browser/terminalActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 50a7fc56a29..9e6c751c89c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -1654,7 +1654,7 @@ export function registerTerminalActions() { registerActiveInstanceAction({ id: TerminalCommandId.StartVoice, - title: localize2('workbench.action.terminal.startVoice', "Start Terminal Voice"), + title: localize2('workbench.action.terminal.startDictation', "Start Dictation in Terminal"), precondition: ContextKeyExpr.and(HasSpeechProvider, sharedWhenClause.terminalAvailable), f1: true, run: (activeInstance, c, accessor) => { @@ -1665,7 +1665,7 @@ export function registerTerminalActions() { registerActiveInstanceAction({ id: TerminalCommandId.StopVoice, - title: localize2('workbench.action.terminal.stopVoice', "Stop Terminal Voice"), + title: localize2('workbench.action.terminal.stopDictation', "Stop Dictation in Terminal"), precondition: ContextKeyExpr.and(HasSpeechProvider, sharedWhenClause.terminalAvailable), f1: true, run: (activeInstance, c, accessor) => { From 0a0f196666c687e0dd0642411a70ba0e7f9cb1c1 Mon Sep 17 00:00:00 2001 From: Luis Sousa Date: Mon, 26 Feb 2024 14:19:56 -0300 Subject: [PATCH 0186/1175] Feat: Add PascalCase to CaseActions (#206259) feat: Add PascalCase to CaseActions --- .../browser/linesOperations.ts | 32 +++++++++ .../test/browser/linesOperations.test.ts | 70 ++++++++++++++++++- 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts b/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts index 74d7849587e..dd6b3158491 100644 --- a/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts @@ -1187,6 +1187,35 @@ export class CamelCaseAction extends AbstractCaseAction { } } +export class PascalCaseAction extends AbstractCaseAction { + public static wordBoundary = new BackwardsCompatibleRegExp('[_\\s-]', 'gm'); + public static wordBoundaryToMaintain = new BackwardsCompatibleRegExp('(?<=\\.)', 'gm'); + + constructor() { + super({ + id: 'editor.action.transformToPascalcase', + label: nls.localize('editor.transformToPascalcase', "Transform to Pascal Case"), + alias: 'Transform to Pascal Case', + precondition: EditorContextKeys.writable + }); + } + + protected _modifyText(text: string, wordSeparators: string): string { + const wordBoundary = PascalCaseAction.wordBoundary.get(); + const wordBoundaryToMaintain = PascalCaseAction.wordBoundaryToMaintain.get(); + + if (!wordBoundary || !wordBoundaryToMaintain) { + // cannot support this + return text; + } + + const wordsWithMaintainBoundaries = text.split(wordBoundaryToMaintain); + const words = wordsWithMaintainBoundaries.map((word: string) => word.split(wordBoundary)).flat(); + return words.map((word: string) => word.substring(0, 1).toLocaleUpperCase() + word.substring(1)) + .join(''); + } +} + export class KebabCaseAction extends AbstractCaseAction { public static isSupported(): boolean { @@ -1257,6 +1286,9 @@ if (SnakeCaseAction.caseBoundary.isSupported() && SnakeCaseAction.singleLetters. if (CamelCaseAction.wordBoundary.isSupported()) { registerEditorAction(CamelCaseAction); } +if (PascalCaseAction.wordBoundary.isSupported()) { + registerEditorAction(PascalCaseAction); +} if (TitleCaseAction.titleBoundary.isSupported()) { registerEditorAction(TitleCaseAction); } diff --git a/src/vs/editor/contrib/linesOperations/test/browser/linesOperations.test.ts b/src/vs/editor/contrib/linesOperations/test/browser/linesOperations.test.ts index 3df2a1f682c..795bf69bec5 100644 --- a/src/vs/editor/contrib/linesOperations/test/browser/linesOperations.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/browser/linesOperations.test.ts @@ -12,7 +12,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { Handler } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; -import { CamelCaseAction, DeleteAllLeftAction, DeleteAllRightAction, DeleteDuplicateLinesAction, DeleteLinesAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, KebabCaseAction, LowerCaseAction, SnakeCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TitleCaseAction, TransposeAction, UpperCaseAction } from 'vs/editor/contrib/linesOperations/browser/linesOperations'; +import { CamelCaseAction, PascalCaseAction, DeleteAllLeftAction, DeleteAllRightAction, DeleteDuplicateLinesAction, DeleteLinesAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, KebabCaseAction, LowerCaseAction, SnakeCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TitleCaseAction, TransposeAction, UpperCaseAction } from 'vs/editor/contrib/linesOperations/browser/linesOperations'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; @@ -935,6 +935,74 @@ suite('Editor Contrib - Line Operations', () => { assertSelection(editor, new Selection(11, 1, 11, 11)); } ); + + withTestCodeEditor( + [ + 'hello world', + 'öçşğü', + 'parseHTMLString', + 'getElementById', + 'PascalCase', + 'öçşÖÇŞğüĞÜ', + 'audioConverter.convertM4AToMP3();', + 'Capital_Snake_Case', + 'parseHTML4String', + 'Kebab-Case', + ], {}, (editor) => { + const model = editor.getModel()!; + const pascalCaseAction = new PascalCaseAction(); + + editor.setSelection(new Selection(1, 1, 1, 12)); + executeAction(pascalCaseAction, editor); + assert.strictEqual(model.getLineContent(1), 'HelloWorld'); + assertSelection(editor, new Selection(1, 1, 1, 11)); + + editor.setSelection(new Selection(2, 1, 2, 6)); + executeAction(pascalCaseAction, editor); + assert.strictEqual(model.getLineContent(2), 'Öçşğü'); + assertSelection(editor, new Selection(2, 1, 2, 6)); + + editor.setSelection(new Selection(3, 1, 3, 16)); + executeAction(pascalCaseAction, editor); + assert.strictEqual(model.getLineContent(3), 'ParseHTMLString'); + assertSelection(editor, new Selection(3, 1, 3, 16)); + + editor.setSelection(new Selection(4, 1, 4, 15)); + executeAction(pascalCaseAction, editor); + assert.strictEqual(model.getLineContent(4), 'GetElementById'); + assertSelection(editor, new Selection(4, 1, 4, 15)); + + editor.setSelection(new Selection(5, 1, 5, 11)); + executeAction(pascalCaseAction, editor); + assert.strictEqual(model.getLineContent(5), 'PascalCase'); + assertSelection(editor, new Selection(5, 1, 5, 11)); + + editor.setSelection(new Selection(6, 1, 6, 11)); + executeAction(pascalCaseAction, editor); + assert.strictEqual(model.getLineContent(6), 'ÖçşÖÇŞğüĞÜ'); + assertSelection(editor, new Selection(6, 1, 6, 11)); + + editor.setSelection(new Selection(7, 1, 7, 34)); + executeAction(pascalCaseAction, editor); + assert.strictEqual(model.getLineContent(7), 'AudioConverter.ConvertM4AToMP3();'); + assertSelection(editor, new Selection(7, 1, 7, 34)); + + editor.setSelection(new Selection(8, 1, 8, 19)); + executeAction(pascalCaseAction, editor); + assert.strictEqual(model.getLineContent(8), 'CapitalSnakeCase'); + assertSelection(editor, new Selection(8, 1, 8, 17)); + + editor.setSelection(new Selection(9, 1, 9, 17)); + executeAction(pascalCaseAction, editor); + assert.strictEqual(model.getLineContent(9), 'ParseHTML4String'); + assertSelection(editor, new Selection(9, 1, 9, 17)); + + editor.setSelection(new Selection(10, 1, 10, 11)); + executeAction(pascalCaseAction, editor); + assert.strictEqual(model.getLineContent(10), 'KebabCase'); + assertSelection(editor, new Selection(10, 1, 10, 10)); + } + ); }); suite('DeleteAllRightAction', () => { From 6350f21dfe5614d722def7e259103e846ef5bf2c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 26 Feb 2024 10:07:28 -0800 Subject: [PATCH 0187/1175] Reveal link source while navigating detected links Part of #206127 --- .../terminal/browser/media/terminal.css | 5 + .../contrib/terminal/browser/terminal.ts | 6 +- .../browser/xterm/markNavigationAddon.ts | 97 +++++++++++++++++-- .../browser/terminal.links.contribution.ts | 2 +- .../links/browser/terminalLinkQuickpick.ts | 46 ++++++++- 5 files changed, 145 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 488815cf258..e30f5dd92d9 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -474,6 +474,11 @@ pointer-events: none; } +.terminal-range-highlight { + outline: 1px solid var(--vscode-focusBorder); + pointer-events: none; +} + .terminal-command-guide { left: 0; border: 1.5px solid #ffffff; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 9a1bd1ab1b7..d8d7ebeb111 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -23,7 +23,7 @@ import { ITerminalStatusList } from 'vs/workbench/contrib/terminal/browser/termi import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; import { IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfiguration, ITerminalFont, ITerminalProcessExtHostProxy, ITerminalProcessInfo } from 'vs/workbench/contrib/terminal/common/terminal'; import { ISimpleSelectedSuggestion } from 'vs/workbench/services/suggest/browser/simpleSuggestWidget'; -import type { IMarker, ITheme, Terminal as RawXtermTerminal } from '@xterm/xterm'; +import type { IMarker, ITheme, Terminal as RawXtermTerminal, IBufferRange } from '@xterm/xterm'; import { ScrollPosition } from 'vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { GroupIdentifier } from 'vs/workbench/common/editor'; @@ -118,8 +118,12 @@ export interface IMarkTracker { scrollToLine(line: number, position: ScrollPosition): void; revealCommand(command: ITerminalCommand, position?: ScrollPosition): void; + revealRange(range: IBufferRange): void; registerTemporaryDecoration(marker: IMarker, endMarker: IMarker | undefined, showOutline: boolean): void; showCommandGuide(command: ITerminalCommand | undefined): void; + + saveScrollState(): void; + restoreScrollState(): 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 499e1d2f57d..bb6907c4e78 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts @@ -7,7 +7,7 @@ import { coalesce } from 'vs/base/common/arrays'; 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'; +import type { Terminal, IMarker, ITerminalAddon, IDecoration, IBufferRange } from '@xterm/xterm'; import { timeout } from 'vs/base/common/async'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TERMINAL_OVERVIEW_RULER_CURSOR_FOREGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; @@ -24,6 +24,11 @@ export const enum ScrollPosition { Middle } +interface IScrollToMarkerOptions { + hideDecoration?: boolean; + bufferRange?: IBufferRange; +} + export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITerminalAddon { private _currentMarker: IMarker | Boundary = Boundary.Bottom; private _selectionStart: IMarker | Boundary | null = null; @@ -219,7 +224,7 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe } } - private _scrollToMarker(start: IMarker | number, position: ScrollPosition, end?: IMarker | number, hideDecoration?: boolean): void { + private _scrollToMarker(start: IMarker | number, position: ScrollPosition, end?: IMarker | number, options?: IScrollToMarkerOptions): void { if (!this._terminal) { return; } @@ -227,8 +232,12 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe const line = this.getTargetScrollLine(toLineIndex(start), position); this._terminal.scrollToLine(line); } - if (!hideDecoration) { - this.registerTemporaryDecoration(start, end, true); + if (!options?.hideDecoration) { + if (options?.bufferRange) { + this._highlightBufferRange(options.bufferRange); + } else { + this.registerTemporaryDecoration(start, end, true); + } } } @@ -260,6 +269,16 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe ); } + revealRange(range: IBufferRange): void { + // TODO: Allow room for sticky scroll + this._scrollToMarker( + range.start.y - 1, + ScrollPosition.Middle, + range.end.y - 1, + { bufferRange: range } + ); + } + showCommandGuide(command: ITerminalCommand | undefined): void { if (!this._terminal) { return; @@ -314,6 +333,72 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe } } + + private _scrollState: { viewportY: number } | undefined; + + saveScrollState(): void { + this._scrollState = { viewportY: this._terminal?.buffer.active.viewportY ?? 0 }; + } + + restoreScrollState(): void { + if (this._scrollState && this._terminal) { + this._terminal.scrollToLine(this._scrollState.viewportY); + this._scrollState = undefined; + } + } + + private _highlightBufferRange(range: IBufferRange): void { + if (!this._terminal) { + return; + } + + // TODO: Save original scroll point + + this._resetNavigationDecorations(); + const startLine = range.start.y; + const decorationCount = range.end.y - range.start.y + 1; + for (let i = 0; i < decorationCount; i++) { + const decoration = this._terminal.registerDecoration({ + marker: this._createMarkerForOffset(startLine - 1, i), + x: range.start.x - 1, + width: (range.end.x - 1) - (range.start.x - 1) + 1, + overviewRulerOptions: undefined + }); + if (decoration) { + this._navigationDecorations?.push(decoration); + let renderedElement: HTMLElement | undefined; + + decoration.onRender(element => { + if (!renderedElement) { + renderedElement = element; + // if (i === 0) { + // element.classList.add('top'); + // } + // if (i === decorationCount - 1) { + // element.classList.add('bottom'); + // } + element.classList.add('terminal-range-highlight'); + } + if (this._terminal?.element) { + // element.style.marginLeft = `-${getWindow(this._terminal.element).getComputedStyle(this._terminal.element).paddingLeft}`; + } + }); + // TODO: Scroll may be under sticky scroll + + // 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 + // if (showOutline) { + // timeout(350).then(() => { + // if (renderedElement) { + // renderedElement.classList.remove('terminal-scroll-highlight-outline'); + // } + // }); + // } + } + } + } + registerTemporaryDecoration(marker: IMarker | number, endMarker: IMarker | number | undefined, showOutline: boolean): void { if (!this._terminal) { return; @@ -373,7 +458,7 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe } getTargetScrollLine(line: number, position: ScrollPosition): number { - // Middle is treated at 1/4 of the viewport's size because context below is almost always + // Middle is treated as 1/4 of the viewport's size because context below is almost always // more important than context above in the terminal. if (this._terminal && position === ScrollPosition.Middle) { return Math.max(line - Math.floor(this._terminal.rows / 4), 0); @@ -397,7 +482,7 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe return; } const endMarker = endMarkerId ? detectionCapability.getMark(endMarkerId) : startMarker; - this._scrollToMarker(startMarker, ScrollPosition.Top, endMarker, !highlight); + this._scrollToMarker(startMarker, ScrollPosition.Top, endMarker, { hideDecoration: !highlight }); } selectToPreviousMark(): void { diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts index 0327aa48168..2a0269486a3 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts @@ -83,7 +83,7 @@ class TerminalLinkContribution extends DisposableStore implements ITerminalContr }); } const links = await this._getLinks(); - return await this._terminalLinkQuickpick.show(links); + return await this._terminalLinkQuickpick.show(this._instance, links); } private async _getLinks(): Promise<{ viewport: IDetectedLinks; all: Promise }> { diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts index 2c042949a9d..f496f89cbfc 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts @@ -8,7 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; import { QuickPickItem, IQuickInputService, IQuickPickItem, QuickInputHideReason } from 'vs/platform/quickinput/common/quickInput'; import { IDetectedLinks } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager'; -import { TerminalLinkQuickPickEvent } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalLinkQuickPickEvent, type IDetachedTerminalInstance, type ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import type { ILink } from '@xterm/xterm'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; @@ -30,6 +30,8 @@ export class TerminalLinkQuickpick extends DisposableStore { private readonly _editorSequencer = new Sequencer(); private readonly _editorViewState: EditorViewState; + private _instance: ITerminalInstance | IDetachedTerminalInstance | undefined; + private readonly _onDidRequestMoreLinks = this.add(new Emitter()); readonly onDidRequestMoreLinks = this._onDidRequestMoreLinks.event; @@ -45,7 +47,9 @@ export class TerminalLinkQuickpick extends DisposableStore { this._editorViewState = new EditorViewState(_editorService); } - async show(links: { viewport: IDetectedLinks; all: Promise }): Promise { + async show(instance: ITerminalInstance | IDetachedTerminalInstance, links: { viewport: IDetectedLinks; all: Promise }): Promise { + this._instance = instance; + // Get raw link picks const wordPicks = links.viewport.wordLinks ? await this._generatePicks(links.viewport.wordLinks) : undefined; const filePicks = links.viewport.fileLinks ? await this._generatePicks(links.viewport.fileLinks) : undefined; @@ -122,6 +126,18 @@ export class TerminalLinkQuickpick extends DisposableStore { return new Promise(r => { disposables.add(pick.onDidHide(({ reason }) => { + + // Restore terminal scroll state + if (this._terminalScrollStateSaved) { + const markTracker = this._instance?.xterm?.markTracker; + if (markTracker) { + markTracker.restoreScrollState(); + // TODO: This name isn't great + markTracker.clearMarker(); + this._terminalScrollStateSaved = false; + } + } + // Restore view state upon cancellation if we changed it // but only when the picker was closed via explicit user // gesture and not e.g. when focus was lost because that @@ -208,11 +224,18 @@ export class TerminalLinkQuickpick extends DisposableStore { } private _previewItem(item: ITerminalLinkQuickPickItem | IQuickPickItem) { - if (!item || !('link' in item) || !item.link || !('uri' in item.link) || !item.link.uri) { + if (!item || !('link' in item) || !item.link) { return; } + // Any link can be previewed in the termninal const link = item.link; + this._previewItemInTerminal(link); + + if (!('uri' in link) || !link.uri) { + return; + } + if (link.type !== TerminalBuiltinLinkType.LocalFile) { return; } @@ -223,6 +246,10 @@ export class TerminalLinkQuickpick extends DisposableStore { return; } + this._previewItemInEditor(link); + } + + private _previewItemInEditor(link: TerminalLink) { const linkSuffix = link.parsedLink ? link.parsedLink.suffix : getLinkSuffix(link.text); const selection = linkSuffix?.row === undefined ? undefined : { startLineNumber: linkSuffix.row ?? 1, @@ -245,6 +272,19 @@ export class TerminalLinkQuickpick extends DisposableStore { } }); } + + private _terminalScrollStateSaved: boolean = false; + private _previewItemInTerminal(link: ILink) { + const xterm = this._instance?.xterm; + if (!xterm) { + return; + } + if (!this._terminalScrollStateSaved) { + xterm.markTracker.saveScrollState(); + this._terminalScrollStateSaved = true; + } + xterm.markTracker.revealRange(link.range); + } } export interface ITerminalLinkQuickPickItem extends IQuickPickItem { From 920a3a701eeb5830d94f00813d444873599e44ea Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 26 Feb 2024 10:56:20 -0800 Subject: [PATCH 0188/1175] Pick up latest TS for building VS Code (#206264) --- build/azure-pipelines/common/publish.js | 2 +- build/lib/compilation.js | 2 +- package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build/azure-pipelines/common/publish.js b/build/azure-pipelines/common/publish.js index e6b24921ac1..b690ae5c792 100644 --- a/build/azure-pipelines/common/publish.js +++ b/build/azure-pipelines/common/publish.js @@ -595,7 +595,7 @@ async function main() { operations.push({ name: artifact.name, operation }); resultPromise = Promise.allSettled(operations.map(o => o.operation)); } - await new Promise(c => setTimeout(c, 10000)); + await new Promise(c => setTimeout(c, 10_000)); } console.log(`Found all ${done.size + processing.size} artifacts, waiting for ${processing.size} artifacts to finish publishing...`); const artifactsInProgress = operations.filter(o => processing.has(o.name)); diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 35bc464d34a..e7a460de7d0 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -99,7 +99,7 @@ function transpileTask(src, out, swc) { exports.transpileTask = transpileTask; function compileTask(src, out, build, options = {}) { const task = () => { - if (os.totalmem() < 4000000000) { + if (os.totalmem() < 4_000_000_000) { throw new Error('compilation requires 4GB of RAM'); } const compile = createCompile(src, build, true, false); diff --git a/package.json b/package.json index 2f127741d55..30ed28ac516 100644 --- a/package.json +++ b/package.json @@ -209,7 +209,7 @@ "ts-loader": "^9.4.2", "ts-node": "^10.9.1", "tsec": "0.2.7", - "typescript": "^5.4.0-dev.20240206", + "typescript": "^5.5.0-dev.20240226", "typescript-formatter": "7.1.0", "util": "^0.12.4", "vscode-nls-dev": "^3.3.1", diff --git a/yarn.lock b/yarn.lock index 98368474549..056c1e1bce0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9636,10 +9636,10 @@ typescript@^4.7.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== -typescript@^5.4.0-dev.20240206: - version "5.4.0-dev.20240206" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.0-dev.20240206.tgz#75755acb115e1176958d511d11eb018694e74987" - integrity sha512-8P1XYxDbG/AyGE5tB8+JpeiQfS5ye1BTvIVDZaHhoK9nJuCn4nkB0L66lvfwYB+46hA4rLo3vE3WkIToSYtqQA== +typescript@^5.5.0-dev.20240226: + version "5.5.0-dev.20240226" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.0-dev.20240226.tgz#b571688666f07e4d7db4c9863f3ee1401e161a7a" + integrity sha512-mLY9/pjzSCr7JLkMKHS3KQUKX+LPO9WWjiR+mRcWKcskSdMBZ0j1TPhk/zUyuBklOf3YX4orkvamNiZWZEK0CQ== typical@^4.0.0: version "4.0.0" From a2030c81feeec0d803dfe60e5b4a113c3c7b47e1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 26 Feb 2024 11:10:01 -0800 Subject: [PATCH 0189/1175] Update issue notebook milestones (#206278) --- .vscode/notebooks/api.github-issues | 2 +- .vscode/notebooks/endgame.github-issues | 2 +- .vscode/notebooks/my-endgame.github-issues | 2 +- .vscode/notebooks/my-work.github-issues | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 2a6f3ec1bc5..6b8a385ec42 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"February 2024\"" + "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"March 2024\"" }, { "kind": 1, diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index ee1084be56d..750e53e4b26 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"February 2024\"" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"March 2024\"" }, { "kind": 1, diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index a286082c738..ab59f23283f 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"February 2024\"\n\n$MINE=assignee:@me" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"March 2024\"\n\n$MINE=assignee:@me" }, { "kind": 1, diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index 2a3f9159703..b23dacf87e4 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n// current milestone name\n$MILESTONE=milestone:\"February 2024\"\n" + "value": "// list of repos we work in\n$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n// current milestone name\n$MILESTONE=milestone:\"March 2024\"\n" }, { "kind": 1, From ead787f6f01630a1261fe80abe87242c9170ebc6 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 26 Feb 2024 11:13:02 -0800 Subject: [PATCH 0190/1175] fix issue --- .../contrib/terminalContrib/chat/browser/terminalChatWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 180d51c1549..aa43972ef64 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -9,7 +9,7 @@ import { MarkdownString } from 'vs/base/common/htmlContent'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/terminalChatWidget'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/model'; From c9e61431bae75f54303d2c00b83c6ed6b8ff2f3c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 26 Feb 2024 11:17:28 -0800 Subject: [PATCH 0191/1175] Show all links if resolved within 500ms Fixes #206280 --- .../links/browser/terminalLinkQuickpick.ts | 78 ++++++++++--------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts index f496f89cbfc..544612bcb53 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts @@ -14,7 +14,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import type { TerminalLink } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLink'; -import { Sequencer } from 'vs/base/common/async'; +import { Sequencer, timeout } from 'vs/base/common/async'; import { EditorViewState } from 'vs/workbench/browser/quickaccess'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; @@ -50,11 +50,17 @@ export class TerminalLinkQuickpick extends DisposableStore { async show(instance: ITerminalInstance | IDetachedTerminalInstance, links: { viewport: IDetectedLinks; all: Promise }): Promise { this._instance = instance; + // Allow all links a small amount of time to elapse to finish, if this is not done in this + // time they will be loaded upon the first filter. + const result = await Promise.race([links.all, timeout(500)]); + const usingAllLinks = typeof result === 'object'; + const resolvedLinks = usingAllLinks ? result : links.viewport; + // Get raw link picks - const wordPicks = links.viewport.wordLinks ? await this._generatePicks(links.viewport.wordLinks) : undefined; - const filePicks = links.viewport.fileLinks ? await this._generatePicks(links.viewport.fileLinks) : undefined; - const folderPicks = links.viewport.folderLinks ? await this._generatePicks(links.viewport.folderLinks) : undefined; - const webPicks = links.viewport.webLinks ? await this._generatePicks(links.viewport.webLinks) : undefined; + const wordPicks = resolvedLinks.wordLinks ? await this._generatePicks(resolvedLinks.wordLinks) : undefined; + const filePicks = resolvedLinks.fileLinks ? await this._generatePicks(resolvedLinks.fileLinks) : undefined; + const folderPicks = resolvedLinks.folderLinks ? await this._generatePicks(resolvedLinks.folderLinks) : undefined; + const webPicks = resolvedLinks.webLinks ? await this._generatePicks(resolvedLinks.webLinks) : undefined; const picks: LinkQuickPickItem[] = []; if (webPicks) { @@ -88,36 +94,38 @@ export class TerminalLinkQuickpick extends DisposableStore { // ASAP with only the viewport entries. let accepted = false; const disposables = new DisposableStore(); - disposables.add(Event.once(pick.onDidChangeValue)(async () => { - const allLinks = await links.all; - if (accepted) { - return; - } - const wordIgnoreLinks = [...(allLinks.fileLinks ?? []), ...(allLinks.folderLinks ?? []), ...(allLinks.webLinks ?? [])]; - - const wordPicks = allLinks.wordLinks ? await this._generatePicks(allLinks.wordLinks, wordIgnoreLinks) : undefined; - const filePicks = allLinks.fileLinks ? await this._generatePicks(allLinks.fileLinks) : undefined; - const folderPicks = allLinks.folderLinks ? await this._generatePicks(allLinks.folderLinks) : undefined; - const webPicks = allLinks.webLinks ? await this._generatePicks(allLinks.webLinks) : undefined; - const picks: LinkQuickPickItem[] = []; - if (webPicks) { - picks.push({ type: 'separator', label: localize('terminal.integrated.urlLinks', "Url") }); - picks.push(...webPicks); - } - if (filePicks) { - picks.push({ type: 'separator', label: localize('terminal.integrated.localFileLinks', "File") }); - picks.push(...filePicks); - } - if (folderPicks) { - picks.push({ type: 'separator', label: localize('terminal.integrated.localFolderLinks', "Folder") }); - picks.push(...folderPicks); - } - if (wordPicks) { - picks.push({ type: 'separator', label: localize('terminal.integrated.searchLinks', "Workspace Search") }); - picks.push(...wordPicks); - } - pick.items = picks; - })); + if (!usingAllLinks) { + disposables.add(Event.once(pick.onDidChangeValue)(async () => { + const allLinks = await links.all; + if (accepted) { + return; + } + const wordIgnoreLinks = [...(allLinks.fileLinks ?? []), ...(allLinks.folderLinks ?? []), ...(allLinks.webLinks ?? [])]; + + const wordPicks = allLinks.wordLinks ? await this._generatePicks(allLinks.wordLinks, wordIgnoreLinks) : undefined; + const filePicks = allLinks.fileLinks ? await this._generatePicks(allLinks.fileLinks) : undefined; + const folderPicks = allLinks.folderLinks ? await this._generatePicks(allLinks.folderLinks) : undefined; + const webPicks = allLinks.webLinks ? await this._generatePicks(allLinks.webLinks) : undefined; + const picks: LinkQuickPickItem[] = []; + if (webPicks) { + picks.push({ type: 'separator', label: localize('terminal.integrated.urlLinks', "Url") }); + picks.push(...webPicks); + } + if (filePicks) { + picks.push({ type: 'separator', label: localize('terminal.integrated.localFileLinks', "File") }); + picks.push(...filePicks); + } + if (folderPicks) { + picks.push({ type: 'separator', label: localize('terminal.integrated.localFolderLinks', "Folder") }); + picks.push(...folderPicks); + } + if (wordPicks) { + picks.push({ type: 'separator', label: localize('terminal.integrated.searchLinks', "Workspace Search") }); + picks.push(...wordPicks); + } + pick.items = picks; + })); + } disposables.add(pick.onDidChangeActive(async () => { const [item] = pick.activeItems; From 754dc0c68a7f1137695f1774425ed8bcb873c2b8 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Mon, 26 Feb 2024 11:25:01 -0800 Subject: [PATCH 0192/1175] Run in Section for notebook sticky scroll context menu (#205307) * breadcrumbs in nb sticky context menu * run section nb sticky scroll context menu * implement actionRunner for run in section ctx menu * use context for run in section args * nit + toggle verbage fix --- .../parts/editor/breadcrumbsControl.ts | 5 +- .../viewParts/notebookEditorStickyScroll.ts | 74 ++++++++++++++++--- 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 00e7153c46f..b97793d6ddd 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -612,13 +612,14 @@ registerAction2(class ToggleBreadcrumb extends Action2 { toggled: { condition: ContextKeyExpr.equals('config.breadcrumbs.enabled', true), title: localize('cmd.toggle2', "Breadcrumbs"), - mnemonicTitle: localize({ key: 'miBreadcrumbs2', comment: ['&& denotes a mnemonic'] }, "&&Breadcrumbs") + mnemonicTitle: localize({ key: 'miBreadcrumbs2', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breadcrumbs") }, menu: [ { id: MenuId.CommandPalette }, { id: MenuId.MenubarAppearanceMenu, group: '4_editor', order: 2 }, { id: MenuId.NotebookToolbar, group: 'notebookLayout', order: 2 }, - { id: MenuId.StickyScrollContext } + { id: MenuId.StickyScrollContext }, + { id: MenuId.NotebookStickyScrollContext, group: 'notebookView', order: 2 } ] }); } diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts index a02e1bb2010..1fb8e60d3db 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts @@ -34,17 +34,21 @@ export class ToggleNotebookStickyScroll extends Action2 { id: 'notebook.action.toggleNotebookStickyScroll', title: { ...localize2('toggleStickyScroll', "Toggle Notebook Sticky Scroll"), - mnemonicTitle: localize({ key: 'mitoggleStickyScroll', comment: ['&& denotes a mnemonic'] }, "&&Toggle Notebook Sticky Scroll"), + mnemonicTitle: localize({ key: 'mitoggleNotebookStickyScroll', comment: ['&& denotes a mnemonic'] }, "&&Toggle Notebook Sticky Scroll"), }, category: Categories.View, toggled: { condition: ContextKeyExpr.equals('config.notebook.stickyScroll.enabled', true), title: localize('notebookStickyScroll', "Notebook Sticky Scroll"), - mnemonicTitle: localize({ key: 'miNotebookStickyScroll', comment: ['&& denotes a mnemonic'] }, "&&Notebook Sticky Scroll"), + mnemonicTitle: localize({ key: 'mitoggleNotebookStickyScroll', comment: ['&& denotes a mnemonic'] }, "&&Toggle Notebook Sticky Scroll"), }, menu: [ { id: MenuId.CommandPalette }, - { id: MenuId.NotebookStickyScrollContext } + { + id: MenuId.NotebookStickyScrollContext, + group: 'notebookView', + order: 2 + } ] }); } @@ -56,6 +60,51 @@ export class ToggleNotebookStickyScroll extends Action2 { } } +type RunInSectionContext = { + target: HTMLElement; + currentStickyLines: Map; + notebookEditor: INotebookEditor; +}; + +export class RunInSectionStickyScroll extends Action2 { + constructor() { + super({ + id: 'notebook.action.runInSection', + title: { + ...localize2('runInSectionStickyScroll', "Run Section"), + mnemonicTitle: localize({ key: 'mirunInSectionStickyScroll', comment: ['&& denotes a mnemonic'] }, "&&Run Section"), + }, + menu: [ + { + id: MenuId.NotebookStickyScrollContext, + group: 'notebookExecution', + order: 1 + } + ] + }); + } + + override async run(accessor: ServicesAccessor, context: RunInSectionContext, ...args: any[]): Promise { + const selectedElement = context.target.parentElement; + const stickyLines: Map = context.currentStickyLines; + + const selectedOutlineEntry = Array.from(stickyLines.values()).find(entry => entry.line.element.contains(selectedElement))?.line.entry; + if (!selectedOutlineEntry) { + return; + } + + const flatList: OutlineEntry[] = []; + selectedOutlineEntry.asFlatList(flatList); + + const cellViewModels = flatList.map(entry => entry.cell); + const notebookEditor: INotebookEditor = context.notebookEditor; + notebookEditor.executeNotebookCells(cellViewModels); + } +} + export class NotebookStickyLine extends Disposable { constructor( public readonly element: HTMLElement, @@ -78,14 +127,6 @@ export class NotebookStickyLine extends Disposable { } })); - // folding icon hovers - // this._register(DOM.addDisposableListener(this.element, DOM.EventType.MOUSE_OVER, () => { - // this.foldingIcon.setVisible(true); - // })); - // this._register(DOM.addDisposableListener(this.element, DOM.EventType.MOUSE_OUT, () => { - // this.foldingIcon.setVisible(false); - // })); - } private toggleFoldRange(currentState: CellFoldingState) { @@ -145,7 +186,6 @@ export class NotebookStickyScroll extends Disposable { private readonly _onDidChangeNotebookStickyScroll = this._register(new Emitter()); readonly onDidChangeNotebookStickyScroll: Event = this._onDidChangeNotebookStickyScroll.event; - getDomNode(): HTMLElement { return this.domNode; } @@ -205,9 +245,17 @@ export class NotebookStickyScroll extends Disposable { private onContextMenu(e: MouseEvent) { const event = new StandardMouseEvent(DOM.getWindow(this.domNode), e); + + const context: RunInSectionContext = { + target: event.target, + currentStickyLines: this.currentStickyLines, + notebookEditor: this.notebookEditor, + }; + this._contextMenuService.showContextMenu({ menuId: MenuId.NotebookStickyScrollContext, getAnchor: () => event, + menuActionOptions: { shouldForwardArgs: true, arg: context }, }); } @@ -384,6 +432,7 @@ export class NotebookStickyScroll extends Disposable { stickyHeader.innerText = entry.label; stickyElement.append(stickyFoldingIcon.domNode, stickyHeader); + return new NotebookStickyLine(stickyElement, stickyFoldingIcon, stickyHeader, entry, notebookEditor); } @@ -490,3 +539,4 @@ export function computeContent(notebookEditor: INotebookEditor, notebookCellList } registerAction2(ToggleNotebookStickyScroll); +registerAction2(RunInSectionStickyScroll); From d4b102e34470574cf8c6eedb7bb6895268674ba5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 26 Feb 2024 11:28:42 -0800 Subject: [PATCH 0193/1175] Always scroll when sticky scroll is enabled --- .../browser/xterm/markNavigationAddon.ts | 36 ++++++------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts index bb6907c4e78..aae4ac6f275 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts @@ -13,6 +13,8 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TERMINAL_OVERVIEW_RULER_CURSOR_FOREGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { getWindow } from 'vs/base/browser/dom'; import { ICurrentPartialCommand } from 'vs/platform/terminal/common/capabilities/commandDetection/terminalCommand'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; enum Boundary { Top, @@ -26,6 +28,8 @@ export const enum ScrollPosition { interface IScrollToMarkerOptions { hideDecoration?: boolean; + /** Scroll even if the line is within the viewport */ + forceScroll?: boolean; bufferRange?: IBufferRange; } @@ -48,6 +52,7 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe constructor( private readonly _capabilities: ITerminalCapabilityStore, + @IConfigurationService private readonly _configurationService: IConfigurationService, @IThemeService private readonly _themeService: IThemeService ) { super(); @@ -228,7 +233,7 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe if (!this._terminal) { return; } - if (!this._isMarkerInViewport(this._terminal, start)) { + if (!this._isMarkerInViewport(this._terminal, start) || options?.forceScroll) { const line = this.getTargetScrollLine(toLineIndex(start), position); this._terminal.scrollToLine(line); } @@ -270,12 +275,15 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe } revealRange(range: IBufferRange): void { - // TODO: Allow room for sticky scroll this._scrollToMarker( range.start.y - 1, ScrollPosition.Middle, range.end.y - 1, - { bufferRange: range } + { + bufferRange: range, + // Ensure scroll shows the line when sticky scroll is enabled + forceScroll: !!this._configurationService.getValue(TerminalSettingId.StickyScrollEnabled) + } ); } @@ -352,8 +360,6 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe return; } - // TODO: Save original scroll point - this._resetNavigationDecorations(); const startLine = range.start.y; const decorationCount = range.end.y - range.start.y + 1; @@ -371,30 +377,10 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe decoration.onRender(element => { if (!renderedElement) { renderedElement = element; - // if (i === 0) { - // element.classList.add('top'); - // } - // if (i === decorationCount - 1) { - // element.classList.add('bottom'); - // } element.classList.add('terminal-range-highlight'); } - if (this._terminal?.element) { - // element.style.marginLeft = `-${getWindow(this._terminal.element).getComputedStyle(this._terminal.element).paddingLeft}`; - } }); - // TODO: Scroll may be under sticky scroll - - // 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 - // if (showOutline) { - // timeout(350).then(() => { - // if (renderedElement) { - // renderedElement.classList.remove('terminal-scroll-highlight-outline'); - // } - // }); - // } } } } From d1c62c90be88d326d1b8ea73c7bb7a9b6fb550ad Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 26 Feb 2024 11:29:19 -0800 Subject: [PATCH 0194/1175] Add inline code for a few special character in docs (#206277) --- src/vscode-dts/vscode.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index a8fef891e89..614dcc68cf1 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -1356,7 +1356,7 @@ declare module 'vscode' { export interface TextEditorEdit { /** * Replace a certain text region with a new value. - * You can use \r\n or \n in `value` and they will be normalized to the current {@link TextDocument document}. + * You can use `\r\n` or `\n` in `value` and they will be normalized to the current {@link TextDocument document}. * * @param location The range this operation should remove. * @param value The new text this operation should insert after removing `location`. @@ -1365,7 +1365,7 @@ declare module 'vscode' { /** * Insert text at a location. - * You can use \r\n or \n in `value` and they will be normalized to the current {@link TextDocument document}. + * You can use `\r\n` or `\n` in `value` and they will be normalized to the current {@link TextDocument document}. * Although the equivalent text edit can be made with {@link TextEditorEdit.replace replace}, `insert` will produce a different resulting selection (it will get moved). * * @param location The position where the new text should be inserted. @@ -7335,7 +7335,7 @@ declare module 'vscode' { * * @param text The text to send. * @param shouldExecute Indicates that the text being sent should be executed rather than just inserted in the terminal. - * The character(s) added are \n or \r\n, depending on the platform. This defaults to `true`. + * The character(s) added are `\n` or `\r\n`, depending on the platform. This defaults to `true`. */ sendText(text: string, shouldExecute?: boolean): void; From de7da9f4af0c7c440bddd46a6fb0a47781fa6371 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 26 Feb 2024 11:29:37 -0800 Subject: [PATCH 0195/1175] Improve creation of text models for chat code blocks (#205943) * Improve creation of text models for chat code blocks Refactors the chat code block logic to better support cross code blocks IntelliSense. Previously we only created text models for the visible editors in chat. With this new approach, we instead create a unique text model for each code block in the conversation. This allows us our IntelliSense features to work even if a code block is not visible in chat Also uses this as a change to remove some duplicate I introduced to support local file editors in chat Still a draft as the text model creation should be moved out of the chat list renderer * Move model updating logic into view model * Small cleanup --- .../contrib/chat/browser/chatListRenderer.ts | 115 +++--- .../contrib/chat/browser/chatWidget.ts | 65 +++- .../contrib/chat/browser/codeBlockPart.ts | 337 +++++------------- .../contrib/chat/common/chatViewModel.ts | 99 ++++- .../chat/common/codeBlockModelCollection.ts | 53 +++ .../inlineChat/browser/inlineChatWidget.ts | 7 +- 6 files changed, 360 insertions(+), 316 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index c0f1827014a..93b651276e5 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -21,7 +21,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; import { FuzzyScore } from 'vs/base/common/filters'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; -import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, IReference, toDisposable } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; import { marked } from 'vs/base/common/marked/marked'; import { FileAccess, Schemas, matchesSomeScheme } from 'vs/base/common/network'; @@ -30,10 +30,9 @@ import { basename } from 'vs/base/common/path'; import { equalsIgnoreCase } from 'vs/base/common/strings'; import { ThemeIcon } from 'vs/base/common/themables'; import { URI } from 'vs/base/common/uri'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IMarkdownRenderResult, MarkdownRenderer } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer'; import { Range } from 'vs/editor/common/core/range'; +import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; import { localize } from 'vs/nls'; import { IMenuEntryActionViewItemOptions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; @@ -41,7 +40,6 @@ import { MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; 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 { ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; import { FileKind, FileType } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -56,9 +54,9 @@ 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 { ChatMarkdownDecorationsRenderer, annotateSpecialMarkdownContent, extractVulnerabilitiesFromText } from 'vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer'; +import { ChatMarkdownDecorationsRenderer, IMarkdownVulnerability, annotateSpecialMarkdownContent, extractVulnerabilitiesFromText } from 'vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer'; import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions'; -import { ChatCodeBlockContentProvider, ICodeBlockData, ICodeBlockPart, LocalFileCodeBlockPart, SimpleCodeBlockPart, localFileLanguageId, parseLocalFileData } from 'vs/workbench/contrib/chat/browser/codeBlockPart'; +import { ChatCodeBlockContentProvider, CodeBlockPart, ICodeBlockData, ICodeBlockPart, localFileLanguageId, parseLocalFileData } 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 { IChatProgressRenderableResponseContent } from 'vs/workbench/contrib/chat/common/chatModel'; @@ -68,6 +66,7 @@ import { IChatProgressMessageRenderData, IChatRenderData, IChatResponseMarkdownR import { IWordCountResult, getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; import { createFileIconThemableTreeContainerScope } from 'vs/workbench/contrib/files/browser/views/explorerView'; import { IFilesConfiguration } from 'vs/workbench/contrib/files/common/files'; +import { CodeBlockModelCollection } from '../common/codeBlockModelCollection'; const $ = dom.$; @@ -133,47 +132,30 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer => { - if (input.resource.scheme !== Schemas.vscodeChatCodeBlock) { - return null; - } - const block = this._editorPool.find(input.resource); - if (!block) { - return null; - } - if (input.options?.selection) { - block.editor.setSelection({ - startLineNumber: input.options.selection.startLineNumber, - startColumn: input.options.selection.startColumn, - endLineNumber: input.options.selection.startLineNumber ?? input.options.selection.endLineNumber, - endColumn: input.options.selection.startColumn ?? input.options.selection.endColumn - }); - } - return block.editor; - })); - this._usedReferencesEnabled = configService.getValue('chat.experimental.usedReferences') ?? true; this._register(configService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('chat.experimental.usedReferences')) { @@ -186,6 +168,10 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { - let data: ICodeBlockData; + const index = codeBlockIndex++; + let textModel: Promise>; + let range: Range | undefined; + let vulns: readonly IMarkdownVulnerability[] | undefined; if (equalsIgnoreCase(languageId, localFileLanguageId)) { try { const parsedBody = parseLocalFileData(text); - data = { type: 'localFile', uri: parsedBody.uri, range: parsedBody.range && Range.lift(parsedBody.range), codeBlockIndex: codeBlockIndex++, element, hideToolbar: false, parentContextKeyService: templateData.contextKeyService }; + range = parsedBody.range && Range.lift(parsedBody.range); + textModel = this.textModelService.createModelReference(parsedBody.uri); } catch (e) { - console.error(e); return $('div'); } } else { - const vulns = extractVulnerabilitiesFromText(text); - const hideToolbar = isResponseVM(element) && element.errorDetails?.responseIsFiltered; - data = { type: 'code', languageId, text: vulns.newText, codeBlockIndex: codeBlockIndex++, element, hideToolbar, parentContextKeyService: templateData.contextKeyService, vulns: vulns.vulnerabilities }; + const blockModel = this.codeBlockModelCollection.get(element.id, index); + if (!blockModel) { + console.error('Trying to render code block without model', element.id, index); + return $('div'); + } + + textModel = blockModel; + const extractedVulns = extractVulnerabilitiesFromText(text); + vulns = extractedVulns.vulnerabilities; + textModel.then(ref => ref.object.textEditorModel.setValue(extractedVulns.newText)); } - const ref = this.renderCodeBlock(data); + const hideToolbar = isResponseVM(element) && element.errorDetails?.responseIsFiltered; + const ref = this.renderCodeBlock({ languageId, textModel, codeBlockIndex: index, element, range, hideToolbar, parentContextKeyService: templateData.contextKeyService, vulns }); // Attach this after updating text/layout of the editor, so it should only be fired when the size updates later (horizontal scrollbar, wrapping) // not during a renderElement OR a progressive render (when we will be firing this event anyway at the end of the render) @@ -899,15 +896,18 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer this.codeBlocksByEditorUri.delete(ref.object.uri))); + if (ref.object.uri) { + const uri = ref.object.uri; + this.codeBlocksByEditorUri.set(uri, info); + disposables.add(toDisposable(() => this.codeBlocksByEditorUri.delete(uri))); + } } orderedDisposablesList.push(ref); return ref.object.element; @@ -933,7 +933,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { - const ref = this._editorPool.get(data); + const ref = this._editorPool.get(); const editorInfo = ref.object; editorInfo.render(data, this._currentLayoutWidth); @@ -1068,41 +1068,28 @@ interface IDisposableReference extends IDisposable { isStale: () => boolean; } -class EditorPool extends Disposable { +export class EditorPool extends Disposable { - private readonly _simpleEditorPool: ResourcePool; - private readonly _localFileEditorPool: ResourcePool; + private readonly _pool: ResourcePool; - public *inUse(): Iterable { - yield* this._simpleEditorPool.inUse; - yield* this._localFileEditorPool.inUse; + public inUse(): Iterable { + return this._pool.inUse; } constructor( - private readonly options: ChatEditorOptions, + options: ChatEditorOptions, delegate: IChatRendererDelegate, overflowWidgetsDomNode: HTMLElement | undefined, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, ) { super(); - this._simpleEditorPool = this._register(new ResourcePool(() => { - return this.instantiationService.createInstance(SimpleCodeBlockPart, this.options, MenuId.ChatCodeBlock, delegate, overflowWidgetsDomNode); + this._pool = this._register(new ResourcePool(() => { + return instantiationService.createInstance(CodeBlockPart, options, MenuId.ChatCodeBlock, delegate, overflowWidgetsDomNode); })); - this._localFileEditorPool = this._register(new ResourcePool(() => { - return this.instantiationService.createInstance(LocalFileCodeBlockPart, this.options, MenuId.ChatCodeBlock, delegate, overflowWidgetsDomNode); - })); - } - - get(data: ICodeBlockData): IDisposableReference { - return this.getFromPool(data.type === 'localFile' ? this._localFileEditorPool : this._simpleEditorPool); - } - - find(resource: URI): SimpleCodeBlockPart | undefined { - return Array.from(this._simpleEditorPool.inUse).find(part => part.uri?.toString() === resource.toString()); } - private getFromPool(pool: ResourcePool): IDisposableReference { - const codeBlock = pool.get(); + get(): IDisposableReference { + const codeBlock = this._pool.get(); let stale = false; return { object: codeBlock, @@ -1110,7 +1097,7 @@ class EditorPool extends Disposable { dispose: () => { codeBlock.reset(); stale = true; - pool.release(codeBlock); + this._pool.release(codeBlock); } }; } diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index ab61b6bde4f..b2e1680a28c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -5,37 +5,41 @@ import * as dom from 'vs/base/browser/dom'; import { ITreeContextMenuEvent, ITreeElement } from 'vs/base/browser/ui/tree/tree'; -import { disposableTimeout } from 'vs/base/common/async'; +import { disposableTimeout, timeout } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter } from 'vs/base/common/event'; import { Disposable, DisposableStore, IDisposable, MutableDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; import { isDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/chat'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { MenuId } from 'vs/platform/actions/common/actions'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; import { ILogService } from 'vs/platform/log/common/log'; -import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ChatTreeItem, IChatAccessibilityService, IChatCodeBlockInfo, IChatFileTreeInfo, IChatWidget, IChatWidgetService, IChatWidgetViewContext, IChatWidgetViewOptions } from 'vs/workbench/contrib/chat/browser/chat'; 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 { IChatAgentCommand, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; 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 { IChatFollowup, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; -import { ChatViewModel, IChatResponseViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IParsedChatRequest, chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser'; -import { IChatAgentCommand, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatFollowup, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; +import { ChatViewModel, IChatResponseViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; +import { CodeBlockModelCollection } from 'vs/workbench/contrib/chat/common/codeBlockModelCollection'; +import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; const $ = dom.$; @@ -98,6 +102,7 @@ export class ChatWidget extends Disposable implements IChatWidget { private tree!: WorkbenchObjectTree; private renderer!: ChatListItemRenderer; + private readonly _codeBlockModelCollection: CodeBlockModelCollection; private inputPart!: ChatInputPart; private editorOptions!: ChatEditorOptions; @@ -149,6 +154,7 @@ export class ChatWidget extends Disposable implements IChatWidget { readonly viewContext: IChatWidgetViewContext, private readonly viewOptions: IChatWidgetViewOptions, private readonly styles: IChatWidgetStyles, + @ICodeEditorService codeEditorService: ICodeEditorService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IChatService private readonly chatService: IChatService, @@ -158,13 +164,51 @@ export class ChatWidget extends Disposable implements IChatWidget { @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILogService private readonly _logService: ILogService, - @IThemeService private readonly _themeService: IThemeService + @IThemeService private readonly _themeService: IThemeService, ) { super(); CONTEXT_IN_CHAT_SESSION.bindTo(contextKeyService).set(true); this.requestInProgress = CONTEXT_CHAT_REQUEST_IN_PROGRESS.bindTo(contextKeyService); this._register((chatWidgetService as ChatWidgetService).register(this)); + + this._codeBlockModelCollection = this._register(instantiationService.createInstance(CodeBlockModelCollection)); + + this._register(codeEditorService.registerCodeEditorOpenHandler(async (input: ITextResourceEditorInput, _source: ICodeEditor | null, _sideBySide?: boolean): Promise => { + if (input.resource.scheme !== Schemas.vscodeChatCodeBlock) { + return null; + } + + const responseId = input.resource.path.split('/').at(1); + if (!responseId) { + return null; + } + + const item = this.viewModel?.getItems().find(item => item.id === responseId); + if (!item) { + return null; + } + + this.reveal(item); + + await timeout(0); // wait for list to actually render + + for (const editor of this.renderer.editorsInUse() ?? []) { + if (editor.uri?.toString() === input.resource.toString()) { + const inner = editor.editor; + if (input.options?.selection) { + inner.setSelection({ + startLineNumber: input.options.selection.startLineNumber, + startColumn: input.options.selection.startColumn, + endLineNumber: input.options.selection.startLineNumber ?? input.options.selection.endLineNumber, + endColumn: input.options.selection.startColumn ?? input.options.selection.endColumn + }); + } + return inner; + } + } + return null; + })); } get supportsFileReferences(): boolean { @@ -340,6 +384,7 @@ export class ChatWidget extends Disposable implements IChatWidget { this.editorOptions, options, rendererDelegate, + this._codeBlockModelCollection, overflowWidgetsContainer, )); this._register(this.renderer.onDidClickFollowup(item => { @@ -490,8 +535,10 @@ export class ChatWidget extends Disposable implements IChatWidget { throw new Error('Call render() before setModel()'); } + this._codeBlockModelCollection.clear(); + this.container.setAttribute('data-session-id', model.sessionId); - this.viewModel = this.instantiationService.createInstance(ChatViewModel, model); + this.viewModel = this.instantiationService.createInstance(ChatViewModel, model, this._codeBlockModelCollection); this.viewModelDisposables.add(this.viewModel.onDidChange(e => { this.requestInProgress.set(this.viewModel!.requestInProgress); this.onDidChangeItems(); @@ -757,6 +804,8 @@ export class ChatWidget extends Disposable implements IChatWidget { this.inputPart.saveState(); return { inputValue: this.getInput(), inputState: this.collectInputState() }; } + + } export class ChatWidgetService implements IChatWidgetService { diff --git a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts index 8669cadcce7..b6d4500e448 100644 --- a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts +++ b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts @@ -9,18 +9,15 @@ import * as dom from 'vs/base/browser/dom'; import { Button } from 'vs/base/browser/ui/button/button'; import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IReference, MutableDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IReference } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { generateUuid } from 'vs/base/common/uuid'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { EDITOR_FONT_DEFAULTS, EditorOption, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; -import { ILanguageService } from 'vs/editor/common/languages/language'; -import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; import { EndOfLinePreference, ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/model'; import { IResolvedTextEditorModel, ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -50,27 +47,19 @@ import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/ const $ = dom.$; -interface ICodeBlockDataCommon { - codeBlockIndex: number; - element: unknown; - parentContextKeyService?: IContextKeyService; - hideToolbar?: boolean; -} +export interface ICodeBlockData { + readonly codeBlockIndex: number; + readonly element: unknown; -export interface ISimpleCodeBlockData extends ICodeBlockDataCommon { - type: 'code'; - text: string; - languageId: string; - vulns?: IMarkdownVulnerability[]; -} + readonly textModel: Promise>; + readonly languageId: string; -export interface ILocalFileCodeBlockData extends ICodeBlockDataCommon { - type: 'localFile'; - uri: URI; - range?: Range; -} + readonly vulns?: readonly IMarkdownVulnerability[]; + readonly range?: Range; -export type ICodeBlockData = ISimpleCodeBlockData | ILocalFileCodeBlockData; + readonly parentContextKeyService?: IContextKeyService; + readonly hideToolbar?: boolean; +} /** * Special markdown code block language id used to render a local file. @@ -118,19 +107,20 @@ export interface ICodeBlockActionContext { } -export interface ICodeBlockPart { +export interface ICodeBlockPart { + readonly editor: CodeEditorWidget; readonly onDidChangeContentHeight: Event; readonly element: HTMLElement; - readonly uri: URI; + readonly uri: URI | undefined; layout(width: number): void; - render(data: Data, width: number): Promise; + render(data: ICodeBlockData, width: number): Promise; focus(): void; reset(): unknown; dispose(): void; } const defaultCodeblockPadding = 10; -abstract class BaseCodeBlockPart extends Disposable implements ICodeBlockPart { +export class CodeBlockPart extends Disposable implements ICodeBlockPart { protected readonly _onDidChangeContentHeight = this._register(new Emitter()); public readonly onDidChangeContentHeight = this._onDidChangeContentHeight.event; @@ -138,9 +128,12 @@ abstract class BaseCodeBlockPart extends Disposable protected readonly toolbar: MenuWorkbenchToolBar; private readonly contextKeyService: IContextKeyService; - abstract readonly uri: URI; public readonly element: HTMLElement; + private readonly vulnsButton: Button; + private readonly vulnsListElement: HTMLElement; + + private currentCodeBlockData: ICodeBlockData | undefined; private currentScrollWidth = 0; constructor( @@ -152,7 +145,7 @@ abstract class BaseCodeBlockPart extends Disposable @IContextKeyService contextKeyService: IContextKeyService, @IModelService protected readonly modelService: IModelService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IAccessibilityService private readonly accessibilityService: IAccessibilityService + @IAccessibilityService private readonly accessibilityService: IAccessibilityService, ) { super(); this.element = $('.interactive-result-code-block'); @@ -173,6 +166,13 @@ abstract class BaseCodeBlockPart extends Disposable scrollbar: { alwaysConsumeMouseWheel: false }, + definitionLinkOpensInPeek: false, + gotoLocation: { + multiple: 'goto', + multipleDeclarations: 'goto', + multipleDefinitions: 'goto', + multipleImplementations: 'goto', + }, ariaLabel: localize('chat.codeBlockHelp', 'Code block'), overflowWidgetsDomNode, ...this.getEditorOptionsFromConfig(), @@ -187,6 +187,31 @@ abstract class BaseCodeBlockPart extends Disposable } })); + 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); })); @@ -229,7 +254,27 @@ abstract class BaseCodeBlockPart extends Disposable } } - protected abstract createEditor(instantiationService: IInstantiationService, parent: HTMLElement, options: Readonly): CodeEditorWidget; + get uri(): URI | undefined { + return this.editor.getModel()?.uri; + } + + private createEditor(instantiationService: IInstantiationService, parent: HTMLElement, options: Readonly): CodeEditorWidget { + return this._register(instantiationService.createInstance(CodeEditorWidget, parent, options, { + isSimpleWidget: false, + contributions: EditorExtensionsRegistry.getSomeEditorContributions([ + MenuPreventer.ID, + SelectionClipboardContributionID, + ContextMenuController.ID, + + WordHighlighterContribution.ID, + ViewportSemanticTokensContribution.ID, + BracketMatchingController.ID, + SmartSelectController.ID, + HoverController.ID, + GotoDefinitionAtPositionEditorContribution.ID, + ]) + })); + } focus(): void { this.editor.focus(); @@ -277,17 +322,22 @@ abstract class BaseCodeBlockPart extends Disposable this.updatePaddingForLayout(); } - protected getContentHeight() { + private getContentHeight() { + if (this.currentCodeBlockData?.range) { + const lineCount = this.currentCodeBlockData.range.endLineNumber - this.currentCodeBlockData.range.startLineNumber + 1; + const lineHeight = this.editor.getOption(EditorOption.lineHeight); + return lineCount * lineHeight; + } return this.editor.getContentHeight(); } - async render(data: Data, width: number) { + async render(data: ICodeBlockData, width: number) { if (data.parentContextKeyService) { this.contextKeyService.updateParent(data.parentContextKeyService); } if (this.options.configuration.resultEditor.wordWrap === 'on') { - // Intialize the editor with the new proper width so that getContentHeight + // Initialize the editor with the new proper width so that getContentHeight // will be computed correctly in the next call to layout() this.layout(width); } @@ -302,102 +352,6 @@ abstract class BaseCodeBlockPart extends Disposable } else { dom.show(this.toolbar.getElement()); } - } - - protected abstract updateEditor(data: Data): void | Promise; - - reset() { - this.clearWidgets(); - } - - private clearWidgets() { - HoverController.get(this.editor)?.hideContentHover(); - } -} - - -export class SimpleCodeBlockPart extends BaseCodeBlockPart { - - private readonly vulnsButton: Button; - private readonly vulnsListElement: HTMLElement; - - private currentCodeBlockData: ISimpleCodeBlockData | undefined; - - private readonly textModel: Promise; - - private readonly _uri: URI; - - constructor( - options: ChatEditorOptions, - menuId: MenuId, - delegate: IChatRendererDelegate, - overflowWidgetsDomNode: HTMLElement | undefined, - @IInstantiationService instantiationService: IInstantiationService, - @IContextKeyService contextKeyService: IContextKeyService, - @IModelService modelService: IModelService, - @ITextModelService textModelService: ITextModelService, - @IConfigurationService configurationService: IConfigurationService, - @IAccessibilityService accessibilityService: IAccessibilityService, - @ILanguageService private readonly languageService: ILanguageService, - ) { - super(options, menuId, delegate, overflowWidgetsDomNode, instantiationService, contextKeyService, modelService, configurationService, accessibilityService); - - 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._uri = URI.from({ scheme: Schemas.vscodeChatCodeBlock, path: generateUuid() }); - this.textModel = textModelService.createModelReference(this._uri).then(ref => { - this.editor.setModel(ref.object.textEditorModel); - this._register(ref); - return ref.object.textEditorModel; - }); - - 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); - }); - } - - get uri(): URI { - return this._uri; - } - - protected override createEditor(instantiationService: IInstantiationService, parent: HTMLElement, options: Readonly): CodeEditorWidget { - return this._register(instantiationService.createInstance(CodeEditorWidget, parent, options, { - isSimpleWidget: false, - contributions: EditorExtensionsRegistry.getSomeEditorContributions([ - MenuPreventer.ID, - SelectionClipboardContributionID, - ContextMenuController.ID, - - WordHighlighterContribution.ID, - ViewportSemanticTokensContribution.ID, - BracketMatchingController.ID, - SmartSelectController.ID, - HoverController.ID, - GotoDefinitionAtPositionEditorContribution.ID, - ]) - })); - } - - override async render(data: ISimpleCodeBlockData, width: number): Promise { - await super.render(data, width); if (data.vulns?.length && isResponseVM(data.element)) { dom.clearNode(this.vulnsListElement); @@ -410,20 +364,27 @@ export class SimpleCodeBlockPart extends BaseCodeBlockPart } } - protected override async updateEditor(data: ISimpleCodeBlockData): Promise { - this.editor.setModel(await this.textModel); - const text = this.fixCodeText(data.text, data.languageId); - this.setText(text); + reset() { + this.clearWidgets(); + } + + private clearWidgets() { + HoverController.get(this.editor)?.hideContentHover(); + } - const vscodeLanguageId = this.languageService.getLanguageIdByLanguageName(data.languageId) ?? undefined; - this.setLanguage(vscodeLanguageId); - data.languageId = vscodeLanguageId ?? 'plaintext'; + private async updateEditor(data: ICodeBlockData): Promise { + const textModel = (await data.textModel).object.textEditorModel; + this.editor.setModel(textModel); + if (data.range) { + this.editor.setSelection(data.range); + this.editor.revealRangeInCenter(data.range, ScrollType.Immediate); + } this.toolbar.context = { - code: data.text, + code: textModel.getTextBuffer().getValueInRange(data.range ?? textModel.getFullModelRange(), EndOfLinePreference.TextDefined), codeBlockIndex: data.codeBlockIndex, element: data.element, - languageId: data.languageId + languageId: textModel.getLanguageId() } satisfies ICodeBlockActionContext; } @@ -438,110 +399,8 @@ export class SimpleCodeBlockPart extends BaseCodeBlockPart 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 { - if (languageId === 'php') { - if (!text.trim().startsWith('<')) { - return ``; - } - } - - return text; - } - - private async setText(newText: string): Promise { - const model = await this.textModel; - const currentText = model.getValue(EndOfLinePreference.LF); - if (newText === currentText) { - return; - } - - if (newText.startsWith(currentText)) { - const text = newText.slice(currentText.length); - const lastLine = model.getLineCount(); - const lastCol = model.getLineMaxColumn(lastLine); - model.applyEdits([{ range: new Range(lastLine, lastCol, lastLine, lastCol), text }]); - } else { - // console.log(`Failed to optimize setText`); - model.setValue(newText); - } - } - - private async setLanguage(vscodeLanguageId: string | undefined): Promise { - (await this.textModel).setLanguage(vscodeLanguageId ?? PLAINTEXT_LANGUAGE_ID); - } } -export class LocalFileCodeBlockPart extends BaseCodeBlockPart { - - private readonly textModelReference = this._register(new MutableDisposable>()); - private currentCodeBlockData?: ILocalFileCodeBlockData; - - constructor( - options: ChatEditorOptions, - menuId: MenuId, - delegate: IChatRendererDelegate, - overflowWidgetsDomNode: HTMLElement | undefined, - @IInstantiationService instantiationService: IInstantiationService, - @IContextKeyService contextKeyService: IContextKeyService, - @IModelService modelService: IModelService, - @ITextModelService private readonly textModelService: ITextModelService, - @IConfigurationService configurationService: IConfigurationService, - @IAccessibilityService accessibilityService: IAccessibilityService - ) { - super(options, menuId, delegate, overflowWidgetsDomNode, instantiationService, contextKeyService, modelService, configurationService, accessibilityService); - } - - get uri(): URI { - return this.currentCodeBlockData!.uri; - } - - protected override getContentHeight() { - if (this.currentCodeBlockData?.range) { - const lineCount = this.currentCodeBlockData.range.endLineNumber - this.currentCodeBlockData.range.startLineNumber + 1; - const lineHeight = this.editor.getOption(EditorOption.lineHeight); - return lineCount * lineHeight; - } - return super.getContentHeight(); - } - - protected override createEditor(instantiationService: IInstantiationService, parent: HTMLElement, options: Readonly): CodeEditorWidget { - return this._register(instantiationService.createInstance(CodeEditorWidget, parent, { - ...options, - }, { - // TODO: be more selective about contributions - })); - } - - protected override async updateEditor(data: ILocalFileCodeBlockData): Promise { - let model: ITextModel; - if (this.currentCodeBlockData?.uri.toString() === data.uri.toString()) { - this.currentCodeBlockData = data; - model = this.editor.getModel()!; - } else { - this.currentCodeBlockData = data; - const result = await this.textModelService.createModelReference(data.uri); - model = result.object.textEditorModel; - this.textModelReference.value = result; - this.editor.setModel(model); - } - - - if (data.range) { - this.editor.setSelection(data.range); - this.editor.revealRangeInCenter(data.range, ScrollType.Immediate); - } - - this.toolbar.context = { - code: model.getTextBuffer().getValueInRange(data.range ?? model.getFullModelRange(), EndOfLinePreference.TextDefined), - codeBlockIndex: data.codeBlockIndex, - element: data.element, - languageId: model.getLanguageId() - } satisfies ICodeBlockActionContext; - } -} - - export class ChatCodeBlockContentProvider extends Disposable implements ITextModelContentProvider { constructor( diff --git a/src/vs/workbench/contrib/chat/common/chatViewModel.ts b/src/vs/workbench/contrib/chat/common/chatViewModel.ts index a91512a6840..fc1328ca803 100644 --- a/src/vs/workbench/contrib/chat/common/chatViewModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatViewModel.ts @@ -5,14 +5,19 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { marked } from 'vs/base/common/marked/marked'; import { URI } from 'vs/base/common/uri'; +import { Range } from 'vs/editor/common/core/range'; +import { ILanguageService } from 'vs/editor/common/languages/language'; +import { EndOfLinePreference } from 'vs/editor/common/model'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { IChatAgentCommand, IChatAgentData, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatModelInitState, IChatModel, IChatRequestModel, IChatResponseModel, IChatWelcomeMessageContent, IResponse } from 'vs/workbench/contrib/chat/common/chatModel'; import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChatContentReference, IChatProgressMessage, IChatFollowup, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatUsedContext, InteractiveSessionVoteDirection, IChatCommandButton } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatCommandButton, IChatContentReference, IChatFollowup, IChatProgressMessage, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatUsedContext, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; +import { CodeBlockModelCollection } from './codeBlockModelCollection'; export function isRequestVM(item: unknown): item is IChatRequestViewModel { return !!item && typeof item === 'object' && 'message' in item; @@ -134,6 +139,7 @@ export interface IChatResponseViewModel { } export class ChatViewModel extends Disposable implements IChatViewModel { + private readonly _onDidDisposeModel = this._register(new Emitter()); readonly onDidDisposeModel = this._onDidDisposeModel.event; @@ -179,12 +185,17 @@ export class ChatViewModel extends Disposable implements IChatViewModel { constructor( private readonly _model: IChatModel, + public readonly codeBlockModelCollection: CodeBlockModelCollection, @IInstantiationService private readonly instantiationService: IInstantiationService, + @ILanguageService private readonly languageService: ILanguageService, ) { super(); _model.getRequests().forEach((request, i) => { - this._items.push(new ChatRequestViewModel(request)); + const requestModel = new ChatRequestViewModel(request); + this._items.push(requestModel); + this.updateCodeBlockTextModels(requestModel); + if (request.response) { this.onAddResponse(request.response); } @@ -193,7 +204,10 @@ export class ChatViewModel extends Disposable implements IChatViewModel { this._register(_model.onDidDispose(() => this._onDidDisposeModel.fire())); this._register(_model.onDidChange(e => { if (e.kind === 'addRequest') { - this._items.push(new ChatRequestViewModel(e.request)); + const requestModel = new ChatRequestViewModel(e.request); + this._items.push(requestModel); + this.updateCodeBlockTextModels(requestModel); + if (e.request.response) { this.onAddResponse(e.request.response); } @@ -224,8 +238,12 @@ export class ChatViewModel extends Disposable implements IChatViewModel { private onAddResponse(responseModel: IChatResponseModel) { const response = this.instantiationService.createInstance(ChatResponseViewModel, responseModel); - this._register(response.onDidChange(() => this._onDidChange.fire(null))); + this._register(response.onDidChange(() => { + this.updateCodeBlockTextModels(response); + return this._onDidChange.fire(null); + })); this._items.push(response); + this.updateCodeBlockTextModels(response); } getItems(): (IChatRequestViewModel | IChatResponseViewModel | IChatWelcomeMessageViewModel)[] { @@ -238,6 +256,79 @@ export class ChatViewModel extends Disposable implements IChatViewModel { .filter((item): item is ChatResponseViewModel => item instanceof ChatResponseViewModel) .forEach((item: ChatResponseViewModel) => item.dispose()); } + + private updateCodeBlockTextModels(model: IChatRequestViewModel | IChatResponseViewModel) { + const content = isRequestVM(model) ? model.messageText : model.response.asString(); + const renderer = new marked.Renderer(); + + let codeBlockIndex = 0; + renderer.code = (value, languageId) => { + languageId ??= ''; + const newText = this.fixCodeText(value, languageId); + const textModel = this.codeBlockModelCollection.getOrCreate(model.id, codeBlockIndex++); + textModel.then(ref => { + const model = ref.object.textEditorModel; + if (languageId) { + const vscodeLanguageId = this.languageService.getLanguageIdByLanguageName(languageId); + if (vscodeLanguageId && vscodeLanguageId !== ref.object.textEditorModel.getLanguageId()) { + ref.object.textEditorModel.setLanguage(vscodeLanguageId); + } + } + + const currentText = ref.object.textEditorModel.getValue(EndOfLinePreference.LF); + if (newText === currentText) { + return; + } + + if (newText.startsWith(currentText)) { + const text = newText.slice(currentText.length); + const lastLine = model.getLineCount(); + const lastCol = model.getLineMaxColumn(lastLine); + model.applyEdits([{ range: new Range(lastLine, lastCol, lastLine, lastCol), text }]); + } else { + // console.log(`Failed to optimize setText`); + model.setValue(newText); + } + }); + return ''; + }; + + marked.parse(this.ensureFencedCodeBlocksTerminated(content), { renderer }); + } + + private fixCodeText(text: string, languageId: string): string { + if (languageId === 'php') { + if (!text.trim().startsWith('<')) { + return ``; + } + } + + return text; + } + + /** + * Marked doesn't consistently render fenced code blocks that aren't terminated. + * + * Try to close them ourselves to workaround this. + */ + private ensureFencedCodeBlocksTerminated(content: string): string { + const lines = content.split('\n'); + let inCodeBlock = false; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (line.startsWith('```')) { + inCodeBlock = !inCodeBlock; + } + } + + // If we're still in a code block at the end of the content, add a closing fence + if (inCodeBlock) { + lines.push('```'); + } + + return lines.join('\n'); + } } export class ChatRequestViewModel implements IChatRequestViewModel { diff --git a/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts b/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts new file mode 100644 index 00000000000..edf5b4ae445 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.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 { Disposable, IReference } from 'vs/base/common/lifecycle'; +import { ResourceMap } from 'vs/base/common/map'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; + + +export class CodeBlockModelCollection extends Disposable { + + private readonly _models = new ResourceMap>>(); + + constructor( + @ITextModelService private readonly textModelService: ITextModelService + ) { + super(); + } + + public override dispose(): void { + super.dispose(); + this.clear(); + } + + get(responseId: string, codeBlockIndex: number): Promise> | undefined { + const uri = this.getUri(responseId, codeBlockIndex); + return this._models.get(uri); + } + + getOrCreate(responseId: string, codeBlockIndex: number): Promise> { + const existing = this.get(responseId, codeBlockIndex); + if (existing) { + return existing; + } + + const uri = this.getUri(responseId, codeBlockIndex); + const ref = this.textModelService.createModelReference(uri); + this._models.set(uri, ref); + return ref; + } + + clear(): void { + this._models.forEach(async (model) => (await model).dispose()); + this._models.clear(); + } + + private getUri(responseId: string, index: number): URI { + return URI.from({ scheme: Schemas.vscodeChatCodeBlock, path: `/${responseId}/${index}` }); + } +} diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 04d8fe1ff4f..5af8f2acc53 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -62,6 +62,7 @@ import { SlashCommandContentWidget } from 'vs/workbench/contrib/chat/browser/cha import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatModel, ChatResponseModel } from 'vs/workbench/contrib/chat/common/chatModel'; import { ChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; +import { CodeBlockModelCollection } from 'vs/workbench/contrib/chat/common/codeBlockModelCollection'; import { ExpansionState, HunkData, HunkInformation, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { asRange, invertLineRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; import { ACTION_ACCEPT_CHANGES, ACTION_REGENERATE_RESPONSE, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_END, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_INNER_CURSOR_START, CTX_INLINE_CHAT_MESSAGE_CROP_STATE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_VISIBLE, IInlineChatFollowup, IInlineChatSlashCommand, MENU_INLINE_CHAT_INPUT, MENU_INLINE_CHAT_WIDGET, MENU_INLINE_CHAT_WIDGET_FEEDBACK, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, MENU_INLINE_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; @@ -240,6 +241,7 @@ export class InlineChatWidget { private _slashCommandUsedDisposables = this._store.add(new DisposableStore()); private _chatMessage: MarkdownString | undefined; + private readonly _codeBlockModelCollection: CodeBlockModelCollection; constructor( private readonly parentEditor: ICodeEditor, @@ -451,6 +453,9 @@ export class InlineChatWidget { this._elements.followUps.ariaLabel = this._accessibleViewService.getOpenAriaHint(AccessibilityVerbositySettingId.InlineChat); } })); + + // Code block rendering + this._codeBlockModelCollection = this._store.add(this._instantiationService.createInstance(CodeBlockModelCollection)); } @@ -615,7 +620,7 @@ export class InlineChatWidget { 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, undefined)); + const renderer = this._chatMessageDisposables.add(this._instantiationService.createInstance(ChatListItemRenderer, this._editorOptions, renderOptions, chatRendererDelegate, this._codeBlockModelCollection, undefined)); 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); From f9377b87afb68830547acf13a81119b6e07f1b5b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 26 Feb 2024 11:29:51 -0800 Subject: [PATCH 0196/1175] clearMarker -> clear --- src/vs/workbench/contrib/terminal/browser/terminal.ts | 2 +- .../contrib/terminal/browser/xterm/markNavigationAddon.ts | 2 +- .../terminalContrib/links/browser/terminalLinkQuickpick.ts | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index d8d7ebeb111..db2791f9bda 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -113,7 +113,7 @@ export interface IMarkTracker { selectToNextMark(): void; selectToPreviousLine(): void; selectToNextLine(): void; - clearMarker(): void; + clear(): void; scrollToClosestMarker(startMarkerId: string, endMarkerId?: string, highlight?: boolean | undefined): void; scrollToLine(line: number, position: ScrollPosition): void; diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts index aae4ac6f275..9a2fbf8e6d7 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts @@ -101,7 +101,7 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe return undefined; } - clearMarker(): void { + clear(): void { // Clear the current marker so successive focus/selection actions are performed from the // bottom of the buffer this._currentMarker = Boundary.Bottom; diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts index 544612bcb53..e3bd74aec3b 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts @@ -140,8 +140,7 @@ export class TerminalLinkQuickpick extends DisposableStore { const markTracker = this._instance?.xterm?.markTracker; if (markTracker) { markTracker.restoreScrollState(); - // TODO: This name isn't great - markTracker.clearMarker(); + markTracker.clear(); this._terminalScrollStateSaved = false; } } From 12633b44242d02ff0079ddf5c12272669d1c19db Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 26 Feb 2024 11:58:57 -0800 Subject: [PATCH 0197/1175] Ignore links with text that equals the empty string fixes #206258 --- .../terminalContrib/links/browser/terminalLinkParsing.ts | 5 +++++ .../links/test/browser/terminalLinkParsing.test.ts | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing.ts index 8328315b957..94dbbd2f5cd 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing.ts @@ -277,6 +277,11 @@ function detectLinksViaSuffix(line: string): IParsedLink[] { }; path = path.substring(prefix.text.length); + // Don't allow suffix links to be returned when the link itself is the empty string + if (path.trim().length === 0) { + continue; + } + // If there are multiple characters in the prefix, trim the prefix if the _first_ // suffix character is the same as the last prefix character. For example, for the // text `echo "'foo' on line 1"`: diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts index 71d641d0739..04e4aeb5e9b 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts @@ -706,5 +706,11 @@ suite('TerminalLinkParsing', () => { }); } }); + suite('should ignore links with suffixes when the path itself is the empty string', () => { + deepStrictEqual( + detectLinks('""",1', OperatingSystem.Linux), + [] as IParsedLink[] + ); + }); }); }); From 9b3f22b333b4bb4d065abb787411411494f7c45b Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 26 Feb 2024 12:03:16 -0800 Subject: [PATCH 0198/1175] only allow starting chat when terminal agent has been registered --- .../contrib/terminalContrib/chat/browser/terminalChatActions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 04ff3b3a472..878246a2e26 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -28,6 +28,7 @@ registerActiveXtermAction({ precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + TerminalChatContextKeys.agentRegistered ), run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { From 51209b3a1448ff6df84c973ca1ab786fa3a99d72 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 26 Feb 2024 12:26:30 -0800 Subject: [PATCH 0199/1175] on hide, reset input value --- .../contrib/terminalContrib/chat/browser/terminalChatWidget.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index aa43972ef64..74bb3b4ee9d 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -132,6 +132,7 @@ export class TerminalChatWidget extends Disposable { this._inlineChatWidget.updateToolbar(false); this._focusedContextKey.set(false); this._visibleContextKey.set(false); + this._inlineChatWidget.value = ''; this._instance.focus(); } focus(): void { From a1070cb7f172f4930edfe3aa38f7b473c85169f4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 26 Feb 2024 12:40:34 -0800 Subject: [PATCH 0200/1175] set vertical position to below cursor line --- .../terminalContrib/chat/browser/terminalChatWidget.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 74bb3b4ee9d..26efec69639 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -112,9 +112,9 @@ export class TerminalChatWidget extends Disposable { if (!font?.charHeight) { return; } - const cursorY = this._instance.xterm?.raw.buffer.active.cursorY ?? 0; + const cursorY = (this._instance.xterm?.raw.buffer.active.cursorY ?? 0) + 1; const height = font.charHeight * font.lineHeight; - const top = cursorY * height + 10; + const top = cursorY * height + 12; this._container.style.top = `${top}px`; const terminalHeight = this._instance.domElement.clientHeight; if (terminalHeight && top > terminalHeight - this._inlineChatWidget.getHeight()) { From 6b6482cf322cc31960b4c8007d77b31e120167c6 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 26 Feb 2024 16:12:49 -0800 Subject: [PATCH 0201/1175] fix: command quoting for wt.exe (#206305) Fixes #204039 --- .../externalTerminal/node/externalTerminalService.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/externalTerminal/node/externalTerminalService.ts b/src/vs/platform/externalTerminal/node/externalTerminalService.ts index a8df823266a..5086c95a802 100644 --- a/src/vs/platform/externalTerminal/node/externalTerminalService.ts +++ b/src/vs/platform/externalTerminal/node/externalTerminalService.ts @@ -80,8 +80,7 @@ export class WindowsExternalTerminalService extends ExternalTerminalService impl return new Promise((resolve, reject) => { const title = `"${dir} - ${TERMINAL_TITLE}"`; - const command = `""${args.join('" "')}" & pause"`; // use '|' to only pause on non-zero exit code - + const command = `"${args.join('" "')}" & pause`; // use '|' to only pause on non-zero exit code // merge environment variables into a copy of the process.env const env = Object.assign({}, getSanitizedEnvironment(process), envVars); @@ -110,7 +109,7 @@ export class WindowsExternalTerminalService extends ExternalTerminalService impl cmdArgs = ['-d', '.', exec, '/c', command]; } else { spawnExec = WindowsExternalTerminalService.CMD; - cmdArgs = ['/c', 'start', title, '/wait', exec, '/c', command]; + cmdArgs = ['/c', 'start', title, '/wait', exec, '/c', `"${command}"`]; } const cmd = cp.spawn(spawnExec, cmdArgs, options); From 150d9e622b41ac36e186a6d7196dbaa36ccd5254 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 26 Feb 2024 18:39:38 -0800 Subject: [PATCH 0202/1175] debug: cleanup cancellation tokens in inline value preview (#206307) Also fixes a race where we could have outdated decorations show Refs #205966 --- .../test/browser/mainThreadWorkspace.test.ts | 12 ++++----- .../test/browser/bulkCellEdits.test.ts | 4 +-- .../debug/browser/debugEditorContribution.ts | 27 +++++++++++-------- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/api/test/browser/mainThreadWorkspace.test.ts b/src/vs/workbench/api/test/browser/mainThreadWorkspace.test.ts index 2cc5a637a6a..5234d7abb34 100644 --- a/src/vs/workbench/api/test/browser/mainThreadWorkspace.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadWorkspace.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -41,7 +41,7 @@ suite('MainThreadWorkspace', () => { }); const mtw = disposables.add(instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } }))); - return mtw.$startFileSearch(null, { maxResults: 10, includePattern: 'foo', disregardSearchExcludeSettings: true }, new CancellationTokenSource().token); + return mtw.$startFileSearch(null, { maxResults: 10, includePattern: 'foo', disregardSearchExcludeSettings: true }, CancellationToken.None); }); test('exclude defaults', () => { @@ -63,7 +63,7 @@ suite('MainThreadWorkspace', () => { }); const mtw = disposables.add(instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } }))); - return mtw.$startFileSearch(null, { maxResults: 10, includePattern: '', disregardSearchExcludeSettings: true }, new CancellationTokenSource().token); + return mtw.$startFileSearch(null, { maxResults: 10, includePattern: '', disregardSearchExcludeSettings: true }, CancellationToken.None); }); test('disregard excludes', () => { @@ -84,7 +84,7 @@ suite('MainThreadWorkspace', () => { }); const mtw = disposables.add(instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } }))); - return mtw.$startFileSearch(null, { maxResults: 10, includePattern: '', disregardSearchExcludeSettings: true, disregardExcludeSettings: true }, new CancellationTokenSource().token); + return mtw.$startFileSearch(null, { maxResults: 10, includePattern: '', disregardSearchExcludeSettings: true, disregardExcludeSettings: true }, CancellationToken.None); }); test('do not disregard anything if disregardExcludeSettings is true', () => { @@ -106,7 +106,7 @@ suite('MainThreadWorkspace', () => { }); const mtw = disposables.add(instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } }))); - return mtw.$startFileSearch(null, { maxResults: 10, includePattern: '', disregardExcludeSettings: true, disregardSearchExcludeSettings: false }, new CancellationTokenSource().token); + return mtw.$startFileSearch(null, { maxResults: 10, includePattern: '', disregardExcludeSettings: true, disregardSearchExcludeSettings: false }, CancellationToken.None); }); test('exclude string', () => { @@ -120,6 +120,6 @@ suite('MainThreadWorkspace', () => { }); const mtw = disposables.add(instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } }))); - return mtw.$startFileSearch(null, { maxResults: 10, includePattern: '', excludePattern: 'exclude/**', disregardSearchExcludeSettings: true }, new CancellationTokenSource().token); + return mtw.$startFileSearch(null, { maxResults: 10, includePattern: '', excludePattern: 'exclude/**', disregardSearchExcludeSettings: true }, CancellationToken.None); }); }); diff --git a/src/vs/workbench/contrib/bulkEdit/test/browser/bulkCellEdits.test.ts b/src/vs/workbench/contrib/bulkEdit/test/browser/bulkCellEdits.test.ts index 76497826d74..22c507ca0b3 100644 --- a/src/vs/workbench/contrib/bulkEdit/test/browser/bulkCellEdits.test.ts +++ b/src/vs/workbench/contrib/bulkEdit/test/browser/bulkCellEdits.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { URI } from 'vs/base/common/uri'; import { mockObject } from 'vs/base/test/common/mock'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; @@ -35,7 +35,7 @@ suite('BulkCellEdits', function () { const edits = [ new ResourceNotebookCellEdit(inputUri, { index: 0, count: 1, editType: CellEditType.Replace, cells: [] }) ]; - const bce = new BulkCellEdits(new UndoRedoGroup(), new UndoRedoSource(), progress, new CancellationTokenSource().token, edits, editorService, notebookService as any); + const bce = new BulkCellEdits(new UndoRedoGroup(), new UndoRedoSource(), progress, CancellationToken.None, edits, editorService, notebookService as any); await bce.apply(); const resolveArgs = notebookService.resolve.args[0]; diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index a800e241511..30789475e42 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -6,7 +6,7 @@ import { addDisposableListener, isKeyboardEvent } from 'vs/base/browser/dom'; import { DomEmitter } from 'vs/base/browser/event'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { distinct, flatten } from 'vs/base/common/arrays'; +import { distinct } from 'vs/base/common/arrays'; import { RunOnceScheduler } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { memoize } from 'vs/base/common/decorators'; @@ -15,7 +15,7 @@ import { Event } from 'vs/base/common/event'; import { visit } from 'vs/base/common/json'; import { setProperty } from 'vs/base/common/jsonEdit'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { IDisposable, MutableDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, MutableDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { clamp } from 'vs/base/common/numbers'; import { basename } from 'vs/base/common/path'; import * as env from 'vs/base/common/platform'; @@ -217,6 +217,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { private altListener = new MutableDisposable(); private altPressed = false; private oldDecorations = this.editor.createDecorationsCollection(); + private displayedStore = new DisposableStore(); private editorHoverOptions: IEditorHoverOptions | undefined; private readonly debounceInfo: IFeatureDebounceInformation; @@ -237,7 +238,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { ) { this.debounceInfo = featureDebounceService.for(languageFeaturesService.inlineValuesProvider, 'InlineValues', { min: DEAFULT_INLINE_DEBOUNCE_DELAY }); this.hoverWidget = this.instantiationService.createInstance(DebugHoverWidget, this.editor); - this.toDispose = [this.defaultHoverLockout, this.altListener]; + this.toDispose = [this.defaultHoverLockout, this.altListener, this.displayedStore]; this.registerListeners(); this.exceptionWidgetVisible = CONTEXT_EXCEPTION_WIDGET_VISIBLE.bindTo(contextKeyService); this.toggleExceptionWidget(); @@ -639,7 +640,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { private get removeInlineValuesScheduler(): RunOnceScheduler { return new RunOnceScheduler( () => { - this.oldDecorations.clear(); + this.displayedStore.clear(); }, 100 ); @@ -670,10 +671,14 @@ export class DebugEditorContribution implements IDebugEditorContribution { } this.removeInlineValuesScheduler.cancel(); + this.displayedStore.clear(); const viewRanges = this.editor.getVisibleRangesPlusViewportAboveBelow(); let allDecorations: IModelDeltaDecoration[]; + const cts = new CancellationTokenSource(); + this.displayedStore.add(toDisposable(() => cts.dispose(true))); + if (this.languageFeaturesService.inlineValuesProvider.has(model)) { const findVariable = async (_key: string, caseSensitiveLookup: boolean): Promise => { @@ -693,14 +698,13 @@ export class DebugEditorContribution implements IDebugEditorContribution { frameId: stackFrame.frameId, stoppedLocation: new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn + 1, stackFrame.range.endLineNumber, stackFrame.range.endColumn + 1) }; - const token = new CancellationTokenSource().token; const providers = this.languageFeaturesService.inlineValuesProvider.ordered(model).reverse(); allDecorations = []; const lineDecorations = new Map(); - const promises = flatten(providers.map(provider => viewRanges.map(range => Promise.resolve(provider.provideInlineValues(model, range, ctx, token)).then(async (result) => { + const promises = providers.flatMap(provider => viewRanges.map(range => Promise.resolve(provider.provideInlineValues(model, range, ctx, cts.token)).then(async (result) => { if (result) { for (const iv of result) { @@ -753,7 +757,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { } }, err => { onUnexpectedExternalError(err); - })))); + }))); const startTime = Date.now(); @@ -794,12 +798,15 @@ export class DebugEditorContribution implements IDebugEditorContribution { return createInlineValueDecorationsInsideRange(variables, ownRanges, model, this._wordToLineNumbersMap.value); })); - allDecorations = distinct(decorationsPerScope.reduce((previous, current) => previous.concat(current), []), + allDecorations = distinct(decorationsPerScope.flat(), // Deduplicate decorations since same variable can appear in multiple scopes, leading to duplicated decorations #129770 decoration => `${decoration.range.startLineNumber}:${decoration?.options.after?.content}`); } - this.oldDecorations.set(allDecorations); + if (!cts.token.isCancellationRequested) { + this.oldDecorations.set(allDecorations); + this.displayedStore.add(toDisposable(() => this.oldDecorations.clear())); + } } dispose(): void { @@ -810,8 +817,6 @@ export class DebugEditorContribution implements IDebugEditorContribution { this.configurationWidget.dispose(); } this.toDispose = dispose(this.toDispose); - - this.oldDecorations.clear(); } } From 6e1561e0e58a44c2e37293532ad50ac327d9a1b6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 27 Feb 2024 07:27:54 +0100 Subject: [PATCH 0203/1175] editors - introduce `transient` editor state (#205530) --- src/vs/platform/editor/common/editor.ts | 10 ++ .../api/browser/mainThreadEditorTabs.ts | 3 + .../browser/parts/editor/editorGroupView.ts | 14 ++- src/vs/workbench/common/editor.ts | 1 + .../common/editor/editorGroupModel.ts | 75 ++++++++++++- .../common/editor/filteredEditorGroupModel.ts | 1 + .../quickTextSearch/textSearchQuickAccess.ts | 18 +-- .../links/browser/terminalLinkQuickpick.ts | 25 +---- .../editor/common/editorGroupsService.ts | 18 +++ .../test/browser/editorGroupsService.test.ts | 66 +++++++++++ .../history/browser/historyService.ts | 32 ++---- .../services/history/common/history.ts | 6 - .../test/browser/historyService.test.ts | 105 +----------------- .../editor/filteredEditorGroupModel.test.ts | 22 ++++ .../test/browser/workbenchTestServices.ts | 2 + .../test/common/workbenchTestServices.ts | 1 - 16 files changed, 233 insertions(+), 166 deletions(-) diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index 24e2e4c5506..51060787d4a 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -288,6 +288,16 @@ export interface IEditorOptions { * applied when opening the editor. */ viewState?: object; + + /** + * A transient editor will attempt to appear as preview and certain components + * (such as history tracking) may decide to ignore the editor when it becomes + * active. + * This option is meant to be used only when the editor is used for a short + * period of time, for example when opening a preview of the editor from a + * picker control in the background while navigating through results of the picker. + */ + transient?: boolean; } export interface ITextEditorSelection { diff --git a/src/vs/workbench/api/browser/mainThreadEditorTabs.ts b/src/vs/workbench/api/browser/mainThreadEditorTabs.ts index d23f8e91fd5..41ed2e74e2c 100644 --- a/src/vs/workbench/api/browser/mainThreadEditorTabs.ts +++ b/src/vs/workbench/api/browser/mainThreadEditorTabs.ts @@ -553,6 +553,9 @@ export class MainThreadEditorTabs implements MainThreadEditorTabsShape { this._onDidTabPreviewChange(groupId, event.editorIndex, event.editor); break; } + case GroupModelChangeKind.EDITOR_TRANSIENT: + // Currently not exposed in the API + break; case GroupModelChangeKind.EDITOR_MOVE: if (isGroupEditorMoveEvent(event) && event.editor && event.editorIndex !== undefined && event.oldEditorIndex !== undefined) { this._onDidTabMove(groupId, event.editorIndex, event.oldEditorIndex, event.editor); diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index f0bdfdd5bc9..e0761b1f1db 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -886,6 +886,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return this.model.isSticky(editorOrIndex); } + isTransient(editorOrIndex: EditorInput | number): boolean { + return this.model.isTransient(editorOrIndex); + } + isActive(editor: EditorInput | IUntypedEditorInput): boolean { return this.model.isActive(editor); } @@ -1004,6 +1008,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } } + setTransient(candidate: EditorInput | undefined, transient: boolean): void { + const editor = candidate ?? this.activeEditor; + if (editor) { + this.model.setTransient(editor, transient); + } + } + //#endregion //#region openEditor() @@ -1033,7 +1044,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Determine options const pinned = options?.sticky - || !this.groupsView.partOptions.enablePreview + || (!this.groupsView.partOptions.enablePreview && !options?.transient) || editor.isDirty() || (options?.pinned ?? typeof options?.index === 'number' /* unless specified, prefer to pin when opening with index */) || (typeof options?.index === 'number' && this.model.isSticky(options.index)) @@ -1042,6 +1053,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { index: options ? options.index : undefined, pinned, sticky: options?.sticky || (typeof options?.index === 'number' && this.model.isSticky(options.index)), + transient: !!options?.transient, active: this.count === 0 || !options || !options.inactive, supportSideBySide: internalOptions?.supportSideBySide }; diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 9652c2dfe00..c27b43d382c 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1133,6 +1133,7 @@ export const enum GroupModelChangeKind { EDITOR_LABEL, EDITOR_CAPABILITIES, EDITOR_PIN, + EDITOR_TRANSIENT, EDITOR_STICKY, EDITOR_DIRTY, EDITOR_WILL_DISPOSE diff --git a/src/vs/workbench/common/editor/editorGroupModel.ts b/src/vs/workbench/common/editor/editorGroupModel.ts index 8a018234256..17c5ab59c26 100644 --- a/src/vs/workbench/common/editor/editorGroupModel.ts +++ b/src/vs/workbench/common/editor/editorGroupModel.ts @@ -22,7 +22,8 @@ const EditorOpenPositioning = { export interface IEditorOpenOptions { readonly pinned?: boolean; - sticky?: boolean; + readonly sticky?: boolean; + readonly transient?: boolean; active?: boolean; readonly index?: number; readonly supportSideBySide?: SideBySideEditor.ANY | SideBySideEditor.BOTH; @@ -180,6 +181,7 @@ export interface IReadonlyEditorGroupModel { isActive(editor: EditorInput | IUntypedEditorInput): boolean; isPinned(editorOrIndex: EditorInput | number): boolean; isSticky(editorOrIndex: EditorInput | number): boolean; + isTransient(editorOrIndex: EditorInput | number): boolean; isFirst(editor: EditorInput, editors?: EditorInput[]): boolean; isLast(editor: EditorInput, editors?: EditorInput[]): boolean; findEditor(editor: EditorInput | null, options?: IMatchEditorOptions): [EditorInput, number /* index */] | undefined; @@ -217,6 +219,7 @@ export class EditorGroupModel extends Disposable implements IEditorGroupModel { private preview: EditorInput | null = null; // editor in preview state private active: EditorInput | null = null; // editor in active state private sticky = -1; // index of first editor in sticky state + private transient = new Set(); // editors in transient state private editorOpenPositioning: ('left' | 'right' | 'first' | 'last') | undefined; private focusRecentEditorAfterClose: boolean | undefined; @@ -295,6 +298,7 @@ export class EditorGroupModel extends Disposable implements IEditorGroupModel { openEditor(candidate: EditorInput, options?: IEditorOpenOptions): IEditorOpenResult { const makeSticky = options?.sticky || (typeof options?.index === 'number' && this.isSticky(options.index)); const makePinned = options?.pinned || options?.sticky; + const makeTransient = !!options?.transient; const makeActive = options?.active || !this.activeEditor || (!makePinned && this.matches(this.preview, this.activeEditor)); const existingEditorAndIndex = this.findEditor(candidate, options); @@ -381,6 +385,11 @@ export class EditorGroupModel extends Disposable implements IEditorGroupModel { this.preview = newEditor; } + // Handle transient + if (makeTransient) { + this.doSetTransient(newEditor, targetIndex, true); + } + // Listeners this.registerEditorListeners(newEditor); @@ -412,6 +421,9 @@ export class EditorGroupModel extends Disposable implements IEditorGroupModel { this.doPin(existingEditor, existingEditorIndex); } + // Update transient + this.doSetTransient(existingEditor, existingEditorIndex, makeTransient); + // Activate it if (makeActive) { this.doSetActive(existingEditor, existingEditorIndex); @@ -563,6 +575,9 @@ export class EditorGroupModel extends Disposable implements IEditorGroupModel { this.preview = null; } + // Remove from transient + this.transient.delete(editor); + // Remove from arrays this.splice(index, true); @@ -860,6 +875,62 @@ export class EditorGroupModel extends Disposable implements IEditorGroupModel { return index <= this.sticky; } + setTransient(candidate: EditorInput, transient: boolean): EditorInput | undefined { + if (!transient && this.transient.size === 0) { + return; // no transient editor + } + + const res = this.findEditor(candidate); + if (!res) { + return; // not found + } + + const [editor, editorIndex] = res; + + this.doSetTransient(editor, editorIndex, transient); + + return editor; + } + + private doSetTransient(editor: EditorInput, editorIndex: number, transient: boolean): void { + if (transient) { + if (this.transient.has(editor)) { + return; + } + + this.transient.add(editor); + } else { + if (!this.transient.has(editor)) { + return; + } + + this.transient.delete(editor); + } + + // Event + const event: IGroupEditorChangeEvent = { + kind: GroupModelChangeKind.EDITOR_TRANSIENT, + editor, + editorIndex + }; + this._onDidModelChange.fire(event); + } + + isTransient(editorOrIndex: EditorInput | number): boolean { + if (this.transient.size === 0) { + return false; // no transient editor + } + + let editor: EditorInput | undefined; + if (typeof editorOrIndex === 'number') { + editor = this.editors[editorOrIndex]; + } else { + editor = this.findEditor(editorOrIndex)?.[0]; + } + + return !!editor && this.transient.has(editor); + } + private splice(index: number, del: boolean, editor?: EditorInput): void { const editorToDeleteOrReplace = this.editors[index]; @@ -1124,6 +1195,8 @@ export class EditorGroupModel extends Disposable implements IEditorGroupModel { dispose(Array.from(this.editorListeners)); this.editorListeners.clear(); + this.transient.clear(); + super.dispose(); } } diff --git a/src/vs/workbench/common/editor/filteredEditorGroupModel.ts b/src/vs/workbench/common/editor/filteredEditorGroupModel.ts index 7b427fe5ded..390b19874c8 100644 --- a/src/vs/workbench/common/editor/filteredEditorGroupModel.ts +++ b/src/vs/workbench/common/editor/filteredEditorGroupModel.ts @@ -38,6 +38,7 @@ abstract class FilteredEditorGroupModel extends Disposable implements IReadonlyE get previewEditor(): EditorInput | null { return this.model.previewEditor && this.filter(this.model.previewEditor) ? this.model.previewEditor : null; } isPinned(editorOrIndex: EditorInput | number): boolean { return this.model.isPinned(editorOrIndex); } + isTransient(editorOrIndex: EditorInput | number): boolean { return this.model.isTransient(editorOrIndex); } isSticky(editorOrIndex: EditorInput | number): boolean { return this.model.isSticky(editorOrIndex); } isActive(editor: EditorInput | IUntypedEditorInput): boolean { return this.model.isActive(editor); } diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts index 3691e98ec1d..c4e31c3c903 100644 --- a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -30,7 +30,6 @@ import { IPatternInfo, ISearchComplete, ITextQuery, VIEW_ID } from 'vs/workbench import { Event } from 'vs/base/common/event'; import { EditorViewState } from 'vs/workbench/browser/quickaccess'; import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; -import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { Sequencer } from 'vs/base/common/async'; export const TEXT_SEARCH_QUICK_ACCESS_PREFIX = '%'; @@ -84,8 +83,7 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { - // disable and re-enable history service so that we can ignore this history entry - const disposable = this._historyService.suspendTracking(); - try { - await this._editorService.openEditor({ - resource: itemMatch.parent().resource, - options: { preserveFocus: true, revealIfOpened: true, ignoreError: true, selection: itemMatch.range() } - }); - } finally { - disposable.dispose(); - } + await this._editorService.openEditor({ + resource: itemMatch.parent().resource, + options: { transient: true, preserveFocus: true, revealIfOpened: true, ignoreError: true, selection: itemMatch.range() } + }); }); } })); diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts index e3bd74aec3b..bfa7bbfb687 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick.ts @@ -17,11 +17,8 @@ import type { TerminalLink } from 'vs/workbench/contrib/terminalContrib/links/br import { Sequencer, timeout } from 'vs/base/common/async'; import { EditorViewState } from 'vs/workbench/browser/quickaccess'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { getLinkSuffix } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing'; import { TerminalBuiltinLinkType } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import type { IFilesConfiguration } from 'vs/workbench/contrib/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; @@ -36,9 +33,7 @@ export class TerminalLinkQuickpick extends DisposableStore { readonly onDidRequestMoreLinks = this._onDidRequestMoreLinks.event; constructor( - @IConfigurationService private readonly _configurationService: IConfigurationService, @IEditorService private readonly _editorService: IEditorService, - @IHistoryService private readonly _historyService: IHistoryService, @ILabelService private readonly _labelService: ILabelService, @IQuickInputService private readonly _quickInputService: IQuickInputService, @IAccessibleViewService private readonly _accessibleViewService: IAccessibleViewService @@ -247,12 +242,6 @@ export class TerminalLinkQuickpick extends DisposableStore { return; } - // Don't open if preview editors are disabled as it may open many editor - const config = this._configurationService.getValue(); - if (!config.workbench?.editor?.enablePreview) { - return; - } - this._previewItemInEditor(link); } @@ -267,16 +256,10 @@ export class TerminalLinkQuickpick extends DisposableStore { this._editorViewState.set(); this._editorSequencer.queue(async () => { - // disable and re-enable history service so that we can ignore this history entry - const disposable = this._historyService.suspendTracking(); - try { - await this._editorService.openEditor({ - resource: link.uri, - options: { preserveFocus: true, revealIfOpened: true, ignoreError: true, selection, } - }); - } finally { - disposable.dispose(); - } + await this._editorService.openEditor({ + resource: link.uri, + options: { transient: true, preserveFocus: true, revealIfOpened: true, ignoreError: true, selection, } + }); }); } diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index 650df06217d..fd2f81f70e9 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -727,6 +727,11 @@ export interface IEditorGroup { */ isSticky(editorOrIndex: EditorInput | number): boolean; + /** + * Find out if the provided editor or index of editor is transient in the group. + */ + isTransient(editorOrIndex: EditorInput | number): boolean; + /** * Find out if the provided editor is active in the group. */ @@ -832,6 +837,19 @@ export interface IEditorGroup { */ unstickEditor(editor?: EditorInput): void; + /** + * A transient editor will attempt to appear as preview and certain components + * (such as history tracking) may decide to ignore the editor when it becomes + * active. + * This option is meant to be used only when the editor is used for a short + * period of time, for example when opening a preview of the editor from a + * picker control in the background while navigating through results of the picker. + * + * @param editor the editor to update transient state, or the currently active editor + * if unspecified. + */ + setTransient(editor: EditorInput | undefined, transient: boolean): void; + /** * Whether this editor group should be locked or not. * 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 d87cda2d39c..e42c6116bc7 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -477,6 +477,7 @@ suite('EditorGroupsService', () => { const editorCloseEvents: IGroupModelChangeEvent[] = []; let editorPinCounter = 0; let editorStickyCounter = 0; + let editorTransientCounter = 0; let editorCapabilitiesCounter = 0; const editorGroupModelChangeListener = group.onDidModelChange(e => { if (e.kind === GroupModelChangeKind.EDITOR_OPEN) { @@ -489,6 +490,9 @@ suite('EditorGroupsService', () => { } else if (e.kind === GroupModelChangeKind.EDITOR_STICKY) { assert.ok(e.editor); editorStickyCounter++; + } else if (e.kind === GroupModelChangeKind.EDITOR_TRANSIENT) { + assert.ok(e.editor); + editorTransientCounter++; } else if (e.kind === GroupModelChangeKind.EDITOR_CAPABILITIES) { assert.ok(e.editor); editorCapabilitiesCounter++; @@ -593,6 +597,15 @@ suite('EditorGroupsService', () => { group.unstickEditor(input); assert.strictEqual(editorStickyCounter, 2); + assert.strictEqual(group.isTransient(input), false); + assert.strictEqual(editorTransientCounter, 0); + group.setTransient(input, true); + assert.strictEqual(group.isTransient(input), true); + assert.strictEqual(editorTransientCounter, 1); + group.setTransient(input, false); + assert.strictEqual(group.isTransient(input), false); + assert.strictEqual(editorTransientCounter, 2); + editorCloseListener.dispose(); editorWillCloseListener.dispose(); editorDidCloseListener.dispose(); @@ -1817,5 +1830,58 @@ suite('EditorGroupsService', () => { maxiizeGroupEventDisposable.dispose(); }); + test('transient editors - basics', async () => { + const [part] = await createPart(); + const group = part.activeGroup; + + const input = createTestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + const inputInactive = createTestFileEditorInput(URI.file('foo/bar/inactive'), TEST_EDITOR_INPUT_ID); + + await group.openEditor(input, { pinned: true }); + await group.openEditor(inputInactive, { inactive: true }); + + assert.strictEqual(group.isTransient(input), false); + assert.strictEqual(group.isTransient(inputInactive), false); + + group.setTransient(input, true); + + assert.strictEqual(group.isTransient(input), true); + assert.strictEqual(group.isTransient(inputInactive), false); + + group.setTransient(input, false); + + assert.strictEqual(group.isTransient(input), false); + assert.strictEqual(group.isTransient(inputInactive), false); + + const inputTransient = createTestFileEditorInput(URI.file('foo/bar/transient'), TEST_EDITOR_INPUT_ID); + + await group.openEditor(inputTransient, { transient: true }); + assert.strictEqual(group.isTransient(inputTransient), true); + + await group.openEditor(inputTransient, {}); + assert.strictEqual(group.isTransient(inputTransient), false); + }); + + test('transient editors - overrides enablePreview setting', async function () { + const instantiationService = workbenchInstantiationService(undefined, disposables); + const configurationService = new TestConfigurationService(); + await configurationService.setUserConfiguration('workbench', { 'editor': { 'enablePreview': false } }); + instantiationService.stub(IConfigurationService, configurationService); + + const [part] = await createPart(instantiationService); + + const group = part.activeGroup; + assert.strictEqual(group.isEmpty, true); + + const input = createTestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); + const input2 = createTestFileEditorInput(URI.file('foo/bar2'), TEST_EDITOR_INPUT_ID); + + await group.openEditor(input, { pinned: false }); + assert.strictEqual(group.isPinned(input), true); + + await group.openEditor(input2, { transient: true }); + assert.strictEqual(group.isPinned(input2), false); + }); + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/history/browser/historyService.ts b/src/vs/workbench/services/history/browser/historyService.ts index 108685831ad..3cef7fa9416 100644 --- a/src/vs/workbench/services/history/browser/historyService.ts +++ b/src/vs/workbench/services/history/browser/historyService.ts @@ -12,7 +12,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { GoFilter, GoScope, IHistoryService } from 'vs/workbench/services/history/common/history'; import { FileChangesEvent, IFileService, FileChangeType, FILES_EXCLUDE_CONFIG, FileOperationEvent, FileOperation } from 'vs/platform/files/common/files'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { dispose, Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { dispose, Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Emitter, Event } from 'vs/base/common/event'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -73,31 +73,16 @@ export class HistoryService extends Disposable implements IHistoryService { } } - private trackingSuspended = false; - suspendTracking(): IDisposable { - this.trackingSuspended = true; - - return toDisposable(() => this.trackingSuspended = false); - } - private registerListeners(): void { // Mouse back/forward support this.registerMouseNavigationListener(); // Editor changes - this._register(this.editorService.onDidActiveEditorChange((e) => { - if (!this.trackingSuspended) { - this.onDidActiveEditorChange(); - } - })); + this._register(this.editorService.onDidActiveEditorChange(() => this.onDidActiveEditorChange())); this._register(this.editorService.onDidOpenEditorFail(event => this.remove(event.editor))); this._register(this.editorService.onDidCloseEditor(event => this.onDidCloseEditor(event))); - this._register(this.editorService.onDidMostRecentlyActiveEditorsChange(() => { - if (!this.trackingSuspended) { - this.handleEditorEventInRecentEditorsStack(); - } - })); + this._register(this.editorService.onDidMostRecentlyActiveEditorsChange(() => this.handleEditorEventInRecentEditorsStack())); // Editor group changes this._register(this.editorGroupService.onDidRemoveGroup(e => this.onDidRemoveGroup(e))); @@ -188,14 +173,15 @@ export class HistoryService extends Disposable implements IHistoryService { // Dispose old listeners this.activeEditorListeners.clear(); - // Handle editor change - this.handleActiveEditorChange(activeEditorGroup, activeEditorPane); + // Handle editor change unless the editor is transient + if (!activeEditorPane?.group.isTransient(activeEditorPane.input)) { + this.handleActiveEditorChange(activeEditorGroup, activeEditorPane); + } - // Listen to selection changes if the editor pane - // is having a selection concept. + // Listen to selection changes unless the editor is transient if (isEditorPaneWithSelection(activeEditorPane)) { this.activeEditorListeners.add(activeEditorPane.onDidChangeSelection(e => { - if (!this.trackingSuspended) { + if (!activeEditorPane.group.isTransient(activeEditorPane.input)) { this.handleActiveEditorSelectionChangeEvent(activeEditorGroup, activeEditorPane, e); } })); diff --git a/src/vs/workbench/services/history/common/history.ts b/src/vs/workbench/services/history/common/history.ts index 3d135ec2a70..b5abcd2dad3 100644 --- a/src/vs/workbench/services/history/common/history.ts +++ b/src/vs/workbench/services/history/common/history.ts @@ -8,7 +8,6 @@ import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { GroupIdentifier } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { URI } from 'vs/base/common/uri'; -import { IDisposable } from 'vs/base/common/lifecycle'; export const IHistoryService = createDecorator('historyService'); @@ -138,9 +137,4 @@ export interface IHistoryService { * Clear list of recently opened editors. */ clearRecentlyOpened(): void; - - /** - * Temporarily suspend tracking of editor events for the history. - */ - suspendTracking(): IDisposable; } diff --git a/src/vs/workbench/services/history/test/browser/historyService.test.ts b/src/vs/workbench/services/history/test/browser/historyService.test.ts index 43838aad189..28f269d3bee 100644 --- a/src/vs/workbench/services/history/test/browser/historyService.test.ts +++ b/src/vs/workbench/services/history/test/browser/historyService.test.ts @@ -807,7 +807,7 @@ suite('HistoryService', function () { return workbenchTeardown(instantiationService); }); - test('suspend should suspend editor changes- skip two editors and continue (single group)', async () => { + test('transient editors suspends editor change tracking', async () => { const [part, historyService, editorService, , instantiationService] = await createServices(); const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); @@ -821,19 +821,13 @@ suite('HistoryService', function () { assert.strictEqual(part.activeGroup.activeEditor, input1); await editorChangePromise; - const disposable = historyService.suspendTracking(); - - // wait on two editor changes before disposing - editorChangePromise = Event.toPromise(editorService.onDidActiveEditorChange) - .then(() => Event.toPromise(editorService.onDidActiveEditorChange)); - - await part.activeGroup.openEditor(input2, { pinned: true }); + await part.activeGroup.openEditor(input2, { transient: true }); assert.strictEqual(part.activeGroup.activeEditor, input2); - await part.activeGroup.openEditor(input3, { pinned: true }); + await part.activeGroup.openEditor(input3, { transient: true }); assert.strictEqual(part.activeGroup.activeEditor, input3); - await editorChangePromise; - disposable.dispose(); + editorChangePromise = Event.toPromise(editorService.onDidActiveEditorChange) + .then(() => Event.toPromise(editorService.onDidActiveEditorChange)); await part.activeGroup.openEditor(input4, { pinned: true }); assert.strictEqual(part.activeGroup.activeEditor, input4); @@ -856,94 +850,5 @@ suite('HistoryService', function () { return workbenchTeardown(instantiationService); }); - test('suspend should suspend editor changes- skip two editors and continue (multi group)', async () => { - const [part, historyService, editorService, , instantiationService] = await createServices(); - const rootGroup = part.activeGroup; - - const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); - const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID)); - const input3 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID)); - const input4 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID)); - const input5 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar5'), TEST_EDITOR_INPUT_ID)); - - const sideGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); - - let editorChangePromise = Event.toPromise(editorService.onDidActiveEditorChange); - await rootGroup.openEditor(input1, { pinned: true }); - await editorChangePromise; - - const disposable = historyService.suspendTracking(); - editorChangePromise = Event.toPromise(editorService.onDidActiveEditorChange) - .then(() => Event.toPromise(editorService.onDidActiveEditorChange)); - await sideGroup.openEditor(input2, { pinned: true }); - await rootGroup.openEditor(input3, { pinned: true }); - await editorChangePromise; - disposable.dispose(); - - await sideGroup.openEditor(input4, { pinned: true }); - await rootGroup.openEditor(input5, { pinned: true }); - - // stack should be [input1, input4, input5] - await historyService.goBack(); - assert.strictEqual(part.activeGroup.activeEditor, input4); - assert.strictEqual(part.activeGroup, sideGroup); - assert.strictEqual(rootGroup.activeEditor, input5); - - await historyService.goBack(); - assert.strictEqual(part.activeGroup.activeEditor, input1); - assert.strictEqual(part.activeGroup, rootGroup); - assert.strictEqual(sideGroup.activeEditor, input4); - - await historyService.goBack(); - assert.strictEqual(part.activeGroup.activeEditor, input1); - - await historyService.goForward(); - assert.strictEqual(part.activeGroup.activeEditor, input4); - await historyService.goForward(); - assert.strictEqual(part.activeGroup.activeEditor, input5); - - return workbenchTeardown(instantiationService); - }); - - test('suspend should suspend editor changes - interleaved skips', async () => { - const [part, historyService, editorService, , instantiationService] = await createServices(); - - const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); - const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID)); - const input3 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID)); - const input4 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID)); - const input5 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar5'), TEST_EDITOR_INPUT_ID)); - - let editorChangePromise = Event.toPromise(editorService.onDidActiveEditorChange); - await part.activeGroup.openEditor(input1, { pinned: true }); - await editorChangePromise; - - let disposable = historyService.suspendTracking(); - editorChangePromise = Event.toPromise(editorService.onDidActiveEditorChange); - await part.activeGroup.openEditor(input2, { pinned: true }); - await editorChangePromise; - disposable.dispose(); - - await part.activeGroup.openEditor(input3, { pinned: true }); - - disposable = historyService.suspendTracking(); - editorChangePromise = Event.toPromise(editorService.onDidActiveEditorChange); - await part.activeGroup.openEditor(input4, { pinned: true }); - await editorChangePromise; - disposable.dispose(); - - await part.activeGroup.openEditor(input5, { pinned: true }); - - // stack should be [input1, input3, input5] - await historyService.goBack(); - assert.strictEqual(part.activeGroup.activeEditor, input3); - await historyService.goBack(); - assert.strictEqual(part.activeGroup.activeEditor, input1); - await historyService.goBack(); - assert.strictEqual(part.activeGroup.activeEditor, input1); - - return workbenchTeardown(instantiationService); - }); - ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/filteredEditorGroupModel.test.ts b/src/vs/workbench/test/browser/parts/editor/filteredEditorGroupModel.test.ts index e2b8639f9cc..80765957797 100644 --- a/src/vs/workbench/test/browser/parts/editor/filteredEditorGroupModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/filteredEditorGroupModel.test.ts @@ -789,5 +789,27 @@ suite('FilteredEditorGroupModel', () => { assert.strictEqual(label1ChangeCounterUnsticky, 1); }); + test('Sticky/Unsticky isTransient()', async () => { + const model = createEditorGroupModel(); + + const stickyFilteredEditorGroup = disposables.add(new StickyEditorGroupModel(model)); + const unstickyFilteredEditorGroup = disposables.add(new UnstickyEditorGroupModel(model)); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + const input4 = input(); + + model.openEditor(input1, { pinned: true, transient: false }); + model.openEditor(input2, { pinned: true }); + model.openEditor(input3, { pinned: true, transient: true }); + model.openEditor(input4, { pinned: false, transient: true }); + + assert.strictEqual(stickyFilteredEditorGroup.isTransient(input1), false); + assert.strictEqual(unstickyFilteredEditorGroup.isTransient(input2), false); + assert.strictEqual(stickyFilteredEditorGroup.isTransient(input3), true); + assert.strictEqual(unstickyFilteredEditorGroup.isTransient(input4), true); + }); + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 6db63c1cc2e..4f297a34c83 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -904,6 +904,7 @@ export class TestEditorGroupView implements IEditorGroupView { openEditors(_editors: EditorInputWithOptions[]): Promise { throw new Error('not implemented'); } isPinned(_editor: EditorInput): boolean { return false; } isSticky(_editor: EditorInput): boolean { return false; } + isTransient(_editor: EditorInput): boolean { return false; } isActive(_editor: EditorInput | IUntypedEditorInput): boolean { return false; } contains(candidate: EditorInput | IUntypedEditorInput): boolean { return false; } moveEditor(_editor: EditorInput, _target: IEditorGroup, _options?: IEditorOptions): void { } @@ -917,6 +918,7 @@ export class TestEditorGroupView implements IEditorGroupView { pinEditor(_editor?: EditorInput): void { } stickEditor(editor?: EditorInput | undefined): void { } unstickEditor(editor?: EditorInput | undefined): void { } + setTransient(editor: EditorInput | undefined, transient: boolean): void { } lock(locked: boolean): void { } focus(): void { } get scopedContextKeyService(): IContextKeyService { throw new Error('not implemented'); } diff --git a/src/vs/workbench/test/common/workbenchTestServices.ts b/src/vs/workbench/test/common/workbenchTestServices.ts index 1d09528dcf6..1a938d7dbd6 100644 --- a/src/vs/workbench/test/common/workbenchTestServices.ts +++ b/src/vs/workbench/test/common/workbenchTestServices.ts @@ -165,7 +165,6 @@ export class TestHistoryService implements IHistoryService { async openPreviouslyUsedEditor(group?: GroupIdentifier): Promise { } getLastActiveWorkspaceRoot(_schemeFilter: string): URI | undefined { return this.root; } getLastActiveFile(_schemeFilter: string): URI | undefined { return undefined; } - suspendTracking() { return Disposable.None; } } export class TestWorkingCopy extends Disposable implements IWorkingCopy { From 24d41e4a2da7b689a76166eb029ddc9740b3c005 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 27 Feb 2024 08:44:50 +0100 Subject: [PATCH 0204/1175] editor - more `transient` fixes (#206320) * editors - clear preview flag when tranient move leaves and preview is disabled * history - log transient state * editors - update accordingly --- .../browser/parts/editor/editorGroupView.ts | 29 ++++++++++++++ .../test/browser/editorGroupsService.test.ts | 3 ++ .../history/browser/historyService.ts | 39 ++++++++++--------- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index e0761b1f1db..faddd4e7b06 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -544,6 +544,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Visibility this._register(this.groupsView.onDidVisibilityChange(e => this.onDidVisibilityChange(e))); + + // Focus + this._register(this.onDidFocus(() => this.onDidGainFocus())); } private onDidGroupModelChange(e: IGroupModelChangeEvent): void { @@ -578,6 +581,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { case GroupModelChangeKind.EDITOR_DIRTY: this.onDidChangeEditorDirty(e.editor); break; + case GroupModelChangeKind.EDITOR_TRANSIENT: + this.onDidChangeEditorTransient(e.editor); + break; case GroupModelChangeKind.EDITOR_LABEL: this.onDidChangeEditorLabel(e.editor); break; @@ -762,6 +768,17 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.titleControl.updateEditorDirty(editor); } + private onDidChangeEditorTransient(editor: EditorInput): void { + const transient = this.model.isTransient(editor); + + // Transient state overrides the `enablePreview` setting, + // so when an editor leaves the transient state, we have + // to ensure its preview state is also cleared. + if (!transient && !this.groupsView.partOptions.enablePreview) { + this.pinEditor(editor); + } + } + private onDidChangeEditorLabel(editor: EditorInput): void { // Forward to title control @@ -774,6 +791,18 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.editorPane.setVisible(visible); } + private onDidGainFocus(): void { + if (this.activeEditor) { + + // We aggressively clear the transient state of editors + // as soon as the group gains focus. This is to ensure + // that the transient state is not staying around when + // the user interacts with the editor. + + this.setTransient(this.activeEditor, false); + } + } + //#endregion //#region IEditorGroupView 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 e42c6116bc7..f1e25f4a355 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -1881,6 +1881,9 @@ suite('EditorGroupsService', () => { await group.openEditor(input2, { transient: true }); assert.strictEqual(group.isPinned(input2), false); + + group.setTransient(input2, false); + assert.strictEqual(group.isPinned(input2), true); }); ensureNoDisposablesAreLeakedInTestSuite(); diff --git a/src/vs/workbench/services/history/browser/historyService.ts b/src/vs/workbench/services/history/browser/historyService.ts index 3cef7fa9416..efaf3103c6c 100644 --- a/src/vs/workbench/services/history/browser/historyService.ts +++ b/src/vs/workbench/services/history/browser/historyService.ts @@ -35,6 +35,21 @@ import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecyc import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { mainWindow } from 'vs/base/browser/window'; +interface ISerializedEditorHistoryEntry { + readonly editor: Omit & { resource: string }; +} + +interface IRecentlyClosedEditor { + readonly editorId: string | undefined; + readonly editor: IUntypedEditorInput; + + readonly resource: URI | undefined; + readonly associatedResources: URI[]; + + readonly index: number; + readonly sticky: boolean; +} + export class HistoryService extends Disposable implements IHistoryService { declare readonly _serviceBrand: undefined; @@ -47,8 +62,6 @@ export class HistoryService extends Disposable implements IHistoryService { private readonly editorHelper = this.instantiationService.createInstance(EditorHelper); - - constructor( @IEditorService private readonly editorService: EditorServiceImpl, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @@ -59,7 +72,8 @@ export class HistoryService extends Disposable implements IHistoryService { @IWorkspacesService private readonly workspacesService: IWorkspacesService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, - @IContextKeyService private readonly contextKeyService: IContextKeyService + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @ILogService private readonly logService: ILogService ) { super(); @@ -176,6 +190,8 @@ export class HistoryService extends Disposable implements IHistoryService { // Handle editor change unless the editor is transient if (!activeEditorPane?.group.isTransient(activeEditorPane.input)) { this.handleActiveEditorChange(activeEditorGroup, activeEditorPane); + } else { + this.logService.trace(`[History]: ignoring transient editor change (editor: ${activeEditorPane.input?.resource?.toString()}})`); } // Listen to selection changes unless the editor is transient @@ -183,6 +199,8 @@ export class HistoryService extends Disposable implements IHistoryService { this.activeEditorListeners.add(activeEditorPane.onDidChangeSelection(e => { if (!activeEditorPane.group.isTransient(activeEditorPane.input)) { this.handleActiveEditorSelectionChangeEvent(activeEditorGroup, activeEditorPane, e); + } else { + this.logService.trace(`[History]: ignoring transient editor selection change (editor: ${activeEditorPane.input?.resource?.toString()}})`); } })); } @@ -2077,18 +2095,3 @@ class EditorHelper { } } } - -interface ISerializedEditorHistoryEntry { - editor: Omit & { resource: string }; -} - -interface IRecentlyClosedEditor { - editorId: string | undefined; - editor: IUntypedEditorInput; - - resource: URI | undefined; - associatedResources: URI[]; - - index: number; - sticky: boolean; -} From f79ac8713f8d9a355a948ab026b2227f29820780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chanchevrier?= Date: Tue, 27 Feb 2024 09:34:31 +0100 Subject: [PATCH 0205/1175] fix: account for sidebar position when resizing terminal --- .../contrib/terminal/browser/terminalGroup.ts | 74 +++++++++++-------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index 30a95315f33..171c69d6ed3 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -7,7 +7,7 @@ import { TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal' import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, Disposable, DisposableStore, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { SplitView, Orientation, IView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; -import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService'; +import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITerminalInstance, Direction, ITerminalGroup, ITerminalService, ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views'; @@ -42,7 +42,6 @@ class SplitPaneContainer extends Disposable { constructor( private _container: HTMLElement, public orientation: Orientation, - @IWorkbenchLayoutService private readonly _layoutService: IWorkbenchLayoutService ) { super(); this._width = this._container.offsetWidth; @@ -61,25 +60,7 @@ class SplitPaneContainer extends Disposable { this._addChild(instance, index); } - resizePane(index: number, direction: Direction, amount: number, part: Parts): void { - const isHorizontal = (direction === Direction.Left) || (direction === Direction.Right); - - if ((isHorizontal && this.orientation !== Orientation.HORIZONTAL) || - (!isHorizontal && this.orientation !== Orientation.VERTICAL)) { - // Resize the entire pane as a whole - if ( - (this.orientation === Orientation.HORIZONTAL && direction === Direction.Down) || - (this.orientation === Orientation.VERTICAL && direction === Direction.Right) || - (part === Parts.AUXILIARYBAR_PART && direction === Direction.Right) - ) { - amount *= -1; - } - - this._layoutService.resizePart(part, amount, amount); - return; - } - - // Resize left/right in horizontal or up/down in vertical + resizePane(index: number, direction: Direction, amount: number): void { // Only resize when there is more than one pane if (this._children.length <= 1) { return; @@ -570,28 +551,57 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { this.setActiveInstanceByIndex(newIndex); } + private _getPosition(): Position { + switch (this._terminalLocation) { + case ViewContainerLocation.Panel: + return this._panelPosition; + case ViewContainerLocation.Sidebar: + return this._layoutService.getSideBarPosition(); + case ViewContainerLocation.AuxiliaryBar: + return this._layoutService.getSideBarPosition() === Position.LEFT ? Position.RIGHT : Position.LEFT; + } + } + + private _getOrientation(): Orientation { + return this._getPosition() === Position.BOTTOM ? Orientation.HORIZONTAL : Orientation.VERTICAL; + } + resizePane(direction: Direction): void { if (!this._splitPaneContainer) { return; } - const isHorizontal = (direction === Direction.Left || direction === Direction.Right); + const isHorizontalResize = (direction === Direction.Left || direction === Direction.Right); - const part = getPartByLocation(this._terminalLocation); + const groupOrientation = this._getOrientation(); - const isTerminalLeft = this._panelPosition === Position.LEFT || part === Parts.SIDEBAR_PART; - - // Left-positionned panels have inverted controls - // see https://github.com/microsoft/vscode/issues/140873 - const shouldInvertHorizontalResize = (isHorizontal && isTerminalLeft); - - const resizeDirection = shouldInvertHorizontalResize ? direction === Direction.Left ? Direction.Right : Direction.Left : direction; + const shouldResizePart = + (isHorizontalResize && groupOrientation === Orientation.VERTICAL) || + (!isHorizontalResize && groupOrientation === Orientation.HORIZONTAL); const font = this._terminalService.configHelper.getFont(getWindow(this._groupElement)); // TODO: Support letter spacing and line height - const charSize = (isHorizontal ? font.charWidth : font.charHeight); + const charSize = (isHorizontalResize ? font.charWidth : font.charHeight); + if (charSize) { - this._splitPaneContainer.resizePane(this._activeInstanceIndex, resizeDirection, charSize * Constants.ResizePartCellCount, part); + let resizeAmount = charSize * Constants.ResizePartCellCount; + + if (shouldResizePart) { + + const shouldShrink = + (this._getPosition() === Position.LEFT && direction === Direction.Left) || + (this._getPosition() === Position.RIGHT && direction === Direction.Right) || + (this._getPosition() === Position.BOTTOM && direction === Direction.Down); + + if (shouldShrink) { + resizeAmount *= -1; + } + + this._layoutService.resizePart(getPartByLocation(this._terminalLocation), resizeAmount, resizeAmount); + } else { + this._splitPaneContainer.resizePane(this._activeInstanceIndex, direction, resizeAmount); + } + } } From 3a1f27a6e657002489f8034b1f1f1afebd46ca7e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 27 Feb 2024 09:40:43 +0100 Subject: [PATCH 0206/1175] Speech: allow `auto` setting for language to derive from display language (fix #206321) (#206322) --- .../browser/accessibilityConfiguration.ts | 89 ++------------- .../contrib/speech/browser/speechService.ts | 4 +- .../contrib/speech/common/speechService.ts | 102 ++++++++++++++++++ .../speech/test/common/speechService.test.ts | 27 +++++ 4 files changed, 138 insertions(+), 84 deletions(-) create mode 100644 src/vs/workbench/contrib/speech/test/common/speechService.test.ts diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 9a42093991c..33a1379d3bd 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -9,7 +9,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { workbenchConfigurationNodeBase, Extensions as WorkbenchExtensions, IConfigurationMigrationRegistry, ConfigurationKeyValuePairs } from 'vs/workbench/common/configuration'; import { AccessibilityAlertSettingId, AccessibilitySignal } from 'vs/platform/accessibilitySignal/browser/accessibilitySignalService'; -import { ISpeechService } from 'vs/workbench/contrib/speech/common/speechService'; +import { ISpeechService, SPEECH_LANGUAGES, SPEECH_LANGUAGE_CONFIG } from 'vs/workbench/contrib/speech/common/speechService'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Event } from 'vs/base/common/event'; @@ -664,10 +664,9 @@ export function registerAccessibilityConfiguration() { export const enum AccessibilityVoiceSettingId { SpeechTimeout = 'accessibility.voice.speechTimeout', - SpeechLanguage = 'accessibility.voice.speechLanguage' + SpeechLanguage = SPEECH_LANGUAGE_CONFIG } export const SpeechTimeoutDefault = 1200; -const SpeechLanguageDefault = 'en-US'; export class DynamicSpeechAccessibilityConfiguration extends Disposable implements IWorkbenchContribution { @@ -703,10 +702,10 @@ export class DynamicSpeechAccessibilityConfiguration extends Disposable implemen 'tags': ['accessibility'] }, [AccessibilityVoiceSettingId.SpeechLanguage]: { - 'markdownDescription': localize('voice.speechLanguage', "The language that voice speech recognition should recognize."), + 'markdownDescription': localize('voice.speechLanguage', "The language that voice speech recognition should recognize. Select `auto` to use the configured display language if possible. Note that not all display languages maybe supported by speech recognition"), 'type': 'string', 'enum': languagesSorted, - 'default': SpeechLanguageDefault, + 'default': 'auto', 'tags': ['accessibility'], 'enumDescriptions': languagesSorted.map(key => languages[key].name), 'enumItemLabels': languagesSorted.map(key => languages[key].name) @@ -717,84 +716,10 @@ export class DynamicSpeechAccessibilityConfiguration extends Disposable implemen private getLanguages(): { [locale: string]: { name: string } } { return { - ['da-DK']: { - name: localize('speechLanguage.da-DK', "Danish (Denmark)") + ['auto']: { + name: localize('speechLanguage.auto', "Auto (Use Display Language)") }, - ['de-DE']: { - name: localize('speechLanguage.de-DE', "German (Germany)") - }, - ['en-AU']: { - name: localize('speechLanguage.en-AU', "English (Australia)") - }, - ['en-CA']: { - name: localize('speechLanguage.en-CA', "English (Canada)") - }, - ['en-GB']: { - name: localize('speechLanguage.en-GB', "English (United Kingdom)") - }, - ['en-IE']: { - name: localize('speechLanguage.en-IE', "English (Ireland)") - }, - ['en-IN']: { - name: localize('speechLanguage.en-IN', "English (India)") - }, - ['en-NZ']: { - name: localize('speechLanguage.en-NZ', "English (New Zealand)") - }, - [SpeechLanguageDefault]: { - name: localize('speechLanguage.en-US', "English (United States)") - }, - ['es-ES']: { - name: localize('speechLanguage.es-ES', "Spanish (Spain)") - }, - ['es-MX']: { - name: localize('speechLanguage.es-MX', "Spanish (Mexico)") - }, - ['fr-CA']: { - name: localize('speechLanguage.fr-CA', "French (Canada)") - }, - ['fr-FR']: { - name: localize('speechLanguage.fr-FR', "French (France)") - }, - ['hi-IN']: { - name: localize('speechLanguage.hi-IN', "Hindi (India)") - }, - ['it-IT']: { - name: localize('speechLanguage.it-IT', "Italian (Italy)") - }, - ['ja-JP']: { - name: localize('speechLanguage.ja-JP', "Japanese (Japan)") - }, - ['ko-KR']: { - name: localize('speechLanguage.ko-KR', "Korean (South Korea)") - }, - ['nl-NL']: { - name: localize('speechLanguage.nl-NL', "Dutch (Netherlands)") - }, - ['pt-PT']: { - name: localize('speechLanguage.pt-PT', "Portuguese (Portugal)") - }, - ['pt-BR']: { - name: localize('speechLanguage.pt-BR', "Portuguese (Brazil)") - }, - ['ru-RU']: { - name: localize('speechLanguage.ru-RU', "Russian (Russia)") - }, - ['sv-SE']: { - name: localize('speechLanguage.sv-SE', "Swedish (Sweden)") - }, - ['tr-TR']: { - name: localize('speechLanguage.tr-TR', "Turkish (Turkey)") - }, - ['zh-CN']: { - name: localize('speechLanguage.zh-CN', "Chinese (Simplified, China)") - }, - ['zh-HK']: { - name: localize('speechLanguage.zh-HK', "Chinese (Traditional, Hong Kong)") - }, - ['zh-TW']: { - name: localize('speechLanguage.zh-TW', "Chinese (Traditional, Taiwan)") - } + ...SPEECH_LANGUAGES }; } } diff --git a/src/vs/workbench/contrib/speech/browser/speechService.ts b/src/vs/workbench/contrib/speech/browser/speechService.ts index 00943f3262b..d0255933209 100644 --- a/src/vs/workbench/contrib/speech/browser/speechService.ts +++ b/src/vs/workbench/contrib/speech/browser/speechService.ts @@ -11,7 +11,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ILogService } from 'vs/platform/log/common/log'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { DeferredPromise } from 'vs/base/common/async'; -import { ISpeechService, ISpeechProvider, HasSpeechProvider, ISpeechToTextSession, SpeechToTextInProgress, IKeywordRecognitionSession, KeywordRecognitionStatus, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; +import { ISpeechService, ISpeechProvider, HasSpeechProvider, ISpeechToTextSession, SpeechToTextInProgress, IKeywordRecognitionSession, KeywordRecognitionStatus, SpeechToTextStatus, speechLanguageConfigToLanguage, SPEECH_LANGUAGE_CONFIG } from 'vs/workbench/contrib/speech/common/speechService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -80,7 +80,7 @@ export class SpeechService extends Disposable implements ISpeechService { this.logService.warn(`Multiple speech providers registered. Picking first one: ${provider.metadata.displayName}`); } - const language = this.configurationService.getValue('accessibility.voice.speechLanguage'); + const language = speechLanguageConfigToLanguage(this.configurationService.getValue(SPEECH_LANGUAGE_CONFIG)); const session = this._activeSpeechToTextSession = provider.createSpeechToTextSession(token, typeof language === 'string' ? { language } : undefined); const sessionStart = Date.now(); diff --git a/src/vs/workbench/contrib/speech/common/speechService.ts b/src/vs/workbench/contrib/speech/common/speechService.ts index a594b4f3acf..e1bbf0f4f8e 100644 --- a/src/vs/workbench/contrib/speech/common/speechService.ts +++ b/src/vs/workbench/contrib/speech/common/speechService.ts @@ -10,6 +10,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { language } from 'vs/base/common/platform'; export const ISpeechService = createDecorator('speechService'); @@ -97,3 +98,104 @@ export interface ISpeechService { */ recognizeKeyword(token: CancellationToken): Promise; } + +export const SPEECH_LANGUAGE_CONFIG = 'accessibility.voice.speechLanguage'; + +export const SPEECH_LANGUAGES = { + ['da-DK']: { + name: localize('speechLanguage.da-DK', "Danish (Denmark)") + }, + ['de-DE']: { + name: localize('speechLanguage.de-DE', "German (Germany)") + }, + ['en-AU']: { + name: localize('speechLanguage.en-AU', "English (Australia)") + }, + ['en-CA']: { + name: localize('speechLanguage.en-CA', "English (Canada)") + }, + ['en-GB']: { + name: localize('speechLanguage.en-GB', "English (United Kingdom)") + }, + ['en-IE']: { + name: localize('speechLanguage.en-IE', "English (Ireland)") + }, + ['en-IN']: { + name: localize('speechLanguage.en-IN', "English (India)") + }, + ['en-NZ']: { + name: localize('speechLanguage.en-NZ', "English (New Zealand)") + }, + ['en-US']: { + name: localize('speechLanguage.en-US', "English (United States)") + }, + ['es-ES']: { + name: localize('speechLanguage.es-ES', "Spanish (Spain)") + }, + ['es-MX']: { + name: localize('speechLanguage.es-MX', "Spanish (Mexico)") + }, + ['fr-CA']: { + name: localize('speechLanguage.fr-CA', "French (Canada)") + }, + ['fr-FR']: { + name: localize('speechLanguage.fr-FR', "French (France)") + }, + ['hi-IN']: { + name: localize('speechLanguage.hi-IN', "Hindi (India)") + }, + ['it-IT']: { + name: localize('speechLanguage.it-IT', "Italian (Italy)") + }, + ['ja-JP']: { + name: localize('speechLanguage.ja-JP', "Japanese (Japan)") + }, + ['ko-KR']: { + name: localize('speechLanguage.ko-KR', "Korean (South Korea)") + }, + ['nl-NL']: { + name: localize('speechLanguage.nl-NL', "Dutch (Netherlands)") + }, + ['pt-PT']: { + name: localize('speechLanguage.pt-PT', "Portuguese (Portugal)") + }, + ['pt-BR']: { + name: localize('speechLanguage.pt-BR', "Portuguese (Brazil)") + }, + ['ru-RU']: { + name: localize('speechLanguage.ru-RU', "Russian (Russia)") + }, + ['sv-SE']: { + name: localize('speechLanguage.sv-SE', "Swedish (Sweden)") + }, + ['tr-TR']: { + name: localize('speechLanguage.tr-TR', "Turkish (Turkey)") + }, + ['zh-CN']: { + name: localize('speechLanguage.zh-CN', "Chinese (Simplified, China)") + }, + ['zh-HK']: { + name: localize('speechLanguage.zh-HK', "Chinese (Traditional, Hong Kong)") + }, + ['zh-TW']: { + name: localize('speechLanguage.zh-TW', "Chinese (Traditional, Taiwan)") + } +}; + +export function speechLanguageConfigToLanguage(config: unknown, lang = language): string { + if (typeof config === 'string') { + if (config === 'auto') { + if (lang !== 'en') { + const langParts = lang.split('-'); + + return speechLanguageConfigToLanguage(`${langParts[0]}-${(langParts[1] ?? langParts[0]).toUpperCase()}`); + } + } else { + if (SPEECH_LANGUAGES[config as keyof typeof SPEECH_LANGUAGES]) { + return config; + } + } + } + + return 'en-US'; +} diff --git a/src/vs/workbench/contrib/speech/test/common/speechService.test.ts b/src/vs/workbench/contrib/speech/test/common/speechService.test.ts new file mode 100644 index 00000000000..d757eace7e0 --- /dev/null +++ b/src/vs/workbench/contrib/speech/test/common/speechService.test.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { speechLanguageConfigToLanguage } from 'vs/workbench/contrib/speech/common/speechService'; + +suite('SpeechService', () => { + + test('resolve language', async () => { + assert.strictEqual(speechLanguageConfigToLanguage(undefined), 'en-US'); + assert.strictEqual(speechLanguageConfigToLanguage(3), 'en-US'); + assert.strictEqual(speechLanguageConfigToLanguage('foo'), 'en-US'); + assert.strictEqual(speechLanguageConfigToLanguage('foo-bar'), 'en-US'); + + assert.strictEqual(speechLanguageConfigToLanguage('tr-TR'), 'tr-TR'); + assert.strictEqual(speechLanguageConfigToLanguage('zh-TW'), 'zh-TW'); + + assert.strictEqual(speechLanguageConfigToLanguage('auto', 'en'), 'en-US'); + assert.strictEqual(speechLanguageConfigToLanguage('auto', 'tr'), 'tr-TR'); + assert.strictEqual(speechLanguageConfigToLanguage('auto', 'zh-tw'), 'zh-TW'); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); +}); From c036c4dadf9d26ea3a6dffdc73ca3f1d04506b6b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 27 Feb 2024 09:56:36 +0100 Subject: [PATCH 0207/1175] Support local install for more error cases (#206323) --- .../electron-sandbox/remoteExtensionManagementService.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts index 476712eaf1f..68181b0b830 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts @@ -65,7 +65,10 @@ export class NativeRemoteExtensionManagementService extends RemoteExtensionManag } catch (error) { switch (error.name) { case ExtensionManagementErrorCode.Download: + case ExtensionManagementErrorCode.DownloadSignature: + case ExtensionManagementErrorCode.Gallery: case ExtensionManagementErrorCode.Internal: + case ExtensionManagementErrorCode.Unknown: try { this.logService.error(`Error while installing '${extension.identifier.id}' extension in the remote server.`, toErrorMessage(error)); return await this.downloadAndInstall(extension, installOptions || {}); From 82bba2e78bbac1a35809e74cfc878df8a6f193ef Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 27 Feb 2024 11:06:22 +0100 Subject: [PATCH 0208/1175] fix #206249 (#206328) --- src/vs/workbench/api/browser/mainThreadLanguageModels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/browser/mainThreadLanguageModels.ts b/src/vs/workbench/api/browser/mainThreadLanguageModels.ts index 6e33d7da99d..f8167d4dc52 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageModels.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageModels.ts @@ -64,7 +64,7 @@ export class MainThreadLanguageModels implements MainThreadLanguageModelsShape { } dipsosables.add(Registry.as(Extensions.ExtensionFeaturesRegistry).registerExtensionFeature({ id: `lm-${identifier}`, - label: localize('languageModels', "Language Model ({0})", `${identifier}-${metadata.model}`), + label: localize('languageModels', "Language Model ({0})", `${identifier}`), access: { canToggle: false, }, From e9a8b6add5329f8124f23ff7143d95c22ce39f76 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 27 Feb 2024 12:14:40 +0100 Subject: [PATCH 0209/1175] Update grammars (#206330) --- extensions/dart/cgmanifest.json | 2 +- extensions/dart/syntaxes/dart.tmLanguage.json | 10 +- extensions/go/cgmanifest.json | 4 +- extensions/go/syntaxes/go.tmLanguage.json | 140 ++++++++++++------ extensions/latex/cgmanifest.json | 4 +- extensions/razor/cgmanifest.json | 2 +- .../razor/syntaxes/cshtml.tmLanguage.json | 80 +++++++++- extensions/scss/cgmanifest.json | 4 +- 8 files changed, 188 insertions(+), 58 deletions(-) diff --git a/extensions/dart/cgmanifest.json b/extensions/dart/cgmanifest.json index 0086a5158e5..9c90588adf1 100644 --- a/extensions/dart/cgmanifest.json +++ b/extensions/dart/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dart-lang/dart-syntax-highlight", "repositoryUrl": "https://github.com/dart-lang/dart-syntax-highlight", - "commitHash": "0a6648177bdbb91a4e1a38c16e57ede0ccba4f18" + "commitHash": "272e2f89f85073c04b7e15b582257f76d2489970" } }, "licenseDetail": [ diff --git a/extensions/dart/syntaxes/dart.tmLanguage.json b/extensions/dart/syntaxes/dart.tmLanguage.json index ae4db9698e9..cc9dee8d275 100644 --- a/extensions/dart/syntaxes/dart.tmLanguage.json +++ b/extensions/dart/syntaxes/dart.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/dart-lang/dart-syntax-highlight/commit/0a6648177bdbb91a4e1a38c16e57ede0ccba4f18", + "version": "https://github.com/dart-lang/dart-syntax-highlight/commit/272e2f89f85073c04b7e15b582257f76d2489970", "name": "Dart", "scopeName": "source.dart", "patterns": [ @@ -308,7 +308,7 @@ }, { "name": "keyword.control.dart", - "match": "(?\\-]+(?:\\s*)(?:\\/(?:\\/|\\*).*)?)$)", + "match": "(?:(?<=\\))(?:\\s*)((?:(?:\\s*(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?[\\w\\*\\.\\[\\]\\<\\>\\-]+(?:\\s*)(?:\\/(?:\\/|\\*).*)?)$)", "captures": { "1": { "patterns": [ @@ -1272,7 +1267,7 @@ "include": "#parameter-variable-types" }, { - "match": "(?:\\w+)", + "match": "\\w+", "name": "entity.name.type.go" } ] @@ -1513,7 +1508,7 @@ }, "functions_inline": { "comment": "functions in-line with multi return types", - "match": "(?:(\\bfunc\\b)((?:\\((?:[^/]*)\\))(?:\\s+)(?:\\((?:[^/]*)\\)))(?:\\s+)(?=\\{))", + "match": "(?:(\\bfunc\\b)((?:\\((?:[^/]*?)\\))(?:\\s+)(?:\\((?:[^/]*?)\\)))(?:\\s+)(?=\\{))", "captures": { "1": { "name": "keyword.function.go" @@ -1571,7 +1566,7 @@ }, "support_functions": { "comment": "Support Functions", - "match": "(?:(?:((?<=\\.)\\w+)|(\\w+))(\\[(?:(?:[\\w\\.\\*\\[\\]\\{\\}\"\\']+)(?:(?:\\,\\s*(?:[\\w\\.\\*\\[\\]\\{\\}]+))*))?\\])?(?=\\())", + "match": "(?:(?:((?<=\\.)\\b\\w+)|(\\b\\w+))(\\[(?:(?:[\\w\\.\\*\\[\\]\\{\\}\"\\']+)(?:(?:\\,\\s*(?:[\\w\\.\\*\\[\\]\\{\\}]+))*))?\\])?(?=\\())", "captures": { "1": { "name": "entity.name.function.support.go" @@ -1867,11 +1862,18 @@ "patterns": [ { "comment": "Struct variable for struct in struct types", - "begin": "(?:\\s*)?([\\s\\,\\w]+)(?:\\s+)(?:(?:[\\[\\]\\*])+)?(\\bstruct\\b)\\s*(\\{)", + "begin": "(?:(\\w+(?:\\,\\s*\\w+)*)(?:\\s+)(?:(?:[\\[\\]\\*])+)?(\\bstruct\\b)(?:\\s*)(\\{))", "beginCaptures": { "1": { - "match": "(?:\\w+)", - "name": "variable.other.property.go" + "patterns": [ + { + "include": "#type-declarations" + }, + { + "match": "\\w+", + "name": "variable.other.property.go" + } + ] }, "2": { "name": "keyword.struct.go" @@ -1911,6 +1913,42 @@ } }, "patterns": [ + { + "include": "#support_functions" + }, + { + "include": "#type-declarations-without-brackets" + }, + { + "begin": "(?:([\\w\\.\\*]+)?(\\[))", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#type-declarations" + }, + { + "match": "(?:\\w+)", + "name": "entity.name.type.go" + } + ] + }, + "2": { + "name": "punctuation.definition.begin.bracket.square.go" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.definition.end.bracket.square.go" + } + }, + "patterns": [ + { + "include": "#generic_param_types" + } + ] + }, { "begin": "\\(", "beginCaptures": { @@ -1927,18 +1965,12 @@ "patterns": [ { "include": "#function_param_types" - }, - { - "include": "$self" } ] }, { - "include": "#support_functions" - }, - { - "comment": "single declaration | with or declarations", - "match": "((?:\\s+\\|)?(?:[\\w\\.\\[\\]\\*]+)(?:\\s+\\|)?)", + "comment": "other types", + "match": "([\\w\\.]+)", "captures": { "1": { "patterns": [ @@ -1946,10 +1978,7 @@ "include": "#type-declarations" }, { - "include": "#generic_types" - }, - { - "match": "(?:\\w+)", + "match": "\\w+", "name": "entity.name.type.go" } ] @@ -2145,7 +2174,7 @@ }, "after_control_variables": { "comment": "After control variables, to not highlight as a struct/interface (before formatting with gofmt)", - "match": "(?:(?<=\\brange\\b|\\bswitch\\b|\\;|\\bif\\b|\\bfor\\b|\\<|\\>|\\<\\=|\\>\\=|\\=\\=|\\!\\=|\\w(?:\\+|/|\\-|\\*|\\%)|\\w(?:\\+|/|\\-|\\*|\\%)\\=|\\|\\||\\&\\&)(?:\\s*)([[:alnum:]\\-\\_\\!\\.\\[\\]\\<\\>\\=\\*/\\+\\%\\:]+)(?:\\s*)(?=\\{))", + "match": "(?:(?<=\\brange\\b|\\bswitch\\b|\\;|\\bif\\b|\\bfor\\b|\\<|\\>|\\<\\=|\\>\\=|\\=\\=|\\!\\=|\\w(?:\\+|/|\\-|\\*|\\%)|\\w(?:\\+|/|\\-|\\*|\\%)\\=|\\|\\||\\&\\&)(?:\\s*)((?![\\[\\]]+)[[:alnum:]\\-\\_\\!\\.\\[\\]\\<\\>\\=\\*/\\+\\%\\:]+)(?:\\s*)(?=\\{))", "captures": { "1": { "patterns": [ @@ -2234,7 +2263,7 @@ }, { "comment": "make keyword", - "match": "(?:(\\bmake\\b)(?:(\\()((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+(?:\\([^\\)]+\\))?)?(?:[\\w\\.\\*\\[\\]\\{\\}]+)?(?:\\[(?:[^\\]]+)?\\])?(?:[\\w\\.\\*\\[\\]\\{\\}]+)?)?((?:\\,\\s*(?:[\\w\\.\\(\\)]+)?)+)?(\\))))", + "match": "(?:(\\bmake\\b)(?:(\\()((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+(?:\\([^\\)]+\\))?)?(?:[\\w\\.\\*\\[\\]\\{\\}]+)?(?:\\[(?:[^\\]]+)?\\])?(?:[\\w\\.\\*\\[\\]\\{\\}]+)?)?((?:\\,\\s*(?:[\\w\\.\\(\\)/\\+\\-\\<\\>\\&\\|\\%\\*]+)?)+)?(\\))))", "captures": { "1": { "name": "entity.name.function.support.builtin.go" @@ -2291,6 +2320,7 @@ } }, "switch_types": { + "comment": "switch type assertions, only highlights types after case keyword", "begin": "(?<=\\bswitch\\b)(?:\\s*)(?:(\\w+\\s*\\:\\=)?\\s*([\\w\\.\\*\\(\\)\\[\\]]+))(\\.\\(\\btype\\b\\)\\s*)(\\{)", "beginCaptures": { "1": { @@ -2299,7 +2329,7 @@ "include": "#operators" }, { - "match": "(?:\\w+)", + "match": "\\w+", "name": "variable.other.assignment.go" } ] @@ -2313,7 +2343,7 @@ "include": "#type-declarations" }, { - "match": "(?:\\w+)", + "match": "\\w+", "name": "variable.other.go" } ] @@ -2344,9 +2374,7 @@ }, "patterns": [ { - "include": "#type-declarations" - }, - { + "comment": "types after case keyword with single line", "match": "(?:^\\s*(\\bcase\\b))(?:\\s+)([\\w\\.\\,\\*\\=\\<\\>\\!\\s]+)(:)(\\s*/(?:/|\\*)\\s*.*)?$", "captures": { "1": { @@ -2375,6 +2403,30 @@ } } }, + { + "comment": "types after case keyword with multi lines", + "begin": "\\bcase\\b", + "beginCaptures": { + "0": { + "name": "keyword.control.go" + } + }, + "end": "\\:", + "endCaptures": { + "0": { + "name": "punctuation.other.colon.go" + } + }, + "patterns": [ + { + "include": "#type-declarations" + }, + { + "match": "\\w+", + "name": "entity.name.type.go" + } + ] + }, { "include": "$self" } @@ -2573,7 +2625,7 @@ } }, "switch_select_case_variables": { - "comment": "variables after case control keyword in switch/select expression", + "comment": "variables after case control keyword in switch/select expression, to not scope them as property variables", "match": "(?:(?:^\\s*(\\bcase\\b))(?:\\s+)([\\s\\S]+(?:\\:)\\s*(?:/(?:/|\\*).*)?)$)", "captures": { "1": { @@ -2587,6 +2639,9 @@ { "include": "#support_functions" }, + { + "include": "#variable_assignment" + }, { "match": "\\w+", "name": "variable.other.go" @@ -2710,7 +2765,7 @@ }, "double_parentheses_types": { "comment": "double parentheses types", - "match": "(?:(\\((?:[\\w\\.\\[\\]\\*\\&]+)\\))(?=\\())", + "match": "(?:(? Date: Tue, 27 Feb 2024 12:15:12 +0100 Subject: [PATCH 0210/1175] Remove unused code-feature in release notes (#206331) --- src/vs/base/common/network.ts | 5 - .../browser/markdownSettingRenderer.ts | 44 +------- .../update/browser/releaseNotesEditor.ts | 104 +----------------- 3 files changed, 7 insertions(+), 146 deletions(-) diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 5cbdad174b7..cfc5b173887 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -117,11 +117,6 @@ export namespace Schemas { * Scheme used for special rendering of settings in the release notes */ export const codeSetting = 'code-setting'; - - /** - * Scheme used for special rendering of features in the release notes - */ - export const codeFeature = 'code-feature'; } export function matchesScheme(target: URI | string, scheme: string): boolean { diff --git a/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts b/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts index c4cf0aa94fe..095a2978564 100644 --- a/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts +++ b/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts @@ -17,7 +17,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; const codeSettingRegex = /^/; -const codeFeatureRegex = /^/; export class SimpleSettingRenderer { private _defaultSettings: DefaultSettings; @@ -45,10 +44,10 @@ export class SimpleSettingRenderer { getHtmlRenderer(): (html: string) => string { return (html): string => { - const match = codeSettingRegex.exec(html) ?? codeFeatureRegex.exec(html); + const match = codeSettingRegex.exec(html); if (match && match.length === 4) { const settingId = match[2]; - const rendered = this.render(settingId, match[3], match[1] === 'codefeature'); + const rendered = this.render(settingId, match[3]); if (rendered) { html = html.replace(codeSettingRegex, rendered); } @@ -61,10 +60,6 @@ export class SimpleSettingRenderer { return `${Schemas.codeSetting}://${settingId}${value ? `/${value}` : ''}`; } - featureToUriString(settingId: string, value?: any): string { - return `${Schemas.codeFeature}://${settingId}${value ? `/${value}` : ''}`; - } - private settingsGroups: ISettingsGroup[] | undefined = undefined; private getSetting(settingId: string): ISetting | undefined { if (!this.settingsGroups) { @@ -106,16 +101,13 @@ export class SimpleSettingRenderer { } } - private render(settingId: string, newValue: string, asFeature: boolean): string | undefined { + private render(settingId: string, newValue: string): string | undefined { const setting = this.getSetting(settingId); if (!setting) { return ''; } - if (asFeature) { - return this.renderFeature(setting, newValue); - } else { - return this.renderSetting(setting, newValue); - } + + return this.renderSetting(setting, newValue); } private viewInSettingsMessage(settingId: string, alreadyDisplayed: boolean) { @@ -176,15 +168,6 @@ export class SimpleSettingRenderer { `; } - private renderFeature(setting: ISetting, newValue: string): string | undefined { - const href = this.featureToUriString(setting.key, newValue); - const parsedValue = this.parseValue(setting.key, newValue); - const isChecked = this._configurationService.getValue(setting.key) === parsedValue; - this._featuredSettings.set(setting.key, parsedValue); - const title = nls.localize('changeFeatureTitle', "Toggle feature with setting {0}", setting.key); - return `
`; - } - private getSettingMessage(setting: ISetting, newValue: boolean | string | number): string | undefined { if (setting.type === 'boolean') { return this.booleanSettingMessage(setting, newValue as boolean); @@ -289,21 +272,6 @@ export class SimpleSettingRenderer { }); } - private async setFeatureState(uri: URI) { - const settingId = uri.authority; - const newSettingValue = this.parseValue(uri.authority, uri.path.substring(1)); - let valueToSetSetting: any; - if (this._updatedSettings.has(settingId)) { - valueToSetSetting = this._updatedSettings.get(settingId); - this._updatedSettings.delete(settingId); - } else if (newSettingValue !== this._configurationService.getValue(settingId)) { - valueToSetSetting = newSettingValue; - } else { - valueToSetSetting = undefined; - } - await this._configurationService.updateValue(settingId, valueToSetSetting, ConfigurationTarget.USER); - } - async updateSetting(uri: URI, x: number, y: number) { if (uri.scheme === Schemas.codeSetting) { type ReleaseNotesSettingUsedClassification = { @@ -318,8 +286,6 @@ export class SimpleSettingRenderer { settingId: uri.authority }); return this.showContextMenu(uri, x, y); - } else if (uri.scheme === Schemas.codeFeature) { - return this.setFeatureState(uri); } } } diff --git a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts index b5e2de0ed52..9f6cc07ba5f 100644 --- a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts +++ b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts @@ -38,7 +38,6 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService export class ReleaseNotesManager { private readonly _simpleSettingRenderer: SimpleSettingRenderer; private readonly _releaseNotesCache = new Map>(); - private scrollPosition: { x: number; y: number } | undefined; private _currentReleaseNotes: WebviewInput | undefined = undefined; private _lastText: string | undefined; @@ -72,11 +71,9 @@ export class ReleaseNotesManager { if (!this._currentReleaseNotes || !this._lastText) { return; } - const captureScroll = this.scrollPosition; const html = await this.renderBody(this._lastText); if (this._currentReleaseNotes) { this._currentReleaseNotes.webview.setHtml(html); - this._currentReleaseNotes.webview.postMessage({ type: 'setScroll', value: { scrollPosition: captureScroll } }); } } @@ -116,8 +113,6 @@ export class ReleaseNotesManager { disposables.add(this._currentReleaseNotes.webview.onMessage(e => { if (e.message.type === 'showReleaseNotes') { this._configurationService.updateValue('update.showReleaseNotes', e.message.value); - } else if (e.message.type === 'scroll') { - this.scrollPosition = e.message.value.scrollPosition; } else if (e.message.type === 'clickSetting') { const x = this._currentReleaseNotes?.webview.container.offsetLeft + e.message.value.x; const y = this._currentReleaseNotes?.webview.container.offsetTop + e.message.value.y; @@ -234,7 +229,7 @@ export class ReleaseNotesManager { } private async onDidClickLink(uri: URI) { - if (uri.scheme === Schemas.codeSetting || uri.scheme === Schemas.codeFeature) { + if (uri.scheme === Schemas.codeSetting) { // handled in receive message } else { this.addGAParameters(uri, 'ReleaseNotes') @@ -353,64 +348,6 @@ export class ReleaseNotesManager { margin-right: 8px; } - /* codefeature */ - - .codefeature-container { - display: flex; - } - - .codefeature { - position: relative; - display: inline-block; - width: 46px; - height: 24px; - } - - .codefeature-container input { - display: none; - } - - .toggle { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: var(--vscode-button-background); - transition: .4s; - border-radius: 24px; - } - - .toggle:before { - position: absolute; - content: ""; - height: 16px; - width: 16px; - left: 4px; - bottom: 4px; - background-color: var(--vscode-editor-foreground); - transition: .4s; - border-radius: 50%; - } - - input:checked+.codefeature > .toggle:before { - transform: translateX(22px); - } - - .codefeature-container:has(input) .title { - line-height: 30px; - padding-left: 4px; - font-weight: bold; - } - - .codefeature-container:has(input:checked) .title:after { - content: "${nls.localize('disableFeature', "Disable this feature")}"; - } - .codefeature-container:has(input:not(:checked)) .title:after { - content: "${nls.localize('enableFeature', "Enable this feature")}"; - } - header { display: flex; align-items: center; padding-top: 1em; } @@ -443,40 +380,13 @@ export class ReleaseNotesManager { window.addEventListener('message', event => { if (event.data.type === 'showReleaseNotes') { input.checked = event.data.value; - } else if (event.data.type === 'setScroll') { - window.scrollTo(event.data.value.scrollPosition.x, event.data.value.scrollPosition.y); - } else if (event.data.type === 'setFeaturedSettings') { - for (const [settingId, value] of event.data.value) { - const setting = document.getElementById(settingId); - if (setting instanceof HTMLInputElement) { - setting.checked = value; - } - } } }); - window.onscroll = () => { - vscode.postMessage({ - type: 'scroll', - value: { - scrollPosition: { - x: window.scrollX, - y: window.scrollY - } - } - }); - }; - window.addEventListener('click', event => { const href = event.target.href ?? event.target.parentElement.href ?? event.target.parentElement.parentElement?.href; - if (href && (href.startsWith('${Schemas.codeSetting}') || href.startsWith('${Schemas.codeFeature}'))) { + if (href && (href.startsWith('${Schemas.codeSetting}'))) { vscode.postMessage({ type: 'clickSetting', value: { uri: href, x: event.clientX, y: event.clientY }}); - if (href.startsWith('${Schemas.codeFeature}')) { - const featureInput = event.target.parentElement.previousSibling; - if (featureInput instanceof HTMLInputElement) { - featureInput.checked = !featureInput.checked; - } - } } }); @@ -506,7 +416,6 @@ export class ReleaseNotesManager { private onDidChangeActiveWebviewEditor(input: WebviewInput | undefined): void { if (input && input === this._currentReleaseNotes) { this.updateCheckboxWebview(); - this.updateFeaturedSettingsWebview(); } } @@ -518,13 +427,4 @@ export class ReleaseNotesManager { }); } } - - private updateFeaturedSettingsWebview() { - if (this._currentReleaseNotes) { - this._currentReleaseNotes.webview.postMessage({ - type: 'setFeaturedSettings', - value: this._simpleSettingRenderer.featuredSettingStates - }); - } - } } From e378f55a1f9c52edaa4284bf60e990ad3daf2897 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 27 Feb 2024 12:15:34 +0100 Subject: [PATCH 0211/1175] Add error logging for PortAttributesProvider (#206332) --- src/vs/workbench/api/common/extHostTunnelService.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/api/common/extHostTunnelService.ts b/src/vs/workbench/api/common/extHostTunnelService.ts index 7200372acca..650b852e1e4 100644 --- a/src/vs/workbench/api/common/extHostTunnelService.ts +++ b/src/vs/workbench/api/common/extHostTunnelService.ts @@ -103,6 +103,9 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe } registerPortsAttributesProvider(portSelector: PortAttributesSelector, provider: vscode.PortAttributesProvider): vscode.Disposable { + if (portSelector.portRange === undefined && portSelector.commandPattern === undefined) { + this.logService.error('PortAttributesProvider must specify either a portRange or a commandPattern'); + } const providerHandle = this.nextPortAttributesProviderHandle(); this._portAttributesProviders.set(providerHandle, { selector: portSelector, provider }); From 8f44284342a7fe8f566c52bebf8b30746fe5d63e Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 27 Feb 2024 12:28:09 +0100 Subject: [PATCH 0212/1175] add 'AI' as extension category --- src/vs/platform/extensions/common/extensions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 62977ee07e3..331aba1b55f 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -255,6 +255,7 @@ export const EXTENSION_CATEGORIES = [ 'Testing', 'Themes', 'Visualization', + 'AI', 'Chat', 'Other', ]; From cf03d0e639074ad51db82075e5a9745163af7f50 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 27 Feb 2024 14:17:19 +0100 Subject: [PATCH 0213/1175] Error: Uncaught exception in sharedProcess (fix #206035) (#206339) --- src/vs/base/parts/ipc/common/ipc.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index f943347519e..06aefade7ad 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -1093,6 +1093,9 @@ export namespace ProxyChannel { // Buffer any event that should be supported by // iterating over all property keys and finding them + // However, this will not work for services that + // are lazy and use a Proxy within. For that we + // still need to check later (see below). const mapEventNameToEvent = new Map>(); for (const key in handler) { if (propertyIsEvent(key)) { @@ -1108,11 +1111,17 @@ export namespace ProxyChannel { return eventImpl as Event; } - if (propertyIsDynamicEvent(event)) { - const target = handler[event]; - if (typeof target === 'function') { + const target = handler[event]; + if (typeof target === 'function') { + if (propertyIsDynamicEvent(event)) { return target.call(handler, arg); } + + if (propertyIsEvent(event)) { + mapEventNameToEvent.set(event, Event.buffer(handler[event] as Event, true, undefined, disposables)); + + return mapEventNameToEvent.get(event) as Event; + } } throw new ErrorNoTelemetry(`Event not found: ${event}`); From 0c3664116e7e594bbe5f6d4e5bfc032226d1b62e Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 27 Feb 2024 10:36:47 -0300 Subject: [PATCH 0214/1175] Warm up slash command cache- port candidate to main (#206302) Warm up slash command cache (#206290) Fix #206050 --- .../workbench/contrib/chat/common/chatAgents.ts | 15 +++++++++------ .../contrib/chat/common/chatServiceImpl.ts | 14 +++++++++++++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index 4ee4c07f0cf..b1ef64dae9d 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -107,7 +107,10 @@ export const IChatAgentService = createDecorator('chatAgentSe export interface IChatAgentService { _serviceBrand: undefined; - readonly onDidChangeAgents: Event; + /** + * undefined when an agent was removed + */ + readonly onDidChangeAgents: Event; registerAgent(agent: IChatAgent): IDisposable; invokeAgent(id: string, request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise; getFollowups(id: string, request: IChatAgentRequest, result: IChatAgentResult, token: CancellationToken): Promise; @@ -127,8 +130,8 @@ export class ChatAgentService extends Disposable implements IChatAgentService { private readonly _agents = new Map(); - private readonly _onDidChangeAgents = this._register(new Emitter()); - readonly onDidChangeAgents: Event = this._onDidChangeAgents.event; + private readonly _onDidChangeAgents = this._register(new Emitter()); + readonly onDidChangeAgents: Event = this._onDidChangeAgents.event; override dispose(): void { super.dispose(); @@ -140,11 +143,11 @@ export class ChatAgentService extends Disposable implements IChatAgentService { throw new Error(`Already registered an agent with id ${agent.id}`); } this._agents.set(agent.id, { agent }); - this._onDidChangeAgents.fire(); + this._onDidChangeAgents.fire(agent); return toDisposable(() => { if (this._agents.delete(agent.id)) { - this._onDidChangeAgents.fire(); + this._onDidChangeAgents.fire(undefined); } }); } @@ -155,7 +158,7 @@ export class ChatAgentService extends Disposable implements IChatAgentService { throw new Error(`No agent with id ${id} registered`); } data.agent.metadata = { ...data.agent.metadata, ...updateMetadata }; - this._onDidChangeAgents.fire(); + this._onDidChangeAgents.fire(data.agent); } getDefaultAgent(): IChatAgent | undefined { diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 6d89b981afc..e28b1e14e28 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -20,7 +20,7 @@ import { Progress } from 'vs/platform/progress/common/progress'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IChatAgentRequest, IChatAgentResult, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatAgent, IChatAgentRequest, IChatAgentResult, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { ChatModel, ChatModelInitState, ChatRequestModel, ChatWelcomeMessageModel, IChatModel, IChatRequestVariableData, ISerializableChatData, ISerializableChatsData, getHistoryEntriesFromModel, updateRanges } from 'vs/workbench/contrib/chat/common/chatModel'; import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, IParsedChatRequest, getPromptText } from 'vs/workbench/contrib/chat/common/chatParserTypes'; @@ -193,6 +193,12 @@ export class ChatService extends Disposable implements IChatService { } this._register(storageService.onWillSaveState(() => this.saveState())); + + this._register(Event.debounce(this.chatAgentService.onDidChangeAgents, () => { }, 500)(() => { + for (const model of this._sessionModels.values()) { + this.warmSlashCommandCache(model); + } + })); } private saveState(): void { @@ -358,9 +364,15 @@ export class ChatService extends Disposable implements IChatService { this.initializeSession(model, CancellationToken.None); } + private warmSlashCommandCache(model: IChatModel, agent?: IChatAgent) { + const agents = agent ? [agent] : this.chatAgentService.getAgents(); + agents.forEach(agent => agent.provideSlashCommands(model, [], CancellationToken.None)); + } + private async initializeSession(model: ChatModel, token: CancellationToken): Promise { try { this.trace('initializeSession', `Initialize session ${model.sessionId}`); + this.warmSlashCommandCache(model); model.startInitialize(); await this.extensionService.activateByEvent(`onInteractiveSession:${model.providerId}`); From 03bd0bb8d117dbe4ea13da19b03b82e8b70194a8 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 27 Feb 2024 15:16:20 +0100 Subject: [PATCH 0215/1175] Commenting range resource change proposal (#206346) Part of #185551 --- src/vs/editor/common/languages.ts | 8 +++++++ .../api/browser/mainThreadComments.ts | 8 +++++++ .../workbench/api/common/extHost.protocol.ts | 1 + .../workbench/api/common/extHostComments.ts | 11 +++++++++- .../comments/browser/commentService.ts | 21 ++++++++++++++++++- .../comments/browser/commentsController.ts | 7 ++++++- .../common/extensionsApiProposals.ts | 1 + ...posed.commentingRangeResourcesChanged.d.ts | 11 ++++++++++ 8 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.commentingRangeResourcesChanged.d.ts diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 16550bdef8d..01eb0df109e 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -1888,6 +1888,14 @@ export interface CommentThreadChangedEvent { readonly changed: CommentThread[]; } +/** + * @internal + */ +export interface CommentingRangeResources { + schemes: string[]; + uris: URI[]; +} + export interface CodeLens { range: IRange; id?: string; diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index 538c754e1b3..f885f89333a 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -550,6 +550,14 @@ export class MainThreadComments extends Disposable implements MainThreadComments provider.updateFeatures(features); } + async $onDidChangeResourcesWithCommentingRanges(handle: number, schemes: string[], uris: UriComponents[]): Promise { + const provider = this._commentControllers.get(handle); + if (!provider) { + return; + } + this._commentService.setResourcesWithCommentingRanges(provider.id, { schemes, uris: uris.map(uri => URI.revive(uri)) }); + } + $createCommentThread(handle: number, commentThreadHandle: number, threadId: string, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 29b99b8cc41..b512d5da601 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -146,6 +146,7 @@ export interface MainThreadCommentsShape extends IDisposable { $updateCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, changes: CommentThreadChanges): void; $deleteCommentThread(handle: number, commentThreadHandle: number): void; $updateCommentingRanges(handle: number): void; + $onDidChangeResourcesWithCommentingRanges(handle: number, schemes: string[], uris: UriComponents[]): Promise; } export interface AuthenticationForceNewSessionOptions { diff --git a/src/vs/workbench/api/common/extHostComments.ts b/src/vs/workbench/api/common/extHostComments.ts index 0f04b3f2455..18764746b77 100644 --- a/src/vs/workbench/api/common/extHostComments.ts +++ b/src/vs/workbench/api/common/extHostComments.ts @@ -563,8 +563,17 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo return this._commentingRangeProvider; } + private _commentingRangeProviderResourcesChanged: vscode.Disposable | undefined; set commentingRangeProvider(provider: vscode.CommentingRangeProvider | undefined) { this._commentingRangeProvider = provider; + this._commentingRangeProviderResourcesChanged?.dispose(); + this._commentingRangeProviderResourcesChanged = undefined; + if (this._commentingRangeProvider?.onDidChangeResourcesWithCommentingRanges) { + checkProposedApiEnabled(this._extension, 'commentingRangeResourcesChanged'); + this._commentingRangeProviderResourcesChanged = this._commentingRangeProvider.onDidChangeResourcesWithCommentingRanges(e => { + proxy.$onDidChangeResourcesWithCommentingRanges(this.handle, e.schemes, e.resources); + }); + } proxy.$updateCommentingRanges(this.handle); } @@ -695,7 +704,7 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo this._threads.forEach(value => { value.dispose(); }); - + this._commentingRangeProviderResourcesChanged?.dispose(); this._localDisposables.forEach(disposable => disposable.dispose()); } } diff --git a/src/vs/workbench/contrib/comments/browser/commentService.ts b/src/vs/workbench/contrib/comments/browser/commentService.ts index 1072a60aedf..2d9dcdde5e6 100644 --- a/src/vs/workbench/contrib/comments/browser/commentService.ts +++ b/src/vs/workbench/contrib/comments/browser/commentService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CommentThreadChangedEvent, CommentInfo, Comment, CommentReaction, CommentingRanges, CommentThread, CommentOptions, PendingCommentThread } from 'vs/editor/common/languages'; +import { CommentThreadChangedEvent, CommentInfo, Comment, CommentReaction, CommentingRanges, CommentThread, CommentOptions, PendingCommentThread, CommentingRangeResources } from 'vs/editor/common/languages'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; @@ -112,6 +112,8 @@ export interface ICommentService { enableCommenting(enable: boolean): void; registerContinueOnCommentProvider(provider: IContinueOnCommentProvider): IDisposable; removeContinueOnComment(pendingComment: { range: IRange | undefined; uri: URI; owner: string; isReply?: boolean }): PendingCommentThread | undefined; + setResourcesWithCommentingRanges(owner: string, resources: CommentingRangeResources): void; + resourceHasCommentingRanges(resource: URI): boolean; } const CONTINUE_ON_COMMENTS = 'comments.continueOnComments'; @@ -169,6 +171,8 @@ export class CommentService extends Disposable implements ICommentService { private readonly _commentsModel: CommentsModel = this._register(new CommentsModel()); public readonly commentsModel: ICommentsModel = this._commentsModel; + private _commentingRangeResources = new Map(); // owner -> CommentingRangeResources + constructor( @IInstantiationService protected readonly instantiationService: IInstantiationService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @@ -494,4 +498,19 @@ export class CommentService extends Disposable implements ICommentService { } return changedOwners; } + + setResourcesWithCommentingRanges(owner: string, resources: CommentingRangeResources): void { + this._commentingRangeResources.set(owner, resources); + } + + resourceHasCommentingRanges(resource: URI): boolean { + for (const resources of this._commentingRangeResources.values()) { + if (resources.schemes.includes(resource.scheme)) { + return true; + } else if (resources.uris.some(uri => uri.toString() === resource.toString())) { + return true; + } + } + return false; + } } diff --git a/src/vs/workbench/contrib/comments/browser/commentsController.ts b/src/vs/workbench/contrib/comments/browser/commentsController.ts index 4faf49116c0..1f795367c89 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsController.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsController.ts @@ -780,6 +780,7 @@ export class CommentController implements IEditorContribution { public onModelChanged(): void { this.localToDispose.clear(); + this.tryUpdateReservedSpace(); this.removeCommentWidgetsAndStoreCache(); if (!this.editor) { @@ -1194,10 +1195,14 @@ export class CommentController implements IEditorContribution { return; } - const hasCommentsOrRanges = this._commentInfos.some(info => { + const hasCommentsOrRangesInInfo = this._commentInfos.some(info => { const hasRanges = Boolean(info.commentingRanges && (Array.isArray(info.commentingRanges) ? info.commentingRanges : info.commentingRanges.ranges).length); return hasRanges || (info.threads.length > 0); }); + const uri = this.editor.getModel()?.uri; + const resourceHasCommentingRanges = uri ? this.commentService.resourceHasCommentingRanges(uri) : false; + + const hasCommentsOrRanges = hasCommentsOrRangesInInfo || resourceHasCommentingRanges; if (hasCommentsOrRanges && !this._commentingRangeSpaceReserved && this.commentService.isCommentingEnabled) { this._commentingRangeSpaceReserved = true; diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 76ffed1feeb..48e060c3e8f 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -20,6 +20,7 @@ export const allApiProposals = Object.freeze({ codeActionRanges: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codeActionRanges.d.ts', codiconDecoration: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codiconDecoration.d.ts', commentReactor: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.commentReactor.d.ts', + commentingRangeResourcesChanged: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.commentingRangeResourcesChanged.d.ts', commentsDraftState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.commentsDraftState.d.ts', contribCommentEditorActionsMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribCommentEditorActionsMenu.d.ts', contribCommentPeekContext: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribCommentPeekContext.d.ts', diff --git a/src/vscode-dts/vscode.proposed.commentingRangeResourcesChanged.d.ts b/src/vscode-dts/vscode.proposed.commentingRangeResourcesChanged.d.ts new file mode 100644 index 00000000000..7a4f22aecf7 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.commentingRangeResourcesChanged.d.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * 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 CommentingRangeProvider { + readonly onDidChangeResourcesWithCommentingRanges?: Event<{ schemes: string[]; resources: Uri[] }>; + } +} From 3b8fe3ec81c6f49d109830db8b775beba23196af Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:55:13 +0100 Subject: [PATCH 0216/1175] Add custom hover to highlight labels and links (#206351) Add custom hover to highlight labels and Links --- .../browser/ui/actionbar/actionViewItems.ts | 2 +- .../ui/highlightedlabel/highlightedLabel.ts | 23 +++++++++++--- src/vs/base/browser/ui/iconLabel/iconLabel.ts | 14 +++++---- src/vs/base/browser/ui/icons/iconSelectBox.ts | 2 +- .../test/browser/highlightedLabel.test.ts | 4 +++ .../gotoSymbol/browser/peek/referencesTree.ts | 9 ++++-- src/vs/platform/opener/browser/link.ts | 19 +++++++++--- .../bulkEdit/browser/preview/bulkEditTree.ts | 2 +- .../browser/outline/documentSymbolsTree.ts | 6 +++- .../contrib/debug/browser/baseDebugView.ts | 2 +- .../contrib/debug/browser/callStackView.ts | 7 +++-- .../contrib/debug/browser/replViewer.ts | 4 +-- .../contrib/debug/browser/variablesView.ts | 2 +- .../debug/test/browser/baseDebugView.test.ts | 4 +-- .../contrib/markers/browser/markersTable.ts | 31 +++++++++++++------ .../markers/browser/markersTreeViewer.ts | 24 ++++++++------ .../preferences/browser/keybindingsEditor.ts | 18 +++++++---- 17 files changed, 117 insertions(+), 56 deletions(-) diff --git a/src/vs/base/browser/ui/actionbar/actionViewItems.ts b/src/vs/base/browser/ui/actionbar/actionViewItems.ts index 698d711f4dc..df6ec99660c 100644 --- a/src/vs/base/browser/ui/actionbar/actionViewItems.ts +++ b/src/vs/base/browser/ui/actionbar/actionViewItems.ts @@ -227,7 +227,7 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem { this.updateAriaLabel(); if (this.options.hoverDelegate?.showNativeHover) { - /* While custom hover is not supported with context view */ + /* While custom hover is not inside custom hover */ this.element.title = title; } else { if (!this.customHover && title !== '') { diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index c2b41545d79..5aaa8bcc551 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -4,7 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { Disposable } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; /** @@ -22,13 +26,15 @@ export interface IHighlightedLabelOptions { * Whether the label supports rendering icons. */ readonly supportIcons?: boolean; + + readonly hoverDelegate?: IHoverDelegate; } /** * A widget which can render a label with substring highlights, often * originating from a filter function like the fuzzy matcher. */ -export class HighlightedLabel { +export class HighlightedLabel extends Disposable { private readonly domNode: HTMLElement; private text: string = ''; @@ -36,13 +42,16 @@ export class HighlightedLabel { private highlights: readonly IHighlight[] = []; private supportIcons: boolean; private didEverRender: boolean = false; + private customHover: ICustomHover | undefined; /** * Create a new {@link HighlightedLabel}. * * @param container The parent container to append to. */ - constructor(container: HTMLElement, options?: IHighlightedLabelOptions) { + constructor(container: HTMLElement, private readonly options?: IHighlightedLabelOptions) { + super(); + this.supportIcons = options?.supportIcons ?? false; this.domNode = dom.append(container, dom.$('span.monaco-highlighted-label')); } @@ -125,10 +134,16 @@ export class HighlightedLabel { dom.reset(this.domNode, ...children); - if (this.title) { + if (this.options?.hoverDelegate?.showNativeHover) { + /* While custom hover is not inside custom hover */ this.domNode.title = this.title; } else { - this.domNode.removeAttribute('title'); + if (!this.customHover && this.title !== '') { + const hoverDelegate = this.options?.hoverDelegate ?? getDefaultHoverDelegate('mouse'); + this.customHover = this._store.add(setupCustomHover(hoverDelegate, this.domNode, this.title)); + } else if (this.customHover) { + this.customHover.update(this.title); + } } this.didEverRender = true; diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 5bf35b8ffc2..c0e0544a93b 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -109,7 +109,7 @@ export class IconLabel extends Disposable { this.nameContainer = dom.append(this.labelContainer, dom.$('span.monaco-icon-name-container')); if (options?.supportHighlights || options?.supportIcons) { - this.nameNode = new LabelWithHighlights(this.nameContainer, !!options.supportIcons); + this.nameNode = this._register(new LabelWithHighlights(this.nameContainer, !!options.supportIcons)); } else { this.nameNode = new Label(this.nameContainer); } @@ -218,7 +218,7 @@ export class IconLabel extends Disposable { if (!this.descriptionNode) { const descriptionContainer = this._register(new FastLabelNode(dom.append(this.labelContainer, dom.$('span.monaco-icon-description-container')))); if (this.creationOptions?.supportDescriptionHighlights) { - this.descriptionNode = new HighlightedLabel(dom.append(descriptionContainer.element, dom.$('span.label-description')), { supportIcons: !!this.creationOptions.supportIcons }); + this.descriptionNode = this._register(new HighlightedLabel(dom.append(descriptionContainer.element, dom.$('span.label-description')), { supportIcons: !!this.creationOptions.supportIcons })); } else { this.descriptionNode = this._register(new FastLabelNode(dom.append(descriptionContainer.element, dom.$('span.label-description')))); } @@ -291,13 +291,15 @@ function splitMatches(labels: string[], separator: string, matches: readonly IMa }); } -class LabelWithHighlights { +class LabelWithHighlights extends Disposable { private label: string | string[] | undefined = undefined; private singleLabel: HighlightedLabel | undefined = undefined; private options: IIconLabelValueOptions | undefined; - constructor(private container: HTMLElement, private supportIcons: boolean) { } + constructor(private container: HTMLElement, private supportIcons: boolean) { + super(); + } setLabel(label: string | string[], options?: IIconLabelValueOptions): void { if (this.label === label && equals(this.options, options)) { @@ -311,7 +313,7 @@ class LabelWithHighlights { if (!this.singleLabel) { this.container.innerText = ''; this.container.classList.remove('multiple'); - this.singleLabel = new HighlightedLabel(dom.append(this.container, dom.$('a.label-name', { id: options?.domId })), { supportIcons: this.supportIcons }); + this.singleLabel = this._register(new HighlightedLabel(dom.append(this.container, dom.$('a.label-name', { id: options?.domId })), { supportIcons: this.supportIcons })); } this.singleLabel.set(label, options?.matches, undefined, options?.labelEscapeNewLines); @@ -329,7 +331,7 @@ class LabelWithHighlights { const id = options?.domId && `${options?.domId}_${i}`; const name = dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i, 'role': 'treeitem' }); - const highlightedLabel = new HighlightedLabel(dom.append(this.container, name), { supportIcons: this.supportIcons }); + const highlightedLabel = this._register(new HighlightedLabel(dom.append(this.container, name), { supportIcons: this.supportIcons })); highlightedLabel.set(l, m, undefined, options?.labelEscapeNewLines); if (i < label.length - 1) { diff --git a/src/vs/base/browser/ui/icons/iconSelectBox.ts b/src/vs/base/browser/ui/icons/iconSelectBox.ts index 465c1dc1181..b59529ffd81 100644 --- a/src/vs/base/browser/ui/icons/iconSelectBox.ts +++ b/src/vs/base/browser/ui/icons/iconSelectBox.ts @@ -81,7 +81,7 @@ export class IconSelectBox extends Disposable { dom.append(iconSelectBoxContainer, this.scrollableElement.getDomNode()); if (this.options.showIconInfo) { - this.iconIdElement = new HighlightedLabel(dom.append(dom.append(iconSelectBoxContainer, dom.$('.icon-select-id-container')), dom.$('.icon-select-id-label'))); + this.iconIdElement = this._register(new HighlightedLabel(dom.append(dom.append(iconSelectBoxContainer, dom.$('.icon-select-id-container')), dom.$('.icon-select-id-label')))); } const iconsDisposables = disposables.add(new MutableDisposable()); diff --git a/src/vs/base/test/browser/highlightedLabel.test.ts b/src/vs/base/test/browser/highlightedLabel.test.ts index 4f5eb5ca015..fe2ceb43d61 100644 --- a/src/vs/base/test/browser/highlightedLabel.test.ts +++ b/src/vs/base/test/browser/highlightedLabel.test.ts @@ -61,5 +61,9 @@ suite('HighlightedLabel', () => { assert.deepStrictEqual(highlights, [{ start: 5, end: 8 }, { start: 10, end: 11 }]); }); + teardown(() => { + label.dispose(); + }); + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesTree.ts b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesTree.ts index ec127689cb0..bb76dd67d6c 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesTree.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesTree.ts @@ -162,12 +162,14 @@ export class FileReferencesRenderer implements ITreeRenderer, index: number, templateData: OneReferenceTemplate): void { templateData.set(node.element, node.filterData); } - disposeTemplate(): void { + disposeTemplate(templateData: OneReferenceTemplate): void { + templateData.dispose(); } } diff --git a/src/vs/platform/opener/browser/link.ts b/src/vs/platform/opener/browser/link.ts index 2b455fa8dc6..a52cad5c5f0 100644 --- a/src/vs/platform/opener/browser/link.ts +++ b/src/vs/platform/opener/browser/link.ts @@ -12,6 +12,8 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import 'vs/css!./link'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; export interface ILinkDescriptor { readonly label: string | HTMLElement; @@ -28,6 +30,8 @@ export interface ILinkOptions { export class Link extends Disposable { private el: HTMLAnchorElement; + private hover?: ICustomHover; + private _enabled: boolean = true; get enabled(): boolean { @@ -68,9 +72,7 @@ export class Link extends Disposable { this.el.tabIndex = link.tabIndex; } - if (typeof link.title !== 'undefined') { - this.el.title = link.title; - } + this.setTooltip(link.title); this._link = link; } @@ -86,9 +88,10 @@ export class Link extends Disposable { this.el = append(container, $('a.monaco-link', { tabIndex: _link.tabIndex ?? 0, href: _link.href, - title: _link.title }, _link.label)); + this.setTooltip(_link.title); + this.el.setAttribute('role', 'button'); const onClickEmitter = this._register(new DomEmitter(this.el, 'click')); @@ -117,4 +120,12 @@ export class Link extends Disposable { this.enabled = true; } + + private setTooltip(title: string | undefined): void { + if (!this.hover && title) { + this.hover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.el, title)); + } else if (this.hover) { + this.hover.update(title); + } + } } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts index c5fd28d6a8e..e45a95008c3 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts @@ -580,7 +580,7 @@ class TextEditElementTemplate { this._icon = document.createElement('div'); container.appendChild(this._icon); - this._label = new HighlightedLabel(container); + this._label = this._disposables.add(new HighlightedLabel(container)); } dispose(): void { diff --git a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts index c4f2a2def5a..3d9af339abe 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts @@ -66,6 +66,10 @@ class DocumentSymbolGroupTemplate { readonly labelContainer: HTMLElement, readonly label: HighlightedLabel, ) { } + + dispose() { + this.label.dispose(); + } } class DocumentSymbolTemplate { @@ -107,7 +111,7 @@ export class DocumentSymbolGroupRenderer implements ITreeRenderer implements IT templateDisposable.add(setupCustomHover(getDefaultHoverDelegate('mouse'), lazyButton, localize('debug.lazyButton.tooltip', "Click to expand"))); const value = dom.append(expression, $('span.value')); - const label = new HighlightedLabel(name); + const label = templateDisposable.add(new HighlightedLabel(name)); const inputBoxContainer = dom.append(expression, $('.inputBoxContainer')); diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 139928ec9fe..b9d319bad85 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -541,8 +541,8 @@ class SessionsRenderer implements ICompressibleTreeRenderer { renderVariable(variable, { expression, name, value, label, lazyButton }, false, [], linkDetector); assert.strictEqual(value.textContent, 'hey'); assert.strictEqual(label.element.textContent, 'foo:'); - assert.strictEqual(label.element.title, 'string'); variable.value = isWindows ? 'C:\\foo.js:5' : '/foo.js:5'; expression = $('.'); @@ -122,8 +121,9 @@ suite('Debug - Base Debug View', () => { renderVariable(variable, { expression, name, value, label, lazyButton }, false, [], linkDetector); assert.strictEqual(name.className, 'virtual'); assert.strictEqual(label.element.textContent, 'console:'); - assert.strictEqual(label.element.title, 'console'); assert.strictEqual(value.className, 'value number'); + + label.dispose(); }); test('statusbar in debug mode', () => { diff --git a/src/vs/workbench/contrib/markers/browser/markersTable.ts b/src/vs/workbench/contrib/markers/browser/markersTable.ts index bb63d92e9d2..44467e7e01d 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTable.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTable.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; import { ITableContextMenuEvent, ITableEvent, ITableRenderer, ITableVirtualDelegate } from 'vs/base/browser/ui/table/table'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IOpenEvent, IWorkbenchTableOptions, WorkbenchTable } from 'vs/platform/list/browser/listService'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; @@ -43,6 +43,7 @@ interface IMarkerCodeColumnTemplateData { readonly sourceLabel: HighlightedLabel; readonly codeLabel: HighlightedLabel; readonly codeLink: Link; + readonly templateDisposable: DisposableStore; } interface IMarkerFileColumnTemplateData { @@ -121,17 +122,18 @@ class MarkerCodeColumnRenderer implements ITableRenderer { @@ -185,7 +189,9 @@ class MarkerMessageColumnRenderer implements ITableRenderer { @@ -216,7 +222,10 @@ class MarkerFileColumnRenderer implements ITableRenderer { @@ -236,7 +245,9 @@ class MarkerOwnerColumnRenderer implements ITableRenderer { diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 180a378ac81..a3556f80678 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -51,6 +51,8 @@ import { MarkersContextKeys, MarkersViewMode } from 'vs/workbench/contrib/marker import { unsupportedSchemas } from 'vs/platform/markers/common/markerService'; import { defaultCountBadgeStyles } from 'vs/platform/theme/browser/defaultStyles'; import Severity from 'vs/base/common/severity'; +import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; +import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; interface IResourceMarkersTemplateData { readonly resourceLabel: IResourceLabel; @@ -285,6 +287,7 @@ class MarkerWidget extends Disposable { private readonly icon: HTMLElement; private readonly iconContainer: HTMLElement; private readonly messageAndDetailsContainer: HTMLElement; + private readonly messageAndDetailsContainerHover: ICustomHover; private readonly disposables = this._register(new DisposableStore()); constructor( @@ -304,6 +307,7 @@ class MarkerWidget extends Disposable { this.iconContainer = dom.append(parent, dom.$('')); this.icon = dom.append(this.iconContainer, dom.$('')); this.messageAndDetailsContainer = dom.append(parent, dom.$('.marker-message-details-container')); + this.messageAndDetailsContainerHover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.messageAndDetailsContainer, '')); } render(element: Marker, filterData: MarkerFilterData | undefined): void { @@ -366,13 +370,13 @@ class MarkerWidget extends Disposable { const viewState = this.markersViewModel.getViewModel(element); const multiline = !viewState || viewState.multiline; const lineMatches = filterData && filterData.lineMatches || []; - this.messageAndDetailsContainer.title = element.marker.message; + this.messageAndDetailsContainerHover.update(element.marker.message); const lineElements: HTMLElement[] = []; for (let index = 0; index < (multiline ? lines.length : 1); index++) { const lineElement = dom.append(this.messageAndDetailsContainer, dom.$('.marker-message-line')); const messageElement = dom.append(lineElement, dom.$('.marker-message')); - const highlightedLabel = new HighlightedLabel(messageElement); + const highlightedLabel = this.disposables.add(new HighlightedLabel(messageElement)); highlightedLabel.set(lines[index].length > 1000 ? `${lines[index].substring(0, 1000)}...` : lines[index], lineMatches[index]); if (lines[index] === '') { lineElement.style.height = `${VirtualDelegate.LINE_HEIGHT}px`; @@ -387,18 +391,18 @@ class MarkerWidget extends Disposable { parent.classList.add('details-container'); if (marker.source || marker.code) { - const source = new HighlightedLabel(dom.append(parent, dom.$('.marker-source'))); + const source = this.disposables.add(new HighlightedLabel(dom.append(parent, dom.$('.marker-source')))); const sourceMatches = filterData && filterData.sourceMatches || []; source.set(marker.source, sourceMatches); if (marker.code) { if (typeof marker.code === 'string') { - const code = new HighlightedLabel(dom.append(parent, dom.$('.marker-code'))); + const code = this.disposables.add(new HighlightedLabel(dom.append(parent, dom.$('.marker-code')))); const codeMatches = filterData && filterData.codeMatches || []; code.set(marker.code, codeMatches); } else { const container = dom.$('.marker-code'); - const code = new HighlightedLabel(container); + const code = this.disposables.add(new HighlightedLabel(container)); const link = marker.code.target.toString(true); this.disposables.add(new Link(parent, { href: link, label: container, title: link }, undefined, this._openerService)); const codeMatches = filterData && filterData.codeMatches || []; @@ -443,15 +447,15 @@ export class RelatedInformationRenderer implements ITreeRenderer Date: Tue, 27 Feb 2024 10:10:08 -0600 Subject: [PATCH 0217/1175] progress on AI TextSearchProvider (#205319) These are the initial steps to having an API to contribute AI text results. --- .../workbench/api/browser/mainThreadSearch.ts | 24 +- .../workbench/api/common/extHost.api.impl.ts | 6 + .../workbench/api/common/extHost.protocol.ts | 2 + src/vs/workbench/api/common/extHostSearch.ts | 51 +++- .../api/test/node/extHostSearch.test.ts | 4 + .../search/test/browser/searchModel.test.ts | 26 +- .../common/extensionsApiProposals.ts | 1 + .../services/search/common/search.ts | 27 +- .../services/search/common/searchExtTypes.ts | 40 +++ .../services/search/common/searchService.ts | 78 +++++- .../search/common/textSearchManager.ts | 31 ++- .../services/search/node/textSearchManager.ts | 2 +- .../vscode.proposed.aiTextSearchProvider.d.ts | 261 ++++++++++++++++++ 13 files changed, 516 insertions(+), 37 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.aiTextSearchProvider.d.ts diff --git a/src/vs/workbench/api/browser/mainThreadSearch.ts b/src/vs/workbench/api/browser/mainThreadSearch.ts index 797a4ee92a4..236148f08ff 100644 --- a/src/vs/workbench/api/browser/mainThreadSearch.ts +++ b/src/vs/workbench/api/browser/mainThreadSearch.ts @@ -9,7 +9,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { IFileMatch, IFileQuery, IRawFileMatch2, ISearchComplete, ISearchCompleteStats, ISearchProgressItem, ISearchResultProvider, ISearchService, ITextQuery, QueryType, SearchProviderType } from 'vs/workbench/services/search/common/search'; +import { IFileMatch, IFileQuery, IRawFileMatch2, ISearchComplete, ISearchCompleteStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextQuery, QueryType, SearchProviderType } from 'vs/workbench/services/search/common/search'; import { ExtHostContext, ExtHostSearchShape, MainContext, MainThreadSearchShape } from '../common/extHost.protocol'; import { revive } from 'vs/base/common/marshalling'; @@ -38,6 +38,10 @@ export class MainThreadSearch implements MainThreadSearchShape { this._searchProvider.set(handle, new RemoteSearchProvider(this._searchService, SearchProviderType.text, scheme, handle, this._proxy)); } + $registerAITextSearchProvider(handle: number, scheme: string): void { + this._searchProvider.set(handle, new RemoteSearchProvider(this._searchService, SearchProviderType.aiText, scheme, handle, this._proxy)); + } + $registerFileSearchProvider(handle: number, scheme: string): void { this._searchProvider.set(handle, new RemoteSearchProvider(this._searchService, SearchProviderType.file, scheme, handle, this._proxy)); } @@ -64,7 +68,6 @@ export class MainThreadSearch implements MainThreadSearchShape { provider.handleFindMatch(session, data); } - $handleTelemetry(eventName: string, data: any): void { this._telemetryService.publicLog(eventName, data); } @@ -126,7 +129,7 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { return this.doSearch(query, onProgress, token); } - doSearch(query: ITextQuery | IFileQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): Promise { + doSearch(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): Promise { if (!query.folderQueries.length) { throw new Error('Empty folderQueries'); } @@ -134,9 +137,7 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { const search = new SearchOperation(onProgress); this._searches.set(search.id, search); - const searchP = query.type === QueryType.File - ? this._proxy.$provideFileSearchResults(this._handle, search.id, query, token) - : this._proxy.$provideTextSearchResults(this._handle, search.id, query, token); + const searchP = this._provideSearchResults(query, search.id, token); return Promise.resolve(searchP).then((result: ISearchCompleteStats) => { this._searches.delete(search.id); @@ -169,4 +170,15 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { } }); } + + private _provideSearchResults(query: ISearchQuery, session: number, token: CancellationToken): Promise { + switch (query.type) { + case QueryType.File: + return this._proxy.$provideFileSearchResults(this._handle, session, query, token); + case QueryType.Text: + return this._proxy.$provideTextSearchResults(this._handle, session, query, token); + default: + return this._proxy.$provideAITextSearchResults(this._handle, session, query, token); + } + } } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 65b718f3f05..20b128c9af4 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1112,6 +1112,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'textSearchProvider'); return extHostSearch.registerTextSearchProvider(scheme, provider); }, + registerAITextSearchProvider: (scheme: string, provider: vscode.AITextSearchProvider) => { + // there are some dependencies on textSearchProvider, so we need to check for both + checkProposedApiEnabled(extension, 'aiTextSearchProvider'); + checkProposedApiEnabled(extension, 'textSearchProvider'); + return extHostSearch.registerAITextSearchProvider(scheme, provider); + }, registerRemoteAuthorityResolver: (authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver) => { checkProposedApiEnabled(extension, 'resolvers'); return extensionService.registerRemoteAuthorityResolver(authorityPrefix, resolver); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index b512d5da601..869fc7dc50d 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1404,6 +1404,7 @@ export interface MainThreadLabelServiceShape extends IDisposable { export interface MainThreadSearchShape extends IDisposable { $registerFileSearchProvider(handle: number, scheme: string): void; + $registerAITextSearchProvider(handle: number, scheme: string): void; $registerTextSearchProvider(handle: number, scheme: string): void; $unregisterProvider(handle: number): void; $handleFileMatch(handle: number, session: number, data: UriComponents[]): void; @@ -1817,6 +1818,7 @@ export interface ExtHostSecretStateShape { export interface ExtHostSearchShape { $enableExtensionHostSearch(): void; $provideFileSearchResults(handle: number, session: number, query: search.IRawQuery, token: CancellationToken): Promise; + $provideAITextSearchResults(handle: number, session: number, query: search.IRawAITextQuery, token: CancellationToken): Promise; $provideTextSearchResults(handle: number, session: number, query: search.IRawTextQuery, token: CancellationToken): Promise; $clearCache(cacheKey: string): Promise; } diff --git a/src/vs/workbench/api/common/extHostSearch.ts b/src/vs/workbench/api/common/extHostSearch.ts index d0289669abf..c2e0b93f7b9 100644 --- a/src/vs/workbench/api/common/extHostSearch.ts +++ b/src/vs/workbench/api/common/extHostSearch.ts @@ -11,13 +11,14 @@ import { FileSearchManager } from 'vs/workbench/services/search/common/fileSearc import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; import { ILogService } from 'vs/platform/log/common/log'; -import { IRawFileQuery, ISearchCompleteStats, IFileQuery, IRawTextQuery, IRawQuery, ITextQuery, IFolderQuery } from 'vs/workbench/services/search/common/search'; +import { IRawFileQuery, ISearchCompleteStats, IFileQuery, IRawTextQuery, IRawQuery, ITextQuery, IFolderQuery, IRawAITextQuery, IAITextQuery } from 'vs/workbench/services/search/common/search'; import { URI, UriComponents } from 'vs/base/common/uri'; import { TextSearchManager } from 'vs/workbench/services/search/common/textSearchManager'; import { CancellationToken } from 'vs/base/common/cancellation'; export interface IExtHostSearch extends ExtHostSearchShape { registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider): IDisposable; + registerAITextSearchProvider(scheme: string, provider: vscode.AITextSearchProvider): IDisposable; registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider): IDisposable; doInternalFileSearchWithCustomCallback(query: IFileQuery, token: CancellationToken, handleFileMatch: (data: URI[]) => void): Promise; } @@ -31,6 +32,10 @@ export class ExtHostSearch implements ExtHostSearchShape { private readonly _textSearchProvider = new Map(); private readonly _textSearchUsedSchemes = new Set(); + + private readonly _aiTextSearchProvider = new Map(); + private readonly _aiTextSearchUsedSchemes = new Set(); + private readonly _fileSearchProvider = new Map(); private readonly _fileSearchUsedSchemes = new Set(); @@ -62,6 +67,22 @@ export class ExtHostSearch implements ExtHostSearchShape { }); } + registerAITextSearchProvider(scheme: string, provider: vscode.AITextSearchProvider): IDisposable { + if (this._aiTextSearchUsedSchemes.has(scheme)) { + throw new Error(`an AI text search provider for the scheme '${scheme}'is already registered`); + } + + this._aiTextSearchUsedSchemes.add(scheme); + const handle = this._handlePool++; + this._aiTextSearchProvider.set(handle, provider); + this._proxy.$registerAITextSearchProvider(handle, this._transformScheme(scheme)); + return toDisposable(() => { + this._aiTextSearchUsedSchemes.delete(scheme); + this._aiTextSearchProvider.delete(handle); + this._proxy.$unregisterProvider(handle); + }); + } + registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider): IDisposable { if (this._fileSearchUsedSchemes.has(scheme)) { throw new Error(`a file search provider for the scheme '${scheme}' is already registered`); @@ -86,7 +107,7 @@ export class ExtHostSearch implements ExtHostSearchShape { this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource)); }, token); } else { - throw new Error('unknown provider: ' + handle); + throw new Error('3 unknown provider: ' + handle); } } @@ -103,7 +124,7 @@ export class ExtHostSearch implements ExtHostSearchShape { $provideTextSearchResults(handle: number, session: number, rawQuery: IRawTextQuery, token: vscode.CancellationToken): Promise { const provider = this._textSearchProvider.get(handle); if (!provider || !provider.provideTextSearchResults) { - throw new Error(`Unknown provider ${handle}`); + throw new Error(`2 Unknown provider ${handle}`); } const query = reviveQuery(rawQuery); @@ -111,17 +132,35 @@ export class ExtHostSearch implements ExtHostSearchShape { return engine.search(progress => this._proxy.$handleTextMatch(handle, session, progress), token); } + $provideAITextSearchResults(handle: number, session: number, rawQuery: IRawAITextQuery, token: vscode.CancellationToken): Promise { + const provider = this._aiTextSearchProvider.get(handle); + if (!provider || !provider.provideAITextSearchResults) { + throw new Error(`1 Unknown provider ${handle}`); + } + + const query = reviveQuery(rawQuery); + const engine = this.createAITextSearchManager(query, provider); + return engine.search(progress => this._proxy.$handleTextMatch(handle, session, progress), token); + } + $enableExtensionHostSearch(): void { } protected createTextSearchManager(query: ITextQuery, provider: vscode.TextSearchProvider): TextSearchManager { - return new TextSearchManager(query, provider, { - readdir: resource => Promise.resolve([]), // TODO@rob implement + return new TextSearchManager({ query, provider }, { + readdir: resource => Promise.resolve([]), toCanonicalName: encoding => encoding }, 'textSearchProvider'); } + + protected createAITextSearchManager(query: IAITextQuery, provider: vscode.AITextSearchProvider): TextSearchManager { + return new TextSearchManager({ query, provider }, { + readdir: resource => Promise.resolve([]), + toCanonicalName: encoding => encoding + }, 'aiTextSearchProvider'); + } } -export function reviveQuery(rawQuery: U): U extends IRawTextQuery ? ITextQuery : IFileQuery { +export function reviveQuery(rawQuery: U): U extends IRawTextQuery ? ITextQuery : U extends IRawAITextQuery ? IAITextQuery : IFileQuery { return { ...rawQuery, // TODO@rob ??? ...{ diff --git a/src/vs/workbench/api/test/node/extHostSearch.test.ts b/src/vs/workbench/api/test/node/extHostSearch.test.ts index 1d903c40815..1502ef3f565 100644 --- a/src/vs/workbench/api/test/node/extHostSearch.test.ts +++ b/src/vs/workbench/api/test/node/extHostSearch.test.ts @@ -43,6 +43,10 @@ class MockMainThreadSearch implements MainThreadSearchShape { this.lastHandle = handle; } + $registerAITextSearchProvider(handle: number, scheme: string): void { + this.lastHandle = handle; + } + $unregisterProvider(handle: number): void { } diff --git a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts index 24e2448a9b3..17f9e88b338 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts @@ -14,7 +14,7 @@ import { ModelService } from 'vs/editor/common/services/modelService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextQuery, ITextSearchMatch, OneLineRange, QueryType, TextSearchMatch } from 'vs/workbench/services/search/common/search'; +import { IAITextQuery, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextQuery, ITextSearchMatch, OneLineRange, QueryType, TextSearchMatch } from 'vs/workbench/services/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { CellMatch, MatchInNotebook, SearchModel } from 'vs/workbench/contrib/search/browser/searchModel'; @@ -122,6 +122,14 @@ suite('SearchModel', () => { }); }, + aiTextSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void, notebookURIs?: ResourceSet): Promise { + return new Promise(resolve => { + queueMicrotask(() => { + results.forEach(onProgress!); + resolve(complete!); + }); + }); + }, textSearchSplitSyncAsync(query: ITextQuery, token?: CancellationToken | undefined, onProgress?: ((result: ISearchProgressItem) => void) | undefined): { syncResults: ISearchComplete; asyncResults: Promise } { return { syncResults: { @@ -153,6 +161,11 @@ suite('SearchModel', () => { }); }); }, + aiTextSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void, notebookURIs?: ResourceSet): Promise { + return new Promise((resolve, reject) => { + reject(error); + }); + }, textSearchSplitSyncAsync(query: ITextQuery, token?: CancellationToken | undefined, onProgress?: ((result: ISearchProgressItem) => void) | undefined): { syncResults: ISearchComplete; asyncResults: Promise } { return { syncResults: { @@ -188,6 +201,17 @@ suite('SearchModel', () => { }); }); }, + aiTextSearch(query: IAITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void, notebookURIs?: ResourceSet): Promise { + const disposable = token?.onCancellationRequested(() => tokenSource.cancel()); + if (disposable) { + store.add(disposable); + } + + return Promise.resolve({ + results: [], + messages: [] + }); + }, textSearchSplitSyncAsync(query: ITextQuery, token?: CancellationToken | undefined, onProgress?: ((result: ISearchProgressItem) => void) | undefined): { syncResults: ISearchComplete; asyncResults: Promise } { const disposable = token?.onCancellationRequested(() => tokenSource.cancel()); if (disposable) { diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 48e060c3e8f..7e70af867c3 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -8,6 +8,7 @@ export const allApiProposals = Object.freeze({ activeComment: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.activeComment.d.ts', aiRelatedInformation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.aiRelatedInformation.d.ts', + aiTextSearchProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.aiTextSearchProvider.d.ts', authGetSessions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authGetSessions.d.ts', authSession: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authSession.d.ts', canonicalUriProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.canonicalUriProvider.d.ts', diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index ca93adc467b..0ebb5a9f69d 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -44,6 +44,7 @@ export const ISearchService = createDecorator('searchService'); export interface ISearchService { readonly _serviceBrand: undefined; textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise; + aiTextSearch(query: IAITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise; textSearchSplitSyncAsync(query: ITextQuery, token?: CancellationToken | undefined, onProgress?: ((result: ISearchProgressItem) => void) | undefined, notebookFilesToIgnore?: ResourceSet, asyncNotebookFilesToIgnore?: Promise): { syncResults: ISearchComplete; asyncResults: Promise }; fileSearch(query: IFileQuery, token?: CancellationToken): Promise; clearCache(cacheKey: string): Promise; @@ -55,7 +56,8 @@ export interface ISearchService { */ export const enum SearchProviderType { file, - text + text, + aiText } export interface ISearchResultProvider { @@ -123,17 +125,32 @@ export interface ITextQueryProps extends ICommonQueryPr userDisabledExcludesAndIgnoreFiles?: boolean; } +export interface IAITextQueryProps extends ICommonQueryProps { + type: QueryType.aiText; + contentPattern: string; + + previewOptions?: ITextSearchPreviewOptions; + maxFileSize?: number; + afterContext?: number; + beforeContext?: number; + + userDisabledExcludesAndIgnoreFiles?: boolean; +} + export type IFileQuery = IFileQueryProps; export type IRawFileQuery = IFileQueryProps; export type ITextQuery = ITextQueryProps; export type IRawTextQuery = ITextQueryProps; +export type IAITextQuery = IAITextQueryProps; +export type IRawAITextQuery = IAITextQueryProps; -export type IRawQuery = IRawTextQuery | IRawFileQuery; -export type ISearchQuery = ITextQuery | IFileQuery; +export type IRawQuery = IRawTextQuery | IRawFileQuery | IRawAITextQuery; +export type ISearchQuery = ITextQuery | IFileQuery | IAITextQuery; export const enum QueryType { File = 1, - Text = 2 + Text = 2, + aiText = 3 } /* __GDPR__FRAGMENT__ @@ -249,7 +266,7 @@ export const enum SearchCompletionExitCode { } export interface ITextSearchStats { - type: 'textSearchProvider' | 'searchProcess'; + type: 'textSearchProvider' | 'searchProcess' | 'aiTextSearchProvider'; } export interface IFileSearchStats { diff --git a/src/vs/workbench/services/search/common/searchExtTypes.ts b/src/vs/workbench/services/search/common/searchExtTypes.ts index 6d037174bed..48da6b3f96d 100644 --- a/src/vs/workbench/services/search/common/searchExtTypes.ts +++ b/src/vs/workbench/services/search/common/searchExtTypes.ts @@ -221,6 +221,35 @@ export interface TextSearchOptions extends SearchOptions { */ afterContext?: number; } +/** + * Options that apply to AI text search. + */ +export interface AITextSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults: number; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; + + /** + * Exclude files larger than `maxFileSize` in bytes. + */ + maxFileSize?: number; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; +} /** * Represents the severity of a TextSearchComplete message. @@ -390,6 +419,17 @@ export interface TextSearchProvider { provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: IProgress, token: CancellationToken): ProviderResult; } +export interface AITextSearchProvider { + /** + * Provide results that match the given text pattern. + * @param query The parameter for this query. + * @param options A set of options to consider while searching. + * @param progress A progress callback that must be invoked for all results. + * @param token A cancellation token. + */ + provideAITextSearchResults(query: string, options: AITextSearchOptions, progress: IProgress, token: CancellationToken): ProviderResult; +} + /** * Options that can be set on a findTextInFiles search. */ diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index ac934456627..42ffb1111ed 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -21,7 +21,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, ISearchComplete, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, isFileMatch, isProgressMessage, ITextQuery, pathIncludedInQuery, QueryType, SEARCH_RESULT_LANGUAGE_ID, SearchError, SearchErrorCode, SearchProviderType } from 'vs/workbench/services/search/common/search'; +import { deserializeSearchError, FileMatch, IAITextQuery, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, ISearchComplete, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, isFileMatch, isProgressMessage, ITextQuery, pathIncludedInQuery, QueryType, SEARCH_RESULT_LANGUAGE_ID, SearchError, SearchErrorCode, SearchProviderType } from 'vs/workbench/services/search/common/search'; import { getTextSearchMatchWithModelContext, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; export class SearchService extends Disposable implements ISearchService { @@ -30,9 +30,11 @@ export class SearchService extends Disposable implements ISearchService { private readonly fileSearchProviders = new Map(); private readonly textSearchProviders = new Map(); + private readonly aiTextSearchProviders = new Map(); private deferredFileSearchesByScheme = new Map>(); private deferredTextSearchesByScheme = new Map>(); + private deferredAITextSearchesByScheme = new Map>(); private loggedSchemesMissingProviders = new Set(); @@ -57,6 +59,9 @@ export class SearchService extends Disposable implements ISearchService { } else if (type === SearchProviderType.text) { list = this.textSearchProviders; deferredMap = this.deferredTextSearchesByScheme; + } else if (type === SearchProviderType.aiText) { + list = this.aiTextSearchProviders; + deferredMap = this.deferredAITextSearchesByScheme; } else { throw new Error('Unknown SearchProviderType'); } @@ -84,6 +89,24 @@ export class SearchService extends Disposable implements ISearchService { }; } + async aiTextSearch(query: IAITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise { + const onProviderProgress = (progress: ISearchProgressItem) => { + // Match + if (onProgress) { // don't override open editor results + if (isFileMatch(progress)) { + onProgress(progress); + } else { + onProgress(progress); + } + } + + if (isProgressMessage(progress)) { + this.logService.debug('SearchService#search', progress.message); + } + }; + return this.doSearch(query, token, onProviderProgress); + } + textSearchSplitSyncAsync( query: ITextQuery, token?: CancellationToken | undefined, @@ -205,9 +228,7 @@ export class SearchService extends Disposable implements ISearchService { } private async waitForProvider(queryType: QueryType, scheme: string): Promise { - const deferredMap: Map> = queryType === QueryType.File ? - this.deferredFileSearchesByScheme : - this.deferredTextSearchesByScheme; + const deferredMap: Map> = this.getDeferredTextSearchesByScheme(queryType); if (deferredMap.has(scheme)) { return deferredMap.get(scheme)!.p; @@ -218,6 +239,32 @@ export class SearchService extends Disposable implements ISearchService { } } + private getSearchProvider(type: QueryType): Map { + switch (type) { + case QueryType.File: + return this.fileSearchProviders; + case QueryType.Text: + return this.textSearchProviders; + case QueryType.aiText: + return this.aiTextSearchProviders; + default: + throw new Error(`Unknown query type: ${type}`); + } + } + + private getDeferredTextSearchesByScheme(type: QueryType): Map> { + switch (type) { + case QueryType.File: + return this.deferredFileSearchesByScheme; + case QueryType.Text: + return this.deferredTextSearchesByScheme; + case QueryType.aiText: + return this.deferredAITextSearchesByScheme; + default: + throw new Error(`Unknown query type: ${type}`); + } + } + private async searchWithProviders(query: ISearchQuery, onProviderProgress: (progress: ISearchProgressItem) => void, token?: CancellationToken) { const e2eSW = StopWatch.create(false); @@ -225,16 +272,12 @@ export class SearchService extends Disposable implements ISearchService { const fqs = this.groupFolderQueriesByScheme(query); const someSchemeHasProvider = [...fqs.keys()].some(scheme => { - return query.type === QueryType.File ? - this.fileSearchProviders.has(scheme) : - this.textSearchProviders.has(scheme); + return this.getSearchProvider(query.type).has(scheme); }); await Promise.all([...fqs.keys()].map(async scheme => { const schemeFQs = fqs.get(scheme)!; - let provider = query.type === QueryType.File ? - this.fileSearchProviders.get(scheme) : - this.textSearchProviders.get(scheme); + let provider = this.getSearchProvider(query.type).get(scheme); if (!provider) { if (someSchemeHasProvider) { @@ -259,9 +302,18 @@ export class SearchService extends Disposable implements ISearchService { } }; - searchPs.push(query.type === QueryType.File ? - provider.fileSearch(oneSchemeQuery, token) : - provider.textSearch(oneSchemeQuery, onProviderProgress, token)); + const doProviderSearch = () => { + switch (query.type) { + case QueryType.File: + return provider.fileSearch(oneSchemeQuery, token); + case QueryType.Text: + return provider.textSearch(oneSchemeQuery, onProviderProgress, token); + default: + return provider.textSearch(oneSchemeQuery, onProviderProgress, token); + } + }; + + searchPs.push(doProviderSearch()); })); return Promise.all(searchPs).then(completes => { diff --git a/src/vs/workbench/services/search/common/textSearchManager.ts b/src/vs/workbench/services/search/common/textSearchManager.ts index cb4af3dd4e9..eb83a7ae27e 100644 --- a/src/vs/workbench/services/search/common/textSearchManager.ts +++ b/src/vs/workbench/services/search/common/textSearchManager.ts @@ -11,14 +11,20 @@ import { Schemas } from 'vs/base/common/network'; import * as path from 'vs/base/common/path'; import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { hasSiblingPromiseFn, IExtendedExtensionSearchOptions, IFileMatch, IFolderQuery, IPatternInfo, ISearchCompleteStats, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchResult, ITextSearchStats, QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/common/search'; -import { Range, TextSearchComplete, TextSearchMatch, TextSearchOptions, TextSearchProvider, TextSearchQuery, TextSearchResult } from 'vs/workbench/services/search/common/searchExtTypes'; +import { hasSiblingPromiseFn, IAITextQuery, IExtendedExtensionSearchOptions, IFileMatch, IFolderQuery, IPatternInfo, ISearchCompleteStats, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchResult, ITextSearchStats, QueryGlobTester, QueryType, resolvePatternsForProvider } from 'vs/workbench/services/search/common/search'; +import { AITextSearchProvider, Range, TextSearchComplete, TextSearchMatch, TextSearchOptions, TextSearchProvider, TextSearchQuery, TextSearchResult } from 'vs/workbench/services/search/common/searchExtTypes'; export interface IFileUtils { readdir: (resource: URI) => Promise; toCanonicalName: (encoding: string) => string; } +interface IAITextQueryProviderPair { + query: IAITextQuery; provider: AITextSearchProvider; +} +interface ITextQueryProviderPair { + query: ITextQuery; provider: TextSearchProvider; +} export class TextSearchManager { private collector: TextSearchResultsCollector | null = null; @@ -26,7 +32,13 @@ export class TextSearchManager { private isLimitHit = false; private resultCount = 0; - constructor(private query: ITextQuery, private provider: TextSearchProvider, private fileUtils: IFileUtils, private processType: ITextSearchStats['type']) { } + constructor(private queryProviderPair: IAITextQueryProviderPair | ITextQueryProviderPair, + private fileUtils: IFileUtils, + private processType: ITextSearchStats['type']) { } + + private get query() { + return this.queryProviderPair.query; + } search(onProgress: (matches: IFileMatch[]) => void, token: CancellationToken): Promise { const folderQueries = this.query.folderQueries || []; @@ -146,7 +158,14 @@ export class TextSearchManager { }; const searchOptions = this.getSearchOptionsForFolder(folderQuery); - const result = await this.provider.provideTextSearchResults(patternInfoToQuery(this.query.contentPattern), searchOptions, progress, token); + + + let result; + if (this.queryProviderPair.query.type === QueryType.aiText) { + result = await (this.queryProviderPair as IAITextQueryProviderPair).provider.provideAITextSearchResults(this.queryProviderPair.query.contentPattern, searchOptions, progress, token); + } else { + result = await (this.queryProviderPair as ITextQueryProviderPair).provider.provideTextSearchResults(patternInfoToQuery(this.queryProviderPair.query.contentPattern), searchOptions, progress, token); + } if (testingPs.length) { await Promise.all(testingPs); } @@ -196,7 +215,9 @@ export class TextSearchManager { afterContext: this.query.afterContext, beforeContext: this.query.beforeContext }; - (options).usePCRE2 = this.query.usePCRE2; + if ('usePCRE2' in this.query) { + (options).usePCRE2 = this.query.usePCRE2; + } return options; } } diff --git a/src/vs/workbench/services/search/node/textSearchManager.ts b/src/vs/workbench/services/search/node/textSearchManager.ts index 42cb5e59e80..34cf4cce311 100644 --- a/src/vs/workbench/services/search/node/textSearchManager.ts +++ b/src/vs/workbench/services/search/node/textSearchManager.ts @@ -12,7 +12,7 @@ import { TextSearchManager } from 'vs/workbench/services/search/common/textSearc export class NativeTextSearchManager extends TextSearchManager { constructor(query: ITextQuery, provider: TextSearchProvider, _pfs: typeof pfs = pfs, processType: ITextSearchStats['type'] = 'searchProcess') { - super(query, provider, { + super({ query, provider }, { readdir: resource => _pfs.Promises.readdir(resource.fsPath), toCanonicalName: name => toCanonicalName(name) }, processType); diff --git a/src/vscode-dts/vscode.proposed.aiTextSearchProvider.d.ts b/src/vscode-dts/vscode.proposed.aiTextSearchProvider.d.ts new file mode 100644 index 00000000000..93f9761211b --- /dev/null +++ b/src/vscode-dts/vscode.proposed.aiTextSearchProvider.d.ts @@ -0,0 +1,261 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/205317 + + /** + * The parameters of a query for text search. + */ + export interface TextSearchQuery { + /** + * The text pattern to search for. + */ + pattern: string; + + /** + * Whether or not `pattern` should match multiple lines of text. + */ + isMultiline?: boolean; + + /** + * Whether or not `pattern` should be interpreted as a regular expression. + */ + isRegExp?: boolean; + + /** + * Whether or not the search should be case-sensitive. + */ + isCaseSensitive?: boolean; + + /** + * Whether or not to search for whole word matches only. + */ + isWordMatch?: boolean; + } + + /** + * Options common to file and text search + */ + export interface SearchOptions { + /** + * The root folder to search within. + */ + folder: Uri; + + /** + * Files that match an `includes` glob pattern should be included in the search. + */ + includes: GlobString[]; + + /** + * Files that match an `excludes` glob pattern should be excluded from the search. + */ + excludes: GlobString[]; + + /** + * Whether external files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useIgnoreFiles"`. + */ + useIgnoreFiles: boolean; + + /** + * Whether symlinks should be followed while searching. + * See the vscode setting `"search.followSymlinks"`. + */ + followSymlinks: boolean; + + /** + * Whether global files that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useGlobalIgnoreFiles"`. + */ + useGlobalIgnoreFiles: boolean; + + /** + * Whether files in parent directories that exclude files, like .gitignore, should be respected. + * See the vscode setting `"search.useParentIgnoreFiles"`. + */ + useParentIgnoreFiles: boolean; + } + + /** + * Options to specify the size of the result text preview. + * These options don't affect the size of the match itself, just the amount of preview text. + */ + export interface TextSearchPreviewOptions { + /** + * The maximum number of lines in the preview. + * Only search providers that support multiline search will ever return more than one line in the match. + */ + matchLines: number; + + /** + * The maximum number of characters included per line. + */ + charsPerLine: number; + } + + /** + * Options that apply to AI text search. + */ + export interface AITextSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults: number; + + /** + * Options to specify the size of the result text preview. + */ + previewOptions?: TextSearchPreviewOptions; + + /** + * Exclude files larger than `maxFileSize` in bytes. + */ + maxFileSize?: number; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; + } + + + + /** + * A message regarding a completed search. + */ + export interface TextSearchCompleteMessage { + /** + * Markdown text of the message. + */ + text: string; + /** + * Whether the source of the message is trusted, command links are disabled for untrusted message sources. + * Messaged are untrusted by default. + */ + trusted?: boolean; + /** + * The message type, this affects how the message will be rendered. + */ + type: TextSearchCompleteMessageType; + } + + /** + * Information collected when text search is complete. + */ + export interface TextSearchComplete { + /** + * Whether the search hit the limit on the maximum number of search results. + * `maxResults` on {@linkcode AITextSearchOptions} specifies the max number of results. + * - If exactly that number of matches exist, this should be false. + * - If `maxResults` matches are returned and more exist, this should be true. + * - If search hits an internal limit which is less than `maxResults`, this should be true. + */ + limitHit?: boolean; + + /** + * Additional information regarding the state of the completed search. + * + * Messages with "Information" style support links in markdown syntax: + * - Click to [run a command](command:workbench.action.OpenQuickPick) + * - Click to [open a website](https://aka.ms) + * + * Commands may optionally return { triggerSearch: true } to signal to the editor that the original search should run be again. + */ + message?: TextSearchCompleteMessage | TextSearchCompleteMessage[]; + } + + /** + * A preview of the text result. + */ + export interface TextSearchMatchPreview { + /** + * The matching lines of text, or a portion of the matching line that contains the match. + */ + text: string; + + /** + * The Range within `text` corresponding to the text of the match. + * The number of matches must match the TextSearchMatch's range property. + */ + matches: Range | Range[]; + } + + /** + * A match from a text search + */ + export interface TextSearchMatch { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * The range of the match within the document, or multiple ranges for multiple matches. + */ + ranges: Range | Range[]; + + /** + * A preview of the text match. + */ + preview: TextSearchMatchPreview; + } + + /** + * A line of context surrounding a TextSearchMatch. + */ + export interface TextSearchContext { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * One line of text. + * previewOptions.charsPerLine applies to this + */ + text: string; + + /** + * The line number of this line of context. + */ + lineNumber: number; + } + + + /** + * An AITextSearchProvider provides additional AI text search results in the workspace. + */ + export interface AITextSearchProvider { + /** + * Provide results that match the given text pattern. + * @param query The parameter for this query. + * @param options A set of options to consider while searching. + * @param progress A progress callback that must be invoked for all results. + * @param token A cancellation token. + */ + provideAITextSearchResults(query: string, options: AITextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult; + } + + export namespace workspace { + /** + * Register an AI text search provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerAITextSearchProvider(scheme: string, provider: AITextSearchProvider): Disposable; + } +} From 1025d0dafbc2ab15f18f7e74fd0850ce38ba414e Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Tue, 27 Feb 2024 17:33:49 +0100 Subject: [PATCH 0218/1175] Render hovers over context views (#206353) render hovers over context views --- src/vs/base/browser/ui/contextview/contextview.ts | 5 ++++- src/vs/editor/browser/services/hoverService/hoverService.ts | 3 +++ src/vs/platform/contextview/browser/contextView.ts | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index af49847a810..a7debba4e6c 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -60,6 +60,9 @@ export interface IDelegate { canRelayout?: boolean; // default: true onDOMEvent?(e: Event, activeElement: HTMLElement): void; onHide?(data?: unknown): void; + + // context views with higher layers are rendered over contet views with lower layers + layer?: number; // Default: 0 } export interface IContextViewProvider { @@ -222,7 +225,7 @@ export class ContextView extends Disposable { this.view.className = 'context-view'; this.view.style.top = '0px'; this.view.style.left = '0px'; - this.view.style.zIndex = '2575'; + this.view.style.zIndex = `${2575 + (delegate.layer ?? 0)}`; this.view.style.position = this.useFixedPosition ? 'fixed' : 'absolute'; DOM.show(this.view); diff --git a/src/vs/editor/browser/services/hoverService/hoverService.ts b/src/vs/editor/browser/services/hoverService/hoverService.ts index f3d6b5c9f16..38357608b86 100644 --- a/src/vs/editor/browser/services/hoverService/hoverService.ts +++ b/src/vs/editor/browser/services/hoverService/hoverService.ts @@ -194,6 +194,9 @@ function getHoverOptionsIdentity(options: IHoverOptions | undefined): IHoverOpti class HoverContextViewDelegate implements IDelegate { + // Render over all other context views + public readonly layer = 1; + get anchorPosition() { return this._hover.anchor; } diff --git a/src/vs/platform/contextview/browser/contextView.ts b/src/vs/platform/contextview/browser/contextView.ts index 10158c8d75c..87c58811d8d 100644 --- a/src/vs/platform/contextview/browser/contextView.ts +++ b/src/vs/platform/contextview/browser/contextView.ts @@ -43,6 +43,9 @@ export interface IContextViewDelegate { focus?(): void; anchorAlignment?: AnchorAlignment; anchorAxisAlignment?: AnchorAxisAlignment; + + // context views with higher layers are rendered over contet views with lower layers + layer?: number; // Default: 0 } export const IContextMenuService = createDecorator('contextMenuService'); From ef300d9945b03454b84ac03d7e4562f3e9c39d75 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 27 Feb 2024 09:04:54 -0800 Subject: [PATCH 0219/1175] Fix extra background in markdown code blocks (#206304) Fixes #205129 --- .../contrib/markdown/browser/markdownDocumentRenderer.ts | 4 ++-- .../workbench/contrib/webview/browser/pre/index-no-csp.html | 4 ++++ src/vs/workbench/contrib/webview/browser/pre/index.html | 6 +++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts index 8ac086cada8..9b18b5cd028 100644 --- a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts +++ b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts @@ -205,7 +205,7 @@ export async function renderMarkdownDocument( } if (typeof lang !== 'string') { - callback(null, `${escape(code)}`); + callback(null, escape(code)); return ''; } @@ -217,7 +217,7 @@ export async function renderMarkdownDocument( const languageId = languageService.getLanguageIdByLanguageName(lang) ?? languageService.getLanguageIdByLanguageName(lang.split(/\s+|:|,|(?!^)\{|\?]/, 1)[0]); const html = await tokenizeToString(languageService, code, languageId); - callback(null, `${html}`); + callback(null, html); }); return ''; }; diff --git a/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html b/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html index 8b22da14204..45a4086cc9e 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html +++ b/src/vs/workbench/contrib/webview/browser/pre/index-no-csp.html @@ -128,6 +128,10 @@ border-radius: 4px; } + pre code { + padding: 0; + } + blockquote { background: var(--vscode-textBlockQuote-background); border-color: var(--vscode-textBlockQuote-border); diff --git a/src/vs/workbench/contrib/webview/browser/pre/index.html b/src/vs/workbench/contrib/webview/browser/pre/index.html index 277a619ddc7..0bf6401663d 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/index.html +++ b/src/vs/workbench/contrib/webview/browser/pre/index.html @@ -5,7 +5,7 @@ + content="default-src 'none'; script-src 'sha256-zUToXLPvfhhGwJ9G2aKXxI1DL+LnafBpNyx1mQ/S5qU=' 'self'; frame-src 'self'; style-src 'unsafe-inline';"> Date: Tue, 27 Feb 2024 18:14:05 +0100 Subject: [PATCH 0220/1175] refine language model (#206358) * api - use `LanguageModelChat` prefix for messages and response, add todos * update todos, tweak how `chatRequest` errors, remove `LanguageModelChatResponse#result` * api - refine language model access removes `requestLanguageModelAccess`, removes `LanguageModelChatResponse#result`, adds `LanguageModelChatRequestOptions`, refines how errors happen when making a chat request * use `throw` over Promise.reject * don't error from `_getAuthAccess`, polish error messages * rename to `sendChatRequest` --- .../workbench/api/common/extHost.api.impl.ts | 29 +-- .../api/common/extHostLanguageModels.ts | 185 ++++++++---------- .../api/common/extHostTypeConverters.ts | 16 +- src/vs/workbench/api/common/extHostTypes.ts | 6 +- .../vscode.proposed.chatProvider.d.ts | 2 +- .../vscode.proposed.languageModels.d.ts | 136 +++++-------- 6 files changed, 152 insertions(+), 222 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 20b128c9af4..1b9c7eb2b08 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +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'; @@ -1431,10 +1431,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // namespace: lm const lm: typeof vscode.lm = { - requestLanguageModelAccess(id, options) { - checkProposedApiEnabled(extension, 'languageModels'); - return extHostChatProvider.requestLanguageModelAccess(extension, id, options); - }, get languageModels() { checkProposedApiEnabled(extension, 'languageModels'); return extHostChatProvider.getLanguageModelIds(); @@ -1443,19 +1439,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'languageModels'); return extHostChatProvider.onDidChangeProviders(listener, thisArgs, disposables); }, - chatRequest(languageModel: string, messages: vscode.LanguageModelMessage[], optionsOrToken: { [name: string]: any } | vscode.CancellationToken, token?: vscode.CancellationToken) { + sendChatRequest(languageModel: string, messages: vscode.LanguageModelChatMessage[], options: vscode.LanguageModelChatRequestOptions, token: vscode.CancellationToken) { checkProposedApiEnabled(extension, 'languageModels'); - let options: Record; - if (CancellationToken.isCancellationToken(optionsOrToken)) { - options = {}; - token = optionsOrToken; - } else if (CancellationToken.isCancellationToken(token)) { - options = optionsOrToken; - token = token; - } else { - throw new Error('Invalid arguments'); - } - return extHostChatProvider.makeChatRequest(extension, languageModel, messages, options, token); + return extHostChatProvider.sendChatRequest(extension, languageModel, messages, options, token); } }; @@ -1699,9 +1685,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ChatResponseCommandButtonPart: extHostTypes.ChatResponseCommandButtonPart, ChatRequestTurn: extHostTypes.ChatRequestTurn, ChatResponseTurn: extHostTypes.ChatResponseTurn, - LanguageModelSystemMessage: extHostTypes.LanguageModelSystemMessage, - LanguageModelUserMessage: extHostTypes.LanguageModelUserMessage, - LanguageModelAssistantMessage: extHostTypes.LanguageModelAssistantMessage, + LanguageModelChatSystemMessage: extHostTypes.LanguageModelChatSystemMessage, + LanguageModelChatUserMessage: extHostTypes.LanguageModelChatUserMessage, + LanguageModelChatAssistantMessage: extHostTypes.LanguageModelChatAssistantMessage, + LanguageModelSystemMessage: extHostTypes.LanguageModelChatSystemMessage, + LanguageModelUserMessage: extHostTypes.LanguageModelChatUserMessage, + LanguageModelAssistantMessage: extHostTypes.LanguageModelChatAssistantMessage, NewSymbolName: extHostTypes.NewSymbolName, NewSymbolNameTag: extHostTypes.NewSymbolNameTag, InlineEdit: extHostTypes.InlineEdit, diff --git a/src/vs/workbench/api/common/extHostLanguageModels.ts b/src/vs/workbench/api/common/extHostLanguageModels.ts index aa45f8f58aa..e51c875984d 100644 --- a/src/vs/workbench/api/common/extHostLanguageModels.ts +++ b/src/vs/workbench/api/common/extHostLanguageModels.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostLanguageModelsShape, IMainContext, MainContext, MainThreadLanguageModelsShape } from 'vs/workbench/api/common/extHost.protocol'; @@ -12,12 +12,12 @@ import type * as vscode from 'vscode'; import { Progress } from 'vs/platform/progress/common/progress'; import { IChatMessage, IChatResponseFragment, ILanguageModelChatMetadata } from 'vs/workbench/contrib/chat/common/languageModels'; import { ExtensionIdentifier, ExtensionIdentifierMap, ExtensionIdentifierSet, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { AsyncIterableSource } from 'vs/base/common/async'; -import { Emitter, Event } from 'vs/base/common/event'; +import { AsyncIterableSource, Barrier } from 'vs/base/common/async'; +import { Emitter } from 'vs/base/common/event'; import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication'; import { localize } from 'vs/nls'; import { INTERNAL_AUTH_PROVIDER_PREFIX } from 'vs/workbench/services/authentication/common/authentication'; -import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { CancellationError } from 'vs/base/common/errors'; type LanguageModelData = { readonly extension: ExtensionIdentifier; @@ -36,43 +36,23 @@ class LanguageModelResponseStream { } } -class LanguageModelRequest { +class LanguageModelResponse { - static fromError(err: Error): vscode.LanguageModelResponse { - return new LanguageModelRequest(Promise.reject(err), new CancellationTokenSource()).apiObject; - } - - readonly apiObject: vscode.LanguageModelResponse; + readonly apiObject: vscode.LanguageModelChatResponse; private readonly _responseStreams = new Map(); private readonly _defaultStream = new AsyncIterableSource(); private _isDone: boolean = false; + private _isStreaming: boolean = false; + + constructor() { - constructor( - promise: Promise, - readonly cts: CancellationTokenSource - ) { const that = this; this.apiObject = { - result: promise, + // result: promise, stream: that._defaultStream.asyncIterable, - // responses: AsyncIterable[] // FUTURE responses per N + // streams: AsyncIterable[] // FUTURE responses per N }; - - promise.then(() => { - for (const stream of this._streams()) { - stream.resolve(); - } - }).catch(err => { - if (!(err instanceof Error)) { - err = new Error(toErrorMessage(err), { cause: err }); - } - for (const stream of this._streams()) { - stream.reject(err); - } - }).finally(() => { - this._isDone = true; - }); } private * _streams() { @@ -89,6 +69,7 @@ class LanguageModelRequest { if (this._isDone) { return; } + this._isStreaming = true; let res = this._responseStreams.get(fragment.index); if (!res) { if (this._responseStreams.size === 0) { @@ -102,6 +83,24 @@ class LanguageModelRequest { res.stream.emitOne(fragment.part); } + get isStreaming(): boolean { + return this._isStreaming; + } + + reject(err: Error): void { + this._isDone = true; + for (const stream of this._streams()) { + stream.reject(err); + } + } + + resolve(): void { + this._isDone = true; + for (const stream of this._streams()) { + stream.resolve(); + } + } + } export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { @@ -116,7 +115,7 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { private readonly _languageModels = new Map(); private readonly _languageModelIds = new Set(); // these are ALL models, not just the one in this EH private readonly _modelAccessList = new ExtensionIdentifierMap(); - private readonly _pendingRequest = new Map(); + private readonly _pendingRequest = new Map(); constructor( @@ -191,7 +190,7 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { // cancel pending requests for this model for (const [key, value] of this._pendingRequest) { if (value.languageModelId === id) { - value.res.cts.cancel(); + value.res.reject(new CancellationError()); this._pendingRequest.delete(key); } } @@ -227,86 +226,54 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { } } - async makeChatRequest(extension: IExtensionDescription, languageModelId: string, messages: vscode.LanguageModelMessage[], options: Record, token: CancellationToken) { + async sendChatRequest(extension: IExtensionDescription, languageModelId: string, messages: vscode.LanguageModelChatMessage[], options: vscode.LanguageModelChatRequestOptions, token: CancellationToken) { const from = extension.identifier; - // const justification = options?.justification; // TODO@jrieken - const metadata = await this._proxy.$prepareChatAccess(from, languageModelId, undefined); + const metadata = await this._proxy.$prepareChatAccess(from, languageModelId, options.justification); if (!metadata || !this._languageModelIds.has(languageModelId)) { - return LanguageModelRequest.fromError(new Error(`Language model ${languageModelId} is unknown`)); + throw new Error(`Language model '${languageModelId}' is unknown.`); } if (this._isUsingAuth(from, metadata)) { - await this._getAuthAccess(extension, { identifier: metadata.extension, displayName: metadata.auth.providerLabel }, undefined); + const success = await this._getAuthAccess(extension, { identifier: metadata.extension, displayName: metadata.auth.providerLabel }, options.justification, options.silent); - if (!this._modelAccessList.get(from)?.has(metadata.extension)) { - return LanguageModelRequest.fromError(new Error('Access to chat has been revoked')); + if (!success || !this._modelAccessList.get(from)?.has(metadata.extension)) { + throw new Error(`Language model '${languageModelId}' cannot be used by '${from.value}'.`); } } - const cts = new CancellationTokenSource(token); const requestId = (Math.random() * 1e6) | 0; - const requestPromise = this._proxy.$fetchResponse(from, languageModelId, requestId, messages.map(typeConvert.LanguageModelMessage.from), options ?? {}, cts.token); - const res = new LanguageModelRequest(requestPromise, cts); + const requestPromise = this._proxy.$fetchResponse(from, languageModelId, requestId, messages.map(typeConvert.LanguageModelMessage.from), options.modelOptions ?? {}, token); + + const barrier = new Barrier(); + + const res = new LanguageModelResponse(); this._pendingRequest.set(requestId, { languageModelId, res }); - requestPromise.finally(() => { + let error: Error | undefined; + + requestPromise.catch(err => { + if (barrier.isOpen()) { + // we received an error while streaming. this means we need to reject the "stream" + // because we have already returned the request object + res.reject(err); + } else { + error = err; + } + }).finally(() => { this._pendingRequest.delete(requestId); - cts.dispose(); + res.resolve(); + barrier.open(); }); - return res.apiObject; - } + await barrier.wait(); - async requestLanguageModelAccess(extension: IExtensionDescription, languageModelId: string, options?: vscode.LanguageModelAccessOptions): Promise { - const from = extension.identifier; - const justification = options?.justification; - const metadata = await this._proxy.$prepareChatAccess(from, languageModelId, justification); - - if (!metadata) { - throw new Error(`Language model '${languageModelId}' NOT found`); + if (error) { + throw new Error(`Language model '${languageModelId}' errored, check cause for more details`, { cause: error }); } - if (this._isUsingAuth(from, metadata)) { - await this._getAuthAccess(extension, { identifier: metadata.extension, displayName: metadata.auth.providerLabel }, justification); - } - - const that = this; - - return { - get model() { - return metadata.model; - }, - get isRevoked() { - return (that._isUsingAuth(from, metadata) && !that._modelAccessList.get(from)?.has(metadata.extension)) || !that._languageModelIds.has(languageModelId); - }, - get onDidChangeAccess() { - const onDidRemoveLM = Event.filter(that._onDidChangeProviders.event, e => e.removed.includes(languageModelId)); - const onDidChangeModelAccess = Event.filter(that._onDidChangeModelAccess.event, e => ExtensionIdentifier.equals(e.from, from) && ExtensionIdentifier.equals(e.to, metadata.extension)); - return Event.signal(Event.any(onDidRemoveLM, onDidChangeModelAccess)); - }, - makeChatRequest(messages, options, token) { - if (that._isUsingAuth(from, metadata) && !that._modelAccessList.get(from)?.has(metadata.extension)) { - throw new Error('Access to chat has been revoked'); - } - if (!that._languageModelIds.has(languageModelId)) { - throw new Error('Language Model has been removed'); - } - const cts = new CancellationTokenSource(token); - const requestId = (Math.random() * 1e6) | 0; - const requestPromise = that._proxy.$fetchResponse(from, languageModelId, requestId, messages.map(typeConvert.LanguageModelMessage.from), options ?? {}, cts.token); - const res = new LanguageModelRequest(requestPromise, cts); - that._pendingRequest.set(requestId, { languageModelId, res }); - - requestPromise.finally(() => { - that._pendingRequest.delete(requestId); - cts.dispose(); - }); - - return res.apiObject; - }, - }; + return res.apiObject; } async $handleResponseFragment(requestId: number, chunk: IChatResponseFragment): Promise { @@ -317,22 +284,32 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { } // BIG HACK: Using AuthenticationProviders to check access to Language Models - private async _getAuthAccess(from: IExtensionDescription, to: { identifier: ExtensionIdentifier; displayName: string }, justification?: string): Promise { + private async _getAuthAccess(from: IExtensionDescription, to: { identifier: ExtensionIdentifier; displayName: string }, justification: string | undefined, silent: boolean | undefined): Promise { // This needs to be done in both MainThread & ExtHost ChatProvider const providerId = INTERNAL_AUTH_PROVIDER_PREFIX + to.identifier.value; const session = await this._extHostAuthentication.getSession(from, providerId, [], { silent: true }); - if (!session) { - try { - const detail = justification - ? localize('chatAccessWithJustification', "To allow access to the language models provided by {0}. Justification:\n\n{1}", to.displayName, justification) - : localize('chatAccess', "To allow access to the language models provided by {0}", to.displayName); - await this._extHostAuthentication.getSession(from, providerId, [], { forceNewSession: { detail } }); - } catch (err) { - throw new Error('Access to language models has not been granted'); - } + + if (session) { + this.$updateModelAccesslist([{ from: from.identifier, to: to.identifier, enabled: true }]); + return true; } - this.$updateModelAccesslist([{ from: from.identifier, to: to.identifier, enabled: true }]); + if (silent) { + return false; + } + + try { + const detail = justification + ? localize('chatAccessWithJustification', "To allow access to the language models provided by {0}. Justification:\n\n{1}", to.displayName, justification) + : localize('chatAccess', "To allow access to the language models provided by {0}", to.displayName); + await this._extHostAuthentication.getSession(from, providerId, [], { forceNewSession: { detail } }); + this.$updateModelAccesslist([{ from: from.identifier, to: to.identifier, enabled: true }]); + return true; + + } catch (err) { + // ignore + return false; + } } private _isUsingAuth(from: ExtensionIdentifier, toMetadata: ILanguageModelChatMetadata): toMetadata is ILanguageModelChatMetadata & { auth: NonNullable } { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 816f0227361..68cbe330492 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2239,20 +2239,20 @@ export namespace ChatInlineFollowup { export namespace LanguageModelMessage { - export function to(message: chatProvider.IChatMessage): vscode.LanguageModelMessage { + export function to(message: chatProvider.IChatMessage): vscode.LanguageModelChatMessage { switch (message.role) { - case chatProvider.ChatMessageRole.System: return new types.LanguageModelSystemMessage(message.content); - case chatProvider.ChatMessageRole.User: return new types.LanguageModelUserMessage(message.content); - case chatProvider.ChatMessageRole.Assistant: return new types.LanguageModelAssistantMessage(message.content); + case chatProvider.ChatMessageRole.System: return new types.LanguageModelChatSystemMessage(message.content); + case chatProvider.ChatMessageRole.User: return new types.LanguageModelChatUserMessage(message.content); + case chatProvider.ChatMessageRole.Assistant: return new types.LanguageModelChatAssistantMessage(message.content); } } - export function from(message: vscode.LanguageModelMessage): chatProvider.IChatMessage { - if (message instanceof types.LanguageModelSystemMessage) { + export function from(message: vscode.LanguageModelChatMessage): chatProvider.IChatMessage { + if (message instanceof types.LanguageModelChatSystemMessage) { return { role: chatProvider.ChatMessageRole.System, content: message.content }; - } else if (message instanceof types.LanguageModelUserMessage) { + } else if (message instanceof types.LanguageModelChatUserMessage) { return { role: chatProvider.ChatMessageRole.User, content: message.content }; - } else if (message instanceof types.LanguageModelAssistantMessage) { + } else if (message instanceof types.LanguageModelChatAssistantMessage) { return { role: chatProvider.ChatMessageRole.Assistant, content: message.content }; } else { throw new Error('Invalid LanguageModelMessage'); diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 4fb94e3f1c8..4eec0964cd2 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4271,7 +4271,7 @@ export class ChatResponseTurn implements vscode.ChatResponseTurn { ) { } } -export class LanguageModelSystemMessage { +export class LanguageModelChatSystemMessage { content: string; constructor(content: string) { @@ -4279,7 +4279,7 @@ export class LanguageModelSystemMessage { } } -export class LanguageModelUserMessage { +export class LanguageModelChatUserMessage { content: string; name: string | undefined; @@ -4289,7 +4289,7 @@ export class LanguageModelUserMessage { } } -export class LanguageModelAssistantMessage { +export class LanguageModelChatAssistantMessage { content: string; constructor(content: string) { diff --git a/src/vscode-dts/vscode.proposed.chatProvider.d.ts b/src/vscode-dts/vscode.proposed.chatProvider.d.ts index 7a9e140b5a5..b6aa1ffdadf 100644 --- a/src/vscode-dts/vscode.proposed.chatProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.chatProvider.d.ts @@ -16,7 +16,7 @@ declare module 'vscode' { * Represents a large language model that accepts ChatML messages and produces a streaming response */ export interface ChatResponseProvider { - provideLanguageModelResponse2(messages: LanguageModelMessage[], options: { [name: string]: any }, extensionId: string, progress: Progress, token: CancellationToken): Thenable; + provideLanguageModelResponse2(messages: LanguageModelChatMessage[], options: { [name: string]: any }, extensionId: string, progress: Progress, token: CancellationToken): Thenable; } export interface ChatResponseProviderMetadata { diff --git a/src/vscode-dts/vscode.proposed.languageModels.d.ts b/src/vscode-dts/vscode.proposed.languageModels.d.ts index 258a5bf18e9..59b053f149d 100644 --- a/src/vscode-dts/vscode.proposed.languageModels.d.ts +++ b/src/vscode-dts/vscode.proposed.languageModels.d.ts @@ -8,20 +8,14 @@ declare module 'vscode' { /** * Represents a language model response. * - * @see {@link LanguageModelAccess.makeChatRequest} + * @see {@link LanguageModelAccess.chatRequest} */ - export interface LanguageModelResponse { - - /** - * The overall result of the request which represents failure or success - * but. The concrete value is not specified and depends on the selected language model. - * - * *Note* that the actual response represented by the {@link LanguageModelResponse.stream `stream`}-property - */ - result: Thenable; + export interface LanguageModelChatResponse { /** * An async iterable that is a stream of text chunks forming the overall response. + * + * *Note* that this stream will error when during receiving an error occurrs. */ stream: AsyncIterable; } @@ -35,7 +29,7 @@ declare module 'vscode' { * *Note* that a language model may choose to add additional system messages to the ones * provided by extensions. */ - export class LanguageModelSystemMessage { + export class LanguageModelChatSystemMessage { /** * The content of this message. @@ -53,7 +47,7 @@ declare module 'vscode' { /** * A language model message that represents a user message. */ - export class LanguageModelUserMessage { + export class LanguageModelChatUserMessage { /** * The content of this message. @@ -78,7 +72,7 @@ declare module 'vscode' { * A language model message that represents an assistant message, usually in response to a user message * or as a sample response/reply-pair. */ - export class LanguageModelAssistantMessage { + export class LanguageModelChatAssistantMessage { /** * The content of this message. @@ -93,63 +87,46 @@ declare module 'vscode' { constructor(content: string); } - export type LanguageModelMessage = LanguageModelSystemMessage | LanguageModelUserMessage | LanguageModelAssistantMessage; + export type LanguageModelChatMessage = LanguageModelChatSystemMessage | LanguageModelChatUserMessage | LanguageModelChatAssistantMessage; + /** - * Represents access to using a language model. Access can be revoked at any time and extension - * must check if the access is {@link LanguageModelAccess.isRevoked still valid} before using it. + * An event describing the change in the set of available language models. */ - export interface LanguageModelAccess { - - /** - * Whether the access to the language model has been revoked. - */ - readonly isRevoked: boolean; - - /** - * An event that is fired when the access the language model has has been revoked or re-granted. - */ - // TODO@API NAME? - readonly onDidChangeAccess: Event; - + export interface LanguageModelChangeEvent { /** - * The name of the model. - * - * It is expected that the model name can be used to lookup properties like token limits or what - * `options` are available. + * Added language models. */ - readonly model: string; - + readonly added: readonly string[]; /** - * Make a request to the language model. - * - * *Note:* This will throw an error if access has been revoked. - * - * @param messages - * @param options + * Removed language models. */ - makeChatRequest(messages: LanguageModelMessage[], options: { [name: string]: any }, token: CancellationToken): LanguageModelResponse; + readonly removed: readonly string[]; } - export interface LanguageModelAccessOptions { + /** + * Options for making a chat request using a language model. + * + * @see {@link lm.chatRequest} + */ + export interface LanguageModelChatRequestOptions { + /** * A human-readable message that explains why access to a language model is needed and what feature is enabled by it. */ justification?: string; - } - /** - * An event describing the change in the set of available language models. - */ - export interface LanguageModelChangeEvent { /** - * Added language models. + * Do not show the consent UI if the user has not yet granted access to the language model but fail the request instead. */ - readonly added: readonly string[]; + // TODO@API refine/define + silent?: boolean; + /** - * Removed language models. + * A set of options that control the behavior of the language model. These options are specific to the language model + * and need to be lookup in the respective documentation. */ - readonly removed: readonly string[]; + modelOptions?: { [name: string]: any }; } /** @@ -157,52 +134,31 @@ declare module 'vscode' { */ export namespace lm { - /** - * Request access to a language model. - * - * - *Note 1:* This function will throw an error when the user didn't grant access or when the - * requested language model is not available. - * - * - *Note 2:* It is OK to hold on to the returned access object and use it later, but extensions - * should check {@link LanguageModelAccess.isRevoked} before using it. - * - * @param id The id of the language model, see {@link languageModels} for valid values. - * @returns A thenable that resolves to a language model access object, rejects if access wasn't granted - */ - export function requestLanguageModelAccess(id: string, options?: LanguageModelAccessOptions): Thenable; - - - /** * Make a chat request using a language model. * - * *Note* that language model use may be subject to access restrictions and user consent. This function always returns a response-object - * but its {@link LanguageModelResponse.result `result`}-property may indicate failure, e.g. due to + * *Note* that language model use may be subject to access restrictions and user consent. This function will return a rejected promise + * if access to the language model is not possible. Reasons for this can be: * * - user consent not given * - quote limits exceeded * - model does not exist * * @param languageModel A language model identifier. See {@link languageModels} for aviailable values. - * @param messages - * @param options - * @param token + * @param messages An array of message instances. + * @param options Objects that control the request. + * @param token A cancellation token which controls the request. See {@link CancellationTokenSource} for how to create one. + * @returns A thenable that resolves to a {@link LanguageModelChatResponse}. The promise will reject when making the request failed. */ // TODO@API refine doc // TODO@API define specific error types? - // TODO@API NAME: chatRequest - // TODO@API NAME: ChatRequestXYZMessage - // TODO@API NAME: ChatRequestResponse - export function chatRequest(languageModel: string, messages: LanguageModelMessage[], options: { [name: string]: any }, token: CancellationToken): Thenable; - - /** - * @see {@link chatRequest} - */ - export function chatRequest(languageModel: string, messages: LanguageModelMessage[], token: CancellationToken): Thenable; - - // TODO@API probe on having access - // TODO@API, BETTER?: ExtensionContext.permissions.languageModels: Record; - // export function canMakeChatRequest(languageModel: string): Thenable; + // TODO@API NAME: sendChatRequest, fetchChatResponse, makeChatRequest, chat, chatRequest sendChatRequest + // TODO@API ExtensionContext#permission#languageModels: { languageModel: string: LanguageModelAccessInformation} + // TODO@API ✅ NAME: LanguageModelChatXYZMessage + // TODO@API ✅ errors on everything that prevents us to make the actual request + // TODO@API ✅ double auth + // TODO@API ✅ NAME: LanguageModelChatResponse, ChatResponse, ChatRequestResponse + export function sendChatRequest(languageModel: string, messages: LanguageModelChatMessage[], options: LanguageModelChatRequestOptions, token: CancellationToken): Thenable; /** * The identifiers of all language models that are currently available. @@ -214,4 +170,12 @@ declare module 'vscode' { */ export const onDidChangeLanguageModels: Event; } + + // export function chatRequest2(languageModel: string, callback: (request: LanguageModelRequest) => R): Thenable; + + // interface LanguageModelRequest { + // readonly quota: any; + // readonly permissions: any; + // makeRequest(messages: LanguageModelChatMessage[], options: { [name: string]: any }, token: CancellationToken): LanguageModelChatResponse; + // } } From 8fd15a863481726f1ea4bdd4bd4f26f40f3e592d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 27 Feb 2024 18:50:12 +0100 Subject: [PATCH 0221/1175] add `LanguageModelError`-type (#206361) https://github.com/microsoft/vscode/issues/206265 --- .../workbench/api/common/extHost.api.impl.ts | 1 + .../api/common/extHostLanguageModels.ts | 11 ++++-- src/vs/workbench/api/common/extHostTypes.ts | 20 +++++++++++ .../vscode.proposed.languageModels.d.ts | 34 +++++++++++++++++-- 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 1b9c7eb2b08..8a99d63cb4c 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1691,6 +1691,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I LanguageModelSystemMessage: extHostTypes.LanguageModelChatSystemMessage, LanguageModelUserMessage: extHostTypes.LanguageModelChatUserMessage, LanguageModelAssistantMessage: extHostTypes.LanguageModelChatAssistantMessage, + LanguageModelError: extHostTypes.LanguageModelError, NewSymbolName: extHostTypes.NewSymbolName, NewSymbolNameTag: extHostTypes.NewSymbolNameTag, InlineEdit: extHostTypes.InlineEdit, diff --git a/src/vs/workbench/api/common/extHostLanguageModels.ts b/src/vs/workbench/api/common/extHostLanguageModels.ts index e51c875984d..ea10161e75f 100644 --- a/src/vs/workbench/api/common/extHostLanguageModels.ts +++ b/src/vs/workbench/api/common/extHostLanguageModels.ts @@ -8,6 +8,7 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostLanguageModelsShape, IMainContext, MainContext, MainThreadLanguageModelsShape } from 'vs/workbench/api/common/extHost.protocol'; import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; +import { LanguageModelError } from 'vs/workbench/api/common/extHostTypes'; import type * as vscode from 'vscode'; import { Progress } from 'vs/platform/progress/common/progress'; import { IChatMessage, IChatResponseFragment, ILanguageModelChatMetadata } from 'vs/workbench/contrib/chat/common/languageModels'; @@ -232,14 +233,14 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { const metadata = await this._proxy.$prepareChatAccess(from, languageModelId, options.justification); if (!metadata || !this._languageModelIds.has(languageModelId)) { - throw new Error(`Language model '${languageModelId}' is unknown.`); + throw LanguageModelError.NotFound(`Language model '${languageModelId}' is unknown.`); } if (this._isUsingAuth(from, metadata)) { const success = await this._getAuthAccess(extension, { identifier: metadata.extension, displayName: metadata.auth.providerLabel }, options.justification, options.silent); if (!success || !this._modelAccessList.get(from)?.has(metadata.extension)) { - throw new Error(`Language model '${languageModelId}' cannot be used by '${from.value}'.`); + throw LanguageModelError.NoPermissions(`Language model '${languageModelId}' cannot be used by '${from.value}'.`); } } @@ -270,7 +271,11 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { await barrier.wait(); if (error) { - throw new Error(`Language model '${languageModelId}' errored, check cause for more details`, { cause: error }); + throw new LanguageModelError( + `Language model '${languageModelId}' errored, check cause for more details`, + 'Unknown', + error + ); } return res.apiObject; diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 4eec0964cd2..2e1ff171d1f 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4297,6 +4297,26 @@ export class LanguageModelChatAssistantMessage { } } +export class LanguageModelError extends Error { + + static NotFound(message?: string): LanguageModelError { + return new LanguageModelError(message, LanguageModelError.NotFound.name); + } + + static NoPermissions(message?: string): LanguageModelError { + return new LanguageModelError(message, LanguageModelError.NoPermissions.name); + } + + readonly code: string; + + constructor(message?: string, code?: string, cause?: Error) { + super(message, { cause }); + this.name = 'LanguageModelError'; + this.code = code ?? ''; + } + +} + //#endregion //#region ai diff --git a/src/vscode-dts/vscode.proposed.languageModels.d.ts b/src/vscode-dts/vscode.proposed.languageModels.d.ts index 59b053f149d..775e4d5e1dc 100644 --- a/src/vscode-dts/vscode.proposed.languageModels.d.ts +++ b/src/vscode-dts/vscode.proposed.languageModels.d.ts @@ -104,6 +104,36 @@ declare module 'vscode' { readonly removed: readonly string[]; } + /** + * An error type for language model specific errors. + * + * Consumers of language models should check the code property to determine specific + * failure causes, like `if(someError.code === vscode.LanguageModelError.NotFound.name) {...}` + * for the case of referring to an unknown language model. + */ + export class LanguageModelError extends Error { + + /** + * The language model does not exist. + */ + static NotFound(message?: string): LanguageModelError; + + /** + * The requestor does not have permissions to use this + * language model + */ + static NoPermissions(message?: string): LanguageModelError; + + /** + * A code that identifies this error. + * + * Possible values are names of errors, like {@linkcode LanguageModelError.NotFound NotFound}, + * or `Unknown` for unspecified errors from the language model itself. In the latter case the + * `cause`-property will contain the actual error. + */ + readonly code: string; + } + /** * Options for making a chat request using a language model. * @@ -151,9 +181,9 @@ declare module 'vscode' { * @returns A thenable that resolves to a {@link LanguageModelChatResponse}. The promise will reject when making the request failed. */ // TODO@API refine doc - // TODO@API define specific error types? - // TODO@API NAME: sendChatRequest, fetchChatResponse, makeChatRequest, chat, chatRequest sendChatRequest // TODO@API ExtensionContext#permission#languageModels: { languageModel: string: LanguageModelAccessInformation} + // TODO@API ✅ define specific error types? + // TODO@API ✅ NAME: sendChatRequest, fetchChatResponse, makeChatRequest, chat, chatRequest sendChatRequest // TODO@API ✅ NAME: LanguageModelChatXYZMessage // TODO@API ✅ errors on everything that prevents us to make the actual request // TODO@API ✅ double auth From 1d3ff8e891800d26b639d9ecc3656aa75f8ea983 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:53:46 +0100 Subject: [PATCH 0222/1175] Refactor and Improve Hover Functionality (#206357) * minor hover improvements? * :lipstick: --- src/vs/platform/hover/browser/hover.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/hover/browser/hover.ts b/src/vs/platform/hover/browser/hover.ts index 3a44ead957b..3dd6ec94d02 100644 --- a/src/vs/platform/hover/browser/hover.ts +++ b/src/vs/platform/hover/browser/hover.ts @@ -241,7 +241,7 @@ export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate private _delay: number; get delay(): number { - if (this.instantHover && Date.now() - this.lastHoverHideTime < this.timeLimit) { + if (this.isInstantlyHovering()) { return 0; // show instantly when a hover was recently shown } return this._delay; @@ -280,6 +280,8 @@ export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate })); } + const id = options.content instanceof HTMLElement ? undefined : options.content.toString(); + return this.hoverService.showHover({ ...options, ...overrideOptions, @@ -288,14 +290,20 @@ export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate hideOnKeyDown: true, ...overrideOptions.persistence }, + id, appearance: { ...options.appearance, compact: true, + skipFadeInAnimation: this.isInstantlyHovering(), ...overrideOptions.appearance } }, focus); } + private isInstantlyHovering(): boolean { + return this.instantHover && Date.now() - this.lastHoverHideTime < this.timeLimit; + } + setInstantHoverTimeLimit(timeLimit: number): void { if (!this.instantHover) { throw new Error('Instant hover is not enabled'); From 7d546baaeacf0a045fa68259f934109c9f51e973 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 27 Feb 2024 10:08:15 -0800 Subject: [PATCH 0223/1175] align start command name with inline chat, get it to show up in command pallette --- .../contrib/inlineChat/browser/inlineChatActions.ts | 2 +- .../terminalContrib/chat/browser/terminalChatActions.ts | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 75ca16a1f24..ae407d07b67 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -35,7 +35,7 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr CommandsRegistry.registerCommandAlias('interactiveEditor.start', 'inlineChat.start'); CommandsRegistry.registerCommandAlias('interactive.acceptChanges', ACTION_ACCEPT_CHANGES); -export const LOCALIZED_START_INLINE_CHAT_STRING = localize2('run', 'Start Inline Chat'); +export const LOCALIZED_START_INLINE_CHAT_STRING = localize2('run', 'Start in Editor'); export const START_INLINE_CHAT = registerIcon('start-inline-chat', Codicon.sparkle, localize('startInlineChat', 'Icon which spawns the inline chat from the editor toolbar.')); // some gymnastics to enable hold for speech without moving the StartSessionAction into the electron-layer diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 878246a2e26..d7e2d3850d3 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -9,7 +9,8 @@ import { localize2 } from 'vs/nls'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; -import { CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { AbstractInlineChatAction } from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions'; +import { CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_HAS_PROVIDER } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; @@ -18,17 +19,18 @@ import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/cha registerActiveXtermAction({ id: TerminalChatCommandId.Start, - title: localize2('startChat', 'Start Chat'), + title: localize2('startChat', 'Start in Terminal'), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyI, when: ContextKeyExpr.and(TerminalContextKeys.focusInAny), weight: KeybindingWeight.WorkbenchContrib, }, f1: true, + category: AbstractInlineChatAction.category, precondition: ContextKeyExpr.and( ContextKeyExpr.has(`config.${TerminalSettingId.ExperimentalInlineChat}`), ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - TerminalChatContextKeys.agentRegistered + CTX_INLINE_CHAT_HAS_PROVIDER ), run: (_xterm, _accessor, activeInstance) => { if (isDetachedTerminalInstance(activeInstance)) { From 70e4a844210fc82f0e58b2f7bb363fbe601a29d2 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 27 Feb 2024 10:16:24 -0800 Subject: [PATCH 0224/1175] Move off in Webview --- .../api/browser/mainThreadCodeInsets.ts | 5 ++++- .../api/browser/mainThreadWebviewPanels.ts | 4 +++- .../customEditor/browser/customEditorInput.ts | 9 ++++++++- .../browser/customEditorInputFactory.ts | 6 +++++- .../customEditor/browser/customEditors.ts | 12 ++++++----- .../extensions/browser/extensionEditor.ts | 11 +++++++++- .../browser/diff/notebookDiffEditor.ts | 3 +-- .../view/renderers/backLayerWebView.ts | 2 +- .../contrib/webview/browser/overlayWebview.ts | 4 ++++ .../contrib/webview/browser/webview.ts | 7 ++++++- .../contrib/webview/browser/webviewElement.ts | 20 ++++++++++++------- .../browser/webviewWindowDragMonitor.ts | 10 +++++----- .../electron-sandbox/webviewCommands.ts | 6 +++--- .../webviewPanel/browser/webviewEditor.ts | 5 ++--- .../browser/webviewEditorInputSerializer.ts | 9 ++++++++- .../webviewView/browser/webviewViewPane.ts | 8 +++++--- .../browser/gettingStarted.ts | 6 +++--- 17 files changed, 88 insertions(+), 39 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts index 4ad9e654807..2d514b09b39 100644 --- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts +++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { getWindow } from 'vs/base/browser/dom'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { isEqual } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; @@ -88,6 +89,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape { } const disposables = new DisposableStore(); + const codeWindow = getWindow(editor.getDomNode()); const webview = this._webviewService.createWebviewElement({ title: undefined, @@ -95,7 +97,8 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape { enableFindWidget: false, }, contentOptions: reviveWebviewContentOptions(options), - extension: { id: extensionId, location: URI.revive(extensionLocation) } + extension: { id: extensionId, location: URI.revive(extensionLocation) }, + codeWindow: codeWindow }); const webviewZone = new EditorWebviewZone(editor, line, height, webview); diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts index 996a8a2e657..69ca28691de 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { getActiveWindow } from 'vs/base/browser/dom'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; import { Disposable, DisposableMap } from 'vs/base/common/lifecycle'; @@ -170,7 +171,8 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc title: initData.title, options: reviveWebviewOptions(initData.panelOptions), contentOptions: reviveWebviewContentOptions(initData.webviewOptions), - extension + extension, + codeWindow: getActiveWindow() }, this.webviewPanelViewType.fromExternal(viewType), initData.title, mainThreadShowOptions); this.addWebviewInput(handle, webview, { serializeBuffersForPostMessage: initData.serializeBuffersForPostMessage }); diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 4e677142d69..1119354a07a 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { getWindowById } from 'vs/base/browser/dom'; +import { mainWindow } from 'vs/base/browser/window'; import { VSBuffer } from 'vs/base/common/buffer'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IReference } from 'vs/base/common/lifecycle'; @@ -37,18 +39,21 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput { resource: URI, viewType: string, group: GroupIdentifier | undefined, + windowId: number, options?: { readonly customClasses?: string; readonly oldResource?: URI }, ): EditorInput { return instantiationService.invokeFunction(accessor => { // If it's an untitled file we must populate the untitledDocumentData const untitledString = accessor.get(IUntitledTextEditorService).getValue(resource); const untitledDocumentData = untitledString ? VSBuffer.fromString(untitledString) : undefined; + const codeWindow = getWindowById(windowId)?.window ?? mainWindow; const webview = accessor.get(IWebviewService).createWebviewOverlay({ providedViewType: viewType, title: undefined, options: { customClasses: options?.customClasses }, contentOptions: {}, extension: undefined, + codeWindow }); const input = instantiationService.createInstance(CustomEditorInput, { resource, viewType }, webview, { untitledDocumentData: untitledDocumentData, oldResource: options?.oldResource }); if (typeof group !== 'undefined') { @@ -65,6 +70,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput { private _defaultDirtyState: boolean | undefined; private readonly _backupId: string | undefined; + private readonly _windowId: number; private readonly _untitledDocumentData: VSBuffer | undefined; @@ -91,6 +97,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput { this._defaultDirtyState = options.startsDirty; this._backupId = options.backupId; this._untitledDocumentData = options.untitledDocumentData; + this._windowId = webview.codeWindow.vscodeWindowId; this.registerListeners(); } @@ -250,7 +257,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput { } public override copy(): EditorInput { - return CustomEditorInput.create(this.instantiationService, this.resource, this.viewType, this.group, this.webview.options); + return CustomEditorInput.create(this.instantiationService, this.resource, this.viewType, this.group, this._windowId, this.webview.options); } public override isReadonly(): boolean | IMarkdownString { diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts index 8dc11b1af6b..d79928406dd 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { getActiveWindow } from 'vs/base/browser/dom'; +import { CodeWindow } from 'vs/base/browser/window'; import { Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; @@ -100,7 +102,7 @@ export class CustomEditorInputSerializer extends WebviewEditorInputSerializer { } } -function reviveWebview(webviewService: IWebviewService, data: { origin: string | undefined; viewType: string; state: any; webviewOptions: WebviewOptions; contentOptions: WebviewContentOptions; extension?: WebviewExtensionDescription }) { +function reviveWebview(webviewService: IWebviewService, data: { origin: string | undefined; viewType: string; state: any; webviewOptions: WebviewOptions; contentOptions: WebviewContentOptions; extension?: WebviewExtensionDescription; codeWindow: CodeWindow }) { const webview = webviewService.createWebviewOverlay({ providedViewType: data.viewType, origin: data.origin, @@ -112,6 +114,7 @@ function reviveWebview(webviewService: IWebviewService, data: { origin: string | }, contentOptions: data.contentOptions, extension: data.extension, + codeWindow: data.codeWindow }); webview.state = data.state; return webview; @@ -185,6 +188,7 @@ export class ComplexCustomWorkingCopyEditorHandler extends Disposable implements contentOptions: restoreWebviewContentOptions(backupData.webview.options), state: backupData.webview.state, extension, + codeWindow: getActiveWindow() }); const editor = this._instantiationService.createInstance(CustomEditorInput, { resource: URI.revive(backupData.editorResource), viewType: backupData.viewType }, webview, { backupId: backupData.backupId }); diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 6c99db87599..d03d8c9b492 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -29,6 +29,7 @@ import { IEditorResolverService, IEditorType, RegisteredEditorPriority } from 'v import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ContributedCustomEditors } from '../common/contributedCustomEditors'; import { CustomEditorInput } from './customEditorInput'; +import { mainWindow } from 'vs/base/browser/window'; export class CustomEditorService extends Disposable implements ICustomEditorService { _serviceBrand: any; @@ -131,10 +132,10 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ }, { createEditorInput: ({ resource }, group) => { - return { editor: CustomEditorInput.create(this.instantiationService, resource, contributedEditor.id, group.id) }; + return { editor: CustomEditorInput.create(this.instantiationService, resource, contributedEditor.id, group.id, group.windowId) }; }, createUntitledEditorInput: ({ resource }, group) => { - return { editor: CustomEditorInput.create(this.instantiationService, resource ?? URI.from({ scheme: Schemas.untitled, authority: `Untitled-${this._untitledCounter++}` }), contributedEditor.id, group.id) }; + return { editor: CustomEditorInput.create(this.instantiationService, resource ?? URI.from({ scheme: Schemas.untitled, authority: `Untitled-${this._untitledCounter++}` }), contributedEditor.id, group.id, group.windowId) }; }, createDiffEditorInput: (diffEditorInput, group) => { return { editor: this.createDiffEditorInput(diffEditorInput, contributedEditor.id, group) }; @@ -150,8 +151,8 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ editorID: string, group: IEditorGroup ): DiffEditorInput { - const modifiedOverride = CustomEditorInput.create(this.instantiationService, assertIsDefined(editor.modified.resource), editorID, group.id, { customClasses: 'modified' }); - const originalOverride = CustomEditorInput.create(this.instantiationService, assertIsDefined(editor.original.resource), editorID, group.id, { customClasses: 'original' }); + const modifiedOverride = CustomEditorInput.create(this.instantiationService, assertIsDefined(editor.modified.resource), editorID, group.id, group.windowId, { customClasses: 'modified' }); + const originalOverride = CustomEditorInput.create(this.instantiationService, assertIsDefined(editor.original.resource), editorID, group.id, group.windowId, { customClasses: 'original' }); return this.instantiationService.createInstance(DiffEditorInput, editor.label, editor.description, originalOverride, modifiedOverride, true); } @@ -245,7 +246,8 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ let replacement: EditorInput | IResourceEditorInput; if (possibleEditors.defaultEditor) { const viewType = possibleEditors.defaultEditor.id; - replacement = CustomEditorInput.create(this.instantiationService, newResource, viewType, group); + const windowId = this.editorGroupService.getGroup(group)?.windowId ?? mainWindow.vscodeWindowId; + replacement = CustomEditorInput.create(this.instantiationService, newResource, viewType, group, windowId); } else { replacement = { resource: newResource, options: { override: DEFAULT_EDITOR_ASSOCIATION.id } }; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 8f4ca1d9d6a..65cf6d01bc3 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { $, Dimension, addDisposableListener, append, setParentFlowTo } from 'vs/base/browser/dom'; +import { $, Dimension, addDisposableListener, append, getWindow, getWindowById, setParentFlowTo } from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget'; @@ -661,6 +661,14 @@ export class ExtensionEditor extends EditorPane { return Promise.resolve(null); } + let codeWindow; + if (this.group?.windowId) { + const windowById = getWindowById(this.group.windowId); + codeWindow = windowById?.window ?? getWindow(container); + } else { + codeWindow = getWindow(container); + } + const webview = this.contentDisposables.add(this.webviewService.createWebviewOverlay({ title, options: { @@ -670,6 +678,7 @@ export class ExtensionEditor extends EditorPane { }, contentOptions: {}, extension: undefined, + codeWindow: codeWindow })); webview.initialScrollProgress = this.initialScrollProgress.get(webviewIndex) || 0; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts index e2de1a22096..58807d6a591 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts @@ -47,7 +47,6 @@ import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { cellIndexesToRanges, cellRangesToIndexes } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { NotebookDiffOverviewRuler } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler'; import { registerZIndex, ZIndex } from 'vs/platform/layout/browser/zIndexRegistry'; -import { mainWindow } from 'vs/base/browser/window'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; const $ = DOM.$; @@ -154,7 +153,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD @ICodeEditorService codeEditorService: ICodeEditorService ) { super(NotebookTextDiffEditor.ID, telemetryService, themeService, storageService); - this._notebookOptions = new NotebookOptions(DOM.getWindowById(this.group?.windowId, true).window ?? mainWindow, this.configurationService, notebookExecutionStateService, codeEditorService, false); + this._notebookOptions = new NotebookOptions(DOM.getWindowById(this.group?.windowId, true).window, this.configurationService, notebookExecutionStateService, codeEditorService, false); this._register(this._notebookOptions); this._revealFirst = true; } 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 dd421641fb8..15b3d151ca3 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -560,7 +560,7 @@ export class BackLayerWebView extends Themable { this.webview.mountTo(this.element); this._register(this.webview); - this._register(new WebviewWindowDragMonitor(() => this.webview)); + this._register(new WebviewWindowDragMonitor(codeWindow, () => this.webview)); const initializePromise = new DeferredPromise(); diff --git a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts index 0c60dbd1e68..e58e399be2f 100644 --- a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts +++ b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts @@ -6,6 +6,7 @@ import { Dimension } from 'vs/base/browser/dom'; import { FastDomNode } from 'vs/base/browser/fastDomNode'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { CodeWindow } from 'vs/base/browser/window'; import { Emitter } from 'vs/base/common/event'; import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; @@ -44,6 +45,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { public readonly providedViewType?: string; public origin: string; + public codeWindow: CodeWindow; private _container: FastDomNode | undefined; @@ -62,6 +64,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { this._extension = initInfo.extension; this._options = initInfo.options; this._contentOptions = initInfo.contentOptions; + this.codeWindow = initInfo.codeWindow; } public get isFocused() { @@ -197,6 +200,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { options: this._options, contentOptions: this._contentOptions, extension: this.extension, + codeWindow: this.codeWindow }); this._webview.value = webview; webview.state = this._state; diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index dfd7c6fc5b4..c115a9927af 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -78,7 +78,7 @@ export interface WebviewInitInfo { readonly contentOptions: WebviewContentOptions; readonly extension: WebviewExtensionDescription | undefined; - readonly codeWindow?: CodeWindow; + readonly codeWindow: CodeWindow; } export const enum WebviewContentPurpose { @@ -189,6 +189,11 @@ export interface IWebview extends IDisposable { */ readonly origin: string; + /** + * The code window the webview is contained within. + */ + readonly codeWindow: CodeWindow; + /** * Set html content of the webview. */ diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 6514979da3d..2df70a23b32 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -37,7 +37,7 @@ import { WebviewFindDelegate, WebviewFindWidget } from 'vs/workbench/contrib/web import { FromWebviewMessage, KeyEvent, ToWebviewMessage } from 'vs/workbench/contrib/webview/browser/webviewMessages'; import { decodeAuthority, webviewGenericCspSource, webviewRootResourceAuthority } from 'vs/workbench/contrib/webview/common/webview'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { $window } from 'vs/base/browser/window'; +import { CodeWindow } from 'vs/base/browser/window'; interface WebviewContent { readonly html: string; @@ -88,6 +88,11 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD */ public readonly origin: string; + /** + * The code window the webview is contained within. + */ + public readonly codeWindow: CodeWindow; + private readonly _encodedWebviewOriginPromise: Promise; private _encodedWebviewOrigin: string | undefined; @@ -103,7 +108,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD if (!this._focused) { return false; } - if ($window.document.activeElement && $window.document.activeElement !== this.element) { + if (this.codeWindow.document.activeElement && this.codeWindow.document.activeElement !== this.element) { // looks like https://github.com/microsoft/vscode/issues/132641 // where the focus is actually not in the `