From b9dc1b6a96f2b6ef228f310b380921d0b1e0b856 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Mon, 9 Oct 2023 12:05:52 +0200 Subject: [PATCH 01/55] turn setting into enum --- src/vs/workbench/browser/contextkeys.ts | 2 +- src/vs/workbench/browser/layout.ts | 4 +- .../workbench/browser/parts/editor/editor.ts | 2 +- .../browser/parts/editor/editorDropTarget.ts | 2 +- .../browser/parts/editor/editorGroupView.ts | 6 +- .../browser/parts/editor/editorTabsControl.ts | 4 +- .../parts/editor/editorTitleControl.ts | 27 ++++---- .../parts/editor/noEditorTabsControl.ts | 62 +++++++++++++++++++ .../browser/workbench.contribution.ts | 21 ++++--- src/vs/workbench/common/editor.ts | 2 +- src/vs/workbench/common/theme.ts | 2 +- .../test/browser/editorGroupsService.test.ts | 6 +- 12 files changed, 104 insertions(+), 36 deletions(-) create mode 100644 src/vs/workbench/browser/parts/editor/noEditorTabsControl.ts diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index feca5c8da58..205f570c15f 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -265,7 +265,7 @@ export class WorkbenchContextKeysHandler extends Disposable { } private updateEditorAreaContextKeys(): void { - this.editorTabsVisibleContext.set(!!this.editorGroupService.partOptions.showTabs); + this.editorTabsVisibleContext.set(this.editorGroupService.partOptions.showTabs === 'multiple'); } private updateEditorContextKeys(): void { diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 1ccffa5770e..01d611d63a4 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1227,8 +1227,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.state.runtime.zenMode.transitionDisposables.add(this.editorService.onDidVisibleEditorsChange(() => setLineNumbers('off'))); } - if (config.hideTabs && this.editorGroupService.partOptions.showTabs) { - this.state.runtime.zenMode.transitionDisposables.add(this.editorGroupService.enforcePartOptions({ showTabs: false })); + if (config.hideTabs && this.editorGroupService.partOptions.showTabs === 'multiple') { + this.state.runtime.zenMode.transitionDisposables.add(this.editorGroupService.enforcePartOptions({ showTabs: 'single' })); } if (config.silentNotifications && zenModeExitInfo.handleNotificationsDoNotDisturbMode) { diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index a2a26d6f7ad..a0edbc678d6 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -25,7 +25,7 @@ export const DEFAULT_EDITOR_MIN_DIMENSIONS = new Dimension(220, 70); export const DEFAULT_EDITOR_MAX_DIMENSIONS = new Dimension(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY); export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = { - showTabs: true, + showTabs: 'multiple', highlightModifiedTabs: false, tabCloseButton: 'right', tabSizing: 'fit', diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index db792c89da9..6ba5020526d 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -530,7 +530,7 @@ class DropOverlay extends Themable { private getOverlayOffsetHeight(): number { // With tabs and opened editors: use the area below tabs as drop target - if (!this.groupView.isEmpty && this.editorGroupService.partOptions.showTabs) { + if (!this.groupView.isEmpty && this.editorGroupService.partOptions.showTabs === 'multiple') { return this.groupView.titleHeight.offset; } diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 495b28c2684..4ebd1ae2571 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -463,7 +463,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } private updateTitleContainer(): void { - this.titleContainer.classList.toggle('tabs', this.groupsView.partOptions.showTabs); + this.titleContainer.classList.toggle('tabs', this.groupsView.partOptions.showTabs === 'multiple'); this.titleContainer.classList.toggle('show-file-icons', this.groupsView.partOptions.showIcons); } @@ -699,7 +699,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Title control switch between singleEditorTabs, multiEditorTabs and multiRowEditorTabs if ( event.oldPartOptions.showTabs !== event.newPartOptions.showTabs || - (event.oldPartOptions.showTabs && event.oldPartOptions.pinnedTabsOnSeparateRow !== event.newPartOptions.pinnedTabsOnSeparateRow) + (event.oldPartOptions.showTabs === 'multiple' && event.oldPartOptions.pinnedTabsOnSeparateRow !== event.newPartOptions.pinnedTabsOnSeparateRow) ) { // Re-layout @@ -1885,7 +1885,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } const { showTabs } = this.groupsView.partOptions; - this.titleContainer.style.backgroundColor = this.getColor(showTabs ? EDITOR_GROUP_HEADER_TABS_BACKGROUND : EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND) || ''; + this.titleContainer.style.backgroundColor = this.getColor(showTabs === 'multiple' ? EDITOR_GROUP_HEADER_TABS_BACKGROUND : EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND) || ''; // Editor container this.editorContainer.style.backgroundColor = this.getColor(editorBackground) || ''; diff --git a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts index e862a401765..fc75ae4bfde 100644 --- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts @@ -290,7 +290,7 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC // Drag all tabs of the group if tabs are enabled let hasDataTransfer = false; - if (this.groupsView.partOptions.showTabs) { + if (this.groupsView.partOptions.showTabs === 'multiple') { hasDataTransfer = this.doFillResourceDataTransfers(this.groupView.getEditors(EditorsOrder.SEQUENTIAL), e); } @@ -309,7 +309,7 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC // Drag Image if (this.groupView.activeEditor) { let label = this.groupView.activeEditor.getName(); - if (this.groupsView.partOptions.showTabs && this.groupView.count > 1) { + if (this.groupsView.partOptions.showTabs === 'multiple' && this.groupView.count > 1) { label = localize('draggedEditorGroup', "{0} (+{1})", label, this.groupView.count - 1); } diff --git a/src/vs/workbench/browser/parts/editor/editorTitleControl.ts b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts index 69aa10b14b7..38ea136a2a0 100644 --- a/src/vs/workbench/browser/parts/editor/editorTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts @@ -17,6 +17,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { MultiRowEditorControl } from 'vs/workbench/browser/parts/editor/multiRowEditorTabsControl'; import { IReadonlyEditorGroupModel } from 'vs/workbench/common/editor/editorGroupModel'; +import { NoEditorTabsControl } from 'vs/workbench/browser/parts/editor/noEditorTabsControl'; export interface IEditorTitleControlDimensions { @@ -57,22 +58,26 @@ export class EditorTitleControl extends Themable { } private createEditorTabsControl(): IEditorTabsControl { - let control: IEditorTabsControl; - if (this.groupsView.partOptions.showTabs) { - if (this.groupsView.partOptions.pinnedTabsOnSeparateRow) { - control = this.instantiationService.createInstance(MultiRowEditorControl, this.parent, this.editorPartsView, this.groupsView, this.groupView, this.model); - } else { - control = this.instantiationService.createInstance(MultiEditorTabsControl, this.parent, this.editorPartsView, this.groupsView, this.groupView, this.model); - } - } else { - control = this.instantiationService.createInstance(SingleEditorTabsControl, this.parent, this.editorPartsView, this.groupsView, this.groupView, this.model); + let tabsControlType; + switch (this.groupsView.partOptions.showTabs) { + case 'none': + tabsControlType = NoEditorTabsControl; + break; + case 'single': + tabsControlType = SingleEditorTabsControl; + break; + case 'multiple': + default: + tabsControlType = this.groupsView.partOptions.pinnedTabsOnSeparateRow ? MultiRowEditorControl : MultiEditorTabsControl; + break; } + const control = this.instantiationService.createInstance(tabsControlType, this.parent, this.editorPartsView, this.groupsView, this.groupView, this.model); return this.editorTabsControlDisposable.add(control); } private createBreadcrumbsControl(): BreadcrumbsControlFactory | undefined { - if (!this.groupsView.partOptions.showTabs) { + if (this.groupsView.partOptions.showTabs !== 'multiple') { return undefined; // single tabs have breadcrumbs inlined } @@ -170,7 +175,7 @@ export class EditorTitleControl extends Themable { // Update editor tabs control if options changed if ( oldOptions.showTabs !== newOptions.showTabs || - (newOptions.showTabs && oldOptions.pinnedTabsOnSeparateRow !== newOptions.pinnedTabsOnSeparateRow) + (newOptions.showTabs === 'multiple' && oldOptions.pinnedTabsOnSeparateRow !== newOptions.pinnedTabsOnSeparateRow) ) { // Clear old this.editorTabsControlDisposable.clear(); diff --git a/src/vs/workbench/browser/parts/editor/noEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/noEditorTabsControl.ts new file mode 100644 index 00000000000..4d426dcf52f --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/noEditorTabsControl.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/singleeditortabscontrol'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { EditorTabsControl, IToolbarActions } from 'vs/workbench/browser/parts/editor/editorTabsControl'; +import { Dimension } from 'vs/base/browser/dom'; +import { IEditorTitleControlDimensions } from 'vs/workbench/browser/parts/editor/editorTitleControl'; + +export class NoEditorTabsControl extends EditorTabsControl { + + protected override create(parent: HTMLElement): void { + super.create(parent); + } + + protected override prepareEditorActions(editorActions: IToolbarActions): IToolbarActions { + return { + primary: [], + secondary: [] + }; + } + + openEditor(editor: EditorInput): boolean { + return false; + } + + openEditors(editors: EditorInput[]): boolean { + return false; + } + + beforeCloseEditor(editor: EditorInput): void { } + + closeEditor(editor: EditorInput): void { } + + closeEditors(editors: EditorInput[]): void { } + + moveEditor(editor: EditorInput, fromIndex: number, targetIndex: number): void { } + + pinEditor(editor: EditorInput): void { } + + stickEditor(editor: EditorInput): void { } + + unstickEditor(editor: EditorInput): void { } + + setActive(isActive: boolean): void { } + + updateEditorLabel(editor: EditorInput): void { } + + updateEditorDirty(editor: EditorInput): void { } + + override updateStyles(): void { } + + getHeight(): number { + return 0; + } + + layout(dimensions: IEditorTitleControlDimensions): Dimension { + return new Dimension(dimensions.container.width, this.getHeight()); + } +} diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 2017f37c3b9..c4c8f6f8895 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -38,23 +38,24 @@ const registry = Registry.as(ConfigurationExtensions.Con default: 'default', }, 'workbench.editor.showTabs': { - 'type': 'boolean', + 'type': 'string', + 'enum': ['multiple', 'single', 'none'], 'description': localize('showEditorTabs', "Controls whether opened editors should show in tabs or not."), - 'default': true + 'default': 'multiple' }, 'workbench.editor.wrapTabs': { 'type': 'boolean', - 'markdownDescription': localize('wrapTabs', "Controls whether tabs should be wrapped over multiple lines when exceeding available space or whether a scrollbar should appear instead. This value is ignored when `#workbench.editor.showTabs#` is disabled."), + 'markdownDescription': localize('wrapTabs', "Controls whether tabs should be wrapped over multiple lines when exceeding available space or whether a scrollbar should appear instead. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`."), 'default': false }, 'workbench.editor.scrollToSwitchTabs': { 'type': 'boolean', - 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'scrollToSwitchTabs' }, "Controls whether scrolling over tabs will open them or not. By default tabs will only reveal upon scrolling, but not open. You can press and hold the Shift-key while scrolling to change this behavior for that duration. This value is ignored when `#workbench.editor.showTabs#` is disabled."), + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'scrollToSwitchTabs' }, "Controls whether scrolling over tabs will open them or not. By default tabs will only reveal upon scrolling, but not open. You can press and hold the Shift-key while scrolling to change this behavior for that duration. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`."), 'default': false }, 'workbench.editor.highlightModifiedTabs': { 'type': 'boolean', - 'markdownDescription': localize('highlightModifiedTabs', "Controls whether a top border is drawn on tabs for editors that have unsaved changes. This value is ignored when `#workbench.editor.showTabs#` is disabled."), + 'markdownDescription': localize('highlightModifiedTabs', "Controls whether a top border is drawn on tabs for editors that have unsaved changes. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`."), 'default': false }, 'workbench.editor.decorations.badges': { @@ -140,7 +141,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'type': 'string', 'enum': ['left', 'right', 'off'], 'default': 'right', - 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorTabCloseButton' }, "Controls the position of the editor's tabs close buttons, or disables them when set to 'off'. This value is ignored when `#workbench.editor.showTabs#` is disabled.") + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorTabCloseButton' }, "Controls the position of the editor's tabs close buttons, or disables them when set to 'off'. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.") }, 'workbench.editor.tabSizing': { 'type': 'string', @@ -151,7 +152,7 @@ const registry = Registry.as(ConfigurationExtensions.Con localize('workbench.editor.tabSizing.shrink', "Allow tabs to get smaller when the available space is not enough to show all tabs at once."), localize('workbench.editor.tabSizing.fixed', "Make all tabs the same size, while allowing them to get smaller when the available space is not enough to show all tabs at once.") ], - 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'tabSizing' }, "Controls the size of editor tabs. This value is ignored when `#workbench.editor.showTabs#` is disabled.") + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'tabSizing' }, "Controls the size of editor tabs. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.") }, 'workbench.editor.tabSizingFixedMinWidth': { 'type': 'number', @@ -169,7 +170,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'type': 'string', 'enum': ['default', 'compact'], 'default': 'default', - 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.editor.tabHeight' }, "Controls the height of editor tabs. Also applies to the title control bar when `#workbench.editor.showTabs#` is disabled.") + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.editor.tabHeight' }, "Controls the height of editor tabs. Also applies to the title control bar when `#workbench.editor.showTabs#` is not set to `multiple`.") }, 'workbench.editor.pinnedTabSizing': { 'type': 'string', @@ -180,7 +181,7 @@ const registry = Registry.as(ConfigurationExtensions.Con localize('workbench.editor.pinnedTabSizing.compact', "A pinned tab will show in a compact form with only icon or first letter of the editor name."), localize('workbench.editor.pinnedTabSizing.shrink', "A pinned tab shrinks to a compact fixed size showing parts of the editor name.") ], - 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'pinnedTabSizing' }, "Controls the size of pinned editor tabs. Pinned tabs are sorted to the beginning of all opened tabs and typically do not close until unpinned. This value is ignored when `#workbench.editor.showTabs#` is disabled.") + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'pinnedTabSizing' }, "Controls the size of pinned editor tabs. Pinned tabs are sorted to the beginning of all opened tabs and typically do not close until unpinned. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.") }, 'workbench.editor.pinnedTabsOnSeparateRow': { 'type': 'boolean', @@ -317,7 +318,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'workbench.editor.doubleClickTabToToggleEditorGroupSizes': { 'type': 'boolean', 'default': true, - 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'doubleClickTabToToggleEditorGroupSizes' }, "Controls whether to maximize/restore the editor group when double clicking on a tab. This value is ignored when `#workbench.editor.showTabs#` is disabled.") + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'doubleClickTabToToggleEditorGroupSizes' }, "Controls whether to maximize/restore the editor group when double clicking on a tab. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.") }, 'workbench.editor.limit.enabled': { 'type': 'boolean', diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 41664fd79f8..5c3e8230881 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1090,7 +1090,7 @@ export interface IWorkbenchEditorConfiguration { } interface IEditorPartConfiguration { - showTabs?: boolean; + showTabs?: 'multiple' | 'single' | 'none'; wrapTabs?: boolean; scrollToSwitchTabs?: boolean; highlightModifiedTabs?: boolean; diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 9d31beaefc2..9de1bf36c1b 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -256,7 +256,7 @@ export const EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND = registerColor('editorGroup light: editorBackground, hcDark: editorBackground, hcLight: editorBackground -}, localize('editorGroupHeaderBackground', "Background color of the editor group title header when tabs are disabled (`\"workbench.editor.showTabs\": false`). Editor groups are the containers of editors.")); +}, localize('editorGroupHeaderBackground', "Background color of the editor group title header when `workbench.editor.showTabs` is not set to multiple. Editor groups are the containers of editors.")); export const EDITOR_GROUP_HEADER_BORDER = registerColor('editorGroupHeader.border', { dark: null, 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 200a6e33a42..02d63db45ad 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -408,9 +408,9 @@ suite('EditorGroupsService', () => { const currentOptions = part.partOptions; assert.ok(currentOptions); - disposables.add(part.enforcePartOptions({ showTabs: false })); - assert.strictEqual(part.partOptions.showTabs, false); - assert.strictEqual(newOptions.showTabs, false); + disposables.add(part.enforcePartOptions({ showTabs: 'single' })); + assert.strictEqual(part.partOptions.showTabs, 'single'); + assert.strictEqual(newOptions.showTabs, 'single'); assert.strictEqual(oldOptions, currentOptions); }); From 2e04ce400a4664e7c162a831c1cfa0f9c1405b52 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Mon, 9 Oct 2023 14:45:07 +0200 Subject: [PATCH 02/55] Migration support --- src/vs/workbench/browser/parts/editor/editor.ts | 8 ++++++++ src/vs/workbench/browser/workbench.contribution.ts | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index a0edbc678d6..6a462490090 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -80,6 +80,14 @@ export function getEditorPartOptions(configurationService: IConfigurationService } } + // showTabs ensure correct enum value + if (typeof options.showTabs === 'boolean') { + // Migration service kicks in very late and can cause a flicker otherwise + options.showTabs = options.showTabs ? 'multiple' : 'single'; + } else if (options.showTabs !== 'multiple' && options.showTabs !== 'single' && options.showTabs !== 'none') { + options.showTabs = 'multiple'; + } + const windowConfig = configurationService.getValue(); if (windowConfig?.window?.density?.editorTabHeight) { options.tabHeight = windowConfig.window.density.editorTabHeight; diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index de682c9af53..6cc79eea671 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -766,3 +766,14 @@ Registry.as(Extensions.ConfigurationMigration) return result; } }]); + +Registry.as(Extensions.ConfigurationMigration) + .registerConfigurationMigrations([{ + key: 'workbench.editor.showTabs', migrateFn: (value: any) => { + const result: ConfigurationKeyValuePairs = [['workbench.editor.showTabs', { value: value }]]; + if (value === false) { + result.push(['workbench.editor.showTabs', { value: 'single' }]); + } + return result; + } + }]); From 30871cbe2dae534e2ff85a2bb97c2345d16a3c6d Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Tue, 10 Oct 2023 09:25:49 +0200 Subject: [PATCH 03/55] :lipstick: --- .../browser/parts/editor/media/singleeditortabscontrol.css | 2 +- .../workbench/browser/parts/editor/singleEditorTabsControl.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/media/singleeditortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/singleeditortabscontrol.css index 893920c71c5..f3a7d7b2484 100644 --- a/src/vs/workbench/browser/parts/editor/media/singleeditortabscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/singleeditortabscontrol.css @@ -28,7 +28,7 @@ /* Breadcrumbs (inline next to single editor tab) */ -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .no-tabs.title-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .single-tab.title-label { flex: none; } diff --git a/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts index 8efda41b36d..0ea58b717d2 100644 --- a/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts @@ -317,7 +317,7 @@ export class SingleEditorTabsControl extends EditorTabsControl { { title, italic: !isEditorPinned, - extraClasses: ['no-tabs', 'title-label'].concat(editor.getLabelExtraClasses()), + extraClasses: ['single-tab', 'title-label'].concat(editor.getLabelExtraClasses()), fileDecorations: { colors: Boolean(options.decorations?.colors), badges: Boolean(options.decorations?.badges) From 5c9a261b6adbc284ab62a02117fd25eb6c75cbf8 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Tue, 10 Oct 2023 09:48:04 +0200 Subject: [PATCH 04/55] :lipstick: --- .../workbench/browser/parts/editor/editor.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 6a462490090..2d0ce727b05 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -80,6 +80,17 @@ export function getEditorPartOptions(configurationService: IConfigurationService } } + const windowConfig = configurationService.getValue(); + if (windowConfig?.window?.density?.editorTabHeight) { + options.tabHeight = windowConfig.window.density.editorTabHeight; + } + + validateEditorPartOptions(options); + + return options; +} + +function validateEditorPartOptions(options: IEditorPartOptions) { // showTabs ensure correct enum value if (typeof options.showTabs === 'boolean') { // Migration service kicks in very late and can cause a flicker otherwise @@ -87,13 +98,6 @@ export function getEditorPartOptions(configurationService: IConfigurationService } else if (options.showTabs !== 'multiple' && options.showTabs !== 'single' && options.showTabs !== 'none') { options.showTabs = 'multiple'; } - - const windowConfig = configurationService.getValue(); - if (windowConfig?.window?.density?.editorTabHeight) { - options.tabHeight = windowConfig.window.density.editorTabHeight; - } - - return options; } /** From 6a0b43656e6c0e18b637d5c7dbb460658aee5f58 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Tue, 10 Oct 2023 10:00:26 +0200 Subject: [PATCH 05/55] :lipstick: --- src/vs/workbench/common/theme.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 9de1bf36c1b..a2d462a28c7 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -256,7 +256,7 @@ export const EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND = registerColor('editorGroup light: editorBackground, hcDark: editorBackground, hcLight: editorBackground -}, localize('editorGroupHeaderBackground', "Background color of the editor group title header when `workbench.editor.showTabs` is not set to multiple. Editor groups are the containers of editors.")); +}, localize('editorGroupHeaderBackground', "Background color of the editor group title header when (`\"workbench.editor.showTabs\": \"single\"`). Editor groups are the containers of editors.")); export const EDITOR_GROUP_HEADER_BORDER = registerColor('editorGroupHeader.border', { dark: null, From 2fdc60de8f2426495903f18b373e68438dc4d3c7 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Sun, 15 Oct 2023 17:36:44 +0200 Subject: [PATCH 06/55] zen mode show tabs --- src/vs/workbench/browser/layout.ts | 6 +-- .../parts/editor/editorTitleControl.ts | 2 +- .../browser/workbench.contribution.ts | 39 ++++++++++++++----- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 91394f798dc..e21aabd3fb2 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1232,8 +1232,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.state.runtime.zenMode.transitionDisposables.add(this.editorService.onDidVisibleEditorsChange(() => setLineNumbers('off'))); } - if (config.hideTabs && this.editorGroupService.partOptions.showTabs === 'multiple') { - this.state.runtime.zenMode.transitionDisposables.add(this.editorGroupService.enforcePartOptions({ showTabs: 'single' })); + if (config.showTabs !== this.editorGroupService.partOptions.showTabs) { + this.state.runtime.zenMode.transitionDisposables.add(this.editorGroupService.enforcePartOptions({ showTabs: config.showTabs })); } if (config.silentNotifications && zenModeExitInfo.handleNotificationsDoNotDisturbMode) { @@ -2305,7 +2305,7 @@ type ZenModeConfiguration = { hideActivityBar: boolean; hideLineNumbers: boolean; hideStatusBar: boolean; - hideTabs: boolean; + showTabs: 'multiple' | 'single' | 'none'; restore: boolean; silentNotifications: boolean; }; diff --git a/src/vs/workbench/browser/parts/editor/editorTitleControl.ts b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts index 38ea136a2a0..6330c3e814a 100644 --- a/src/vs/workbench/browser/parts/editor/editorTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts @@ -78,7 +78,7 @@ export class EditorTitleControl extends Themable { private createBreadcrumbsControl(): BreadcrumbsControlFactory | undefined { if (this.groupsView.partOptions.showTabs !== 'multiple') { - return undefined; // single tabs have breadcrumbs inlined + return undefined; // Single tabs have breadcrumbs inlined. No tabs have no breadcrumbs. } // Breadcrumbs container diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 6cc79eea671..c3b83193950 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -41,7 +41,12 @@ const registry = Registry.as(ConfigurationExtensions.Con 'workbench.editor.showTabs': { 'type': 'string', 'enum': ['multiple', 'single', 'none'], - 'description': localize('showEditorTabs', "Controls whether opened editors should show in tabs or not."), + 'enumDescriptions': [ + localize('workbench.editor.showTabs.multiple', "Each editor is displayed as a tab in the editor title area."), + localize('workbench.editor.showTabs.single', "The active editor is displayed as a single large tab in the editor title area."), + localize('workbench.editor.showTabs.none', "The editor title area is not displayed."), + ], + 'description': localize('showEditorTabs', "Controls whether opened editors should show as individual tabs, one single large tab or if the title area should not be shown."), 'default': 'multiple' }, 'workbench.editor.wrapTabs': { @@ -187,7 +192,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'workbench.editor.pinnedTabsOnSeparateRow': { 'type': 'boolean', 'default': false, - 'markdownDescription': localize('workbench.editor.pinnedTabsOnSeparateRow', "When enabled, displays pinned tabs in a separate row above all other tabs. This value is ignored when `#workbench.editor.showTabs#` is disabled."), + 'markdownDescription': localize('workbench.editor.pinnedTabsOnSeparateRow', "When enabled, displays pinned tabs in a separate row above all other tabs. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`."), }, 'workbench.editor.preventPinnedEditorClose': { 'type': 'string', @@ -722,10 +727,16 @@ const registry = Registry.as(ConfigurationExtensions.Con 'default': true, 'description': localize('zenMode.centerLayout', "Controls whether turning on Zen Mode also centers the layout.") }, - 'zenMode.hideTabs': { - 'type': 'boolean', - 'default': true, - 'description': localize('zenMode.hideTabs', "Controls whether turning on Zen Mode also hides workbench tabs.") + 'zenMode.showTabs': { + 'type': 'string', + 'enum': ['multiple', 'single', 'none'], + 'description': localize('zenMode.showTabs', "Controls whether turning on Zen Mode should show muötiple editor tabs, a single editor tab or hide the editor title area completely."), + 'enumDescriptions': [ + localize('zenMode.showTabs.multiple', "Each editor is displayed as a tab in the editor title area."), + localize('zenMode.showTabs.single', "The active editor is displayed as a single large tab in the editor title area."), + localize('zenMode.showTabs.none', "The editor title area is not displayed."), + ], + 'default': 'multiple' }, 'zenMode.hideStatusBar': { 'type': 'boolean', @@ -770,9 +781,19 @@ Registry.as(Extensions.ConfigurationMigration) Registry.as(Extensions.ConfigurationMigration) .registerConfigurationMigrations([{ key: 'workbench.editor.showTabs', migrateFn: (value: any) => { - const result: ConfigurationKeyValuePairs = [['workbench.editor.showTabs', { value: value }]]; - if (value === false) { - result.push(['workbench.editor.showTabs', { value: 'single' }]); + if (typeof value === 'boolean') { + value = value ? 'multiple' : 'single'; + } + return [['workbench.editor.showTabs', { value: value }]]; + } + }]); + +Registry.as(Extensions.ConfigurationMigration) + .registerConfigurationMigrations([{ + key: 'zenMode.hideTabs', migrateFn: (value: any) => { + const result: ConfigurationKeyValuePairs = [['zenMode.hideTabs', { value: undefined }]]; + if (value === true) { + result.push(['zenMode.showTabs', { value: 'single' }]); } return result; } From df912eeb56acfb3b6a2fd519451168228bb4edee Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Sun, 15 Oct 2023 17:48:13 +0200 Subject: [PATCH 07/55] contextkey has --- src/vs/workbench/browser/actions/layoutActions.ts | 4 ++-- src/vs/workbench/browser/parts/editor/editor.contribution.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 7dd8459f9aa..d4387fb9ea5 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -470,7 +470,7 @@ export class ToggleStatusbarVisibilityAction extends Action2 { registerAction2(ToggleStatusbarVisibilityAction); -// --- Bse Class Toggle Boolean Setting Action +// --- Base Class Toggle Boolean Setting Action abstract class BaseToggleBooleanSettingAction extends Action2 { @@ -524,7 +524,7 @@ export class ToggleSeparatePinnedTabsAction extends BaseToggleBooleanSettingActi original: 'Separate Pinned Editor Tabs' }, category: Categories.View, - precondition: ContextKeyExpr.has('config.workbench.editor.showTabs'), + precondition: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'multiple'), f1: true }); } diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 7289ba1aca6..18cbd10b7d3 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -355,7 +355,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_ MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_DOWN, title: localize('splitDown', "Split Down") }, group: '2_split', order: 20 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_LEFT, title: localize('splitLeft', "Split Left") }, group: '2_split', order: 30 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_RIGHT, title: localize('splitRight', "Split Right") }, group: '2_split', order: 40 }); -MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: ToggleTabsVisibilityAction.ID, title: localize('toggleTabs', "Editor Tabs"), toggled: ContextKeyExpr.has('config.workbench.editor.showTabs') }, group: '3_config', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: ToggleTabsVisibilityAction.ID, title: localize('toggleTabs', "Editor Tabs"), toggled: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'multiple') }, group: '3_config', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: ToggleSeparatePinnedTabsAction.ID, title: localize('toggleSeparatePinnedEditorTabs', "Separate Pinned Editor Tabs"), toggled: ContextKeyExpr.has('config.workbench.editor.pinnedTabsOnSeparateRow') }, when: EditorPinnedAndUnpinnedTabsContext, group: '3_config', order: 20 }); // Editor Title Context Menu From e859dc6a999d5b5b6cf26a09e07dc5f71ec0bb46 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Sun, 15 Oct 2023 18:39:33 +0200 Subject: [PATCH 08/55] :lipstick: --- src/vs/workbench/browser/workbench.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index c3b83193950..c6c287adb4d 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -730,7 +730,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'zenMode.showTabs': { 'type': 'string', 'enum': ['multiple', 'single', 'none'], - 'description': localize('zenMode.showTabs', "Controls whether turning on Zen Mode should show muötiple editor tabs, a single editor tab or hide the editor title area completely."), + 'description': localize('zenMode.showTabs', "Controls whether turning on Zen Mode should show multiple editor tabs, a single editor tab or hide the editor title area completely."), 'enumDescriptions': [ localize('zenMode.showTabs.multiple', "Each editor is displayed as a tab in the editor title area."), localize('zenMode.showTabs.single', "The active editor is displayed as a single large tab in the editor title area."), From 46e73c7837a5c27930d87da1d84f6783f7ebdc9f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 16 Oct 2023 15:15:18 -0700 Subject: [PATCH 09/55] fix #195582 --- .../accessibility/browser/accessibleView.ts | 4 +- .../contrib/terminal/browser/terminal.ts | 6 + .../terminal/browser/terminalInstance.ts | 166 +++++++++--------- .../terminalAccessibleBufferProvider.ts | 14 +- 4 files changed, 102 insertions(+), 88 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 8a7d668a8a8..b1036069275 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -57,7 +57,7 @@ export interface IAccessibleContentProvider { actions?: IAction[]; provideContent(): string; onClose(): void; - onKeyUp?(e: IKeyboardEvent): void; + onKeyDown?(e: IKeyboardEvent): void; previous?(): void; next?(): void; /** @@ -507,7 +507,6 @@ export class AccessibleView extends Disposable { setTimeout(() => provider.onClose(), 100); }; const disposableStore = new DisposableStore(); - disposableStore.add(this._editorWidget.onKeyUp((e) => provider.onKeyUp?.(e))); disposableStore.add(this._editorWidget.onKeyDown((e) => { if (e.keyCode === KeyCode.Escape) { handleEscape(e); @@ -518,6 +517,7 @@ export class AccessibleView extends Disposable { e.preventDefault(); e.stopPropagation(); } + provider.onKeyDown?.(e); })); disposableStore.add(addDisposableListener(this._toolbar.getElement(), EventType.KEY_DOWN, (e: KeyboardEvent) => { const keyboardEvent = new StandardKeyboardEvent(e); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index ea1b9088874..d7bc90e1e8b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -993,6 +993,12 @@ export interface ITerminalInstance { * Gets a terminal contribution by its ID. */ getContribution(id: string): T | null; + + /** + * Whether the event should be handled by xterm.js or the workbench. + * @param event the event to process + */ + shouldProcessKeyEvent(event: KeyboardEvent): boolean; } export const enum XtermTerminalConstants { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index a7c08bc63d8..339a9e4ce5c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -908,92 +908,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._setAriaLabel(xterm.raw, this._instanceId, this._title); - xterm.raw.attachCustomKeyEventHandler((event: KeyboardEvent): boolean => { - // Disable all input if the terminal is exiting - if (this._isExiting) { - return false; - } + xterm.raw.attachCustomKeyEventHandler((event: KeyboardEvent): boolean => this.shouldProcessKeyEvent(event)); - const standardKeyboardEvent = new StandardKeyboardEvent(event); - const resolveResult = this._keybindingService.softDispatch(standardKeyboardEvent, standardKeyboardEvent.target); - - // Respect chords if the allowChords setting is set and it's not Escape. Escape is - // handled specially for Zen Mode's Escape, Escape chord, plus it's important in - // terminals generally - const isValidChord = resolveResult.kind === ResultKind.MoreChordsNeeded && this._configHelper.config.allowChords && event.key !== 'Escape'; - if (this._keybindingService.inChordMode || isValidChord) { - event.preventDefault(); - return false; - } - - const SHOW_TERMINAL_CONFIG_PROMPT_KEY = 'terminal.integrated.showTerminalConfigPrompt'; - const EXCLUDED_KEYS = ['RightArrow', 'LeftArrow', 'UpArrow', 'DownArrow', 'Space', 'Meta', 'Control', 'Shift', 'Alt', '', 'Delete', 'Backspace', 'Tab']; - - // only keep track of input if prompt hasn't already been shown - if (this._storageService.getBoolean(SHOW_TERMINAL_CONFIG_PROMPT_KEY, StorageScope.APPLICATION, true) && - !EXCLUDED_KEYS.includes(event.key) && - !event.ctrlKey && - !event.shiftKey && - !event.altKey) { - this._hasHadInput = true; - } - - // for keyboard events that resolve to commands described - // within commandsToSkipShell, either alert or skip processing by xterm.js - if (resolveResult.kind === ResultKind.KbFound && resolveResult.commandId && this._skipTerminalCommands.some(k => k === resolveResult.commandId) && !this._configHelper.config.sendKeybindingsToShell) { - // don't alert when terminal is opened or closed - if (this._storageService.getBoolean(SHOW_TERMINAL_CONFIG_PROMPT_KEY, StorageScope.APPLICATION, true) && - this._hasHadInput && - !TERMINAL_CREATION_COMMANDS.includes(resolveResult.commandId)) { - this._notificationService.prompt( - Severity.Info, - nls.localize('keybindingHandling', "Some keybindings don't go to the terminal by default and are handled by {0} instead.", this._productService.nameLong), - [ - { - label: nls.localize('configureTerminalSettings', "Configure Terminal Settings"), - run: () => { - this._preferencesService.openSettings({ jsonEditor: false, query: `@id:${TerminalSettingId.CommandsToSkipShell},${TerminalSettingId.SendKeybindingsToShell},${TerminalSettingId.AllowChords}` }); - } - } as IPromptChoice - ] - ); - this._storageService.store(SHOW_TERMINAL_CONFIG_PROMPT_KEY, false, StorageScope.APPLICATION, StorageTarget.USER); - } - event.preventDefault(); - return false; - } - - // Skip processing by xterm.js of keyboard events that match menu bar mnemonics - if (this._configHelper.config.allowMnemonics && !isMacintosh && event.altKey) { - return false; - } - - // If tab focus mode is on, tab is not passed to the terminal - if (TabFocus.getTabFocusMode() && event.key === 'Tab') { - return false; - } - - // Prevent default when shift+tab is being sent to the terminal to avoid it bubbling up - // and changing focus https://github.com/microsoft/vscode/issues/188329 - if (event.key === 'Tab' && event.shiftKey) { - event.preventDefault(); - return true; - } - - // Always have alt+F4 skip the terminal on Windows and allow it to be handled by the - // system - if (isWindows && event.altKey && event.key === 'F4' && !event.ctrlKey) { - return false; - } - - // Fallback to force ctrl+v to paste on browsers that do not support - // navigator.clipboard.readText - if (!BrowserFeatures.clipboard.readText && event.key === 'v' && event.ctrlKey) { - return false; - } - - return true; - }); this._register(dom.addDisposableListener(xterm.raw.element, 'mousedown', () => { // We need to listen to the mouseup event on the document since the user may release // the mouse button anywhere outside of _xterm.element. @@ -1053,6 +969,86 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } } + shouldProcessKeyEvent(event: KeyboardEvent): boolean { + // Disable all input if the terminal is exiting + if (this._isExiting) { + return false; + } + + const standardKeyboardEvent = new StandardKeyboardEvent(event); + const resolveResult = this._keybindingService.softDispatch(standardKeyboardEvent, standardKeyboardEvent.target); + + // Respect chords if the allowChords setting is set and it's not Escape. Escape is + // handled specially for Zen Mode's Escape, Escape chord, plus it's important in + // terminals generally + const isValidChord = resolveResult.kind === ResultKind.MoreChordsNeeded && this._configHelper.config.allowChords && event.key !== 'Escape'; + if (this._keybindingService.inChordMode || isValidChord) { + event.preventDefault(); + return false; + } + + const SHOW_TERMINAL_CONFIG_PROMPT_KEY = 'terminal.integrated.showTerminalConfigPrompt'; + const EXCLUDED_KEYS = ['RightArrow', 'LeftArrow', 'UpArrow', 'DownArrow', 'Space', 'Meta', 'Control', 'Shift', 'Alt', '', 'Delete', 'Backspace', 'Tab']; + + // only keep track of input if prompt hasn't already been shown + if (this._storageService.getBoolean(SHOW_TERMINAL_CONFIG_PROMPT_KEY, StorageScope.APPLICATION, true) && + !EXCLUDED_KEYS.includes(event.key) && + !event.ctrlKey && + !event.shiftKey && + !event.altKey) { + this._hasHadInput = true; + } + + // for keyboard events that resolve to commands described + // within commandsToSkipShell, either alert or skip processing by xterm.js + if (resolveResult.kind === ResultKind.KbFound && resolveResult.commandId && this._skipTerminalCommands.some(k => k === resolveResult.commandId) && !this._configHelper.config.sendKeybindingsToShell) { + // don't alert when terminal is opened or closed + if (this._storageService.getBoolean(SHOW_TERMINAL_CONFIG_PROMPT_KEY, StorageScope.APPLICATION, true) && + this._hasHadInput && + !TERMINAL_CREATION_COMMANDS.includes(resolveResult.commandId)) { + this._notificationService.prompt( + Severity.Info, + nls.localize('keybindingHandling', "Some keybindings don't go to the terminal by default and are handled by {0} instead.", this._productService.nameLong), + [ + { + label: nls.localize('configureTerminalSettings', "Configure Terminal Settings"), + run: () => { + this._preferencesService.openSettings({ jsonEditor: false, query: `@id:${TerminalSettingId.CommandsToSkipShell},${TerminalSettingId.SendKeybindingsToShell},${TerminalSettingId.AllowChords}` }); + } + } as IPromptChoice + ] + ); + this._storageService.store(SHOW_TERMINAL_CONFIG_PROMPT_KEY, false, StorageScope.APPLICATION, StorageTarget.USER); + } + event.preventDefault(); + return false; + } + + // Skip processing by xterm.js of keyboard events that match menu bar mnemonics + if (this._configHelper.config.allowMnemonics && !isMacintosh && event.altKey) { + return false; + } + + // If tab focus mode is on, tab is not passed to the terminal + if (TabFocus.getTabFocusMode() && event.key === 'Tab') { + return false; + } + + // Always have alt+F4 skip the terminal on Windows and allow it to be handled by the + // system + if (isWindows && event.altKey && event.key === 'F4' && !event.ctrlKey) { + return false; + } + + // Fallback to force ctrl+v to paste on browsers that do not support + // navigator.clipboard.readText + if (!BrowserFeatures.clipboard.readText && event.key === 'v' && event.ctrlKey) { + return false; + } + + return true; + } + resetFocusContextKey(): void { this._terminalFocusContextKey.reset(); this._terminalShellIntegrationEnabledContextKey.reset(); diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts index 057c0ab530f..ac195f84cd6 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Emitter } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IModelService } from 'vs/editor/common/services/model'; @@ -24,7 +25,7 @@ export class TerminalAccessibleBufferProvider extends DisposableStore implements readonly onDidRequestClearLastProvider = this._onDidRequestClearProvider.event; private _focusedInstance: ITerminalInstance | undefined; constructor( - private readonly _instance: Pick, + private readonly _instance: Pick, private _bufferTracker: BufferContentTracker, customHelp: () => string, @IModelService _modelService: IModelService, @@ -50,6 +51,13 @@ export class TerminalAccessibleBufferProvider extends DisposableStore implements })); } + onKeyDown(e: IKeyboardEvent): void { + if (!this._instance.shouldProcessKeyEvent(e.browserEvent) || !isSingleLetterKey(e.browserEvent)) { + return; + } + this._instance.focus(); + } + onClose() { this._instance.focus(); } @@ -115,3 +123,7 @@ export class TerminalAccessibleBufferProvider extends DisposableStore implements } } export interface ICommandWithEditorLine { command: ITerminalCommand | ICurrentPartialCommand; lineNumber: number } + +function isSingleLetterKey(event: KeyboardEvent): boolean { + return event.key.length === 1 && !event.ctrlKey && !event.altKey && !event.metaKey && !event.shiftKey; +} From 712a1b5455f0b9ca620472f5334652142dd97cf8 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 17 Oct 2023 15:24:41 +0200 Subject: [PATCH 10/55] - remove diff locking - Remove unused variable and property in InlineChatLivePreviewWidget --- .../inlineChat/browser/inlineChatLivePreviewWidget.ts | 10 ---------- .../inlineChat/browser/inlineChatStrategies.ts | 11 ++--------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts index e4a8b22b0aa..63e66be5f3d 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts @@ -46,7 +46,6 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { private readonly _diffEditor: IDiffEditor; private _dim: Dimension | undefined; private _isVisible: boolean = false; - private _isDiffLocked: boolean = false; constructor( editor: ICodeEditor, @@ -141,7 +140,6 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { override show(): void { assertType(this.editor.hasModel()); this._sessionStore.clear(); - this._isDiffLocked = false; this._isVisible = true; this._sessionStore.add(this._diffEditor.onDidUpdateDiff(() => { @@ -159,17 +157,9 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { this._updateFromChanges(this._session.wholeRange.value, this._session.lastTextModelChanges); } - lockToDiff(): void { - this._isDiffLocked = true; - } - private _updateFromChanges(range: Range, changes: readonly DetailedLineRangeMapping[]): void { assertType(this.editor.hasModel()); - if (this._isDiffLocked) { - return; - } - if (changes.length === 0 || this._session.textModel0.getValueLength() === 0) { // no change or changes to an empty file this._logService.debug('[IE] livePreview-mode: no diff'); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 95480e8fb06..8cdcaa4998d 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -238,7 +238,6 @@ export class LiveStrategy extends EditModeStrategy { protected readonly _session: Session, protected readonly _editor: ICodeEditor, protected readonly _widget: InlineChatWidget, - @IContextKeyService contextKeyService: IContextKeyService, @IConfigurationService configService: IConfigurationService, @IStorageService protected _storageService: IStorageService, @IBulkEditService protected readonly _bulkEditService: IBulkEditService, @@ -390,14 +389,13 @@ export class LivePreviewStrategy extends LiveStrategy { session: Session, editor: ICodeEditor, widget: InlineChatWidget, - @IContextKeyService contextKeyService: IContextKeyService, @IConfigurationService configService: IConfigurationService, @IStorageService storageService: IStorageService, @IBulkEditService bulkEditService: IBulkEditService, @IEditorWorkerService editorWorkerService: IEditorWorkerService, @IInstantiationService instaService: IInstantiationService, ) { - super(session, editor, widget, contextKeyService, configService, storageService, bulkEditService, editorWorkerService, instaService); + super(session, editor, widget, configService, storageService, bulkEditService, editorWorkerService, instaService); this._diffZone = new Lazy(() => instaService.createInstance(InlineChatLivePreviewWidget, editor, session)); this._previewZone = new Lazy(() => instaService.createInstance(InlineChatFileCreatePreviewWidget, editor)); @@ -431,11 +429,6 @@ export class LivePreviewStrategy extends LiveStrategy { } } - override async undoChanges(response: EditResponse): Promise { - this._diffZone.value.lockToDiff(); - super.undoChanges(response); - } - protected override _doToggleDiff(): void { const scrollState = StableEditorScrollState.capture(this._editor); if (this._diffEnabled) { @@ -447,7 +440,7 @@ export class LivePreviewStrategy extends LiveStrategy { } override hasFocus(): boolean { - return super.hasFocus() || this._diffZone.value.hasFocus() || this._previewZone.value.hasFocus(); + return super.hasFocus() || Boolean(this._diffZone.rawValue?.hasFocus()) || Boolean(this._previewZone.rawValue?.hasFocus()); } override getWidgetPosition(): Position | undefined { From 569cf2b3d9b19d9294a28daaba3be4b88dfcf812 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 17 Oct 2023 11:30:53 -0700 Subject: [PATCH 11/55] More minor follow up style tweaks --- src/vs/workbench/contrib/chat/browser/media/chat.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index d5d5a10dce4..cbab68d1e76 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -332,10 +332,12 @@ .interactive-session .interactive-input-part .interactive-input-followups .interactive-session-followups .monaco-button { display: block; color: var(--vscode-textLink-foreground); + font-size: 12px; } .interactive-session .interactive-input-part .interactive-input-followups .interactive-session-followups code { font-family: var(--monaco-monospace-font); + font-size: 11px; } .interactive-session .interactive-input-part .interactive-input-followups .interactive-session-followups .monaco-button .codicon-sparkle { From 5d945cf09763dd36ba4a9fad907d940add16a43d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 17 Oct 2023 11:50:45 -0700 Subject: [PATCH 12/55] Make xterm.js multi-window aware Fixes #195577 --- package.json | 14 ++--- remote/package.json | 14 ++--- remote/web/package.json | 10 ++-- remote/web/yarn.lock | 40 ++++++------- remote/yarn.lock | 56 +++++++++---------- .../terminal/browser/terminalInstance.ts | 6 ++ yarn.lock | 56 +++++++++---------- 7 files changed, 101 insertions(+), 95 deletions(-) diff --git a/package.json b/package.json index bfe56304cee..0a4ec7729cd 100644 --- a/package.json +++ b/package.json @@ -96,14 +96,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.4.0-beta.31", - "xterm-addon-canvas": "0.6.0-beta.31", + "xterm": "5.4.0-beta.32", + "xterm-addon-canvas": "0.6.0-beta.32", "xterm-addon-image": "0.6.0-beta.21", - "xterm-addon-search": "0.14.0-beta.30", - "xterm-addon-serialize": "0.12.0-beta.30", - "xterm-addon-unicode11": "0.7.0-beta.30", - "xterm-addon-webgl": "0.17.0-beta.30", - "xterm-headless": "5.4.0-beta.31", + "xterm-addon-search": "0.14.0-beta.31", + "xterm-addon-serialize": "0.12.0-beta.31", + "xterm-addon-unicode11": "0.7.0-beta.31", + "xterm-addon-webgl": "0.17.0-beta.31", + "xterm-headless": "5.4.0-beta.32", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index a144d3f85b2..dbb57afa152 100644 --- a/remote/package.json +++ b/remote/package.json @@ -26,14 +26,14 @@ "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", - "xterm": "5.4.0-beta.31", - "xterm-addon-canvas": "0.6.0-beta.31", + "xterm": "5.4.0-beta.32", + "xterm-addon-canvas": "0.6.0-beta.32", "xterm-addon-image": "0.6.0-beta.21", - "xterm-addon-search": "0.14.0-beta.30", - "xterm-addon-serialize": "0.12.0-beta.30", - "xterm-addon-unicode11": "0.7.0-beta.30", - "xterm-addon-webgl": "0.17.0-beta.30", - "xterm-headless": "5.4.0-beta.31", + "xterm-addon-search": "0.14.0-beta.31", + "xterm-addon-serialize": "0.12.0-beta.31", + "xterm-addon-unicode11": "0.7.0-beta.31", + "xterm-addon-webgl": "0.17.0-beta.31", + "xterm-headless": "5.4.0-beta.32", "yauzl": "^2.9.2", "yazl": "^2.4.3" } diff --git a/remote/web/package.json b/remote/web/package.json index 0fb5ee2bcfc..2634c6ff3b8 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -11,11 +11,11 @@ "tas-client-umd": "0.1.8", "vscode-oniguruma": "1.7.0", "vscode-textmate": "9.0.0", - "xterm": "5.4.0-beta.31", - "xterm-addon-canvas": "0.6.0-beta.31", + "xterm": "5.4.0-beta.32", + "xterm-addon-canvas": "0.6.0-beta.32", "xterm-addon-image": "0.6.0-beta.21", - "xterm-addon-search": "0.14.0-beta.30", - "xterm-addon-unicode11": "0.7.0-beta.30", - "xterm-addon-webgl": "0.17.0-beta.30" + "xterm-addon-search": "0.14.0-beta.31", + "xterm-addon-unicode11": "0.7.0-beta.31", + "xterm-addon-webgl": "0.17.0-beta.31" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index bd6e5adf267..6eb7304ba80 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -68,32 +68,32 @@ vscode-textmate@9.0.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-9.0.0.tgz#313c6c8792b0507aef35aeb81b6b370b37c44d6c" integrity sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg== -xterm-addon-canvas@0.6.0-beta.31: - version "0.6.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.6.0-beta.31.tgz#04ebde31c5e01b2595b966a2861deaec9927e1cb" - integrity sha512-/Dz90IF5FQqzAitKi3k/JEyyRMhSuQG8PVtB2NwOlWUcE3Ukp6gJMFdkyfOOt0Lx/8oyWR7xoDgKY3bxbzpkGQ== +xterm-addon-canvas@0.6.0-beta.32: + version "0.6.0-beta.32" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.6.0-beta.32.tgz#c9e74dd72fcc981a2e0cbd0b82827676bc5c74b9" + integrity sha512-Xw7oE4dbS+x+pu6cGW1bDSXcVviuorLz1OLaYw46jjmDezIqQIIEMhSMOprExFEWgeRQ9AEN4lPqw6aH87V74w== xterm-addon-image@0.6.0-beta.21: version "0.6.0-beta.21" resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.21.tgz#e3708bc504c56a23ff31f12a2eeb335331a92aac" integrity sha512-8/PTaXVPa4kQ0xzVeuZZk10OpbZBj2cgfwhM2B0ChSPvwrk0lX+ksnXdtDKH3tg+JYvo7fIhNXtkr4NwWt7VJQ== -xterm-addon-search@0.14.0-beta.30: - version "0.14.0-beta.30" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.14.0-beta.30.tgz#a84321ded127ab13a0bdbf901d2524900330f6ea" - integrity sha512-e5qb68lmpxQ1cG4oJKq9NC61oV2xGynRyruB2luerGeXPhqkGj9RSDeOqgCWbnQNTfBmkROzrn02MeJAsoqvGQ== +xterm-addon-search@0.14.0-beta.31: + version "0.14.0-beta.31" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.14.0-beta.31.tgz#933ca5d2d642dacad29f2cfbd50830cff83bc274" + integrity sha512-JRY1ukhoh32D0AMz78xpumQkLgkcP9d3GXj6gzVHZZsjLAMDaJYEubYq1bUhM7IGHUyg+x0sdRJyx7d6fJpiQg== -xterm-addon-unicode11@0.7.0-beta.30: - version "0.7.0-beta.30" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.7.0-beta.30.tgz#2de2c412d41823f31b66f68c7d8d0fb9e1a66cd3" - integrity sha512-pLSSBxwCOD5aShGnk6VveLHpjDwEDrIci2WnVcuWIbPaqHkB16d6l17jJ50843TaW66k1Np3ZCpDteOoC0Z6Kw== +xterm-addon-unicode11@0.7.0-beta.31: + version "0.7.0-beta.31" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.7.0-beta.31.tgz#abcba752172323f31312bd8a3f9b6a049dbca6e3" + integrity sha512-vvBKJbBoLbeIf2++6D16VnOOwevZE3nyO/PDZ7cyTJK1eYR73rr8ZbjUrH92YoTu4Z8MpZFepGQOgK/vlAQMwQ== -xterm-addon-webgl@0.17.0-beta.30: - version "0.17.0-beta.30" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.17.0-beta.30.tgz#e4d7b18eb8f2b0be6ee8bf35185e91b33570e67f" - integrity sha512-SjdfIOmx9xunom2Bk//iQ2DoqYlvAsunEWD3nxdED0oYYf1SPlKxt3I47YHWVshacw6QPZEJHVXJ6K+kHlel/Q== +xterm-addon-webgl@0.17.0-beta.31: + version "0.17.0-beta.31" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.17.0-beta.31.tgz#3cd29b4858e3f4f6dd5a8dd969454e85e1f43baa" + integrity sha512-vYHj+HlTcqUlFFVuoCTjlgh89/lIoSkZ7Nc87cwSFTrJsl07qoKutmpupqFXyjhbEA1fQY2SuQLx08Gmf2jWkQ== -xterm@5.4.0-beta.31: - version "5.4.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.4.0-beta.31.tgz#598f66cfa49609d4e4935fbaf00aadff8e23d174" - integrity sha512-lAuiiWxxU8s0UaDwuJZupoBOtb9bY5ouBkOufnfpLK05ACm0046TPxs3bg05jPUI8y5y/qLgKqK0L5TxAiZ8WA== +xterm@5.4.0-beta.32: + version "5.4.0-beta.32" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.4.0-beta.32.tgz#1b4242cf1c0c1a5a1070da58d3f11956b537130a" + integrity sha512-mWTwEiNBFMF89oqVfi6qTM2Py5gC1Mwvslx1KxmI2Ukgh9v3CrqKDhj29eY1ZeAo0uuYknFWKyuexqp+3SHJCA== diff --git a/remote/yarn.lock b/remote/yarn.lock index 4b7185b4ffa..b14b646d31c 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -591,45 +591,45 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -xterm-addon-canvas@0.6.0-beta.31: - version "0.6.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.6.0-beta.31.tgz#04ebde31c5e01b2595b966a2861deaec9927e1cb" - integrity sha512-/Dz90IF5FQqzAitKi3k/JEyyRMhSuQG8PVtB2NwOlWUcE3Ukp6gJMFdkyfOOt0Lx/8oyWR7xoDgKY3bxbzpkGQ== +xterm-addon-canvas@0.6.0-beta.32: + version "0.6.0-beta.32" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.6.0-beta.32.tgz#c9e74dd72fcc981a2e0cbd0b82827676bc5c74b9" + integrity sha512-Xw7oE4dbS+x+pu6cGW1bDSXcVviuorLz1OLaYw46jjmDezIqQIIEMhSMOprExFEWgeRQ9AEN4lPqw6aH87V74w== xterm-addon-image@0.6.0-beta.21: version "0.6.0-beta.21" resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.21.tgz#e3708bc504c56a23ff31f12a2eeb335331a92aac" integrity sha512-8/PTaXVPa4kQ0xzVeuZZk10OpbZBj2cgfwhM2B0ChSPvwrk0lX+ksnXdtDKH3tg+JYvo7fIhNXtkr4NwWt7VJQ== -xterm-addon-search@0.14.0-beta.30: - version "0.14.0-beta.30" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.14.0-beta.30.tgz#a84321ded127ab13a0bdbf901d2524900330f6ea" - integrity sha512-e5qb68lmpxQ1cG4oJKq9NC61oV2xGynRyruB2luerGeXPhqkGj9RSDeOqgCWbnQNTfBmkROzrn02MeJAsoqvGQ== +xterm-addon-search@0.14.0-beta.31: + version "0.14.0-beta.31" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.14.0-beta.31.tgz#933ca5d2d642dacad29f2cfbd50830cff83bc274" + integrity sha512-JRY1ukhoh32D0AMz78xpumQkLgkcP9d3GXj6gzVHZZsjLAMDaJYEubYq1bUhM7IGHUyg+x0sdRJyx7d6fJpiQg== -xterm-addon-serialize@0.12.0-beta.30: - version "0.12.0-beta.30" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.12.0-beta.30.tgz#80c4101f45a463ef139705bcd3dcaf0811f51ea4" - integrity sha512-nZP0ip5bd9LBoCTN9vCnn4iLatF4RRwzLupQf9r2N9x1bULzTZ1kAXAQe5gghsXjSEDDtyY2LzGigqTd2KVAqQ== +xterm-addon-serialize@0.12.0-beta.31: + version "0.12.0-beta.31" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.12.0-beta.31.tgz#2a95dc1e12f4097e2894b04c9cb8fff0bc0b858c" + integrity sha512-h2rWR+Lfi1Iv4VkLUlrBMYh5Mdq8vux2BKyCJe6a1ZnEu5Dzb0VuiNxfTKXTCT5M83nMn7TCB9TX0E8z6bs7xw== -xterm-addon-unicode11@0.7.0-beta.30: - version "0.7.0-beta.30" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.7.0-beta.30.tgz#2de2c412d41823f31b66f68c7d8d0fb9e1a66cd3" - integrity sha512-pLSSBxwCOD5aShGnk6VveLHpjDwEDrIci2WnVcuWIbPaqHkB16d6l17jJ50843TaW66k1Np3ZCpDteOoC0Z6Kw== +xterm-addon-unicode11@0.7.0-beta.31: + version "0.7.0-beta.31" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.7.0-beta.31.tgz#abcba752172323f31312bd8a3f9b6a049dbca6e3" + integrity sha512-vvBKJbBoLbeIf2++6D16VnOOwevZE3nyO/PDZ7cyTJK1eYR73rr8ZbjUrH92YoTu4Z8MpZFepGQOgK/vlAQMwQ== -xterm-addon-webgl@0.17.0-beta.30: - version "0.17.0-beta.30" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.17.0-beta.30.tgz#e4d7b18eb8f2b0be6ee8bf35185e91b33570e67f" - integrity sha512-SjdfIOmx9xunom2Bk//iQ2DoqYlvAsunEWD3nxdED0oYYf1SPlKxt3I47YHWVshacw6QPZEJHVXJ6K+kHlel/Q== +xterm-addon-webgl@0.17.0-beta.31: + version "0.17.0-beta.31" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.17.0-beta.31.tgz#3cd29b4858e3f4f6dd5a8dd969454e85e1f43baa" + integrity sha512-vYHj+HlTcqUlFFVuoCTjlgh89/lIoSkZ7Nc87cwSFTrJsl07qoKutmpupqFXyjhbEA1fQY2SuQLx08Gmf2jWkQ== -xterm-headless@5.4.0-beta.31: - version "5.4.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.4.0-beta.31.tgz#9538553c7426222f94d7da7ed467e699ebaeeedd" - integrity sha512-EE/ZlsZcBE5VOkjQU/KdRL4gvSkfrC2P7VxrmK1+PLc6+QMjPxs60A4Pun3mIIS0MFfN23p6hmN22GAXVckCXA== +xterm-headless@5.4.0-beta.32: + version "5.4.0-beta.32" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.4.0-beta.32.tgz#0d5cd35e1a0372888055ff0b06dfe17457979a6c" + integrity sha512-DQduq8KSoQZyRrQAFB+FkcY2UMxCW39P1/duOpksebc6PT9pbGkyPe5s+AdUQGiYzriEpzVtKUzDcquoVmpPhA== -xterm@5.4.0-beta.31: - version "5.4.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.4.0-beta.31.tgz#598f66cfa49609d4e4935fbaf00aadff8e23d174" - integrity sha512-lAuiiWxxU8s0UaDwuJZupoBOtb9bY5ouBkOufnfpLK05ACm0046TPxs3bg05jPUI8y5y/qLgKqK0L5TxAiZ8WA== +xterm@5.4.0-beta.32: + version "5.4.0-beta.32" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.4.0-beta.32.tgz#1b4242cf1c0c1a5a1070da58d3f11956b537130a" + integrity sha512-mWTwEiNBFMF89oqVfi6qTM2Py5gC1Mwvslx1KxmI2Ukgh9v3CrqKDhj29eY1ZeAo0uuYknFWKyuexqp+3SHJCA== yallist@^4.0.0: version "4.0.0" diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index a7c08bc63d8..32bcc05c715 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -863,6 +863,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // The container changed, reattach this._container = container; this._container.appendChild(this._wrapperElement); + + // If xterm is already attached, call open again to pick up any changes to the window. + if (this.xterm?.raw.element) { + this.xterm.raw.open(this.xterm.raw.element); + } + this.xterm?.refresh(); setTimeout(() => this._initDragAndDrop(container)); diff --git a/yarn.lock b/yarn.lock index 4a964f90843..020414666f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10285,45 +10285,45 @@ xtend@~4.0.0, xtend@~4.0.1: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -xterm-addon-canvas@0.6.0-beta.31: - version "0.6.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.6.0-beta.31.tgz#04ebde31c5e01b2595b966a2861deaec9927e1cb" - integrity sha512-/Dz90IF5FQqzAitKi3k/JEyyRMhSuQG8PVtB2NwOlWUcE3Ukp6gJMFdkyfOOt0Lx/8oyWR7xoDgKY3bxbzpkGQ== +xterm-addon-canvas@0.6.0-beta.32: + version "0.6.0-beta.32" + resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.6.0-beta.32.tgz#c9e74dd72fcc981a2e0cbd0b82827676bc5c74b9" + integrity sha512-Xw7oE4dbS+x+pu6cGW1bDSXcVviuorLz1OLaYw46jjmDezIqQIIEMhSMOprExFEWgeRQ9AEN4lPqw6aH87V74w== xterm-addon-image@0.6.0-beta.21: version "0.6.0-beta.21" resolved "https://registry.yarnpkg.com/xterm-addon-image/-/xterm-addon-image-0.6.0-beta.21.tgz#e3708bc504c56a23ff31f12a2eeb335331a92aac" integrity sha512-8/PTaXVPa4kQ0xzVeuZZk10OpbZBj2cgfwhM2B0ChSPvwrk0lX+ksnXdtDKH3tg+JYvo7fIhNXtkr4NwWt7VJQ== -xterm-addon-search@0.14.0-beta.30: - version "0.14.0-beta.30" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.14.0-beta.30.tgz#a84321ded127ab13a0bdbf901d2524900330f6ea" - integrity sha512-e5qb68lmpxQ1cG4oJKq9NC61oV2xGynRyruB2luerGeXPhqkGj9RSDeOqgCWbnQNTfBmkROzrn02MeJAsoqvGQ== +xterm-addon-search@0.14.0-beta.31: + version "0.14.0-beta.31" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.14.0-beta.31.tgz#933ca5d2d642dacad29f2cfbd50830cff83bc274" + integrity sha512-JRY1ukhoh32D0AMz78xpumQkLgkcP9d3GXj6gzVHZZsjLAMDaJYEubYq1bUhM7IGHUyg+x0sdRJyx7d6fJpiQg== -xterm-addon-serialize@0.12.0-beta.30: - version "0.12.0-beta.30" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.12.0-beta.30.tgz#80c4101f45a463ef139705bcd3dcaf0811f51ea4" - integrity sha512-nZP0ip5bd9LBoCTN9vCnn4iLatF4RRwzLupQf9r2N9x1bULzTZ1kAXAQe5gghsXjSEDDtyY2LzGigqTd2KVAqQ== +xterm-addon-serialize@0.12.0-beta.31: + version "0.12.0-beta.31" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.12.0-beta.31.tgz#2a95dc1e12f4097e2894b04c9cb8fff0bc0b858c" + integrity sha512-h2rWR+Lfi1Iv4VkLUlrBMYh5Mdq8vux2BKyCJe6a1ZnEu5Dzb0VuiNxfTKXTCT5M83nMn7TCB9TX0E8z6bs7xw== -xterm-addon-unicode11@0.7.0-beta.30: - version "0.7.0-beta.30" - resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.7.0-beta.30.tgz#2de2c412d41823f31b66f68c7d8d0fb9e1a66cd3" - integrity sha512-pLSSBxwCOD5aShGnk6VveLHpjDwEDrIci2WnVcuWIbPaqHkB16d6l17jJ50843TaW66k1Np3ZCpDteOoC0Z6Kw== +xterm-addon-unicode11@0.7.0-beta.31: + version "0.7.0-beta.31" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.7.0-beta.31.tgz#abcba752172323f31312bd8a3f9b6a049dbca6e3" + integrity sha512-vvBKJbBoLbeIf2++6D16VnOOwevZE3nyO/PDZ7cyTJK1eYR73rr8ZbjUrH92YoTu4Z8MpZFepGQOgK/vlAQMwQ== -xterm-addon-webgl@0.17.0-beta.30: - version "0.17.0-beta.30" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.17.0-beta.30.tgz#e4d7b18eb8f2b0be6ee8bf35185e91b33570e67f" - integrity sha512-SjdfIOmx9xunom2Bk//iQ2DoqYlvAsunEWD3nxdED0oYYf1SPlKxt3I47YHWVshacw6QPZEJHVXJ6K+kHlel/Q== +xterm-addon-webgl@0.17.0-beta.31: + version "0.17.0-beta.31" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.17.0-beta.31.tgz#3cd29b4858e3f4f6dd5a8dd969454e85e1f43baa" + integrity sha512-vYHj+HlTcqUlFFVuoCTjlgh89/lIoSkZ7Nc87cwSFTrJsl07qoKutmpupqFXyjhbEA1fQY2SuQLx08Gmf2jWkQ== -xterm-headless@5.4.0-beta.31: - version "5.4.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.4.0-beta.31.tgz#9538553c7426222f94d7da7ed467e699ebaeeedd" - integrity sha512-EE/ZlsZcBE5VOkjQU/KdRL4gvSkfrC2P7VxrmK1+PLc6+QMjPxs60A4Pun3mIIS0MFfN23p6hmN22GAXVckCXA== +xterm-headless@5.4.0-beta.32: + version "5.4.0-beta.32" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.4.0-beta.32.tgz#0d5cd35e1a0372888055ff0b06dfe17457979a6c" + integrity sha512-DQduq8KSoQZyRrQAFB+FkcY2UMxCW39P1/duOpksebc6PT9pbGkyPe5s+AdUQGiYzriEpzVtKUzDcquoVmpPhA== -xterm@5.4.0-beta.31: - version "5.4.0-beta.31" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.4.0-beta.31.tgz#598f66cfa49609d4e4935fbaf00aadff8e23d174" - integrity sha512-lAuiiWxxU8s0UaDwuJZupoBOtb9bY5ouBkOufnfpLK05ACm0046TPxs3bg05jPUI8y5y/qLgKqK0L5TxAiZ8WA== +xterm@5.4.0-beta.32: + version "5.4.0-beta.32" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.4.0-beta.32.tgz#1b4242cf1c0c1a5a1070da58d3f11956b537130a" + integrity sha512-mWTwEiNBFMF89oqVfi6qTM2Py5gC1Mwvslx1KxmI2Ukgh9v3CrqKDhj29eY1ZeAo0uuYknFWKyuexqp+3SHJCA== y18n@^3.2.1: version "3.2.2" From ddf6c14e17502e15e823470ac68c4bece7e238f7 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 17 Oct 2023 12:42:25 -0700 Subject: [PATCH 13/55] fix #195208 --- .../browser/parts/notifications/notificationsCenter.ts | 3 +++ .../browser/parts/notifications/notificationsCommands.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index 3d7b73c376a..c7e9ab26652 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -25,6 +25,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { assertAllDefined, assertIsDefined } from 'vs/base/common/types'; import { NotificationsCenterVisibleContext } from 'vs/workbench/common/contextkeys'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; export class NotificationsCenter extends Themable implements INotificationsCenterController { @@ -53,6 +54,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IKeybindingService private readonly keybindingService: IKeybindingService, @INotificationService private readonly notificationService: INotificationService, + @IAccessibleNotificationService private readonly accessibleNotificationService: IAccessibleNotificationService ) { super(themeService); @@ -329,6 +331,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente if (!notification.hasProgress) { notification.close(); } + this.accessibleNotificationService.notify(AccessibleNotificationEvent.Clear); } } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index 21a7891569b..d341f22ee65 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -19,6 +19,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ActionRunner, IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { hash } from 'vs/base/common/hash'; import { firstOrDefault } from 'vs/base/common/arrays'; +import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; // Center export const SHOW_NOTIFICATIONS_CENTER = 'notifications.showList'; @@ -138,9 +139,11 @@ export function registerNotificationCommands(center: INotificationsCenterControl primary: KeyMod.CtrlCmd | KeyCode.Backspace }, handler: (accessor, args?) => { + const accessibleNotificationService = accessor.get(IAccessibleNotificationService); const notification = getNotificationFromContext(accessor.get(IListService), args); if (notification && !notification.hasProgress) { notification.close(); + accessibleNotificationService.notify(AccessibleNotificationEvent.Clear); } } }); From ce8fef79899c980a01dd004575e4a0dcb49ef7c6 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 17 Oct 2023 12:52:08 -0700 Subject: [PATCH 14/55] change default for alerts to always, ignore alerts when audio cues are enabled --- .../browser/accessibilityConfiguration.ts | 8 ++++---- .../browser/accessibleNotificationService.ts | 13 ++++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 2e2eb2e188a..86b8003c0dd 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -125,10 +125,10 @@ const configuration: IConfigurationNode = { ...baseProperty }, [AccessibilityAlertSettingId.Save]: { - 'markdownDescription': localize('alert.save', "When in screen reader mode, alerts when a file is saved. Also see {0}", '`#audioCues.save#`'), + 'markdownDescription': localize('alert.save', "When in screen reader mode, alerts when a file is saved. Note that this will be ignored when {0} is enabled.", '`#audioCues.save#`'), 'type': 'string', 'enum': ['userGesture', 'always', 'never'], - 'default': 'never', + 'default': 'always', 'enumDescriptions': [ localize('alert.save.userGesture', "Alerts when a file is saved via user gesture."), localize('alert.save.always', "Alerts whenever is a file is saved, including auto save."), @@ -137,10 +137,10 @@ const configuration: IConfigurationNode = { tags: ['accessibility'] }, [AccessibilityAlertSettingId.Format]: { - 'markdownDescription': localize('alert.format', "When in screen reader mode, alerts when a file or notebook cell is formatted. Also see {0}", '`#audioCues.format#`'), + 'markdownDescription': localize('alert.format', "When in screen reader mode, alerts when a file or notebook cell is formatted. Note that this will be ignored when {0} is enabled.", '`#audioCues.format#`'), 'type': 'string', 'enum': ['userGesture', 'always', 'never'], - 'default': 'never', + 'default': 'always', 'enumDescriptions': [ localize('alert.format.userGesture', "Alerts when a file is formatted via user gesture."), localize('alert.format.always', "Alerts whenever is a file is formatted, including auto save, on cell execution, and more."), diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleNotificationService.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleNotificationService.ts index 62d1c14ca23..7e8503931d9 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleNotificationService.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleNotificationService.ts @@ -50,16 +50,19 @@ export class AccessibleNotificationService extends Disposable implements IAccess if (!alertSetting) { return; } - const alertSettingValue: NotificationSetting = this._configurationService.getValue(alertSetting); - if (this._shouldNotify(alertSettingValue, userGesture)) { - this._logService.debug('AccessibleNotificationService alerting: ', alertMessage); - this._accessibilityService.alert(alertMessage); - } const audioCueSetting: NotificationSetting = this._configurationService.getValue(audioCue.settingsKey); if (this._shouldNotify(audioCueSetting, userGesture)) { this._logService.debug('AccessibleNotificationService playing sound: ', audioCue.name); + console.log('AccessibleNotificationService playing sound: ', audioCue.name); // Play sound bypasses the usual audio cue checks IE screen reader optimized, auto, etc. this._audioCueService.playSound(audioCue.sound.getSound(), true); + return; + } + const alertSettingValue: NotificationSetting = this._configurationService.getValue(alertSetting); + if (this._shouldNotify(alertSettingValue, userGesture)) { + this._logService.debug('AccessibleNotificationService alerting: ', alertMessage); + console.log('AccessibleNotificationService alerting: ', alertMessage); + this._accessibilityService.alert(alertMessage); } } From 99d8d7f17aaf3e9c3d80bc095a27363177838c3f Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 17 Oct 2023 13:17:26 -0700 Subject: [PATCH 15/55] Fix command center border in hc themes (#195822) --- src/vs/workbench/common/theme.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 9d31beaefc2..d2ffebad5f9 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -911,7 +911,7 @@ export const COMMAND_CENTER_ACTIVEBACKGROUND = registerColor( ); // border: active and inactive. defaults to active background export const COMMAND_CENTER_BORDER = registerColor( - 'commandCenter.border', { dark: transparent(TITLE_BAR_ACTIVE_FOREGROUND, .20), hcDark: transparent(TITLE_BAR_ACTIVE_FOREGROUND, .60), light: transparent(TITLE_BAR_ACTIVE_FOREGROUND, .20), hcLight: transparent(TITLE_BAR_ACTIVE_FOREGROUND, .60) }, + 'commandCenter.border', { dark: transparent(TITLE_BAR_ACTIVE_FOREGROUND, .20), hcDark: contrastBorder, light: transparent(TITLE_BAR_ACTIVE_FOREGROUND, .20), hcLight: contrastBorder }, localize('commandCenter-border', "Border color of the command center"), false ); From 243e6cec5096b301259ca1f780e50b135f308d33 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 17 Oct 2023 13:18:28 -0700 Subject: [PATCH 16/55] Tweak text preformat foreground and background (#195821) --- build/lib/stylelint/vscode-known-variables.json | 9 +++++---- extensions/theme-defaults/themes/dark_modern.json | 2 ++ extensions/theme-defaults/themes/light_modern.json | 2 ++ src/vs/platform/theme/common/colorRegistry.ts | 5 +++-- src/vs/workbench/contrib/chat/browser/media/chat.css | 4 ++++ .../preferences/browser/media/settingsEditor2.css | 9 +++++++++ 6 files changed, 25 insertions(+), 6 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 6a99e81b5a4..70475768de0 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -681,6 +681,7 @@ "--vscode-textCodeBlock-background", "--vscode-textLink-activeForeground", "--vscode-textLink-foreground", + "--vscode-textPreformat-background", "--vscode-textPreformat-foreground", "--vscode-textSeparator-foreground", "--vscode-titleBar-activeBackground", @@ -695,6 +696,8 @@ "--vscode-tree-indentGuidesStroke", "--vscode-tree-tableColumnsBorder", "--vscode-tree-tableOddRowsBackground", + "--vscode-voiceRecording-background", + "--vscode-voiceRecording-dimmedBackground", "--vscode-walkThrough-embeddedEditorBackground", "--vscode-walkthrough-stepTitle-foreground", "--vscode-welcomePage-background", @@ -706,9 +709,7 @@ "--vscode-widget-border", "--vscode-widget-shadow", "--vscode-window-activeBorder", - "--vscode-window-inactiveBorder", - "--vscode-voiceRecording-background", - "--vscode-voiceRecording-dimmedBackground" + "--vscode-window-inactiveBorder" ], "others": [ "--background-dark", @@ -782,4 +783,4 @@ "--z-index-notebook-sticky-scroll", "--zoom-factor" ] -} +} \ No newline at end of file diff --git a/extensions/theme-defaults/themes/dark_modern.json b/extensions/theme-defaults/themes/dark_modern.json index e1055e8a4e2..b7d24260f83 100644 --- a/extensions/theme-defaults/themes/dark_modern.json +++ b/extensions/theme-defaults/themes/dark_modern.json @@ -113,6 +113,8 @@ "textCodeBlock.background": "#2B2B2B", "textLink.activeForeground": "#40A6FF", "textLink.foreground": "#40A6FF", + "textPreformat.foreground": "#D0D0D0", + "textPreformat.background": "#3C3C3C", "textSeparator.foreground": "#21262D", "titleBar.activeBackground": "#181818", "titleBar.activeForeground": "#CCCCCC", diff --git a/extensions/theme-defaults/themes/light_modern.json b/extensions/theme-defaults/themes/light_modern.json index d5bf68ba831..a89defee385 100644 --- a/extensions/theme-defaults/themes/light_modern.json +++ b/extensions/theme-defaults/themes/light_modern.json @@ -131,6 +131,8 @@ "textCodeBlock.background": "#F8F8F8", "textLink.activeForeground": "#005FB8", "textLink.foreground": "#005FB8", + "textPreformat.foreground": "#3B3B3B", + "textPreformat.background": "#0000001F", "textSeparator.foreground": "#21262D", "titleBar.activeBackground": "#F8F8F8", "titleBar.activeForeground": "#1E1E1E", diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 9f66d2cd1a5..6f83635a148 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -227,8 +227,9 @@ export const selectionBackground = registerColor('selection.background', { light export const textSeparatorForeground = registerColor('textSeparator.foreground', { light: '#0000002e', dark: '#ffffff2e', hcDark: Color.black, hcLight: '#292929' }, nls.localize('textSeparatorForeground', "Color for text separators.")); export const textLinkForeground = registerColor('textLink.foreground', { light: '#006AB1', dark: '#3794FF', hcDark: '#3794FF', hcLight: '#0F4A85' }, nls.localize('textLinkForeground', "Foreground color for links in text.")); export const textLinkActiveForeground = registerColor('textLink.activeForeground', { light: '#006AB1', dark: '#3794FF', hcDark: '#3794FF', hcLight: '#0F4A85' }, nls.localize('textLinkActiveForeground', "Foreground color for links in text when clicked on and on mouse hover.")); -export const textPreformatForeground = registerColor('textPreformat.foreground', { light: '#A31515', dark: '#D7BA7D', hcDark: '#D7BA7D', hcLight: '#292929' }, nls.localize('textPreformatForeground', "Foreground color for preformatted text segments.")); -export const textBlockQuoteBackground = registerColor('textBlockQuote.background', { light: '#7f7f7f1a', dark: '#7f7f7f1a', hcDark: null, hcLight: '#F2F2F2' }, nls.localize('textBlockQuoteBackground', "Background color for block quotes in text.")); +export const textPreformatForeground = registerColor('textPreformat.foreground', { light: '#A31515', dark: '#D7BA7D', hcDark: '#000000', hcLight: '#FFFFFF' }, nls.localize('textPreformatForeground', "Foreground color for preformatted text segments.")); +export const textPreformatBackground = registerColor('textPreformat.background', { light: '#0000001A', dark: '#FFFFFF1A', hcDark: '#FFFFFF', hcLight: '#09345f' }, nls.localize('textPreformatBackground', "Background color for preformatted text segments.")); +export const textBlockQuoteBackground = registerColor('textBlockQuote.background', { light: '#f2f2f2', dark: '#222222', hcDark: null, hcLight: '#F2F2F2' }, nls.localize('textBlockQuoteBackground', "Background color for block quotes in text.")); export const textBlockQuoteBorder = registerColor('textBlockQuote.border', { light: '#007acc80', dark: '#007acc80', hcDark: Color.white, hcLight: '#292929' }, nls.localize('textBlockQuoteBorder', "Border color for block quotes in text.")); export const textCodeBlockBackground = registerColor('textCodeBlock.background', { light: '#dcdcdc66', dark: '#0a0a0a66', hcDark: Color.black, hcLight: '#F2F2F2' }, nls.localize('textCodeBlockBackground', "Background color for code blocks in text.")); diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index cbab68d1e76..c36ac52db15 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -169,7 +169,11 @@ .interactive-item-container .monaco-tokenized-source, .interactive-item-container code { font-family: var(--monaco-monospace-font); + font-size: 12px; color: var(--vscode-textPreformat-foreground); + background-color: var(--vscode-textPreformat-background); + padding: 1px 3px; + border-radius: 4px; } .interactive-item-container.interactive-item-compact { diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index e5dd093c6bd..04c45a90360 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -531,7 +531,11 @@ line-height: 15px; /** For some reason, this is needed, otherwise will take up 20px height */ font-family: var(--monaco-monospace-font); + font-size: 12px; color: var(--vscode-textPreformat-foreground); + background-color: var(--vscode-textPreformat-background); + padding: 1px 3px; + border-radius: 4px; } .settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-markdown .monaco-tokenized-source { @@ -597,7 +601,12 @@ } .monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown code { + font-family: var(--monaco-monospace-font); + font-size: 12px; color: var(--vscode-textPreformat-foreground); + background-color: var(--vscode-textPreformat-background); + padding: 2px 5px; + border-radius: 4px; } .monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a, From 95d6813cacc2929483dc4e87886981e7cfccc5f2 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 17 Oct 2023 13:40:08 -0700 Subject: [PATCH 17/55] handle it all in shouldFocusTerminal --- .../terminalAccessibleBufferProvider.ts | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts index ac195f84cd6..fbe9c5ed18d 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts @@ -3,12 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Emitter } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IModelService } from 'vs/editor/common/services/model'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ResultKind } from 'vs/platform/keybinding/common/keybindingResolver'; import { TerminalCapability, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities'; import { ICurrentPartialCommand } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability'; import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; @@ -29,17 +31,18 @@ export class TerminalAccessibleBufferProvider extends DisposableStore implements private _bufferTracker: BufferContentTracker, customHelp: () => string, @IModelService _modelService: IModelService, - @IConfigurationService _configurationService: IConfigurationService, + @IConfigurationService configurationService: IConfigurationService, @IContextKeyService _contextKeyService: IContextKeyService, - @ITerminalService _terminalService: ITerminalService + @ITerminalService _terminalService: ITerminalService, + @IKeybindingService private readonly _keybindingService: IKeybindingService ) { super(); this.options.customHelp = customHelp; - this.options.position = _configurationService.getValue(TerminalSettingId.AccessibleViewPreserveCursorPosition) ? 'initial-bottom' : 'bottom'; + this.options.position = configurationService.getValue(TerminalSettingId.AccessibleViewPreserveCursorPosition) ? 'initial-bottom' : 'bottom'; this.add(this._instance.onDisposed(() => this._onDidRequestClearProvider.fire(AccessibleViewProviderId.Terminal))); - this.add(_configurationService.onDidChangeConfiguration(e => { + this.add(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(TerminalSettingId.AccessibleViewPreserveCursorPosition)) { - this.options.position = _configurationService.getValue(TerminalSettingId.AccessibleViewPreserveCursorPosition) ? 'initial-bottom' : 'bottom'; + this.options.position = configurationService.getValue(TerminalSettingId.AccessibleViewPreserveCursorPosition) ? 'initial-bottom' : 'bottom'; } })); this._focusedInstance = _terminalService.activeInstance; @@ -52,7 +55,7 @@ export class TerminalAccessibleBufferProvider extends DisposableStore implements } onKeyDown(e: IKeyboardEvent): void { - if (!this._instance.shouldProcessKeyEvent(e.browserEvent) || !isSingleLetterKey(e.browserEvent)) { + if (!isSingleLetterKey(e.browserEvent, this._keybindingService)) { return; } this._instance.focus(); @@ -124,6 +127,13 @@ export class TerminalAccessibleBufferProvider extends DisposableStore implements } export interface ICommandWithEditorLine { command: ITerminalCommand | ICurrentPartialCommand; lineNumber: number } -function isSingleLetterKey(event: KeyboardEvent): boolean { +function isSingleLetterKey(event: KeyboardEvent, keybindingService: IKeybindingService): boolean { + const standardKeyboardEvent = new StandardKeyboardEvent(event); + const resolveResult = keybindingService.softDispatch(standardKeyboardEvent, standardKeyboardEvent.target); + + const isValidChord = resolveResult.kind === ResultKind.MoreChordsNeeded; + if (keybindingService.inChordMode || isValidChord) { + return false; + } return event.key.length === 1 && !event.ctrlKey && !event.altKey && !event.metaKey && !event.shiftKey; } From 78dbe65f578840cf8475c36df256562863a913bd Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 17 Oct 2023 13:42:35 -0700 Subject: [PATCH 18/55] revert some changes, handle everything in shouldFocusTerminal --- .../contrib/terminal/browser/terminal.ts | 6 - .../terminal/browser/terminalInstance.ts | 166 +++++++++--------- .../terminalAccessibleBufferProvider.ts | 4 +- 3 files changed, 87 insertions(+), 89 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index d7bc90e1e8b..ea1b9088874 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -993,12 +993,6 @@ export interface ITerminalInstance { * Gets a terminal contribution by its ID. */ getContribution(id: string): T | null; - - /** - * Whether the event should be handled by xterm.js or the workbench. - * @param event the event to process - */ - shouldProcessKeyEvent(event: KeyboardEvent): boolean; } export const enum XtermTerminalConstants { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 339a9e4ce5c..a7c08bc63d8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -908,8 +908,92 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._setAriaLabel(xterm.raw, this._instanceId, this._title); - xterm.raw.attachCustomKeyEventHandler((event: KeyboardEvent): boolean => this.shouldProcessKeyEvent(event)); + xterm.raw.attachCustomKeyEventHandler((event: KeyboardEvent): boolean => { + // Disable all input if the terminal is exiting + if (this._isExiting) { + return false; + } + const standardKeyboardEvent = new StandardKeyboardEvent(event); + const resolveResult = this._keybindingService.softDispatch(standardKeyboardEvent, standardKeyboardEvent.target); + + // Respect chords if the allowChords setting is set and it's not Escape. Escape is + // handled specially for Zen Mode's Escape, Escape chord, plus it's important in + // terminals generally + const isValidChord = resolveResult.kind === ResultKind.MoreChordsNeeded && this._configHelper.config.allowChords && event.key !== 'Escape'; + if (this._keybindingService.inChordMode || isValidChord) { + event.preventDefault(); + return false; + } + + const SHOW_TERMINAL_CONFIG_PROMPT_KEY = 'terminal.integrated.showTerminalConfigPrompt'; + const EXCLUDED_KEYS = ['RightArrow', 'LeftArrow', 'UpArrow', 'DownArrow', 'Space', 'Meta', 'Control', 'Shift', 'Alt', '', 'Delete', 'Backspace', 'Tab']; + + // only keep track of input if prompt hasn't already been shown + if (this._storageService.getBoolean(SHOW_TERMINAL_CONFIG_PROMPT_KEY, StorageScope.APPLICATION, true) && + !EXCLUDED_KEYS.includes(event.key) && + !event.ctrlKey && + !event.shiftKey && + !event.altKey) { + this._hasHadInput = true; + } + + // for keyboard events that resolve to commands described + // within commandsToSkipShell, either alert or skip processing by xterm.js + if (resolveResult.kind === ResultKind.KbFound && resolveResult.commandId && this._skipTerminalCommands.some(k => k === resolveResult.commandId) && !this._configHelper.config.sendKeybindingsToShell) { + // don't alert when terminal is opened or closed + if (this._storageService.getBoolean(SHOW_TERMINAL_CONFIG_PROMPT_KEY, StorageScope.APPLICATION, true) && + this._hasHadInput && + !TERMINAL_CREATION_COMMANDS.includes(resolveResult.commandId)) { + this._notificationService.prompt( + Severity.Info, + nls.localize('keybindingHandling', "Some keybindings don't go to the terminal by default and are handled by {0} instead.", this._productService.nameLong), + [ + { + label: nls.localize('configureTerminalSettings', "Configure Terminal Settings"), + run: () => { + this._preferencesService.openSettings({ jsonEditor: false, query: `@id:${TerminalSettingId.CommandsToSkipShell},${TerminalSettingId.SendKeybindingsToShell},${TerminalSettingId.AllowChords}` }); + } + } as IPromptChoice + ] + ); + this._storageService.store(SHOW_TERMINAL_CONFIG_PROMPT_KEY, false, StorageScope.APPLICATION, StorageTarget.USER); + } + event.preventDefault(); + return false; + } + + // Skip processing by xterm.js of keyboard events that match menu bar mnemonics + if (this._configHelper.config.allowMnemonics && !isMacintosh && event.altKey) { + return false; + } + + // If tab focus mode is on, tab is not passed to the terminal + if (TabFocus.getTabFocusMode() && event.key === 'Tab') { + return false; + } + + // Prevent default when shift+tab is being sent to the terminal to avoid it bubbling up + // and changing focus https://github.com/microsoft/vscode/issues/188329 + if (event.key === 'Tab' && event.shiftKey) { + event.preventDefault(); + return true; + } + + // Always have alt+F4 skip the terminal on Windows and allow it to be handled by the + // system + if (isWindows && event.altKey && event.key === 'F4' && !event.ctrlKey) { + return false; + } + + // Fallback to force ctrl+v to paste on browsers that do not support + // navigator.clipboard.readText + if (!BrowserFeatures.clipboard.readText && event.key === 'v' && event.ctrlKey) { + return false; + } + + return true; + }); this._register(dom.addDisposableListener(xterm.raw.element, 'mousedown', () => { // We need to listen to the mouseup event on the document since the user may release // the mouse button anywhere outside of _xterm.element. @@ -969,86 +1053,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } } - shouldProcessKeyEvent(event: KeyboardEvent): boolean { - // Disable all input if the terminal is exiting - if (this._isExiting) { - return false; - } - - const standardKeyboardEvent = new StandardKeyboardEvent(event); - const resolveResult = this._keybindingService.softDispatch(standardKeyboardEvent, standardKeyboardEvent.target); - - // Respect chords if the allowChords setting is set and it's not Escape. Escape is - // handled specially for Zen Mode's Escape, Escape chord, plus it's important in - // terminals generally - const isValidChord = resolveResult.kind === ResultKind.MoreChordsNeeded && this._configHelper.config.allowChords && event.key !== 'Escape'; - if (this._keybindingService.inChordMode || isValidChord) { - event.preventDefault(); - return false; - } - - const SHOW_TERMINAL_CONFIG_PROMPT_KEY = 'terminal.integrated.showTerminalConfigPrompt'; - const EXCLUDED_KEYS = ['RightArrow', 'LeftArrow', 'UpArrow', 'DownArrow', 'Space', 'Meta', 'Control', 'Shift', 'Alt', '', 'Delete', 'Backspace', 'Tab']; - - // only keep track of input if prompt hasn't already been shown - if (this._storageService.getBoolean(SHOW_TERMINAL_CONFIG_PROMPT_KEY, StorageScope.APPLICATION, true) && - !EXCLUDED_KEYS.includes(event.key) && - !event.ctrlKey && - !event.shiftKey && - !event.altKey) { - this._hasHadInput = true; - } - - // for keyboard events that resolve to commands described - // within commandsToSkipShell, either alert or skip processing by xterm.js - if (resolveResult.kind === ResultKind.KbFound && resolveResult.commandId && this._skipTerminalCommands.some(k => k === resolveResult.commandId) && !this._configHelper.config.sendKeybindingsToShell) { - // don't alert when terminal is opened or closed - if (this._storageService.getBoolean(SHOW_TERMINAL_CONFIG_PROMPT_KEY, StorageScope.APPLICATION, true) && - this._hasHadInput && - !TERMINAL_CREATION_COMMANDS.includes(resolveResult.commandId)) { - this._notificationService.prompt( - Severity.Info, - nls.localize('keybindingHandling', "Some keybindings don't go to the terminal by default and are handled by {0} instead.", this._productService.nameLong), - [ - { - label: nls.localize('configureTerminalSettings', "Configure Terminal Settings"), - run: () => { - this._preferencesService.openSettings({ jsonEditor: false, query: `@id:${TerminalSettingId.CommandsToSkipShell},${TerminalSettingId.SendKeybindingsToShell},${TerminalSettingId.AllowChords}` }); - } - } as IPromptChoice - ] - ); - this._storageService.store(SHOW_TERMINAL_CONFIG_PROMPT_KEY, false, StorageScope.APPLICATION, StorageTarget.USER); - } - event.preventDefault(); - return false; - } - - // Skip processing by xterm.js of keyboard events that match menu bar mnemonics - if (this._configHelper.config.allowMnemonics && !isMacintosh && event.altKey) { - return false; - } - - // If tab focus mode is on, tab is not passed to the terminal - if (TabFocus.getTabFocusMode() && event.key === 'Tab') { - return false; - } - - // Always have alt+F4 skip the terminal on Windows and allow it to be handled by the - // system - if (isWindows && event.altKey && event.key === 'F4' && !event.ctrlKey) { - return false; - } - - // Fallback to force ctrl+v to paste on browsers that do not support - // navigator.clipboard.readText - if (!BrowserFeatures.clipboard.readText && event.key === 'v' && event.ctrlKey) { - return false; - } - - return true; - } - resetFocusContextKey(): void { this._terminalFocusContextKey.reset(); this._terminalShellIntegrationEnabledContextKey.reset(); diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts index fbe9c5ed18d..0b889427bc8 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts @@ -55,7 +55,7 @@ export class TerminalAccessibleBufferProvider extends DisposableStore implements } onKeyDown(e: IKeyboardEvent): void { - if (!isSingleLetterKey(e.browserEvent, this._keybindingService)) { + if (!shouldFocusTerminal(e.browserEvent, this._keybindingService)) { return; } this._instance.focus(); @@ -127,7 +127,7 @@ export class TerminalAccessibleBufferProvider extends DisposableStore implements } export interface ICommandWithEditorLine { command: ITerminalCommand | ICurrentPartialCommand; lineNumber: number } -function isSingleLetterKey(event: KeyboardEvent, keybindingService: IKeybindingService): boolean { +function shouldFocusTerminal(event: KeyboardEvent, keybindingService: IKeybindingService): boolean { const standardKeyboardEvent = new StandardKeyboardEvent(event); const resolveResult = keybindingService.softDispatch(standardKeyboardEvent, standardKeyboardEvent.target); From d3264a27169bc9c8eacaf7904fabe651b7502a50 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 17 Oct 2023 13:44:21 -0700 Subject: [PATCH 19/55] rm something --- .../accessibility/browser/terminalAccessibleBufferProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts index 0b889427bc8..0540afae829 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts @@ -27,7 +27,7 @@ export class TerminalAccessibleBufferProvider extends DisposableStore implements readonly onDidRequestClearLastProvider = this._onDidRequestClearProvider.event; private _focusedInstance: ITerminalInstance | undefined; constructor( - private readonly _instance: Pick, + private readonly _instance: Pick, private _bufferTracker: BufferContentTracker, customHelp: () => string, @IModelService _modelService: IModelService, From a28793a1af9db9afba0886d988c82ba346ff2af8 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 17 Oct 2023 14:48:18 -0700 Subject: [PATCH 20/55] Update monospace font size --- src/vs/workbench/contrib/chat/browser/media/chat.css | 2 +- .../contrib/preferences/browser/media/settingsEditor2.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index c36ac52db15..5f5d22d3842 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -169,7 +169,7 @@ .interactive-item-container .monaco-tokenized-source, .interactive-item-container code { font-family: var(--monaco-monospace-font); - font-size: 12px; + font-size: 11px; color: var(--vscode-textPreformat-foreground); background-color: var(--vscode-textPreformat-background); padding: 1px 3px; diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 04c45a90360..a2bfc42d085 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -531,7 +531,7 @@ line-height: 15px; /** For some reason, this is needed, otherwise will take up 20px height */ font-family: var(--monaco-monospace-font); - font-size: 12px; + font-size: 11px; color: var(--vscode-textPreformat-foreground); background-color: var(--vscode-textPreformat-background); padding: 1px 3px; From d279a8206caa92d7c5e55ecba5e22e8e83667b46 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Tue, 17 Oct 2023 14:55:54 -0700 Subject: [PATCH 21/55] feat: support welcome view in chat pane (#195830) --- src/vs/workbench/contrib/chat/browser/chatViewPane.ts | 4 ++++ src/vs/workbench/contrib/chat/common/chatService.ts | 1 + src/vs/workbench/contrib/chat/common/chatServiceImpl.ts | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts index ba56b069fcc..9be6f027aba 100644 --- a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts @@ -82,6 +82,10 @@ export class ChatViewPane extends ViewPane implements IChatViewPane { this.viewState.sessionId = model.sessionId; } + override shouldShowWelcome(): boolean { + return !this.chatService.hasProviders(); + } + protected override renderBody(parent: HTMLElement): void { try { super.renderBody(parent); diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index bb52eeaceef..38d48e94815 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -265,6 +265,7 @@ export interface IChatService { onDidSubmitSlashCommand: Event<{ slashCommand: string; sessionId: string }>; registerProvider(provider: IChatProvider): IDisposable; + hasProviders(): boolean; getProviderInfos(): IChatProviderInfo[]; startSession(providerId: string, token: CancellationToken): ChatModel | undefined; getSession(sessionId: string): IChatModel | undefined; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 0e7382192dd..cbeb0dad572 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -760,6 +760,10 @@ export class ChatService extends Disposable implements IChatService { }); } + hasProviders(): boolean { + return this._providers.size > 0; + } + getProviderInfos(): IChatProviderInfo[] { return Array.from(this._providers.values()).map(provider => { return { From 97afb0a422a8f347438243df943d7564d4cc9adb Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 17 Oct 2023 15:01:32 -0700 Subject: [PATCH 22/55] Match pill dimensions to new styling --- src/vs/workbench/contrib/chat/browser/media/chat.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index c36ac52db15..329ef7136ed 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -427,9 +427,9 @@ .interactive-item-container .chat-resource-widget { background-color: var(--vscode-chat-slashCommandBackground); color: var(--vscode-chat-slashCommandForeground); - border-radius: 3px; + border-radius: 4px; white-space: nowrap; - padding: 1px; + padding: 1px 3px; } .interactive-session .chat-used-context.chat-used-context-collapsed .chat-used-context-list { From 65cc0f6d1ba5112df7213d868b3be86f3006d952 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 17 Oct 2023 15:35:12 -0700 Subject: [PATCH 23/55] Use new styling in inline widget --- .../workbench/contrib/inlineChat/browser/inlineChat.css | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css index 92b75867291..9ffe8efc5bb 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css @@ -164,6 +164,15 @@ margin: unset; } +.monaco-editor .inline-chat .markdownMessage .message code { + font-family: var(--monaco-monospace-font); + font-size: 12px; + color: var(--vscode-textPreformat-foreground); + background-color: var(--vscode-textPreformat-background); + padding: 1px 3px; + border-radius: 4px; +} + .monaco-editor .inline-chat .markdownMessage .message .interactive-result-code-block { margin: 16px 0; } From 880cb517e1e1d8085cd87182c3e3c31c7f81845a Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Tue, 17 Oct 2023 15:55:55 -0700 Subject: [PATCH 24/55] Add more SBOMs (#195736) --- .../alpine/product-build-alpine.yml | 13 +++++++++++ .../cli/cli-compile-and-publish.yml | 23 +++++++++++++++++++ .../darwin/product-build-darwin.yml | 5 ++-- .../linux/product-build-linux.yml | 1 + .../linux/snap-build-linux.yml | 13 +++++++++++ .../azure-pipelines/web/product-build-web.yml | 11 +++++++++ .../win32/product-build-win32.yml | 5 ++-- 7 files changed, 67 insertions(+), 4 deletions(-) diff --git a/build/azure-pipelines/alpine/product-build-alpine.yml b/build/azure-pipelines/alpine/product-build-alpine.yml index 4e318644d08..77f97561426 100644 --- a/build/azure-pipelines/alpine/product-build-alpine.yml +++ b/build/azure-pipelines/alpine/product-build-alpine.yml @@ -138,6 +138,19 @@ steps: condition: and(succeededOrFailed(), notIn(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')) displayName: Generate artifact prefix + - script: mkdir $(agent.builddirectory)/vscode-alpine-$(VSCODE_ARCH) + displayName: Make folder for SBOM + + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM + inputs: + BuildDropPath: $(agent.builddirectory)/vscode-alpine-$(VSCODE_ARCH) + PackageName: Visual Studio Code Server + + - publish: $(agent.builddirectory)/vscode-alpine-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM + artifact: $(ARTIFACT_PREFIX)sbom_vscode_alpine_$(VSCODE_ARCH) + - publish: $(SERVER_PATH) artifact: $(ARTIFACT_PREFIX)vscode_server_alpine_$(VSCODE_ARCH)_archive-unsigned displayName: Publish server archive diff --git a/build/azure-pipelines/cli/cli-compile-and-publish.yml b/build/azure-pipelines/cli/cli-compile-and-publish.yml index af9960d7f5b..05ad5538d87 100644 --- a/build/azure-pipelines/cli/cli-compile-and-publish.yml +++ b/build/azure-pipelines/cli/cli-compile-and-publish.yml @@ -106,3 +106,26 @@ steps: - publish: $(Build.ArtifactStagingDirectory)/${{ parameters.VSCODE_CLI_ARTIFACT }}.tar.gz artifact: ${{ parameters.VSCODE_CLI_ARTIFACT }} displayName: Publish ${{ parameters.VSCODE_CLI_ARTIFACT }} artifact + + # Make a folder for the SBOM for the specific artifact + - ${{ if contains(parameters.VSCODE_CLI_TARGET, '-windows-') }}: + - powershell: mkdir $(Build.ArtifactStagingDirectory)/sbom_${{ parameters.VSCODE_CLI_ARTIFACT }} + displayName: Make folder for SBOM (Windows) + + - ${{ else }}: + - script: mkdir $(Build.ArtifactStagingDirectory)/sbom_${{ parameters.VSCODE_CLI_ARTIFACT }} + displayName: Make folder for SBOM (non-Windows) + + # The if cases above are for different OSes, + # but we're still in the branch where the cli is being published in general. + # Generate and publish an SBOM. + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM + inputs: + BuildComponentPath: $(Build.SourcesDirectory)/cli + BuildDropPath: $(Build.ArtifactStagingDirectory)/sbom_${{ parameters.VSCODE_CLI_ARTIFACT }} + PackageName: Visual Studio Code CLI + + - publish: $(Build.ArtifactStagingDirectory)/sbom_${{ parameters.VSCODE_CLI_ARTIFACT }}/_manifest + displayName: Publish SBOM + artifact: sbom_${{ parameters.VSCODE_CLI_ARTIFACT }} diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 35b207cb7f5..4a4c587c7aa 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -219,16 +219,17 @@ steps: - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (server) inputs: + BuildComponentPath: $(Build.SourcesDirectory)/remote BuildDropPath: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) PackageName: Visual Studio Code Server - publish: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (client) - artifact: $(ARTIFACT_PREFIX)sbom_client_darwin_$(VSCODE_ARCH)_sbom + artifact: $(ARTIFACT_PREFIX)sbom_vscode_client_darwin_$(VSCODE_ARCH) - publish: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (server) - artifact: $(ARTIFACT_PREFIX)sbom_server_darwin_$(VSCODE_ARCH)_sbom + artifact: $(ARTIFACT_PREFIX)sbom_vscode_server_darwin_$(VSCODE_ARCH) - publish: $(CLIENT_PATH) artifact: $(ARTIFACT_PREFIX)unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 3923d7d105f..b2c993b4a14 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -331,6 +331,7 @@ steps: - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (server) inputs: + BuildComponentPath: $(Build.SourcesDirectory)/remote BuildDropPath: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH) PackageName: Visual Studio Code Server diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index c74783da146..7f00653a4bd 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -50,6 +50,19 @@ steps: echo "##vso[task.setvariable variable=SNAP_PATH]$SNAP_PATH" displayName: Prepare for publish + - script: mkdir -p $(agent.builddirectory)/vscode-snap-linux-$(VSCODE_ARCH) + displayName: Make folder for SBOM + + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM + inputs: + BuildDropPath: $(agent.builddirectory)/vscode-snap-linux-$(VSCODE_ARCH) + PackageName: Visual Studio Code Snap + + - publish: $(agent.builddirectory)/vscode-snap-linux-$(VSCODE_ARCH)/_manifest + displayName: Publish SBOM + artifact: $(ARTIFACT_PREFIX)sbom_vscode_client_linux_snap_$(VSCODE_ARCH) + - publish: $(SNAP_PATH) artifact: vscode_client_linux_$(VSCODE_ARCH)_snap displayName: Publish snap package diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index ecdfe91ed0b..74ebc3df8f5 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -107,6 +107,7 @@ steps: displayName: Build - task: AzureCLI@2 + displayName: Fetch secrets from Azure inputs: azureSubscription: "vscode-builds-subscription" scriptType: pscore @@ -151,6 +152,16 @@ steps: condition: and(succeededOrFailed(), notIn(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')) displayName: Generate artifact prefix + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: Generate SBOM + inputs: + BuildDropPath: $(agent.builddirectory)/vscode-web + PackageName: Visual Studio Code Web + + - publish: $(agent.builddirectory)/vscode-web/_manifest + displayName: Publish SBOM (client) + artifact: $(ARTIFACT_PREFIX)sbom_vscode_web + - publish: $(WEB_PATH) artifact: $(ARTIFACT_PREFIX)vscode_web_linux_standalone_archive-unsigned condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], '')) diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index ebfbc701146..ab0813b9f76 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -322,17 +322,18 @@ steps: - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (server) inputs: + BuildComponentPath: $(Build.SourcesDirectory)/remote BuildDropPath: $(agent.builddirectory)/vscode-server-win32-$(VSCODE_ARCH) PackageName: Visual Studio Code Server condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - publish: $(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (client) - artifact: $(ARTIFACT_PREFIX)sbom_client_win32_$(VSCODE_ARCH) + artifact: $(ARTIFACT_PREFIX)sbom_vscode_client_win32_$(VSCODE_ARCH) - publish: $(agent.builddirectory)/vscode-server-win32-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (server) - artifact: $(ARTIFACT_PREFIX)sbom_server_win32_$(VSCODE_ARCH) + artifact: $(ARTIFACT_PREFIX)sbom_vscode_server_win32_$(VSCODE_ARCH) condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - publish: $(CLIENT_PATH) From 5d896cae2feb876b5430bb85ad54a0e405fbc162 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 17 Oct 2023 16:01:52 -0700 Subject: [PATCH 25/55] Revert font size change --- src/vs/workbench/contrib/chat/browser/media/chat.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 18a2af99b69..329ef7136ed 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -169,7 +169,7 @@ .interactive-item-container .monaco-tokenized-source, .interactive-item-container code { font-family: var(--monaco-monospace-font); - font-size: 11px; + font-size: 12px; color: var(--vscode-textPreformat-foreground); background-color: var(--vscode-textPreformat-background); padding: 1px 3px; From 3d9e1b78dbdc40fb858ae6988d108d1f9814f879 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Tue, 17 Oct 2023 16:02:44 -0700 Subject: [PATCH 26/55] Warn when enum-related fields set without enum (#195838) * Warn when enum-related fields set without enum * polish --- .../services/preferences/common/preferencesModels.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 512cca25488..89022b946b7 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -687,6 +687,10 @@ export class DefaultSettings extends Disposable { } } + if (!enumToUse && (prop.enumItemLabels || enumDescriptions || enumDescriptionsAreMarkdown)) { + console.error(`The setting ${key} has enum-related fields, but doesn't have an enum field. This setting may render improperly in the Settings editor.`); + } + result.push({ key, value, @@ -706,6 +710,7 @@ export class DefaultSettings extends Disposable { enum: enumToUse, enumDescriptions: enumDescriptions, enumDescriptionsAreMarkdown: enumDescriptionsAreMarkdown, + enumItemLabels: prop.enumItemLabels, uniqueItems: prop.uniqueItems, tags: prop.tags, disallowSyncIgnore: prop.disallowSyncIgnore, @@ -714,7 +719,6 @@ export class DefaultSettings extends Disposable { deprecationMessage: prop.markdownDeprecationMessage || prop.deprecationMessage, deprecationMessageIsMarkdown: !!prop.markdownDeprecationMessage, validator: createValidator(prop), - enumItemLabels: prop.enumItemLabels, allKeysAreBoolean, editPresentation: prop.editPresentation, order: prop.order, @@ -1058,7 +1062,7 @@ class SettingsContentBuilder { setting.descriptionRanges.push({ startLineNumber: this.lineCountWithOffset, startColumn: this.lastLine.indexOf(line) + 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length }); } - if (setting.enumDescriptions && setting.enumDescriptions.some(desc => !!desc)) { + if (setting.enum && setting.enumDescriptions?.some(desc => !!desc)) { setting.enumDescriptions.forEach((desc, i) => { const displayEnum = escapeInvisibleChars(String(setting.enum![i])); const line = desc ? From 3d01829e872d95605e8f93b225fce60daa867a91 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 17 Oct 2023 16:06:32 -0700 Subject: [PATCH 27/55] cli: increase socket search timeout (#195837) Fixes #195823 --- cli/src/tunnels/code_server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/tunnels/code_server.rs b/cli/src/tunnels/code_server.rs index e17e32f8840..06e42681799 100644 --- a/cli/src/tunnels/code_server.rs +++ b/cli/src/tunnels/code_server.rs @@ -510,7 +510,7 @@ impl<'a> ServerBuilder<'a> { let (mut origin, listen_rx) = monitor_server::(child, Some(log_file), plog, false); - let socket = match timeout(Duration::from_secs(8), listen_rx).await { + let socket = match timeout(Duration::from_secs(30), listen_rx).await { Err(e) => { origin.kill().await; Err(wrap(e, "timed out looking for socket")) From c1b146e35a54bc84d614b8eca91098ca4461093e Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 17 Oct 2023 16:27:14 -0700 Subject: [PATCH 28/55] Restore chat code block background color --- src/vs/workbench/contrib/chat/browser/media/chat.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 329ef7136ed..f835ffeffd2 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -264,7 +264,7 @@ .interactive-response .interactive-result-code-block .interactive-result-editor .monaco-editor, .interactive-response .interactive-result-code-block .interactive-result-editor .monaco-editor .margin, .interactive-response .interactive-result-code-block .interactive-result-editor .monaco-editor .monaco-editor-background { - background-color: var(--vscode-interactive-result-editor-background-color); + background-color: var(--vscode-interactive-result-editor-background-color) !important; } .interactive-item-compact .interactive-result-code-block { From e5b7340d189d6814b139401009b2f8ab3cda0ea1 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Tue, 17 Oct 2023 16:48:12 -0700 Subject: [PATCH 29/55] fix: rerender chat pane when provider is added (#195845) --- src/vs/workbench/contrib/chat/browser/chatViewPane.ts | 3 +++ src/vs/workbench/contrib/chat/common/chatService.ts | 1 + src/vs/workbench/contrib/chat/common/chatServiceImpl.ts | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts index 9be6f027aba..d5ced73f860 100644 --- a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts @@ -66,6 +66,9 @@ export class ChatViewPane extends ViewPane implements IChatViewPane { // View state for the ViewPane is currently global per-provider basically, but some other strictly per-model state will require a separate memento. this.memento = new Memento('interactive-session-view-' + this.chatViewOptions.providerId, this.storageService); this.viewState = this.memento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE) as IViewPaneState; + this._register(this.chatService.onDidRegisterProvider(({ providerId }) => { + if (providerId === this.chatViewOptions.providerId) { this.updateModel(); } + })); } private updateModel(model?: IChatModel | undefined): void { diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 38d48e94815..a065dc06655 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -264,6 +264,7 @@ export interface IChatService { transferredSessionData: IChatTransferredSessionData | undefined; onDidSubmitSlashCommand: Event<{ slashCommand: string; sessionId: string }>; + onDidRegisterProvider: Event<{ providerId: string }>; registerProvider(provider: IChatProvider): IDisposable; hasProviders(): boolean; getProviderInfos(): IChatProviderInfo[]; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index cbeb0dad572..848a64ded4b 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -150,6 +150,9 @@ export class ChatService extends Disposable implements IChatService { private readonly _onDidDisposeSession = this._register(new Emitter<{ sessionId: string }>()); public readonly onDidDisposeSession = this._onDidDisposeSession.event; + private readonly _onDidRegisterProvider = this._register(new Emitter<{ providerId: string }>()); + public readonly onDidRegisterProvider = this._onDidRegisterProvider.event; + constructor( @IStorageService private readonly storageService: IStorageService, @ILogService private readonly logService: ILogService, @@ -743,6 +746,7 @@ export class ChatService extends Disposable implements IChatService { this._providers.set(provider.id, provider); this._hasProvider.set(true); + this._onDidRegisterProvider.fire({ providerId: provider.id }); Array.from(this._sessionModels.values()) .filter(model => model.providerId === provider.id) From 070fd9bbae7e52ea57c1e23748bb7774a58ace8a Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 17 Oct 2023 17:18:05 -0700 Subject: [PATCH 30/55] Filtered blur shouldn't apply to info message (#195848) --- src/vs/workbench/contrib/chat/browser/media/chat.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index f835ffeffd2..1ed1ca11c89 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -362,7 +362,7 @@ color: var(--vscode-icon-foreground) !important; } -.interactive-item-container.filtered-response .value .rendered-markdown { +.interactive-item-container.filtered-response .value > .rendered-markdown { -webkit-mask-image: linear-gradient(rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0.05)); mask-image: linear-gradient(rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0.05)); } From 672033e15161cc7ec32af9c2a1cbf467b04fb887 Mon Sep 17 00:00:00 2001 From: Yuto Liyosa <75252297+MrYuto@users.noreply.github.com> Date: Wed, 18 Oct 2023 03:53:59 +0330 Subject: [PATCH 31/55] Resolve absolute file target links in tsconfig (#195514) (#195759) fix #195514 again --- .../src/languageFeatures/tsconfig.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/tsconfig.ts b/extensions/typescript-language-features/src/languageFeatures/tsconfig.ts index 34ed6828145..398c8c10ee2 100644 --- a/extensions/typescript-language-features/src/languageFeatures/tsconfig.ts +++ b/extensions/typescript-language-features/src/languageFeatures/tsconfig.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as jsonc from 'jsonc-parser'; -import { posix } from 'path'; +import { isAbsolute, posix } from 'path'; import * as vscode from 'vscode'; import { Utils } from 'vscode-uri'; import { coalesce } from '../utils/arrays'; @@ -95,6 +95,10 @@ class TsconfigLinkProvider implements vscode.DocumentLinkProvider { } private getFileTarget(document: vscode.TextDocument, node: jsonc.Node): vscode.Uri { + if (isAbsolute(node.value)) { + return vscode.Uri.file(node.value); + } + return vscode.Uri.joinPath(Utils.dirname(document.uri), node.value); } From 5c148ef006c85cd5dc3fb1574ea8fd9cceaa488d Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 17 Oct 2023 17:24:59 -0700 Subject: [PATCH 32/55] More references list styling updates (#195847) --- .../contrib/chat/browser/chatListRenderer.ts | 2 +- .../contrib/chat/browser/media/chat.css | 20 ++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 9427302ee8d..b42d4685e25 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -620,7 +620,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 1ed1ca11c89..04abd853552 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -17,7 +17,7 @@ padding: 16px 20px 16px 20px; display: flex; flex-direction: column; - gap: 6px; + gap: 8px; color: var(--vscode-interactive-session-foreground); cursor: default; @@ -436,10 +436,17 @@ display: none; } +.interactive-session .chat-used-context { + display: flex; + flex-direction: column; + gap: 6px; +} + .interactive-session .chat-used-context-list { border: 1px solid var(--vscode-chat-requestBorder); border-radius: 4px; padding: 4px; + margin-bottom: 8px; } .interactive-session .chat-used-context-list .monaco-list .monaco-list-row { @@ -464,13 +471,16 @@ padding: 0; text-align: initial; justify-content: initial; - margin-bottom: 6px; } -.interactive-session .chat-used-context-label .monaco-text-button { - outline-offset: unset !important; +.interactive-session .chat-used-context-label .monaco-text-button:focus { + outline: none; +} + +.interactive-session .chat-used-context-label .monaco-text-button:focus-visible { + outline: 1px solid var(--vscode-focusBorder); } .interactive-session .chat-used-context .chat-used-context-label .monaco-button .codicon { - margin: 0 2px 0 0; + margin: 0 0 0 4px; } From 7a9b57fd528979141d50d3d8827b7da815fbca91 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 17 Oct 2023 17:33:28 -0700 Subject: [PATCH 33/55] Look up correct result for chat user action (#195849) * Look up correct result for chat user action * Only reply followups are sent in the user action event --- .../api/browser/mainThreadChatAgents2.ts | 14 +++++------- .../workbench/api/common/extHost.protocol.ts | 4 ++-- .../api/common/extHostChatAgents2.ts | 22 ++++++++++++++----- .../browser/actions/chatCodeblockActions.ts | 2 ++ .../contrib/chat/browser/chatListRenderer.ts | 1 + .../contrib/chat/browser/chatWidget.ts | 9 +++++++- .../contrib/chat/common/chatModel.ts | 10 +++++---- .../contrib/chat/common/chatService.ts | 3 ++- .../contrib/chat/common/chatServiceImpl.ts | 3 +-- .../contrib/chat/common/chatViewModel.ts | 6 +++++ ...scode.proposed.interactiveUserActions.d.ts | 2 +- 11 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts index a80992df95d..07a10a92e9c 100644 --- a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts +++ b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts @@ -8,7 +8,6 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { Disposable, DisposableMap } from 'vs/base/common/lifecycle'; import { revive } from 'vs/base/common/marshalling'; import { UriComponents } from 'vs/base/common/uri'; -import { generateUuid } from 'vs/base/common/uuid'; import { ExtHostChatAgentsShape2, ExtHostContext, IChatResponseProgressDto, IChatResponseProgressFileTreeData, IExtensionChatAgentMetadata, ILocationDto, MainContext, MainThreadChatAgentsShape2 } from 'vs/workbench/api/common/extHost.protocol'; import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { isCompleteInteractiveProgressTreeData } from 'vs/workbench/contrib/chat/common/chatModel'; @@ -44,13 +43,13 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA this._proxy.$releaseSession(e.sessionId); })); this._register(this._chatService.onDidPerformUserAction(e => { - if (e.agentId) { + if (typeof e.agentId === 'string') { for (const [handle, agent] of this._agents) { if (agent.name === e.agentId) { if (e.action.kind === 'vote') { - this._proxy.$acceptFeedback(handle, e.sessionId, e.action.direction); + this._proxy.$acceptFeedback(handle, e.sessionId, e.requestId, e.action.direction); } else { - this._proxy.$acceptAction(handle, e.sessionId, e); + this._proxy.$acceptAction(handle, e.sessionId, e.requestId, e); } break; } @@ -68,12 +67,11 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA id: name, metadata: revive(metadata), invoke: async (request, progress, history, token) => { - const requestId = generateUuid(); - this._pendingProgress.set(requestId, progress); + this._pendingProgress.set(request.requestId, progress); try { - return await this._proxy.$invokeAgent(handle, request.sessionId, requestId, request, { history }, token) ?? {}; + return await this._proxy.$invokeAgent(handle, request.sessionId, request.requestId, request, { history }, token) ?? {}; } finally { - this._pendingProgress.delete(requestId); + this._pendingProgress.delete(request.requestId); } }, provideFollowups: async (sessionId, token): Promise => { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 969de0075e2..674cb4bb2a4 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1185,8 +1185,8 @@ export interface ExtHostChatAgentsShape2 { $invokeAgent(handle: number, sessionId: string, requestId: string, request: IChatAgentRequest, context: { history: IChatMessage[] }, token: CancellationToken): Promise; $provideSlashCommands(handle: number, token: CancellationToken): Promise; $provideFollowups(handle: number, sessionId: string, token: CancellationToken): Promise; - $acceptFeedback(handle: number, sessionId: string, vote: InteractiveSessionVoteDirection): void; - $acceptAction(handle: number, sessionId: string, action: IChatUserActionEvent): void; + $acceptFeedback(handle: number, sessionId: string, requestId: string, vote: InteractiveSessionVoteDirection): void; + $acceptAction(handle: number, sessionId: string, requestId: string, action: IChatUserActionEvent): void; $releaseSession(sessionId: string): void; } diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index c202d7b1031..4044bb79786 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -30,6 +30,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { private readonly _proxy: MainThreadChatAgentsShape2; private readonly _previousResultMap: Map = new Map(); + private readonly _resultsBySessionAndRequestId: Map> = new Map(); constructor( mainContext: IMainContext, @@ -49,6 +50,10 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { } async $invokeAgent(handle: number, sessionId: string, requestId: string, request: IChatAgentRequest, context: { history: IChatMessage[] }, token: CancellationToken): Promise { + // Clear the previous result so that $acceptFeedback or $acceptAction during a request will be ignored. + // We may want to support sending those during a request. + this._previousResultMap.delete(sessionId); + const agent = this._agents.get(handle); if (!agent) { throw new Error(`[CHAT](${handle}) CANNOT invoke agent because the agent is not registered`); @@ -100,6 +105,13 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { return await raceCancellation(Promise.resolve(task).then((result) => { if (result) { this._previousResultMap.set(sessionId, result); + let sessionResults = this._resultsBySessionAndRequestId.get(sessionId); + if (!sessionResults) { + sessionResults = new Map(); + this._resultsBySessionAndRequestId.set(sessionId, sessionResults); + } + sessionResults.set(requestId, result); + return { errorDetails: result.errorDetails }; // TODO timings here } else { this._previousResultMap.delete(sessionId); @@ -124,6 +136,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { $releaseSession(sessionId: string): void { this._previousResultMap.delete(sessionId); + this._resultsBySessionAndRequestId.delete(sessionId); } async $provideSlashCommands(handle: number, token: CancellationToken): Promise { @@ -149,12 +162,12 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { return agent.provideFollowups(result, token); } - $acceptFeedback(handle: number, sessionId: string, vote: InteractiveSessionVoteDirection): void { + $acceptFeedback(handle: number, sessionId: string, requestId: string, vote: InteractiveSessionVoteDirection): void { const agent = this._agents.get(handle); if (!agent) { return; } - const result = this._previousResultMap.get(sessionId); + const result = this._resultsBySessionAndRequestId.get(sessionId)?.get(requestId); if (!result) { return; } @@ -171,12 +184,12 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { agent.acceptFeedback(Object.freeze({ result, kind })); } - $acceptAction(handle: number, sessionId: string, action: IChatUserActionEvent): void { + $acceptAction(handle: number, sessionId: string, requestId: string, action: IChatUserActionEvent): void { const agent = this._agents.get(handle); if (!agent) { return; } - const result = this._previousResultMap.get(sessionId); + const result = this._resultsBySessionAndRequestId.get(sessionId)?.get(requestId); if (!result) { return; } @@ -302,7 +315,6 @@ class ExtHostChatAgent { that._iconPath = v; updateMetadataSoon(); }, - // onDidPerformAction get slashCommandProvider() { return that._slashCommandProvider; }, diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index c29335d7607..3356f592c73 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -110,6 +110,7 @@ export function registerChatCodeBlockActions() { providerId: context.element.providerId, agentId: context.element.agent?.id, sessionId: context.element.sessionId, + requestId: context.element.requestId, action: { kind: 'copy', responseId: context.element.providerResponseId, @@ -154,6 +155,7 @@ export function registerChatCodeBlockActions() { providerId: context.element.providerId, agentId: context.element.agent?.id, sessionId: context.element.sessionId, + requestId: context.element.requestId, action: { kind: 'copy', codeBlockIndex: context.codeBlockIndex, diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index b42d4685e25..af6ba1145eb 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -353,6 +353,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer this._onDidFocus.fire())); this._register(this.inputPart.onDidAcceptFollowup(e => { - // this.chatService.notifyUserAction if (!this.viewModel) { return; } + + if (!e.response) { + // Followups can be shown by the welcome message, then there is no response associated. + // At some point we probably want telemetry for these too. + return; + } + this.chatService.notifyUserAction({ providerId: this.viewModel.providerId, sessionId: this.viewModel.sessionId, + requestId: e.response.requestId, agentId: e.response?.agent?.id, action: { kind: 'followUp', diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 43f64cd6bd7..8b928207f83 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -53,6 +53,7 @@ export interface IChatResponseModel { readonly id: string; readonly providerId: string; readonly providerResponseId: string | undefined; + readonly requestId: string; readonly username: string; readonly avatarIconUri?: URI; readonly session: IChatModel; @@ -294,6 +295,7 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel _response: IMarkdownString | ReadonlyArray, public readonly session: ChatModel, public readonly agent: IChatAgent | undefined, + public readonly requestId: string, private _isComplete: boolean = false, private _isCanceled = false, private _vote?: InteractiveSessionVoteDirection, @@ -565,7 +567,7 @@ export class ChatModel extends Disposable implements IChatModel { const request = new ChatRequestModel(this, parsedRequest, raw.providerRequestId); if (raw.response || raw.responseErrorDetails) { const agent = raw.agent && this.chatAgentService.getAgents().find(a => a.id === raw.agent!.id); // TODO do something reasonable if this agent has disappeared since the last session - request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, true, raw.isCanceled, raw.vote, raw.providerRequestId, raw.responseErrorDetails, raw.followups); + request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, request.id, true, raw.isCanceled, raw.vote, raw.providerRequestId, raw.responseErrorDetails, raw.followups); if (raw.usedContext) { // @ulugbekna: if this's a new vscode sessions, doc versions are incorrect anyway? request.response.updateContent(raw.usedContext); } @@ -652,7 +654,7 @@ export class ChatModel extends Disposable implements IChatModel { } const request = new ChatRequestModel(this, message); - request.response = new ChatResponseModel([], this, chatAgent); + request.response = new ChatResponseModel([], this, chatAgent, request.id); this._requests.push(request); this._onDidChange.fire({ kind: 'addRequest', request }); @@ -665,7 +667,7 @@ export class ChatModel extends Disposable implements IChatModel { } if (!request.response) { - request.response = new ChatResponseModel([], this, undefined); + request.response = new ChatResponseModel([], this, undefined, request.id); } if (request.response.isComplete) { @@ -710,7 +712,7 @@ export class ChatModel extends Disposable implements IChatModel { } if (!request.response) { - request.response = new ChatResponseModel([], this, undefined); + request.response = new ChatResponseModel([], this, undefined, request.id); } request.response.setErrorDetails(rawResponse.errorDetails); diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index a065dc06655..369840dfe8e 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -212,7 +212,7 @@ export interface IChatCommandAction { export interface IChatFollowupAction { kind: 'followUp'; - followup: IChatFollowup; + followup: IChatReplyFollowup; } export type ChatUserAction = IChatVoteAction | IChatCopyAction | IChatInsertAction | IChatTerminalAction | IChatCommandAction | IChatFollowupAction; @@ -222,6 +222,7 @@ export interface IChatUserActionEvent { providerId: string; agentId: string | undefined; sessionId: string; + requestId: string; } export interface IChatDynamicRequest { diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 848a64ded4b..04fad070cd9 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -12,7 +12,6 @@ import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle' import { revive } from 'vs/base/common/marshalling'; import { StopWatch } from 'vs/base/common/stopwatch'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { generateUuid } from 'vs/base/common/uuid'; import { localize } from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -521,7 +520,7 @@ export class ChatService extends Disposable implements IChatService { request = model.addRequest(parsedRequest, agent); const requestProps: IChatAgentRequest = { sessionId, - requestId: generateUuid(), + requestId: request.id, message, variables: {}, command: agentSlashCommandPart?.command.name ?? '', diff --git a/src/vs/workbench/contrib/chat/common/chatViewModel.ts b/src/vs/workbench/contrib/chat/common/chatViewModel.ts index 365303eaf7b..dbf12f34f08 100644 --- a/src/vs/workbench/contrib/chat/common/chatViewModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatViewModel.ts @@ -88,6 +88,8 @@ export interface IChatResponseViewModel { readonly dataId: string; readonly providerId: string; readonly providerResponseId: string | undefined; + /** The ID of the associated IChatRequestViewModel */ + readonly requestId: string; readonly username: string; readonly avatarIconUri?: URI; readonly agent?: IChatAgent; @@ -326,6 +328,10 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi return this._model.vote; } + get requestId() { + return this._model.requestId; + } + renderData: IChatResponseRenderData | undefined = undefined; currentRenderedHeight: number | undefined; diff --git a/src/vscode-dts/vscode.proposed.interactiveUserActions.d.ts b/src/vscode-dts/vscode.proposed.interactiveUserActions.d.ts index f9b400c1d94..e038f1f2ed6 100644 --- a/src/vscode-dts/vscode.proposed.interactiveUserActions.d.ts +++ b/src/vscode-dts/vscode.proposed.interactiveUserActions.d.ts @@ -64,7 +64,7 @@ declare module 'vscode' { export interface InteractiveSessionFollowupAction { // eslint-disable-next-line local/vscode-dts-string-type-literals kind: 'followUp'; - followup: InteractiveSessionFollowup; + followup: InteractiveSessionReplyFollowup; } export type InteractiveSessionUserAction = InteractiveSessionVoteAction | InteractiveSessionCopyAction | InteractiveSessionInsertAction | InteractiveSessionTerminalAction | InteractiveSessionCommandAction; From 8e17fcac92090f85499b57e7b66ccc9e5891f698 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 17 Oct 2023 17:40:58 -0700 Subject: [PATCH 34/55] Update input placeholder color in dark modern --- extensions/theme-defaults/themes/dark_modern.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/theme-defaults/themes/dark_modern.json b/extensions/theme-defaults/themes/dark_modern.json index b7d24260f83..b4cd0a9f721 100644 --- a/extensions/theme-defaults/themes/dark_modern.json +++ b/extensions/theme-defaults/themes/dark_modern.json @@ -49,7 +49,7 @@ "input.background": "#313131", "input.border": "#3C3C3C", "input.foreground": "#CCCCCC", - "input.placeholderForeground": "#9D9D9D", + "input.placeholderForeground": "#818181", "inputOption.activeBackground": "#2489DB82", "inputOption.activeBorder": "#2488DB", "keybindingLabel.foreground": "#CCCCCC", From 49671031d136ff9bd62c65367c4eac684f0bada6 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 17 Oct 2023 18:15:34 -0700 Subject: [PATCH 35/55] Increase link contrast in dark modern --- extensions/theme-defaults/themes/dark_modern.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/theme-defaults/themes/dark_modern.json b/extensions/theme-defaults/themes/dark_modern.json index b7d24260f83..32f82f053cc 100644 --- a/extensions/theme-defaults/themes/dark_modern.json +++ b/extensions/theme-defaults/themes/dark_modern.json @@ -111,8 +111,8 @@ "textBlockQuote.background": "#2B2B2B", "textBlockQuote.border": "#616161", "textCodeBlock.background": "#2B2B2B", - "textLink.activeForeground": "#40A6FF", - "textLink.foreground": "#40A6FF", + "textLink.activeForeground": "#4daafc", + "textLink.foreground": "#4daafc", "textPreformat.foreground": "#D0D0D0", "textPreformat.background": "#3C3C3C", "textSeparator.foreground": "#21262D", From 5cbc3f8a1dc6abc2cbb49f402520227f2f3fee6f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 17 Oct 2023 21:02:04 -0700 Subject: [PATCH 36/55] Add missing requestIDs (#195857) --- .../browser/actions/chatCodeblockActions.ts | 19 +++++++++++-------- .../chat/browser/actions/chatTitleActions.ts | 12 +++++++----- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index 3356f592c73..f43739183dd 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -28,7 +28,7 @@ import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatAct import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { ICodeBlockActionContext } from 'vs/workbench/contrib/chat/browser/codeBlockPart'; import { CONTEXT_IN_CHAT_SESSION, CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; -import { IChatCopyAction, IChatService, IChatUserActionEvent, IDocumentContext, InteractiveSessionCopyKind } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatCopyAction, IChatService, IDocumentContext, InteractiveSessionCopyKind } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatResponseViewModel, isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { CTX_INLINE_CHAT_VISIBLE } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { insertCell } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations'; @@ -106,7 +106,7 @@ export function registerChatCodeBlockActions() { if (isResponseVM(context.element)) { const chatService = accessor.get(IChatService); - chatService.notifyUserAction({ + chatService.notifyUserAction({ providerId: context.element.providerId, agentId: context.element.agent?.id, sessionId: context.element.sessionId, @@ -330,13 +330,14 @@ export function registerChatCodeBlockActions() { private notifyUserAction(accessor: ServicesAccessor, context: ICodeBlockActionContext) { if (isResponseVM(context.element)) { const chatService = accessor.get(IChatService); - chatService.notifyUserAction({ + chatService.notifyUserAction({ providerId: context.element.providerId, agentId: context.element.agent?.id, sessionId: context.element.sessionId, + requestId: context.element.requestId, action: { kind: 'insert', - responseId: context.element.providerResponseId, + responseId: context.element.providerResponseId!, codeBlockIndex: context.codeBlockIndex, totalCharacters: context.code.length, } @@ -378,13 +379,14 @@ export function registerChatCodeBlockActions() { editorService.openEditor({ contents: context.code, languageId: context.languageId, resource: undefined }); if (isResponseVM(context.element)) { - chatService.notifyUserAction({ + chatService.notifyUserAction({ providerId: context.element.providerId, agentId: context.element.agent?.id, sessionId: context.element.sessionId, + requestId: context.element.requestId, action: { kind: 'insert', - responseId: context.element.providerResponseId, + responseId: context.element.providerResponseId!, codeBlockIndex: context.codeBlockIndex, totalCharacters: context.code.length, newFile: true @@ -463,13 +465,14 @@ export function registerChatCodeBlockActions() { terminal.sendText(context.code, false, true); if (isResponseVM(context.element)) { - chatService.notifyUserAction({ + chatService.notifyUserAction({ providerId: context.element.providerId, agentId: context.element.agent?.id, sessionId: context.element.sessionId, + requestId: context.element.requestId, action: { kind: 'runInTerminal', - responseId: context.element.providerResponseId, + responseId: context.element.providerResponseId!, codeBlockIndex: context.codeBlockIndex, languageId: context.languageId, } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts index b2c5828ece2..4d9bf1fe75b 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts @@ -16,7 +16,7 @@ import { ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/ import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions'; import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { CONTEXT_IN_CHAT_INPUT, CONTEXT_IN_CHAT_SESSION, CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys'; -import { IChatService, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatService, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { isRequestVM, isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellEditType, CellKind, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -52,14 +52,15 @@ export function registerChatTitleActions() { } const chatService = accessor.get(IChatService); - chatService.notifyUserAction({ + chatService.notifyUserAction({ providerId: item.providerId, agentId: item.agent?.id, sessionId: item.sessionId, + requestId: item.requestId, action: { kind: 'vote', direction: InteractiveSessionVoteDirection.Up, - responseId: item.providerResponseId, + responseId: item.providerResponseId!, } }); item.setVote(InteractiveSessionVoteDirection.Up); @@ -94,14 +95,15 @@ export function registerChatTitleActions() { } const chatService = accessor.get(IChatService); - chatService.notifyUserAction({ + chatService.notifyUserAction({ providerId: item.providerId, agentId: item.agent?.id, sessionId: item.sessionId, + requestId: item.requestId, action: { kind: 'vote', direction: InteractiveSessionVoteDirection.Down, - responseId: item.providerResponseId, + responseId: item.providerResponseId!, } }); item.setVote(InteractiveSessionVoteDirection.Down); From 58bdf2c7c40be6f4494ae35a09528708832cf357 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Oct 2023 06:51:06 +0200 Subject: [PATCH 37/55] :up: distro (#195858) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6301522d2f2..8f91ee4159f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.84.0", - "distro": "0f218422a902175f8b82cbf0f13fa4feb278f22a", + "distro": "b30d9687a6941b0d17b73334fc5a0f12590bff90", "author": { "name": "Microsoft Corporation" }, From 493d20b9a27307773007f33da92591fc0a03b4f9 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 17 Oct 2023 22:04:02 -0700 Subject: [PATCH 38/55] Fix accepting the welcome message followup (#195862) --- src/vs/workbench/contrib/chat/browser/chatWidget.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index fb9d7b92dcd..65e2434e130 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -416,6 +416,8 @@ export class ChatWidget extends Disposable implements IChatWidget { return; } + this.acceptInput(e.followup.message); + if (!e.response) { // Followups can be shown by the welcome message, then there is no response associated. // At some point we probably want telemetry for these too. @@ -432,7 +434,6 @@ export class ChatWidget extends Disposable implements IChatWidget { followup: e.followup }, }); - this.acceptInput(e.followup.message); })); this._register(this.inputPart.onDidChangeHeight(() => this.bodyDimension && this.layout(this.bodyDimension.height, this.bodyDimension.width))); } From 7ff40aa77dd883eaa451d345880937ad56714708 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 17 Oct 2023 22:07:25 -0700 Subject: [PATCH 39/55] Revert "fix: rerender chat pane when provider is added (#195845)" (#195860) This reverts commit e5b7340d189d6814b139401009b2f8ab3cda0ea1. --- src/vs/workbench/contrib/chat/browser/chatViewPane.ts | 3 --- src/vs/workbench/contrib/chat/common/chatService.ts | 1 - src/vs/workbench/contrib/chat/common/chatServiceImpl.ts | 4 ---- 3 files changed, 8 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts index d5ced73f860..9be6f027aba 100644 --- a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts @@ -66,9 +66,6 @@ export class ChatViewPane extends ViewPane implements IChatViewPane { // View state for the ViewPane is currently global per-provider basically, but some other strictly per-model state will require a separate memento. this.memento = new Memento('interactive-session-view-' + this.chatViewOptions.providerId, this.storageService); this.viewState = this.memento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE) as IViewPaneState; - this._register(this.chatService.onDidRegisterProvider(({ providerId }) => { - if (providerId === this.chatViewOptions.providerId) { this.updateModel(); } - })); } private updateModel(model?: IChatModel | undefined): void { diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 369840dfe8e..d3e13df3fe3 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -265,7 +265,6 @@ export interface IChatService { transferredSessionData: IChatTransferredSessionData | undefined; onDidSubmitSlashCommand: Event<{ slashCommand: string; sessionId: string }>; - onDidRegisterProvider: Event<{ providerId: string }>; registerProvider(provider: IChatProvider): IDisposable; hasProviders(): boolean; getProviderInfos(): IChatProviderInfo[]; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 04fad070cd9..2b09ccbe24b 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -149,9 +149,6 @@ export class ChatService extends Disposable implements IChatService { private readonly _onDidDisposeSession = this._register(new Emitter<{ sessionId: string }>()); public readonly onDidDisposeSession = this._onDidDisposeSession.event; - private readonly _onDidRegisterProvider = this._register(new Emitter<{ providerId: string }>()); - public readonly onDidRegisterProvider = this._onDidRegisterProvider.event; - constructor( @IStorageService private readonly storageService: IStorageService, @ILogService private readonly logService: ILogService, @@ -745,7 +742,6 @@ export class ChatService extends Disposable implements IChatService { this._providers.set(provider.id, provider); this._hasProvider.set(true); - this._onDidRegisterProvider.fire({ providerId: provider.id }); Array.from(this._sessionModels.values()) .filter(model => model.providerId === provider.id) From 198bbdf35eff992267104ced9bb575152cd645a7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Oct 2023 09:33:39 +0200 Subject: [PATCH 40/55] aux window - avoid more global `window` (#195869) * aux window - avoid more global `window` * aux window - avoid more global `document` --- src/vs/base/browser/dom.ts | 9 +-------- src/vs/workbench/browser/layout.ts | 6 +++--- .../parts/notifications/notificationsCenter.ts | 6 +++--- .../browser/parts/notifications/notificationsList.ts | 6 +++--- .../parts/notifications/notificationsToasts.ts | 6 +++--- .../contrib/accessibility/browser/accessibleView.ts | 4 ++-- .../contrib/codeEditor/browser/toggleWordWrap.ts | 11 ++++++++--- .../workbench/contrib/debug/browser/linkDetector.ts | 3 ++- src/vs/workbench/contrib/debug/browser/repl.ts | 10 ++++++---- .../contrib/files/browser/views/explorerView.ts | 2 +- .../contrib/files/browser/views/explorerViewer.ts | 7 ++++--- .../workbench/contrib/markers/browser/markersView.ts | 4 ++-- .../browser/contrib/clipboard/notebookClipboard.ts | 4 ++-- .../contrib/notebook/browser/notebookEditorWidget.ts | 2 +- .../workbench/contrib/scm/browser/scm.contribution.ts | 7 ++++--- src/vs/workbench/contrib/search/browser/searchView.ts | 4 ++-- .../welcomeWalkthrough/browser/walkThroughPart.ts | 4 ++-- 17 files changed, 49 insertions(+), 46 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index d8302103599..024e9988544 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -602,14 +602,7 @@ export function getLargestChildWidth(parent: HTMLElement, children: HTMLElement[ // ---------------------------------------------------------------------------------------- export function isAncestor(testChild: Node | null, testAncestor: Node | null): boolean { - while (testChild) { - if (testChild === testAncestor) { - return true; - } - testChild = testChild.parentNode; - } - - return false; + return Boolean(testAncestor?.contains(testChild)); } const parentFlowToDataKey = 'parentFlowToElementId'; diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 2708caa9350..51a6a96f7bc 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -5,7 +5,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { EventType, addDisposableListener, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize, getActiveDocument, getWindows } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize, getActiveDocument, getWindows, getActiveWindow } from 'vs/base/browser/dom'; import { onDidChangeFullscreen, isFullscreen, isWCOEnabled } from 'vs/base/browser/browser'; import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; import { isWindows, isLinux, isMacintosh, isWeb, isNative, isIOS } from 'vs/base/common/platform'; @@ -1463,8 +1463,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } resizePart(part: Parts, sizeChangeWidth: number, sizeChangeHeight: number): void { - const sizeChangePxWidth = Math.sign(sizeChangeWidth) * computeScreenAwareSize(window, Math.abs(sizeChangeWidth)); - const sizeChangePxHeight = Math.sign(sizeChangeHeight) * computeScreenAwareSize(window, Math.abs(sizeChangeHeight)); + const sizeChangePxWidth = Math.sign(sizeChangeWidth) * computeScreenAwareSize(getActiveWindow(), Math.abs(sizeChangeWidth)); + const sizeChangePxHeight = Math.sign(sizeChangeHeight) * computeScreenAwareSize(getActiveWindow(), Math.abs(sizeChangeHeight)); let viewSize: IViewSize; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index c7e9ab26652..51115350f94 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -14,7 +14,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { INotificationsCenterController, NotificationActionRunner } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { NotificationsList } from 'vs/workbench/browser/parts/notifications/notificationsList'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { isAncestor, Dimension } from 'vs/base/browser/dom'; +import { Dimension, isAncestorOfActiveElement } from 'vs/base/browser/dom'; import { widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { localize } from 'vs/nls'; @@ -222,7 +222,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente notificationsList.updateNotificationsList(e.index, 1, [e.item]); break; case NotificationChangeType.REMOVE: - focusEditor = isAncestor(document.activeElement, notificationsCenterContainer); + focusEditor = isAncestorOfActiveElement(notificationsCenterContainer); notificationsList.updateNotificationsList(e.index, 1); e.item.updateVisibility(false); break; @@ -247,7 +247,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente return; // already hidden } - const focusEditor = isAncestor(document.activeElement, this.notificationsCenterContainer); + const focusEditor = isAncestorOfActiveElement(this.notificationsCenterContainer); // Hide this._isVisible = false; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index 727e2e866a3..5ac8c173685 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/notificationsList'; import { localize } from 'vs/nls'; -import { isAncestor, trackFocus } from 'vs/base/browser/dom'; +import { isAncestorOfActiveElement, trackFocus } from 'vs/base/browser/dom'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IListAccessibilityProvider, IListOptions } from 'vs/base/browser/ui/list/listWidget'; @@ -133,7 +133,7 @@ export class NotificationsList extends Disposable { updateNotificationsList(start: number, deleteCount: number, items: INotificationViewItem[] = []) { const [list, listContainer] = assertAllDefined(this.list, this.listContainer); - const listHasDOMFocus = isAncestor(document.activeElement, listContainer); + const listHasDOMFocus = isAncestorOfActiveElement(listContainer); // Remember focus and relative top of that item const focusedIndex = list.getFocus()[0]; @@ -223,7 +223,7 @@ export class NotificationsList extends Disposable { return false; // not created yet } - return isAncestor(document.activeElement, this.listContainer); + return isAncestorOfActiveElement(this.listContainer); } layout(width: number, maxHeight?: number): void { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 35785314672..f6fa63e49f2 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/notificationsToasts'; import { localize } from 'vs/nls'; import { INotificationsModel, NotificationChangeType, INotificationChangeEvent, INotificationViewItem, NotificationViewItemContentChangeKind } from 'vs/workbench/common/notifications'; import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { isAncestor, addDisposableListener, EventType, Dimension, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { addDisposableListener, EventType, Dimension, scheduleAtNextAnimationFrame, isAncestorOfActiveElement } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { NotificationsList } from 'vs/workbench/browser/parts/notifications/notificationsList'; import { Event, Emitter } from 'vs/base/common/event'; @@ -322,7 +322,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast // UI const notificationToast = this.mapNotificationToToast.get(item); if (notificationToast) { - const toastHasDOMFocus = isAncestor(document.activeElement, notificationToast.container); + const toastHasDOMFocus = isAncestorOfActiveElement(notificationToast.container); if (toastHasDOMFocus) { focusEditor = !(this.focusNext() || this.focusPrevious()); // focus next if any, otherwise focus editor } @@ -380,7 +380,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast } hide(): void { - const focusEditor = this.notificationsToastsContainer ? isAncestor(document.activeElement, this.notificationsToastsContainer) : false; + const focusEditor = this.notificationsToastsContainer ? isAncestorOfActiveElement(this.notificationsToastsContainer) : false; this.removeToasts(); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index f7090c8f8e8..c73385b4246 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EventType, addDisposableListener, isActiveElement } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, getActiveWindow, isActiveElement } from 'vs/base/browser/dom'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { alert } from 'vs/base/browser/ui/aria/aria'; @@ -264,7 +264,7 @@ export class AccessibleView extends Disposable { return; } const delegate: IContextViewDelegate = { - getAnchor: () => { return { x: (window.innerWidth / 2) - ((Math.min(this._layoutService.dimension.width * 0.62 /* golden cut */, DIMENSIONS.MAX_WIDTH)) / 2), y: this._layoutService.offset.quickPickTop }; }, + getAnchor: () => { return { x: (getActiveWindow().innerWidth / 2) - ((Math.min(this._layoutService.dimension.width * 0.62 /* golden cut */, DIMENSIONS.MAX_WIDTH)) / 2), y: this._layoutService.offset.quickPickTop }; }, render: (container) => { container.classList.add('accessible-view-container'); return this._render(provider!, container, showAccessibleViewHelp); diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts index 85be3c855a2..d3849333ddc 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts @@ -21,6 +21,8 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Event } from 'vs/base/common/event'; +import { addDisposableListener, onDidRegisterWindow } from 'vs/base/browser/dom'; const transientWordWrapState = 'transientWordWrapState'; const isWordWrapMinifiedKey = 'isWordWrapMinified'; @@ -255,7 +257,7 @@ function canToggleWordWrap(codeEditorService: ICodeEditorService, editor: ICodeE return true; } -class EditorWordWrapContextKeyTracker implements IWorkbenchContribution { +class EditorWordWrapContextKeyTracker extends Disposable implements IWorkbenchContribution { private readonly _canToggleWordWrap: IContextKey; private readonly _editorWordWrap: IContextKey; @@ -267,8 +269,11 @@ class EditorWordWrapContextKeyTracker implements IWorkbenchContribution { @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, @IContextKeyService private readonly _contextService: IContextKeyService, ) { - window.addEventListener('focus', () => this._update(), true); - window.addEventListener('blur', () => this._update(), true); + super(); + this._register(Event.runAndSubscribe(onDidRegisterWindow, ({ window, disposableStore }) => { + disposableStore.add(addDisposableListener(window, 'focus', () => this._update(), true)); + disposableStore.add(addDisposableListener(window, 'blur', () => this._update(), true)); + }, { window, disposableStore: this._store })); this._editorService.onDidActiveEditorChange(() => this._update()); this._canToggleWordWrap = CAN_TOGGLE_WORD_WRAP.bindTo(this._contextService); this._editorWordWrap = EDITOR_WORD_WRAP.bindTo(this._contextService); diff --git a/src/vs/workbench/contrib/debug/browser/linkDetector.ts b/src/vs/workbench/contrib/debug/browser/linkDetector.ts index abfca65cb23..484f3286dba 100644 --- a/src/vs/workbench/contrib/debug/browser/linkDetector.ts +++ b/src/vs/workbench/contrib/debug/browser/linkDetector.ts @@ -18,6 +18,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { getWindow } from 'vs/base/browser/dom'; const CONTROL_CODES = '\\u0000-\\u0020\\u007f-\\u009f'; const WEB_LINK_REGEX = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|data:|www\\.)[^\\s' + CONTROL_CODES + '"]{2,}[^\\s' + CONTROL_CODES + '"\')}\\],:;.!?]', 'ug'); @@ -201,7 +202,7 @@ export class LinkDetector { link.onmousemove = (event) => { link.classList.toggle('pointer', platform.isMacintosh ? event.metaKey : event.ctrlKey); }; link.onmouseleave = () => link.classList.remove('pointer'); link.onclick = (event) => { - const selection = window.getSelection(); + const selection = getWindow(link).getSelection(); if (!selection || selection.type === 'Range') { return; // do not navigate when user is selecting } diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 7be4e8eb0bd..9805ab089a4 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -570,16 +570,18 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { this._register(registerNavigableContainer({ focusNotifiers: [this, this.filterWidget], focusNextWidget: () => { + const element = this.tree?.getHTMLElement(); if (this.filterWidget.hasFocus()) { this.tree?.domFocus(); - } else if (this.tree?.getHTMLElement() === document.activeElement) { + } else if (element && dom.isActiveElement(element)) { this.focus(); } }, focusPreviousWidget: () => { + const element = this.tree?.getHTMLElement(); if (this.replInput.hasTextFocus()) { this.tree?.domFocus(); - } else if (this.tree?.getHTMLElement() === document.activeElement) { + } else if (element && dom.isActiveElement(element)) { this.focusFilter(); } } @@ -648,7 +650,7 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { this._register(tree.onContextMenu(e => this.onContextMenu(e))); let lastSelectedString: string; this._register(tree.onMouseClick(() => { - const selection = window.getSelection(); + const selection = dom.getWindow(this.treeContainer).getSelection(); if (!selection || selection.type !== 'Range' || lastSelectedString === selection.toString()) { // only focus the input if the user is not currently selecting. this.replInput.focus(); @@ -1070,7 +1072,7 @@ registerAction2(class extends Action2 { async run(accessor: ServicesAccessor, element: IReplElement): Promise { const clipboardService = accessor.get(IClipboardService); const debugService = accessor.get(IDebugService); - const nativeSelection = window.getSelection(); + const nativeSelection = dom.getActiveWindow().getSelection(); const selectedText = nativeSelection?.toString(); if (selectedText && selectedText.length > 0) { return clipboardService.writeText(selectedText); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index d3d8d5c7e59..ca2b5d06823 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -325,7 +325,7 @@ export class ExplorerView extends ViewPane implements IExplorerView { } hasFocus(): boolean { - return DOM.isAncestor(document.activeElement, this.container); + return DOM.isAncestorOfActiveElement(this.container); } getContext(respectMultiSelection: boolean): ExplorerItem[] { diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index bfebab5f73d..d567ec80916 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -621,11 +621,12 @@ export class FilesRenderer implements ICompressibleTreeRenderer { const contextKeyService = accessor.get(IContextKeyService); - const context = contextKeyService.getContext(document.activeElement); + const context = contextKeyService.getContext(getActiveElement()); const repositoryId = context.getValue('scmRepository'); if (!repositoryId) { @@ -336,7 +337,7 @@ const viewNextCommitCommand = { handler: (accessor: ServicesAccessor) => { const contextKeyService = accessor.get(IContextKeyService); const scmService = accessor.get(ISCMService); - const context = contextKeyService.getContext(document.activeElement); + const context = contextKeyService.getContext(getActiveElement()); const repositoryId = context.getValue('scmRepository'); const repository = repositoryId ? scmService.getRepository(repositoryId) : undefined; repository?.input.showNextHistoryValue(); @@ -349,7 +350,7 @@ const viewPreviousCommitCommand = { handler: (accessor: ServicesAccessor) => { const contextKeyService = accessor.get(IContextKeyService); const scmService = accessor.get(ISCMService); - const context = contextKeyService.getContext(document.activeElement); + const context = contextKeyService.getContext(getActiveElement()); const repositoryId = context.getValue('scmRepository'); const repository = repositoryId ? scmService.getRepository(repositoryId) : undefined; repository?.input.showPreviousHistoryValue(); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index f1434f91db9..6bc3f38e680 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1072,7 +1072,7 @@ export class SearchView extends ViewPane { } private updateTextFromFindWidget(controller: CommonFindController, { allowSearchOnType = true }): boolean { - if (!this.searchConfig.seedWithNearestWord && (window.getSelection()?.toString() ?? '') === '') { + if (!this.searchConfig.seedWithNearestWord && (dom.getActiveWindow().getSelection()?.toString() ?? '') === '') { return false; } @@ -1275,7 +1275,7 @@ export class SearchView extends ViewPane { } private getSearchTextFromEditor(allowUnselectedWord: boolean, editor?: IEditor): string | null { - if (dom.isAncestor(document.activeElement, this.getContainer())) { + if (dom.isAncestorOfActiveElement(this.getContainer())) { return null; } diff --git a/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts index 002c6ef9c5f..29f5aace84c 100644 --- a/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts @@ -32,7 +32,7 @@ import { UILabelProvider } from 'vs/base/common/keybindingLabels'; import { OS, OperatingSystem } from 'vs/base/common/platform'; import { deepClone } from 'vs/base/common/objects'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { addDisposableListener, Dimension, safeInnerHtml, size } from 'vs/base/browser/dom'; +import { addDisposableListener, Dimension, getWindow, safeInnerHtml, size } from 'vs/base/browser/dom'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -156,7 +156,7 @@ export class WalkThroughPart extends EditorPane { this.content.addEventListener('click', event => { for (let node = event.target as HTMLElement; node; node = node.parentNode as HTMLElement) { if (node instanceof HTMLAnchorElement && node.href) { - const baseElement = window.document.getElementsByTagName('base')[0] || window.location; + const baseElement = node.ownerDocument.getElementsByTagName('base')[0] || getWindow(node).location; if (baseElement && node.href.indexOf(baseElement.href) >= 0 && node.hash) { const scrollTarget = this.content.querySelector(node.hash); const innerContent = this.content.firstElementChild; From ae2dd44a30ae5211685ae5d0b4713850af8ceda2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Oct 2023 10:38:24 +0200 Subject: [PATCH 41/55] voice - fix issue with missing provider registration event (#195873) --- .../contrib/chat/electron-sandbox/chat.contribution.ts | 8 ++++++-- src/vs/workbench/contrib/speech/common/speechService.ts | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) 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 409022e8651..b382598b7bb 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts @@ -32,9 +32,13 @@ class VoiceChatActionsContributor extends Disposable implements IWorkbenchContri constructor(@ISpeechService speechService: ISpeechService) { super(); - this._register(Event.once(speechService.onDidRegisterSpeechProvider)(() => { + if (speechService.hasSpeechProvider) { registerVoiceChatActions(); - })); + } else { + this._register(Event.once(speechService.onDidRegisterSpeechProvider)(() => { + registerVoiceChatActions(); + })); + } } } diff --git a/src/vs/workbench/contrib/speech/common/speechService.ts b/src/vs/workbench/contrib/speech/common/speechService.ts index c071ce0746d..021a676e580 100644 --- a/src/vs/workbench/contrib/speech/common/speechService.ts +++ b/src/vs/workbench/contrib/speech/common/speechService.ts @@ -47,6 +47,8 @@ export interface ISpeechService { readonly onDidRegisterSpeechProvider: Event; readonly onDidUnregisterSpeechProvider: Event; + readonly hasSpeechProvider: boolean; + registerSpeechProvider(identifier: string, provider: ISpeechProvider): IDisposable; createSpeechToTextSession(token: CancellationToken): ISpeechToTextSession; @@ -62,6 +64,8 @@ export class SpeechService implements ISpeechService { private readonly _onDidUnregisterSpeechProvider = new Emitter(); readonly onDidUnregisterSpeechProvider = this._onDidUnregisterSpeechProvider.event; + get hasSpeechProvider(): boolean { return this.providers.size > 0; } + private readonly providers = new Map(); constructor(@ILogService private readonly logService: ILogService) { } From 6db41844bc97b1a86841724c3c38b44001f22d59 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 18 Oct 2023 11:39:22 +0200 Subject: [PATCH 42/55] * show a diff zone per "change region" * don't show whole range highlights anymore --- .../browser/inlineChatController.ts | 47 +++++---- .../browser/inlineChatLivePreviewWidget.ts | 84 +++++++--------- .../browser/inlineChatStrategies.ts | 95 +++++++++++++------ .../contrib/inlineChat/browser/utils.ts | 8 +- 4 files changed, 132 insertions(+), 102 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index c10fb758a41..5f6b0882d69 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -8,14 +8,14 @@ import { Barrier, raceCancellationError } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter, Event } from 'vs/base/common/event'; -import { DisposableStore, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { StopWatch } from 'vs/base/common/stopwatch'; import { assertType } from 'vs/base/common/types'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; -import { ModelDecorationOptions, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; +import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { IModelService } from 'vs/editor/common/services/model'; import { InlineCompletionsController } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController'; @@ -39,7 +39,6 @@ import { generateUuid } from 'vs/base/common/uuid'; import { TextEdit } from 'vs/editor/common/languages'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { IModelDeltaDecoration } from 'vs/editor/common/model'; export const enum State { CREATE_SESSION = 'CREATE_SESSION', @@ -93,12 +92,12 @@ export class InlineChatController implements IEditorContribution { return editor.getContribution(INLINE_CHAT_ID); } - private static _decoBlock = ModelDecorationOptions.register({ - description: 'inline-chat', - showIfCollapsed: false, - isWholeLine: true, - className: 'inline-chat-block-selection', - }); + // private static _decoBlock = ModelDecorationOptions.register({ + // description: 'inline-chat', + // showIfCollapsed: false, + // isWholeLine: true, + // className: 'inline-chat-block-selection', + // }); private static _promptHistory: string[] = []; private _historyOffset: number = -1; @@ -335,22 +334,22 @@ export class InlineChatController implements IEditorContribution { this._sessionStore.clear(); - const wholeRangeDecoration = this._editor.createDecorationsCollection(); - const updateWholeRangeDecoration = () => { + // const wholeRangeDecoration = this._editor.createDecorationsCollection(); + // const updateWholeRangeDecoration = () => { - const range = this._activeSession!.wholeRange.value; - const decorations: IModelDeltaDecoration[] = []; - if (!range.isEmpty()) { - decorations.push({ - range, - options: InlineChatController._decoBlock - }); - } - wholeRangeDecoration.set(decorations); - }; - this._sessionStore.add(toDisposable(() => wholeRangeDecoration.clear())); - this._sessionStore.add(this._activeSession.wholeRange.onDidChange(updateWholeRangeDecoration)); - updateWholeRangeDecoration(); + // const range = this._activeSession!.wholeRange.value; + // const decorations: IModelDeltaDecoration[] = []; + // if (!range.isEmpty()) { + // decorations.push({ + // range, + // options: InlineChatController._decoBlock + // }); + // } + // wholeRangeDecoration.set(decorations); + // }; + // this._sessionStore.add(toDisposable(() => wholeRangeDecoration.clear())); + // this._sessionStore.add(this._activeSession.wholeRange.onDidChange(updateWholeRangeDecoration)); + // updateWholeRangeDecoration(); this._zone.value.widget.updateSlashCommands(this._activeSession.session.slashCommands ?? []); this._updatePlaceholder(); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts index 63e66be5f3d..11b1281498c 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Dimension, h } from 'vs/base/browser/dom'; -import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; +import { MutableDisposable } from 'vs/base/common/lifecycle'; import { assertType } from 'vs/base/common/types'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; @@ -35,14 +35,14 @@ import { ILanguageService } from 'vs/editor/common/languages/language'; import { FoldingController } from 'vs/editor/contrib/folding/browser/folding'; import { WordHighlighterContribution } from 'vs/editor/contrib/wordHighlighter/browser/wordHighlighter'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { generateUuid } from 'vs/base/common/uuid'; export class InlineChatLivePreviewWidget extends ZoneWidget { - private static readonly _hideId = 'overlayDiff'; + private readonly _hideId = `overlayDiff:${generateUuid()}`; private readonly _elements = h('div.inline-chat-diff-widget@domNode'); - private readonly _sessionStore = this._disposables.add(new DisposableStore()); private readonly _diffEditor: IDiffEditor; private _dim: Dimension | undefined; private _isVisible: boolean = false; @@ -50,6 +50,7 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { constructor( editor: ICodeEditor, private readonly _session: Session, + onDidChangeDiff: (() => void) | undefined, @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, @ILogService private readonly _logService: ILogService, @@ -92,6 +93,10 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { lineDecorationsWidth: editor.getLayoutInfo().decorationsWidth }); + if (onDidChangeDiff) { + this._disposables.add(this._diffEditor.onDidUpdateDiff(() => { onDidChangeDiff(); })); + } + const highlighter = WordHighlighterContribution.get(editor); if (highlighter) { this._disposables.add(highlighter.linkWordHighlighters(this._diffEditor.getModifiedEditor())); @@ -132,33 +137,17 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { override hide(): void { this._cleanupFullDiff(); - this._sessionStore.clear(); super.hide(); this._isVisible = false; } override show(): void { - assertType(this.editor.hasModel()); - this._sessionStore.clear(); - this._isVisible = true; - - this._sessionStore.add(this._diffEditor.onDidUpdateDiff(() => { - const result = this._diffEditor.getDiffComputationResult(); - const hasFocus = this._diffEditor.hasTextFocus(); - this._updateFromChanges(this._session.wholeRange.value, result?.changes2 ?? []); - // TODO@jrieken find a better fix for this. this is the challenge: - // the _doShowForChanges 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(); - } - })); - this._updateFromChanges(this._session.wholeRange.value, this._session.lastTextModelChanges); + throw new Error('use showForChanges'); } - private _updateFromChanges(range: Range, changes: readonly DetailedLineRangeMapping[]): void { - assertType(this.editor.hasModel()); + showForChanges(changes: readonly DetailedLineRangeMapping[]): void { + const hasFocus = this._diffEditor.hasTextFocus(); + this._isVisible = true; if (changes.length === 0 || this._session.textModel0.getValueLength() === 0) { // no change or changes to an empty file @@ -167,16 +156,26 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { } else { // complex changes this._logService.debug('[IE] livePreview-mode: full diff'); - this._renderChangesWithFullDiff(changes, range); + this._renderChangesWithFullDiff(changes); + } + + // 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(); } } + // --- full diff - private _renderChangesWithFullDiff(changes: readonly DetailedLineRangeMapping[], range: Range) { + private _renderChangesWithFullDiff(changes: readonly DetailedLineRangeMapping[]) { + assertType(this.editor.hasModel()); - const modified = this.editor.getModel()!; - const ranges = this._computeHiddenRanges(modified, range, changes); + const modified = this.editor.getModel(); + const ranges = this._computeHiddenRanges(modified, changes); this._hideEditorRanges(this.editor, [ranges.modifiedHidden]); this._hideEditorRanges(this._diffEditor.getOriginalEditor(), ranges.originalDiffHidden); @@ -187,25 +186,20 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { const lineCountModified = ranges.modifiedHidden.length; const lineCountOriginal = ranges.originalHidden.length; - const lineHeightDiff = Math.max(lineCountModified, lineCountOriginal); - const lineHeightPadding = (this.editor.getOption(EditorOption.lineHeight) / 12) /* padding-top/bottom*/; - const heightInLines = lineHeightDiff + lineHeightPadding; + const heightInLines = Math.max(lineCountModified, lineCountOriginal); super.show(ranges.anchor, heightInLines); this._logService.debug(`[IE] diff SHOWING at ${ranges.anchor} with ${heightInLines} lines height`); } private _cleanupFullDiff() { - this.editor.setHiddenAreas([], InlineChatLivePreviewWidget._hideId); - this._diffEditor.getOriginalEditor().setHiddenAreas([], InlineChatLivePreviewWidget._hideId); - this._diffEditor.getModifiedEditor().setHiddenAreas([], InlineChatLivePreviewWidget._hideId); + this.editor.setHiddenAreas([], this._hideId); + this._diffEditor.getOriginalEditor().setHiddenAreas([], this._hideId); + this._diffEditor.getModifiedEditor().setHiddenAreas([], this._hideId); super.hide(); } - private _computeHiddenRanges(model: ITextModel, range: Range, changes: readonly DetailedLineRangeMapping[]) { - if (changes.length === 0) { - changes = [new DetailedLineRangeMapping(LineRange.fromRange(range), LineRange.fromRange(range), undefined)]; - } + private _computeHiddenRanges(model: ITextModel, changes: readonly DetailedLineRangeMapping[]) { let originalLineRange = changes[0].original; let modifiedLineRange = changes[0].modified; @@ -214,16 +208,8 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { modifiedLineRange = modifiedLineRange.join(changes[i].modified); } - const startDelta = modifiedLineRange.startLineNumber - range.startLineNumber; - if (startDelta > 0) { - modifiedLineRange = new LineRange(modifiedLineRange.startLineNumber - startDelta, modifiedLineRange.endLineNumberExclusive); - originalLineRange = new LineRange(originalLineRange.startLineNumber - startDelta, originalLineRange.endLineNumberExclusive); - } - - const endDelta = range.endLineNumber - (modifiedLineRange.endLineNumberExclusive - 1); - if (endDelta > 0) { - modifiedLineRange = new LineRange(modifiedLineRange.startLineNumber, modifiedLineRange.endLineNumberExclusive + endDelta); - originalLineRange = new LineRange(originalLineRange.startLineNumber, originalLineRange.endLineNumberExclusive + endDelta); + if (originalLineRange.isEmpty) { + originalLineRange = new LineRange(originalLineRange.startLineNumber, originalLineRange.endLineNumberExclusive + 1); } const originalDiffHidden = invertLineRange(originalLineRange, this._session.textModel0); @@ -256,7 +242,7 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { } else { hiddenRanges = lineRanges.map(lineRangeAsRange); } - editor.setHiddenAreas(hiddenRanges, InlineChatLivePreviewWidget._hideId); + editor.setHiddenAreas(hiddenRanges, this._hideId); this._logService.debug(`[IE] diff HIDING ${hiddenRanges} for ${editor.getId()} with ${String(editor.getModel()?.uri)}`); } @@ -276,7 +262,7 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { 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 - 12 /* padding */)); + this._diffEditor.layout(this._dim.with(undefined, this._dim.height)); this._logService.debug('[IE] diff LAYOUT', this._dim); } } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 8cdcaa4998d..946a46f1251 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -3,15 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { equals, tail } from 'vs/base/common/arrays'; import { Event } from 'vs/base/common/event'; import { Lazy } from 'vs/base/common/lazy'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; -import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll'; import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; import { TextEdit } from 'vs/editor/common/languages'; import { ICursorStateComputer, IModelDecorationOptions, IModelDeltaDecoration, ITextModel, IValidEditOperation } from 'vs/editor/common/model'; @@ -242,7 +243,7 @@ export class LiveStrategy extends EditModeStrategy { @IStorageService protected _storageService: IStorageService, @IBulkEditService protected readonly _bulkEditService: IBulkEditService, @IEditorWorkerService protected readonly _editorWorkerService: IEditorWorkerService, - @IInstantiationService private readonly _instaService: IInstantiationService, + @IInstantiationService protected readonly _instaService: IInstantiationService, ) { super(); this._diffEnabled = configService.getValue('inlineChat.showDiff'); @@ -382,8 +383,9 @@ export class LiveStrategy extends EditModeStrategy { export class LivePreviewStrategy extends LiveStrategy { - private readonly _diffZone: Lazy; private readonly _previewZone: Lazy; + private readonly _diffZonePool: InlineChatLivePreviewWidget[] = []; + private _currentLineRangeGroups: DetailedLineRangeMapping[][] = []; constructor( session: Session, @@ -397,30 +399,82 @@ export class LivePreviewStrategy extends LiveStrategy { ) { super(session, editor, widget, configService, storageService, bulkEditService, editorWorkerService, instaService); - this._diffZone = new Lazy(() => instaService.createInstance(InlineChatLivePreviewWidget, editor, session)); this._previewZone = new Lazy(() => instaService.createInstance(InlineChatFileCreatePreviewWidget, editor)); } override dispose(): void { - this._diffZone.rawValue?.hide(); - this._diffZone.rawValue?.dispose(); + for (const zone of this._diffZonePool) { + zone.hide(); + zone.dispose(); + } this._previewZone.rawValue?.hide(); this._previewZone.rawValue?.dispose(); super.dispose(); } - override async renderProgressChanges(): Promise { - if (!this._diffZone.value.isVisible) { - this._diffZone.value.show(); + + private async _renderDiffZones() { + const diff = await this._editorWorkerService.computeDiff(this._session.textModel0.uri, this._session.textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: 5000, computeMoves: false }, 'advanced'); + if (!diff || diff.changes.length === 0) { + return; } + + const groups: DetailedLineRangeMapping[][] = []; + let group = [diff.changes[0]]; + groups.push(group); + + for (let i = 1; i < diff.changes.length; i++) { + const last = tail(group); + const next = diff.changes[i]; + + // when the distance between the two changes is less than 75% of the total number of lines changed + // they get merged into the same group + const treshold = Math.ceil((next.modified.length + last.modified.length) * .75); + if (next.modified.startLineNumber - last.modified.endLineNumberExclusive <= treshold) { + group.push(next); + } else { + group = [next]; + groups.push(group); + } + } + + const beforeAndNowAreEqual = equals(this._currentLineRangeGroups, groups, (groupA, groupB) => { + return equals(groupA, groupB, (mappingA, mappingB) => { + return mappingA.original.equals(mappingB.original) && mappingA.modified.equals(mappingB.modified); + }); + }); + + if (beforeAndNowAreEqual) { + return; + } + + this._currentLineRangeGroups = groups; + + const handleDiff = () => { + this._renderDiffZones(); + }; + + // create enough zones + while (groups.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 < groups.length; i++) { + this._diffZonePool[i].showForChanges(groups[i]); + } + // hide unused zones + for (let i = groups.length; i < this._diffZonePool.length; i++) { + this._diffZonePool[i].hide(); + } + } + + override async renderProgressChanges(): Promise { + return this._renderDiffZones(); } override async renderChanges(response: EditResponse) { this._updateSummaryMessage(); - if (this._diffEnabled) { - this._diffZone.value.show(); - } + await this._renderDiffZones(); if (response.singleCreateFileEdit) { this._previewZone.value.showCreation(this._session.wholeRange.value.collapseToEnd(), response.singleCreateFileEdit.uri, await Promise.all(response.singleCreateFileEdit.edits)); @@ -429,25 +483,12 @@ export class LivePreviewStrategy extends LiveStrategy { } } - protected override _doToggleDiff(): void { - const scrollState = StableEditorScrollState.capture(this._editor); - if (this._diffEnabled) { - this._diffZone.value.show(); - } else { - this._diffZone.value.hide(); - } - scrollState.restore(this._editor); - } - override hasFocus(): boolean { - return super.hasFocus() || Boolean(this._diffZone.rawValue?.hasFocus()) || Boolean(this._previewZone.rawValue?.hasFocus()); + return super.hasFocus() || Boolean(this._previewZone.rawValue?.hasFocus()) || this._diffZonePool.some(zone => zone.isVisible && zone.hasFocus()); } override getWidgetPosition(): Position | undefined { - if (this._session.lastTextModelChanges.length) { - return this._session.wholeRange.value.getStartPosition().delta(-1); - } - return this._session.wholeRange.value.getStartPosition().delta(-1); + return undefined; } } diff --git a/src/vs/workbench/contrib/inlineChat/browser/utils.ts b/src/vs/workbench/contrib/inlineChat/browser/utils.ts index 880c7778b11..a2a77ac6f2d 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/utils.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/utils.ts @@ -12,8 +12,12 @@ export function invertLineRange(range: LineRange, model: ITextModel): LineRange[ return []; } const result: LineRange[] = []; - result.push(new LineRange(1, range.startLineNumber)); - result.push(new LineRange(range.endLineNumberExclusive, model.getLineCount() + 1)); + if (range.startLineNumber > 1) { + result.push(new LineRange(1, range.startLineNumber)); + } + if (range.endLineNumberExclusive < model.getLineCount() + 1) { + result.push(new LineRange(range.endLineNumberExclusive, model.getLineCount() + 1)); + } return result.filter(r => !r.isEmpty); } From 8c810ad98d21be2184dd3e9233d5c91f5ff102c2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Oct 2023 11:42:59 +0200 Subject: [PATCH 43/55] chat - relayout when input toolbar changes (#195877) --- .../workbench/contrib/chat/browser/chatInputPart.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 7576aadb922..e81c2705d7a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -79,6 +79,9 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge private inputEditorHasText: IContextKey; private providerId: string | undefined; + private cachedDimensions: dom.Dimension | undefined; + private cachedToolbarWidth: number | undefined; + readonly inputUri = URI.parse(`${ChatInputPart.INPUT_SCHEME}:input-${ChatInputPart._counter++}`); constructor( @@ -251,6 +254,11 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge })); this.toolbar.getElement().classList.add('interactive-execute-toolbar'); this.toolbar.context = { widget }; + this._register(this.toolbar.onDidChangeMenuItems(() => { + if (this.cachedDimensions && typeof this.cachedToolbarWidth === 'number' && this.cachedToolbarWidth !== this.toolbar.getItemsWidth()) { + this.layout(this.cachedDimensions.height, this.cachedDimensions.width); + } + })); if (this.options.renderStyle === 'compact') { const toolbarSide = this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, inputAndSideToolbar, MenuId.ChatInputSide, { @@ -285,6 +293,8 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge } layout(height: number, width: number): number { + this.cachedDimensions = new dom.Dimension(width, height); + return this._layout(height, width); } @@ -301,7 +311,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const editorBorder = 2; const editorPadding = 8; - const executeToolbarWidth = this.toolbar.getItemsWidth(); + const executeToolbarWidth = this.cachedToolbarWidth = this.toolbar.getItemsWidth(); const sideToolbarWidth = this.options.renderStyle === 'compact' ? 20 : 0; const initialEditorScrollWidth = this._inputEditor.getScrollWidth(); From f6e7767e8993f369f3ae467a3ea29a5134a79d79 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 18 Oct 2023 11:56:12 +0200 Subject: [PATCH 44/55] tweak position and margin --- .../browser/inlineChatController.ts | 2 +- .../browser/inlineChatStrategies.ts | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 5f6b0882d69..c5d59349076 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -687,7 +687,6 @@ export class InlineChatController implements IEditorContribution { assertType(this._strategy); const { response } = this._activeSession.lastExchange!; - this._showWidget(false); let status: string | undefined; @@ -751,6 +750,7 @@ export class InlineChatController implements IEditorContribution { await this._strategy.renderChanges(response); } this._chatAccessibilityService.acceptResponse(status); + this._showWidget(false); return State.WAIT_FOR_INPUT; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 946a46f1251..4fa8470f957 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -361,19 +361,11 @@ export class LiveStrategy extends EditModeStrategy { } override getWidgetPosition(): Position | undefined { - const lastTextModelChanges = this._session.lastTextModelChanges; - let lastLineOfLocalEdits: number | undefined; - for (const change of lastTextModelChanges) { - const changeEndLineNumber = change.modified.endLineNumberExclusive - 1; - if (typeof lastLineOfLocalEdits === 'undefined' || lastLineOfLocalEdits < changeEndLineNumber) { - lastLineOfLocalEdits = changeEndLineNumber; - } - } - return lastLineOfLocalEdits ? new Position(lastLineOfLocalEdits, 1) : undefined; + return undefined; } override needsMargin(): boolean { - return !Boolean(this._session.lastTextModelChanges.length); + return true; } hasFocus(): boolean { @@ -488,6 +480,13 @@ export class LivePreviewStrategy extends LiveStrategy { } override getWidgetPosition(): Position | undefined { + for (let i = this._diffZonePool.length - 1; i >= 0; i--) { + const zone = this._diffZonePool[i]; + if (zone.isVisible && zone.position) { + // above last view zone + return zone.position.delta(-1); + } + } return undefined; } } From e47adcbf4bfdbe76aa6240d7186f2723d236d066 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Wed, 18 Oct 2023 12:07:15 +0200 Subject: [PATCH 45/55] Adapt context menu and commands --- .../browser/actions/layoutActions.ts | 99 +++++++++++++------ .../parts/editor/editor.contribution.ts | 7 +- 2 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index d4387fb9ea5..a1024ebff97 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -470,49 +470,87 @@ export class ToggleStatusbarVisibilityAction extends Action2 { registerAction2(ToggleStatusbarVisibilityAction); -// --- Base Class Toggle Boolean Setting Action +// --- Hide Editor Tabs -abstract class BaseToggleBooleanSettingAction extends Action2 { +export class HideEditorTabsAction extends Action2 { - protected abstract get settingId(): string; - - override run(accessor: ServicesAccessor): Promise { - const configurationService = accessor.get(IConfigurationService); - - const oldettingValue = configurationService.getValue(this.settingId); - const newSettingValue = !oldettingValue; - - return configurationService.updateValue(this.settingId, newSettingValue); - } -} - -// --- Toggle Tabs Visibility - -export class ToggleTabsVisibilityAction extends BaseToggleBooleanSettingAction { - - static readonly ID = 'workbench.action.toggleTabsVisibility'; + static readonly ID = 'workbench.action.hideEditorTabs'; constructor() { super({ - id: ToggleTabsVisibilityAction.ID, + id: HideEditorTabsAction.ID, title: { - value: localize('toggleTabs', "Toggle Editor Tab Visibility"), - original: 'Toggle Editor Tab Visibility' + value: localize('hideEditorTabs', "Hide Editor Tabs"), + original: 'Hide Editor Tabs' }, category: Categories.View, + precondition: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none').negate(), f1: true }); } - protected override get settingId(): string { - return 'workbench.editor.showTabs'; + override run(accessor: ServicesAccessor): Promise { + const configurationService = accessor.get(IConfigurationService); + return configurationService.updateValue('workbench.editor.showTabs', 'none'); } } -registerAction2(ToggleTabsVisibilityAction); +registerAction2(HideEditorTabsAction); + +// --- Show Multiple Editor Tabs + +export class ShowMultipleEditorTabsAction extends Action2 { + + static readonly ID = 'workbench.action.showMultipleEditorTabs'; + + constructor() { + super({ + id: ShowMultipleEditorTabsAction.ID, + title: { + value: localize('showMultipleEditorTabs', "Show Multiple Editor Tabs"), + original: 'Show Multiple Editor Tabs' + }, + category: Categories.View, + precondition: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'multiple').negate(), + f1: true + }); + } + + override run(accessor: ServicesAccessor): Promise { + const configurationService = accessor.get(IConfigurationService); + return configurationService.updateValue('workbench.editor.showTabs', 'multiple'); + } +} +registerAction2(ShowMultipleEditorTabsAction); + +// --- Show Single Editor Tab + +export class ShowSingleEditorTabAction extends Action2 { + + static readonly ID = 'workbench.action.showEditorTab'; + + constructor() { + super({ + id: ShowSingleEditorTabAction.ID, + title: { + value: localize('showSingleEditorTab', "Show Single Editor Tab"), + original: 'Show Single Editor Tab' + }, + category: Categories.View, + precondition: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'single').negate(), + f1: true + }); + } + + override run(accessor: ServicesAccessor): Promise { + const configurationService = accessor.get(IConfigurationService); + return configurationService.updateValue('workbench.editor.showTabs', 'single'); + } +} +registerAction2(ShowSingleEditorTabAction); // --- Toggle Pinned Tabs On Separate Row -export class ToggleSeparatePinnedTabsAction extends BaseToggleBooleanSettingAction { +export class ToggleSeparatePinnedTabsAction extends Action2 { static readonly ID = 'workbench.action.toggleSeparatePinnedEditorTabs'; @@ -529,8 +567,13 @@ export class ToggleSeparatePinnedTabsAction extends BaseToggleBooleanSettingActi }); } - protected override get settingId(): string { - return 'workbench.editor.pinnedTabsOnSeparateRow'; + override run(accessor: ServicesAccessor): Promise { + const configurationService = accessor.get(IConfigurationService); + + const oldettingValue = configurationService.getValue('workbench.editor.pinnedTabsOnSeparateRow'); + const newSettingValue = !oldettingValue; + + return configurationService.updateValue('workbench.editor.pinnedTabsOnSeparateRow', newSettingValue); } } registerAction2(ToggleSeparatePinnedTabsAction); diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 18cbd10b7d3..cca074fe569 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -66,7 +66,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { UntitledTextEditorInputSerializer, UntitledTextEditorWorkingCopyEditorHandler } from 'vs/workbench/services/untitled/common/untitledTextEditorHandler'; import { DynamicEditorConfigurations } from 'vs/workbench/browser/parts/editor/editorConfiguration'; -import { ToggleSeparatePinnedTabsAction, ToggleTabsVisibilityAction } from 'vs/workbench/browser/actions/layoutActions'; +import { HideEditorTabsAction, ShowMultipleEditorTabsAction, ShowSingleEditorTabAction, ToggleSeparatePinnedTabsAction } from 'vs/workbench/browser/actions/layoutActions'; import product from 'vs/platform/product/common/product'; //#region Editor Registrations @@ -355,7 +355,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_ MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_DOWN, title: localize('splitDown', "Split Down") }, group: '2_split', order: 20 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_LEFT, title: localize('splitLeft', "Split Left") }, group: '2_split', order: 30 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_RIGHT, title: localize('splitRight', "Split Right") }, group: '2_split', order: 40 }); -MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: ToggleTabsVisibilityAction.ID, title: localize('toggleTabs', "Editor Tabs"), toggled: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'multiple') }, group: '3_config', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: ShowSingleEditorTabAction.ID, title: localize('showSingleTab', "Show Single Tab") }, group: '3_config', order: 10, when: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'multiple') }); +MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: HideEditorTabsAction.ID, title: localize('hideTabBar', "Hide Tab Bar") }, group: '3_config', order: 15, when: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none').negate() }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: ToggleSeparatePinnedTabsAction.ID, title: localize('toggleSeparatePinnedEditorTabs', "Separate Pinned Editor Tabs"), toggled: ContextKeyExpr.has('config.workbench.editor.pinnedTabsOnSeparateRow') }, when: EditorPinnedAndUnpinnedTabsContext, group: '3_config', order: 20 }); // Editor Title Context Menu @@ -374,6 +375,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_ED MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR_RIGHT, title: localize('splitRight', "Split Right") }, group: '5_split', order: 40 }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR_IN_GROUP, title: localize('splitInGroup', "Split in Group") }, group: '6_split_in_group', order: 10, when: ActiveEditorCanSplitInGroupContext }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: JOIN_EDITOR_IN_GROUP, title: localize('joinInGroup', "Join in Group") }, group: '6_split_in_group', order: 10, when: SideBySideEditorActiveContext }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: ShowMultipleEditorTabsAction.ID, title: localize('showMultipleTabs', "Show Multiple Tabs") }, group: '7_config', order: 10, when: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'single') }); +MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: HideEditorTabsAction.ID, title: localize('hideTabBar', "Hide Tab Bar") }, group: '7_config', order: 20, when: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none').negate() }); // Editor Title Menu MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_DIFF_SIDE_BY_SIDE, title: localize('inlineView', "Inline View"), toggled: ContextKeyExpr.equals('config.diffEditor.renderSideBySide', false) }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') }); From 303a246f0655b5108ea6e5aeeb9f694affabba7d Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 18 Oct 2023 12:12:31 +0200 Subject: [PATCH 46/55] remove `Session#lastTextModelChanges` because it is often stale. Instead compute on demand --- .../inlineChat/browser/inlineChatActions.ts | 6 +-- .../browser/inlineChatController.ts | 43 ++++++++----------- .../inlineChat/browser/inlineChatSession.ts | 17 ++------ .../browser/inlineChatStrategies.ts | 14 +++--- .../inlineChat/browser/inlineChatWidget.ts | 31 +++++++------ .../test/browser/inlineChatController.test.ts | 8 ++-- 6 files changed, 52 insertions(+), 67 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 03802665d4b..2f2cb558da9 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -331,7 +331,7 @@ export class DiscardAction extends AbstractInlineChatAction { } async runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController, _editor: ICodeEditor, ..._args: any[]): Promise { - ctrl.cancelSession(); + await ctrl.cancelSession(); } } @@ -357,7 +357,7 @@ export class DiscardToClipboardAction extends AbstractInlineChatAction { override async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController): Promise { const clipboardService = accessor.get(IClipboardService); - const changedText = ctrl.cancelSession(); + const changedText = await ctrl.cancelSession(); if (changedText !== undefined) { clipboardService.writeText(changedText); } @@ -381,7 +381,7 @@ export class DiscardUndoToNewFileAction extends AbstractInlineChatAction { override async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController, editor: ICodeEditor, ..._args: any[]): Promise { const editorService = accessor.get(IEditorService); - const changedText = ctrl.cancelSession(); + const changedText = await ctrl.cancelSession(); if (changedText !== undefined) { const input: IUntitledTextResourceEditorInput = { forceUntitled: true, resource: undefined, contents: changedText, languageId: editor.getModel()?.getLanguageId() }; editorService.openEditor(input, SIDE_GROUP); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index c5d59349076..5ca18ef7a67 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -15,9 +15,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; -import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; -import { IModelService } from 'vs/editor/common/services/model'; import { InlineCompletionsController } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController'; import { localize } from 'vs/nls'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; @@ -129,7 +127,6 @@ export class InlineChatController implements IEditorContribution { @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, @ILogService private readonly _logService: ILogService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IModelService private readonly _modelService: IModelService, @IDialogService private readonly _dialogService: IDialogService, @IContextKeyService contextKeyService: IContextKeyService, @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, @@ -572,7 +569,7 @@ export class InlineChatController implements IEditorContribution { throw new Error('Progress in NOT supported in non-live mode'); } progressEdits.push(data.edits); - await this._makeChanges(progressEdits, false); + await this._makeChanges(data.edits, false); await this._strategy?.renderProgressChanges(); } }); @@ -594,8 +591,9 @@ export class InlineChatController implements IEditorContribution { response = new MarkdownResponse(this._activeSession.textModelN.uri, reply); } else if (reply) { const editResponse = new EditResponse(this._activeSession.textModelN.uri, this._activeSession.textModelN.getAlternativeVersionId(), reply, progressEdits); - if (editResponse.allLocalEdits.length > progressEdits.length) { - await this._makeChanges(editResponse.allLocalEdits, true); + const offset = editResponse.allLocalEdits.length - progressEdits.length; + for (let i = offset; i < editResponse.allLocalEdits.length; i++) { + await this._makeChanges(editResponse.allLocalEdits[i], true); } response = editResponse; } else { @@ -648,26 +646,11 @@ export class InlineChatController implements IEditorContribution { return State.SHOW_RESPONSE; } - private async _makeChanges(allEdits: TextEdit[][], computeMoreMinimalEdits: boolean) { + private async _makeChanges(lastEdits: TextEdit[], computeMoreMinimalEdits: boolean) { assertType(this._activeSession); assertType(this._strategy); - if (allEdits.length === 0) { - return; - } - - // diff-changes from model0 -> modelN+1 - { - const lastEdits = allEdits[allEdits.length - 1]; - const textModelNplus1 = this._modelService.createModel(createTextBufferFactoryFromSnapshot(this._activeSession.textModelN.createSnapshot()), null, undefined, true); - textModelNplus1.applyEdits(lastEdits.map(TextEdit.asEditOperation)); - const diff = await this._editorWorkerService.computeDiff(this._activeSession.textModel0.uri, textModelNplus1.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: 5000, computeMoves: false }, 'advanced'); - this._activeSession.lastTextModelChanges = diff?.changes ?? []; - textModelNplus1.dispose(); - } - // make changes from modelN -> modelN+1 - const lastEdits = allEdits[allEdits.length - 1]; const moreMinimalEdits = computeMoreMinimalEdits ? await this._editorWorkerService.computeHumanReadableDiff(this._activeSession.textModelN.uri, lastEdits) : undefined; const editOperations = (moreMinimalEdits ?? lastEdits).map(TextEdit.asEditOperation); this._log('edits from PROVIDER and after making them MORE MINIMAL', this._activeSession.provider.debugName, lastEdits, moreMinimalEdits); @@ -909,11 +892,19 @@ export class InlineChatController implements IEditorContribution { this._messages.fire(Message.ACCEPT_SESSION); } - cancelSession() { - const result = this._activeSession?.asChangedText(); - if (this._activeSession?.lastExchange && InlineChatController.isEditOrMarkdownResponse(this._activeSession.lastExchange.response)) { - this._activeSession.provider.handleInlineChatResponseFeedback?.(this._activeSession.session, this._activeSession.lastExchange.response.raw, InlineChatResponseFeedbackKind.Undone); + async cancelSession() { + + let result: string | undefined; + if (this._activeSession) { + + const diff = await this._editorWorkerService.computeDiff(this._activeSession.textModel0.uri, this._activeSession.textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: 5000, computeMoves: false }, 'advanced'); + result = this._activeSession.asChangedText(diff?.changes ?? []); + + if (this._activeSession.lastExchange && InlineChatController.isEditOrMarkdownResponse(this._activeSession.lastExchange.response)) { + this._activeSession.provider.handleInlineChatResponseFeedback?.(this._activeSession.session, this._activeSession.lastExchange.response.raw, InlineChatResponseFeedbackKind.Undone); + } } + this._messages.fire(Message.CANCEL_SESSION); return result; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index 8de79727cea..b5daf7c9c27 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -23,9 +23,9 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Iterable } from 'vs/base/common/iterator'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { isCancellationError } from 'vs/base/common/errors'; -import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { raceCancellation } from 'vs/base/common/async'; +import { LineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; export type Recording = { when: Date; @@ -112,7 +112,6 @@ export class Session { private _lastInput: SessionPrompt | undefined; private _lastExpansionState: ExpansionState | undefined; - private _lastTextModelChanges: readonly DetailedLineRangeMapping[] | undefined; private _isUnstashed: boolean = false; private readonly _exchange: SessionExchange[] = []; private readonly _startTime = new Date(); @@ -187,26 +186,18 @@ export class Session { return this._exchange[this._exchange.length - 1]; } - get lastTextModelChanges() { - return this._lastTextModelChanges ?? []; - } - - set lastTextModelChanges(changes: readonly DetailedLineRangeMapping[]) { - this._lastTextModelChanges = changes; - } - get hasChangedText(): boolean { return !this.textModel0.equalsTextBuffer(this.textModelN.getTextBuffer()); } - asChangedText(): string | undefined { - if (!this._lastTextModelChanges || this._lastTextModelChanges.length === 0) { + asChangedText(changes: readonly LineRangeMapping[]): string | undefined { + if (changes.length === 0) { return undefined; } let startLine = Number.MAX_VALUE; let endLine = Number.MIN_VALUE; - for (const change of this._lastTextModelChanges) { + for (const change of changes) { startLine = Math.min(startLine, change.modified.startLineNumber); endLine = Math.max(endLine, change.modified.endLineNumberExclusive); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 4fa8470f957..9821f36e6a3 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -12,7 +12,7 @@ import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; -import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { DetailedLineRangeMapping, LineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; import { TextEdit } from 'vs/editor/common/languages'; import { ICursorStateComputer, IModelDecorationOptions, IModelDeltaDecoration, ITextModel, IValidEditOperation } from 'vs/editor/common/model'; @@ -133,7 +133,7 @@ export class PreviewStrategy extends EditModeStrategy { override async renderChanges(response: EditResponse): Promise { if (response.allLocalEdits.length > 0) { const allEditOperation = response.allLocalEdits.map(edits => edits.map(TextEdit.asEditOperation)); - this._widget.showEditsPreview(this._session.textModel0, allEditOperation, this._session.lastTextModelChanges); + await this._widget.showEditsPreview(this._session.textModel0, this._session.textModelN, allEditOperation); } else { this._widget.hideEditsPreview(); } @@ -327,9 +327,9 @@ export class LiveStrategy extends EditModeStrategy { } override async renderChanges(response: EditResponse) { - + const diff = await this._editorWorkerService.computeDiff(this._session.textModel0.uri, this._session.textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: 5000, computeMoves: false }, 'advanced'); + this._updateSummaryMessage(diff?.changes ?? []); this._inlineDiffDecorations.update(); - this._updateSummaryMessage(); if (response.singleCreateFileEdit) { this._widget.showCreatePreview(response.singleCreateFileEdit.uri, await Promise.all(response.singleCreateFileEdit.edits)); @@ -344,9 +344,9 @@ export class LiveStrategy extends EditModeStrategy { } } - protected _updateSummaryMessage() { + protected _updateSummaryMessage(mappings: readonly LineRangeMapping[]) { let linesChanged = 0; - for (const change of this._session.lastTextModelChanges) { + for (const change of mappings) { linesChanged += change.changedLineCount; } let message: string; @@ -440,6 +440,7 @@ export class LivePreviewStrategy extends LiveStrategy { return; } + this._updateSummaryMessage(diff.changes); this._currentLineRangeGroups = groups; const handleDiff = () => { @@ -465,7 +466,6 @@ export class LivePreviewStrategy extends LiveStrategy { override async renderChanges(response: EditResponse) { - this._updateSummaryMessage(); await this._renderDiffZones(); if (response.singleCreateFileEdit) { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index db86fea8ea0..a4223bb8d8d 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -36,7 +36,6 @@ import { FileKind } from 'vs/platform/files/common/files'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { LanguageSelector } from 'vs/editor/common/languageSelector'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; -import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { invertLineRange, lineRangeAsRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; import { LineRange } from 'vs/editor/common/core/lineRange'; @@ -62,6 +61,7 @@ import { MenuId } from 'vs/platform/actions/common/actions'; import { editorForeground, inputBackground, editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { CodeBlockPart } from 'vs/workbench/contrib/chat/browser/codeBlockPart'; import { Lazy } from 'vs/base/common/lazy'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; const defaultAriaLabel = localize('aria-label', "Inline Chat Input"); @@ -215,7 +215,8 @@ export class InlineChatWidget { @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IContextMenuService private readonly _contextMenuService: IContextMenuService, - @IAccessibleViewService private readonly _accessibleViewService: IAccessibleViewService + @IAccessibleViewService private readonly _accessibleViewService: IAccessibleViewService, + @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService ) { // input editor logic @@ -664,27 +665,29 @@ export class InlineChatWidget { // --- preview - showEditsPreview(textModelv0: ITextModel, allEdits: ISingleEditOperation[][], changes: readonly DetailedLineRangeMapping[]) { - if (changes.length === 0) { + async showEditsPreview(textModel0: ITextModel, textModelN: ITextModel, allEdits: ISingleEditOperation[][]) { + + const diff = await this._editorWorkerService.computeDiff(textModel0.uri, textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: 5000, computeMoves: false }, 'advanced'); + if (!diff || diff.changes.length === 0) { this.hideEditsPreview(); return; } this._elements.previewDiff.classList.remove('hidden'); - const languageSelection: ILanguageSelection = { languageId: textModelv0.getLanguageId(), onDidChange: Event.None }; - const modified = this._modelService.createModel(createTextBufferFactoryFromSnapshot(textModelv0.createSnapshot()), languageSelection, undefined, true); + const languageSelection: ILanguageSelection = { languageId: textModel0.getLanguageId(), onDidChange: Event.None }; + const modified = this._modelService.createModel(createTextBufferFactoryFromSnapshot(textModel0.createSnapshot()), languageSelection, undefined, true); for (const edits of allEdits) { modified.applyEdits(edits, false); } - this._previewDiffEditor.value.setModel({ original: textModelv0, modified }); + this._previewDiffEditor.value.setModel({ original: textModel0, modified }); // joined ranges - let originalLineRange = changes[0].original; - let modifiedLineRange = changes[0].modified; - for (let i = 1; i < changes.length; i++) { - originalLineRange = originalLineRange.join(changes[i].original); - modifiedLineRange = modifiedLineRange.join(changes[i].modified); + let originalLineRange = diff.changes[0].original; + let modifiedLineRange = diff.changes[0].modified; + for (let i = 1; i < diff.changes.length; i++) { + originalLineRange = originalLineRange.join(diff.changes[i].original); + modifiedLineRange = modifiedLineRange.join(diff.changes[i].modified); } // apply extra padding @@ -695,10 +698,10 @@ export class InlineChatWidget { const newEndLineModified = Math.min(modifiedLineRange.endLineNumberExclusive + pad, modified.getLineCount()); modifiedLineRange = new LineRange(modifiedLineRange.startLineNumber, newEndLineModified); - const newEndLineOriginal = Math.min(originalLineRange.endLineNumberExclusive + pad, textModelv0.getLineCount()); + const newEndLineOriginal = Math.min(originalLineRange.endLineNumberExclusive + pad, textModel0.getLineCount()); originalLineRange = new LineRange(originalLineRange.startLineNumber, newEndLineOriginal); - const hiddenOriginal = invertLineRange(originalLineRange, textModelv0); + const hiddenOriginal = invertLineRange(originalLineRange, textModel0); const hiddenModified = invertLineRange(modifiedLineRange, modified); this._previewDiffEditor.value.getOriginalEditor().setHiddenAreas(hiddenOriginal.map(lineRangeAsRange), 'diff-hidden'); this._previewDiffEditor.value.getModifiedEditor().setHiddenAreas(hiddenModified.map(lineRangeAsRange), 'diff-hidden'); 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 bb25fb4e308..822c5f8064c 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -173,7 +173,7 @@ suite('InteractiveChatController', function () { const run = ctrl.run({ message: 'Hello', autoSend: true }); await p; assert.ok(ctrl.getWidgetPosition() !== undefined); - ctrl.cancelSession(); + await ctrl.cancelSession(); await run; @@ -205,7 +205,7 @@ suite('InteractiveChatController', function () { assert.ok(session); assert.deepStrictEqual(session.wholeRange.value, new Range(1, 1, 1, 6)); - ctrl.cancelSession(); + await ctrl.cancelSession(); d.dispose(); }); @@ -235,7 +235,7 @@ suite('InteractiveChatController', function () { assert.ok(session); assert.deepStrictEqual(session.wholeRange.value, new Range(1, 1, 1, 6)); - ctrl.cancelSession(); + await ctrl.cancelSession(); d.dispose(); }); @@ -298,7 +298,7 @@ suite('InteractiveChatController', function () { assert.deepStrictEqual(session.wholeRange.value, new Range(1, 1, 4, 12)); - ctrl.cancelSession(); + await ctrl.cancelSession(); await r; }); From 6fa270070d87361f903214b131239bc2d499a4bb Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 18 Oct 2023 12:24:02 +0200 Subject: [PATCH 47/55] fix final edit --- .../contrib/inlineChat/browser/inlineChatController.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 5ca18ef7a67..1ec6178f510 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -591,8 +591,7 @@ export class InlineChatController implements IEditorContribution { response = new MarkdownResponse(this._activeSession.textModelN.uri, reply); } else if (reply) { const editResponse = new EditResponse(this._activeSession.textModelN.uri, this._activeSession.textModelN.getAlternativeVersionId(), reply, progressEdits); - const offset = editResponse.allLocalEdits.length - progressEdits.length; - for (let i = offset; i < editResponse.allLocalEdits.length; i++) { + for (let i = progressEdits.length; i < editResponse.allLocalEdits.length; i++) { await this._makeChanges(editResponse.allLocalEdits[i], true); } response = editResponse; From cdc2850a7292dbfe4004c02f2de7f40f0c5807d1 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 18 Oct 2023 12:29:25 +0200 Subject: [PATCH 48/55] polish badge (#195890) --- src/vs/workbench/browser/parts/media/paneCompositePart.css | 4 ++-- .../workbench/browser/parts/titlebar/media/titlebarpart.css | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/media/paneCompositePart.css b/src/vs/workbench/browser/parts/media/paneCompositePart.css index 0006a43986d..b30eba09a97 100644 --- a/src/vs/workbench/browser/parts/media/paneCompositePart.css +++ b/src/vs/workbench/browser/parts/media/paneCompositePart.css @@ -172,12 +172,12 @@ .monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.icon .badge.compact .badge-content { position: absolute; - top: 11px; + top: 12px; right: 0px; font-size: 9px; font-weight: 600; min-width: 12px; - height: 12px; + height: 11px; padding: 0 2px; border-radius: 16px; text-align: center; diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 5109fdeaf92..a9f73b33351 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -444,12 +444,12 @@ .monaco-workbench .part.titlebar > .titlebar-container > .titlebar-right > .global-actions-container .monaco-action-bar .action-item.icon .badge.compact .badge-content { position: absolute; - top: 10px; + top: 11px; right: 0px; font-size: 9px; font-weight: 600; min-width: 12px; - height: 12px; + height: 11px; padding: 0 2px; border-radius: 16px; text-align: center; From e240d95168b8ec29a4deaf096aca1ad86d612fe6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Oct 2023 12:40:26 +0200 Subject: [PATCH 49/55] windows - add and use `IBaseWindow` (#195896) --- src/vs/platform/window/electron-main/window.ts | 7 +++++-- src/vs/platform/windows/electron-main/windowImpl.ts | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/window/electron-main/window.ts b/src/vs/platform/window/electron-main/window.ts index 8330d6f39a5..379bdd161f6 100644 --- a/src/vs/platform/window/electron-main/window.ts +++ b/src/vs/platform/window/electron-main/window.ts @@ -13,7 +13,11 @@ import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataPro import { INativeWindowConfiguration } from 'vs/platform/window/common/window'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; -export interface ICodeWindow extends IDisposable { +export interface IBaseWindow extends IDisposable { + focus(options?: { force: boolean }): void; +} + +export interface ICodeWindow extends IBaseWindow { readonly onWillLoad: Event; readonly onDidSignalReady: Event; @@ -49,7 +53,6 @@ export interface ICodeWindow extends IDisposable { load(config: INativeWindowConfiguration, options?: { isReload?: boolean }): void; reload(cli?: NativeParsedArgs): void; - focus(options?: { force: boolean }): void; close(): void; getBounds(): Rectangle; diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index da1c92d03b6..32168a31b7e 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -35,7 +35,7 @@ import { getMenuBarVisibility, getTitleBarStyle, IFolderToOpen, INativeWindowCon import { defaultBrowserWindowOptions, IWindowsMainService, OpenContext } from 'vs/platform/windows/electron-main/windows'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, toWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; -import { IWindowState, ICodeWindow, ILoadEvent, WindowMode, WindowError, LoadReason, defaultWindowState } from 'vs/platform/window/electron-main/window'; +import { IWindowState, ICodeWindow, ILoadEvent, WindowMode, WindowError, LoadReason, defaultWindowState, IBaseWindow } from 'vs/platform/window/electron-main/window'; import { Color } from 'vs/base/common/color'; import { IPolicyService } from 'vs/platform/policy/common/policy'; import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; @@ -82,7 +82,7 @@ const enum ReadyState { READY } -export abstract class BaseWindow extends Disposable { +export abstract class BaseWindow extends Disposable implements IBaseWindow { protected abstract getWin(): BrowserWindow | null; From a61980d2b033ab0185471c74cc062c580c82d296 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 18 Oct 2023 14:38:46 +0200 Subject: [PATCH 50/55] - always use `computeMoreMinimalEdits` to reduce flicker for empty edits - don't use automatic inline diff'ing for predictable heights - fix widget positioning off by one --- .../contrib/inlineChat/browser/inlineChatController.ts | 9 +++++++-- .../inlineChat/browser/inlineChatLivePreviewWidget.ts | 1 + .../contrib/inlineChat/browser/inlineChatStrategies.ts | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 1ec6178f510..bb504a95533 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -569,7 +569,7 @@ export class InlineChatController implements IEditorContribution { throw new Error('Progress in NOT supported in non-live mode'); } progressEdits.push(data.edits); - await this._makeChanges(data.edits, false); + await this._makeChanges(data.edits, true); await this._strategy?.renderProgressChanges(); } }); @@ -650,10 +650,15 @@ export class InlineChatController implements IEditorContribution { assertType(this._strategy); // make changes from modelN -> modelN+1 - const moreMinimalEdits = computeMoreMinimalEdits ? await this._editorWorkerService.computeHumanReadableDiff(this._activeSession.textModelN.uri, lastEdits) : undefined; + const moreMinimalEdits = computeMoreMinimalEdits ? await this._editorWorkerService.computeMoreMinimalEdits(this._activeSession.textModelN.uri, lastEdits) : undefined; const editOperations = (moreMinimalEdits ?? lastEdits).map(TextEdit.asEditOperation); this._log('edits from PROVIDER and after making them MORE MINIMAL', this._activeSession.provider.debugName, lastEdits, moreMinimalEdits); + if (editOperations.length === 0) { + // nothing left to do + return; + } + try { this._ignoreModelContentChanged = true; this._activeSession.wholeRange.trackEdits(editOperations); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts index 11b1281498c..005d0216fb0 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts @@ -80,6 +80,7 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { stickyScroll: { enabled: false }, minimap: { enabled: false }, isInEmbeddedEditor: true, + useInlineViewWhenSpaceIsLimited: false, overflowWidgetsDomNode: editor.getOverflowWidgetsDomNode(), onlyShowAccessibleDiffViewer: this.accessibilityService.isScreenReaderOptimized(), }, { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 9821f36e6a3..d47b03f78c1 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -484,7 +484,7 @@ export class LivePreviewStrategy extends LiveStrategy { const zone = this._diffZonePool[i]; if (zone.isVisible && zone.position) { // above last view zone - return zone.position.delta(-1); + return zone.position; } } return undefined; From 45dda0e1daab6dc9d84ea210b29e9142dca37d4f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Oct 2023 14:42:02 +0200 Subject: [PATCH 51/55] debt - remove old style voice transcriber (#195883) --- build/gulpfile.vscode.js | 1 - build/lib/i18n.resources.json | 4 - src/vs/code/electron-main/app.ts | 12 - .../sharedProcess/contrib/voiceTranscriber.ts | 212 ------------------ .../node/sharedProcess/sharedProcessMain.ts | 8 +- .../electron-main/sharedProcess.ts | 7 +- .../node/voiceRecognitionService.ts | 81 ------- .../voiceTranscriptionWorklet.ts | 127 ----------- .../workbenchVoiceRecognitionService.ts | 208 ----------------- src/vs/workbench/workbench.desktop.main.ts | 1 - 10 files changed, 3 insertions(+), 658 deletions(-) delete mode 100644 src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts delete mode 100644 src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts delete mode 100644 src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts delete mode 100644 src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 2d2451c28e8..4ae98a95577 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -73,7 +73,6 @@ const vscodeResources = [ 'out-build/vs/workbench/contrib/terminal/browser/media/*.sh', 'out-build/vs/workbench/contrib/terminal/browser/media/*.zsh', 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', - 'out-build/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.js', 'out-build/vs/**/markdown.css', 'out-build/vs/workbench/contrib/tasks/**/*.json', '!**/test/**' diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 2d03fbd5712..65f37d5e283 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -82,10 +82,6 @@ "name": "vs/workbench/services/assignment", "project": "vscode-workbench" }, - { - "name": "vs/workbench/services/voiceRecognition", - "project": "vscode-workbench" - }, { "name": "vs/workbench/contrib/extensions", "project": "vscode-workbench" diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index fb758311a1a..147ea68d2ab 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -165,10 +165,6 @@ export class CodeApplication extends Disposable { const isUrlFromWebview = (requestingUrl: string | undefined) => requestingUrl?.startsWith(`${Schemas.vscodeWebview}://`); - const allowedPermissionsInMainFrame = new Set( - this.productService.quality === 'stable' ? [] : ['media'] - ); - const allowedPermissionsInWebview = new Set([ 'clipboard-read', 'clipboard-sanitized-write', @@ -179,10 +175,6 @@ export class CodeApplication extends Disposable { return callback(allowedPermissionsInWebview.has(permission)); } - if (details.isMainFrame && details.securityOrigin === `${Schemas.vscodeFileResource}://${VSCODE_AUTHORITY}/`) { - return callback(allowedPermissionsInMainFrame.has(permission)); - } - return callback(false); }); @@ -191,10 +183,6 @@ export class CodeApplication extends Disposable { return allowedPermissionsInWebview.has(permission); } - if (details.isMainFrame && details.securityOrigin === `${Schemas.vscodeFileResource}://${VSCODE_AUTHORITY}/`) { - return allowedPermissionsInMainFrame.has(permission); - } - return false; }); diff --git a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts deleted file mode 100644 index 6f29b8d3a23..00000000000 --- a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts +++ /dev/null @@ -1,212 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Event } from 'vs/base/common/event'; -import { MessagePortMain, MessageEvent } from 'vs/base/parts/sandbox/node/electronTypes'; -import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/node/voiceRecognitionService'; -import { ILogService } from 'vs/platform/log/common/log'; -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { LimitedQueue, Queue } from 'vs/base/common/async'; - -export class VoiceTranscriptionManager extends Disposable { - - private static USE_SLIDING_WINDOW = !!process.env.VSCODE_VOICE_USE_SLIDING_WINDOW; - - constructor( - private readonly onDidWindowConnectRaw: Event, - @IVoiceRecognitionService private readonly voiceRecognitionService: IVoiceRecognitionService, - @ILogService private readonly logService: ILogService - ) { - super(); - - this.registerListeners(); - } - - private registerListeners(): void { - this._register(this.onDidWindowConnectRaw(port => { - this.logService.info(`[voice] transcriber: new connection (sliding window: ${VoiceTranscriptionManager.USE_SLIDING_WINDOW})`); - - if (VoiceTranscriptionManager.USE_SLIDING_WINDOW) { - this._register(new SlidingWindowVoiceTranscriber(port, this.voiceRecognitionService, this.logService)); - } else { - this._register(new FullWindowVoiceTranscriber(port, this.voiceRecognitionService, this.logService)); - } - })); - } -} - -abstract class VoiceTranscriber extends Disposable { - - protected static MAX_DATA_LENGTH = 30 /* seconds */ * 16000 /* sampling rate */ * 16 /* bith depth */ * 1 /* channels */ / 8; - - constructor( - protected readonly port: MessagePortMain, - protected readonly voiceRecognitionService: IVoiceRecognitionService, - protected readonly logService: ILogService - ) { - super(); - - this.registerListeners(); - } - - private registerListeners(): void { - const cts = new CancellationTokenSource(); - this._register(toDisposable(() => cts.dispose(true))); - - const requestHandler = (e: MessageEvent) => { - if (!(e.data instanceof Float32Array)) { - return; - } - - this.handleRequest(e.data, cts.token); - }; - this.port.on('message', requestHandler); - this._register(toDisposable(() => this.port.off('message', requestHandler))); - - this.port.start(); - - let closed = false; - this.port.on('close', () => { - this.logService.info(`[voice] transcriber: closed connection`); - - closed = true; - this.dispose(); - }); - - this._register(toDisposable(() => { - if (!closed) { - this.port.close(); - } - })); - } - - protected abstract handleRequest(data: Float32Array, cancellation: CancellationToken): Promise; - - protected joinFloat32Arrays(float32Arrays: Float32Array[]): Float32Array { - const result = new Float32Array(float32Arrays.reduce((prev, curr) => prev + curr.length, 0)); - - let offset = 0; - for (const float32Array of float32Arrays) { - result.set(float32Array, offset); - offset += float32Array.length; - } - - return result; - } -} - -class SlidingWindowVoiceTranscriber extends VoiceTranscriber { - - private readonly transcriptionQueue = this._register(new Queue()); - - private transcribedResults: string[] = []; - private data: Float32Array = new Float32Array(0); - - protected async handleRequest(data: Float32Array, cancellation: CancellationToken): Promise { - if (data.length > 0) { - this.logService.info(`[voice] transcriber: voice detected, storing in buffer`); - - this.data = this.data ? this.joinFloat32Arrays([this.data, data]) : data; - } else { - this.logService.info(`[voice] transcriber: silence detected, transcribing window...`); - - const data = this.data.slice(0); - this.data = new Float32Array(0); - - this.transcriptionQueue.queue(() => this.transcribe(data, cancellation)); - } - } - - private async transcribe(data: Float32Array, cancellation: CancellationToken): Promise { - if (cancellation.isCancellationRequested) { - return; - } - - if (data.length > VoiceTranscriber.MAX_DATA_LENGTH) { - this.logService.warn(`[voice] transcriber: refusing to accept more than 30s of audio data`); - return; - } - - if (data.length !== 0) { - const result = await this.voiceRecognitionService.transcribe(data, cancellation); - if (result) { - this.transcribedResults.push(result); - } - } - - if (cancellation.isCancellationRequested) { - return; - } - - this.port.postMessage(this.transcribedResults.join(' ')); - } - - override dispose(): void { - super.dispose(); - - this.data = new Float32Array(0); - } -} - -class FullWindowVoiceTranscriber extends VoiceTranscriber { - - private readonly transcriptionQueue = new LimitedQueue(); - - private data: Float32Array | undefined = undefined; - - private transcribedDataLength = 0; - private transcribedResult = ''; - - protected async handleRequest(data: Float32Array, cancellation: CancellationToken): Promise { - const dataCandidate = this.data ? this.joinFloat32Arrays([this.data, data]) : data; - if (dataCandidate.length > VoiceTranscriber.MAX_DATA_LENGTH) { - this.logService.warn(`[voice] transcriber: refusing to accept more than 30s of audio data`); - return; - } - - this.data = dataCandidate; - - this.transcriptionQueue.queue(() => this.transcribe(cancellation)); - } - - private async transcribe(cancellation: CancellationToken): Promise { - if (cancellation.isCancellationRequested) { - return; - } - - const data = this.data?.slice(0); - if (!data) { - return; - } - - let result: string; - if (data.length === this.transcribedDataLength) { - // Optimization: if the data is the same as the last time - // we transcribed, don't transcribe again, just return the - // same result as we had last time. - this.logService.info(`[voice] transcriber: silence detected, reusing previous transcription result`); - result = this.transcribedResult; - } else { - this.logService.info(`[voice] transcriber: voice detected, transcribing everything...`); - result = await this.voiceRecognitionService.transcribe(data, cancellation); - } - - this.transcribedResult = result; - this.transcribedDataLength = data.length; - - if (cancellation.isCancellationRequested) { - return; - } - - this.port.postMessage(result); - } - - override dispose(): void { - super.dispose(); - - this.data = undefined; - } -} diff --git a/src/vs/code/node/sharedProcess/sharedProcessMain.ts b/src/vs/code/node/sharedProcess/sharedProcessMain.ts index 557f8b6a6fe..84d080486a3 100644 --- a/src/vs/code/node/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcess/sharedProcessMain.ts @@ -114,8 +114,6 @@ import { IRemoteSocketFactoryService, RemoteSocketFactoryService } from 'vs/plat import { RemoteConnectionType } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { IVoiceRecognitionService, VoiceRecognitionService } from 'vs/platform/voiceRecognition/node/voiceRecognitionService'; -import { VoiceTranscriptionManager } from 'vs/code/node/sharedProcess/contrib/voiceTranscriber'; import { SharedProcessRawConnection, SharedProcessLifecycle } from 'vs/platform/sharedProcess/common/sharedProcess'; class SharedProcessMain extends Disposable implements IClientConnectionFilter { @@ -183,8 +181,7 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { instantiationService.createInstance(LogsDataCleaner), instantiationService.createInstance(LocalizationsUpdater), instantiationService.createInstance(ExtensionsContributions), - instantiationService.createInstance(UserDataProfilesCleaner), - instantiationService.createInstance(VoiceTranscriptionManager, this.onDidWindowConnectRaw.event) + instantiationService.createInstance(UserDataProfilesCleaner) )); } @@ -367,9 +364,6 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { // Remote Tunnel services.set(IRemoteTunnelService, new SyncDescriptor(RemoteTunnelService)); - // Voice Recognition - services.set(IVoiceRecognitionService, new SyncDescriptor(VoiceRecognitionService)); - return new InstantiationService(services); } diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index 7372f366459..66defd005d1 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -19,7 +19,6 @@ import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtil import { parseSharedProcessDebugPort } from 'vs/platform/environment/node/environmentService'; import { assertIsDefined } from 'vs/base/common/types'; import { SharedProcessChannelConnection, SharedProcessRawConnection, SharedProcessLifecycle } from 'vs/platform/sharedProcess/common/sharedProcess'; -import { IProductService } from 'vs/platform/product/common/productService'; export class SharedProcess extends Disposable { @@ -35,8 +34,7 @@ export class SharedProcess extends Disposable { @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @ILogService private readonly logService: ILogService, @ILoggerMainService private readonly loggerMainService: ILoggerMainService, - @IPolicyService private readonly policyService: IPolicyService, - @IProductService private readonly productService: IProductService + @IPolicyService private readonly policyService: IPolicyService ) { super(); @@ -165,8 +163,7 @@ export class SharedProcess extends Disposable { type: 'shared-process', entryPoint: 'vs/code/node/sharedProcess/sharedProcessMain', payload: this.createSharedProcessConfiguration(), - execArgv, - allowLoadingUnsignedLibraries: !!process.env.VSCODE_VOICE_MODULE_PATH && this.productService.quality !== 'stable' // TODO@bpasero package + execArgv }); } diff --git a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts deleted file mode 100644 index 03f7a16ca95..00000000000 --- a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts +++ /dev/null @@ -1,81 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { CancellationToken } from 'vs/base/common/cancellation'; -import { ILogService } from 'vs/platform/log/common/log'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IProductService } from 'vs/platform/product/common/productService'; - -export const IVoiceRecognitionService = createDecorator('voiceRecognitionService'); - -export interface IVoiceRecognitionService { - - readonly _serviceBrand: undefined; - - /** - * Given a buffer of audio data, attempts to - * transcribe the spoken words into text. - * - * @param channelData the raw audio data obtained - * from the microphone as uncompressed PCM data: - * - 1 channel (mono) - * - 16khz sampling rate - * - 16bit sample size - */ - transcribe(channelData: Float32Array, cancellation: CancellationToken): Promise; -} - -export class VoiceRecognitionService implements IVoiceRecognitionService { - - declare readonly _serviceBrand: undefined; - - constructor( - @ILogService private readonly logService: ILogService, - @IProductService private readonly productService: IProductService - ) { } - - async transcribe(channelData: Float32Array, cancellation: CancellationToken): Promise { - const modulePath = process.env.VSCODE_VOICE_MODULE_PATH; // TODO@bpasero package - if (!modulePath || this.productService.quality === 'stable') { - this.logService.error(`[voice] transcribe(${channelData.length}): Voice recognition not yet supported`); - throw new Error('Voice recognition not yet supported!'); - } - - const now = Date.now(); - - try { - const voiceModule: { - transcribe: ( - audioBuffer: { channelCount: 1; samplingRate: 16000; bitDepth: 16; channelData: Float32Array }, - options: { - language: string | 'auto'; - signal: AbortSignal; - } - ) => Promise; - } = require.__$__nodeRequire(modulePath); - - const abortController = new AbortController(); - cancellation.onCancellationRequested(() => abortController.abort()); - - const text = await voiceModule.transcribe({ - samplingRate: 16000, - bitDepth: 16, - channelCount: 1, - channelData - }, { - language: 'en', - signal: abortController.signal - }); - - this.logService.info(`[voice] transcribe(${channelData.length}): Text "${text}", took ${Date.now() - now}ms)`); - - return text; - } catch (error) { - this.logService.error(`[voice] transcribe(${channelData.length}): Failed width "${error}", took ${Date.now() - now}ms)`); - - throw error; - } - } -} diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts deleted file mode 100644 index cb0083c81eb..00000000000 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts +++ /dev/null @@ -1,127 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare class AudioWorkletProcessor { - - readonly port: MessagePort; - - process(inputs: [Float32Array[]], outputs: [Float32Array[]]): boolean; -} - -interface IVoiceTranscriptionWorkletOptions extends AudioWorkletNodeOptions { - processorOptions: { - readonly bufferTimespan: number; - readonly vadThreshold: number; - }; -} - -class VoiceTranscriptionWorklet extends AudioWorkletProcessor { - - private startTime: number | undefined = undefined; - private stopped: boolean = false; - - private buffer: Float32Array[] = []; - - private sharedProcessConnection: MessagePort | undefined = undefined; - - constructor(private readonly options: IVoiceTranscriptionWorkletOptions) { - super(); - - this.registerListeners(); - } - - private registerListeners() { - this.port.onmessage = event => { - switch (event.data) { - case 'vscode:startVoiceTranscription': { - this.sharedProcessConnection = event.ports[0]; - - this.sharedProcessConnection.onmessage = event => { - if (this.stopped) { - return; - } - - if (typeof event.data === 'string') { - this.port.postMessage(event.data); - } - }; - - this.sharedProcessConnection.start(); - break; - } - - case 'vscode:stopVoiceTranscription': { - this.stopped = true; - - this.sharedProcessConnection?.close(); - this.sharedProcessConnection = undefined; - - break; - } - } - }; - } - - override process(inputs: [Float32Array[]]): boolean { - if (this.startTime === undefined) { - this.startTime = Date.now(); - } - - const inputChannelData = inputs[0][0]; - if ((!(inputChannelData instanceof Float32Array))) { - return !this.stopped; - } - - this.buffer.push(inputChannelData.slice(0)); - - if (Date.now() - this.startTime > this.options.processorOptions.bufferTimespan && this.sharedProcessConnection) { - const buffer = this.joinFloat32Arrays(this.buffer); - this.buffer = []; - - // Send buffer to shared process for transcription. - // Send an empty buffer if it appears to be silence - // so that we can still trigger the transcription - // service and let it know about this. - - this.sharedProcessConnection.postMessage(this.appearsToBeSilence(buffer) ? new Float32Array(0) : buffer); - - this.startTime = Date.now(); - } - - return !this.stopped; - } - - private appearsToBeSilence(data: Float32Array): boolean { - - // This is the most simple Voice Activity Detection (VAD) - // and it is based on the Root Mean Square (RMS) of the signal - // with a certain threshold. Good for testing but probably - // not suitable for shipping to stable (TODO@bpasero). - - let sum = 0; - for (let i = 0; i < data.length; i++) { - sum += data[i] * data[i]; - } - - const rms = Math.sqrt(sum / data.length); - - return rms < this.options.processorOptions.vadThreshold; - } - - private joinFloat32Arrays(float32Arrays: Float32Array[]): Float32Array { - const result = new Float32Array(float32Arrays.reduce((prev, curr) => prev + curr.length, 0)); - - let offset = 0; - for (const float32Array of float32Arrays) { - result.set(float32Array, offset); - offset += float32Array.length; - } - - return result; - } -} - -// @ts-ignore -registerProcessor('voice-transcription-worklet', VoiceTranscriptionWorklet); diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts deleted file mode 100644 index 0f279ac26ea..00000000000 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ /dev/null @@ -1,208 +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 { localize } from 'vs/nls'; -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Emitter, Event } from 'vs/base/common/event'; -import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { DeferredPromise } from 'vs/base/common/async'; -import { FileAccess } from 'vs/base/common/network'; -import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; -import { INotificationService } from 'vs/platform/notification/common/notification'; - -export const IWorkbenchVoiceRecognitionService = createDecorator('workbenchVoiceRecognitionService'); - -export interface IWorkbenchVoiceRecognitionOptions { - - /** - * Optional event that is fired when the user cancels the voice recognition. - */ - readonly onDidCancel?: () => void; -} - -export interface IWorkbenchVoiceRecognitionService { - - readonly _serviceBrand: undefined; - - /** - * Starts listening to the microphone transcribing the voice to text. Microphone - * recording starts when the returned promise is resolved. - * - * @param cancellation a cancellation token to stop transcribing and - * listening to the microphone. - */ - transcribe(cancellation: CancellationToken, options?: IWorkbenchVoiceRecognitionOptions): Promise>; -} - -interface IVoiceTranscriptionWorkletOptions extends AudioWorkletNodeOptions { - processorOptions: { - readonly bufferTimespan: number; - readonly vadThreshold: number; - }; -} - -class VoiceTranscriptionWorkletNode extends AudioWorkletNode { - - constructor( - context: BaseAudioContext, - options: IVoiceTranscriptionWorkletOptions, - private readonly onDidTranscribe: Emitter, - private readonly sharedProcessService: ISharedProcessService - ) { - super(context, 'voice-transcription-worklet', options); - - this.registerListeners(); - } - - private registerListeners(): void { - this.port.onmessage = e => { - if (typeof e.data === 'string') { - this.onDidTranscribe.fire(e.data); - } - }; - } - - async start(token: CancellationToken): Promise { - token.onCancellationRequested(() => this.stop()); - - const sharedProcessConnection = await this.sharedProcessService.createRawConnection(); - - if (token.isCancellationRequested) { - this.stop(); - return; - } - - this.port.postMessage('vscode:startVoiceTranscription', [sharedProcessConnection]); - } - - private stop(): void { - this.port.postMessage('vscode:stopVoiceTranscription'); - this.disconnect(); - } -} - -export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognitionService { - - declare readonly _serviceBrand: undefined; - - private static readonly AUDIO_SAMPLING_RATE = 16000; - private static readonly AUDIO_BIT_DEPTH = 16; - private static readonly AUDIO_CHANNELS = 1; - - private static readonly BUFFER_TIMESPAN = 1000; - private static readonly VAD_THRESHOLD = 0.02; - - constructor( - @IProgressService private readonly progressService: IProgressService, - @ISharedProcessService private readonly sharedProcessService: ISharedProcessService, - @INotificationService private readonly notificationService: INotificationService - ) { } - - async transcribe(cancellation: CancellationToken, options?: IWorkbenchVoiceRecognitionOptions): Promise> { - const cts = new CancellationTokenSource(cancellation); - - const onDidTranscribe = new Emitter(); - cts.token.onCancellationRequested(() => { - onDidTranscribe.dispose(); - options?.onDidCancel?.(); - }); - - await this.doTranscribe(onDidTranscribe, cts); - - return onDidTranscribe.event; - } - - private doTranscribe(onDidTranscribe: Emitter, cts: CancellationTokenSource): Promise { - const recordingReady = new DeferredPromise(); - cts.token.onCancellationRequested(() => recordingReady.complete()); - - this.progressService.withProgress({ - location: ProgressLocation.Window, - title: localize('voiceTranscription', "Voice Transcription"), - cancellable: true - }, async progress => { - const recordingDone = new DeferredPromise(); - try { - progress.report({ message: localize('voiceTranscriptionGettingReady', "Getting microphone ready...") }); - - const microphoneDevice = await navigator.mediaDevices.getUserMedia({ - audio: { - sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLING_RATE, - sampleSize: WorkbenchVoiceRecognitionService.AUDIO_BIT_DEPTH, - channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, - autoGainControl: true, - noiseSuppression: true, - echoCancellation: false - } - }); - - if (cts.token.isCancellationRequested) { - return; - } - - const audioContext = new AudioContext({ - sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLING_RATE, - latencyHint: 'interactive' - }); - - const microphoneSource = audioContext.createMediaStreamSource(microphoneDevice); - - cts.token.onCancellationRequested(() => { - try { - for (const track of microphoneDevice.getTracks()) { - track.stop(); - } - - microphoneSource.disconnect(); - audioContext.close(); - } finally { - recordingDone.complete(); - } - }); - - await audioContext.audioWorklet.addModule(FileAccess.asBrowserUri('vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.js').toString(true)); - - if (cts.token.isCancellationRequested) { - return; - } - - const voiceTranscriptionTarget = new VoiceTranscriptionWorkletNode(audioContext, { - channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, - channelCountMode: 'explicit', - processorOptions: { - bufferTimespan: WorkbenchVoiceRecognitionService.BUFFER_TIMESPAN, - vadThreshold: WorkbenchVoiceRecognitionService.VAD_THRESHOLD - } - }, onDidTranscribe, this.sharedProcessService); - await voiceTranscriptionTarget.start(cts.token); - - if (cts.token.isCancellationRequested) { - return; - } - - microphoneSource.connect(voiceTranscriptionTarget); - - progress.report({ message: localize('voiceTranscriptionRecording', "Recording from microphone...") }); - recordingReady.complete(); - - return recordingDone.p; - } catch (error) { - this.notificationService.error(localize('voiceTranscriptionError', "Voice transcription failed: {0}", error.message)); - - recordingReady.error(error); - recordingDone.error(error); - } - }, () => { - cts.cancel(); - }); - - return recordingReady.p; - } -} - -// Register Service -registerSingleton(IWorkbenchVoiceRecognitionService, WorkbenchVoiceRecognitionService, InstantiationType.Delayed); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 4f5ab26bb35..f590f1f8815 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -75,7 +75,6 @@ import 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentServi import 'vs/workbench/services/integrity/electron-sandbox/integrityService'; import 'vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService'; import 'vs/workbench/services/checksum/electron-sandbox/checksumService'; -import 'vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService'; import 'vs/platform/remote/electron-sandbox/sharedProcessTunnelService'; import 'vs/workbench/services/tunnel/electron-sandbox/tunnelService'; import 'vs/platform/diagnostics/electron-sandbox/diagnosticsService'; From 970cc6eaa530f35bd14baba649d3c82c9d05be9f Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Wed, 18 Oct 2023 15:21:17 +0200 Subject: [PATCH 52/55] Add show tabs submenu to editor tabs bar --- src/vs/platform/actions/common/actions.ts | 1 + .../workbench/browser/parts/editor/editor.contribution.ts | 8 ++++---- .../workbench/browser/parts/editor/noEditorTabsControl.ts | 8 +------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 10a31c98531..081100f8528 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -69,6 +69,7 @@ export class MenuId { static readonly EmptyEditorGroup = new MenuId('EmptyEditorGroup'); static readonly EmptyEditorGroupContext = new MenuId('EmptyEditorGroupContext'); static readonly EditorTabsBarContext = new MenuId('EditorTabsBarContext'); + static readonly EditorTabsBarShowTabsSubmenu = new MenuId('EditorTabsBarShowTabsSubmenu'); static readonly ExplorerContext = new MenuId('ExplorerContext'); static readonly ExplorerContextShare = new MenuId('ExplorerContextShare'); static readonly ExtensionContext = new MenuId('ExtensionContext'); diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index cca074fe569..71003f0f7d4 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -355,8 +355,10 @@ MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_ MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_DOWN, title: localize('splitDown', "Split Down") }, group: '2_split', order: 20 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_LEFT, title: localize('splitLeft', "Split Left") }, group: '2_split', order: 30 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_RIGHT, title: localize('splitRight', "Split Right") }, group: '2_split', order: 40 }); -MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: ShowSingleEditorTabAction.ID, title: localize('showSingleTab', "Show Single Tab") }, group: '3_config', order: 10, when: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'multiple') }); -MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: HideEditorTabsAction.ID, title: localize('hideTabBar', "Hide Tab Bar") }, group: '3_config', order: 15, when: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none').negate() }); +MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { submenu: MenuId.EditorTabsBarShowTabsSubmenu, title: localize('showTabs', "Show Tabs"), group: '3_config', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsSubmenu, { command: { id: ShowMultipleEditorTabsAction.ID, title: localize('multipleTabs', "Multiple Tab"), toggled: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'multiple') }, group: '1_config', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsSubmenu, { command: { id: ShowSingleEditorTabAction.ID, title: localize('singleTab', "Single Tab"), toggled: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'single') }, group: '1_config', order: 20 }); +MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsSubmenu, { command: { id: HideEditorTabsAction.ID, title: localize('hideTabBar', "Hide Tab Bar"), toggled: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none') }, group: '1_config', order: 30 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: ToggleSeparatePinnedTabsAction.ID, title: localize('toggleSeparatePinnedEditorTabs', "Separate Pinned Editor Tabs"), toggled: ContextKeyExpr.has('config.workbench.editor.pinnedTabsOnSeparateRow') }, when: EditorPinnedAndUnpinnedTabsContext, group: '3_config', order: 20 }); // Editor Title Context Menu @@ -375,8 +377,6 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_ED MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR_RIGHT, title: localize('splitRight', "Split Right") }, group: '5_split', order: 40 }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: SPLIT_EDITOR_IN_GROUP, title: localize('splitInGroup', "Split in Group") }, group: '6_split_in_group', order: 10, when: ActiveEditorCanSplitInGroupContext }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: JOIN_EDITOR_IN_GROUP, title: localize('joinInGroup', "Join in Group") }, group: '6_split_in_group', order: 10, when: SideBySideEditorActiveContext }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: ShowMultipleEditorTabsAction.ID, title: localize('showMultipleTabs', "Show Multiple Tabs") }, group: '7_config', order: 10, when: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'single') }); -MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: HideEditorTabsAction.ID, title: localize('hideTabBar', "Hide Tab Bar") }, group: '7_config', order: 20, when: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none').negate() }); // Editor Title Menu MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_DIFF_SIDE_BY_SIDE, title: localize('inlineView', "Inline View"), toggled: ContextKeyExpr.equals('config.diffEditor.renderSideBySide', false) }, group: '1_diff', order: 10, when: ContextKeyExpr.has('isInDiffEditor') }); diff --git a/src/vs/workbench/browser/parts/editor/noEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/noEditorTabsControl.ts index 4d426dcf52f..f265b196212 100644 --- a/src/vs/workbench/browser/parts/editor/noEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/noEditorTabsControl.ts @@ -11,11 +11,7 @@ import { IEditorTitleControlDimensions } from 'vs/workbench/browser/parts/editor export class NoEditorTabsControl extends EditorTabsControl { - protected override create(parent: HTMLElement): void { - super.create(parent); - } - - protected override prepareEditorActions(editorActions: IToolbarActions): IToolbarActions { + protected prepareEditorActions(editorActions: IToolbarActions): IToolbarActions { return { primary: [], secondary: [] @@ -50,8 +46,6 @@ export class NoEditorTabsControl extends EditorTabsControl { updateEditorDirty(editor: EditorInput): void { } - override updateStyles(): void { } - getHeight(): number { return 0; } From fa0a3031eccc52d7e723d752d40e2e6cabb3ae47 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 18 Oct 2023 15:44:15 +0200 Subject: [PATCH 53/55] SCM - Source Control Repositories view to use scm.providerCountBadge (#194696) * SCM - Source Control Repositories view to use scm.providerCountBadge * Update setting description * Pull request feedback --- .../contrib/scm/browser/scm.contribution.ts | 2 +- .../scm/browser/scmRepositoriesViewPane.ts | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 689016f02bf..924f9f477a1 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -228,7 +228,7 @@ Registry.as(ConfigurationExtensions.Configuration).regis localize('scm.providerCountBadge.auto', "Only show count badge for Source Control Provider when non-zero."), localize('scm.providerCountBadge.visible', "Show Source Control Provider count badges.") ], - description: localize('scm.providerCountBadge', "Controls the count badges on Source Control Provider headers. These headers only appear when there is more than one provider."), + markdownDescription: localize('scm.providerCountBadge', "Controls the count badges on Source Control Provider headers. These headers appear in the \"Source Control\", and \"Source Control Sync\" views when there is more than one provider or when the {0} setting is enabled, as well as in the \"Source Control Repositories\" view.", '\`#scm.alwaysShowRepositories#\`'), default: 'hidden' }, 'scm.defaultViewMode': { diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts index ab4e907d21b..3cd3d80fb18 100644 --- a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts @@ -5,6 +5,7 @@ import 'vs/css!./media/scm'; import { localize } from 'vs/nls'; +import { Event } from 'vs/base/common/event'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPane'; import { append, $ } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; @@ -24,6 +25,7 @@ import { RepositoryRenderer } from 'vs/workbench/contrib/scm/browser/scmReposito import { collectContextMenuActions, getActionViewItemProvider } from 'vs/workbench/contrib/scm/browser/util'; import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { Iterable } from 'vs/base/common/iterator'; +import { DisposableStore } from 'vs/base/common/lifecycle'; class ListDelegate implements IListVirtualDelegate { @@ -39,6 +41,7 @@ class ListDelegate implements IListVirtualDelegate { export class SCMRepositoriesViewPane extends ViewPane { private list!: WorkbenchList; + private readonly disposables = new DisposableStore(); constructor( options: IViewPaneOptions, @@ -61,6 +64,14 @@ export class SCMRepositoriesViewPane extends ViewPane { const listContainer = append(container, $('.scm-view.scm-repositories-view')); + const updateProviderCountVisibility = () => { + const value = this.configurationService.getValue<'hidden' | 'auto' | 'visible'>('scm.providerCountBadge'); + listContainer.classList.toggle('hide-provider-counts', value === 'hidden'); + listContainer.classList.toggle('auto-provider-counts', value === 'auto'); + }; + this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.providerCountBadge'), this.disposables)(updateProviderCountVisibility)); + updateProviderCountVisibility(); + const delegate = new ListDelegate(); const renderer = this.instantiationService.createInstance(RepositoryRenderer, getActionViewItemProvider(this.instantiationService)); const identityProvider = { getId: (r: ISCMRepository) => r.provider.id }; @@ -179,4 +190,9 @@ export class SCMRepositoriesViewPane extends ViewPane { this.list.setFocus([selection[0]]); } } + + override dispose(): void { + this.disposables.dispose(); + super.dispose(); + } } From e26251254a3c4ad0ad7d426b3b72b40ebc76b646 Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 18 Oct 2023 16:20:00 +0200 Subject: [PATCH 54/55] Update src/vs/workbench/browser/parts/editor/editor.contribution.ts Co-authored-by: Benjamin Pasero --- src/vs/workbench/browser/parts/editor/editor.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 71003f0f7d4..9e657431c9a 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -356,7 +356,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_ MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_LEFT, title: localize('splitLeft', "Split Left") }, group: '2_split', order: 30 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: SPLIT_EDITOR_RIGHT, title: localize('splitRight', "Split Right") }, group: '2_split', order: 40 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { submenu: MenuId.EditorTabsBarShowTabsSubmenu, title: localize('showTabs', "Show Tabs"), group: '3_config', order: 10 }); -MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsSubmenu, { command: { id: ShowMultipleEditorTabsAction.ID, title: localize('multipleTabs', "Multiple Tab"), toggled: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'multiple') }, group: '1_config', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsSubmenu, { command: { id: ShowMultipleEditorTabsAction.ID, title: localize('multipleTabs', "Multiple Tabs"), toggled: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'multiple') }, group: '1_config', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsSubmenu, { command: { id: ShowSingleEditorTabAction.ID, title: localize('singleTab', "Single Tab"), toggled: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'single') }, group: '1_config', order: 20 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsSubmenu, { command: { id: HideEditorTabsAction.ID, title: localize('hideTabBar', "Hide Tab Bar"), toggled: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none') }, group: '1_config', order: 30 }); MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { command: { id: ToggleSeparatePinnedTabsAction.ID, title: localize('toggleSeparatePinnedEditorTabs', "Separate Pinned Editor Tabs"), toggled: ContextKeyExpr.has('config.workbench.editor.pinnedTabsOnSeparateRow') }, when: EditorPinnedAndUnpinnedTabsContext, group: '3_config', order: 20 }); From af0e723d17885198779b7ea80bf089bf5a712982 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 18 Oct 2023 16:49:10 +0200 Subject: [PATCH 55/55] no diff editor when just dealing with inserts (#195910) --- .../browser/inlineChatLivePreviewWidget.ts | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts index 005d0216fb0..12dbe5db481 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts @@ -21,7 +21,7 @@ import { LineRange } from 'vs/editor/common/core/lineRange'; import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { Position } from 'vs/editor/common/core/position'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; -import { ScrollType } from 'vs/editor/common/editorCommon'; +import { IEditorDecorationsCollection, ScrollType } from 'vs/editor/common/editorCommon'; import { ILogService } from 'vs/platform/log/common/log'; import { lineRangeAsRange, invertLineRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; import { ResourceLabel } from 'vs/workbench/browser/labels'; @@ -43,7 +43,9 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { private readonly _elements = h('div.inline-chat-diff-widget@domNode'); + private readonly _decorationCollection: IEditorDecorationsCollection; private readonly _diffEditor: IDiffEditor; + private _dim: Dimension | undefined; private _isVisible: boolean = false; @@ -60,6 +62,8 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { super.create(); assertType(editor.hasModel()); + this._decorationCollection = editor.createDecorationsCollection(); + const diffContributions = EditorExtensionsRegistry .getEditorContributions() .filter(c => c.id !== INLINE_CHAT_ID && c.id !== FoldingController.ID); @@ -137,6 +141,7 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { } override hide(): void { + this._decorationCollection.clear(); this._cleanupFullDiff(); super.hide(); this._isVisible = false; @@ -150,13 +155,17 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { const hasFocus = this._diffEditor.hasTextFocus(); this._isVisible = true; - if (changes.length === 0 || this._session.textModel0.getValueLength() === 0) { + const onlyInserts = changes.every(change => change.original.isEmpty); + + if (onlyInserts || changes.length === 0 || 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(changes); } else { // complex changes this._logService.debug('[IE] livePreview-mode: full diff'); + this._decorationCollection.clear(); this._renderChangesWithFullDiff(changes); } @@ -170,6 +179,22 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { } + private _renderInsertWithHighlight(changes: readonly DetailedLineRangeMapping[]) { + assertType(this.editor.hasModel()); + + const ranges = this._computeHiddenRanges(this.editor.getModel(), changes); + + this._decorationCollection.set([{ + range: lineRangeAsRange(ranges.modifiedHidden), + options: { + description: 'inline-chat-insert', + showIfCollapsed: false, + isWholeLine: true, + className: 'inline-chat-lines-inserted-range', + } + }]); + } + // --- full diff private _renderChangesWithFullDiff(changes: readonly DetailedLineRangeMapping[]) {