From cea2397e06c29614551f71d9934788169cc1fc20 Mon Sep 17 00:00:00 2001 From: Nick Rayburn Date: Thu, 27 May 2021 23:13:03 -0500 Subject: [PATCH 001/333] npm check for root package.json before findFiles --- extensions/npm/src/tasks.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 911d66fbe3bd3..533ec5c56b693 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -365,12 +365,16 @@ export function getPackageJsonUriFromTask(task: Task): Uri | null { } export async function hasPackageJson(): Promise { + // Faster than `findFiles` for workspaces with a root package.json. + if (await hasRootPackageJson()) { + return true; + } const token = new CancellationTokenSource(); // Search for files for max 1 second. const timeout = setTimeout(() => token.cancel(), 1000); const files = await workspace.findFiles('**/package.json', undefined, 1, token.token); clearTimeout(timeout); - return files.length > 0 || await hasRootPackageJson(); + return files.length > 0; } async function hasRootPackageJson(): Promise { From 2f40bba1a297da6c6e0881b856a9719e391620ad Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 20 Dec 2023 16:54:43 +0100 Subject: [PATCH 002/333] first version od quick voice that starts inline chat with what you said --- .../inlineChat/browser/inlineChatActions.ts | 2 +- .../inlineChat.contribution.ts | 13 + .../electron-sandbox/inlineChatQuickVoice.css | 39 +++ .../electron-sandbox/inlineChatQuickVoice.ts | 226 ++++++++++++++++++ src/vs/workbench/workbench.desktop.main.ts | 2 + 5 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts create mode 100644 src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css create mode 100644 src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 337fcaaf5515f..bdcb583a3994e 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -93,7 +93,7 @@ export class UnstashSessionAction extends EditorAction2 { } } -abstract class AbstractInlineChatAction extends EditorAction2 { +export abstract class AbstractInlineChatAction extends EditorAction2 { static readonly category = { value: localize('cat', 'Inline Chat'), original: 'Inline Chat' }; diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts new file mode 100644 index 0000000000000..d60b942064404 --- /dev/null +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { registerAction2 } from 'vs/platform/actions/common/actions'; +import { CancelAction, InlineChatQuickVoice, StartAction, StopAction } from 'vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice'; + +registerEditorContribution(InlineChatQuickVoice.ID, InlineChatQuickVoice, EditorContributionInstantiation.Eager); // EAGER because of notebook dispose/create of editors +registerAction2(StartAction); +registerAction2(StopAction); +registerAction2(CancelAction); diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css new file mode 100644 index 0000000000000..784d716d80fc1 --- /dev/null +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .inline-chat-quick-voice { + background-color: var(--vscode-editor-background); + padding: 2px; + border-radius: 8px; + display: flex; + align-items: center; + box-shadow: 0 4px 8px var(--vscode-inlineChat-shadow); + white-space: nowrap; + z-index: 1000; +} + +.monaco-editor .inline-chat-quick-voice .codicon.codicon-mic-filled { + display: flex; + align-items: center; + width: 16px; + height: 16px; +} + +.monaco-editor .inline-chat-quick-voice.recording .codicon.codicon-mic-filled { + color: var(--vscode-activityBarBadge-background); + animation: ani-inline-chat 1s infinite; +} + +@keyframes ani-inline-chat { + 0% { + color: var(--vscode-editorCursor-background); + } + 50% { + color: var(--vscode-activityBarBadge-background); + } + 100% { + color: var(--vscode-editorCursor-background); + } +} diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts new file mode 100644 index 0000000000000..88333cf12151a --- /dev/null +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts @@ -0,0 +1,226 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./inlineChatQuickVoice'; +import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { Codicon } from 'vs/base/common/codicons'; +import { KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { localize, localize2 } from 'vs/nls'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { HasSpeechProvider, ISpeechService, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; +import { h, reset } from 'vs/base/browser/dom'; +import { IDimension } from 'vs/editor/common/core/dimension'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { AbstractInlineChatAction } from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions'; + +const CTX_QUICK_CHAT_IN_PROGRESS = new RawContextKey('inlineChat.quickChatInProgress', false); + +export class StartAction extends EditorAction2 { + + constructor() { + super({ + id: 'inlineChat.quickVoice.start', + title: localize2('start', "Start Inline Voice Chat"), + category: AbstractInlineChatAction.category, + precondition: ContextKeyExpr.and(HasSpeechProvider, CTX_QUICK_CHAT_IN_PROGRESS.toNegated()), + keybinding: { + primary: KeyChord(KeyCode.F12, KeyCode.F12), + weight: KeybindingWeight.WorkbenchContrib + } + }); + } + + override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]) { + InlineChatQuickVoice.get(editor)?.start(); + } +} + +export class StopAction extends EditorAction2 { + + constructor() { + super({ + id: 'inlineChat.quickVoice.stop', + title: localize2('stop', "Stop Inline Voice Chat"), + category: AbstractInlineChatAction.category, + precondition: ContextKeyExpr.and(HasSpeechProvider, CTX_QUICK_CHAT_IN_PROGRESS), + keybinding: { + primary: KeyChord(KeyCode.F12, KeyCode.F12), + weight: KeybindingWeight.WorkbenchContrib + } + }); + } + + override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]) { + InlineChatQuickVoice.get(editor)?.stop(); + } +} + +export class CancelAction extends EditorAction2 { + + constructor() { + super({ + id: 'inlineChat.quickVoice.Cancel', + title: localize('Cancel', "Cancel Inline Voice Chat"), + category: AbstractInlineChatAction.category, + precondition: ContextKeyExpr.and(HasSpeechProvider, CTX_QUICK_CHAT_IN_PROGRESS), + keybinding: { + primary: KeyCode.Escape, + weight: KeybindingWeight.WorkbenchContrib + } + }); + } + + override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]) { + InlineChatQuickVoice.get(editor)?.cancel(); + } +} + +class QuickVoiceWidget implements IContentWidget { + + readonly suppressMouseDown = true; + + private readonly _domNode = document.createElement('div'); + private readonly _elements = h('.inline-chat-quick-voice@main', [ + h('span@mic'), + h('span@message'), + ]); + + constructor(private readonly _editor: ICodeEditor) { + this._domNode.appendChild(this._elements.root); + this._domNode.style.zIndex = '1000'; + reset(this._elements.mic, renderIcon(Codicon.micFilled)); + } + + getId(): string { + return 'inlineChatQuickVoice'; + } + + getDomNode(): HTMLElement { + return this._domNode; + } + + getPosition(): IContentWidgetPosition | null { + if (!this._editor.hasModel()) { + return null; + } + const selection = this._editor.getSelection(); + // const position = this._editor.getPosition(); + return { + position: selection.getPosition(), + preference: [ + selection.getPosition().equals(selection.getStartPosition()) ? ContentWidgetPositionPreference.ABOVE : ContentWidgetPositionPreference.BELOW, + ContentWidgetPositionPreference.EXACT + ] + }; + } + + beforeRender(): IDimension | null { + const lineHeight = this._editor.getOption(EditorOption.lineHeight); + this._elements.main.style.lineHeight = `${lineHeight}px`; + this._elements.main.style.height = `${lineHeight}px`; + return null; + } + + // --- + + updateInput(input: string | undefined): void { + this._elements.message.textContent = input ?? ''; + } + + active(): void { + this._elements.main.classList.add('recording'); + } + + reset(): void { + this._elements.main.classList.remove('recording'); + this.updateInput(undefined); + } +} + +export class InlineChatQuickVoice implements IEditorContribution { + + static readonly ID = 'inlineChatQuickVoice'; + + static get(editor: ICodeEditor): InlineChatQuickVoice | null { + return editor.getContribution(InlineChatQuickVoice.ID); + } + + private readonly _ctxQuickChatInProgress: IContextKey; + private readonly _widget: QuickVoiceWidget; + private _finishCallback?: (abort: boolean) => void; + + constructor( + private readonly _editor: ICodeEditor, + @ISpeechService private readonly _speechService: ISpeechService, + @IContextKeyService contextKeyService: IContextKeyService, + ) { + this._widget = new QuickVoiceWidget(this._editor); + this._ctxQuickChatInProgress = CTX_QUICK_CHAT_IN_PROGRESS.bindTo(contextKeyService); + } + + dispose(): void { + this._finishCallback?.(true); + } + + start() { + + const cts = new CancellationTokenSource(); + this._editor.addContentWidget(this._widget); + this._ctxQuickChatInProgress.set(true); + + let message: string | undefined; + + const session = this._speechService.createSpeechToTextSession(cts.token); + const listener = session.onDidChange(e => { + + if (cts.token.isCancellationRequested) { + return; + } + + switch (e.status) { + case SpeechToTextStatus.Started: + this._widget.active(); + break; + case SpeechToTextStatus.Stopped: + break; + case SpeechToTextStatus.Recognizing: + case SpeechToTextStatus.Recognized: + message = e.text; + this._widget.updateInput(message); + break; + } + }); + + const done = (abort: boolean) => { + cts.dispose(true); + listener.dispose(); + this._widget.reset(); + this._editor.removeContentWidget(this._widget); + this._ctxQuickChatInProgress.reset(); + + if (!abort && message) { + InlineChatController.get(this._editor)?.run({ message, autoSend: true }); + } + }; + + this._finishCallback = done; + } + + stop(): void { + this._finishCallback?.(false); + } + + cancel(): void { + this._finishCallback?.(true); + } + +} diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 7f311e34818b5..e9005c84d11dd 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -174,6 +174,8 @@ import 'vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribu // Chat import 'vs/workbench/contrib/chat/electron-sandbox/chat.contribution'; +import 'vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution'; + //#endregion From 06f41fda5dca4cbde66ff9053b81cc0f8382ee4a Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 4 Jan 2024 15:53:31 -0600 Subject: [PATCH 003/333] add initial voice actions in terminal and editor area --- .../electron-sandbox/actions/voiceActions.ts | 121 ++++++++++++++++++ .../electron-sandbox/chat.contribution.ts | 3 + 2 files changed, 124 insertions(+) create mode 100644 src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts new file mode 100644 index 0000000000000..b8160eaa173ba --- /dev/null +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts @@ -0,0 +1,121 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { localize } from 'vs/nls'; +import { Action2 } from 'vs/platform/actions/common/actions'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { HasSpeechProvider, ISpeechService, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + + +export class StartEditorSpeechToTextAction extends Action2 { + + static readonly ID = 'workbench.action.startEditorSpeechToText'; + + constructor() { + super({ + id: 'workbench.action.startEditorSpeechToText', + title: { + value: localize('workbench.action.startEditorSpeechToText', "Start Editor Speech To Text"), + original: 'Start Editor Speech To Text' + }, + precondition: ContextKeyExpr.and(HasSpeechProvider, EditorContextKeys.focus), + f1: true + }); + } + + async run(accessor: ServicesAccessor): Promise { + const instantiationService = accessor.get(IInstantiationService); + VoiceSession.getInstance(instantiationService).start('editor'); + } +} + + +export class StartTerminalSpeechToTextAction extends Action2 { + + static readonly ID = 'workbench.action.startTerminalSpeechToText'; + + constructor() { + super({ + id: 'workbench.action.startTerminalSpeechToText', + title: { + value: localize('workbench.action.startTerminalSpeechToText', "Start Terminal Speech To Text"), + original: 'Start Terminal Speech To Text' + }, + precondition: ContextKeyExpr.and(HasSpeechProvider, TerminalContextKeys.focus), + f1: true + }); + } + + async run(accessor: ServicesAccessor): Promise { + const instantiationService = accessor.get(IInstantiationService); + VoiceSession.getInstance(instantiationService).start('terminal'); + } +} + +class VoiceSession extends Disposable { + private static instance: VoiceSession | undefined = undefined; + static getInstance(instantiationService: IInstantiationService): VoiceSession { + if (!VoiceSession.instance) { + VoiceSession.instance = instantiationService.createInstance(VoiceSession); + } + + return VoiceSession.instance; + } + private _cancellationTokenSource: CancellationTokenSource | undefined; + private _disposables = new DisposableStore(); + constructor( + private readonly _speechService: ISpeechService, + private readonly _editorService: IEditorService, + private readonly _terminalService: ITerminalService + ) { + super(); + } + start(type: 'terminal' | 'editor'): void { + this.stop(); + if (this._cancellationTokenSource) { + this._cancellationTokenSource.cancel(); + } else { + this._cancellationTokenSource = new CancellationTokenSource(); + } + const session = this._disposables.add(this._speechService.createSpeechToTextSession(this._cancellationTokenSource!.token)); + this._disposables.add(session.onDidChange((e) => { + switch (e.status) { + case SpeechToTextStatus.Started: + console.log('started', e.text); + break; + case SpeechToTextStatus.Recognizing: + console.log('recognizing', e.text); + if (type === 'terminal') { + this._terminalService.activeInstance?.sendText(e.text!, true); + } else { + this._editorService.activeTextEditorControl?.trigger?.('type', 'type', { text: e.text }); + } + break; + case SpeechToTextStatus.Recognized: + console.log('recognized', e.text); + if (type === 'terminal') { + this._terminalService.activeInstance?.sendText(e.text!, true); + } else { + this._editorService.activeTextEditorControl?.trigger?.('type', 'type', { text: e.text }); + } + break; + case SpeechToTextStatus.Stopped: + console.log('stopped', e.text); + break; + } + })); + } + stop(): void { + this._cancellationTokenSource?.cancel(); + this._disposables.dispose(); + } +} 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 9ba5430737e4a..fb529a7342c46 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts @@ -5,6 +5,7 @@ import { InlineVoiceChatAction, QuickVoiceChatAction, StartVoiceChatAction, StopListeningInInlineChatAction, StopListeningInQuickChatAction, StopListeningInChatEditorAction, StopListeningInChatViewAction, VoiceChatInChatViewAction, StopListeningAction, StopListeningAndSubmitAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; import { registerAction2 } from 'vs/platform/actions/common/actions'; +import { StartEditorSpeechToTextAction, StartTerminalSpeechToTextAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions'; registerAction2(StartVoiceChatAction); @@ -20,3 +21,5 @@ registerAction2(StopListeningInChatEditorAction); registerAction2(StopListeningInQuickChatAction); registerAction2(StopListeningInInlineChatAction); +registerAction2(StartTerminalSpeechToTextAction); +registerAction2(StartEditorSpeechToTextAction); From 5fcae79502f84526fdf5aa8ca60206bc645be5c3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 4 Jan 2024 15:58:10 -0600 Subject: [PATCH 004/333] stop on stop --- .../contrib/chat/electron-sandbox/actions/voiceActions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts index b8160eaa173ba..0f460badb759f 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts @@ -110,6 +110,7 @@ class VoiceSession extends Disposable { break; case SpeechToTextStatus.Stopped: console.log('stopped', e.text); + this.stop(); break; } })); From db577c2430d2ea71fbdf41c58f0b0c388e04a718 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 4 Jan 2024 17:22:58 -0600 Subject: [PATCH 005/333] service injection --- .../contrib/chat/electron-sandbox/actions/voiceActions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts index 0f460badb759f..2ffc602203a03 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts @@ -73,9 +73,9 @@ class VoiceSession extends Disposable { private _cancellationTokenSource: CancellationTokenSource | undefined; private _disposables = new DisposableStore(); constructor( - private readonly _speechService: ISpeechService, - private readonly _editorService: IEditorService, - private readonly _terminalService: ITerminalService + @ISpeechService private readonly _speechService: ISpeechService, + @IEditorService readonly _editorService: IEditorService, + @ITerminalService readonly _terminalService: ITerminalService ) { super(); } From c37b435618f84a885e531bbb94593c0cd8e9f6f4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 5 Jan 2024 12:19:56 -0600 Subject: [PATCH 006/333] move things around --- .../actions/voice.contribution.ts | 10 ++++ .../electron-sandbox/actions/voiceActions.ts | 57 +++++++------------ .../electron-sandbox/chat.contribution.ts | 3 - 3 files changed, 32 insertions(+), 38 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/electron-sandbox/actions/voice.contribution.ts diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voice.contribution.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voice.contribution.ts new file mode 100644 index 0000000000000..9cd18e8c82fb4 --- /dev/null +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voice.contribution.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerAction2 } from 'vs/platform/actions/common/actions'; +import { StartTerminalSpeechToTextAction, StopTerminalSpeechToTextAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions'; + +registerAction2(StartTerminalSpeechToTextAction); +registerAction2(StopTerminalSpeechToTextAction); diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts index 2ffc602203a03..a32b3f54eabb7 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts @@ -5,7 +5,6 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { localize } from 'vs/nls'; import { Action2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -13,42 +12,41 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { HasSpeechProvider, ISpeechService, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -export class StartEditorSpeechToTextAction extends Action2 { +export class StartTerminalSpeechToTextAction extends Action2 { - static readonly ID = 'workbench.action.startEditorSpeechToText'; + static readonly ID = 'workbench.action.startTerminalSpeechToText'; constructor() { super({ - id: 'workbench.action.startEditorSpeechToText', + id: 'workbench.action.startTerminalSpeechToText', title: { - value: localize('workbench.action.startEditorSpeechToText', "Start Editor Speech To Text"), - original: 'Start Editor Speech To Text' + value: localize('workbench.action.startTerminalSpeechToText', "Start Terminal Speech To Text"), + original: 'Start Terminal Speech To Text' }, - precondition: ContextKeyExpr.and(HasSpeechProvider, EditorContextKeys.focus), + precondition: ContextKeyExpr.and(HasSpeechProvider, TerminalContextKeys.focus), f1: true }); } async run(accessor: ServicesAccessor): Promise { const instantiationService = accessor.get(IInstantiationService); - VoiceSession.getInstance(instantiationService).start('editor'); + VoiceSession.getInstance(instantiationService).start(); } } -export class StartTerminalSpeechToTextAction extends Action2 { +export class StopTerminalSpeechToTextAction extends Action2 { - static readonly ID = 'workbench.action.startTerminalSpeechToText'; + static readonly ID = 'workbench.action.stopTerminalSpeechToText'; constructor() { super({ - id: 'workbench.action.startTerminalSpeechToText', + id: 'workbench.action.stopTerminalSpeechToText', title: { - value: localize('workbench.action.startTerminalSpeechToText', "Start Terminal Speech To Text"), - original: 'Start Terminal Speech To Text' + value: localize('workbench.action.stopTerminalSpeechToText', "Stop Terminal Speech To Text"), + original: 'Stop Terminal Speech To Text' }, precondition: ContextKeyExpr.and(HasSpeechProvider, TerminalContextKeys.focus), f1: true @@ -57,7 +55,7 @@ export class StartTerminalSpeechToTextAction extends Action2 { async run(accessor: ServicesAccessor): Promise { const instantiationService = accessor.get(IInstantiationService); - VoiceSession.getInstance(instantiationService).start('terminal'); + VoiceSession.getInstance(instantiationService).stop(); } } @@ -74,42 +72,31 @@ class VoiceSession extends Disposable { private _disposables = new DisposableStore(); constructor( @ISpeechService private readonly _speechService: ISpeechService, - @IEditorService readonly _editorService: IEditorService, @ITerminalService readonly _terminalService: ITerminalService ) { super(); } - start(type: 'terminal' | 'editor'): void { + start(): void { this.stop(); - if (this._cancellationTokenSource) { - this._cancellationTokenSource.cancel(); - } else { - this._cancellationTokenSource = new CancellationTokenSource(); - } + this._cancellationTokenSource = new CancellationTokenSource(); const session = this._disposables.add(this._speechService.createSpeechToTextSession(this._cancellationTokenSource!.token)); this._disposables.add(session.onDidChange((e) => { + if (this._cancellationTokenSource?.token.isCancellationRequested) { + return; + } switch (e.status) { case SpeechToTextStatus.Started: - console.log('started', e.text); break; case SpeechToTextStatus.Recognizing: - console.log('recognizing', e.text); - if (type === 'terminal') { - this._terminalService.activeInstance?.sendText(e.text!, true); - } else { - this._editorService.activeTextEditorControl?.trigger?.('type', 'type', { text: e.text }); - } + // TODO: start audio cue, show in status bar break; case SpeechToTextStatus.Recognized: - console.log('recognized', e.text); - if (type === 'terminal') { - this._terminalService.activeInstance?.sendText(e.text!, true); - } else { - this._editorService.activeTextEditorControl?.trigger?.('type', 'type', { text: e.text }); + if (e.text) { + this._terminalService.activeInstance?.sendText(e.text, false); } break; case SpeechToTextStatus.Stopped: - console.log('stopped', e.text); + // TODO: stop audio cue, hide in status bar this.stop(); break; } 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 fb529a7342c46..9ba5430737e4a 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts @@ -5,7 +5,6 @@ import { InlineVoiceChatAction, QuickVoiceChatAction, StartVoiceChatAction, StopListeningInInlineChatAction, StopListeningInQuickChatAction, StopListeningInChatEditorAction, StopListeningInChatViewAction, VoiceChatInChatViewAction, StopListeningAction, StopListeningAndSubmitAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; import { registerAction2 } from 'vs/platform/actions/common/actions'; -import { StartEditorSpeechToTextAction, StartTerminalSpeechToTextAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions'; registerAction2(StartVoiceChatAction); @@ -21,5 +20,3 @@ registerAction2(StopListeningInChatEditorAction); registerAction2(StopListeningInQuickChatAction); registerAction2(StopListeningInInlineChatAction); -registerAction2(StartTerminalSpeechToTextAction); -registerAction2(StartEditorSpeechToTextAction); From 13fcd1a8c3591fcb167f502ace45771862bf5015 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 5 Jan 2024 12:41:54 -0600 Subject: [PATCH 007/333] move around --- .../electron-sandbox/actions/voice.contribution.ts | 10 ---------- .../contrib/chat/electron-sandbox/chat.contribution.ts | 3 +++ 2 files changed, 3 insertions(+), 10 deletions(-) delete mode 100644 src/vs/workbench/contrib/chat/electron-sandbox/actions/voice.contribution.ts diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voice.contribution.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voice.contribution.ts deleted file mode 100644 index 9cd18e8c82fb4..0000000000000 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voice.contribution.ts +++ /dev/null @@ -1,10 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { registerAction2 } from 'vs/platform/actions/common/actions'; -import { StartTerminalSpeechToTextAction, StopTerminalSpeechToTextAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions'; - -registerAction2(StartTerminalSpeechToTextAction); -registerAction2(StopTerminalSpeechToTextAction); 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 9ba5430737e4a..68c167ab1c799 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts @@ -5,6 +5,7 @@ import { InlineVoiceChatAction, QuickVoiceChatAction, StartVoiceChatAction, StopListeningInInlineChatAction, StopListeningInQuickChatAction, StopListeningInChatEditorAction, StopListeningInChatViewAction, VoiceChatInChatViewAction, StopListeningAction, StopListeningAndSubmitAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; import { registerAction2 } from 'vs/platform/actions/common/actions'; +import { StartTerminalSpeechToTextAction, StopTerminalSpeechToTextAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions'; registerAction2(StartVoiceChatAction); @@ -20,3 +21,5 @@ registerAction2(StopListeningInChatEditorAction); registerAction2(StopListeningInQuickChatAction); registerAction2(StopListeningInInlineChatAction); +registerAction2(StartTerminalSpeechToTextAction); +registerAction2(StopTerminalSpeechToTextAction); From 7156038cfc6d17eb2b4b4ab41b5000faa7869d90 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 5 Jan 2024 12:46:28 -0600 Subject: [PATCH 008/333] rename things --- .../actions/{voiceActions.ts => voiceTerminalActions.ts} | 1 - .../{chat.contribution.ts => voice.contribution.ts} | 2 +- src/vs/workbench/workbench.desktop.main.ts | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) rename src/vs/workbench/contrib/chat/electron-sandbox/actions/{voiceActions.ts => voiceTerminalActions.ts} (99%) rename src/vs/workbench/contrib/chat/electron-sandbox/{chat.contribution.ts => voice.contribution.ts} (98%) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts similarity index 99% rename from src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts rename to src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts index a32b3f54eabb7..c91b3d2dd100c 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts @@ -13,7 +13,6 @@ import { HasSpeechProvider, ISpeechService, SpeechToTextStatus } from 'vs/workbe import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; - export class StartTerminalSpeechToTextAction extends Action2 { static readonly ID = 'workbench.action.startTerminalSpeechToText'; diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts b/src/vs/workbench/contrib/chat/electron-sandbox/voice.contribution.ts similarity index 98% rename from src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts rename to src/vs/workbench/contrib/chat/electron-sandbox/voice.contribution.ts index 68c167ab1c799..2eb682451efc5 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/voice.contribution.ts @@ -5,7 +5,7 @@ import { InlineVoiceChatAction, QuickVoiceChatAction, StartVoiceChatAction, StopListeningInInlineChatAction, StopListeningInQuickChatAction, StopListeningInChatEditorAction, StopListeningInChatViewAction, VoiceChatInChatViewAction, StopListeningAction, StopListeningAndSubmitAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; import { registerAction2 } from 'vs/platform/actions/common/actions'; -import { StartTerminalSpeechToTextAction, StopTerminalSpeechToTextAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceActions'; +import { StartTerminalSpeechToTextAction, StopTerminalSpeechToTextAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions'; registerAction2(StartVoiceChatAction); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 7f311e34818b5..ad942cb8973d1 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -173,7 +173,7 @@ import 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.contributio import 'vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution'; // Chat -import 'vs/workbench/contrib/chat/electron-sandbox/chat.contribution'; +import 'vs/workbench/contrib/chat/electron-sandbox/voice.contribution'; //#endregion From 41c99fd2c560f3ae5e7b2639995f237fa46f720d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 5 Jan 2024 15:10:50 -0600 Subject: [PATCH 009/333] on dispose/change of active instance, stop --- .../chat/electron-sandbox/actions/voiceTerminalActions.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts index c91b3d2dd100c..ec695b3089439 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts @@ -74,6 +74,8 @@ class VoiceSession extends Disposable { @ITerminalService readonly _terminalService: ITerminalService ) { super(); + this._register(this._terminalService.onDidChangeActiveInstance(() => this.stop())); + this._register(this._terminalService.onDidDisposeInstance(() => this.stop())); } start(): void { this.stop(); From a9d3d00283f6aba6a220c4c0ed8078a2ff13c388 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 11 Jan 2024 08:03:07 -0800 Subject: [PATCH 010/333] add speech timeout --- .../actions/voiceTerminalActions.ts | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts index ec695b3089439..58404b0ff1537 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts @@ -3,15 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { RunOnceScheduler } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { Action2 } from 'vs/platform/actions/common/actions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { AccessibilityVoiceSettingId, SpeechTimeoutDefault } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { HasSpeechProvider, ISpeechService, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { isNumber } from 'vs/base/common/types'; export class StartTerminalSpeechToTextAction extends Action2 { @@ -59,6 +63,7 @@ export class StopTerminalSpeechToTextAction extends Action2 { } class VoiceSession extends Disposable { + private _input: string = ''; private static instance: VoiceSession | undefined = undefined; static getInstance(instantiationService: IInstantiationService): VoiceSession { if (!VoiceSession.instance) { @@ -71,7 +76,8 @@ class VoiceSession extends Disposable { private _disposables = new DisposableStore(); constructor( @ISpeechService private readonly _speechService: ISpeechService, - @ITerminalService readonly _terminalService: ITerminalService + @ITerminalService readonly _terminalService: ITerminalService, + @IConfigurationService readonly configurationService: IConfigurationService ) { super(); this._register(this._terminalService.onDidChangeActiveInstance(() => this.stop())); @@ -79,6 +85,14 @@ class VoiceSession extends Disposable { } start(): void { this.stop(); + let voiceTimeout = this.configurationService.getValue(AccessibilityVoiceSettingId.SpeechTimeout); + if (!isNumber(voiceTimeout) || voiceTimeout < 0) { + voiceTimeout = SpeechTimeoutDefault; + } + const acceptTranscriptionScheduler = this._disposables.add(new RunOnceScheduler(() => { + this._terminalService.activeInstance?.sendText(this._input, false); + this.stop(); + }, voiceTimeout)); this._cancellationTokenSource = new CancellationTokenSource(); const session = this._disposables.add(this._speechService.createSpeechToTextSession(this._cancellationTokenSource!.token)); this._disposables.add(session.onDidChange((e) => { @@ -89,15 +103,21 @@ class VoiceSession extends Disposable { case SpeechToTextStatus.Started: break; case SpeechToTextStatus.Recognizing: - // TODO: start audio cue, show in status bar + // TODO: start audio cue, show in terminal by the cursor + if (voiceTimeout > 0) { + acceptTranscriptionScheduler.cancel(); + } break; case SpeechToTextStatus.Recognized: if (e.text) { - this._terminalService.activeInstance?.sendText(e.text, false); + this._input = [this._input, e.text].join(' '); + } + if (voiceTimeout > 0) { + acceptTranscriptionScheduler.schedule(); } break; case SpeechToTextStatus.Stopped: - // TODO: stop audio cue, hide in status bar + // TODO: stop audio cue, hide in terminal by the cursor this.stop(); break; } @@ -106,5 +126,6 @@ class VoiceSession extends Disposable { stop(): void { this._cancellationTokenSource?.cancel(); this._disposables.dispose(); + this._input = ''; } } From 97f6e8e25579d57acbe4316deefa998dee1cf8b6 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 11 Jan 2024 09:30:11 -0800 Subject: [PATCH 011/333] rearrange, work on decoration --- .../electron-sandbox/voice.contribution.ts | 2 +- .../contrib/terminal/browser/terminal.ts | 5 +- .../terminal/browser/terminalInstance.ts | 6 +- .../browser}/voiceTerminalActions.ts | 63 +++++++++++++++---- 4 files changed, 61 insertions(+), 15 deletions(-) rename src/vs/workbench/contrib/{chat/electron-sandbox/actions => terminal/browser}/voiceTerminalActions.ts (72%) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/voice.contribution.ts b/src/vs/workbench/contrib/chat/electron-sandbox/voice.contribution.ts index 2eb682451efc5..89f8f793d4836 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/voice.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/voice.contribution.ts @@ -5,7 +5,7 @@ import { InlineVoiceChatAction, QuickVoiceChatAction, StartVoiceChatAction, StopListeningInInlineChatAction, StopListeningInQuickChatAction, StopListeningInChatEditorAction, StopListeningInChatViewAction, VoiceChatInChatViewAction, StopListeningAction, StopListeningAndSubmitAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; import { registerAction2 } from 'vs/platform/actions/common/actions'; -import { StartTerminalSpeechToTextAction, StopTerminalSpeechToTextAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions'; +import { StartTerminalSpeechToTextAction, StopTerminalSpeechToTextAction } from 'vs/workbench/contrib/terminal/browser/voiceTerminalActions'; registerAction2(StartVoiceChatAction); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index f5dfc7d63a0cc..540bf2041534b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -23,7 +23,7 @@ import { ITerminalStatusList } from 'vs/workbench/contrib/terminal/browser/termi import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; import { IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfiguration, ITerminalFont, ITerminalProcessExtHostProxy, ITerminalProcessInfo } from 'vs/workbench/contrib/terminal/common/terminal'; import { ISimpleSelectedSuggestion } from 'vs/workbench/services/suggest/browser/simpleSuggestWidget'; -import type { IMarker, ITheme, Terminal as RawXtermTerminal } from '@xterm/xterm'; +import type { IMarker, ITheme, IDecoration, IDecorationOptions, Terminal as RawXtermTerminal } from '@xterm/xterm'; import { ScrollPosition } from 'vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { GroupIdentifier } from 'vs/workbench/common/editor'; @@ -792,6 +792,9 @@ export interface ITerminalInstance extends IBaseTerminalInstance { */ registerMarker(): IMarker | undefined; + + registerDecoration(options: IDecorationOptions): IDecoration | undefined; + /** * Adds a marker to the buffer, mapping it to an ID if provided. */ diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 5ac44dc27182e..02bcb8397699c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -84,7 +84,7 @@ import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/ import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { importAMDNodeModule } from 'vs/amdX'; -import type { IMarker, Terminal as XTermTerminal } from '@xterm/xterm'; +import type { IMarker, IDecorationOptions, IDecoration, Terminal as XTermTerminal } from '@xterm/xterm'; import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { shouldPasteTerminalText } from 'vs/workbench/contrib/terminal/common/terminalClipboard'; @@ -1444,6 +1444,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return this.xterm?.raw.registerMarker(); } + public registerDecoration(options: IDecorationOptions): IDecoration | undefined { + return this.xterm?.raw.registerDecoration(options); + } + public addBufferMarker(properties: IMarkProperties): void { this.capabilities.get(TerminalCapability.BufferMarkDetection)?.addMark(properties); } diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts b/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts similarity index 72% rename from src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts rename to src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts index 58404b0ff1537..e728bd61eba0b 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceTerminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts @@ -16,6 +16,10 @@ import { HasSpeechProvider, ISpeechService, SpeechToTextStatus } from 'vs/workbe import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { isNumber } from 'vs/base/common/types'; +import type { IDecoration, Terminal } from '@xterm/xterm'; +import { IXtermMarker } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { ThemeIcon } from 'vs/base/common/themables'; +import { Codicon } from 'vs/base/common/codicons'; export class StartTerminalSpeechToTextAction extends Action2 { @@ -35,7 +39,7 @@ export class StartTerminalSpeechToTextAction extends Action2 { async run(accessor: ServicesAccessor): Promise { const instantiationService = accessor.get(IInstantiationService); - VoiceSession.getInstance(instantiationService).start(); + TerminalVoiceSession.getInstance(instantiationService).start(); } } @@ -58,31 +62,40 @@ export class StopTerminalSpeechToTextAction extends Action2 { async run(accessor: ServicesAccessor): Promise { const instantiationService = accessor.get(IInstantiationService); - VoiceSession.getInstance(instantiationService).stop(); + TerminalVoiceSession.getInstance(instantiationService).stop(); } } -class VoiceSession extends Disposable { +export class TerminalVoiceSession extends Disposable { private _input: string = ''; - private static instance: VoiceSession | undefined = undefined; - static getInstance(instantiationService: IInstantiationService): VoiceSession { - if (!VoiceSession.instance) { - VoiceSession.instance = instantiationService.createInstance(VoiceSession); + private _xterm: Terminal | undefined; + private _decoration: IDecoration | undefined; + private _marker: IXtermMarker | undefined; + private static _instance: TerminalVoiceSession | undefined = undefined; + static getInstance(instantiationService: IInstantiationService): TerminalVoiceSession { + if (!TerminalVoiceSession._instance) { + TerminalVoiceSession._instance = instantiationService.createInstance(TerminalVoiceSession); } - return VoiceSession.instance; + return TerminalVoiceSession._instance; } private _cancellationTokenSource: CancellationTokenSource | undefined; private _disposables = new DisposableStore(); constructor( @ISpeechService private readonly _speechService: ISpeechService, @ITerminalService readonly _terminalService: ITerminalService, - @IConfigurationService readonly configurationService: IConfigurationService + @IConfigurationService readonly configurationService: IConfigurationService, + @IInstantiationService readonly _instantationService: IInstantiationService ) { super(); this._register(this._terminalService.onDidChangeActiveInstance(() => this.stop())); this._register(this._terminalService.onDidDisposeInstance(() => this.stop())); } + + activate(terminal: Terminal): void { + this._xterm = terminal; + } + start(): void { this.stop(); let voiceTimeout = this.configurationService.getValue(AccessibilityVoiceSettingId.SpeechTimeout); @@ -102,12 +115,18 @@ class VoiceSession extends Disposable { switch (e.status) { case SpeechToTextStatus.Started: break; - case SpeechToTextStatus.Recognizing: - // TODO: start audio cue, show in terminal by the cursor + case SpeechToTextStatus.Recognizing: { + if (!this._decoration) { + console.log('terminal', this._xterm); + // TODO: start audio cue, show in terminal by the cursor + this._createDecoration(); + } + if (voiceTimeout > 0) { acceptTranscriptionScheduler.cancel(); } break; + } case SpeechToTextStatus.Recognized: if (e.text) { this._input = [this._input, e.text].join(' '); @@ -124,8 +143,28 @@ class VoiceSession extends Disposable { })); } stop(): void { + this._marker?.dispose(); + this._decoration?.dispose(); this._cancellationTokenSource?.cancel(); - this._disposables.dispose(); + this._disposables.clear(); this._input = ''; } + + private _createDecoration(): void { + this._marker = this._terminalService.activeInstance?.registerMarker(); + if (!this._marker) { + return; + } + this._decoration = this._terminalService.activeInstance?.registerDecoration({ + marker: this._marker, + layer: 'top' + }); + this._decoration?.onRender((e: HTMLElement) => { + e.classList.add(...ThemeIcon.asClassNameArray(Codicon.mic)); + e.classList.add('quick-fix'); + }); + console.log(this._decoration?.element); + } } + + From 2828de7e302e0a3cf7d67e6d870d4fec4976dce9 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 11 Jan 2024 13:07:23 -0800 Subject: [PATCH 012/333] use symbol map --- .../contrib/terminal/browser/terminal.ts | 5 +-- .../terminal/browser/terminalInstance.ts | 6 +-- .../terminal/browser/voiceTerminalActions.ts | 45 +++++++++++++------ 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 540bf2041534b..f5dfc7d63a0cc 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -23,7 +23,7 @@ import { ITerminalStatusList } from 'vs/workbench/contrib/terminal/browser/termi import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; import { IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfiguration, ITerminalFont, ITerminalProcessExtHostProxy, ITerminalProcessInfo } from 'vs/workbench/contrib/terminal/common/terminal'; import { ISimpleSelectedSuggestion } from 'vs/workbench/services/suggest/browser/simpleSuggestWidget'; -import type { IMarker, ITheme, IDecoration, IDecorationOptions, Terminal as RawXtermTerminal } from '@xterm/xterm'; +import type { IMarker, ITheme, Terminal as RawXtermTerminal } from '@xterm/xterm'; import { ScrollPosition } from 'vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { GroupIdentifier } from 'vs/workbench/common/editor'; @@ -792,9 +792,6 @@ export interface ITerminalInstance extends IBaseTerminalInstance { */ registerMarker(): IMarker | undefined; - - registerDecoration(options: IDecorationOptions): IDecoration | undefined; - /** * Adds a marker to the buffer, mapping it to an ID if provided. */ diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 02bcb8397699c..5ac44dc27182e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -84,7 +84,7 @@ import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/ import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { importAMDNodeModule } from 'vs/amdX'; -import type { IMarker, IDecorationOptions, IDecoration, Terminal as XTermTerminal } from '@xterm/xterm'; +import type { IMarker, Terminal as XTermTerminal } from '@xterm/xterm'; import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { shouldPasteTerminalText } from 'vs/workbench/contrib/terminal/common/terminalClipboard'; @@ -1444,10 +1444,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return this.xterm?.raw.registerMarker(); } - public registerDecoration(options: IDecorationOptions): IDecoration | undefined { - return this.xterm?.raw.registerDecoration(options); - } - public addBufferMarker(properties: IMarkProperties): void { this.capabilities.get(TerminalCapability.BufferMarkDetection)?.addMark(properties); } diff --git a/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts b/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts index e728bd61eba0b..c9947a3f2d25e 100644 --- a/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts @@ -16,7 +16,7 @@ import { HasSpeechProvider, ISpeechService, SpeechToTextStatus } from 'vs/workbe import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { isNumber } from 'vs/base/common/types'; -import type { IDecoration, Terminal } from '@xterm/xterm'; +import type { IDecoration } from '@xterm/xterm'; import { IXtermMarker } from 'vs/platform/terminal/common/capabilities/capabilities'; import { ThemeIcon } from 'vs/base/common/themables'; import { Codicon } from 'vs/base/common/codicons'; @@ -68,7 +68,6 @@ export class StopTerminalSpeechToTextAction extends Action2 { export class TerminalVoiceSession extends Disposable { private _input: string = ''; - private _xterm: Terminal | undefined; private _decoration: IDecoration | undefined; private _marker: IXtermMarker | undefined; private static _instance: TerminalVoiceSession | undefined = undefined; @@ -92,10 +91,6 @@ export class TerminalVoiceSession extends Disposable { this._register(this._terminalService.onDidDisposeInstance(() => this.stop())); } - activate(terminal: Terminal): void { - this._xterm = terminal; - } - start(): void { this.stop(); let voiceTimeout = this.configurationService.getValue(AccessibilityVoiceSettingId.SpeechTimeout); @@ -108,19 +103,19 @@ export class TerminalVoiceSession extends Disposable { }, voiceTimeout)); this._cancellationTokenSource = new CancellationTokenSource(); const session = this._disposables.add(this._speechService.createSpeechToTextSession(this._cancellationTokenSource!.token)); + this._disposables.add(session.onDidChange((e) => { if (this._cancellationTokenSource?.token.isCancellationRequested) { return; } switch (e.status) { case SpeechToTextStatus.Started: - break; - case SpeechToTextStatus.Recognizing: { if (!this._decoration) { - console.log('terminal', this._xterm); // TODO: start audio cue, show in terminal by the cursor this._createDecoration(); } + break; + case SpeechToTextStatus.Recognizing: { if (voiceTimeout > 0) { acceptTranscriptionScheduler.cancel(); @@ -129,7 +124,26 @@ export class TerminalVoiceSession extends Disposable { } case SpeechToTextStatus.Recognized: if (e.text) { - this._input = [this._input, e.text].join(' '); + const str = [this._input, e.text].join(' ').replaceAll(/[.,?;!]/g, ''); + const symbolMap: { [key: string]: string } = { + 'ampersand': '&', + 'dollar': '$', + 'at': '@', + 'percent': '%', + 'asterisk': '*', + 'plus': '+', + 'equals': '=', + 'exclamation': '!', + 'slash': '/', + 'backslash': '\\', + 'dot': '.', + 'period': '.', + }; + + for (const symbol in symbolMap) { + const regex: RegExp = new RegExp(symbol); + this._input = str.replace(regex, symbolMap[symbol]); + } } if (voiceTimeout > 0) { acceptTranscriptionScheduler.schedule(); @@ -140,11 +154,13 @@ export class TerminalVoiceSession extends Disposable { this.stop(); break; } + console.log(e.status); })); } stop(): void { this._marker?.dispose(); this._decoration?.dispose(); + this._decoration = undefined; this._cancellationTokenSource?.cancel(); this._disposables.clear(); this._input = ''; @@ -155,15 +171,18 @@ export class TerminalVoiceSession extends Disposable { if (!this._marker) { return; } - this._decoration = this._terminalService.activeInstance?.registerDecoration({ + this._decoration = this._terminalService.activeInstance?.xterm?.raw.registerDecoration({ marker: this._marker, - layer: 'top' + layer: 'top', + x: this._terminalService.activeInstance.xterm?.raw?.buffer.active.cursorX ?? this._terminalService.activeInstance.xterm?.raw?.buffer.active.cursorX! ?? 0, }); + console.log(this._terminalService.activeInstance?.xterm?.raw?.buffer.active.cursorX); this._decoration?.onRender((e: HTMLElement) => { e.classList.add(...ThemeIcon.asClassNameArray(Codicon.mic)); e.classList.add('quick-fix'); + e.style.zIndex = '1000'; + e.style.paddingLeft = '5px'; }); - console.log(this._decoration?.element); } } From eee233f1ed71b24e83acdb6ca499fc0d728d5aea Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 11 Jan 2024 14:35:42 -0800 Subject: [PATCH 013/333] get it to work --- .../terminal/browser/voiceTerminalActions.ts | 83 ++++++++++++------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts b/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts index c9947a3f2d25e..404068b4f593e 100644 --- a/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts @@ -12,7 +12,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { AccessibilityVoiceSettingId, SpeechTimeoutDefault } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; -import { HasSpeechProvider, ISpeechService, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; +import { HasSpeechProvider, ISpeechService, ISpeechToTextEvent, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { isNumber } from 'vs/base/common/types'; @@ -62,7 +62,7 @@ export class StopTerminalSpeechToTextAction extends Action2 { async run(accessor: ServicesAccessor): Promise { const instantiationService = accessor.get(IInstantiationService); - TerminalVoiceSession.getInstance(instantiationService).stop(); + TerminalVoiceSession.getInstance(instantiationService).stop(true); } } @@ -71,6 +71,7 @@ export class TerminalVoiceSession extends Disposable { private _decoration: IDecoration | undefined; private _marker: IXtermMarker | undefined; private static _instance: TerminalVoiceSession | undefined = undefined; + private _acceptTranscriptionScheduler: RunOnceScheduler | undefined; static getInstance(instantiationService: IInstantiationService): TerminalVoiceSession { if (!TerminalVoiceSession._instance) { TerminalVoiceSession._instance = instantiationService.createInstance(TerminalVoiceSession); @@ -97,7 +98,7 @@ export class TerminalVoiceSession extends Disposable { if (!isNumber(voiceTimeout) || voiceTimeout < 0) { voiceTimeout = SpeechTimeoutDefault; } - const acceptTranscriptionScheduler = this._disposables.add(new RunOnceScheduler(() => { + this._acceptTranscriptionScheduler = this._disposables.add(new RunOnceScheduler(() => { this._terminalService.activeInstance?.sendText(this._input, false); this.stop(); }, voiceTimeout)); @@ -110,54 +111,36 @@ export class TerminalVoiceSession extends Disposable { } switch (e.status) { case SpeechToTextStatus.Started: + // TODO: play start audio cue if (!this._decoration) { - // TODO: start audio cue, show in terminal by the cursor this._createDecoration(); } break; case SpeechToTextStatus.Recognizing: { - + this._updateInput(e); if (voiceTimeout > 0) { - acceptTranscriptionScheduler.cancel(); + this._acceptTranscriptionScheduler!.cancel(); } break; } case SpeechToTextStatus.Recognized: - if (e.text) { - const str = [this._input, e.text].join(' ').replaceAll(/[.,?;!]/g, ''); - const symbolMap: { [key: string]: string } = { - 'ampersand': '&', - 'dollar': '$', - 'at': '@', - 'percent': '%', - 'asterisk': '*', - 'plus': '+', - 'equals': '=', - 'exclamation': '!', - 'slash': '/', - 'backslash': '\\', - 'dot': '.', - 'period': '.', - }; - - for (const symbol in symbolMap) { - const regex: RegExp = new RegExp(symbol); - this._input = str.replace(regex, symbolMap[symbol]); - } - } + this._updateInput(e); if (voiceTimeout > 0) { - acceptTranscriptionScheduler.schedule(); + this._acceptTranscriptionScheduler!.schedule(); } break; case SpeechToTextStatus.Stopped: - // TODO: stop audio cue, hide in terminal by the cursor + // TODO: play stop audio cue this.stop(); break; } - console.log(e.status); })); } - stop(): void { + stop(send?: boolean): void { + if (send) { + this._acceptTranscriptionScheduler!.cancel(); + this._terminalService.activeInstance?.sendText(this._input, false); + } this._marker?.dispose(); this._decoration?.dispose(); this._decoration = undefined; @@ -166,6 +149,42 @@ export class TerminalVoiceSession extends Disposable { this._input = ''; } + private _updateInput(e: ISpeechToTextEvent): void { + if (e.text) { + let input = e.text.replaceAll(/[.,?;!]/g, ''); + const symbolMap: { [key: string]: string } = { + 'Ampersand': '&', + 'ampersand': '&', + 'Dollar': '$', + 'dollar': '$', + 'Percent': '%', + 'percent': '%', + 'Asterisk': '*', + 'asterisk': '*', + 'Plus': '+', + 'plus': '+', + 'Equals': '=', + 'equals': '=', + 'Exclamation': '!', + 'exclamation': '!', + 'Slash': '/', + 'slash': '/', + 'Backslash': '\\', + 'backslash': '\\', + 'Dot': '.', + 'dot': '.', + 'Period': '.', + 'period': '.' + }; + + for (const symbol in symbolMap) { + const regex: RegExp = new RegExp(symbol); + input = input.replace(regex, symbolMap[symbol]); + } + this._input = ' ' + input; + } + } + private _createDecoration(): void { this._marker = this._terminalService.activeInstance?.registerMarker(); if (!this._marker) { From db58cfdb063c44d7c621b2941793025335d2bebf Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 11 Jan 2024 15:12:27 -0800 Subject: [PATCH 014/333] cleanup --- .../terminal/browser/voiceTerminalActions.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts b/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts index 404068b4f593e..e55f9c4f4cfc4 100644 --- a/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts @@ -186,20 +186,23 @@ export class TerminalVoiceSession extends Disposable { } private _createDecoration(): void { - this._marker = this._terminalService.activeInstance?.registerMarker(); + const activeInstance = this._terminalService.activeInstance; + const xterm = activeInstance?.xterm?.raw; + if (!xterm) { + return; + } + this._marker = activeInstance.registerMarker(); if (!this._marker) { return; } - this._decoration = this._terminalService.activeInstance?.xterm?.raw.registerDecoration({ + this._decoration = xterm.registerDecoration({ marker: this._marker, layer: 'top', - x: this._terminalService.activeInstance.xterm?.raw?.buffer.active.cursorX ?? this._terminalService.activeInstance.xterm?.raw?.buffer.active.cursorX! ?? 0, + x: xterm.buffer.active.cursorX ?? 0, }); - console.log(this._terminalService.activeInstance?.xterm?.raw?.buffer.active.cursorX); this._decoration?.onRender((e: HTMLElement) => { e.classList.add(...ThemeIcon.asClassNameArray(Codicon.mic)); e.classList.add('quick-fix'); - e.style.zIndex = '1000'; e.style.paddingLeft = '5px'; }); } From 448fab1a7390fdb8edd61d2f864db36f4eed5525 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 12 Jan 2024 15:59:44 +0100 Subject: [PATCH 015/333] handle Recognized and Recognizing differently --- .../inlineChat/electron-sandbox/inlineChatQuickVoice.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts index 88333cf12151a..b666c9bbbc38a 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts @@ -193,8 +193,11 @@ export class InlineChatQuickVoice implements IEditorContribution { case SpeechToTextStatus.Stopped: break; case SpeechToTextStatus.Recognizing: + // TODO@jrieken special rendering for "in-flight" message? + this._widget.updateInput(!message ? e.text : `${message} ${e.text}`); + break; case SpeechToTextStatus.Recognized: - message = e.text; + message = !message ? e.text : `${message} ${e.text}`; this._widget.updateInput(message); break; } From 7f825208bf67fe5f81131e0ed36dcb9fa063e403 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 16 Jan 2024 12:58:50 +0530 Subject: [PATCH 016/333] fix duplicate registrations (#202557) --- .../workbench/contrib/userDataSync/browser/userDataSync.ts | 1 + .../contrib/userDataSync/browser/userDataSyncViews.ts | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index ce24dc14b3ef8..a4dd67a7f8b1c 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -840,6 +840,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private conflictsActionDisposable = this._register(new MutableDisposable()); private registerShowConflictsAction(): void { + this.conflictsActionDisposable.value = undefined; const that = this; this.conflictsActionDisposable.value = registerAction2(class TurningOnSyncAction extends Action2 { constructor() { diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts index 4362953931dea..248d8c0756ff0 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts @@ -224,7 +224,7 @@ export class UserDataSyncDataViews extends Disposable { registerAction2(class extends Action2 { constructor() { super({ - id: `workbench.actions.sync.resolveResource`, + id: `workbench.actions.sync.${viewId}.resolveResource`, title: localize('workbench.actions.sync.resolveResourceRef', "Show raw JSON sync data"), menu: { id: MenuId.ViewItemContext, @@ -242,7 +242,7 @@ export class UserDataSyncDataViews extends Disposable { registerAction2(class extends Action2 { constructor() { super({ - id: `workbench.actions.sync.compareWithLocal`, + id: `workbench.actions.sync.${viewId}.compareWithLocal`, title: localize('workbench.actions.sync.compareWithLocal', "Compare with Local"), menu: { id: MenuId.ViewItemContext, @@ -267,7 +267,7 @@ export class UserDataSyncDataViews extends Disposable { registerAction2(class extends Action2 { constructor() { super({ - id: `workbench.actions.sync.replaceCurrent`, + id: `workbench.actions.sync.${viewId}.replaceCurrent`, title: localize('workbench.actions.sync.replaceCurrent', "Restore"), icon: Codicon.discard, menu: { From a3e16ceaaeaa8e40106bd274c2590094d3ec35db Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 16 Jan 2024 08:49:36 +0100 Subject: [PATCH 017/333] zoom - fix keybinding tooltips (#202560) --- src/vs/workbench/electron-sandbox/window.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 6fe76f608793a..be37ea1a41ade 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -1153,7 +1153,7 @@ class ZoomStatusEntry extends Disposable { const zoomOutAction: Action = disposables.add(new Action('workbench.action.zoomOut', localize('zoomOut', "Zoom Out"), ThemeIcon.asClassName(Codicon.remove), true, () => this.commandService.executeCommand(zoomOutAction.id))); const zoomInAction: Action = disposables.add(new Action('workbench.action.zoomIn', localize('zoomIn', "Zoom In"), ThemeIcon.asClassName(Codicon.plus), true, () => this.commandService.executeCommand(zoomInAction.id))); const zoomResetAction: Action = disposables.add(new Action('workbench.action.zoomReset', localize('zoomReset', "Reset"), undefined, true, () => this.commandService.executeCommand(zoomResetAction.id))); - zoomResetAction.tooltip = localize('zoomResetLabel', "Reset Zoom ({1})", zoomResetAction.label, this.keybindingService.lookupKeybinding(zoomResetAction.id)?.getLabel()); + zoomResetAction.tooltip = localize('zoomResetLabel', "{0} ({1})", zoomResetAction.label, this.keybindingService.lookupKeybinding(zoomResetAction.id)?.getLabel()); const zoomSettingsAction: Action = disposables.add(new Action('workbench.action.openSettings', localize('zoomSettings', "Settings"), ThemeIcon.asClassName(Codicon.settingsGear), true, () => this.commandService.executeCommand(zoomSettingsAction.id, 'window.zoom'))); const zoomLevelLabel = disposables.add(new Action('zoomLabel', undefined, undefined, false)); From 70869b7bc6206fe54062bf0ed41405931b7590cf Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 16 Jan 2024 13:35:22 +0530 Subject: [PATCH 018/333] fix #78245 (#202562) --- src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts index 6c498870ddddf..2e978db3d4daf 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts @@ -241,10 +241,6 @@ function canToggleWordWrap(codeEditorService: ICodeEditorService, editor: ICodeE if (!model) { return false; } - if (model.uri.scheme === 'output') { - // in output editor - return false; - } if (editor.getOption(EditorOption.inDiffEditor)) { // this editor belongs to a diff editor for (const diffEditor of codeEditorService.listDiffEditors()) { From 61112949a619959f3fe709d21a549a594c53ebe9 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 16 Jan 2024 09:33:07 +0100 Subject: [PATCH 019/333] Git - do not show checkout commands when searching (#202567) --- extensions/git/src/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index a5f20e4108638..c803c61842567 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -23,7 +23,7 @@ import { RemoteSourceAction } from './api/git-base'; abstract class CheckoutCommandItem implements QuickPickItem { abstract get label(): string; get description(): string { return ''; } - get alwaysShow(): boolean { return true; } + get alwaysShow(): boolean { return false; } } class CreateBranchItem extends CheckoutCommandItem { From 48bc94d6465cdd54ae51b81c90401d2e97b6f28c Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:02:32 +0100 Subject: [PATCH 020/333] =?UTF-8?q?Git=20-=20=F0=9F=92=84=20make=20stash?= =?UTF-8?q?=20picker=20async=20(#202573)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/git/src/commands.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index c803c61842567..81abbd94aa049 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -262,6 +262,14 @@ class RepositoryItem implements QuickPickItem { constructor(public readonly path: string) { } } +class StashItem implements QuickPickItem { + get label(): string { return `#${this.stash.index}: ${this.stash.description}`; } + + get description(): string | undefined { return this.stash.branchName; } + + constructor(readonly stash: Stash) { } +} + interface ScmCommandOptions { repository?: boolean; diff?: boolean; @@ -3690,17 +3698,15 @@ export class CommandCenter { } private async pickStash(repository: Repository, placeHolder: string): Promise { - const stashes = await repository.getStashes(); - - if (stashes.length === 0) { - window.showInformationMessage(l10n.t('There are no stashes in the repository.')); - return; - } - - const picks = stashes.map(stash => ({ label: `#${stash.index}: ${stash.description}`, description: stash.branchName, stash })); - const result = await window.showQuickPick(picks, { placeHolder }); + const getStashQuickPickItems = async (): Promise => { + const stashes = await repository.getStashes(); + return stashes.length > 0 ? + stashes.map(stash => new StashItem(stash)) : + [{ label: l10n.t('$(info) This repository has no stashes.') }]; + }; - return result?.stash; + const result = await window.showQuickPick(getStashQuickPickItems(), { placeHolder }); + return result instanceof StashItem ? result.stash : undefined; } private async getStashFromUri(uri: Uri | undefined): Promise<{ repository: Repository; stash: Stash } | undefined> { From da97e117733bbf68b1e2fac8612a9a1f3ea822d6 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 16 Jan 2024 14:38:26 +0100 Subject: [PATCH 021/333] update quick voice keybindings/chords (disable) --- .../electron-sandbox/inlineChatQuickVoice.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts index b666c9bbbc38a..1e47eb5d485ef 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts @@ -6,7 +6,7 @@ import 'vs/css!./inlineChatQuickVoice'; import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { Codicon } from 'vs/base/common/codicons'; -import { KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { KeyCode } from 'vs/base/common/keyCodes'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; @@ -32,10 +32,11 @@ export class StartAction extends EditorAction2 { title: localize2('start', "Start Inline Voice Chat"), category: AbstractInlineChatAction.category, precondition: ContextKeyExpr.and(HasSpeechProvider, CTX_QUICK_CHAT_IN_PROGRESS.toNegated()), - keybinding: { - primary: KeyChord(KeyCode.F12, KeyCode.F12), - weight: KeybindingWeight.WorkbenchContrib - } + f1: true, + // keybinding: { + // primary: KeyChord(KeyCode.F12, KeyCode.F12), + // weight: KeybindingWeight.WorkbenchContrib + // } }); } @@ -52,8 +53,9 @@ export class StopAction extends EditorAction2 { title: localize2('stop', "Stop Inline Voice Chat"), category: AbstractInlineChatAction.category, precondition: ContextKeyExpr.and(HasSpeechProvider, CTX_QUICK_CHAT_IN_PROGRESS), + f1: true, keybinding: { - primary: KeyChord(KeyCode.F12, KeyCode.F12), + primary: KeyCode.Escape, weight: KeybindingWeight.WorkbenchContrib } }); From b8b5a4aad050f5ffabce934aa36f72b0f2c13665 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 16 Jan 2024 14:50:19 +0100 Subject: [PATCH 022/333] Move hunk computation into sessions, let strategies render them (#202578) * chore - move chat session service implementation and interface into their own files * chore - move chat saving service implementation and interface into their own files * - move hunks into session (instead of strategy) - recompute them after receiving AI changes - accept& discard moves hunks from textModelN to textModel0 and vice versa - service renames - tests * - session doesn't know about an editor, only service does - allow to "move" session to a different editor - let controller pickup session after move to its editor - session saving picks up orphand sessions * try to restore editors when group is still valid * ctrl - don't pause when cancellation happens during session create * fix tests --- .../emptyTextEditorHint.ts | 2 +- .../browser/inlineChat.contribution.ts | 10 +- .../inlineChat/browser/inlineChatActions.ts | 2 +- .../browser/inlineChatController.ts | 92 ++-- .../inlineChat/browser/inlineChatNotebook.ts | 2 +- .../browser/inlineChatSavingService.ts | 16 + ...ving.ts => inlineChatSavingServiceImpl.ts} | 153 +++--- .../inlineChat/browser/inlineChatSession.ts | 348 ++++++------- .../browser/inlineChatSessionService.ts | 53 ++ .../browser/inlineChatSessionServiceImpl.ts | 223 +++++++++ .../browser/inlineChatStrategies.ts | 456 +++++++----------- .../contrib/inlineChat/browser/utils.ts | 6 + .../test/browser/inlineChatController.test.ts | 8 +- .../test/browser/inlineChatSession.test.ts | 316 +++++++++++- .../contrib/editorHint/emptyCellEditorHint.ts | 2 +- .../view/cellParts/chat/cellChatController.ts | 3 +- 16 files changed, 1113 insertions(+), 579 deletions(-) create mode 100644 src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingService.ts rename src/vs/workbench/contrib/inlineChat/browser/{inlineChatSaving.ts => inlineChatSavingServiceImpl.ts} (52%) create mode 100644 src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionService.ts create mode 100644 src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts diff --git a/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts b/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts index ba47bfed2e7d6..c103f95c23aed 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts @@ -21,7 +21,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IContentActionHandler, renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; import { ApplyFileSnippetAction } from 'vs/workbench/contrib/snippets/browser/commands/fileTemplateSnippets'; -import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; import { IInlineChatService, IInlineChatSessionProvider } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts index bf3ff77ac1800..d68c3dc3dc8fc 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts @@ -10,17 +10,19 @@ import * as InlineChatActions from 'vs/workbench/contrib/inlineChat/browser/inli import { IInlineChatService, INLINE_CHAT_ID, INTERACTIVE_EDITOR_ACCESSIBILITY_HELP_ID } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { InlineChatServiceImpl } from 'vs/workbench/contrib/inlineChat/common/inlineChatServiceImpl'; -import { IInlineChatSessionService, InlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { InlineChatSessionServiceImpl } from './inlineChatSessionServiceImpl'; +import { IInlineChatSessionService } from './inlineChatSessionService'; import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { InlineChatNotebookContribution } from 'vs/workbench/contrib/inlineChat/browser/inlineChatNotebook'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { InlineChatAccessibleViewContribution } from './inlineChatAccessibleView'; -import { IInlineChatSavingService, InlineChatSavingService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSaving'; +import { InlineChatSavingServiceImpl } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl'; +import { IInlineChatSavingService } from './inlineChatSavingService'; registerSingleton(IInlineChatService, InlineChatServiceImpl, InstantiationType.Delayed); -registerSingleton(IInlineChatSessionService, InlineChatSessionService, InstantiationType.Delayed); -registerSingleton(IInlineChatSavingService, InlineChatSavingService, InstantiationType.Delayed); +registerSingleton(IInlineChatSessionService, InlineChatSessionServiceImpl, InstantiationType.Delayed); +registerSingleton(IInlineChatSavingService, InlineChatSavingServiceImpl, InstantiationType.Delayed); registerEditorContribution(INLINE_CHAT_ID, InlineChatController, EditorContributionInstantiation.Eager); // EAGER because of notebook dispose/create of editors registerEditorContribution(INTERACTIVE_EDITOR_ACCESSIBILITY_HELP_ID, InlineChatActions.InlineAccessibilityHelpContribution, EditorContributionInstantiation.Eventually); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index ecca297e8687e..c16518017fbd6 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -22,7 +22,7 @@ import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/ import { IUntitledTextResourceEditorInput } from 'vs/workbench/common/editor'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { fromNow } from 'vs/base/common/date'; -import { IInlineChatSessionService, Recording } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { IInlineChatSessionService, Recording } from './inlineChatSessionService'; import { runAccessibilityHelpAction } from 'vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp'; import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; import { Disposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index a6f3a10de3760..91e8f5a812665 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -39,8 +39,9 @@ import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/cont import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; -import { IInlineChatSavingService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSaving'; -import { EmptyResponse, ErrorResponse, ExpansionState, IInlineChatSessionService, ReplyResponse, Session, SessionExchange, SessionPrompt } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { IInlineChatSavingService } from './inlineChatSavingService'; +import { EmptyResponse, ErrorResponse, ExpansionState, ReplyResponse, Session, SessionExchange, SessionPrompt } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { IInlineChatSessionService } from './inlineChatSessionService'; import { EditModeStrategy, LivePreviewStrategy, LiveStrategy, PreviewStrategy, ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies'; import { IInlineChatMessageAppender, InlineChatZoneWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_RESPONSE_TYPES, CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING, CTX_INLINE_CHAT_USER_DID_EDIT, EditMode, IInlineChatProgressItem, IInlineChatRequest, IInlineChatResponse, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseFeedbackKind, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; @@ -122,7 +123,7 @@ export class InlineChatController implements IEditorContribution { readonly onDidCancelInput = Event.filter(this._messages.event, m => m === Message.CANCEL_INPUT || m === Message.CANCEL_SESSION, this._store); private readonly _sessionStore: DisposableStore = this._store.add(new DisposableStore()); - private readonly _pausedStrategies = new Map(); + private _session?: Session; private _strategy?: EditModeStrategy; private _ignoreModelContentChanged = false; @@ -161,17 +162,19 @@ export class InlineChatController implements IEditorContribution { return; } - this._log('session RESUMING', e); + this._log('session RESUMING after model change', e); await this.run({ existingSession }); - this._log('session done or paused'); })); - this._log('NEW controller'); - this._store.add(this._inlineChatSessionService.onDidEndSession(e => { - this._pausedStrategies.get(e.session)?.dispose(); - this._pausedStrategies.delete(e.session); + this._store.add(this._inlineChatSessionService.onDidMoveSession(async e => { + if (e.editor === this._editor) { + this._log('session RESUMING after move', e); + await this.run({ existingSession: e.session }); + } })); + this._log('NEW controller'); + InlineChatController._promptHistory = JSON.parse(_storageService.get(InlineChatController._storageKey, StorageScope.PROFILE, '[]')); this._historyUpdate = (prompt: string) => { const idx = InlineChatController._promptHistory.indexOf(prompt); @@ -264,7 +267,7 @@ export class InlineChatController implements IEditorContribution { } } - private async [State.CREATE_SESSION](options: InlineChatRunOptions): Promise { + private async [State.CREATE_SESSION](options: InlineChatRunOptions): Promise { assertType(this._session === undefined); assertType(this._editor.hasModel()); @@ -305,7 +308,10 @@ export class InlineChatController implements IEditorContribution { msgListener.dispose(); if (createSessionCts.token.isCancellationRequested) { - return State.PAUSE; + if (session) { + this._inlineChatSessionService.releaseSession(session); + } + return State.CANCEL; } } @@ -317,24 +323,18 @@ export class InlineChatController implements IEditorContribution { return State.CANCEL; } - if (this._pausedStrategies.has(session)) { - // maybe a strategy was previously paused, use it - this._strategy = this._pausedStrategies.get(session)!; - this._pausedStrategies.delete(session); - } else { - // create a new strategy - switch (session.editMode) { - case EditMode.Live: - this._strategy = this._instaService.createInstance(LiveStrategy, session, this._editor, this._zone.value); - break; - case EditMode.Preview: - this._strategy = this._instaService.createInstance(PreviewStrategy, session, this._zone.value); - break; - case EditMode.LivePreview: - default: - this._strategy = this._instaService.createInstance(LivePreviewStrategy, session, this._editor, this._zone.value); - break; - } + // create a new strategy + switch (session.editMode) { + case EditMode.Live: + this._strategy = this._instaService.createInstance(LiveStrategy, session, this._editor, this._zone.value); + break; + case EditMode.Preview: + this._strategy = this._instaService.createInstance(PreviewStrategy, session, this._zone.value); + break; + case EditMode.LivePreview: + default: + this._strategy = this._instaService.createInstance(LivePreviewStrategy, session, this._editor, this._zone.value); + break; } this._session = session; @@ -690,7 +690,13 @@ export class InlineChatController implements IEditorContribution { msgListener.dispose(); typeListener.dispose(); - if (request.live && !(response instanceof ReplyResponse)) { + if (response instanceof ReplyResponse) { + // update hunks after a reply response + await this._session.hunkData.recompute(); + + } else if (request.live) { + // undo changes that might have been made when not + // having a reply response this._strategy?.undoChanges(modelAltVersionIdNow); } @@ -803,7 +809,6 @@ export class InlineChatController implements IEditorContribution { this._resetWidget(); - this._pausedStrategies.set(this._session, this._strategy); this._strategy.pause?.(); this._session = undefined; } @@ -831,21 +836,22 @@ export class InlineChatController implements IEditorContribution { } private async[State.CANCEL]() { - assertType(this._session); - assertType(this._strategy); - this._sessionStore.clear(); - - try { - await this._strategy.cancel(); - } catch (err) { - this._dialogService.error(localize('err.discard', "Failed to discard changes.", toErrorMessage(err))); - this._log('FAILED to discard changes'); - this._log(err); + if (this._session) { + // assertType(this._session); + assertType(this._strategy); + this._sessionStore.clear(); + + try { + await this._strategy.cancel(); + } catch (err) { + this._dialogService.error(localize('err.discard', "Failed to discard changes.", toErrorMessage(err))); + this._log('FAILED to discard changes'); + this._log(err); + } + this._inlineChatSessionService.releaseSession(this._session); } this._resetWidget(); - this._inlineChatSessionService.releaseSession(this._session); - this._strategy?.dispose(); this._strategy = undefined; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatNotebook.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatNotebook.ts index 7846aa594a9f4..2a360f95b2a0b 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatNotebook.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatNotebook.ts @@ -9,7 +9,7 @@ import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; -import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { IInlineChatSessionService } from './inlineChatSessionService'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingService.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingService.ts new file mode 100644 index 0000000000000..0ed8719ecf810 --- /dev/null +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingService.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; + + +export const IInlineChatSavingService = createDecorator('IInlineChatSavingService '); + +export interface IInlineChatSavingService { + _serviceBrand: undefined; + + markChanged(session: Session): void; + +} diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSaving.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts similarity index 52% rename from src/vs/workbench/contrib/inlineChat/browser/inlineChatSaving.ts rename to src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts index c6f7dba516b1d..82effd20020f5 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSaving.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts @@ -5,37 +5,31 @@ import { raceCancellation } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { DisposableStore, MutableDisposable, dispose } from 'vs/base/common/lifecycle'; -import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { DisposableStore, IDisposable, MutableDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { ITextModel } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { DEFAULT_EDITOR_ASSOCIATION, SaveReason } from 'vs/workbench/common/editor'; -import { IInlineChatSessionService, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { IInlineChatSessionService } from './inlineChatSessionService'; import { InlineChatConfigKeys } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { GroupsOrder, IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; - -export const IInlineChatSavingService = createDecorator('IInlineChatSavingService '); - -export interface IInlineChatSavingService { - _serviceBrand: undefined; - - markChanged(session: Session): void; - -} +import { IInlineChatSavingService } from './inlineChatSavingService'; +import { Iterable } from 'vs/base/common/iterator'; +import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; interface SessionData { readonly dispose: () => void; readonly session: Session; - readonly group: IEditorGroup; + readonly groupCandidate: IEditorGroup; } -export class InlineChatSavingService implements IInlineChatSavingService { +export class InlineChatSavingServiceImpl implements IInlineChatSavingService { declare readonly _serviceBrand: undefined; @@ -68,13 +62,12 @@ export class InlineChatSavingService implements IInlineChatSavingService { this._installSaveParticpant(); } - const disposable = this._fileConfigService.disableAutoSave(session.textModelN.uri); - const group = this._getEditorGroup(session); + const saveConfig = this._fileConfigService.disableAutoSave(session.textModelN.uri); this._sessionData.set(session, { + groupCandidate: this._editorGroupService.activeGroup, session, - group, dispose: () => { - disposable.dispose(); + saveConfig.dispose(); this._sessionData.delete(session); if (this._sessionData.size === 0) { this._saveParticipant.clear(); @@ -114,60 +107,104 @@ export class InlineChatSavingService implements IInlineChatSavingService { return; } - const store = new DisposableStore(); - - const allDone = new Promise(resolve => { - store.add(this._inlineChatSessionService.onDidEndSession(e => { - - const data = sessions.get(e.session); - if (!data) { - return; - } - - data.dispose(); - sessions.delete(e.session); - - if (sessions.size === 0) { - resolve(); // DONE, release save block! - } - })); - }); - progress.report({ message: sessions.size === 1 ? localize('inlineChat', "Waiting for Inline Chat changes to be Accepted or Discarded...") : localize('inlineChat.N', "Waiting for Inline Chat changes in {0} editors to be Accepted or Discarded...", sessions.size) }); - await this._revealInlineChatSessions(sessions.values()); + // reveal all sessions in order and also show dangling sessions + const { groups, orphans } = this._getGroupsAndOrphans(sessions.values()); + const editorsOpenedAndSessionsEnded = this._openAndWait(groups, token).then(() => { + if (token.isCancellationRequested) { + return; + } + return this._openAndWait(Iterable.map(orphans, s => [this._editorGroupService.activeGroup, s]), token); + }); - try { - await raceCancellation(allDone, token); - } finally { - store.dispose(); - } - } + // fallback: resolve when all sessions for this model have been resolved. this is independent of the editor opening + const allSessionsEnded = this._waitForSessions(Iterable.concat(groups.values(), orphans), token); - private _getEditorGroup(session: Session): IEditorGroup { - const candidate = this._editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).find(group => { - return getCodeEditor(group.activeEditorPane?.getControl()) === session.editor; - }); - return candidate ?? this._editorGroupService.activeGroup; + await Promise.race([allSessionsEnded, editorsOpenedAndSessionsEnded]); } - private async _revealInlineChatSessions(sessions: Iterable): Promise { + private _getGroupsAndOrphans(sessions: Iterable) { - for (const data of sessions) { + const groupByEditor = new Map(); + for (const group of this._editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE)) { + const candidate = group.activeEditorPane?.getControl(); + if (isCodeEditor(candidate)) { + groupByEditor.set(candidate, group); + } + } - const inputs = data.group - .findEditors(data.session.textModelN.uri) - .filter(input => input.editorId === DEFAULT_EDITOR_ASSOCIATION.id); + const groups = new Map(); + const orphans = new Set(); - if (inputs.length === 0) { - await this._editorService.openEditor({ resource: data.session.textModelN.uri }, data.group); + for (const data of sessions) { + const editor = this._inlineChatSessionService.getCodeEditor(data.session); + const group = groupByEditor.get(editor); + if (group) { + // there is only one session per group because all sessions have the same model + // because we save one file. + groups.set(group, data); + } else if (this._editorGroupService.groups.includes(data.groupCandidate)) { + // the group candidate is still there. use it + groups.set(data.groupCandidate, data); } else { - await data.group.openEditor(inputs[0]); + orphans.add(data); } } + return { groups, orphans }; + } + + private async _openAndWait(groups: Iterable<[IEditorGroup, SessionData]>, token: CancellationToken) { + const sessions = new Set(); + for (const [group, data] of groups) { + const input: IResourceEditorInput = { resource: data.session.textModelN.uri, options: { override: DEFAULT_EDITOR_ASSOCIATION.id } }; + const pane = await this._editorService.openEditor(input, group); + const ctrl = pane?.getControl(); + if (!isCodeEditor(ctrl)) { + // PANIC + return; + } + this._inlineChatSessionService.moveSession(data.session, ctrl); + sessions.add(data); + } + await this._waitForSessions(sessions, token); + } + + private async _waitForSessions(iterable: Iterable, token: CancellationToken) { + + const sessions = new Map(); + for (const item of iterable) { + sessions.set(item.session, item); + } + + if (sessions.size === 0) { + // nothing to do + return; + } + + let listener: IDisposable | undefined; + + const whenEnded = new Promise(resolve => { + listener = this._inlineChatSessionService.onDidEndSession(e => { + const data = sessions.get(e.session); + if (data) { + data.dispose(); + sessions.delete(e.session); + if (sessions.size === 0) { + resolve(); // DONE, release waiting + } + } + }); + }); + + try { + await raceCancellation(whenEnded, token); + } finally { + listener?.dispose(); + } } } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index 4ece029e74b5f..97c7d2cab7c6b 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -8,23 +8,13 @@ import { Emitter, Event } from 'vs/base/common/event'; import { ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { IWorkspaceTextEdit, TextEdit, WorkspaceEdit } from 'vs/editor/common/languages'; import { IModelDecorationOptions, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; -import { EditMode, IInlineChatSessionProvider, IInlineChatSession, IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatResponse, IInlineChatService, InlineChatResponseType, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { EditMode, IInlineChatSessionProvider, IInlineChatSession, IInlineChatBulkEditResponse, IInlineChatEditResponse, InlineChatResponseType, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IModelService } from 'vs/editor/common/services/model'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { ModelDecorationOptions, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; -import { ILogService } from 'vs/platform/log/common/log'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { Iterable } from 'vs/base/common/iterator'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { isCancellationError } from 'vs/base/common/errors'; import { EditOperation, ISingleEditOperation } from 'vs/editor/common/core/editOperation'; -import { raceCancellation } from 'vs/base/common/async'; -import { DetailedLineRangeMapping, LineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { DetailedLineRangeMapping, LineRangeMapping, RangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -32,14 +22,15 @@ import { ILanguageService } from 'vs/editor/common/languages/language'; import { ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; +import { Recording } from './inlineChatSessionService'; +import { LineRange } from 'vs/editor/common/core/lineRange'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; +import { asRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; +import { coalesceInPlace } from 'vs/base/common/arrays'; +import { Iterable } from 'vs/base/common/iterator'; -export type Recording = { - when: Date; - session: IInlineChatSession; - exchanges: { prompt: string; res: IInlineChatResponse }[]; -}; -type TelemetryData = { +export type TelemetryData = { extension: string; rounds: string; undos: string; @@ -50,7 +41,7 @@ type TelemetryData = { editMode: string; }; -type TelemetryDataClassification = { +export type TelemetryDataClassification = { owner: 'jrieken'; comment: 'Data about an interaction editor session'; extension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The extension providing the data' }; @@ -69,7 +60,7 @@ export enum ExpansionState { NOT_CROPPED = 'not_cropped' } -class SessionWholeRange { +export class SessionWholeRange { private static readonly _options: IModelDecorationOptions = ModelDecorationOptions.register({ description: 'inlineChat/session/wholeRange' }); @@ -149,12 +140,12 @@ export class Session { constructor( readonly editMode: EditMode, - readonly editor: ICodeEditor, readonly textModel0: ITextModel, readonly textModelN: ITextModel, readonly provider: IInlineChatSessionProvider, readonly session: IInlineChatSession, - readonly wholeRange: SessionWholeRange + readonly wholeRange: SessionWholeRange, + readonly hunkData: HunkData, ) { this.textModelNAltVersion = textModelN.getAlternativeVersionId(); this._teldata = { @@ -412,182 +403,201 @@ export class ReplyResponse { } } -export interface ISessionKeyComputer { - getComparisonKey(editor: ICodeEditor, uri: URI): string; -} - -export const IInlineChatSessionService = createDecorator('IInlineChatSessionService'); - -export interface IInlineChatSessionService { - _serviceBrand: undefined; - - onWillStartSession: Event; - - onDidEndSession: Event<{ editor: ICodeEditor; session: Session }>; +// --- - createSession(editor: IActiveCodeEditor, options: { editMode: EditMode; wholeRange?: IRange }, token: CancellationToken): Promise; +export class HunkData { - getSession(editor: ICodeEditor, uri: URI): Session | undefined; + private static readonly _HUNK_TRACKED_RANGE = ModelDecorationOptions.register({ + description: 'inline-chat-hunk-tracked-range', + }); - releaseSession(session: Session): void; - - registerSessionKeyComputer(scheme: string, value: ISessionKeyComputer): IDisposable; - - // - - recordings(): readonly Recording[]; - - dispose(): void; -} - -type SessionData = { - session: Session; - store: IDisposable; -}; + private static readonly _HUNK_THRESHOLD = 8; -export class InlineChatSessionService implements IInlineChatSessionService { - - declare _serviceBrand: undefined; - - private readonly _onWillStartSession = new Emitter(); - readonly onWillStartSession: Event = this._onWillStartSession.event; - - private readonly _onDidEndSession = new Emitter<{ editor: ICodeEditor; session: Session }>(); - readonly onDidEndSession: Event<{ editor: ICodeEditor; session: Session }> = this._onDidEndSession.event; - - private readonly _sessions = new Map(); - private readonly _keyComputers = new Map(); - private _recordings: Recording[] = []; + private readonly _data = new Map(); constructor( - @IInlineChatService private readonly _inlineChatService: IInlineChatService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, - @IModelService private readonly _modelService: IModelService, - @ITextModelService private readonly _textModelService: ITextModelService, - @ILogService private readonly _logService: ILogService, + @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, + private readonly _textModel0: ITextModel, + private readonly _textModelN: ITextModel, ) { } - dispose() { - this._onWillStartSession.dispose(); - this._onDidEndSession.dispose(); - this._sessions.forEach(x => x.store.dispose()); - this._sessions.clear(); + dispose(): void { + if (!this._textModelN.isDisposed()) { + this._textModelN.changeDecorations(accessor => { + for (const { textModelNDecorations } of this._data.values()) { + textModelNDecorations.forEach(accessor.removeDecoration, accessor); + } + }); + } + if (!this._textModel0.isDisposed()) { + this._textModel0.changeDecorations(accessor => { + for (const { textModel0Decorations } of this._data.values()) { + textModel0Decorations.forEach(accessor.removeDecoration, accessor); + } + }); + } } - async createSession(editor: IActiveCodeEditor, options: { editMode: EditMode; wholeRange?: Range }, token: CancellationToken): Promise { + async recompute() { - const provider = Iterable.first(this._inlineChatService.getAllProvider()); - if (!provider) { - this._logService.trace('[IE] NO provider found'); - return undefined; - } + const diff = await this._editorWorkerService.computeDiff(this._textModel0.uri, this._textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: false }, 'advanced'); - this._onWillStartSession.fire(editor); - - const textModel = editor.getModel(); - const selection = editor.getSelection(); - let raw: IInlineChatSession | undefined | null; - try { - raw = await raceCancellation( - Promise.resolve(provider.prepareInlineChatSession(textModel, selection, token)), - token - ); - } catch (error) { - this._logService.error('[IE] FAILED to prepare session', provider.debugName); - this._logService.error(error); - return undefined; - } - if (!raw) { - this._logService.trace('[IE] NO session', provider.debugName); - return undefined; + if (!diff || diff.changes.length === 0) { + // return new HunkData([], session); + return; } - this._logService.trace('[IE] NEW session', provider.debugName); - - this._logService.trace(`[IE] creating NEW session for ${editor.getId()}, ${provider.debugName}`); - const store = new DisposableStore(); - - // create: keep a reference to prevent disposal of the "actual" model - const refTextModelN = await this._textModelService.createModelReference(textModel.uri); - store.add(refTextModelN); - - // create: keep a snapshot of the "actual" model - const textModel0 = this._modelService.createModel( - createTextBufferFactoryFromSnapshot(textModel.createSnapshot()), - { languageId: textModel.getLanguageId(), onDidChange: Event.None }, - undefined, true - ); - store.add(textModel0); - - let wholeRange = options.wholeRange; - if (!wholeRange) { - wholeRange = raw.wholeRange ? Range.lift(raw.wholeRange) : editor.getSelection(); + + // merge changes neighboring changes + const mergedChanges = [diff.changes[0]]; + for (let i = 1; i < diff.changes.length; i++) { + const lastChange = mergedChanges[mergedChanges.length - 1]; + const thisChange = diff.changes[i]; + if (thisChange.modified.startLineNumber - lastChange.modified.endLineNumberExclusive <= HunkData._HUNK_THRESHOLD) { + mergedChanges[mergedChanges.length - 1] = new DetailedLineRangeMapping( + lastChange.original.join(thisChange.original), + lastChange.modified.join(thisChange.modified), + (lastChange.innerChanges ?? []).concat(thisChange.innerChanges ?? []) + ); + } else { + mergedChanges.push(thisChange); + } } + const hunks = mergedChanges.map(change => new RawHunk(change.original, change.modified, change.innerChanges ?? [])); - // install managed-marker for the decoration range - const wholeRangeMgr = new SessionWholeRange(textModel, wholeRange); - store.add(wholeRangeMgr); + this._textModelN.changeDecorations(accessorN => { - const session = new Session(options.editMode, editor, textModel0, textModel, provider, raw, wholeRangeMgr); + this._textModel0.changeDecorations(accessor0 => { - // store: key -> session - const key = this._key(editor, textModel.uri); - if (this._sessions.has(key)) { - store.dispose(); - throw new Error(`Session already stored for ${key}`); - } - this._sessions.set(key, { session, store }); - return session; - } + // clean up old decorations + for (const { textModelNDecorations, textModel0Decorations } of this._data.values()) { + textModelNDecorations.forEach(accessorN.removeDecoration, accessorN); + textModel0Decorations.forEach(accessor0.removeDecoration, accessor0); + } - releaseSession(session: Session): void { + this._data.clear(); - const { editor } = session; + // add new decorations + for (const hunk of hunks) { - // cleanup - for (const [key, value] of this._sessions) { - if (value.session === session) { - value.store.dispose(); - this._sessions.delete(key); - this._logService.trace(`[IE] did RELEASED session for ${editor.getId()}, ${session.provider.debugName}`); - break; - } - } + const textModelNDecorations: string[] = []; + const textModel0Decorations: string[] = []; - // keep recording - const newLen = this._recordings.unshift(session.asRecording()); - if (newLen > 5) { - this._recordings.pop(); - } + textModelNDecorations.push(accessorN.addDecoration(asRange(hunk.modified, this._textModelN), HunkData._HUNK_TRACKED_RANGE)); + textModel0Decorations.push(accessor0.addDecoration(asRange(hunk.original, this._textModel0), HunkData._HUNK_TRACKED_RANGE)); - // send telemetry - this._telemetryService.publicLog2('interactiveEditor/session', session.asTelemetryData()); + for (const change of hunk.changes) { + textModelNDecorations.push(accessorN.addDecoration(change.modifiedRange, HunkData._HUNK_TRACKED_RANGE)); + textModel0Decorations.push(accessor0.addDecoration(change.originalRange, HunkData._HUNK_TRACKED_RANGE)); + } - this._onDidEndSession.fire({ editor, session }); - } + this._data.set(hunk, { + textModelNDecorations, + textModel0Decorations, + state: HunkState.Pending + }); + } + }); + }); + } + + get size(): number { + return this._data.size; + } + + get pending(): number { + return Iterable.reduce(this._data.values(), (r, { state }) => r + (state === HunkState.Pending ? 1 : 0), 0); + } + + getInfo(): HunkInformation[] { + + const result: HunkInformation[] = []; + + for (const [hunk, data] of this._data.entries()) { + const item: HunkInformation = { + getState: () => { + return data.state; + }, + isInsertion: () => { + return hunk.original.isEmpty; + }, + getRangesN: () => { + const ranges = data.textModelNDecorations.map(id => this._textModelN.getDecorationRange(id)); + coalesceInPlace(ranges); + return ranges; + }, + getRanges0: () => { + const ranges = data.textModel0Decorations.map(id => this._textModel0.getDecorationRange(id)); + coalesceInPlace(ranges); + return ranges; + }, + discardChanges: () => { + // DISCARD: replace modified range with original value. The modified range is retrieved from a decoration + // which was created above so that typing in the editor keeps discard working. + if (data.state === HunkState.Pending) { + const edits: ISingleEditOperation[] = []; + const rangesN = item.getRangesN(); + const ranges0 = item.getRanges0(); + for (let i = 1; i < rangesN.length; i++) { + const modifiedRange = rangesN[i]; + const originalValue = this._textModel0.getValueInRange(ranges0[i]); + edits.push(EditOperation.replace(modifiedRange, originalValue)); + } + this._textModelN.pushEditOperations(null, edits, () => null); + data.state = HunkState.Rejected; + } + }, + acceptChanges: () => { + // ACCEPT: replace original range with modified value. The modified value is retrieved from the model via + // its decoration and the original range is retrieved from the hunk. + if (data.state === HunkState.Pending) { + const edits: ISingleEditOperation[] = []; + const rangesN = item.getRangesN(); + const ranges0 = item.getRanges0(); + for (let i = 1; i < ranges0.length; i++) { + const originalRange = ranges0[i]; + const modifiedValue = this._textModelN.getValueInRange(rangesN[i]); + edits.push(EditOperation.replace(originalRange, modifiedValue)); + } + this._textModel0.pushEditOperations(null, edits, () => null); + data.state = HunkState.Accepted; + } + } + }; + result.push(item); + } - getSession(editor: ICodeEditor, uri: URI): Session | undefined { - const key = this._key(editor, uri); - return this._sessions.get(key)?.session; + return result; } +} + +class RawHunk { + constructor( + readonly original: LineRange, + readonly modified: LineRange, + readonly changes: RangeMapping[] + ) { } +} - private _key(editor: ICodeEditor, uri: URI): string { - const item = this._keyComputers.get(uri.scheme); - return item - ? item.getComparisonKey(editor, uri) - : `${editor.getId()}@${uri.toString()}`; +export const enum HunkState { + Pending = 0, + Accepted = 1, + Rejected = 2 +} - } +export interface HunkInformation { + /** + * The first element [0] is the whole modified range and subsequent elements are word-level changes + */ + getRangesN(): Range[]; - registerSessionKeyComputer(scheme: string, value: ISessionKeyComputer): IDisposable { - this._keyComputers.set(scheme, value); - return toDisposable(() => this._keyComputers.delete(scheme)); - } + getRanges0(): Range[]; - // --- debug + isInsertion(): boolean; - recordings(): readonly Recording[] { - return this._recordings; - } + discardChanges(): void; + + acceptChanges(): void; + getState(): HunkState; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionService.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionService.ts new file mode 100644 index 0000000000000..ee8de25c12d6e --- /dev/null +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionService.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { URI } from 'vs/base/common/uri'; +import { Event } from 'vs/base/common/event'; +import { EditMode, IInlineChatSession, IInlineChatResponse } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { IRange } from 'vs/editor/common/core/range'; +import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Session } from './inlineChatSession'; + + +export type Recording = { + when: Date; + session: IInlineChatSession; + exchanges: { prompt: string; res: IInlineChatResponse }[]; +}; + +export interface ISessionKeyComputer { + getComparisonKey(editor: ICodeEditor, uri: URI): string; +} + +export const IInlineChatSessionService = createDecorator('IInlineChatSessionService'); + +export interface IInlineChatSessionService { + _serviceBrand: undefined; + + onWillStartSession: Event; + + onDidMoveSession: Event<{ editor: ICodeEditor; session: Session }>; + + onDidEndSession: Event<{ editor: ICodeEditor; session: Session }>; + + createSession(editor: IActiveCodeEditor, options: { editMode: EditMode; wholeRange?: IRange }, token: CancellationToken): Promise; + + moveSession(session: Session, newEditor: ICodeEditor): void; + + getCodeEditor(session: Session): ICodeEditor; + + getSession(editor: ICodeEditor, uri: URI): Session | undefined; + + releaseSession(session: Session): void; + + registerSessionKeyComputer(scheme: string, value: ISessionKeyComputer): IDisposable; + + // + recordings(): readonly Recording[]; + + dispose(): void; +} diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts new file mode 100644 index 0000000000000..57e865322f9a4 --- /dev/null +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts @@ -0,0 +1,223 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { URI } from 'vs/base/common/uri'; +import { Emitter, Event } from 'vs/base/common/event'; +import { EditMode, IInlineChatSession, IInlineChatService } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { Range } from 'vs/editor/common/core/range'; +import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IModelService } from 'vs/editor/common/services/model'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; +import { ILogService } from 'vs/platform/log/common/log'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Iterable } from 'vs/base/common/iterator'; +import { raceCancellation } from 'vs/base/common/async'; +import { Recording, IInlineChatSessionService, ISessionKeyComputer } from './inlineChatSessionService'; +import { HunkData, Session, SessionWholeRange, TelemetryData, TelemetryDataClassification } from './inlineChatSession'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; + +type SessionData = { + editor: ICodeEditor; + session: Session; + store: IDisposable; +}; + +export class InlineChatSessionServiceImpl implements IInlineChatSessionService { + + declare _serviceBrand: undefined; + + private readonly _onWillStartSession = new Emitter(); + readonly onWillStartSession: Event = this._onWillStartSession.event; + + private readonly _onDidMoveSession = new Emitter<{ session: Session; editor: ICodeEditor }>(); + readonly onDidMoveSession: Event<{ session: Session; editor: ICodeEditor }> = this._onDidMoveSession.event; + + private readonly _onDidEndSession = new Emitter<{ editor: ICodeEditor; session: Session }>(); + readonly onDidEndSession: Event<{ editor: ICodeEditor; session: Session }> = this._onDidEndSession.event; + + private readonly _sessions = new Map(); + private readonly _keyComputers = new Map(); + private _recordings: Recording[] = []; + + constructor( + @IInlineChatService private readonly _inlineChatService: IInlineChatService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IModelService private readonly _modelService: IModelService, + @ITextModelService private readonly _textModelService: ITextModelService, + @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, + @ILogService private readonly _logService: ILogService + ) { } + + dispose() { + this._onWillStartSession.dispose(); + this._onDidEndSession.dispose(); + this._sessions.forEach(x => x.store.dispose()); + this._sessions.clear(); + } + + async createSession(editor: IActiveCodeEditor, options: { editMode: EditMode; wholeRange?: Range }, token: CancellationToken): Promise { + + const provider = Iterable.first(this._inlineChatService.getAllProvider()); + if (!provider) { + this._logService.trace('[IE] NO provider found'); + return undefined; + } + + this._onWillStartSession.fire(editor); + + const textModel = editor.getModel(); + const selection = editor.getSelection(); + let raw: IInlineChatSession | undefined | null; + try { + raw = await raceCancellation( + Promise.resolve(provider.prepareInlineChatSession(textModel, selection, token)), + token + ); + } catch (error) { + this._logService.error('[IE] FAILED to prepare session', provider.debugName); + this._logService.error(error); + return undefined; + } + if (!raw) { + this._logService.trace('[IE] NO session', provider.debugName); + return undefined; + } + this._logService.trace('[IE] NEW session', provider.debugName); + + this._logService.trace(`[IE] creating NEW session for ${editor.getId()}, ${provider.debugName}`); + const store = new DisposableStore(); + + // create: keep a reference to prevent disposal of the "actual" model + const refTextModelN = await this._textModelService.createModelReference(textModel.uri); + store.add(refTextModelN); + + // create: keep a snapshot of the "actual" model + const textModel0 = this._modelService.createModel( + createTextBufferFactoryFromSnapshot(textModel.createSnapshot()), + { languageId: textModel.getLanguageId(), onDidChange: Event.None }, + undefined, true + ); + store.add(textModel0); + + let wholeRange = options.wholeRange; + if (!wholeRange) { + wholeRange = raw.wholeRange ? Range.lift(raw.wholeRange) : editor.getSelection(); + } + + + // install managed-marker for the decoration range + const wholeRangeMgr = new SessionWholeRange(textModel, wholeRange); + store.add(wholeRangeMgr); + + const hunkData = new HunkData(this._editorWorkerService, textModel0, textModel); + store.add(hunkData); + + const session = new Session(options.editMode, textModel0, textModel, provider, raw, wholeRangeMgr, hunkData); + + // store: key -> session + const key = this._key(editor, textModel.uri); + if (this._sessions.has(key)) { + store.dispose(); + throw new Error(`Session already stored for ${key}`); + } + this._sessions.set(key, { session, editor, store }); + return session; + } + + moveSession(session: Session, target: ICodeEditor): void { + const newKey = this._key(target, session.textModelN.uri); + const existing = this._sessions.get(newKey); + if (existing) { + if (existing.session !== session) { + throw new Error(`Cannot move session because the target editor already/still has one`); + } else { + // noop + return; + } + } + + let found = false; + for (const [oldKey, data] of this._sessions) { + if (data.session === session) { + found = true; + this._sessions.delete(oldKey); + this._sessions.set(newKey, { ...data, editor: target }); + this._logService.trace(`[IE] did MOVE session for ${data.editor.getId()} to NEW EDITOR ${target.getId()}, ${session.provider.debugName}`); + this._onDidMoveSession.fire({ session, editor: target }); + break; + } + } + if (!found) { + throw new Error(`Cannot move session because it is not stored`); + } + } + + releaseSession(session: Session): void { + + let data: SessionData | undefined; + + // cleanup + for (const [key, value] of this._sessions) { + if (value.session === session) { + data = value; + value.store.dispose(); + this._sessions.delete(key); + this._logService.trace(`[IE] did RELEASED session for ${value.editor.getId()}, ${session.provider.debugName}`); + break; + } + } + + if (!data) { + // double remove + return; + } + + // keep recording + const newLen = this._recordings.unshift(session.asRecording()); + if (newLen > 5) { + this._recordings.pop(); + } + + // send telemetry + this._telemetryService.publicLog2('interactiveEditor/session', session.asTelemetryData()); + + this._onDidEndSession.fire({ editor: data.editor, session }); + } + + getCodeEditor(session: Session): ICodeEditor { + for (const [, data] of this._sessions) { + if (data.session === session) { + return data.editor; + } + } + throw new Error('session not found'); + } + + getSession(editor: ICodeEditor, uri: URI): Session | undefined { + const key = this._key(editor, uri); + return this._sessions.get(key)?.session; + } + + private _key(editor: ICodeEditor, uri: URI): string { + const item = this._keyComputers.get(uri.scheme); + return item + ? item.getComparisonKey(editor, uri) + : `${editor.getId()}@${uri.toString()}`; + + } + + registerSessionKeyComputer(scheme: string, value: ISessionKeyComputer): IDisposable { + this._keyComputers.set(scheme, value); + return toDisposable(() => this._keyComputers.delete(scheme)); + } + + // --- debug + recordings(): readonly Recording[] { + return this._recordings; + } + +} diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 7fe41b4a2442c..f118144130e2d 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -8,9 +8,8 @@ import { coalesceInPlace, equals, tail } from 'vs/base/common/arrays'; import { AsyncIterableSource, IntervalTimer } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; -import { Iterable } from 'vs/base/common/iterator'; import { Lazy } from 'vs/base/common/lazy'; -import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { themeColorFromId } from 'vs/base/common/themables'; import { ICodeEditor, IViewZone, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll'; @@ -20,7 +19,7 @@ import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { DetailedLineRangeMapping, LineRangeMapping, RangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { LineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; import { TextEdit } from 'vs/editor/common/languages'; import { ICursorStateComputer, IIdentifiedSingleEditOperation, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel, IValidEditOperation, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; @@ -34,9 +33,11 @@ import { IProgress, Progress } from 'vs/platform/progress/common/progress'; import { SaveReason } from 'vs/workbench/common/editor'; import { countWords, getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; import { InlineChatFileCreatePreviewWidget, InlineChatLivePreviewWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget'; -import { ReplyResponse, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { HunkInformation, ReplyResponse, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { InlineChatZoneWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_DOCUMENT_CHANGED, overviewRulerInlineChatDiffInserted } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { HunkState } from './inlineChatSession'; +import { assertType } from 'vs/base/common/types'; export abstract class EditModeStrategy { @@ -464,32 +465,8 @@ export function asProgressiveEdit(interval: IntervalTimer, edit: IIdentifiedSing } -// --- - -class Hunk { - constructor( - readonly original: LineRange, - readonly modified: LineRange, - readonly changes: RangeMapping[] - ) { } -} - -type HunkTrackedRange = { - /** - * The first element [0] is the whole modified range and subsequent elements are word-level changes - */ - getRanges(): Range[]; - - discardChanges(): void; -}; - -const enum HunkState { - Accepted = 1, - Rejected = 2, -} - type HunkDisplayData = { - acceptedOrRejected: HunkState | undefined; + decorationIds: string[]; viewZoneId: string | undefined; @@ -500,21 +477,12 @@ type HunkDisplayData = { acceptHunk: () => void; discardHunk: () => void; toggleDiff?: () => any; + remove(): void; }; -interface HunkDisplay { - renderHunks(): HunkDisplayData | undefined; - hideHunks(): void; - discardHunks(): void; -} export class LiveStrategy extends EditModeStrategy { - private readonly _decoTrackedRange = ModelDecorationOptions.register({ - description: 'inline-tracked-range', - className: 'inline-chat-tracked-range' - }); - private readonly _decoInsertedText = ModelDecorationOptions.register({ description: 'inline-modified-line', className: 'inline-chat-inserted-range-linehighlight', @@ -531,7 +499,6 @@ export class LiveStrategy extends EditModeStrategy { }); private readonly _store = new DisposableStore(); - private readonly _renderStore = new DisposableStore(); private readonly _previewZone: Lazy; private readonly _ctxCurrentChangeHasDiff: IContextKey; @@ -571,13 +538,16 @@ export class LiveStrategy extends EditModeStrategy { private _resetDiff(): void { this._ctxCurrentChangeHasDiff.reset(); this._ctxCurrentChangeShowsDiff.reset(); - this._renderStore.clear(); this._zone.widget.updateStatus(''); this._progressiveEditingDecorations.clear(); + + + for (const data of this._hunkDisplayData.values()) { + data.remove(); + } } override pause = () => { - this._hunkDisplay?.hideHunks(); this._ctxCurrentChangeShowsDiff.reset(); }; @@ -596,7 +566,9 @@ export class LiveStrategy extends EditModeStrategy { } async cancel() { - this._hunkDisplay?.discardHunks(); + for (const item of this._session.hunkData.getInfo()) { + item.discardChanges(); + } this._resetDiff(); } @@ -659,7 +631,7 @@ export class LiveStrategy extends EditModeStrategy { } } - private _hunkDisplay?: HunkDisplay; + private readonly _hunkDisplayData = new Map(); override async renderChanges(response: ReplyResponse) { @@ -671,270 +643,171 @@ export class LiveStrategy extends EditModeStrategy { this._progressiveEditingDecorations.clear(); - if (!this._hunkDisplay) { + const renderHunks = () => { - this._renderStore.add(toDisposable(() => { - this._hunkDisplay?.hideHunks(); - this._hunkDisplay = undefined; - })); + let widgetData: HunkDisplayData | undefined; - const hunkTrackedRanges = new Map(); - const hunkDisplayData = new Map(); + changeDecorationsAndViewZones(this._editor, (decorationsAccessor, viewZoneAccessor) => { - // (INIT) compute hunks - const hunks = await this._computeHunks(); - if (hunks.length === 0) { - this._hunkDisplay = { renderHunks() { return undefined; }, hideHunks() { }, discardHunks() { } }; - return undefined; - } + const keysNow = new Set(this._hunkDisplayData.keys()); + widgetData = undefined; - // (INIT) add tracked ranges per hunk - const model = this._editor.getModel()!; - model.changeDecorations(accessor => { - for (const hunk of hunks) { - const decorationIds: string[] = []; - const modifiedRange = asRange(hunk.modified, this._session.textModelN); - decorationIds.push(accessor.addDecoration(modifiedRange, this._decoTrackedRange)); - for (const change of hunk.changes) { - decorationIds.push(accessor.addDecoration(change.modifiedRange, this._decoTrackedRange)); - } - const hunkLiveInfo: HunkTrackedRange = { - getRanges: () => { - const ranges = decorationIds.map(id => model.getDecorationRange(id)); - coalesceInPlace(ranges); - return ranges; - }, - discardChanges: () => { - const edits: ISingleEditOperation[] = []; - const ranges = hunkLiveInfo.getRanges(); - for (let i = 1; i < ranges.length; i++) { - // DISCARD: replace modified range with original value. The modified range is retrieved from a decoration - // which was created above so that typing in the editor keeps discard working. - const modifiedRange = ranges[i]; - const originalValue = this._session.textModel0.getValueInRange(hunk.changes[i - 1].originalRange); - edits.push(EditOperation.replace(modifiedRange, originalValue)); - } - this._session.textModelN.pushEditOperations(null, edits, () => null); - } - }; - hunkTrackedRanges.set(hunk, hunkLiveInfo); - this._renderStore.add(toDisposable(() => { - model.deltaDecorations(decorationIds, []); - })); - } - }); + for (const hunkData of this._session.hunkData.getInfo()) { - let widgetData: HunkDisplayData | undefined; + keysNow.delete(hunkData); - const renderHunks = () => { - - changeDecorationsAndViewZones(this._editor, (decorationsAccessor, viewZoneAccessor) => { - - widgetData = undefined; - - for (const hunk of hunks) { - - const hunkRanges = hunkTrackedRanges.get(hunk)!.getRanges(); - let data = hunkDisplayData.get(hunk); - if (!data) { - // first time -> create decoration - const decorationIds: string[] = []; - for (let i = 0; i < hunkRanges.length; i++) { - decorationIds.push(decorationsAccessor.addDecoration(hunkRanges[i], i === 0 - ? this._decoInsertedText - : this._decoInsertedTextRange) - ); - } - - const acceptHunk = () => { - // ACCEPT: stop rendering this as inserted - hunkDisplayData.get(hunk)!.acceptedOrRejected = HunkState.Accepted; - renderHunks(); - }; - - const discardHunk = () => { - const info = hunkTrackedRanges.get(hunk)!; - info.discardChanges(); - hunkDisplayData.get(hunk)!.acceptedOrRejected = HunkState.Rejected; - renderHunks(); - }; - - // original view zone - const mightContainNonBasicASCII = this._session.textModel0.mightContainNonBasicASCII() ?? false; - const mightContainRTL = this._session.textModel0.mightContainRTL() ?? false; - const renderOptions = RenderOptions.fromEditor(this._editor); - const source = new LineSource( - hunk.original.mapToLineArray(l => this._session.textModel0.tokenization.getLineTokens(l)), - [], - mightContainNonBasicASCII, - mightContainRTL, + const hunkRanges = hunkData.getRangesN(); + let data = this._hunkDisplayData.get(hunkData); + if (!data) { + // first time -> create decoration + const decorationIds: string[] = []; + for (let i = 0; i < hunkRanges.length; i++) { + decorationIds.push(decorationsAccessor.addDecoration(hunkRanges[i], i === 0 + ? this._decoInsertedText + : this._decoInsertedTextRange) ); - const domNode = document.createElement('div'); - domNode.className = 'inline-chat-original-zone2'; - const result = renderLines(source, renderOptions, [new InlineDecoration(new Range(hunk.original.startLineNumber, 1, hunk.original.startLineNumber, 1), '', InlineDecorationType.Regular)], domNode); - const viewZoneData: IViewZone = { - afterLineNumber: -1, - heightInLines: result.heightInLines, - domNode, - }; - - const toggleDiff = () => { - const scrollState = StableEditorScrollState.capture(this._editor); - if (!data!.viewZoneId) { - - this._editor.changeViewZones(accessor => { - const [hunkRange] = hunkTrackedRanges.get(hunk)!.getRanges(); - viewZoneData.afterLineNumber = hunkRange.startLineNumber - 1; - data!.viewZoneId = accessor.addZone(viewZoneData); - }); - this._ctxCurrentChangeShowsDiff.set(true); + } + + const acceptHunk = () => { + hunkData.acceptChanges(); + renderHunks(); + }; + + const discardHunk = () => { + hunkData.discardChanges(); + renderHunks(); + }; + + // original view zone + const mightContainNonBasicASCII = this._session.textModel0.mightContainNonBasicASCII(); + const mightContainRTL = this._session.textModel0.mightContainRTL(); + const renderOptions = RenderOptions.fromEditor(this._editor); + const originalRange = hunkData.getRanges0()[0]; + const source = new LineSource( + LineRange.fromRangeInclusive(originalRange).mapToLineArray(l => this._session.textModel0.tokenization.getLineTokens(l)), + [], + mightContainNonBasicASCII, + mightContainRTL, + ); + const domNode = document.createElement('div'); + domNode.className = 'inline-chat-original-zone2'; + const result = renderLines(source, renderOptions, [new InlineDecoration(new Range(originalRange.startLineNumber, 1, originalRange.startLineNumber, 1), '', InlineDecorationType.Regular)], domNode); + const viewZoneData: IViewZone = { + afterLineNumber: -1, + heightInLines: result.heightInLines, + domNode, + }; + + const toggleDiff = () => { + const scrollState = StableEditorScrollState.capture(this._editor); + changeDecorationsAndViewZones(this._editor, (_decorationsAccessor, viewZoneAccessor) => { + assertType(data); + if (!data.viewZone) { + const [hunkRange] = hunkData.getRangesN(); + viewZoneData.afterLineNumber = hunkRange.startLineNumber - 1; + data.viewZoneId = viewZoneAccessor.addZone(viewZoneData); } else { - this._editor.changeViewZones(accessor => { - accessor.removeZone(data!.viewZoneId!); - data!.viewZoneId = undefined; - }); - this._ctxCurrentChangeShowsDiff.set(false); + viewZoneAccessor.removeZone(data.viewZoneId!); + data.viewZoneId = undefined; } - scrollState.restore(this._editor); - }; - - const zoneLineNumber = this._zone.position!.lineNumber; - const myDistance = zoneLineNumber <= hunkRanges[0].startLineNumber - ? hunkRanges[0].startLineNumber - zoneLineNumber - : zoneLineNumber - hunkRanges[0].endLineNumber; - - data = { - acceptedOrRejected: undefined, - decorationIds, - viewZoneId: '', - viewZone: viewZoneData, - distance: myDistance, - position: hunkRanges[0].getStartPosition().delta(-1), - acceptHunk, - discardHunk, - toggleDiff: !hunk.original.isEmpty ? toggleDiff : undefined, - }; - - hunkDisplayData.set(hunk, data); - - } else if (data.acceptedOrRejected !== undefined) { - // accepted or rejected -> remove decoration - for (const decorationId of data.decorationIds) { - decorationsAccessor.removeDecoration(decorationId); - } - if (data.viewZoneId) { - viewZoneAccessor.removeZone(data.viewZoneId); - } - - data.decorationIds = []; - data.viewZoneId = undefined; - - } else { - // update distance and position based on modifiedRange-decoration - const zoneLineNumber = this._zone.position!.lineNumber; - const modifiedRangeNow = hunkRanges[0]; - data.position = modifiedRangeNow.getStartPosition().delta(-1); - data.distance = zoneLineNumber <= modifiedRangeNow.startLineNumber - ? modifiedRangeNow.startLineNumber - zoneLineNumber - : zoneLineNumber - modifiedRangeNow.endLineNumber; - } + }); + this._ctxCurrentChangeShowsDiff.set(typeof data?.viewZoneId === 'number'); + scrollState.restore(this._editor); + }; + + const remove = () => { + changeDecorationsAndViewZones(this._editor, (decorationsAccessor, viewZoneAccessor) => { + assertType(data); + for (const decorationId of data.decorationIds) { + decorationsAccessor.removeDecoration(decorationId); + } + if (data.viewZoneId) { + viewZoneAccessor.removeZone(data.viewZoneId); + } + data.decorationIds = []; + data.viewZoneId = undefined; + }); + }; + + const zoneLineNumber = this._zone.position!.lineNumber; + const myDistance = zoneLineNumber <= hunkRanges[0].startLineNumber + ? hunkRanges[0].startLineNumber - zoneLineNumber + : zoneLineNumber - hunkRanges[0].endLineNumber; + + data = { + decorationIds, + viewZoneId: '', + viewZone: viewZoneData, + distance: myDistance, + position: hunkRanges[0].getStartPosition().delta(-1), + acceptHunk, + discardHunk, + toggleDiff: !hunkData.isInsertion() ? toggleDiff : undefined, + remove, + }; + + this._hunkDisplayData.set(hunkData, data); + + } else if (hunkData.getState() !== HunkState.Pending) { + data.remove(); - if (!data.acceptedOrRejected) { - if (!widgetData || data.distance < widgetData.distance) { - widgetData = data; - } - } - } - }); - - if (widgetData) { - this._zone.updatePositionAndHeight(widgetData.position); - this._editor.revealPositionInCenterIfOutsideViewport(widgetData.position); - - const remainingHunks = Iterable.reduce(hunkDisplayData.values(), (p, c) => { return p + (c.acceptedOrRejected ? 0 : 1); }, 0); - this._updateSummaryMessage(remainingHunks); - - this._ctxCurrentChangeHasDiff.set(Boolean(widgetData.toggleDiff)); - this.toggleDiff = widgetData.toggleDiff; - this.acceptHunk = async () => widgetData!.acceptHunk(); - this.discardHunk = async () => widgetData!.discardHunk(); - - } else if (hunkDisplayData.size > 0) { - // everything accepted or rejected - let oneAccepted = false; - for (const data of hunkDisplayData.values()) { - if (data.acceptedOrRejected === HunkState.Accepted) { - oneAccepted = true; - break; - } - } - if (oneAccepted) { - this._onDidAccept.fire(); } else { - this._onDidDiscard.fire(); + // update distance and position based on modifiedRange-decoration + const zoneLineNumber = this._zone.position!.lineNumber; + const modifiedRangeNow = hunkRanges[0]; + data.position = modifiedRangeNow.getStartPosition().delta(-1); + data.distance = zoneLineNumber <= modifiedRangeNow.startLineNumber + ? modifiedRangeNow.startLineNumber - zoneLineNumber + : zoneLineNumber - modifiedRangeNow.endLineNumber; } - } - - return widgetData; - }; - const hideHunks = () => { - changeDecorationsAndViewZones(this._editor, (decorationsAccessor, viewZoneAccessor) => { - for (const data of hunkDisplayData.values()) { - // remove decorations - for (const decorationId of data.decorationIds) { - decorationsAccessor.removeDecoration(decorationId); - } - // remove view zone - if (data.viewZoneId) { - viewZoneAccessor.removeZone(data.viewZoneId); - } - data.viewZone.domNode.remove(); + if (hunkData.getState() === HunkState.Pending && (!widgetData || data.distance < widgetData.distance)) { + widgetData = data; } - }); - hunkDisplayData.clear(); - }; - - const discardHunks = () => { - for (const data of hunkTrackedRanges.values()) { - data.discardChanges(); } - }; - - this._hunkDisplay = { renderHunks, hideHunks, discardHunks }; - } - return this._hunkDisplay?.renderHunks()?.position; - } - - private static readonly HUNK_THRESHOLD = 8; - - private async _computeHunks(): Promise { - const diff = await this._editorWorkerService.computeDiff(this._session.textModel0.uri, this._session.textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: false }, 'advanced'); + for (const key of keysNow) { + const data = this._hunkDisplayData.get(key); + if (data) { + this._hunkDisplayData.delete(key); + data.remove(); + } + } + }); - if (!diff || diff.changes.length === 0) { - return []; - } - - // merge changes neighboring changes - const mergedChanges = [diff.changes[0]]; - for (let i = 1; i < diff.changes.length; i++) { - const lastChange = mergedChanges[mergedChanges.length - 1]; - const thisChange = diff.changes[i]; - if (thisChange.modified.startLineNumber - lastChange.modified.endLineNumberExclusive <= LiveStrategy.HUNK_THRESHOLD) { - mergedChanges[mergedChanges.length - 1] = new DetailedLineRangeMapping( - lastChange.original.join(thisChange.original), - lastChange.modified.join(thisChange.modified), - (lastChange.innerChanges ?? []).concat(thisChange.innerChanges ?? []) - ); - } else { - mergedChanges.push(thisChange); + if (widgetData) { + this._zone.updatePositionAndHeight(widgetData.position); + this._editor.revealPositionInCenterIfOutsideViewport(widgetData.position); + + const remainingHunks = this._session.hunkData.pending; + this._updateSummaryMessage(remainingHunks); + + this._ctxCurrentChangeHasDiff.set(Boolean(widgetData.toggleDiff)); + this.toggleDiff = widgetData.toggleDiff; + this.acceptHunk = async () => widgetData!.acceptHunk(); + this.discardHunk = async () => widgetData!.discardHunk(); + + } else if (this._hunkDisplayData.size > 0) { + // everything accepted or rejected + let oneAccepted = false; + for (const hunkData of this._session.hunkData.getInfo()) { + if (hunkData.getState() === HunkState.Accepted) { + oneAccepted = true; + break; + } + } + if (oneAccepted) { + this._onDidAccept.fire(); + } else { + this._onDidDiscard.fire(); + } } - } - return mergedChanges.map(change => new Hunk(change.original, change.modified, change.innerChanges ?? [])); - } + return widgetData; + }; + return renderHunks()?.position; + } protected _updateSummaryMessage(hunkCount: number) { let message: string; @@ -966,13 +839,6 @@ async function undoModelUntil(model: ITextModel, targetAltVersion: number): Prom } -function asRange(lineRange: LineRange, model: ITextModel): Range { - return lineRange.isEmpty - ? new Range(lineRange.startLineNumber, 1, lineRange.startLineNumber, model.getLineLength(lineRange.startLineNumber)) - : new Range(lineRange.startLineNumber, 1, lineRange.endLineNumberExclusive - 1, model.getLineLength(lineRange.endLineNumberExclusive - 1)); -} - - function changeDecorationsAndViewZones(editor: ICodeEditor, callback: (accessor: IModelDecorationsChangeAccessor, viewZoneAccessor: IViewZoneChangeAccessor) => void): void { editor.changeDecorations(decorationsAccessor => { editor.changeViewZones(viewZoneAccessor => { diff --git a/src/vs/workbench/contrib/inlineChat/browser/utils.ts b/src/vs/workbench/contrib/inlineChat/browser/utils.ts index a2a77ac6f2d06..43364755deb63 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/utils.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/utils.ts @@ -24,3 +24,9 @@ export function invertLineRange(range: LineRange, model: ITextModel): LineRange[ export function lineRangeAsRange(r: LineRange): Range { return new Range(r.startLineNumber, 1, r.endLineNumberExclusive - 1, 1); } + +export function asRange(lineRange: LineRange, model: ITextModel): Range { + return lineRange.isEmpty + ? new Range(lineRange.startLineNumber, 1, lineRange.startLineNumber, model.getLineLength(lineRange.startLineNumber)) + : new Range(lineRange.startLineNumber, 1, lineRange.endLineNumberExclusive - 1, model.getLineLength(lineRange.endLineNumberExclusive - 1)); +} 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 4219dd38c1234..d2b9fe55c3474 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -32,8 +32,10 @@ import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/brows import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { InlineChatController, InlineChatRunOptions, State } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; -import { IInlineChatSavingService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSaving'; -import { IInlineChatSessionService, InlineChatSessionService, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { IInlineChatSavingService } from '../../browser/inlineChatSavingService'; +import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { InlineChatSessionServiceImpl } from '../../browser/inlineChatSessionServiceImpl'; +import { IInlineChatSessionService } from '../../browser/inlineChatSessionService'; import { EditMode, IInlineChatService, InlineChatConfigKeys, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { InlineChatServiceImpl } from 'vs/workbench/contrib/inlineChat/common/inlineChatServiceImpl'; import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; @@ -106,7 +108,7 @@ suite('InteractiveChatController', function () { [IContextKeyService, contextKeyService], [IInlineChatService, inlineChatService], [IDiffProviderFactoryService, new SyncDescriptor(TestDiffProviderFactoryService)], - [IInlineChatSessionService, new SyncDescriptor(InlineChatSessionService)], + [IInlineChatSessionService, new SyncDescriptor(InlineChatSessionServiceImpl)], [IInlineChatSavingService, new class extends mock() { override markChanged(session: Session): void { // noop diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts index 1242e625ef736..f5f42a2bac866 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts @@ -3,14 +3,51 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { Event } from 'vs/base/common/event'; import { mock } from 'vs/base/test/common/mock'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { TestDiffProviderFactoryService } from 'vs/editor/browser/diff/testDiffProviderFactoryService'; +import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IDiffProviderFactoryService } from 'vs/editor/browser/widget/diffEditor/diffProviderFactoryService'; import { Range } from 'vs/editor/common/core/range'; import { ILanguageService } from 'vs/editor/common/languages/language'; -import { ReplyResponse } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; -import { IInlineChatEditResponse, InlineChatResponseType, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { ITextModel } from 'vs/editor/common/model'; +import { IModelService } from 'vs/editor/common/services/model'; +import { instantiateTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; +import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; +import { IChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; +import { IInlineChatSavingService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSavingService'; +import { HunkState, ReplyResponse, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; +import { InlineChatSessionServiceImpl } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl'; +import { EditMode, IInlineChatEditResponse, IInlineChatService, InlineChatResponseType, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { assertType } from 'vs/base/common/types'; +import { InlineChatServiceImpl } from 'vs/workbench/contrib/inlineChat/common/inlineChatServiceImpl'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Position } from 'vs/editor/common/core/position'; +import { DiffAlgorithmName, IEditorWorkerService, ILineChange } from 'vs/editor/common/services/editorWorker'; +import { IDocumentDiff, IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider'; +import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker'; +import { LineRange } from 'vs/editor/common/core/lineRange'; +import { MovedText } from 'vs/editor/common/diff/linesDiffComputer'; +import { LineRangeMapping, DetailedLineRangeMapping, RangeMapping } from 'vs/editor/common/diff/rangeMapping'; + suite('ReplyResponse', function () { @@ -48,3 +85,278 @@ suite('ReplyResponse', function () { } }); }); + +class TestWorkerService extends mock() { + + private readonly _worker = new EditorSimpleWorker(null!, null); + + constructor(@IModelService private readonly _modelService: IModelService) { + super(); + } + + override async computeDiff(original: URI, modified: URI, options: IDocumentDiffProviderOptions, algorithm: DiffAlgorithmName): Promise { + + const originalModel = this._modelService.getModel(original); + const modifiedModel = this._modelService.getModel(modified); + + assertType(originalModel); + assertType(modifiedModel); + + this._worker.acceptNewModel({ + url: originalModel.uri.toString(), + versionId: originalModel.getVersionId(), + lines: originalModel.getLinesContent(), + EOL: originalModel.getEOL(), + }); + + this._worker.acceptNewModel({ + url: modifiedModel.uri.toString(), + versionId: modifiedModel.getVersionId(), + lines: modifiedModel.getLinesContent(), + EOL: modifiedModel.getEOL(), + }); + + const result = await this._worker.computeDiff(originalModel.uri.toString(), modifiedModel.uri.toString(), options, algorithm); + if (!result) { + return result; + } + // Convert from space efficient JSON data to rich objects. + const diff: IDocumentDiff = { + identical: result.identical, + quitEarly: result.quitEarly, + changes: toLineRangeMappings(result.changes), + moves: result.moves.map(m => new MovedText( + new LineRangeMapping(new LineRange(m[0], m[1]), new LineRange(m[2], m[3])), + toLineRangeMappings(m[4]) + )) + }; + return diff; + + function toLineRangeMappings(changes: readonly ILineChange[]): readonly DetailedLineRangeMapping[] { + return changes.map( + (c) => new DetailedLineRangeMapping( + new LineRange(c[0], c[1]), + new LineRange(c[2], c[3]), + c[4]?.map( + (c) => new RangeMapping( + new Range(c[0], c[1], c[2], c[3]), + new Range(c[4], c[5], c[6], c[7]) + ) + ) + ) + ); + } + } +} + +suite('InlineChatSession', function () { + + const store = new DisposableStore(); + let editor: IActiveCodeEditor; + let model: ITextModel; + let instaService: TestInstantiationService; + let inlineChatService: InlineChatServiceImpl; + + let inlineChatSessionService: IInlineChatSessionService; + + setup(function () { + const contextKeyService = new MockContextKeyService(); + inlineChatService = new InlineChatServiceImpl(contextKeyService); + + const serviceCollection = new ServiceCollection( + [IEditorWorkerService, new SyncDescriptor(TestWorkerService)], + [IInlineChatService, inlineChatService], + [IContextKeyService, contextKeyService], + [IDiffProviderFactoryService, new SyncDescriptor(TestDiffProviderFactoryService)], + [IInlineChatSessionService, new SyncDescriptor(InlineChatSessionServiceImpl)], + [IInlineChatSavingService, new class extends mock() { + override markChanged(session: Session): void { + // noop + } + }], + [IEditorProgressService, new class extends mock() { + override show(total: unknown, delay?: unknown): IProgressRunner { + return { + total() { }, + worked(value) { }, + done() { }, + }; + } + }], + [IChatAccessibilityService, new class extends mock() { + override acceptResponse(response: IChatResponseViewModel | undefined, requestId: number): void { } + override acceptRequest(): number { return -1; } + }], + [IAccessibleViewService, new class extends mock() { + override getOpenAriaHint(verbositySettingKey: AccessibilityVerbositySettingId): string | null { + return null; + } + }], + [IConfigurationService, new TestConfigurationService()], + [IViewDescriptorService, new class extends mock() { + override onDidChangeLocation = Event.None; + }] + ); + + store.add(inlineChatService.addProvider({ + debugName: 'Unit Test', + label: 'Unit Test', + prepareInlineChatSession() { + return { + id: Math.random() + }; + }, + provideResponse(session, request) { + return { + type: InlineChatResponseType.EditorEdit, + id: Math.random(), + edits: [{ + range: new Range(1, 1, 1, 1), + text: request.prompt + }] + }; + } + })); + + instaService = store.add(workbenchInstantiationService(undefined, store).createChild(serviceCollection)); + inlineChatSessionService = store.add(instaService.get(IInlineChatSessionService)); + + model = store.add(instaService.get(IModelService).createModel('one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven', null)); + editor = store.add(instantiateTestCodeEditor(instaService, model)); + }); + + teardown(function () { + store.clear(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + + test('Create, release', async function () { + + const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); + assertType(session); + inlineChatSessionService.releaseSession(session); + }); + + test('HunkData, info', async function () { + + const decorationCountThen = model.getAllDecorations().length; + + const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); + assertType(session); + assert.ok(session.textModelN === model); + + editor.executeEdits('test', [EditOperation.insert(new Position(1, 1), 'AI_EDIT\n')]); + + await session.hunkData.recompute(); + assert.strictEqual(session.hunkData.size, 1); + const [hunk] = session.hunkData.getInfo(); + assertType(hunk); + + assert.ok(!session.textModel0.equalsTextBuffer(session.textModelN.getTextBuffer())); + assert.strictEqual(hunk.getState(), HunkState.Pending); + assert.ok(hunk.getRangesN()[0].equalsRange({ startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 7 })); + + editor.executeEdits('test', [EditOperation.insert(new Position(1, 3), 'foobar')]); + assert.ok(hunk.getRangesN()[0].equalsRange({ startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 13 })); + + inlineChatSessionService.releaseSession(session); + + assert.strictEqual(model.getAllDecorations().length, decorationCountThen); // no leaked decorations! + }); + + test('HunkData, accept', async function () { + + const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); + assertType(session); + + editor.executeEdits('test', [EditOperation.insert(new Position(1, 1), 'AI_EDIT\n'), EditOperation.insert(new Position(10, 1), 'AI_EDIT\n')]); + + await session.hunkData.recompute(); + assert.strictEqual(session.hunkData.size, 2); + assert.ok(!session.textModel0.equalsTextBuffer(session.textModelN.getTextBuffer())); + + for (const hunk of session.hunkData.getInfo()) { + assertType(hunk); + assert.strictEqual(hunk.getState(), HunkState.Pending); + hunk.acceptChanges(); + assert.strictEqual(hunk.getState(), HunkState.Accepted); + } + + assert.strictEqual(session.textModel0.getValue(), session.textModelN.getValue()); + inlineChatSessionService.releaseSession(session); + }); + + test('HunkData, reject', async function () { + + const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); + assertType(session); + + editor.executeEdits('test', [EditOperation.insert(new Position(1, 1), 'AI_EDIT\n'), EditOperation.insert(new Position(10, 1), 'AI_EDIT\n')]); + + await session.hunkData.recompute(); + assert.strictEqual(session.hunkData.size, 2); + assert.ok(!session.textModel0.equalsTextBuffer(session.textModelN.getTextBuffer())); + + for (const hunk of session.hunkData.getInfo()) { + assertType(hunk); + assert.strictEqual(hunk.getState(), HunkState.Pending); + hunk.discardChanges(); + assert.strictEqual(hunk.getState(), HunkState.Rejected); + } + + assert.strictEqual(session.textModel0.getValue(), session.textModelN.getValue()); + inlineChatSessionService.releaseSession(session); + }); + + test('HunkData, N rounds', async function () { + + model.setValue('one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven\ntwelwe\nthirteen\nfourteen\nfifteen\nsixteen\nseventeen\neighteen\nnineteen\n'); + + const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); + assertType(session); + + assert.ok(session.textModel0.equalsTextBuffer(session.textModelN.getTextBuffer())); + + assert.strictEqual(session.hunkData.size, 0); + + // ROUND #1 + editor.executeEdits('test', [ + EditOperation.insert(new Position(1, 1), 'AI1'), + EditOperation.insert(new Position(4, 1), 'AI2'), + EditOperation.insert(new Position(19, 1), 'AI3') + ]); + + await session.hunkData.recompute(); + assert.strictEqual(session.hunkData.size, 2); // AI1, AI2 are merged into one hunk, AI3 is a separate hunk + + let [first, second] = session.hunkData.getInfo(); + + assert.ok(model.getValueInRange(first.getRangesN()[0]).includes('AI1')); + assert.ok(model.getValueInRange(first.getRangesN()[0]).includes('AI2')); + assert.ok(model.getValueInRange(second.getRangesN()[0]).includes('AI3')); + + assert.ok(!session.textModel0.getValueInRange(first.getRangesN()[0]).includes('AI1')); + assert.ok(!session.textModel0.getValueInRange(first.getRangesN()[0]).includes('AI2')); + assert.ok(!session.textModel0.getValueInRange(second.getRangesN()[0]).includes('AI3')); + + first.acceptChanges(); + assert.ok(session.textModel0.getValueInRange(first.getRangesN()[0]).includes('AI1')); + assert.ok(session.textModel0.getValueInRange(first.getRangesN()[0]).includes('AI2')); + assert.ok(!session.textModel0.getValueInRange(second.getRangesN()[0]).includes('AI3')); + + + // ROUND #2 + editor.executeEdits('test', [ + EditOperation.insert(new Position(7, 1), 'AI4'), + ]); + await session.hunkData.recompute(); + assert.strictEqual(session.hunkData.size, 2); + + [first, second] = session.hunkData.getInfo(); + assert.ok(model.getValueInRange(first.getRangesN()[0]).includes('AI4')); // the new hunk (in line-order) + assert.ok(model.getValueInRange(second.getRangesN()[0]).includes('AI3')); // the previous hunk remains + + inlineChatSessionService.releaseSession(session); + }); +}); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorHint/emptyCellEditorHint.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorHint/emptyCellEditorHint.ts index 974f005d28473..8bbbe4c775314 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorHint/emptyCellEditorHint.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorHint/emptyCellEditorHint.ts @@ -12,7 +12,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { EmptyTextEditorHintContribution, IEmptyTextEditorHintOptions } from 'vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint'; -import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; import { IInlineChatService } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts index fa7eb4cfc9a14..cfc2b2fc2d375 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts @@ -29,7 +29,8 @@ import { AsyncProgress } from 'vs/platform/progress/common/progress'; import { SaveReason } from 'vs/workbench/common/editor'; import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; -import { EmptyResponse, ErrorResponse, IInlineChatSessionService, ReplyResponse, Session, SessionExchange, SessionPrompt } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { EmptyResponse, ErrorResponse, ReplyResponse, Session, SessionExchange, SessionPrompt } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; import { ProgressingEditsOptions, asProgressiveEdit, performAsyncTextEdit } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_LAST_RESPONSE_TYPE, CTX_INLINE_CHAT_VISIBLE, EditMode, IInlineChatProgressItem, IInlineChatRequest, InlineChatResponseFeedbackKind, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; From 62cb62c07bd4646fa1fe9929e82fba32e81976a7 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 16 Jan 2024 19:21:50 +0530 Subject: [PATCH 023/333] fix #161906 (#202582) --- .../contrib/extensions/browser/extensionsViews.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index db5a0672ed9e3..cd8a1b647cdf5 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -1004,7 +1004,7 @@ export class ExtensionsListView extends ViewPane { this.updateSize(); } - private updateSize() { + protected updateSize() { if (this.options.flexibleHeight) { this.maximumBodySize = this.list?.model.length ? Number.POSITIVE_INFINITY : 0; this.storageService.store(`${this.id}.size`, this.list?.model.length || 0, StorageScope.PROFILE, StorageTarget.MACHINE); @@ -1228,10 +1228,12 @@ export class OutdatedExtensionsView extends ExtensionsListView { if (ExtensionsListView.isSearchExtensionUpdatesQuery(query)) { query = query.replace('@updates', '@outdated'); } + return super.show(query.trim()); + } - const model = await super.show(query.trim()); - this.setExpanded(model.length > 0); - return model; + protected override updateSize() { + super.updateSize(); + this.setExpanded(this.count() > 0); } } From 17130bbeab809557fc9ef57d0ffdc712e06491e9 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Tue, 16 Jan 2024 15:14:52 +0100 Subject: [PATCH 024/333] Refactored title bar style configuration and updated related usage across multiple files --- .../platform/menubar/electron-main/menubar.ts | 6 +-- src/vs/platform/window/common/window.ts | 44 +++++++++++++++---- .../windows/electron-main/windowImpl.ts | 4 +- .../platform/windows/electron-main/windows.ts | 4 +- src/vs/workbench/browser/layout.ts | 17 +++---- .../parts/editor/auxiliaryEditorPart.ts | 4 +- .../browser/parts/titlebar/menubarControl.ts | 4 +- .../browser/parts/titlebar/titlebarPart.ts | 24 +++++----- .../browser/relauncher.contribution.ts | 12 +++-- .../windowIgnoreMenuShortcutsManager.ts | 2 +- .../electron-sandbox/desktop.contribution.ts | 4 +- .../parts/titlebar/titlebarPart.ts | 10 ++--- src/vs/workbench/electron-sandbox/window.ts | 6 +-- .../electron-sandbox/contextmenuService.ts | 4 +- 14 files changed, 87 insertions(+), 58 deletions(-) diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index d8bafd6263e49..6c9e3fcffda2b 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -22,7 +22,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IStateService } from 'vs/platform/state/node/state'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUpdateService, StateType } from 'vs/platform/update/common/update'; -import { getTitleBarStyle, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, IWindowOpenable } from 'vs/platform/window/common/window'; +import { INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, IWindowOpenable, showNativeTitlebar } from 'vs/platform/window/common/window'; import { IWindowsCountChangedEvent, IWindowsMainService, OpenContext } from 'vs/platform/windows/electron-main/windows'; import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService'; @@ -85,7 +85,7 @@ export class Menubar { this.menubarMenus = Object.create(null); this.keybindings = Object.create(null); - if (isMacintosh || getTitleBarStyle(this.configurationService) === 'native') { + if (isMacintosh || showNativeTitlebar(configurationService)) { this.restoreCachedMenubarData(); } @@ -469,7 +469,7 @@ export class Menubar { private shouldDrawMenu(menuId: string): boolean { // We need to draw an empty menu to override the electron default - if (!isMacintosh && getTitleBarStyle(this.configurationService) === 'custom') { + if (!isMacintosh && !showNativeTitlebar(this.configurationService)) { return false; } diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index 25eb202ca4ec9..a4514efd1268e 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -130,7 +130,7 @@ export function getMenuBarVisibility(configurationService: IConfigurationService const titleBarStyle = getTitleBarStyle(configurationService); const menuBarVisibility = configurationService.getValue('window.menuBarVisibility'); - if (menuBarVisibility === 'default' || (titleBarStyle === 'native' && menuBarVisibility === 'compact') || (isMacintosh && isNative)) { + if (menuBarVisibility === 'default' || (titleBarStyle !== TitlebarStyle.CUSTOM && menuBarVisibility === 'compact') || (isMacintosh && isNative)) { return 'classic'; } else { return menuBarVisibility; @@ -148,7 +148,7 @@ export interface IWindowSettings { readonly restoreWindows: 'preserve' | 'all' | 'folders' | 'one' | 'none'; readonly restoreFullscreen: boolean; readonly zoomLevel: number; - readonly titleBarStyle: 'native' | 'custom'; + readonly titleBarStyle: TitlebarStyle; readonly autoDetectHighContrast: boolean; readonly autoDetectColorScheme: boolean; readonly menuBarVisibility: MenuBarVisibility; @@ -165,30 +165,56 @@ export interface IDensitySettings { readonly editorTabHeight: 'default' | 'compact'; } -export function getTitleBarStyle(configurationService: IConfigurationService): 'native' | 'custom' { +export const enum TitlebarStyle { + NATIVE = 'native', + CUSTOM = 'custom', + NATIVE_AND_CUSTOM = 'native-and-custom' +} + +export function showCustomTitlebar(configurationServiceOrTitleStyle: IConfigurationService | TitlebarStyle): boolean { + let titleBarStyle: TitlebarStyle; + if (typeof configurationServiceOrTitleStyle === 'string') { + titleBarStyle = configurationServiceOrTitleStyle; + } else { + titleBarStyle = getTitleBarStyle(configurationServiceOrTitleStyle); + } + return titleBarStyle !== TitlebarStyle.NATIVE; +} + +export function showNativeTitlebar(configurationServiceOrTitleStyle: IConfigurationService | TitlebarStyle): boolean { + let titleBarStyle: TitlebarStyle; + if (typeof configurationServiceOrTitleStyle === 'string') { + titleBarStyle = configurationServiceOrTitleStyle; + } else { + titleBarStyle = getTitleBarStyle(configurationServiceOrTitleStyle); + } + return titleBarStyle !== TitlebarStyle.CUSTOM; +} + +export function getTitleBarStyle(configurationService: IConfigurationService): TitlebarStyle { if (isWeb) { - return 'custom'; + return TitlebarStyle.CUSTOM; } const configuration = configurationService.getValue('window'); if (configuration) { const useNativeTabs = isMacintosh && configuration.nativeTabs === true; if (useNativeTabs) { - return 'native'; // native tabs on sierra do not work with custom title style + return TitlebarStyle.NATIVE; // native tabs on sierra do not work with custom title style } const useSimpleFullScreen = isMacintosh && configuration.nativeFullScreen === false; if (useSimpleFullScreen) { - return 'native'; // simple fullscreen does not work well with custom title style (https://github.com/microsoft/vscode/issues/63291) + return TitlebarStyle.NATIVE; // simple fullscreen does not work well with custom title style (https://github.com/microsoft/vscode/issues/63291) } const style = configuration.titleBarStyle; - if (style === 'native' || style === 'custom') { + if (style === TitlebarStyle.NATIVE || style === TitlebarStyle.CUSTOM || style === TitlebarStyle.NATIVE_AND_CUSTOM) { return style; } } - return isLinux ? 'native' : 'custom'; // default to custom on all macOS and Windows + return isLinux ? TitlebarStyle.NATIVE : TitlebarStyle.CUSTOM; // default to custom on all macOS and Windows } export function useWindowControlsOverlay(configurationService: IConfigurationService): boolean { @@ -196,7 +222,7 @@ export function useWindowControlsOverlay(configurationService: IConfigurationSer return false; // only supported on a desktop Windows instance } - if (getTitleBarStyle(configurationService) === 'native') { + if (showNativeTitlebar(configurationService)) { return false; // only supported when title bar is custom } diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index 4f2bb8182de27..43ce7c4f4ac9d 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -32,7 +32,7 @@ import { IApplicationStorageMainService, IStorageMainService } from 'vs/platform import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ThemeIcon } from 'vs/base/common/themables'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; -import { getMenuBarVisibility, getTitleBarStyle, IFolderToOpen, INativeWindowConfiguration, IWindowSettings, IWorkspaceToOpen, MenuBarVisibility, useNativeFullScreen, useWindowControlsOverlay } from 'vs/platform/window/common/window'; +import { getMenuBarVisibility, IFolderToOpen, INativeWindowConfiguration, IWindowSettings, IWorkspaceToOpen, MenuBarVisibility, showNativeTitlebar, useNativeFullScreen, useWindowControlsOverlay } from 'vs/platform/window/common/window'; 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'; @@ -131,7 +131,7 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow { this._register(Event.fromNodeEventEmitter(this._win, 'leave-full-screen')(() => this._onDidLeaveFullScreen.fire())); // Sheet Offsets - const useCustomTitleStyle = getTitleBarStyle(this.configurationService) === 'custom'; + const useCustomTitleStyle = !showNativeTitlebar(this.configurationService); if (isMacintosh && useCustomTitleStyle) { win.setSheetOffset(isBigSurOrNewer(release()) ? 28 : 22); // offset dialogs by the height of the custom title bar if we have any } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index a658f3fa5ba27..e667ba6f39df7 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { ServicesAccessor, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ICodeWindow, IWindowState } from 'vs/platform/window/electron-main/window'; -import { IOpenEmptyWindowOptions, IWindowOpenable, IWindowSettings, WindowMinimumSize, getTitleBarStyle, useNativeFullScreen, useWindowControlsOverlay, zoomLevelToZoomFactor } from 'vs/platform/window/common/window'; +import { IOpenEmptyWindowOptions, IWindowOpenable, IWindowSettings, WindowMinimumSize, showNativeTitlebar, useNativeFullScreen, useWindowControlsOverlay, zoomLevelToZoomFactor } from 'vs/platform/window/common/window'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { IProductService } from 'vs/platform/product/common/productService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -173,7 +173,7 @@ export function defaultBrowserWindowOptions(accessor: ServicesAccessor, windowSt options.tabbingIdentifier = productService.nameShort; // this opts in to sierra tabs } - const useCustomTitleStyle = getTitleBarStyle(configurationService) === 'custom'; + const useCustomTitleStyle = !showNativeTitlebar(configurationService); if (useCustomTitleStyle) { options.titleBarStyle = 'hidden'; if (!isMacintosh) { diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index bef47d88debd4..a36068ea2d84c 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -19,7 +19,7 @@ import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/co import { ITitleService } from 'vs/workbench/services/title/browser/titleService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { StartupKind, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { getTitleBarStyle, getMenuBarVisibility, IPath } from 'vs/platform/window/common/window'; +import { getMenuBarVisibility, IPath, showNativeTitlebar, showCustomTitlebar } from 'vs/platform/window/common/window'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -366,7 +366,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this._register(addDisposableListener(this.mainContainer, EventType.SCROLL, () => this.mainContainer.scrollTop = 0)); // Menubar visibility changes - if ((isWindows || isLinux || isWeb) && getTitleBarStyle(this.configurationService) === 'custom') { + if ((isWindows || isLinux || isWeb) && !showNativeTitlebar(this.configurationService)) { this._register(this.titleService.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible))); } @@ -453,7 +453,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Changing fullscreen state of the main window has an impact // on custom title bar visibility, so we need to update - if (getTitleBarStyle(this.configurationService) === 'custom') { + if (showCustomTitlebar(this.configurationService)) { // Propagate to grid this.workbenchGrid.setViewVisible(this.titleBarPartView, this.shouldShowTitleBar()); @@ -535,7 +535,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if ( isWeb || isWindows || // not working well with zooming and window control overlays - getTitleBarStyle(this.configurationService) !== 'custom' + showNativeTitlebar(this.configurationService) ) { return; } @@ -1220,15 +1220,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private shouldShowTitleBar(): boolean { // Using the native title bar, don't ever show the custom one - if (getTitleBarStyle(this.configurationService) === 'native') { + if (!showCustomTitlebar(this.configurationService)) { return false; } - // with the command center enabled, we should always show - if (this.configurationService.getValue(LayoutSettings.COMMAND_CENTER)) { - return true; - } - // with the activity bar on top, we should always show if (this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.TOP) { return true; @@ -2068,7 +2063,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi let newVisibilityValue: string; if (currentVisibilityValue === 'visible' || currentVisibilityValue === 'classic') { - newVisibilityValue = getTitleBarStyle(this.configurationService) === 'native' ? 'toggle' : 'compact'; + newVisibilityValue = showNativeTitlebar(this.configurationService) ? 'toggle' : 'compact'; } else { newVisibilityValue = 'classic'; } diff --git a/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts b/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts index d50e62f60952d..3b94637bd57ab 100644 --- a/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts +++ b/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts @@ -14,7 +14,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { getTitleBarStyle } from 'vs/platform/window/common/window'; +import { showCustomTitlebar } from 'vs/platform/window/common/window'; import { IEditorGroupView, IEditorPartsView } from 'vs/workbench/browser/parts/editor/editor'; import { EditorPart, IEditorPartUIState } from 'vs/workbench/browser/parts/editor/editorPart'; import { IAuxiliaryTitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart'; @@ -107,7 +107,7 @@ export class AuxiliaryEditorPart { // Titlebar let titlebarPart: IAuxiliaryTitlebarPart | undefined = undefined; let titlebarPartVisible = false; - const useCustomTitle = isNative && getTitleBarStyle(this.configurationService) === 'custom'; // custom title in aux windows only enabled in native + const useCustomTitle = isNative && showCustomTitlebar(this.configurationService); // custom title in aux windows only enabled in native if (useCustomTitle) { titlebarPart = disposables.add(this.titleService.createAuxiliaryTitlebarPart(auxiliaryWindow.container, editorPart)); titlebarPartVisible = true; diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 80a172c0d157b..43481ae2ad4d9 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/menubarControl'; import { localize, localize2 } from 'vs/nls'; import { IMenuService, MenuId, IMenu, SubmenuItemAction, registerAction2, Action2, MenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; -import { MenuBarVisibility, getTitleBarStyle, IWindowOpenable, getMenuBarVisibility } from 'vs/platform/window/common/window'; +import { MenuBarVisibility, IWindowOpenable, getMenuBarVisibility, showNativeTitlebar } from 'vs/platform/window/common/window'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IAction, Action, SubmenuAction, Separator, IActionRunner, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, toAction } from 'vs/base/common/actions'; import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom'; @@ -351,7 +351,7 @@ export abstract class MenubarControl extends Disposable { } const hasBeenNotified = this.storageService.getBoolean('menubar/accessibleMenubarNotified', StorageScope.APPLICATION, false); - const usingCustomMenubar = getTitleBarStyle(this.configurationService) === 'custom'; + const usingCustomMenubar = !showNativeTitlebar(this.configurationService); if (hasBeenNotified || usingCustomMenubar || !this.accessibilityService.isScreenReaderOptimized()) { return; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 3ca421396a688..1d5fb04ab8ea7 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -8,7 +8,7 @@ import { localize, localize2 } from 'vs/nls'; import { MultiWindowParts, Part } from 'vs/workbench/browser/part'; import { ITitleService } from 'vs/workbench/services/title/browser/titleService'; import { getZoomFactor, isWCOEnabled } from 'vs/base/browser/browser'; -import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/window/common/window'; +import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, TitlebarStyle, showCustomTitlebar, showNativeTitlebar } from 'vs/platform/window/common/window'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; @@ -235,7 +235,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { private readonly hoverDelegate = new TitlebarPartHoverDelegate(this.hoverService, this.configurationService); private readonly titleDisposables = this._register(new DisposableStore()); - private titleBarStyle: 'native' | 'custom' = getTitleBarStyle(this.configurationService); + private titleBarStyle: TitlebarStyle = getTitleBarStyle(this.configurationService); private isInactive: boolean = false; private readonly isAuxiliary: boolean; @@ -299,7 +299,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { oldPartOptions.editorActionsLocation !== newPartOptions.editorActionsLocation || oldPartOptions.showTabs !== newPartOptions.showTabs ) { - if (this.titleBarStyle !== 'native' && this.actionToolBar) { + if (showCustomTitlebar(this.titleBarStyle) && this.actionToolBar) { this.createActionToolBar(); this.createActionToolBarMenus({ editorActions: true }); this._onDidChange.fire(undefined); @@ -310,7 +310,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { protected onConfigurationChanged(event: IConfigurationChangeEvent): void { // Custom menu bar (disabled if auxiliary) - if (!this.isAuxiliary && this.titleBarStyle !== 'native' && (!isMacintosh || isWeb)) { + if (!this.isAuxiliary && !showNativeTitlebar(this.titleBarStyle) && (!isMacintosh || isWeb)) { if (event.affectsConfiguration('window.menuBarVisibility')) { if (this.currentMenubarVisibility === 'compact') { this.uninstallMenubar(); @@ -321,7 +321,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { } // Actions - if (this.titleBarStyle !== 'native' && this.actionToolBar) { + if (showCustomTitlebar(this.titleBarStyle) && this.actionToolBar) { const affectsLayoutControl = event.affectsConfiguration('workbench.layoutControl.enabled'); const affectsActivityControl = event.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION); @@ -388,7 +388,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { this.rightContent = append(this.rootContainer, $('.titlebar-right')); // App Icon (Native Windows/Linux and Web) - if (!isMacintosh && !isWeb) { + if (!isMacintosh && !isWeb && !showNativeTitlebar(this.titleBarStyle)) { this.appIcon = prepend(this.leftContent, $('a.window-appicon')); // Web-only home indicator and menu (not for auxiliary windows) @@ -412,7 +412,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { // Menubar: install a custom menu bar depending on configuration if ( !this.isAuxiliary && - this.titleBarStyle !== 'native' && + !showNativeTitlebar(this.titleBarStyle) && (!isMacintosh || isWeb) && this.currentMenubarVisibility !== 'compact' ) { @@ -424,7 +424,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { this.createTitle(); // Create Toolbar Actions - if (this.titleBarStyle !== 'native') { + if (showCustomTitlebar(this.titleBarStyle)) { this.actionToolBarElement = append(this.rightContent, $('div.action-toolbar-container')); this.createActionToolBar(); this.createActionToolBarMenus(); @@ -442,8 +442,10 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { } } - this.primaryWindowControls = append(primaryControlLocation === 'left' ? this.leftContent : this.rightContent, $('div.window-controls-container.primary')); - append(primaryControlLocation === 'left' ? this.rightContent : this.leftContent, $('div.window-controls-container.secondary')); + if (!showNativeTitlebar(this.titleBarStyle)) { + this.primaryWindowControls = append(primaryControlLocation === 'left' ? this.leftContent : this.rightContent, $('div.window-controls-container.primary')); + append(primaryControlLocation === 'left' ? this.rightContent : this.leftContent, $('div.window-controls-container.secondary')); + } // Context menu over title bar: depending on the OS and the location of the click this will either be // the overall context menu for the entire title bar or a specific title context menu. @@ -736,7 +738,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { private updateLayout(dimension: Dimension): void { this.lastLayoutDimensions = dimension; - if (getTitleBarStyle(this.configurationService) === 'custom') { + if (showCustomTitlebar(this.titleBarStyle)) { const zoomFactor = getZoomFactor(getWindow(this.element)); this.element.style.setProperty('--zoom-factor', zoomFactor.toString()); diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index c41739df745f4..a12266a279b56 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -6,7 +6,7 @@ import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IWindowsConfiguration, IWindowSettings } from 'vs/platform/window/common/window'; +import { IWindowsConfiguration, IWindowSettings, TitlebarStyle } from 'vs/platform/window/common/window'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ConfigurationTarget, IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { localize } from 'vs/nls'; @@ -46,7 +46,8 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo 'security.restrictUNCAccess' ]; - private readonly titleBarStyle = new ChangeObserver<'native' | 'custom'>('string'); + private readonly titleBarStyle = new ChangeObserver('string'); + private previousTitleBarStyle: TitlebarStyle | undefined = undefined; private readonly nativeTabs = new ChangeObserver('boolean'); private readonly nativeFullScreen = new ChangeObserver('boolean'); private readonly clickThroughInactive = new ChangeObserver('boolean'); @@ -85,7 +86,12 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo if (isNative) { // Titlebar style - processChanged((config.window.titleBarStyle === 'native' || config.window.titleBarStyle === 'custom') && this.titleBarStyle.handleChange(config.window?.titleBarStyle)); + processChanged( + (config.window.titleBarStyle === TitlebarStyle.NATIVE || config.window.titleBarStyle === TitlebarStyle.CUSTOM || config.window.titleBarStyle === TitlebarStyle.NATIVE_AND_CUSTOM) && + this.titleBarStyle.handleChange(config.window.titleBarStyle) && + (this.previousTitleBarStyle === TitlebarStyle.CUSTOM || config.window.titleBarStyle === TitlebarStyle.CUSTOM) // only if we come from custom or go to custom + ); + this.previousTitleBarStyle = config.window.titleBarStyle; // macOS: Native tabs processChanged(isMacintosh && this.nativeTabs.handleChange(config.window?.nativeTabs)); diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts b/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts index c3265219d377c..7ae47c23f5d59 100644 --- a/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts +++ b/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts @@ -21,7 +21,7 @@ export class WindowIgnoreMenuShortcutsManager { mainProcessService: IMainProcessService, private readonly _nativeHostService: INativeHostService ) { - this._isUsingNativeTitleBars = configurationService.getValue('window.titleBarStyle') === 'native'; + this._isUsingNativeTitleBars = configurationService.getValue('window.titleBarStyle') !== 'custom'; this._webviewMainService = ProxyChannel.toService(mainProcessService.getChannel('webview')); } diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index f5f5772fccd26..33be5c4b2d9a2 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -227,9 +227,9 @@ import { applicationConfigurationNodeBase, securityConfigurationNodeBase } from 'scope': ConfigurationScope.APPLICATION, 'markdownDescription': localize('window.doubleClickIconToClose', "If enabled, this setting will close the window when the application icon in the title bar is double-clicked. The window will not be able to be dragged by the icon. This setting is effective only if `#window.titleBarStyle#` is set to `custom`.") }, - 'window.titleBarStyle': { + 'window.titleBarStyle': { // Todo 'type': 'string', - 'enum': ['native', 'custom'], + 'enum': ['native', 'custom', 'native-and-custom'], 'default': isLinux ? 'native' : 'custom', 'scope': ConfigurationScope.APPLICATION, 'description': localize('titleBarStyle', "Adjust the appearance of the window title bar to be native by the OS or custom. On Linux and Windows, this setting also affects the application and context menu appearances. Changes require a full restart to apply.") diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 7b95b0c342ca6..8288e6999fe59 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -18,7 +18,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { INativeHostService } from 'vs/platform/native/common/native'; -import { getTitleBarStyle, useWindowControlsOverlay } from 'vs/platform/window/common/window'; +import { showNativeTitlebar, useWindowControlsOverlay } from 'vs/platform/window/common/window'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; @@ -145,7 +145,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart { const targetWindowId = getWindowId(targetWindow); // Native menu controller - if (isMacintosh || getTitleBarStyle(this.configurationService) === 'native') { + if (isMacintosh || showNativeTitlebar(this.configurationService)) { this._register(this.instantiationService.createInstance(NativeMenubarControl)); } @@ -159,7 +159,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart { } // Window Controls (Native Windows/Linux) - if (!isMacintosh && getTitleBarStyle(this.configurationService) !== 'native' && !isWCOEnabled() && this.primaryWindowControls) { + if (!isMacintosh && !showNativeTitlebar(this.configurationService) && !isWCOEnabled() && this.primaryWindowControls) { // Minimize const minimizeIcon = append(this.primaryWindowControls, $('div.window-icon.window-minimize' + ThemeIcon.asCSSSelector(Codicon.chromeMinimize))); @@ -195,7 +195,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart { // Window System Context Menu // See https://github.com/electron/electron/issues/24893 - if (isWindows && getTitleBarStyle(this.configurationService) === 'custom') { + if (isWindows && !showNativeTitlebar(this.configurationService)) { this._register(this.nativeHostService.onDidTriggerWindowSystemContextMenu(({ windowId, x, y }) => { if (targetWindowId !== windowId) { return; @@ -253,7 +253,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart { if ( useWindowControlsOverlay(this.configurationService) || - (isMacintosh && isNative && getTitleBarStyle(this.configurationService) === 'custom') + (isMacintosh && isNative && !showNativeTitlebar(this.configurationService)) ) { // When the user goes into full screen mode, the height of the title bar becomes 0. diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 6fe76f608793a..1a9cbce221ae4 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -14,7 +14,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { EditorResourceAccessor, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors, IResourceDiffEditorInput, IUntypedEditorInput, IEditorPane, isResourceEditorInput, IResourceMergeEditorInput } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { WindowMinimumSize, IOpenFileRequest, getTitleBarStyle, IAddFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest } from 'vs/platform/window/common/window'; +import { WindowMinimumSize, IOpenFileRequest, IAddFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest, showNativeTitlebar } from 'vs/platform/window/common/window'; import { ITitleService } from 'vs/workbench/services/title/browser/titleService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ApplyZoomTarget, applyZoom } from 'vs/platform/window/electron-sandbox/window'; @@ -377,7 +377,7 @@ export class NativeWindow extends BaseWindow { } // Maximize/Restore on doubleclick (for macOS custom title) - if (isMacintosh && getTitleBarStyle(this.configurationService) === 'custom') { + if (isMacintosh && !showNativeTitlebar(this.configurationService)) { this._register(Event.runAndSubscribe(this.layoutService.onDidAddContainer, ({ container, disposables }) => { const targetWindow = getWindow(container); const targetWindowId = targetWindow.vscodeWindowId; @@ -611,7 +611,7 @@ export class NativeWindow extends BaseWindow { this.customTitleContextMenuDisposable.clear(); // Provide new menu if a file is opened and we are on a custom title - if (!filePath || getTitleBarStyle(this.configurationService) !== 'custom') { + if (!filePath || !showNativeTitlebar(this.configurationService)) { return; } diff --git a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts index 381c8be72ec45..b0ca3f2175e75 100644 --- a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts @@ -15,7 +15,7 @@ import { IContextMenuDelegate, IContextMenuEvent } from 'vs/base/browser/context import { createSingleCallFunction } from 'vs/base/common/functional'; import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; import { popup } from 'vs/base/parts/contextmenu/electron-sandbox/contextmenu'; -import { getTitleBarStyle } from 'vs/platform/window/common/window'; +import { showNativeTitlebar } from 'vs/platform/window/common/window'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextMenuMenuDelegate, ContextMenuService as HTMLContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; @@ -48,7 +48,7 @@ export class ContextMenuService implements IContextMenuService { ) { // Custom context menu: Linux/Windows if custom title is enabled - if (!isMacintosh && getTitleBarStyle(configurationService) === 'custom') { + if (!isMacintosh && !showNativeTitlebar(configurationService)) { this.impl = new HTMLContextMenuService(telemetryService, notificationService, contextViewService, keybindingService, menuService, contextKeyService); } From 697936c5f8d87350a59d454108ccb1ab29b42136 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 16 Jan 2024 16:17:40 +0100 Subject: [PATCH 025/333] Git - update getBranchBase so that it returns an upstream branch (#202586) --- extensions/git/src/repository.ts | 41 ++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 94ce234bb01e7..8ac566827a690 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1475,33 +1475,35 @@ export class Repository implements Disposable { async getBranchBase(ref: string): Promise { const branch = await this.getBranch(ref); - const branchMergeBaseConfigKey = `branch.${branch.name}.vscode-merge-base`; + const branchUpstream = await this.getUpstreamBranch(branch); - // Upstream - if (branch.upstream) { - return await this.getBranch(`refs/remotes/${branch.upstream.remote}/${branch.upstream.name}`); + if (branchUpstream) { + return branchUpstream; } // Git config + const mergeBaseConfigKey = `branch.${branch.name}.vscode-merge-base`; + try { - const mergeBase = await this.getConfig(branchMergeBaseConfigKey); - if (mergeBase !== '') { - const mergeBaseBranch = await this.getBranch(mergeBase); - return mergeBaseBranch; + const mergeBase = await this.getConfig(mergeBaseConfigKey); + const branchFromConfig = mergeBase !== '' ? await this.getBranch(mergeBase) : undefined; + if (branchFromConfig) { + return branchFromConfig; } } catch (err) { } // Reflog const branchFromReflog = await this.getBranchBaseFromReflog(ref); - if (branchFromReflog) { - await this.setConfig(branchMergeBaseConfigKey, branchFromReflog.name!); - return branchFromReflog; + const branchFromReflogUpstream = branchFromReflog ? await this.getUpstreamBranch(branchFromReflog) : undefined; + if (branchFromReflogUpstream) { + await this.setConfig(mergeBaseConfigKey, `${branchFromReflogUpstream.remote}/${branchFromReflogUpstream.name}`); + return branchFromReflogUpstream; } // Default branch const defaultBranch = await this.getDefaultBranch(); if (defaultBranch) { - await this.setConfig(branchMergeBaseConfigKey, defaultBranch.name!); + await this.setConfig(mergeBaseConfigKey, `${defaultBranch.remote}/${defaultBranch.name}`); return defaultBranch; } @@ -1552,6 +1554,21 @@ export class Repository implements Disposable { return undefined; } + private async getUpstreamBranch(branch: Branch): Promise { + if (!branch.upstream) { + return undefined; + } + + try { + const upstreamBranch = await this.getBranch(`refs/remotes/${branch.upstream.remote}/${branch.upstream.name}`); + return upstreamBranch; + } + catch (err) { + this.logger.warn(`Failed to get branch details for 'refs/remotes/${branch.upstream.remote}/${branch.upstream.name}': ${err.message}.`); + return undefined; + } + } + async getRefs(query: RefQuery = {}, cancellationToken?: CancellationToken): Promise { const config = workspace.getConfiguration('git'); let defaultSort = config.get<'alphabetically' | 'committerdate'>('branchSortOrder'); From c9127a68f16dccbe4a6f2c3ab461ddfa20e79d83 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 16 Jan 2024 20:58:22 +0530 Subject: [PATCH 026/333] rename (#202588) --- src/vs/platform/download/common/downloadIpc.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/download/common/downloadIpc.ts b/src/vs/platform/download/common/downloadIpc.ts index e83417eed87f4..3e88e8f668e5f 100644 --- a/src/vs/platform/download/common/downloadIpc.ts +++ b/src/vs/platform/download/common/downloadIpc.ts @@ -32,10 +32,10 @@ export class DownloadServiceChannelClient implements IDownloadService { constructor(private channel: IChannel, private getUriTransformer: () => IURITransformer | null) { } async download(from: URI, to: URI): Promise { - const uriTransfomer = this.getUriTransformer(); - if (uriTransfomer) { - from = uriTransfomer.transformOutgoingURI(from); - to = uriTransfomer.transformOutgoingURI(to); + const uriTransformer = this.getUriTransformer(); + if (uriTransformer) { + from = uriTransformer.transformOutgoingURI(from); + to = uriTransformer.transformOutgoingURI(to); } await this.channel.call('download', [from, to]); } From 9204cd8b4b35afbd46040385854da2e803c5b652 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 16 Jan 2024 08:05:18 -0800 Subject: [PATCH 027/333] testing: fix error hovering with no coverage open (#202591) Fixes #202564 --- .../testing/browser/codeCoverageDecorations.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts b/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts index 4c7a6caa21172..c981e552a9738 100644 --- a/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts @@ -105,12 +105,13 @@ export class CodeCoverageDecorations extends Disposable implements IEditorContri })); this._register(editor.onMouseMove(e => { - if (e.target.type === MouseTargetType.GUTTER_LINE_NUMBERS) { + const model = editor.getModel(); + if (e.target.type === MouseTargetType.GUTTER_LINE_NUMBERS && model) { this.hoverLineNumber(editor.getModel()!, e.target.position.lineNumber); } else if (this.lineHoverWidget.hasValue && this.lineHoverWidget.value.getDomNode().contains(e.target.element)) { // don't dismiss the hover - } else if (CodeCoverageDecorations.showInline.get() && e.target.type === MouseTargetType.CONTENT_TEXT) { - this.hoverInlineDecoration(editor.getModel()!, e.target.position); + } else if (CodeCoverageDecorations.showInline.get() && e.target.type === MouseTargetType.CONTENT_TEXT && model) { + this.hoverInlineDecoration(model, e.target.position); } else { this.hoveredStore.clear(); } @@ -168,7 +169,7 @@ export class CodeCoverageDecorations extends Disposable implements IEditorContri } private hoverLineNumber(model: ITextModel, lineNumber: number) { - if (lineNumber === this.hoveredSubject) { + if (lineNumber === this.hoveredSubject || !this.details) { return; } @@ -208,7 +209,7 @@ export class CodeCoverageDecorations extends Disposable implements IEditorContri }); } - this.lineHoverWidget.value.startShowingAt(lineNumber, this.details!, wasPreviouslyHovering); + this.lineHoverWidget.value.startShowingAt(lineNumber, this.details, wasPreviouslyHovering); this.hoveredStore.add(this.editor.onMouseLeave(() => { this.hoveredStore.clear(); From f64beed46107217e0faaa2f3fd67211b64941d6c Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Tue, 16 Jan 2024 08:54:24 -0800 Subject: [PATCH 028/333] don't layout notebook when not visible (#202392) * don't layout notebook when not visible * only call this when hiding --- .../contrib/notebook/browser/notebookEditor.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 694914c99461a..f95c61f73f5a5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -150,6 +150,13 @@ export class NotebookEditor extends EditorPane implements INotebookEditorPane { return this._widget.value; } + override setVisible(visible: boolean, group?: IEditorGroup | undefined): void { + super.setVisible(visible, group); + if (!visible) { + this._widget.value?.onWillHide(); + } + } + protected override setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { super.setEditorVisible(visible, group); if (group) { @@ -552,7 +559,9 @@ export class NotebookEditor extends EditorPane implements INotebookEditorPane { return; } - this._widget.value.layout(dimension, this._rootElement, position); + if (this.isVisible()) { + this._widget.value.layout(dimension, this._rootElement, position); + } } //#endregion From ca3a8724f4a3e6b53cac6426c4ea6f0c9239e207 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 16 Jan 2024 18:05:25 +0100 Subject: [PATCH 029/333] add `IKeybindingService#enableKeybindingHoldMode` --- .../common/abstractKeybindingService.ts | 26 ++++++++++++++++--- .../platform/keybinding/common/keybinding.ts | 9 ++++++- .../test/common/mockKeybindingService.ts | 4 +++ .../keybinding/browser/keybindingService.ts | 4 +++ 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/keybinding/common/abstractKeybindingService.ts b/src/vs/platform/keybinding/common/abstractKeybindingService.ts index cfc5e7e9e110c..0df56233ba139 100644 --- a/src/vs/platform/keybinding/common/abstractKeybindingService.ts +++ b/src/vs/platform/keybinding/common/abstractKeybindingService.ts @@ -53,6 +53,8 @@ export abstract class AbstractKeybindingService extends Disposable implements IK private _ignoreSingleModifiers: KeybindingModifierSet; private _currentSingleModifier: SingleModifierChord | null; private _currentSingleModifierClearTimeout: TimeoutTimer; + private _currentlyDispatchingCommandId: string | null; + protected _isInKeybindingHoldMode: boolean; protected _logging: boolean; @@ -75,6 +77,8 @@ export abstract class AbstractKeybindingService extends Disposable implements IK this._ignoreSingleModifiers = KeybindingModifierSet.EMPTY; this._currentSingleModifier = null; this._currentSingleModifierClearTimeout = new TimeoutTimer(); + this._currentlyDispatchingCommandId = null; + this._isInKeybindingHoldMode = false; this._logging = false; } @@ -362,10 +366,15 @@ export abstract class AbstractKeybindingService extends Disposable implements IK } this._log(`+ Invoking command ${resolveResult.commandId}.`); - if (typeof resolveResult.commandArgs === 'undefined') { - this._commandService.executeCommand(resolveResult.commandId).then(undefined, err => this._notificationService.warn(err)); - } else { - this._commandService.executeCommand(resolveResult.commandId, resolveResult.commandArgs).then(undefined, err => this._notificationService.warn(err)); + this._currentlyDispatchingCommandId = resolveResult.commandId; + try { + if (typeof resolveResult.commandArgs === 'undefined') { + this._commandService.executeCommand(resolveResult.commandId).then(undefined, err => this._notificationService.warn(err)); + } else { + this._commandService.executeCommand(resolveResult.commandId, resolveResult.commandArgs).then(undefined, err => this._notificationService.warn(err)); + } + } finally { + this._currentlyDispatchingCommandId = null; } if (!HIGH_FREQ_COMMANDS.test(resolveResult.commandId)) { @@ -378,6 +387,15 @@ export abstract class AbstractKeybindingService extends Disposable implements IK } } + enableKeybindingHoldMode(commandId: string): boolean { + if (this._currentlyDispatchingCommandId !== commandId) { + return false; + } + this._isInKeybindingHoldMode = true; + this._log(`+ Enabled hold-mode for ${commandId}.`); + return true; + } + mightProducePrintableCharacter(event: IKeyboardEvent): boolean { if (event.ctrlKey || event.metaKey) { // ignore ctrl/cmd-combination but not shift/alt-combinatios diff --git a/src/vs/platform/keybinding/common/keybinding.ts b/src/vs/platform/keybinding/common/keybinding.ts index 9a1e812a22e89..789d044a64f88 100644 --- a/src/vs/platform/keybinding/common/keybinding.ts +++ b/src/vs/platform/keybinding/common/keybinding.ts @@ -65,6 +65,14 @@ export interface IKeybindingService { */ softDispatch(keyboardEvent: IKeyboardEvent, target: IContextKeyServiceTarget): ResolutionResult; + /** + * Enable hold mode for this command. This is only possible if the command is current being dispatched, meaning + * we are after its keydown and before is keyup event. + * + * @returns true is hold mode was enabled + */ + enableKeybindingHoldMode(commandId: string): boolean; + dispatchByUserSettingsLabel(userSettingsLabel: string, target: IContextKeyServiceTarget): void; /** @@ -100,4 +108,3 @@ export interface IKeybindingService { _dumpDebugInfo(): string; _dumpDebugInfoJSON(): string; } - diff --git a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts index c655b9f9b7afc..a9d352e24ea89 100644 --- a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts +++ b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts @@ -147,6 +147,10 @@ export class MockKeybindingService implements IKeybindingService { return false; } + public enableKeybindingHoldMode(commandId: string): boolean { + return false; + } + public mightProducePrintableCharacter(e: IKeyboardEvent): boolean { return false; } diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index ad218733589ad..3222c7dfab661 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -269,6 +269,9 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { // for standard keybindings disposables.add(dom.addDisposableListener(window, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { + if (this._isInKeybindingHoldMode) { + return; + } this.isComposingGlobalContextKey.set(e.isComposing); const keyEvent = new StandardKeyboardEvent(e); this._log(`/ Received keydown event - ${printKeyboardEvent(e)}`); @@ -282,6 +285,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { // for single modifier chord keybindings (e.g. shift shift) disposables.add(dom.addDisposableListener(window, dom.EventType.KEY_UP, (e: KeyboardEvent) => { + this._isInKeybindingHoldMode = false; this.isComposingGlobalContextKey.set(e.isComposing); const keyEvent = new StandardKeyboardEvent(e); const shouldPreventDefault = this._singleModifierDispatch(keyEvent, keyEvent.target); From 351e07bbb6bbc9bcf00baf009610fb31f726b946 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 16 Jan 2024 18:10:26 +0100 Subject: [PATCH 030/333] Error: Cannot register two commands with the same id: workbench.action.focusCommentsPanel (#202596) Fixes #202563 --- .../comments/browser/comments.contribution.ts | 58 +++++++++++++++- .../contrib/comments/browser/commentsView.ts | 66 ++----------------- 2 files changed, 63 insertions(+), 61 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts index d6e05c6e8723f..eb7c912fb6d9a 100644 --- a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts +++ b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts @@ -14,7 +14,7 @@ import * as strings from 'vs/base/common/strings'; import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { AccessibleViewType, IAccessibleContentProvider, IAccessibleViewOptions, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Disposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -27,6 +27,62 @@ import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/co import { COMMENTS_VIEW_ID } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; import { CommentThreadState } from 'vs/editor/common/languages'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; +import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { CONTEXT_KEY_HAS_COMMENTS, CONTEXT_KEY_SOME_COMMENTS_EXPANDED, CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsView'; +import { ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; +import { Codicon } from 'vs/base/common/codicons'; + +CommandsRegistry.registerCommand({ + id: 'workbench.action.focusCommentsPanel', + handler: async (accessor) => { + const viewsService = accessor.get(IViewsService); + viewsService.openView(COMMENTS_VIEW_ID, true); + } +}); + +registerAction2(class Collapse extends ViewAction { + constructor() { + super({ + viewId: COMMENTS_VIEW_ID, + id: 'comments.collapse', + title: nls.localize('collapseAll', "Collapse All"), + f1: false, + icon: Codicon.collapseAll, + menu: { + id: MenuId.ViewTitle, + group: 'navigation', + when: ContextKeyExpr.and(ContextKeyExpr.and(ContextKeyExpr.equals('view', COMMENTS_VIEW_ID), CONTEXT_KEY_HAS_COMMENTS), CONTEXT_KEY_SOME_COMMENTS_EXPANDED), + order: 100 + } + }); + } + runInView(_accessor: ServicesAccessor, view: CommentsPanel) { + view.collapseAll(); + } +}); + +registerAction2(class Expand extends ViewAction { + constructor() { + super({ + viewId: COMMENTS_VIEW_ID, + id: 'comments.expand', + title: nls.localize('expandAll', "Expand All"), + f1: false, + icon: Codicon.expandAll, + menu: { + id: MenuId.ViewTitle, + group: 'navigation', + when: ContextKeyExpr.and(ContextKeyExpr.and(ContextKeyExpr.equals('view', COMMENTS_VIEW_ID), CONTEXT_KEY_HAS_COMMENTS), ContextKeyExpr.not(CONTEXT_KEY_SOME_COMMENTS_EXPANDED.key)), + order: 100 + } + }); + } + runInView(_accessor: ServicesAccessor, view: CommentsPanel) { + view.expandAll(); + } +}); Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ id: 'comments', diff --git a/src/vs/workbench/contrib/comments/browser/commentsView.ts b/src/vs/workbench/contrib/comments/browser/commentsView.ts index 598f2501d3f2c..ef8d51a131364 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsView.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsView.ts @@ -8,27 +8,23 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { basename } from 'vs/base/common/resources'; import { isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { CommentNode, ResourceWithCommentThreads, ICommentThreadChangedEvent } from 'vs/workbench/contrib/comments/common/commentModel'; import { IWorkspaceCommentThreadsEvent, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService'; import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { textLinkForeground, textLinkActiveForeground, focusBorder, textPreformatForeground } from 'vs/platform/theme/common/colorRegistry'; import { ResourceLabels } from 'vs/workbench/browser/labels'; -import { CommentsList, COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE, Filter } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; -import { IViewPaneOptions, ViewAction, FilterViewPane } from 'vs/workbench/browser/parts/views/viewPane'; +import { CommentsList, COMMENTS_VIEW_TITLE, Filter } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; +import { IViewPaneOptions, FilterViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewDescriptorService } from 'vs/workbench/common/views'; -import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; -import { Codicon } from 'vs/base/common/codicons'; import { IEditor } from 'vs/editor/common/editorCommon'; import { TextModel } from 'vs/editor/common/model/textModel'; import { CommentsViewFilterFocusContextKey, ICommentsView } from 'vs/workbench/contrib/comments/browser/comments'; @@ -44,8 +40,8 @@ import { Range } from 'vs/editor/common/core/range'; import { registerNavigableContainer } from 'vs/workbench/browser/actions/widgetNavigationCommands'; import { CommentsModel, ICommentsModel } from 'vs/workbench/contrib/comments/browser/commentsModel'; -const CONTEXT_KEY_HAS_COMMENTS = new RawContextKey('commentsView.hasComments', false); -const CONTEXT_KEY_SOME_COMMENTS_EXPANDED = new RawContextKey('commentsView.someCommentsExpanded', false); +export const CONTEXT_KEY_HAS_COMMENTS = new RawContextKey('commentsView.hasComments', false); +export const CONTEXT_KEY_SOME_COMMENTS_EXPANDED = new RawContextKey('commentsView.someCommentsExpanded', false); const VIEW_STORAGE_ID = 'commentsViewState'; function createResourceCommentsIterator(model: ICommentsModel): Iterable> { @@ -505,53 +501,3 @@ export class CommentsPanel extends FilterViewPane implements ICommentsView { return false; } } - -CommandsRegistry.registerCommand({ - id: 'workbench.action.focusCommentsPanel', - handler: async (accessor) => { - const viewsService = accessor.get(IViewsService); - viewsService.openView(COMMENTS_VIEW_ID, true); - } -}); - -registerAction2(class Collapse extends ViewAction { - constructor() { - super({ - viewId: COMMENTS_VIEW_ID, - id: 'comments.collapse', - title: nls.localize('collapseAll', "Collapse All"), - f1: false, - icon: Codicon.collapseAll, - menu: { - id: MenuId.ViewTitle, - group: 'navigation', - when: ContextKeyExpr.and(ContextKeyExpr.and(ContextKeyExpr.equals('view', COMMENTS_VIEW_ID), CONTEXT_KEY_HAS_COMMENTS), CONTEXT_KEY_SOME_COMMENTS_EXPANDED), - order: 100 - } - }); - } - runInView(_accessor: ServicesAccessor, view: CommentsPanel) { - view.collapseAll(); - } -}); - -registerAction2(class Expand extends ViewAction { - constructor() { - super({ - viewId: COMMENTS_VIEW_ID, - id: 'comments.expand', - title: nls.localize('expandAll', "Expand All"), - f1: false, - icon: Codicon.expandAll, - menu: { - id: MenuId.ViewTitle, - group: 'navigation', - when: ContextKeyExpr.and(ContextKeyExpr.and(ContextKeyExpr.equals('view', COMMENTS_VIEW_ID), CONTEXT_KEY_HAS_COMMENTS), ContextKeyExpr.not(CONTEXT_KEY_SOME_COMMENTS_EXPANDED.key)), - order: 100 - } - }); - } - runInView(_accessor: ServicesAccessor, view: CommentsPanel) { - view.expandAll(); - } -}); From 481d7bb72d759a02a81df86bee8c657e6c9b3474 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 16 Jan 2024 18:16:31 +0100 Subject: [PATCH 031/333] Use hold mode in start inline chat action to enable speech --- .../browser/inlineChat.contribution.ts | 62 ------------- .../inlineChat/browser/inlineChatActions.ts | 36 +------- .../contrib/inlineChat/common/inlineChat.ts | 6 ++ .../inlineChat.contribution.ts | 59 +++++++++++++ .../electron-sandbox/inlineChatActions.ts | 88 +++++++++++++++++++ src/vs/workbench/workbench.common.main.ts | 1 - 6 files changed, 156 insertions(+), 96 deletions(-) delete mode 100644 src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts create mode 100644 src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts deleted file mode 100644 index d68c3dc3dc8fc..0000000000000 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts +++ /dev/null @@ -1,62 +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 { registerAction2 } from 'vs/platform/actions/common/actions'; -import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; -import * as InlineChatActions from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions'; -import { IInlineChatService, INLINE_CHAT_ID, INTERACTIVE_EDITOR_ACCESSIBILITY_HELP_ID } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; -import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { InlineChatServiceImpl } from 'vs/workbench/contrib/inlineChat/common/inlineChatServiceImpl'; -import { InlineChatSessionServiceImpl } from './inlineChatSessionServiceImpl'; -import { IInlineChatSessionService } from './inlineChatSessionService'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { InlineChatNotebookContribution } from 'vs/workbench/contrib/inlineChat/browser/inlineChatNotebook'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { InlineChatAccessibleViewContribution } from './inlineChatAccessibleView'; -import { InlineChatSavingServiceImpl } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl'; -import { IInlineChatSavingService } from './inlineChatSavingService'; - -registerSingleton(IInlineChatService, InlineChatServiceImpl, InstantiationType.Delayed); -registerSingleton(IInlineChatSessionService, InlineChatSessionServiceImpl, InstantiationType.Delayed); -registerSingleton(IInlineChatSavingService, InlineChatSavingServiceImpl, InstantiationType.Delayed); - -registerEditorContribution(INLINE_CHAT_ID, InlineChatController, EditorContributionInstantiation.Eager); // EAGER because of notebook dispose/create of editors -registerEditorContribution(INTERACTIVE_EDITOR_ACCESSIBILITY_HELP_ID, InlineChatActions.InlineAccessibilityHelpContribution, EditorContributionInstantiation.Eventually); - -registerAction2(InlineChatActions.StartSessionAction); -registerAction2(InlineChatActions.CloseAction); -registerAction2(InlineChatActions.ConfigureInlineChatAction); -// registerAction2(InlineChatActions.UnstashSessionAction); -registerAction2(InlineChatActions.MakeRequestAction); -registerAction2(InlineChatActions.StopRequestAction); -registerAction2(InlineChatActions.ReRunRequestAction); -registerAction2(InlineChatActions.DiscardHunkAction); -registerAction2(InlineChatActions.DiscardAction); -registerAction2(InlineChatActions.DiscardToClipboardAction); -registerAction2(InlineChatActions.DiscardUndoToNewFileAction); -registerAction2(InlineChatActions.CancelSessionAction); - -registerAction2(InlineChatActions.ArrowOutUpAction); -registerAction2(InlineChatActions.ArrowOutDownAction); -registerAction2(InlineChatActions.FocusInlineChat); -registerAction2(InlineChatActions.PreviousFromHistory); -registerAction2(InlineChatActions.NextFromHistory); -registerAction2(InlineChatActions.ViewInChatAction); -registerAction2(InlineChatActions.ExpandMessageAction); -registerAction2(InlineChatActions.ContractMessageAction); - -registerAction2(InlineChatActions.ToggleDiffForChange); -registerAction2(InlineChatActions.FeebackHelpfulCommand); -registerAction2(InlineChatActions.FeebackUnhelpfulCommand); -registerAction2(InlineChatActions.ReportIssueForBugCommand); -registerAction2(InlineChatActions.AcceptChanges); - -registerAction2(InlineChatActions.CopyRecordings); - -const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(InlineChatNotebookContribution, LifecyclePhase.Restored); -workbenchContributionsRegistry.registerWorkbenchContribution(InlineChatAccessibleViewContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 0a753c928022a..af47d8f2b764e 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { Codicon } from 'vs/base/common/codicons'; -import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { InlineChatController, InlineChatRunOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; +import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, CTX_INLINE_CHAT_HAS_PROVIDER, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_VISIBLE, MENU_INLINE_CHAT_INPUT, MENU_INLINE_CHAT_WIDGET_DISCARD, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_EDIT_MODE, EditMode, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, CTX_INLINE_CHAT_MESSAGE_CROP_STATE, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, MENU_INLINE_CHAT_WIDGET_FEEDBACK, ACTION_ACCEPT_CHANGES, ACTION_REGENERATE_RESPONSE, CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseTypes, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_INNER_CURSOR_START, CTX_INLINE_CHAT_INNER_CURSOR_END, CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING, InlineChatResponseFeedbackKind, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, MENU_INLINE_CHAT_WIDGET } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { localize, localize2 } from 'vs/nls'; import { IAction2Options, MenuRegistry } from 'vs/platform/actions/common/actions'; @@ -33,37 +33,7 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr CommandsRegistry.registerCommandAlias('interactiveEditor.start', 'inlineChat.start'); export const LOCALIZED_START_INLINE_CHAT_STRING = localize('run', 'Start Inline Chat'); -const START_INLINE_CHAT = registerIcon('start-inline-chat', Codicon.sparkle, localize('startInlineChat', 'Icon which spawns the inline chat from the editor toolbar.')); - -export class StartSessionAction extends EditorAction2 { - - constructor() { - super({ - id: 'inlineChat.start', - title: { value: LOCALIZED_START_INLINE_CHAT_STRING, original: 'Start Inline Chat' }, - category: AbstractInlineChatAction.category, - f1: true, - precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_PROVIDER, EditorContextKeys.writable), - keybinding: { - when: EditorContextKeys.focus, - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyMod.CtrlCmd | KeyCode.KeyI, - secondary: [KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyI)], - }, - icon: START_INLINE_CHAT - }); - } - - - override runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) { - let options: InlineChatRunOptions | undefined; - const arg = _args[0]; - if (arg && InlineChatRunOptions.isInteractiveEditorOptions(arg)) { - options = arg; - } - InlineChatController.get(editor)?.run({ ...options }); - } -} +export const START_INLINE_CHAT = registerIcon('start-inline-chat', Codicon.sparkle, localize('startInlineChat', 'Icon which spawns the inline chat from the editor toolbar.')); export class UnstashSessionAction extends EditorAction2 { constructor() { diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index cef2b7500cad3..1ac180612069f 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -202,6 +202,7 @@ export const enum InlineChatConfigKeys { Mode = 'inlineChat.mode', FinishOnType = 'inlineChat.finishOnType', AcceptedOrDiscardBeforeSave = 'inlineChat.acceptedOrDiscardBeforeSave', + HoldToSpeech = 'inlineChat.holdToSpeech', } Registry.as(Extensions.Configuration).registerConfiguration({ @@ -227,6 +228,11 @@ Registry.as(Extensions.Configuration).registerConfigurat description: localize('acceptedOrDiscardBeforeSave', "Whether pending inline chat sessions prevent saving."), default: true, type: 'boolean' + }, + [InlineChatConfigKeys.HoldToSpeech]: { + description: localize('holdToSpeech', "Whether holding the inline chat keybinding will automatically enable speech recognition."), + default: true, + type: 'boolean' } } }); diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts index d60b942064404..33942d1fea487 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts @@ -6,8 +6,67 @@ import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { registerAction2 } from 'vs/platform/actions/common/actions'; import { CancelAction, InlineChatQuickVoice, StartAction, StopAction } from 'vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice'; +import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; +import * as InlineChatActions from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions'; +import * as StartSessionAction from './inlineChatActions'; +import { IInlineChatService, INLINE_CHAT_ID, INTERACTIVE_EDITOR_ACCESSIBILITY_HELP_ID } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InlineChatServiceImpl } from 'vs/workbench/contrib/inlineChat/common/inlineChatServiceImpl'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { InlineChatNotebookContribution } from 'vs/workbench/contrib/inlineChat/browser/inlineChatNotebook'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { InlineChatSavingServiceImpl } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl'; +import { InlineChatAccessibleViewContribution } from 'vs/workbench/contrib/inlineChat/browser/inlineChatAccessibleView'; +import { IInlineChatSavingService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSavingService'; +import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; +import { InlineChatSessionServiceImpl } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl'; +// --- electron-browser registerEditorContribution(InlineChatQuickVoice.ID, InlineChatQuickVoice, EditorContributionInstantiation.Eager); // EAGER because of notebook dispose/create of editors registerAction2(StartAction); registerAction2(StopAction); registerAction2(CancelAction); + +// --- browser + +registerSingleton(IInlineChatService, InlineChatServiceImpl, InstantiationType.Delayed); +registerSingleton(IInlineChatSessionService, InlineChatSessionServiceImpl, InstantiationType.Delayed); +registerSingleton(IInlineChatSavingService, InlineChatSavingServiceImpl, InstantiationType.Delayed); + +registerEditorContribution(INLINE_CHAT_ID, InlineChatController, EditorContributionInstantiation.Eager); // EAGER because of notebook dispose/create of editors +registerEditorContribution(INTERACTIVE_EDITOR_ACCESSIBILITY_HELP_ID, InlineChatActions.InlineAccessibilityHelpContribution, EditorContributionInstantiation.Eventually); + +registerAction2(StartSessionAction.StartSessionAction); +registerAction2(InlineChatActions.CloseAction); +registerAction2(InlineChatActions.ConfigureInlineChatAction); +// registerAction2(InlineChatActions.UnstashSessionAction); +registerAction2(InlineChatActions.MakeRequestAction); +registerAction2(InlineChatActions.StopRequestAction); +registerAction2(InlineChatActions.ReRunRequestAction); +registerAction2(InlineChatActions.DiscardHunkAction); +registerAction2(InlineChatActions.DiscardAction); +registerAction2(InlineChatActions.DiscardToClipboardAction); +registerAction2(InlineChatActions.DiscardUndoToNewFileAction); +registerAction2(InlineChatActions.CancelSessionAction); + +registerAction2(InlineChatActions.ArrowOutUpAction); +registerAction2(InlineChatActions.ArrowOutDownAction); +registerAction2(InlineChatActions.FocusInlineChat); +registerAction2(InlineChatActions.PreviousFromHistory); +registerAction2(InlineChatActions.NextFromHistory); +registerAction2(InlineChatActions.ViewInChatAction); +registerAction2(InlineChatActions.ExpandMessageAction); +registerAction2(InlineChatActions.ContractMessageAction); + +registerAction2(InlineChatActions.ToggleDiffForChange); +registerAction2(InlineChatActions.FeebackHelpfulCommand); +registerAction2(InlineChatActions.FeebackUnhelpfulCommand); +registerAction2(InlineChatActions.ReportIssueForBugCommand); +registerAction2(InlineChatActions.AcceptChanges); + +registerAction2(InlineChatActions.CopyRecordings); + +const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchContributionsRegistry.registerWorkbenchContribution(InlineChatNotebookContribution, LifecyclePhase.Restored); +workbenchContributionsRegistry.registerWorkbenchContribution(InlineChatAccessibleViewContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts new file mode 100644 index 0000000000000..c0f8c8f5efba8 --- /dev/null +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as dom from 'vs/base/browser/dom'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { InlineChatController, InlineChatRunOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; +import { AbstractInlineChatAction } from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions'; +import { LOCALIZED_START_INLINE_CHAT_STRING, START_INLINE_CHAT } from '../browser/inlineChatActions'; +import { disposableTimeout } from 'vs/base/common/async'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { StartVoiceChatAction, StopListeningAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; +import { CTX_INLINE_CHAT_HAS_PROVIDER, InlineChatConfigKeys } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ISpeechService } from 'vs/workbench/contrib/speech/common/speechService'; + + +export class StartSessionAction extends EditorAction2 { + + constructor() { + super({ + id: 'inlineChat.start', + title: { value: LOCALIZED_START_INLINE_CHAT_STRING, original: 'Start Inline Chat' }, + category: AbstractInlineChatAction.category, + f1: true, + precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_PROVIDER, EditorContextKeys.writable), + keybinding: { + when: EditorContextKeys.focus, + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyCode.KeyI, + secondary: [KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyI)], + }, + icon: START_INLINE_CHAT + }); + } + + + override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) { + + const configService = accessor.get(IConfigurationService); + const speechService = accessor.get(ISpeechService); + const keybindingService = accessor.get(IKeybindingService); + const commandService = accessor.get(ICommandService); + + if (configService.getValue(InlineChatConfigKeys.HoldToSpeech) // enabled + && speechService.hasSpeechProvider // possible + && keybindingService.enableKeybindingHoldMode(this.desc.id) // holding keys + ) { + + + let listening = false; + const handle = disposableTimeout(() => { + // start VOICE input + commandService.executeCommand(StartVoiceChatAction.ID); + listening = true; + }, 100); + + const listener = dom.addDisposableListener( + dom.getWindow(editor.getDomNode()), + dom.EventType.KEY_UP, + (_e: KeyboardEvent) => { + listener.dispose(); // Event.once + if (listening) { + commandService.executeCommand(StopListeningAction.ID).finally(() => { + InlineChatController.get(editor)?.acceptInput(); + }); + } else { + handle.dispose(); + } + } + ); + } + + let options: InlineChatRunOptions | undefined; + const arg = _args[0]; + if (arg && InlineChatRunOptions.isInteractiveEditorOptions(arg)) { + options = arg; + } + InlineChatController.get(editor)?.run({ ...options }); + } +} diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index bcad16fc8025b..ae453cf83d470 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -182,7 +182,6 @@ import 'vs/workbench/contrib/speech/common/speech.contribution'; // Chat import 'vs/workbench/contrib/chat/browser/chat.contribution'; -import 'vs/workbench/contrib/inlineChat/browser/inlineChat.contribution'; // Interactive import 'vs/workbench/contrib/interactive/browser/interactive.contribution'; From fee9ef203aa2ef10b4d8ab8fd019994ec422662f Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 16 Jan 2024 09:25:15 -0800 Subject: [PATCH 032/333] eng: run with coverage on decoration click for more selfhosting (#202592) --- .vscode/settings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index bdbb92f271b03..99e495ab781a5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -170,4 +170,5 @@ }, "css.format.spaceAroundSelectorSeparator": true, "inlineChat.mode": "live", + "testing.defaultGutterClickAction": "runWithCoverage", } From e1e6c5de268228e003033f855f58fa77248271aa Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 16 Jan 2024 09:40:03 -0800 Subject: [PATCH 033/333] Fix #202598. Handle browser detection correctly in notebook webview preload (#202599) --- .../notebook/browser/view/renderers/webviewPreloads.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 32082d471ab25..b147a72091b86 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -9,7 +9,6 @@ import type { IDisposable } from 'vs/base/common/lifecycle'; import type * as webviewMessages from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages'; import type { NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import type * as rendererApi from 'vscode-notebook-renderer'; -import * as browser from 'vs/base/browser/browser'; // !! IMPORTANT !! ---------------------------------------------------------------------------------- // import { RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -92,6 +91,8 @@ declare function __import(path: string): Promise; async function webviewPreloads(ctx: PreloadContext) { // eslint-disable-next-line no-restricted-globals const $window = window as typeof DOM.$window; + const userAgent = navigator.userAgent; + const isChrome = (userAgent.indexOf('Chrome') >= 0); const textEncoder = new TextEncoder(); const textDecoder = new TextDecoder(); @@ -484,9 +485,9 @@ async function webviewPreloads(ctx: PreloadContext) { deltaY: event.deltaY, deltaZ: event.deltaZ, // Refs https://github.com/microsoft/vscode/issues/146403#issuecomment-1854538928 - wheelDelta: event.wheelDelta && browser.isChrome ? (event.wheelDelta / $window.devicePixelRatio) : event.wheelDelta, - wheelDeltaX: event.wheelDeltaX && browser.isChrome ? (event.wheelDeltaX / $window.devicePixelRatio) : event.wheelDeltaX, - wheelDeltaY: event.wheelDeltaY && browser.isChrome ? (event.wheelDeltaY / $window.devicePixelRatio) : event.wheelDeltaY, + wheelDelta: event.wheelDelta && isChrome ? (event.wheelDelta / $window.devicePixelRatio) : event.wheelDelta, + wheelDeltaX: event.wheelDeltaX && isChrome ? (event.wheelDeltaX / $window.devicePixelRatio) : event.wheelDeltaX, + wheelDeltaY: event.wheelDeltaY && isChrome ? (event.wheelDeltaY / $window.devicePixelRatio) : event.wheelDeltaY, detail: event.detail, shiftKey: event.shiftKey, type: event.type From 7b3a15c679ad4b00d0a844ac1c9bfbba3a608c42 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 16 Jan 2024 10:45:36 -0800 Subject: [PATCH 034/333] Support rendering cell index in resource label (#202554) * Support rendering cell index in resource label * localize the title --- src/vs/workbench/browser/labels.ts | 20 +++- .../browser/services/notebookServiceImpl.ts | 15 ++- .../contrib/notebook/common/notebookCommon.ts | 37 +------- .../common/notebookDocumentService.ts | 91 +++++++++++++++++++ src/vs/workbench/workbench.common.main.ts | 1 + 5 files changed, 128 insertions(+), 36 deletions(-) create mode 100644 src/vs/workbench/services/notebook/common/notebookDocumentService.ts diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 74fc9277f80f0..0bb8699861a56 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { dirname, isEqual, basenameOrAuthority } from 'vs/base/common/resources'; import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; @@ -24,6 +25,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { normalizeDriveLetter } from 'vs/base/common/labels'; import { IRange } from 'vs/editor/common/core/range'; import { ThemeIcon } from 'vs/base/common/themables'; +import { INotebookDocumentService } from 'vs/workbench/services/notebook/common/notebookDocumentService'; export interface IResourceLabelProps { resource?: URI | { primary?: URI; secondary?: URI }; @@ -308,7 +310,8 @@ class ResourceLabelWidget extends IconLabel { @IDecorationsService private readonly decorationsService: IDecorationsService, @ILabelService private readonly labelService: ILabelService, @ITextFileService private readonly textFileService: ITextFileService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @INotebookDocumentService private readonly notebookDocumentService: INotebookDocumentService ) { super(container, options); } @@ -465,6 +468,21 @@ class ResourceLabelWidget extends IconLabel { } } + if (!options.forceLabel && !isSideBySideEditor && resource?.scheme === Schemas.vscodeNotebookCell) { + // Notebook cells are embeded in a notebook document + // As such we always ask the actual notebook document + // for its position in the document. + const notebookDocument = this.notebookDocumentService.getNotebook(resource); + const cellIndex = notebookDocument?.getCellIndex(resource); + if (notebookDocument && cellIndex !== undefined && typeof label.name === 'string') { + options.title = localize('notebookCellLabel', "{0} • Cell {1}", label.name, `${cellIndex + 1}`); + } + + if (typeof label.name === 'string' && notebookDocument && cellIndex !== undefined && typeof label.name === 'string') { + label.name = localize('notebookCellLabel', "{0} • Cell {1}", label.name, `${cellIndex + 1}`); + } + } + const hasResourceChanged = this.hasResourceChanged(label); const hasPathLabelChanged = hasResourceChanged || this.hasPathLabelChanged(label); const hasFileKindChanged = this.hasFileKindChanged(options); diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts index 1e207678d7d2b..7c0c31448d4cd 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts @@ -14,6 +14,7 @@ import { Lazy } from 'vs/base/common/lazy'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; +import { isEqual } from 'vs/base/common/resources'; import { isDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -43,6 +44,7 @@ import { IExtensionService, isProposedApiEnabled } from 'vs/workbench/services/e import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { InstallRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { INotebookDocument, INotebookDocumentService } from 'vs/workbench/services/notebook/common/notebookDocumentService'; export class NotebookProviderInfoStore extends Disposable { @@ -414,8 +416,9 @@ export class NotebookOutputRendererInfoStore { } } -class ModelData implements IDisposable { +class ModelData implements IDisposable, INotebookDocument { private readonly _modelEventListeners = new DisposableStore(); + get uri() { return this.model.uri; } constructor( readonly model: NotebookTextModel, @@ -424,6 +427,10 @@ class ModelData implements IDisposable { this._modelEventListeners.add(model.onWillDispose(() => onWillDispose(model))); } + getCellIndex(cellUri: URI): number | undefined { + return this.model.cells.findIndex(cell => isEqual(cell.uri, cellUri)); + } + dispose(): void { this._modelEventListeners.dispose(); } @@ -485,6 +492,7 @@ export class NotebookService extends Disposable implements INotebookService { @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, @IStorageService private readonly _storageService: IStorageService, + @INotebookDocumentService private readonly _notebookDocumentService: INotebookDocumentService ) { super(); @@ -752,7 +760,9 @@ export class NotebookService extends Disposable implements INotebookService { throw new Error(`notebook for ${uri} already exists`); } const notebookModel = this._instantiationService.createInstance(NotebookTextModel, viewType, uri, data.cells, data.metadata, transientOptions); - this._models.set(uri, new ModelData(notebookModel, this._onWillDisposeDocument.bind(this))); + const modelData = new ModelData(notebookModel, this._onWillDisposeDocument.bind(this)); + this._models.set(uri, modelData); + this._notebookDocumentService.addNotebookDocument(modelData); this._onWillAddNotebookDocument.fire(notebookModel); this._onDidAddNotebookDocument.fire(notebookModel); this._postDocumentOpenActivation(viewType); @@ -776,6 +786,7 @@ export class NotebookService extends Disposable implements INotebookService { if (modelData) { this._onWillRemoveNotebookDocument.fire(modelData.model); this._models.delete(model.uri); + this._notebookDocumentService.removeNotebookDocument(modelData); modelData.dispose(); this._onDidRemoveNotebookDocument.fire(modelData.model); } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index e076f6ebf7edc..a4d44cdf61e3a 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { decodeBase64, encodeBase64, VSBuffer } from 'vs/base/common/buffer'; +import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IDiffResult } from 'vs/base/common/diff/diff'; import { Event } from 'vs/base/common/event'; @@ -31,6 +31,7 @@ import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { IWorkingCopyBackupMeta, IWorkingCopySaveEvent } from 'vs/workbench/services/workingCopy/common/workingCopy'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IFileReadLimits } from 'vs/platform/files/common/files'; +import { parse as parseUri, generate as generateUri } from 'vs/workbench/services/notebook/common/notebookDocumentService'; export const NOTEBOOK_EDITOR_ID = 'workbench.editor.notebook'; export const NOTEBOOK_DIFF_EDITOR_ID = 'workbench.editor.notebookTextDiffEditor'; @@ -546,43 +547,13 @@ export interface INotebookContributionData { export namespace CellUri { - export const scheme = Schemas.vscodeNotebookCell; - - - const _lengths = ['W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f']; - const _padRegexp = new RegExp(`^[${_lengths.join('')}]+`); - const _radix = 7; - export function generate(notebook: URI, handle: number): URI { - - const s = handle.toString(_radix); - const p = s.length < _lengths.length ? _lengths[s.length - 1] : 'z'; - - const fragment = `${p}${s}s${encodeBase64(VSBuffer.fromString(notebook.scheme), true, true)}`; - return notebook.with({ scheme, fragment }); + return generateUri(notebook, handle); } export function parse(cell: URI): { notebook: URI; handle: number } | undefined { - if (cell.scheme !== scheme) { - return undefined; - } - - const idx = cell.fragment.indexOf('s'); - if (idx < 0) { - return undefined; - } - - const handle = parseInt(cell.fragment.substring(0, idx).replace(_padRegexp, ''), _radix); - const _scheme = decodeBase64(cell.fragment.substring(idx + 1)).toString(); - - if (isNaN(handle)) { - return undefined; - } - return { - handle, - notebook: cell.with({ scheme: _scheme, fragment: null }) - }; + return parseUri(cell); } export function generateCellOutputUri(notebook: URI, outputId?: string) { diff --git a/src/vs/workbench/services/notebook/common/notebookDocumentService.ts b/src/vs/workbench/services/notebook/common/notebookDocumentService.ts new file mode 100644 index 0000000000000..96af748ef5097 --- /dev/null +++ b/src/vs/workbench/services/notebook/common/notebookDocumentService.ts @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer, decodeBase64, encodeBase64 } from 'vs/base/common/buffer'; +import { ResourceMap } from 'vs/base/common/map'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const INotebookDocumentService = createDecorator('notebookDocumentService'); + +export interface INotebookDocument { + readonly uri: URI; + getCellIndex(cellUri: URI): number | undefined; +} + +const _lengths = ['W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f']; +const _padRegexp = new RegExp(`^[${_lengths.join('')}]+`); +const _radix = 7; +export function parse(cell: URI): { notebook: URI; handle: number } | undefined { + if (cell.scheme !== Schemas.vscodeNotebookCell) { + return undefined; + } + + const idx = cell.fragment.indexOf('s'); + if (idx < 0) { + return undefined; + } + + const handle = parseInt(cell.fragment.substring(0, idx).replace(_padRegexp, ''), _radix); + const _scheme = decodeBase64(cell.fragment.substring(idx + 1)).toString(); + + if (isNaN(handle)) { + return undefined; + } + return { + handle, + notebook: cell.with({ scheme: _scheme, fragment: null }) + }; +} + +export function generate(notebook: URI, handle: number): URI { + + const s = handle.toString(_radix); + const p = s.length < _lengths.length ? _lengths[s.length - 1] : 'z'; + + const fragment = `${p}${s}s${encodeBase64(VSBuffer.fromString(notebook.scheme), true, true)}`; + return notebook.with({ scheme: Schemas.vscodeNotebookCell, fragment }); +} + +export interface INotebookDocumentService { + readonly _serviceBrand: undefined; + + getNotebook(uri: URI): INotebookDocument | undefined; + addNotebookDocument(document: INotebookDocument): void; + removeNotebookDocument(document: INotebookDocument): void; +} + +export class NotebookDocumentWorkbenchService implements INotebookDocumentService { + declare readonly _serviceBrand: undefined; + + private readonly _documents = new ResourceMap(); + + getNotebook(uri: URI): INotebookDocument | undefined { + if (uri.scheme === Schemas.vscodeNotebookCell) { + const cellUri = parse(uri); + if (cellUri) { + const document = this._documents.get(cellUri.notebook); + if (document) { + return document; + } + } + } + + return this._documents.get(uri); + } + + addNotebookDocument(document: INotebookDocument) { + this._documents.set(document.uri, document); + } + + removeNotebookDocument(document: INotebookDocument) { + this._documents.delete(document.uri); + } + +} + +registerSingleton(INotebookDocumentService, NotebookDocumentWorkbenchService, InstantiationType.Delayed); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index bcad16fc8025b..09cd28cf86adc 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -77,6 +77,7 @@ import 'vs/workbench/services/textresourceProperties/common/textResourceProperti import 'vs/workbench/services/textfile/common/textEditorService'; import 'vs/workbench/services/language/common/languageService'; import 'vs/workbench/services/model/common/modelService'; +import 'vs/workbench/services/notebook/common/notebookDocumentService'; import 'vs/workbench/services/commands/common/commandService'; import 'vs/workbench/services/themes/browser/workbenchThemeService'; import 'vs/workbench/services/label/common/labelService'; From 70575102e789216a1e4f5c305d40a14650a1a2b1 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 16 Jan 2024 11:31:15 -0800 Subject: [PATCH 035/333] move to terminal land --- .../electron-sandbox/voice.contribution.ts | 3 -- .../terminal/browser/terminalActions.ts | 33 +++++++++++- .../terminal/browser/voiceTerminalActions.ts | 53 +------------------ .../contrib/terminal/common/terminal.ts | 2 + 4 files changed, 36 insertions(+), 55 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/voice.contribution.ts b/src/vs/workbench/contrib/chat/electron-sandbox/voice.contribution.ts index 89f8f793d4836..9ba5430737e4a 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/voice.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/voice.contribution.ts @@ -5,7 +5,6 @@ import { InlineVoiceChatAction, QuickVoiceChatAction, StartVoiceChatAction, StopListeningInInlineChatAction, StopListeningInQuickChatAction, StopListeningInChatEditorAction, StopListeningInChatViewAction, VoiceChatInChatViewAction, StopListeningAction, StopListeningAndSubmitAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; import { registerAction2 } from 'vs/platform/actions/common/actions'; -import { StartTerminalSpeechToTextAction, StopTerminalSpeechToTextAction } from 'vs/workbench/contrib/terminal/browser/voiceTerminalActions'; registerAction2(StartVoiceChatAction); @@ -21,5 +20,3 @@ registerAction2(StopListeningInChatEditorAction); registerAction2(StopListeningInQuickChatAction); registerAction2(StopListeningInInlineChatAction); -registerAction2(StartTerminalSpeechToTextAction); -registerAction2(StopTerminalSpeechToTextAction); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 3517feca516ab..5e12619c2f1f5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -20,7 +20,7 @@ import { Action2, registerAction2, IAction2Options, MenuId } from 'vs/platform/a import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILabelService } from 'vs/platform/label/common/label'; import { IListService } from 'vs/platform/list/browser/listService'; @@ -64,6 +64,8 @@ import { AccessibleViewProviderId, accessibleViewCurrentProviderId, accessibleVi import { isKeyboardEvent, isMouseEvent, isPointerEvent } from 'vs/base/browser/dom'; import { editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { InstanceContext } from 'vs/workbench/contrib/terminal/browser/terminalContextMenu'; +import { TerminalVoiceSession } from 'vs/workbench/contrib/terminal/browser/voiceTerminalActions'; +import { HasSpeechProvider } from 'vs/workbench/contrib/speech/common/speechService'; export const switchTerminalActionViewItemSeparator = '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500'; export const switchTerminalShowTabsTitle = localize('showTerminalTabs', "Show Tabs"); @@ -1641,6 +1643,35 @@ export function registerTerminalActions() { } } }); + + + registerTerminalAction({ + id: TerminalCommandId.StartSpeechToText, + title: { + value: localize('workbench.action.startTerminalSpeechToText', "Start Terminal Speech To Text"), + original: 'Start Terminal Speech To Text' + }, + precondition: ContextKeyExpr.and(HasSpeechProvider, TerminalContextKeys.focus), + f1: true, + run: (c, accessor) => { + const instantiationService = accessor.get(IInstantiationService); + TerminalVoiceSession.getInstance(instantiationService).start(); + } + }); + + registerTerminalAction({ + id: TerminalCommandId.StopSpeechToText, + title: { + value: localize('workbench.action.stopTerminalSpeechToText', "Stop Terminal Speech To Text"), + original: 'Stop Terminal Speech To Text' + }, + precondition: ContextKeyExpr.and(HasSpeechProvider, TerminalContextKeys.focus), + f1: true, + run: (c, accessor) => { + const instantiationService = accessor.get(IInstantiationService); + TerminalVoiceSession.getInstance(instantiationService).stop(true); + } + }); } interface IRemoteTerminalPick extends IQuickPickItem { diff --git a/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts b/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts index e55f9c4f4cfc4..1ce2f03031759 100644 --- a/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts @@ -6,66 +6,17 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { localize } from 'vs/nls'; -import { Action2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { AccessibilityVoiceSettingId, SpeechTimeoutDefault } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; -import { HasSpeechProvider, ISpeechService, ISpeechToTextEvent, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; +import { ISpeechService, ISpeechToTextEvent, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { isNumber } from 'vs/base/common/types'; import type { IDecoration } from '@xterm/xterm'; import { IXtermMarker } from 'vs/platform/terminal/common/capabilities/capabilities'; import { ThemeIcon } from 'vs/base/common/themables'; import { Codicon } from 'vs/base/common/codicons'; -export class StartTerminalSpeechToTextAction extends Action2 { - - static readonly ID = 'workbench.action.startTerminalSpeechToText'; - - constructor() { - super({ - id: 'workbench.action.startTerminalSpeechToText', - title: { - value: localize('workbench.action.startTerminalSpeechToText', "Start Terminal Speech To Text"), - original: 'Start Terminal Speech To Text' - }, - precondition: ContextKeyExpr.and(HasSpeechProvider, TerminalContextKeys.focus), - f1: true - }); - } - - async run(accessor: ServicesAccessor): Promise { - const instantiationService = accessor.get(IInstantiationService); - TerminalVoiceSession.getInstance(instantiationService).start(); - } -} - - -export class StopTerminalSpeechToTextAction extends Action2 { - - static readonly ID = 'workbench.action.stopTerminalSpeechToText'; - - constructor() { - super({ - id: 'workbench.action.stopTerminalSpeechToText', - title: { - value: localize('workbench.action.stopTerminalSpeechToText', "Stop Terminal Speech To Text"), - original: 'Stop Terminal Speech To Text' - }, - precondition: ContextKeyExpr.and(HasSpeechProvider, TerminalContextKeys.focus), - f1: true - }); - } - - async run(accessor: ServicesAccessor): Promise { - const instantiationService = accessor.get(IInstantiationService); - TerminalVoiceSession.getInstance(instantiationService).stop(true); - } -} - export class TerminalVoiceSession extends Disposable { private _input: string = ''; private _decoration: IDecoration | undefined; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 1063933ba9147..5ff4e94366420 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -495,6 +495,8 @@ export const enum TerminalCommandId { FocusHover = 'workbench.action.terminal.focusHover', ShowEnvironmentContributions = 'workbench.action.terminal.showEnvironmentContributions', ToggleStickyScroll = 'workbench.action.terminal.toggleStickyScroll', + StartSpeechToText = 'workbench.action.startTerminalSpeechToText', + StopSpeechToText = 'workbench.action.stopTerminalSpeechToText', // Developer commands From f58d7ff83df6c72084f83893b3ea4ec37fb225ec Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 16 Jan 2024 11:34:18 -0800 Subject: [PATCH 036/333] cli: fix GLIBC version requirement hardcoded in error message (#202605) Fixes #202082 --- cli/src/util/errors.rs | 2 +- cli/src/util/prereqs.rs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cli/src/util/errors.rs b/cli/src/util/errors.rs index 38d9b36f54bf5..03280d12f0af3 100644 --- a/cli/src/util/errors.rs +++ b/cli/src/util/errors.rs @@ -471,7 +471,7 @@ pub enum CodeError { #[error("platform not currently supported: {0}")] UnsupportedPlatform(String), - #[error("This machine not meet {name}'s prerequisites, expected either...: {bullets}")] + #[error("This machine does not meet {name}'s prerequisites, expected either...: {bullets}")] PrerequisitesFailed { name: &'static str, bullets: String }, #[error("failed to spawn process: {0:?}")] ProcessSpawnFailed(std::io::Error), diff --git a/cli/src/util/prereqs.rs b/cli/src/util/prereqs.rs index dfb68c48afe74..b0a9014cbcf61 100644 --- a/cli/src/util/prereqs.rs +++ b/cli/src/util/prereqs.rs @@ -141,8 +141,8 @@ async fn check_glibc_version() -> Result<(), String> { Ok(()) } else { Err(format!( - "find GLIBC >= 2.17 (but found {} instead) for GNU environments", - v + "find GLIBC >= {} (but found {} instead) for GNU environments", + *MIN_LDD_VERSION, v )) }; } @@ -201,7 +201,8 @@ fn check_for_sufficient_glibcxx_versions(contents: Vec) -> Result<(), String if !all_versions.iter().any(|v| &*MIN_CXX_VERSION >= v) { return Err(format!( - "find GLIBCXX >= 3.4.18 (but found {} instead) for GNU environments", + "find GLIBCXX >= {} (but found {} instead) for GNU environments", + *MIN_CXX_VERSION, all_versions .iter() .map(String::from) From 7efe326c490dcf85ea2ac8e41969647b3db4b6c3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 16 Jan 2024 11:34:21 -0800 Subject: [PATCH 037/333] better precondition, enable commands to appear in command pallette --- .../contrib/terminal/browser/terminalActions.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 5e12619c2f1f5..0f6d1344b7bf1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -1644,30 +1644,29 @@ export function registerTerminalActions() { } }); - - registerTerminalAction({ + registerActiveInstanceAction({ id: TerminalCommandId.StartSpeechToText, title: { value: localize('workbench.action.startTerminalSpeechToText', "Start Terminal Speech To Text"), original: 'Start Terminal Speech To Text' }, - precondition: ContextKeyExpr.and(HasSpeechProvider, TerminalContextKeys.focus), + precondition: ContextKeyExpr.and(HasSpeechProvider, sharedWhenClause.terminalAvailable), f1: true, - run: (c, accessor) => { + run: (activeInstance, c, accessor) => { const instantiationService = accessor.get(IInstantiationService); TerminalVoiceSession.getInstance(instantiationService).start(); } }); - registerTerminalAction({ + registerActiveInstanceAction({ id: TerminalCommandId.StopSpeechToText, title: { value: localize('workbench.action.stopTerminalSpeechToText', "Stop Terminal Speech To Text"), original: 'Stop Terminal Speech To Text' }, - precondition: ContextKeyExpr.and(HasSpeechProvider, TerminalContextKeys.focus), + precondition: ContextKeyExpr.and(HasSpeechProvider, sharedWhenClause.terminalAvailable), f1: true, - run: (c, accessor) => { + run: (activeInstance, c, accessor) => { const instantiationService = accessor.get(IInstantiationService); TerminalVoiceSession.getInstance(instantiationService).stop(true); } From 77894e0a522cfa1adc58faf1a008bf29012a284a Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 16 Jan 2024 11:35:51 -0800 Subject: [PATCH 038/333] rename --- .../contrib/terminal/browser/terminalActions.ts | 6 +++--- ...iceTerminalActions.ts => terminalSpeechToText.ts} | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) rename src/vs/workbench/contrib/terminal/browser/{voiceTerminalActions.ts => terminalSpeechToText.ts} (93%) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 0f6d1344b7bf1..8be2aa0dce69a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -64,7 +64,7 @@ import { AccessibleViewProviderId, accessibleViewCurrentProviderId, accessibleVi import { isKeyboardEvent, isMouseEvent, isPointerEvent } from 'vs/base/browser/dom'; import { editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { InstanceContext } from 'vs/workbench/contrib/terminal/browser/terminalContextMenu'; -import { TerminalVoiceSession } from 'vs/workbench/contrib/terminal/browser/voiceTerminalActions'; +import { TerminalSpeechToTextSession } from 'vs/workbench/contrib/terminal/browser/terminalSpeechToText'; import { HasSpeechProvider } from 'vs/workbench/contrib/speech/common/speechService'; export const switchTerminalActionViewItemSeparator = '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500'; @@ -1654,7 +1654,7 @@ export function registerTerminalActions() { f1: true, run: (activeInstance, c, accessor) => { const instantiationService = accessor.get(IInstantiationService); - TerminalVoiceSession.getInstance(instantiationService).start(); + TerminalSpeechToTextSession.getInstance(instantiationService).start(); } }); @@ -1668,7 +1668,7 @@ export function registerTerminalActions() { f1: true, run: (activeInstance, c, accessor) => { const instantiationService = accessor.get(IInstantiationService); - TerminalVoiceSession.getInstance(instantiationService).stop(true); + TerminalSpeechToTextSession.getInstance(instantiationService).stop(true); } }); } diff --git a/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts similarity index 93% rename from src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts rename to src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts index 1ce2f03031759..d0ea442707922 100644 --- a/src/vs/workbench/contrib/terminal/browser/voiceTerminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts @@ -17,18 +17,18 @@ import { IXtermMarker } from 'vs/platform/terminal/common/capabilities/capabilit import { ThemeIcon } from 'vs/base/common/themables'; import { Codicon } from 'vs/base/common/codicons'; -export class TerminalVoiceSession extends Disposable { +export class TerminalSpeechToTextSession extends Disposable { private _input: string = ''; private _decoration: IDecoration | undefined; private _marker: IXtermMarker | undefined; - private static _instance: TerminalVoiceSession | undefined = undefined; + private static _instance: TerminalSpeechToTextSession | undefined = undefined; private _acceptTranscriptionScheduler: RunOnceScheduler | undefined; - static getInstance(instantiationService: IInstantiationService): TerminalVoiceSession { - if (!TerminalVoiceSession._instance) { - TerminalVoiceSession._instance = instantiationService.createInstance(TerminalVoiceSession); + static getInstance(instantiationService: IInstantiationService): TerminalSpeechToTextSession { + if (!TerminalSpeechToTextSession._instance) { + TerminalSpeechToTextSession._instance = instantiationService.createInstance(TerminalSpeechToTextSession); } - return TerminalVoiceSession._instance; + return TerminalSpeechToTextSession._instance; } private _cancellationTokenSource: CancellationTokenSource | undefined; private _disposables = new DisposableStore(); From 7f400caa8401101b82bf31372671c6c5d83f54d4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 16 Jan 2024 11:38:20 -0800 Subject: [PATCH 039/333] add css class --- src/vs/workbench/contrib/terminal/browser/media/terminal.css | 4 ++++ .../contrib/terminal/browser/terminalSpeechToText.ts | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 488815cf2589b..abd6805fa4ad2 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -565,3 +565,7 @@ .monaco-workbench .xterm.terminal.hide { visibility: hidden; } + +.terminal-speech-to-text { + padding-left: 5px; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts index d0ea442707922..b3738bbc97e9e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts @@ -153,8 +153,7 @@ export class TerminalSpeechToTextSession extends Disposable { }); this._decoration?.onRender((e: HTMLElement) => { e.classList.add(...ThemeIcon.asClassNameArray(Codicon.mic)); - e.classList.add('quick-fix'); - e.style.paddingLeft = '5px'; + e.classList.add('terminal-speech-to-text'); }); } } From 3c4a74323442e8edd05dc3eac38594742b41a8bd Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 16 Jan 2024 11:40:29 -0800 Subject: [PATCH 040/333] Fix insert generated cell location (#202607) --- .../notebook/browser/view/cellParts/chat/cellChatActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions.ts index 7763a3ea06131..cf1d6488b0f9a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatActions.ts @@ -479,7 +479,7 @@ registerAction2(class extends NotebookAction { return undefined; } - const cell = firstArg.index === 0 ? undefined : notebookEditor.cellAt(firstArg.index); + const cell = firstArg.index <= 0 ? undefined : notebookEditor.cellAt(firstArg.index - 1); return { cell, @@ -496,7 +496,7 @@ registerAction2(class extends NotebookAction { const languageService = accessor.get(ILanguageService); newCell = insertCell(languageService, context.notebookEditor, 0, CellKind.Code, 'above', undefined, true); } else { - newCell = insertNewCell(accessor, context, CellKind.Code, 'above', true); + newCell = insertNewCell(accessor, context, CellKind.Code, 'below', true); } if (!newCell) { From 3d0c49ab4d9186684820746a239c2704dce4aa6b Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 16 Jan 2024 11:52:34 -0800 Subject: [PATCH 041/333] testing: use better icon for clear all results (#202608) --- src/vs/workbench/contrib/testing/browser/testExplorerActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts index b23a81c7b973e..e932cb450df8b 100644 --- a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts +++ b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts @@ -913,7 +913,7 @@ export class ClearTestResultsAction extends Action2 { id: TestCommandId.ClearTestResultsAction, title: localize2('testing.clearResults', 'Clear All Results'), category, - icon: Codicon.trash, + icon: Codicon.clearAll, menu: [{ id: MenuId.TestPeekTitle, }, { From 73178af4c28d9795e439da1dc0f333184291cbb3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 16 Jan 2024 12:28:15 -0800 Subject: [PATCH 042/333] add more punctuation to map --- .../contrib/terminal/browser/terminalSpeechToText.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts index b3738bbc97e9e..a7bc31c3b5d7c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts @@ -125,7 +125,11 @@ export class TerminalSpeechToTextSession extends Disposable { 'Dot': '.', 'dot': '.', 'Period': '.', - 'period': '.' + 'period': '.', + 'Quote': '\'', + 'quote': '\'', + 'double quote': '"', + 'Double quote': '"', }; for (const symbol in symbolMap) { From 959eac3b517117decd70a2873ee835705ea673b4 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 16 Jan 2024 14:19:39 -0800 Subject: [PATCH 043/333] Fix layout errors for hidden cells. (#202617) --- .../contrib/notebook/browser/notebookEditorWidget.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 4484d005a1cb9..1a0b859cb60f5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2251,6 +2251,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return; } + if (this._list.getViewIndex(cell) === undefined) { + // Cell can be hidden + return; + } + if (this._list.elementHeight(cell) === height) { return; } From 54e45bd049170054c906f722806b8e08eda153e7 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 16 Jan 2024 14:38:19 -0800 Subject: [PATCH 044/333] fix #199422 --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 3377ae546d0cf..18c3a6a92f447 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -5,7 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { AriaRole } from 'vs/base/browser/ui/aria/aria'; +import { AriaRole, alert } from 'vs/base/browser/ui/aria/aria'; import { Button } from 'vs/base/browser/ui/button/button'; import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; @@ -833,6 +833,11 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer Date: Tue, 16 Jan 2024 16:59:56 -0600 Subject: [PATCH 045/333] Fix partial result transfer in quick search (#201917) --- .../quickTextSearch/textSearchQuickAccess.ts | 44 ++- .../contrib/search/browser/searchModel.ts | 37 ++- .../search/browser/searchResultsView.ts | 9 +- .../contrib/search/browser/searchView.ts | 266 ++++++++++-------- 4 files changed, 203 insertions(+), 153 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts index 1f248e45437e3..171319ef68738 100644 --- a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -2,7 +2,7 @@ * 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 { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IMatch } from 'vs/base/common/filters'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ResourceSet } from 'vs/base/common/map'; @@ -22,12 +22,12 @@ import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspac import { IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; import { searchDetailsIcon, searchOpenInFileIcon, searchActivityBarIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; -import { FileMatch, Match, RenderableMatch, SearchModel, searchComparer } from 'vs/workbench/contrib/search/browser/searchModel'; +import { FileMatch, Match, RenderableMatch, SearchModel, SearchModelLocation, searchComparer } from 'vs/workbench/contrib/search/browser/searchModel'; import { SearchView, getEditorSelectionFromMatch } from 'vs/workbench/contrib/search/browser/searchView'; import { IWorkbenchSearchConfiguration, getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/services/search/common/queryBuilder'; -import { IPatternInfo, ITextQuery, VIEW_ID } from 'vs/workbench/services/search/common/search'; +import { IPatternInfo, ISearchComplete, ITextQuery, VIEW_ID } from 'vs/workbench/services/search/common/search'; export const TEXT_SEARCH_QUICK_ACCESS_PREFIX = '%'; @@ -45,6 +45,10 @@ const MAX_RESULTS_PER_FILE = 10; export class TextSearchQuickAccess extends PickerQuickAccessProvider { private queryBuilder: QueryBuilder; private searchModel: SearchModel; + private currentAsyncSearch: Promise = Promise.resolve({ + results: [], + messages: [] + }); private _getTextQueryBuilderOptions(charsPerLine: number): ITextQueryBuilderOptions { return { @@ -74,6 +78,7 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { - this.moveToSearchViewlet(this.searchModel, undefined); + this.moveToSearchViewlet(undefined); picker.hide(); }); disposables.add(super.provide(picker, token, runOptions)); @@ -137,6 +142,7 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { + this.currentAsyncSearch = result.asyncResults; await result.asyncResults; const syncResultURIs = new ResourceSet(result.syncResults.map(e => e.resource)); return this.searchModel.searchResult.matches().filter(e => !syncResultURIs.has(e.resource)); @@ -147,12 +153,15 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider | undefined = viewlet?.getControl(); if (currentElem) { @@ -181,7 +190,7 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { - this.moveToSearchViewlet(this.searchModel, matches[limit]); + this.moveToSearchViewlet(matches[limit]); } }); break; @@ -212,7 +221,7 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { - this.moveToSearchViewlet(this.searchModel, element); + this.moveToSearchViewlet(element); } }); break; @@ -243,7 +252,7 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { - this.moveToSearchViewlet(this.searchModel, element); + this.moveToSearchViewlet(element); return TriggerAction.CLOSE_PICKER; } }); @@ -270,11 +279,22 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider | Promise | FastAndSlowPicks> | FastAndSlowPicks | null { + const conditionalTokenCts = disposables.add(new CancellationTokenSource()); + + const searchModelAtTimeOfSearch = this.searchModel; + + disposables.add(token.onCancellationRequested(() => { + if (searchModelAtTimeOfSearch.location === SearchModelLocation.QUICK_ACCESS) { + // if the search model has not been imported to the panel, you can cancel + conditionalTokenCts.cancel(); + } + })); + if (contentPattern === '') { this.searchModel.searchResult.clear(); return []; } - const allMatches = this.doSearch(contentPattern, token); + const allMatches = this.doSearch(contentPattern, conditionalTokenCts.token); if (!allMatches) { return null; diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index c52d6d7a914d4..9ff6b1d1072f3 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -1580,6 +1580,7 @@ function createParentList(element: RenderableMatch): RenderableMatch[] { return parentArray; } + export class SearchResult extends Disposable { private _onChange = this._register(new PauseableEmitter({ @@ -1598,7 +1599,7 @@ export class SearchResult extends Disposable { private _onDidChangeModelListener: IDisposable | undefined; constructor( - public searchModel: SearchModel, + public readonly searchModel: SearchModel, @IReplaceService private readonly replaceService: IReplaceService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IModelService private readonly modelService: IModelService, @@ -1607,7 +1608,6 @@ export class SearchResult extends Disposable { ) { super(); this._rangeHighlightDecorations = this.instantiationService.createInstance(RangeHighlightDecorations); - this.modelService.getModels().forEach(model => this.onModelAdded(model)); this._register(this.modelService.onModelAdded(model => this.onModelAdded(model))); @@ -1936,6 +1936,11 @@ export class SearchResult extends Disposable { } } +export enum SearchModelLocation { + PANEL, + QUICK_ACCESS +} + export class SearchModel extends Disposable { private _searchResult: SearchResult; @@ -1957,7 +1962,7 @@ export class SearchModel extends Disposable { private currentCancelTokenSource: CancellationTokenSource | null = null; private searchCancelledForNewSearch: boolean = false; - private _searchResultChangedListener: IDisposable; + public location: SearchModelLocation = SearchModelLocation.PANEL; constructor( @ISearchService private readonly searchService: ISearchService, @@ -1969,7 +1974,7 @@ export class SearchModel extends Disposable { ) { super(); this._searchResult = this.instantiationService.createInstance(SearchResult, this); - this._searchResultChangedListener = this._register(this._searchResult.onChange((e) => this._onSearchResultChanged.fire(e))); + this._register(this._searchResult.onChange((e) => this._onSearchResultChanged.fire(e))); } isReplaceActive(): boolean { @@ -2008,15 +2013,6 @@ export class SearchModel extends Disposable { return this._searchResult; } - set searchResult(searchResult: SearchResult) { - this._searchResult.dispose(); - this._searchResultChangedListener.dispose(); - - this._searchResult = searchResult; - this._searchResult.searchModel = this; - this._searchResultChangedListener = this._register(this._searchResult.onChange((e) => this._onSearchResultChanged.fire(e))); - } - private doSearch(query: ITextQuery, progressEmitter: Emitter, searchQuery: ITextQuery, searchInstanceID: string, onProgress?: (result: ISearchProgressItem) => void, callerToken?: CancellationToken): { asyncResults: Promise; @@ -2145,7 +2141,7 @@ export class SearchModel extends Disposable { } } - private onSearchCompleted(completed: ISearchComplete | null, duration: number, searchInstanceID: string): ISearchComplete | null { + private onSearchCompleted(completed: ISearchComplete | undefined, duration: number, searchInstanceID: string): ISearchComplete | undefined { if (!this._searchQuery) { throw new Error('onSearchCompleted must be called after a search is started'); } @@ -2193,7 +2189,7 @@ export class SearchModel extends Disposable { this.onSearchCompleted( this.searchCancelledForNewSearch ? { exit: SearchCompletionExitCode.NewSearchStarted, results: [], messages: [] } - : null, + : undefined, duration, ''); this.searchCancelledForNewSearch = false; } @@ -2238,10 +2234,6 @@ export class SearchModel extends Disposable { super.dispose(); } - transferSearchResult(other: SearchModel): void { - other.searchResult = this._searchResult; - this._searchResult = this.instantiationService.createInstance(SearchResult, this); - } } export type FileMatchOrMatch = FileMatch | Match; @@ -2262,6 +2254,11 @@ export class SearchViewModelWorkbenchService implements ISearchViewModelWorkbenc } return this._searchModel; } + + set searchModel(searchModel: SearchModel) { + this._searchModel?.dispose(); + this._searchModel = searchModel; + } } export const ISearchViewModelWorkbenchService = createDecorator('searchViewModelWorkbenchService'); @@ -2269,7 +2266,7 @@ export const ISearchViewModelWorkbenchService = createDecorator, index: number, templateData: IMatchTemplate): void { const match = node.element; const preview = match.preview(); - const replace = this.searchModel.isReplaceActive() && - !!this.searchModel.replaceString && + const replace = this.searchView.model.isReplaceActive() && + !!this.searchView.model.replaceString && !(match instanceof MatchInNotebook && match.isReadonly()); templateData.before.textContent = preview.before; @@ -402,7 +401,7 @@ export class MatchRenderer extends Disposable implements ICompressibleTreeRender export class SearchAccessibilityProvider implements IListAccessibilityProvider { constructor( - private searchModel: SearchModel, + private searchView: SearchView, @ILabelService private readonly labelService: ILabelService ) { } @@ -427,7 +426,7 @@ export class SearchAccessibilityProvider implements IListAccessibilityProviderelement; - const searchModel: SearchModel = this.searchModel; + const searchModel: SearchModel = this.searchView.model; const replace = searchModel.isReplaceActive() && !!searchModel.replaceString; const matchString = match.getMatchString(); const range = match.range(); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 423e7aa80aff5..06ee843f9d3d4 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -15,7 +15,7 @@ import * as errors from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import * as env from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; @@ -70,7 +70,7 @@ import * as Constants from 'vs/workbench/contrib/search/common/constants'; import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { getOutOfWorkspaceEditorResources, SearchStateKey, SearchUIState } from 'vs/workbench/contrib/search/common/search'; import { ISearchHistoryService, ISearchHistoryValues, SearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService'; -import { FileMatch, FileMatchOrMatch, FolderMatch, FolderMatchWithResource, IChangeEvent, ISearchViewModelWorkbenchService, Match, MatchInNotebook, RenderableMatch, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; +import { FileMatch, FileMatchOrMatch, FolderMatch, FolderMatchWithResource, IChangeEvent, ISearchViewModelWorkbenchService, Match, MatchInNotebook, RenderableMatch, searchMatchComparer, SearchModel, SearchModelLocation, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; import { createEditorFromSearchResult } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IPreferencesService, ISettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; @@ -161,6 +161,8 @@ export class SearchView extends ViewPane { private _refreshResultsScheduler: RunOnceScheduler; + private _onSearchResultChangedDisposable: IDisposable | undefined; + constructor( options: IViewPaneOptions, @IFileService private readonly fileService: IFileService, @@ -254,7 +256,7 @@ export class SearchView extends ViewPane { this.toggleCollapseStateDelayer = this._register(new Delayer(100)); this.triggerQueryDelayer = this._register(new Delayer(0)); - this.treeAccessibilityProvider = this.instantiationService.createInstance(SearchAccessibilityProvider, this.viewModel); + this.treeAccessibilityProvider = this.instantiationService.createInstance(SearchAccessibilityProvider, this); this.isTreeLayoutViewVisible = this.viewletState['view.treeLayout'] ?? (this.searchConfig.defaultViewMode === ViewMode.Tree); this._refreshResultsScheduler = this._register(new RunOnceScheduler(this._updateResults.bind(this), 80)); @@ -315,6 +317,10 @@ export class SearchView extends ViewPane { return this.viewModel && this.viewModel.searchResult; } + get model(): SearchModel { + return this.viewModel; + } + private onDidChangeWorkbenchState(): void { if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.searchWithoutFolderMessageElement) { dom.hide(this.searchWithoutFolderMessageElement); @@ -333,13 +339,37 @@ export class SearchView extends ViewPane { this.pauseSearching = false; } - public async importSearchResult(searchModel: SearchModel): Promise { - // experimental: used by the quick access search to overwrite a search result - searchModel.transferSearchResult(this.viewModel); + public async replaceSearchModel(searchModel: SearchModel, asyncResults: Promise): Promise { + let progressComplete: () => void; + this.progressService.withProgress({ location: this.getProgressLocation(), delay: 0 }, _progress => { + return new Promise(resolve => progressComplete = resolve); + }); + + const slowTimer = setTimeout(() => { + this.state = SearchUIState.SlowSearch; + }, 2000); + + // remove old model and use the new searchModel + searchModel.location = SearchModelLocation.PANEL; + searchModel.replaceActive = this.viewModel.isReplaceActive(); + this._onSearchResultChangedDisposable?.dispose(); + this._onSearchResultChangedDisposable = this._register(searchModel.onSearchResultChanged((event) => this.onSearchResultsChanged(event))); + + // this call will also dispose of the old model + this.searchViewModelWorkbenchService.searchModel = searchModel; + this.viewModel = searchModel; this.onSearchResultsChanged(); this.refreshInputs(); + asyncResults.then((complete) => { + clearTimeout(slowTimer); + this.onSearchComplete(progressComplete, undefined, undefined, complete); + }, (e) => { + clearTimeout(slowTimer); + this.onSearchError(e, progressComplete, undefined, undefined); + }); + const collapseResults = this.searchConfig.collapseResults; if (collapseResults !== 'alwaysCollapse' && this.viewModel.searchResult.matches().length === 1) { const onlyMatch = this.viewModel.searchResult.matches()[0]; @@ -461,7 +491,7 @@ export class SearchView extends ViewPane { this.toggleQueryDetails(true, true, true); } - this._register(this.viewModel.onSearchResultChanged((event) => this.onSearchResultsChanged(event))); + this._onSearchResultChangedDisposable = this._register(this.viewModel.onSearchResultChanged((event) => this.onSearchResultsChanged(event))); this._register(this.onDidChangeBodyVisibility(visible => this.onVisibilityChanged(visible))); @@ -843,7 +873,7 @@ export class SearchView extends ViewPane { [ this._register(this.instantiationService.createInstance(FolderMatchRenderer, this, this.treeLabels)), this._register(this.instantiationService.createInstance(FileMatchRenderer, this, this.treeLabels)), - this._register(this.instantiationService.createInstance(MatchRenderer, this.viewModel, this)), + this._register(this.instantiationService.createInstance(MatchRenderer, this)), ], { identityProvider, @@ -867,7 +897,6 @@ export class SearchView extends ViewPane { this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); const updateHasSomeCollapsible = () => this.toggleCollapseStateDelayer.trigger(() => this.hasSomeCollapsibleResultKey.set(this.hasSomeCollapsible())); updateHasSomeCollapsible(); - this._register(this.viewModel.onSearchResultChanged(() => updateHasSomeCollapsible())); this._register(this.tree.onDidChangeCollapseState(() => updateHasSomeCollapsible())); this._register(Event.debounce(this.tree.onDidOpen, (last, event) => event, DEBOUNCE_DELAY, true)(options => { @@ -1564,138 +1593,137 @@ export class SearchView extends ViewPane { } } - private doSearch(query: ITextQuery, excludePatternText: string, includePatternText: string, triggeredOnType: boolean): Thenable { - let progressComplete: () => void; - this.progressService.withProgress({ location: this.getProgressLocation(), delay: triggeredOnType ? 300 : 0 }, _progress => { - return new Promise(resolve => progressComplete = resolve); - }); + private onSearchComplete(progressComplete: () => void, excludePatternText?: string, includePatternText?: string, completed?: ISearchComplete) { - this.searchWidget.searchInput?.clearMessage(); - this.state = SearchUIState.Searching; - this.showEmptyStage(); + this.state = SearchUIState.Idle; - const slowTimer = setTimeout(() => { - this.state = SearchUIState.SlowSearch; - }, 2000); - - const onComplete = (completed?: ISearchComplete) => { - clearTimeout(slowTimer); - this.state = SearchUIState.Idle; - - // Complete up to 100% as needed - progressComplete(); + // Complete up to 100% as needed + progressComplete(); - // Do final render, then expand if just 1 file with less than 50 matches - this.onSearchResultsChanged(); + // Do final render, then expand if just 1 file with less than 50 matches + this.onSearchResultsChanged(); - const collapseResults = this.searchConfig.collapseResults; - if (collapseResults !== 'alwaysCollapse' && this.viewModel.searchResult.matches().length === 1) { - const onlyMatch = this.viewModel.searchResult.matches()[0]; - if (onlyMatch.count() < 50) { - this.tree.expand(onlyMatch); - } + const collapseResults = this.searchConfig.collapseResults; + if (collapseResults !== 'alwaysCollapse' && this.viewModel.searchResult.matches().length === 1) { + const onlyMatch = this.viewModel.searchResult.matches()[0]; + if (onlyMatch.count() < 50) { + this.tree.expand(onlyMatch); } + } - this.viewModel.replaceString = this.searchWidget.getReplaceValue(); + this.viewModel.replaceString = this.searchWidget.getReplaceValue(); - const hasResults = !this.viewModel.searchResult.isEmpty(); - if (completed?.exit === SearchCompletionExitCode.NewSearchStarted) { - return; - } + const hasResults = !this.viewModel.searchResult.isEmpty(); + if (completed?.exit === SearchCompletionExitCode.NewSearchStarted) { + return; + } - if (!hasResults) { - const hasExcludes = !!excludePatternText; - const hasIncludes = !!includePatternText; - let message: string; - - if (!completed) { - message = SEARCH_CANCELLED_MESSAGE; - } else if (this.inputPatternIncludes.onlySearchInOpenEditors()) { - if (hasIncludes && hasExcludes) { - message = nls.localize('noOpenEditorResultsIncludesExcludes', "No results found in open editors matching '{0}' excluding '{1}' - ", includePatternText, excludePatternText); - } else if (hasIncludes) { - message = nls.localize('noOpenEditorResultsIncludes', "No results found in open editors matching '{0}' - ", includePatternText); - } else if (hasExcludes) { - message = nls.localize('noOpenEditorResultsExcludes', "No results found in open editors excluding '{0}' - ", excludePatternText); - } else { - message = nls.localize('noOpenEditorResultsFound', "No results found in open editors. Review your settings for configured exclusions and check your gitignore files - "); - } + if (!hasResults) { + const hasExcludes = !!excludePatternText; + const hasIncludes = !!includePatternText; + let message: string; + + if (!completed) { + message = SEARCH_CANCELLED_MESSAGE; + } else if (this.inputPatternIncludes.onlySearchInOpenEditors()) { + if (hasIncludes && hasExcludes) { + message = nls.localize('noOpenEditorResultsIncludesExcludes', "No results found in open editors matching '{0}' excluding '{1}' - ", includePatternText, excludePatternText); + } else if (hasIncludes) { + message = nls.localize('noOpenEditorResultsIncludes', "No results found in open editors matching '{0}' - ", includePatternText); + } else if (hasExcludes) { + message = nls.localize('noOpenEditorResultsExcludes', "No results found in open editors excluding '{0}' - ", excludePatternText); } else { - if (hasIncludes && hasExcludes) { - message = nls.localize('noResultsIncludesExcludes', "No results found in '{0}' excluding '{1}' - ", includePatternText, excludePatternText); - } else if (hasIncludes) { - message = nls.localize('noResultsIncludes', "No results found in '{0}' - ", includePatternText); - } else if (hasExcludes) { - message = nls.localize('noResultsExcludes', "No results found excluding '{0}' - ", excludePatternText); - } else { - message = nls.localize('noResultsFound', "No results found. Review your settings for configured exclusions and check your gitignore files - "); - } + message = nls.localize('noOpenEditorResultsFound', "No results found in open editors. Review your settings for configured exclusions and check your gitignore files - "); } - - // Indicate as status to ARIA - aria.status(message); - - const messageEl = this.clearMessage(); - dom.append(messageEl, message); - - if (!completed) { - const searchAgainButton = this.messageDisposables.add(new SearchLinkButton( - nls.localize('rerunSearch.message', "Search again"), - () => this.triggerQueryChange({ preserveFocus: false }))); - dom.append(messageEl, searchAgainButton.element); - } else if (hasIncludes || hasExcludes) { - const searchAgainButton = this.messageDisposables.add(new SearchLinkButton(nls.localize('rerunSearchInAll.message', "Search again in all files"), this.onSearchAgain.bind(this))); - dom.append(messageEl, searchAgainButton.element); + } else { + if (hasIncludes && hasExcludes) { + message = nls.localize('noResultsIncludesExcludes', "No results found in '{0}' excluding '{1}' - ", includePatternText, excludePatternText); + } else if (hasIncludes) { + message = nls.localize('noResultsIncludes', "No results found in '{0}' - ", includePatternText); + } else if (hasExcludes) { + message = nls.localize('noResultsExcludes', "No results found excluding '{0}' - ", excludePatternText); } else { - const openSettingsButton = this.messageDisposables.add(new SearchLinkButton(nls.localize('openSettings.message', "Open Settings"), this.onOpenSettings.bind(this))); - dom.append(messageEl, openSettingsButton.element); + message = nls.localize('noResultsFound', "No results found. Review your settings for configured exclusions and check your gitignore files - "); } + } - if (completed) { - dom.append(messageEl, $('span', undefined, ' - ')); + // Indicate as status to ARIA + aria.status(message); - const learnMoreButton = this.messageDisposables.add(new SearchLinkButton(nls.localize('openSettings.learnMore', "Learn More"), this.onLearnMore.bind(this))); - dom.append(messageEl, learnMoreButton.element); - } + const messageEl = this.clearMessage(); + dom.append(messageEl, message); - if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { - this.showSearchWithoutFolderMessage(); - } - this.reLayout(); + if (!completed) { + const searchAgainButton = this.messageDisposables.add(new SearchLinkButton( + nls.localize('rerunSearch.message', "Search again"), + () => this.triggerQueryChange({ preserveFocus: false }))); + dom.append(messageEl, searchAgainButton.element); + } else if (hasIncludes || hasExcludes) { + const searchAgainButton = this.messageDisposables.add(new SearchLinkButton(nls.localize('rerunSearchInAll.message', "Search again in all files"), this.onSearchAgain.bind(this))); + dom.append(messageEl, searchAgainButton.element); } else { - this.viewModel.searchResult.toggleHighlights(this.isVisible()); // show highlights - - // Indicate final search result count for ARIA - aria.status(nls.localize('ariaSearchResultsStatus', "Search returned {0} results in {1} files", this.viewModel.searchResult.count(), this.viewModel.searchResult.fileCount())); + const openSettingsButton = this.messageDisposables.add(new SearchLinkButton(nls.localize('openSettings.message', "Open Settings"), this.onOpenSettings.bind(this))); + dom.append(messageEl, openSettingsButton.element); } + if (completed) { + dom.append(messageEl, $('span', undefined, ' - ')); - if (completed && completed.limitHit) { - completed.messages.push({ type: TextSearchCompleteMessageType.Warning, text: nls.localize('searchMaxResultsWarning', "The result set only contains a subset of all matches. Be more specific in your search to narrow down the results.") }); + const learnMoreButton = this.messageDisposables.add(new SearchLinkButton(nls.localize('openSettings.learnMore', "Learn More"), this.onLearnMore.bind(this))); + dom.append(messageEl, learnMoreButton.element); } - if (completed && completed.messages) { - for (const message of completed.messages) { - this.addMessage(message); - } + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { + this.showSearchWithoutFolderMessage(); } - this.reLayout(); - }; + } else { + this.viewModel.searchResult.toggleHighlights(this.isVisible()); // show highlights - const onError = (e: any) => { - clearTimeout(slowTimer); - this.state = SearchUIState.Idle; - if (errors.isCancellationError(e)) { - return onComplete(undefined); - } else { - progressComplete(); - this.searchWidget.searchInput?.showMessage({ content: e.message, type: MessageType.ERROR }); - this.viewModel.searchResult.clear(); + // Indicate final search result count for ARIA + aria.status(nls.localize('ariaSearchResultsStatus', "Search returned {0} results in {1} files", this.viewModel.searchResult.count(), this.viewModel.searchResult.fileCount())); + } + + + if (completed && completed.limitHit) { + completed.messages.push({ type: TextSearchCompleteMessageType.Warning, text: nls.localize('searchMaxResultsWarning', "The result set only contains a subset of all matches. Be more specific in your search to narrow down the results.") }); + } - return Promise.resolve(); + if (completed && completed.messages) { + for (const message of completed.messages) { + this.addMessage(message); } - }; + } + + this.reLayout(); + } + + private onSearchError(e: any, progressComplete: () => void, excludePatternText?: string, includePatternText?: string, completed?: ISearchComplete) { + this.state = SearchUIState.Idle; + if (errors.isCancellationError(e)) { + return this.onSearchComplete(progressComplete, excludePatternText, includePatternText, completed); + } else { + progressComplete(); + this.searchWidget.searchInput?.showMessage({ content: e.message, type: MessageType.ERROR }); + this.viewModel.searchResult.clear(); + + return Promise.resolve(); + } + } + + private doSearch(query: ITextQuery, excludePatternText: string, includePatternText: string, triggeredOnType: boolean): Thenable { + let progressComplete: () => void; + this.progressService.withProgress({ location: this.getProgressLocation(), delay: triggeredOnType ? 300 : 0 }, _progress => { + return new Promise(resolve => progressComplete = resolve); + }); + + this.searchWidget.searchInput?.clearMessage(); + this.state = SearchUIState.Searching; + this.showEmptyStage(); + + const slowTimer = setTimeout(() => { + this.state = SearchUIState.SlowSearch; + }, 2000); this._visibleMatches = 0; @@ -1706,7 +1734,13 @@ export class SearchView extends ViewPane { this.tree.setSelection([]); this.tree.setFocus([]); const result = this.viewModel.search(query); - return result.asyncResults.then(onComplete, onError); + return result.asyncResults.then((complete) => { + clearTimeout(slowTimer); + this.onSearchComplete(progressComplete, excludePatternText, includePatternText, complete); + }, (e) => { + clearTimeout(slowTimer); + this.onSearchError(e, progressComplete, excludePatternText, includePatternText); + }); } private onOpenSettings(e: dom.EventLike): void { From f00ee84937a12ca9e0c2c4492ca38d206d0f29aa Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Tue, 16 Jan 2024 18:21:44 -0600 Subject: [PATCH 046/333] add message for blank picker in quick search (#202625) add message for blank picker --- .../quickTextSearch/textSearchQuickAccess.ts | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts index 171319ef68738..6a43cf2532c6a 100644 --- a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -94,7 +94,11 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { - this.moveToSearchViewlet(undefined); + if (this.searchModel.searchResult.count() > 0) { + this.moveToSearchViewlet(undefined); + } else { + this._viewsService.openView(VIEW_ID, true); + } picker.hide(); }); disposables.add(super.provide(picker, token, runOptions)); @@ -279,9 +283,16 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider | Promise | FastAndSlowPicks> | FastAndSlowPicks | null { - const conditionalTokenCts = disposables.add(new CancellationTokenSource()); - const searchModelAtTimeOfSearch = this.searchModel; + if (contentPattern === '') { + + this.searchModel.searchResult.clear(); + return [{ + label: localize('enterSearchTerm', "Enter a term to search for across your files.") + }]; + } + + const conditionalTokenCts = disposables.add(new CancellationTokenSource()); disposables.add(token.onCancellationRequested(() => { if (searchModelAtTimeOfSearch.location === SearchModelLocation.QUICK_ACCESS) { @@ -289,11 +300,6 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider Date: Tue, 16 Jan 2024 18:42:09 -0600 Subject: [PATCH 047/333] show gradual updates when moving search result to search panel (#202626) --- src/vs/workbench/contrib/search/browser/searchView.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 06ee843f9d3d4..8286ef92bd29e 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -349,6 +349,8 @@ export class SearchView extends ViewPane { this.state = SearchUIState.SlowSearch; }, 2000); + this._refreshResultsScheduler.schedule(); + // remove old model and use the new searchModel searchModel.location = SearchModelLocation.PANEL; searchModel.replaceActive = this.viewModel.isReplaceActive(); From c6f507deeb99925e713271b1048f21dbaab4bd54 Mon Sep 17 00:00:00 2001 From: Bryan Ricker <978899+bricker@users.noreply.github.com> Date: Tue, 16 Jan 2024 18:15:52 -0800 Subject: [PATCH 048/333] doc typo fix (#202429) --- src/vscode-dts/vscode.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 36f2cd8ca2c60..5a7ea8aec469a 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -9598,7 +9598,7 @@ declare module 'vscode' { */ export interface WebviewViewProvider { /** - * Revolves a webview view. + * Resolves a webview view. * * `resolveWebviewView` is called when a view first becomes visible. This may happen when the view is * first loaded or when the user hides and then shows a view again. From 70348b0a63570b8496d6f111043c48aaa1b7c472 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 17 Jan 2024 11:21:37 +0530 Subject: [PATCH 049/333] fix #200010 (#202638) --- .../extensionRecommendationNotificationService.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts index e840bd9b331f2..d175546d78a1b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -41,7 +41,6 @@ type ExtensionWorkspaceRecommendationsNotificationClassification = { const ignoreImportantExtensionRecommendationStorageKey = 'extensionsAssistant/importantRecommendationsIgnore'; const donotShowWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; -const choiceNever = localize('neverShowAgain', "Don't Show Again"); type RecommendationsNotificationActions = { onDidInstallRecommendedExtensions(extensions: IExtension[]): void; @@ -258,14 +257,17 @@ export class ExtensionRecommendationNotificationService extends Disposable imple searchValue = source === RecommendationSource.WORKSPACE ? '@recommended' : extensions.map(extensionId => `@id:${extensionId.identifier.id}`).join(' '); } + const donotShowAgainLabel = source === RecommendationSource.WORKSPACE ? localize('donotShowAgain', "Don't Show Again for this Repository") + : extensions.length > 1 ? localize('donotShowAgainExtension', "Don't Show Again for these Extensions") : localize('donotShowAgainExtensionSingle', "Don't Show Again for this Extension"); + return raceCancellablePromises([ - this._registerP(this.showRecommendationsNotification(extensions, message, searchValue, source, recommendationsNotificationActions)), + this._registerP(this.showRecommendationsNotification(extensions, message, searchValue, donotShowAgainLabel, source, recommendationsNotificationActions)), this._registerP(this.waitUntilRecommendationsAreInstalled(extensions)) ]); } - private showRecommendationsNotification(extensions: IExtension[], message: string, searchValue: string, source: RecommendationSource, + private showRecommendationsNotification(extensions: IExtension[], message: string, searchValue: string, donotShowAgainLabel: string, source: RecommendationSource, { onDidInstallRecommendedExtensions, onDidShowRecommendedExtensions, onDidCancelRecommendedExtensions, onDidNeverShowRecommendedExtensionsAgain }: RecommendationsNotificationActions): CancelablePromise { return createCancelablePromise(async token => { let accepted = false; @@ -296,7 +298,7 @@ export class ExtensionRecommendationNotificationService extends Disposable imple this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); } }, { - label: choiceNever, + label: donotShowAgainLabel, isSecondary: true, run: () => { onDidNeverShowRecommendedExtensionsAgain(extensions); From aed5f4d65d2dbfda2c98abd7eb371751016838ab Mon Sep 17 00:00:00 2001 From: Kim Reenberg Date: Wed, 17 Jan 2024 07:29:01 +0100 Subject: [PATCH 050/333] fix: fallback to strings for non-executable libc.so.6 (#202581) * fix: fallback to strings for non-executable libc.so.6 * chore: use cat over strings --------- Co-authored-by: khreenberg Co-authored-by: Robo --- resources/server/bin/helpers/check-requirements-linux.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/resources/server/bin/helpers/check-requirements-linux.sh b/resources/server/bin/helpers/check-requirements-linux.sh index 625b569602a87..c3d3d8ece6a89 100644 --- a/resources/server/bin/helpers/check-requirements-linux.sh +++ b/resources/server/bin/helpers/check-requirements-linux.sh @@ -81,7 +81,13 @@ if [ -n "$(ldd --version | grep -v musl)" ]; then # Rather than trusting the output of ldd --version (which is not always accurate) # we instead use the version of the cached libc.so.6 file itself. libc_real_path=$(readlink -f "$libc_path") - libc_version=$($libc_real_path --version | sed -n 's/.*stable release version \([0-9]\+\.[0-9]\+\).*/\1/p') + if [ -x "$libc_real_path" ]; then + # get version from executable + libc_version=$($libc_real_path --version | sed -n 's/.*stable release version \([0-9]\+\.[0-9]\+\).*/\1/p') + else + # .so is not executable on this host; try getting from strings + libc_version=$(cat "$libc_real_path" | sed -n 's/.*stable release version \([0-9]\+\.[0-9]\+\).*/\1/p') + fi if [ "$(printf '%s\n' "2.28" "$libc_version" | sort -V | head -n1)" = "2.28" ]; then found_required_glibc=1 else From eb24c3f99c45bae0e5f4773b3e90a3c63a78940d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 17 Jan 2024 16:15:38 +0530 Subject: [PATCH 051/333] fix #199947 (#202654) --- .../browser/parts/views/viewPaneContainer.ts | 34 ++++++++++--------- .../contrib/debug/browser/debugViewlet.ts | 2 +- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 44b825998ab58..3021fe51eee7a 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -13,7 +13,7 @@ import { IAction } from 'vs/base/common/actions'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { combinedDisposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { combinedDisposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { assertIsDefined } from 'vs/base/common/types'; import 'vs/css!./media/paneviewlet'; import * as nls from 'vs/nls'; @@ -324,7 +324,6 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { private readonly visibleViewsCountFromCache: number | undefined; private readonly visibleViewsStorageId: string; protected readonly viewContainerModel: IViewContainerModel; - private viewDisposables: IDisposable[] = []; private readonly _onTitleAreaUpdate: Emitter = this._register(new Emitter()); readonly onTitleAreaUpdate: Event = this._onTitleAreaUpdate.event; @@ -394,7 +393,6 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { this.viewContainer = container; this.visibleViewsStorageId = `${id}.numberOfVisibleViews`; this.visibleViewsCountFromCache = this.storageService.getNumber(this.visibleViewsStorageId, StorageScope.WORKSPACE, undefined); - this._register(toDisposable(() => this.viewDisposables = dispose(this.viewDisposables))); this.viewContainerModel = this.viewDescriptorService.getViewContainerModel(container); } @@ -657,11 +655,11 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { return optimalWidth + additionalMargin; } - addPanes(panes: { pane: ViewPane; size: number; index?: number }[]): void { + addPanes(panes: { pane: ViewPane; size: number; index?: number; disposable: IDisposable }[]): void { const wasMerged = this.isViewMergedWithContainer(); - for (const { pane: pane, size, index } of panes) { - this.addPane(pane, size, index); + for (const { pane: pane, size, index, disposable } of panes) { + this.addPane(pane, size, disposable, index); } this.updateViewHeaders(); @@ -773,7 +771,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { } protected onDidAddViewDescriptors(added: IAddedViewDescriptorRef[]): ViewPane[] { - const panesToAdd: { pane: ViewPane; size: number; index: number }[] = []; + const panesToAdd: { pane: ViewPane; size: number; index: number; disposable: IDisposable }[] = []; for (const { viewDescriptor, collapsed, index, size } of added) { const pane = this.createView(viewDescriptor, @@ -795,8 +793,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { this.viewContainerModel.setCollapsed(viewDescriptor.id, collapsed); }); - this.viewDisposables.splice(index, 0, combinedDisposable(contextMenuDisposable, collapseDisposable)); - panesToAdd.push({ pane, size: size || pane.minimumSize, index }); + panesToAdd.push({ pane, size: size || pane.minimumSize, index, disposable: combinedDisposable(contextMenuDisposable, collapseDisposable) }); } this.addPanes(panesToAdd); @@ -814,14 +811,18 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { removed = removed.sort((a, b) => b.index - a.index); const panesToRemove: ViewPane[] = []; for (const { index } of removed) { - const [disposable] = this.viewDisposables.splice(index, 1); - disposable.dispose(); - panesToRemove.push(this.panes[index]); + const paneItem = this.paneItems[index]; + if (paneItem) { + panesToRemove.push(this.paneItems[index].pane); + } } - this.removePanes(panesToRemove); - for (const pane of panesToRemove) { - pane.setVisible(false); + if (panesToRemove.length) { + this.removePanes(panesToRemove); + + for (const pane of panesToRemove) { + pane.setVisible(false); + } } } @@ -833,7 +834,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { } } - private addPane(pane: ViewPane, size: number, index = this.paneItems.length - 1): void { + private addPane(pane: ViewPane, size: number, disposable: IDisposable, index = this.paneItems.length - 1): void { const onDidFocus = pane.onDidFocus(() => { this._onDidFocusView.fire(pane); this.lastFocusedPane = pane; @@ -862,6 +863,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { }); const store = new DisposableStore(); + store.add(disposable); store.add(combinedDisposable(pane, onDidFocus, onDidBlur, onDidChangeTitleArea, onDidChange, onDidChangeVisibility)); const paneItem: IViewPaneItem = { pane, disposable: store }; diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index 4a8f0e2dc99e8..24c202b7c346f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -133,7 +133,7 @@ export class DebugViewPaneContainer extends ViewPaneContainer { } } - override addPanes(panes: { pane: ViewPane; size: number; index?: number }[]): void { + override addPanes(panes: { pane: ViewPane; size: number; index?: number; disposable: IDisposable }[]): void { super.addPanes(panes); for (const { pane: pane } of panes) { From 695ed7fb5f0b95a04e14cc6d053e8a7a9c27a2de Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 17 Jan 2024 16:38:57 +0530 Subject: [PATCH 052/333] fix #189331 (#202658) --- src/vs/workbench/contrib/markers/browser/markersView.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 82c27df0f16be..b3a9bd0dab1a7 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -1025,8 +1025,10 @@ class MarkersTree extends WorkbenchObjectTree impleme update(resourceMarkers: ResourceMarkers[]): void { for (const resourceMarker of resourceMarkers) { - this.setChildren(resourceMarker, createResourceMarkersIterator(resourceMarker)); - this.rerender(resourceMarker); + if (this.hasElement(resourceMarker)) { + this.setChildren(resourceMarker, createResourceMarkersIterator(resourceMarker)); + this.rerender(resourceMarker); + } } } From efc4fc231c384d35fb77224d77b714a82e27f02a Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 17 Jan 2024 12:00:41 +0100 Subject: [PATCH 053/333] Uses assertFn instead of BugIndicatingError --- src/vs/base/common/observableInternal/derived.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/base/common/observableInternal/derived.ts b/src/vs/base/common/observableInternal/derived.ts index 83abf2444d35b..573b5ad5258ca 100644 --- a/src/vs/base/common/observableInternal/derived.ts +++ b/src/vs/base/common/observableInternal/derived.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BugIndicatingError } from 'vs/base/common/errors'; -import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IReader, IObservable, BaseObservable, IObserver, _setDerivedOpts, IChangeContext, getFunctionName, DebugNameFn, getDebugName, Owner } from 'vs/base/common/observableInternal/base'; +import { assertFn } from 'vs/base/common/assert'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { BaseObservable, DebugNameFn, IChangeContext, IObservable, IObserver, IReader, Owner, _setDerivedOpts, getDebugName, getFunctionName } from 'vs/base/common/observableInternal/base'; import { getLogger } from 'vs/base/common/observableInternal/logging'; export type EqualityComparer = (a: T, b: T) => boolean; @@ -300,9 +300,7 @@ export class Derived extends BaseObservable im r.endUpdate(this); } } - if (this.updateCount < 0) { - throw new BugIndicatingError(); - } + assertFn(() => this.updateCount >= 0); } public handlePossibleChange(observable: IObservable): void { From 7fd2edcf4696f1707be45d38c5bfc1b14dc47f41 Mon Sep 17 00:00:00 2001 From: Robo Date: Wed, 17 Jan 2024 20:18:31 +0900 Subject: [PATCH 054/333] fix: ensure requirements check works for libc development versions (#202660) --- resources/server/bin/helpers/check-requirements-linux.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/server/bin/helpers/check-requirements-linux.sh b/resources/server/bin/helpers/check-requirements-linux.sh index c3d3d8ece6a89..2322093018964 100644 --- a/resources/server/bin/helpers/check-requirements-linux.sh +++ b/resources/server/bin/helpers/check-requirements-linux.sh @@ -83,10 +83,10 @@ if [ -n "$(ldd --version | grep -v musl)" ]; then libc_real_path=$(readlink -f "$libc_path") if [ -x "$libc_real_path" ]; then # get version from executable - libc_version=$($libc_real_path --version | sed -n 's/.*stable release version \([0-9]\+\.[0-9]\+\).*/\1/p') + libc_version=$($libc_real_path --version | sed -n 's/.*release version \([0-9]\+\.[0-9]\+\).*/\1/p') else # .so is not executable on this host; try getting from strings - libc_version=$(cat "$libc_real_path" | sed -n 's/.*stable release version \([0-9]\+\.[0-9]\+\).*/\1/p') + libc_version=$(cat "$libc_real_path" | sed -n 's/.*release version \([0-9]\+\.[0-9]\+\).*/\1/p') fi if [ "$(printf '%s\n' "2.28" "$libc_version" | sort -V | head -n1)" = "2.28" ]; then found_required_glibc=1 From bf53fde347bbcf0c1e90d15255d925c93675ab10 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 17 Jan 2024 12:30:40 +0100 Subject: [PATCH 055/333] move browser things back so that services and dependents are registered properly --- .../browser/inlineChat.contribution.ts | 64 +++++++++++++++++++ .../inlineChat.contribution.ts | 64 ++----------------- src/vs/workbench/workbench.common.main.ts | 1 + 3 files changed, 71 insertions(+), 58 deletions(-) create mode 100644 src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts new file mode 100644 index 0000000000000..0d9c8cce7bef9 --- /dev/null +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { registerAction2 } from 'vs/platform/actions/common/actions'; +import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; +import * as InlineChatActions from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions'; +import { IInlineChatService, INLINE_CHAT_ID, INTERACTIVE_EDITOR_ACCESSIBILITY_HELP_ID } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { InlineChatServiceImpl } from 'vs/workbench/contrib/inlineChat/common/inlineChatServiceImpl'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { InlineChatNotebookContribution } from 'vs/workbench/contrib/inlineChat/browser/inlineChatNotebook'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { InlineChatSavingServiceImpl } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl'; +import { InlineChatAccessibleViewContribution } from 'vs/workbench/contrib/inlineChat/browser/inlineChatAccessibleView'; +import { IInlineChatSavingService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSavingService'; +import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; +import { InlineChatSessionServiceImpl } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl'; + + +// --- browser + +registerSingleton(IInlineChatService, InlineChatServiceImpl, InstantiationType.Delayed); +registerSingleton(IInlineChatSessionService, InlineChatSessionServiceImpl, InstantiationType.Delayed); +registerSingleton(IInlineChatSavingService, InlineChatSavingServiceImpl, InstantiationType.Delayed); + +registerEditorContribution(INLINE_CHAT_ID, InlineChatController, EditorContributionInstantiation.Eager); // EAGER because of notebook dispose/create of editors +registerEditorContribution(INTERACTIVE_EDITOR_ACCESSIBILITY_HELP_ID, InlineChatActions.InlineAccessibilityHelpContribution, EditorContributionInstantiation.Eventually); + +registerAction2(InlineChatActions.CloseAction); +registerAction2(InlineChatActions.ConfigureInlineChatAction); +// registerAction2(InlineChatActions.UnstashSessionAction); +registerAction2(InlineChatActions.MakeRequestAction); +registerAction2(InlineChatActions.StopRequestAction); +registerAction2(InlineChatActions.ReRunRequestAction); +registerAction2(InlineChatActions.DiscardHunkAction); +registerAction2(InlineChatActions.DiscardAction); +registerAction2(InlineChatActions.DiscardToClipboardAction); +registerAction2(InlineChatActions.DiscardUndoToNewFileAction); +registerAction2(InlineChatActions.CancelSessionAction); + +registerAction2(InlineChatActions.ArrowOutUpAction); +registerAction2(InlineChatActions.ArrowOutDownAction); +registerAction2(InlineChatActions.FocusInlineChat); +registerAction2(InlineChatActions.PreviousFromHistory); +registerAction2(InlineChatActions.NextFromHistory); +registerAction2(InlineChatActions.ViewInChatAction); +registerAction2(InlineChatActions.ExpandMessageAction); +registerAction2(InlineChatActions.ContractMessageAction); + +registerAction2(InlineChatActions.ToggleDiffForChange); +registerAction2(InlineChatActions.FeebackHelpfulCommand); +registerAction2(InlineChatActions.FeebackUnhelpfulCommand); +registerAction2(InlineChatActions.ReportIssueForBugCommand); +registerAction2(InlineChatActions.AcceptChanges); + +registerAction2(InlineChatActions.CopyRecordings); + +const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchContributionsRegistry.registerWorkbenchContribution(InlineChatNotebookContribution, LifecyclePhase.Restored); +workbenchContributionsRegistry.registerWorkbenchContribution(InlineChatAccessibleViewContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts index 33942d1fea487..d5233317f5529 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts @@ -6,67 +6,15 @@ import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { registerAction2 } from 'vs/platform/actions/common/actions'; import { CancelAction, InlineChatQuickVoice, StartAction, StopAction } from 'vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice'; -import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; -import * as InlineChatActions from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions'; import * as StartSessionAction from './inlineChatActions'; -import { IInlineChatService, INLINE_CHAT_ID, INTERACTIVE_EDITOR_ACCESSIBILITY_HELP_ID } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; -import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { InlineChatServiceImpl } from 'vs/workbench/contrib/inlineChat/common/inlineChatServiceImpl'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { InlineChatNotebookContribution } from 'vs/workbench/contrib/inlineChat/browser/inlineChatNotebook'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { InlineChatSavingServiceImpl } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl'; -import { InlineChatAccessibleViewContribution } from 'vs/workbench/contrib/inlineChat/browser/inlineChatAccessibleView'; -import { IInlineChatSavingService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSavingService'; -import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; -import { InlineChatSessionServiceImpl } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl'; -// --- electron-browser -registerEditorContribution(InlineChatQuickVoice.ID, InlineChatQuickVoice, EditorContributionInstantiation.Eager); // EAGER because of notebook dispose/create of editors -registerAction2(StartAction); -registerAction2(StopAction); -registerAction2(CancelAction); - -// --- browser - -registerSingleton(IInlineChatService, InlineChatServiceImpl, InstantiationType.Delayed); -registerSingleton(IInlineChatSessionService, InlineChatSessionServiceImpl, InstantiationType.Delayed); -registerSingleton(IInlineChatSavingService, InlineChatSavingServiceImpl, InstantiationType.Delayed); - -registerEditorContribution(INLINE_CHAT_ID, InlineChatController, EditorContributionInstantiation.Eager); // EAGER because of notebook dispose/create of editors -registerEditorContribution(INTERACTIVE_EDITOR_ACCESSIBILITY_HELP_ID, InlineChatActions.InlineAccessibilityHelpContribution, EditorContributionInstantiation.Eventually); +// start and hold for voice registerAction2(StartSessionAction.StartSessionAction); -registerAction2(InlineChatActions.CloseAction); -registerAction2(InlineChatActions.ConfigureInlineChatAction); -// registerAction2(InlineChatActions.UnstashSessionAction); -registerAction2(InlineChatActions.MakeRequestAction); -registerAction2(InlineChatActions.StopRequestAction); -registerAction2(InlineChatActions.ReRunRequestAction); -registerAction2(InlineChatActions.DiscardHunkAction); -registerAction2(InlineChatActions.DiscardAction); -registerAction2(InlineChatActions.DiscardToClipboardAction); -registerAction2(InlineChatActions.DiscardUndoToNewFileAction); -registerAction2(InlineChatActions.CancelSessionAction); -registerAction2(InlineChatActions.ArrowOutUpAction); -registerAction2(InlineChatActions.ArrowOutDownAction); -registerAction2(InlineChatActions.FocusInlineChat); -registerAction2(InlineChatActions.PreviousFromHistory); -registerAction2(InlineChatActions.NextFromHistory); -registerAction2(InlineChatActions.ViewInChatAction); -registerAction2(InlineChatActions.ExpandMessageAction); -registerAction2(InlineChatActions.ContractMessageAction); +// quick voice -registerAction2(InlineChatActions.ToggleDiffForChange); -registerAction2(InlineChatActions.FeebackHelpfulCommand); -registerAction2(InlineChatActions.FeebackUnhelpfulCommand); -registerAction2(InlineChatActions.ReportIssueForBugCommand); -registerAction2(InlineChatActions.AcceptChanges); - -registerAction2(InlineChatActions.CopyRecordings); - -const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchContributionsRegistry.registerWorkbenchContribution(InlineChatNotebookContribution, LifecyclePhase.Restored); -workbenchContributionsRegistry.registerWorkbenchContribution(InlineChatAccessibleViewContribution, LifecyclePhase.Eventually); +registerEditorContribution(InlineChatQuickVoice.ID, InlineChatQuickVoice, EditorContributionInstantiation.Eager); // EAGER because of notebook dispose/create of editors +registerAction2(StartAction); +registerAction2(StopAction); +registerAction2(CancelAction); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index ae453cf83d470..bcad16fc8025b 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -182,6 +182,7 @@ import 'vs/workbench/contrib/speech/common/speech.contribution'; // Chat import 'vs/workbench/contrib/chat/browser/chat.contribution'; +import 'vs/workbench/contrib/inlineChat/browser/inlineChat.contribution'; // Interactive import 'vs/workbench/contrib/interactive/browser/interactive.contribution'; From 6ec5b10065ea3fb689585a303e218ed6e2374953 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 17 Jan 2024 17:04:24 +0530 Subject: [PATCH 056/333] fix #155495 (#202662) --- src/vs/base/browser/indexedDB.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/indexedDB.ts b/src/vs/base/browser/indexedDB.ts index 38be907918e66..6d56022c98cef 100644 --- a/src/vs/base/browser/indexedDB.ts +++ b/src/vs/base/browser/indexedDB.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { getErrorMessage } from 'vs/base/common/errors'; +import { ErrorNoTelemetry, getErrorMessage } from 'vs/base/common/errors'; import { mark } from 'vs/base/common/performance'; class MissingStoresError extends Error { @@ -125,8 +125,8 @@ export class IndexedDB { c(request.result); } }; - transaction.onerror = () => e(transaction.error); - transaction.onabort = () => e(transaction.error); + transaction.onerror = () => e(transaction.error ? ErrorNoTelemetry.fromError(transaction.error) : new ErrorNoTelemetry('unknown error')); + transaction.onabort = () => e(transaction.error ? ErrorNoTelemetry.fromError(transaction.error) : new ErrorNoTelemetry('unknown error')); const request = dbRequestFn(transaction.objectStore(store)); }).finally(() => this.pendingTransactions.splice(this.pendingTransactions.indexOf(transaction), 1)); } From a3f93ac6798cb48e24d5fd6e1981e0b9e99112e6 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 17 Jan 2024 13:02:02 +0100 Subject: [PATCH 057/333] checking the variable _rebuildFromLine is undefined before doing early return --- .../editor/contrib/stickyScroll/browser/stickyScrollWidget.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index f3b0974709c3d..72cec626e8690 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -127,7 +127,9 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } setState(_state: StickyScrollWidgetState | undefined, foldingModel: FoldingModel | null, _rebuildFromLine?: number): void { - if ((!this._previousState && !_state) || (this._previousState && this._previousState.equals(_state))) { + if (typeof _rebuildFromLine === 'undefined' && + ((!this._previousState && !_state) || (this._previousState && this._previousState.equals(_state))) + ) { return; } const isWidgetHeightZero = this._isWidgetHeightZero(_state); From 112b74e32b441b6a9c57a548107e3bb35142ad04 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 17 Jan 2024 13:09:19 +0100 Subject: [PATCH 058/333] checking that _rebuildFromLine is undefined in the early return --- .../editor/contrib/stickyScroll/browser/stickyScrollWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 72cec626e8690..06fb2ce428f5f 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -127,7 +127,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } setState(_state: StickyScrollWidgetState | undefined, foldingModel: FoldingModel | null, _rebuildFromLine?: number): void { - if (typeof _rebuildFromLine === 'undefined' && + if (_rebuildFromLine === undefined && ((!this._previousState && !_state) || (this._previousState && this._previousState.equals(_state))) ) { return; From 99c4d2f6745d5673372831f0d911388a776b7324 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Wed, 17 Jan 2024 14:09:59 +0100 Subject: [PATCH 059/333] Updated layout settings, context menu service, and title bar configurations in various parts of the application. --- .../platform/menubar/electron-main/menubar.ts | 6 +- src/vs/platform/window/common/window.ts | 28 ++++----- .../windows/electron-main/windowImpl.ts | 4 +- .../platform/windows/electron-main/windows.ts | 4 +- .../browser/actions/layoutActions.ts | 2 +- src/vs/workbench/browser/layout.ts | 48 ++++++++++++--- .../parts/activitybar/activitybarPart.ts | 6 +- .../parts/editor/auxiliaryEditorPart.ts | 4 +- .../parts/editor/editor.contribution.ts | 4 +- .../browser/parts/titlebar/menubarControl.ts | 4 +- .../browser/parts/titlebar/titlebarActions.ts | 59 +++++++++++++++++-- .../browser/parts/titlebar/titlebarPart.ts | 20 +++---- .../browser/workbench.contribution.ts | 6 +- .../browser/relauncher.contribution.ts | 8 +-- .../electron-sandbox/desktop.contribution.ts | 11 +++- .../parts/titlebar/titlebarPart.ts | 10 ++-- src/vs/workbench/electron-sandbox/window.ts | 6 +- .../electron-sandbox/contextmenuService.ts | 4 +- .../services/layout/browser/layoutService.ts | 7 +++ 19 files changed, 160 insertions(+), 81 deletions(-) diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 6c9e3fcffda2b..ec33007b54419 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -22,7 +22,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IStateService } from 'vs/platform/state/node/state'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUpdateService, StateType } from 'vs/platform/update/common/update'; -import { INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, IWindowOpenable, showNativeTitlebar } from 'vs/platform/window/common/window'; +import { INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, IWindowOpenable, hasNativeTitlebar } from 'vs/platform/window/common/window'; import { IWindowsCountChangedEvent, IWindowsMainService, OpenContext } from 'vs/platform/windows/electron-main/windows'; import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService'; @@ -85,7 +85,7 @@ export class Menubar { this.menubarMenus = Object.create(null); this.keybindings = Object.create(null); - if (isMacintosh || showNativeTitlebar(configurationService)) { + if (isMacintosh || hasNativeTitlebar(configurationService)) { this.restoreCachedMenubarData(); } @@ -469,7 +469,7 @@ export class Menubar { private shouldDrawMenu(menuId: string): boolean { // We need to draw an empty menu to override the electron default - if (!isMacintosh && !showNativeTitlebar(this.configurationService)) { + if (!isMacintosh && !hasNativeTitlebar(this.configurationService)) { return false; } diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index a4514efd1268e..0a55eaa67e463 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -168,27 +168,19 @@ export interface IDensitySettings { export const enum TitlebarStyle { NATIVE = 'native', CUSTOM = 'custom', - NATIVE_AND_CUSTOM = 'native-and-custom' } -export function showCustomTitlebar(configurationServiceOrTitleStyle: IConfigurationService | TitlebarStyle): boolean { - let titleBarStyle: TitlebarStyle; - if (typeof configurationServiceOrTitleStyle === 'string') { - titleBarStyle = configurationServiceOrTitleStyle; - } else { - titleBarStyle = getTitleBarStyle(configurationServiceOrTitleStyle); - } - return titleBarStyle !== TitlebarStyle.NATIVE; +export function hasCustomTitlebar(configurationService: IConfigurationService, titleBarStyle?: TitlebarStyle): boolean { + // Does not imply that the title bar is visible + + return true; } -export function showNativeTitlebar(configurationServiceOrTitleStyle: IConfigurationService | TitlebarStyle): boolean { - let titleBarStyle: TitlebarStyle; - if (typeof configurationServiceOrTitleStyle === 'string') { - titleBarStyle = configurationServiceOrTitleStyle; - } else { - titleBarStyle = getTitleBarStyle(configurationServiceOrTitleStyle); +export function hasNativeTitlebar(configurationService: IConfigurationService, titleBarStyle?: TitlebarStyle): boolean { + if (!titleBarStyle) { + titleBarStyle = getTitleBarStyle(configurationService); } - return titleBarStyle !== TitlebarStyle.CUSTOM; + return titleBarStyle === TitlebarStyle.NATIVE; } export function getTitleBarStyle(configurationService: IConfigurationService): TitlebarStyle { @@ -209,7 +201,7 @@ export function getTitleBarStyle(configurationService: IConfigurationService): T } const style = configuration.titleBarStyle; - if (style === TitlebarStyle.NATIVE || style === TitlebarStyle.CUSTOM || style === TitlebarStyle.NATIVE_AND_CUSTOM) { + if (style === TitlebarStyle.NATIVE || style === TitlebarStyle.CUSTOM) { return style; } } @@ -222,7 +214,7 @@ export function useWindowControlsOverlay(configurationService: IConfigurationSer return false; // only supported on a desktop Windows instance } - if (showNativeTitlebar(configurationService)) { + if (hasNativeTitlebar(configurationService)) { return false; // only supported when title bar is custom } diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index 43ce7c4f4ac9d..c0ca43e257bc2 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -32,7 +32,7 @@ import { IApplicationStorageMainService, IStorageMainService } from 'vs/platform import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ThemeIcon } from 'vs/base/common/themables'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; -import { getMenuBarVisibility, IFolderToOpen, INativeWindowConfiguration, IWindowSettings, IWorkspaceToOpen, MenuBarVisibility, showNativeTitlebar, useNativeFullScreen, useWindowControlsOverlay } from 'vs/platform/window/common/window'; +import { getMenuBarVisibility, IFolderToOpen, INativeWindowConfiguration, IWindowSettings, IWorkspaceToOpen, MenuBarVisibility, hasNativeTitlebar, useNativeFullScreen, useWindowControlsOverlay } from 'vs/platform/window/common/window'; 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'; @@ -131,7 +131,7 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow { this._register(Event.fromNodeEventEmitter(this._win, 'leave-full-screen')(() => this._onDidLeaveFullScreen.fire())); // Sheet Offsets - const useCustomTitleStyle = !showNativeTitlebar(this.configurationService); + const useCustomTitleStyle = !hasNativeTitlebar(this.configurationService); if (isMacintosh && useCustomTitleStyle) { win.setSheetOffset(isBigSurOrNewer(release()) ? 28 : 22); // offset dialogs by the height of the custom title bar if we have any } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index e667ba6f39df7..d4aac4b6a654d 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { ServicesAccessor, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ICodeWindow, IWindowState } from 'vs/platform/window/electron-main/window'; -import { IOpenEmptyWindowOptions, IWindowOpenable, IWindowSettings, WindowMinimumSize, showNativeTitlebar, useNativeFullScreen, useWindowControlsOverlay, zoomLevelToZoomFactor } from 'vs/platform/window/common/window'; +import { IOpenEmptyWindowOptions, IWindowOpenable, IWindowSettings, WindowMinimumSize, hasNativeTitlebar, useNativeFullScreen, useWindowControlsOverlay, zoomLevelToZoomFactor } from 'vs/platform/window/common/window'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { IProductService } from 'vs/platform/product/common/productService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -173,7 +173,7 @@ export function defaultBrowserWindowOptions(accessor: ServicesAccessor, windowSt options.tabbingIdentifier = productService.nameShort; // this opts in to sierra tabs } - const useCustomTitleStyle = !showNativeTitlebar(configurationService); + const useCustomTitleStyle = !hasNativeTitlebar(configurationService); if (useCustomTitleStyle) { options.titleBarStyle = 'hidden'; if (!isMacintosh) { diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 0727a5a0889a0..06f1875ec000d 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -802,7 +802,7 @@ if (isWindows || isLinux || isWeb) { title: localize('miMenuBarNoMnemonic', "Menu Bar"), toggled: ContextKeyExpr.and(IsMacNativeContext.toNegated(), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'hidden'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'toggle'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'compact')) }, - when: IsAuxiliaryWindowFocusedContext.toNegated(), + when: ContextKeyExpr.and(IsAuxiliaryWindowFocusedContext.toNegated(), ContextKeyExpr.notEquals(`config.window.titleBarStyle`, 'native')), group: menuId === MenuId.TitleBarTitleContext ? '2_config' : undefined, order: 0 }); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index a36068ea2d84c..f276016e8bec4 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -12,14 +12,14 @@ import { isWindows, isLinux, isMacintosh, isWeb, isNative, isIOS } from 'vs/base import { EditorInputCapabilities, GroupIdentifier, isResourceEditorInput, IUntypedEditorInput, pathsToEditors } from 'vs/workbench/common/editor'; import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; -import { Position, Parts, PanelOpensMaximizedOptions, IWorkbenchLayoutService, positionFromString, positionToString, panelOpensMaximizedFromString, PanelAlignment, ActivityBarPosition, LayoutSettings, MULTI_WINDOW_PARTS, SINGLE_WINDOW_PARTS, ZenModeSettings, EditorTabsMode } from 'vs/workbench/services/layout/browser/layoutService'; +import { Position, Parts, PanelOpensMaximizedOptions, IWorkbenchLayoutService, positionFromString, positionToString, panelOpensMaximizedFromString, PanelAlignment, ActivityBarPosition, LayoutSettings, MULTI_WINDOW_PARTS, SINGLE_WINDOW_PARTS, ZenModeSettings, EditorTabsMode, EditorActionsLocation } from 'vs/workbench/services/layout/browser/layoutService'; import { isTemporaryWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITitleService } from 'vs/workbench/services/title/browser/titleService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { StartupKind, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { getMenuBarVisibility, IPath, showNativeTitlebar, showCustomTitlebar } from 'vs/platform/window/common/window'; +import { getMenuBarVisibility, IPath, hasNativeTitlebar, hasCustomTitlebar } from 'vs/platform/window/common/window'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -345,10 +345,12 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if ([ LayoutSettings.ACTIVITY_BAR_LOCATION, LayoutSettings.COMMAND_CENTER, + LayoutSettings.EDITOR_ACTIONS_LOCATION, LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION, LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE, 'window.menuBarVisibility', 'window.titleBarStyle', + 'window.showCustomToolBar', ].some(setting => e.affectsConfiguration(setting))) { this.doUpdateLayoutConfiguration(); } @@ -366,7 +368,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this._register(addDisposableListener(this.mainContainer, EventType.SCROLL, () => this.mainContainer.scrollTop = 0)); // Menubar visibility changes - if ((isWindows || isLinux || isWeb) && !showNativeTitlebar(this.configurationService)) { + if ((isWindows || isLinux || isWeb) && !hasNativeTitlebar(this.configurationService)) { this._register(this.titleService.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible))); } @@ -453,7 +455,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Changing fullscreen state of the main window has an impact // on custom title bar visibility, so we need to update - if (showCustomTitlebar(this.configurationService)) { + if (hasCustomTitlebar(this.configurationService)) { // Propagate to grid this.workbenchGrid.setViewVisible(this.titleBarPartView, this.shouldShowTitleBar()); @@ -489,6 +491,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private doUpdateLayoutConfiguration(skipLayout?: boolean): void { + // Custom Titlebar visibility with native titlebar + this.updateCustomTitleBarVisibility(); + // Menubar visibility this.updateMenubarVisibility(!!skipLayout); @@ -535,7 +540,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if ( isWeb || isWindows || // not working well with zooming and window control overlays - showNativeTitlebar(this.configurationService) + hasNativeTitlebar(this.configurationService) ) { return; } @@ -1219,16 +1224,35 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private shouldShowTitleBar(): boolean { - // Using the native title bar, don't ever show the custom one - if (!showCustomTitlebar(this.configurationService)) { + if (!hasCustomTitlebar(this.configurationService)) { return false; } + const showCustomToolBar = this.configurationService.getValue('window.showCustomToolBar'); + if (showCustomToolBar === 'never' || showCustomToolBar === 'windowed' && this.state.runtime.mainWindowFullscreen) { + return false; + } + + // with the command center enabled, we should always show + if (this.configurationService.getValue(LayoutSettings.COMMAND_CENTER)) { + return true; + } + // with the activity bar on top, we should always show if (this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.TOP) { return true; } + // with the editor actions on top, we should always show + if (this.configurationService.getValue(LayoutSettings.EDITOR_ACTIONS_LOCATION) === EditorActionsLocation.TITLEBAR) { + return true; + } + + // Hide custom title bar when native title bar and custom title bar is empty + if (hasNativeTitlebar(this.configurationService)) { + return false; + } + // macOS desktop does not need a title bar when full screen if (isMacintosh && isNative) { return !this.state.runtime.mainWindowFullscreen; @@ -2055,6 +2079,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } + updateCustomTitleBarVisibility(): void { + const shouldShowTitleBar = this.shouldShowTitleBar(); + const titlebarVisible = this.isVisible(Parts.TITLEBAR_PART); + if (shouldShowTitleBar !== titlebarVisible) { + this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowTitleBar); + } + } + toggleMenuBar(): void { let currentVisibilityValue = getMenuBarVisibility(this.configurationService); if (typeof currentVisibilityValue !== 'string') { @@ -2063,7 +2095,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi let newVisibilityValue: string; if (currentVisibilityValue === 'visible' || currentVisibilityValue === 'classic') { - newVisibilityValue = showNativeTitlebar(this.configurationService) ? 'toggle' : 'compact'; + newVisibilityValue = hasNativeTitlebar(this.configurationService) ? 'toggle' : 'compact'; } else { newVisibilityValue = 'classic'; } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index f3c9c66960abd..30cd016ad19fe 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -35,7 +35,6 @@ import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IViewDescriptorService, ViewContainerLocation, ViewContainerLocationToString } from 'vs/workbench/common/views'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; -import { TitleBarStyleContext } from 'vs/workbench/common/contextkeys'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -387,7 +386,7 @@ registerAction2(class extends Action2 { }, shortTitle: localize('side', "Side"), category: Categories.View, - toggled: ContextKeyExpr.or(ContextKeyExpr.equals(`config.${LayoutSettings.ACTIVITY_BAR_LOCATION}`, ActivityBarPosition.SIDE), ContextKeyExpr.and(ContextKeyExpr.equals(`config.${LayoutSettings.ACTIVITY_BAR_LOCATION}`, ActivityBarPosition.TOP), TitleBarStyleContext.isEqualTo('native'))), + toggled: ContextKeyExpr.equals(`config.${LayoutSettings.ACTIVITY_BAR_LOCATION}`, ActivityBarPosition.SIDE), menu: [{ id: MenuId.ActivityBarPositionMenu, order: 1 @@ -417,11 +416,10 @@ registerAction2(class extends Action2 { toggled: ContextKeyExpr.equals(`config.${LayoutSettings.ACTIVITY_BAR_LOCATION}`, ActivityBarPosition.TOP), menu: [{ id: MenuId.ActivityBarPositionMenu, - when: TitleBarStyleContext.notEqualsTo('native'), order: 2 }, { id: MenuId.CommandPalette, - when: ContextKeyExpr.and(ContextKeyExpr.notEquals(`config.${LayoutSettings.ACTIVITY_BAR_LOCATION}`, ActivityBarPosition.TOP), TitleBarStyleContext.notEqualsTo('native')), + when: ContextKeyExpr.notEquals(`config.${LayoutSettings.ACTIVITY_BAR_LOCATION}`, ActivityBarPosition.TOP), }] }); } diff --git a/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts b/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts index 3b94637bd57ab..c941138a188ac 100644 --- a/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts +++ b/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts @@ -14,7 +14,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { showCustomTitlebar } from 'vs/platform/window/common/window'; +import { hasCustomTitlebar } from 'vs/platform/window/common/window'; import { IEditorGroupView, IEditorPartsView } from 'vs/workbench/browser/parts/editor/editor'; import { EditorPart, IEditorPartUIState } from 'vs/workbench/browser/parts/editor/editorPart'; import { IAuxiliaryTitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart'; @@ -107,7 +107,7 @@ export class AuxiliaryEditorPart { // Titlebar let titlebarPart: IAuxiliaryTitlebarPart | undefined = undefined; let titlebarPartVisible = false; - const useCustomTitle = isNative && showCustomTitlebar(this.configurationService); // custom title in aux windows only enabled in native + const useCustomTitle = isNative && hasCustomTitlebar(this.configurationService); // custom title in aux windows only enabled in native if (useCustomTitle) { titlebarPart = disposables.add(this.titleService.createAuxiliaryTitlebarPart(auxiliaryWindow.container, editorPart)); titlebarPartVisible = true; diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 7081081e504ea..014acd8d37ae6 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -381,8 +381,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTabsBarShowTabsZenModeSubmenu, { comman MenuRegistry.appendMenuItem(MenuId.EditorTabsBarContext, { submenu: MenuId.EditorActionsPositionSubmenu, title: localize('editorActionsPosition', "Editor Actions Position"), group: '4_config', order: 20 }); MenuRegistry.appendMenuItem(MenuId.EditorActionsPositionSubmenu, { command: { id: EditorActionsDefaultAction.ID, title: localize('tabBar', "Tab Bar"), toggled: ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'default') }, group: '1_config', order: 10, when: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none').negate() }); -MenuRegistry.appendMenuItem(MenuId.EditorActionsPositionSubmenu, { command: { id: EditorActionsTitleBarAction.ID, title: localize('titleBar', "Title Bar"), toggled: ContextKeyExpr.or(ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'titleBar'), ContextKeyExpr.and(ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none'), ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'default'))) }, group: '1_config', order: 20, when: ContextKeyExpr.equals('config.window.titleBarStyle', 'native').negate() }); -MenuRegistry.appendMenuItem(MenuId.EditorActionsPositionSubmenu, { command: { id: HideEditorActionsAction.ID, title: localize('hidden', "Hidden"), toggled: ContextKeyExpr.or(ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'hidden'), ContextKeyExpr.and(ContextKeyExpr.equals('config.window.titleBarStyle', 'native'), ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'titleBar'))) }, group: '1_config', order: 30 }); +MenuRegistry.appendMenuItem(MenuId.EditorActionsPositionSubmenu, { command: { id: EditorActionsTitleBarAction.ID, title: localize('titleBar', "Title Bar"), toggled: ContextKeyExpr.or(ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'titleBar'), ContextKeyExpr.and(ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none'), ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'default'))) }, group: '1_config', order: 20 }); +MenuRegistry.appendMenuItem(MenuId.EditorActionsPositionSubmenu, { command: { id: HideEditorActionsAction.ID, title: localize('hidden', "Hidden"), toggled: ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'hidden') }, group: '1_config', order: 30 }); // Editor Title Context Menu MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: CLOSE_EDITOR_COMMAND_ID, title: localize('close', "Close") }, group: '1_close', order: 10 }); diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 43481ae2ad4d9..2923bcc75bb3a 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/menubarControl'; import { localize, localize2 } from 'vs/nls'; import { IMenuService, MenuId, IMenu, SubmenuItemAction, registerAction2, Action2, MenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; -import { MenuBarVisibility, IWindowOpenable, getMenuBarVisibility, showNativeTitlebar } from 'vs/platform/window/common/window'; +import { MenuBarVisibility, IWindowOpenable, getMenuBarVisibility, hasNativeTitlebar } from 'vs/platform/window/common/window'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IAction, Action, SubmenuAction, Separator, IActionRunner, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, toAction } from 'vs/base/common/actions'; import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom'; @@ -351,7 +351,7 @@ export abstract class MenubarControl extends Disposable { } const hasBeenNotified = this.storageService.getBoolean('menubar/accessibleMenubarNotified', StorageScope.APPLICATION, false); - const usingCustomMenubar = !showNativeTitlebar(this.configurationService); + const usingCustomMenubar = !hasNativeTitlebar(this.configurationService); if (hasBeenNotified || usingCustomMenubar || !this.accessibilityService.isScreenReaderOptimized()) { return; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index 303accb164be4..d1fa8f783c1fd 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -14,9 +14,14 @@ import { ACCOUNTS_ACTIVITY_ID, GLOBAL_ACTIVITY_ID } from 'vs/workbench/common/ac import { IAction } from 'vs/base/common/actions'; import { IsAuxiliaryWindowFocusedContext } from 'vs/workbench/common/contextkeys'; +// --- Context Menu Actions --- // + class ToggleConfigAction extends Action2 { - constructor(private readonly section: string, title: string, order: number, mainWindowOnly: boolean) { + constructor(private readonly section: string, title: string, order: number, mainWindowOnly: boolean, showInCustomToolBar: boolean) { + let when = mainWindowOnly ? IsAuxiliaryWindowFocusedContext.toNegated() : ContextKeyExpr.true(); + when = showInCustomToolBar ? when : ContextKeyExpr.and(when, ContextKeyExpr.equals(`config.window.titleBarStyle`, 'native').negate())!; + super({ id: `toggle.${section}`, title, @@ -24,12 +29,12 @@ class ToggleConfigAction extends Action2 { menu: [ { id: MenuId.TitleBarContext, - when: mainWindowOnly ? IsAuxiliaryWindowFocusedContext.toNegated() : undefined, + when, order }, { id: MenuId.TitleBarTitleContext, - when: mainWindowOnly ? IsAuxiliaryWindowFocusedContext.toNegated() : undefined, + when, order, group: '2_config' } @@ -46,13 +51,33 @@ class ToggleConfigAction extends Action2 { registerAction2(class ToggleCommandCenter extends ToggleConfigAction { constructor() { - super(LayoutSettings.COMMAND_CENTER, localize('toggle.commandCenter', 'Command Center'), 1, false); + super(LayoutSettings.COMMAND_CENTER, localize('toggle.commandCenter', 'Command Center'), 1, false, true); } }); registerAction2(class ToggleLayoutControl extends ToggleConfigAction { constructor() { - super('workbench.layoutControl.enabled', localize('toggle.layout', 'Layout Controls'), 2, true); + super('workbench.layoutControl.enabled', localize('toggle.layout', 'Layout Controls'), 2, true, false); + } +}); + +registerAction2(class ToggleCustomToolBar extends Action2 { + static readonly settingsID = `window.showCustomToolBar`; + + constructor() { + super({ + id: `toggle.${ToggleCustomToolBar.settingsID}`, + title: localize('toggle.toolBar', 'Custom Title Bar'), + toggled: ContextKeyExpr.true(), + menu: [ + { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.equals(`config.window.titleBarStyle`, 'native'), group: '1_toggle' }, + ] + }); + } + + run(accessor: ServicesAccessor, ...args: any[]): void { + const configService = accessor.get(IConfigurationService); + configService.updateValue(ToggleCustomToolBar.settingsID, 'never'); } }); @@ -105,6 +130,30 @@ registerAction2(class ToggleEditorActions extends Action2 { } }); +registerAction2(class ToggleActivityBarActions extends Action2 { + static readonly settingsID = `workbench.activityBar.location`; + constructor() { + + super({ + id: `toggle.${ToggleActivityBarActions.settingsID}`, + title: localize('toggle.activityBarActions', 'Activity Bar Actions'), + toggled: ContextKeyExpr.equals(`config.${ToggleActivityBarActions.settingsID}`, 'top'), + menu: [ + { id: MenuId.TitleBarContext, order: 4, when: ContextKeyExpr.notEquals(`config.${ToggleActivityBarActions.settingsID}`, 'side') }, + { id: MenuId.TitleBarTitleContext, order: 4, when: ContextKeyExpr.notEquals(`config.${ToggleActivityBarActions.settingsID}`, 'side'), group: '2_config' } + ] + }); + } + + run(accessor: ServicesAccessor, ...args: any[]): void { + const configService = accessor.get(IConfigurationService); + const oldLocation = configService.getValue(ToggleActivityBarActions.settingsID); + configService.updateValue(ToggleActivityBarActions.settingsID, oldLocation === 'top' ? 'hidden' : 'top'); + } +}); + +// --- Toolbar actions --- // + export const ACCOUNTS_ACTIVITY_TILE_ACTION: IAction = { id: ACCOUNTS_ACTIVITY_ID, label: localize('accounts', "Accounts"), diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 1d5fb04ab8ea7..9ffdb87f91745 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -8,7 +8,7 @@ import { localize, localize2 } from 'vs/nls'; import { MultiWindowParts, Part } from 'vs/workbench/browser/part'; import { ITitleService } from 'vs/workbench/services/title/browser/titleService'; import { getZoomFactor, isWCOEnabled } from 'vs/base/browser/browser'; -import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, TitlebarStyle, showCustomTitlebar, showNativeTitlebar } from 'vs/platform/window/common/window'; +import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, TitlebarStyle, hasCustomTitlebar, hasNativeTitlebar } from 'vs/platform/window/common/window'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; @@ -299,7 +299,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { oldPartOptions.editorActionsLocation !== newPartOptions.editorActionsLocation || oldPartOptions.showTabs !== newPartOptions.showTabs ) { - if (showCustomTitlebar(this.titleBarStyle) && this.actionToolBar) { + if (hasCustomTitlebar(this.configurationService, this.titleBarStyle) && this.actionToolBar) { this.createActionToolBar(); this.createActionToolBarMenus({ editorActions: true }); this._onDidChange.fire(undefined); @@ -310,7 +310,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { protected onConfigurationChanged(event: IConfigurationChangeEvent): void { // Custom menu bar (disabled if auxiliary) - if (!this.isAuxiliary && !showNativeTitlebar(this.titleBarStyle) && (!isMacintosh || isWeb)) { + if (!this.isAuxiliary && !hasNativeTitlebar(this.configurationService, this.titleBarStyle) && (!isMacintosh || isWeb)) { if (event.affectsConfiguration('window.menuBarVisibility')) { if (this.currentMenubarVisibility === 'compact') { this.uninstallMenubar(); @@ -321,7 +321,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { } // Actions - if (showCustomTitlebar(this.titleBarStyle) && this.actionToolBar) { + if (hasCustomTitlebar(this.configurationService, this.titleBarStyle) && this.actionToolBar) { const affectsLayoutControl = event.affectsConfiguration('workbench.layoutControl.enabled'); const affectsActivityControl = event.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION); @@ -388,7 +388,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { this.rightContent = append(this.rootContainer, $('.titlebar-right')); // App Icon (Native Windows/Linux and Web) - if (!isMacintosh && !isWeb && !showNativeTitlebar(this.titleBarStyle)) { + if (!isMacintosh && !isWeb && !hasNativeTitlebar(this.configurationService, this.titleBarStyle)) { this.appIcon = prepend(this.leftContent, $('a.window-appicon')); // Web-only home indicator and menu (not for auxiliary windows) @@ -412,7 +412,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { // Menubar: install a custom menu bar depending on configuration if ( !this.isAuxiliary && - !showNativeTitlebar(this.titleBarStyle) && + !hasNativeTitlebar(this.configurationService, this.titleBarStyle) && (!isMacintosh || isWeb) && this.currentMenubarVisibility !== 'compact' ) { @@ -424,7 +424,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { this.createTitle(); // Create Toolbar Actions - if (showCustomTitlebar(this.titleBarStyle)) { + if (hasCustomTitlebar(this.configurationService, this.titleBarStyle)) { this.actionToolBarElement = append(this.rightContent, $('div.action-toolbar-container')); this.createActionToolBar(); this.createActionToolBarMenus(); @@ -442,7 +442,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { } } - if (!showNativeTitlebar(this.titleBarStyle)) { + if (!hasNativeTitlebar(this.configurationService, this.titleBarStyle)) { this.primaryWindowControls = append(primaryControlLocation === 'left' ? this.leftContent : this.rightContent, $('div.window-controls-container.primary')); append(primaryControlLocation === 'left' ? this.rightContent : this.leftContent, $('div.window-controls-container.secondary')); } @@ -695,7 +695,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { } private get layoutControlEnabled(): boolean { - return !this.isAuxiliary && this.configurationService.getValue('workbench.layoutControl.enabled') !== false; + return !this.isAuxiliary && this.configurationService.getValue('workbench.layoutControl.enabled') !== false && !hasNativeTitlebar(this.configurationService, this.titleBarStyle); } protected get isCommandCenterVisible() { @@ -738,7 +738,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { private updateLayout(dimension: Dimension): void { this.lastLayoutDimensions = dimension; - if (showCustomTitlebar(this.titleBarStyle)) { + if (hasCustomTitlebar(this.configurationService, this.titleBarStyle)) { const zoomFactor = getZoomFactor(getWindow(this.element)); this.element.style.setProperty('--zoom-factor', zoomFactor.toString()); diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 9fa670968d920..869b35f957259 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -11,7 +11,7 @@ import { ConfigurationMigrationWorkbenchContribution, DynamicWorkbenchConfigurat import { isStandalone } from 'vs/base/browser/browser'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { ActivityBarPosition, EditorTabsMode, LayoutSettings } from 'vs/workbench/services/layout/browser/layoutService'; +import { ActivityBarPosition, EditorActionsLocation, EditorTabsMode, LayoutSettings } from 'vs/workbench/services/layout/browser/layoutService'; const registry = Registry.as(ConfigurationExtensions.Configuration); @@ -49,9 +49,9 @@ const registry = Registry.as(ConfigurationExtensions.Con '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.editorActionsLocation': { + [LayoutSettings.EDITOR_ACTIONS_LOCATION]: { 'type': 'string', - 'enum': ['default', 'titleBar', 'hidden'], + 'enum': [EditorActionsLocation.DEFAULT, EditorActionsLocation.TITLEBAR, EditorActionsLocation.HIDDEN], 'enumDescriptions': [ localize({ comment: ['{0} will be a setting name rendered as a link'], key: 'workbench.editor.editorActionsLocation.default' }, "Show editor actions in the window title bar when {0} is set to {1}. Otherwise, editor actions are shown in the editor tab bar.", '`#workbench.editor.showTabs#`', '`none`'), localize({ comment: ['{0} will be a setting name rendered as a link'], key: 'workbench.editor.editorActionsLocation.titleBar' }, "Show editor actions in the window title bar. If {0} is set to {1}, editor actions are hidden.", '`#window.titleBarStyle#`', '`native`'), diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index a12266a279b56..c3f413e8b0901 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -47,7 +47,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo ]; private readonly titleBarStyle = new ChangeObserver('string'); - private previousTitleBarStyle: TitlebarStyle | undefined = undefined; private readonly nativeTabs = new ChangeObserver('boolean'); private readonly nativeFullScreen = new ChangeObserver('boolean'); private readonly clickThroughInactive = new ChangeObserver('boolean'); @@ -86,12 +85,7 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo if (isNative) { // Titlebar style - processChanged( - (config.window.titleBarStyle === TitlebarStyle.NATIVE || config.window.titleBarStyle === TitlebarStyle.CUSTOM || config.window.titleBarStyle === TitlebarStyle.NATIVE_AND_CUSTOM) && - this.titleBarStyle.handleChange(config.window.titleBarStyle) && - (this.previousTitleBarStyle === TitlebarStyle.CUSTOM || config.window.titleBarStyle === TitlebarStyle.CUSTOM) // only if we come from custom or go to custom - ); - this.previousTitleBarStyle = config.window.titleBarStyle; + processChanged((config.window.titleBarStyle === TitlebarStyle.NATIVE || config.window.titleBarStyle === TitlebarStyle.CUSTOM) && this.titleBarStyle.handleChange(config.window?.titleBarStyle)); // macOS: Native tabs processChanged(isMacintosh && this.nativeTabs.handleChange(config.window?.nativeTabs)); diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 33be5c4b2d9a2..9cf09c66a85bc 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -227,13 +227,20 @@ import { applicationConfigurationNodeBase, securityConfigurationNodeBase } from 'scope': ConfigurationScope.APPLICATION, 'markdownDescription': localize('window.doubleClickIconToClose', "If enabled, this setting will close the window when the application icon in the title bar is double-clicked. The window will not be able to be dragged by the icon. This setting is effective only if `#window.titleBarStyle#` is set to `custom`.") }, - 'window.titleBarStyle': { // Todo + 'window.titleBarStyle': { 'type': 'string', - 'enum': ['native', 'custom', 'native-and-custom'], + 'enum': ['native', 'custom'], 'default': isLinux ? 'native' : 'custom', 'scope': ConfigurationScope.APPLICATION, 'description': localize('titleBarStyle', "Adjust the appearance of the window title bar to be native by the OS or custom. On Linux and Windows, this setting also affects the application and context menu appearances. Changes require a full restart to apply.") }, + 'window.showCustomToolBar': { + 'type': 'string', + 'enum': ['default', 'never', 'windowed'], + 'default': 'default', + 'scope': ConfigurationScope.APPLICATION, + 'description': localize('showCustomToolBar', "") // TODO@Ben + }, 'window.dialogStyle': { 'type': 'string', 'enum': ['native', 'custom'], diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 8288e6999fe59..fbf85313f6c7f 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -18,7 +18,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { INativeHostService } from 'vs/platform/native/common/native'; -import { showNativeTitlebar, useWindowControlsOverlay } from 'vs/platform/window/common/window'; +import { hasNativeTitlebar, useWindowControlsOverlay } from 'vs/platform/window/common/window'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; @@ -145,7 +145,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart { const targetWindowId = getWindowId(targetWindow); // Native menu controller - if (isMacintosh || showNativeTitlebar(this.configurationService)) { + if (isMacintosh || hasNativeTitlebar(this.configurationService)) { this._register(this.instantiationService.createInstance(NativeMenubarControl)); } @@ -159,7 +159,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart { } // Window Controls (Native Windows/Linux) - if (!isMacintosh && !showNativeTitlebar(this.configurationService) && !isWCOEnabled() && this.primaryWindowControls) { + if (!isMacintosh && !hasNativeTitlebar(this.configurationService) && !isWCOEnabled() && this.primaryWindowControls) { // Minimize const minimizeIcon = append(this.primaryWindowControls, $('div.window-icon.window-minimize' + ThemeIcon.asCSSSelector(Codicon.chromeMinimize))); @@ -195,7 +195,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart { // Window System Context Menu // See https://github.com/electron/electron/issues/24893 - if (isWindows && !showNativeTitlebar(this.configurationService)) { + if (isWindows && !hasNativeTitlebar(this.configurationService)) { this._register(this.nativeHostService.onDidTriggerWindowSystemContextMenu(({ windowId, x, y }) => { if (targetWindowId !== windowId) { return; @@ -253,7 +253,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart { if ( useWindowControlsOverlay(this.configurationService) || - (isMacintosh && isNative && !showNativeTitlebar(this.configurationService)) + (isMacintosh && isNative && !hasNativeTitlebar(this.configurationService)) ) { // When the user goes into full screen mode, the height of the title bar becomes 0. diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 1a9cbce221ae4..08ce756c0da2e 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -14,7 +14,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { EditorResourceAccessor, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors, IResourceDiffEditorInput, IUntypedEditorInput, IEditorPane, isResourceEditorInput, IResourceMergeEditorInput } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { WindowMinimumSize, IOpenFileRequest, IAddFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest, showNativeTitlebar } from 'vs/platform/window/common/window'; +import { WindowMinimumSize, IOpenFileRequest, IAddFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest, hasNativeTitlebar } from 'vs/platform/window/common/window'; import { ITitleService } from 'vs/workbench/services/title/browser/titleService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ApplyZoomTarget, applyZoom } from 'vs/platform/window/electron-sandbox/window'; @@ -377,7 +377,7 @@ export class NativeWindow extends BaseWindow { } // Maximize/Restore on doubleclick (for macOS custom title) - if (isMacintosh && !showNativeTitlebar(this.configurationService)) { + if (isMacintosh && !hasNativeTitlebar(this.configurationService)) { this._register(Event.runAndSubscribe(this.layoutService.onDidAddContainer, ({ container, disposables }) => { const targetWindow = getWindow(container); const targetWindowId = targetWindow.vscodeWindowId; @@ -611,7 +611,7 @@ export class NativeWindow extends BaseWindow { this.customTitleContextMenuDisposable.clear(); // Provide new menu if a file is opened and we are on a custom title - if (!filePath || !showNativeTitlebar(this.configurationService)) { + if (!filePath || !hasNativeTitlebar(this.configurationService)) { return; } diff --git a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts index b0ca3f2175e75..0ca133483eaaa 100644 --- a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts @@ -15,7 +15,7 @@ import { IContextMenuDelegate, IContextMenuEvent } from 'vs/base/browser/context import { createSingleCallFunction } from 'vs/base/common/functional'; import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; import { popup } from 'vs/base/parts/contextmenu/electron-sandbox/contextmenu'; -import { showNativeTitlebar } from 'vs/platform/window/common/window'; +import { hasNativeTitlebar } from 'vs/platform/window/common/window'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextMenuMenuDelegate, ContextMenuService as HTMLContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; @@ -48,7 +48,7 @@ export class ContextMenuService implements IContextMenuService { ) { // Custom context menu: Linux/Windows if custom title is enabled - if (!isMacintosh && !showNativeTitlebar(configurationService)) { + if (!isMacintosh && !hasNativeTitlebar(configurationService)) { this.impl = new HTMLContextMenuService(telemetryService, notificationService, contextViewService, keybindingService, menuService, contextKeyService); } diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index 10ee5560632d4..c64f875ded06b 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -37,6 +37,7 @@ export const enum ZenModeSettings { export const enum LayoutSettings { ACTIVITY_BAR_LOCATION = 'workbench.activityBar.location', EDITOR_TABS_MODE = 'workbench.editor.showTabs', + EDITOR_ACTIONS_LOCATION = 'workbench.editor.editorActionsLocation', COMMAND_CENTER = 'window.commandCenter', } @@ -52,6 +53,12 @@ export const enum EditorTabsMode { NONE = 'none' } +export const enum EditorActionsLocation { + DEFAULT = 'default', + TITLEBAR = 'titleBar', + HIDDEN = 'hidden' +} + export const enum Position { LEFT, RIGHT, From 25c484e173ab90522eeb3fadc59a2f612ad37c4f Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Wed, 17 Jan 2024 14:41:34 +0100 Subject: [PATCH 060/333] Updated titlebar actions grouping --- .../browser/parts/titlebar/titlebarActions.ts | 9 +++++---- .../workbench/browser/parts/titlebar/titlebarPart.ts | 10 +++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index d1fa8f783c1fd..6fdc5df5b092f 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -30,7 +30,8 @@ class ToggleConfigAction extends Action2 { { id: MenuId.TitleBarContext, when, - order + order, + group: '2_config' }, { id: MenuId.TitleBarTitleContext, @@ -70,7 +71,7 @@ registerAction2(class ToggleCustomToolBar extends Action2 { title: localize('toggle.toolBar', 'Custom Title Bar'), toggled: ContextKeyExpr.true(), menu: [ - { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.equals(`config.window.titleBarStyle`, 'native'), group: '1_toggle' }, + { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.equals(`config.window.titleBarStyle`, 'native'), group: '3_toggle' }, ] }); } @@ -95,7 +96,7 @@ registerAction2(class ToggleEditorActions extends Action2 { title: localize('toggle.editorActions', 'Editor Actions'), toggled: ContextKeyExpr.equals(`config.${ToggleEditorActions.settingsID}`, 'hidden').negate(), menu: [ - { id: MenuId.TitleBarContext, order: 3, when: titleBarContextCondition }, + { id: MenuId.TitleBarContext, order: 3, when: titleBarContextCondition, group: '2_config' }, { id: MenuId.TitleBarTitleContext, order: 3, when: titleBarContextCondition, group: '2_config' } ] }); @@ -139,7 +140,7 @@ registerAction2(class ToggleActivityBarActions extends Action2 { title: localize('toggle.activityBarActions', 'Activity Bar Actions'), toggled: ContextKeyExpr.equals(`config.${ToggleActivityBarActions.settingsID}`, 'top'), menu: [ - { id: MenuId.TitleBarContext, order: 4, when: ContextKeyExpr.notEquals(`config.${ToggleActivityBarActions.settingsID}`, 'side') }, + { id: MenuId.TitleBarContext, order: 4, when: ContextKeyExpr.notEquals(`config.${ToggleActivityBarActions.settingsID}`, 'side'), group: '2_config' }, { id: MenuId.TitleBarTitleContext, order: 4, when: ContextKeyExpr.notEquals(`config.${ToggleActivityBarActions.settingsID}`, 'side'), group: '2_config' } ] }); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 9ffdb87f91745..bb83d9199fcda 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -487,10 +487,14 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { // Text Title if (!this.isCommandCenterVisible) { - this.title.innerText = this.windowTitle.value; - this.titleDisposables.add(this.windowTitle.onDidChange(() => { + if (this.titleBarStyle === 'custom') { + this.title.innerText = this.windowTitle.value; - })); + this.titleDisposables.add(this.windowTitle.onDidChange(() => { + this.title.innerText = this.windowTitle.value; + })); + + } } // Menu Title From d0d22003f7538b27de26460dfe2506bca019905e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 17 Jan 2024 14:42:47 +0100 Subject: [PATCH 061/333] make sure save constent also works for notebooks (#202666) --- .../browser/inlineChatController.ts | 2 +- .../inlineChat/browser/inlineChatNotebook.ts | 37 +++++- .../browser/inlineChatSavingServiceImpl.ts | 122 ++++++++++++++---- 3 files changed, 129 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 91e8f5a812665..d4aab93914912 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -194,7 +194,7 @@ export class InlineChatController implements IEditorContribution { } this._strategy?.dispose(); this._store.dispose(); - this._log('controller disposed'); + this._log('DISPOSED controller'); } private _log(message: string | Error, ...more: any[]): void { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatNotebook.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatNotebook.ts index 2a360f95b2a0b..e074c182fc45c 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatNotebook.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatNotebook.ts @@ -23,17 +23,42 @@ export class InlineChatNotebookContribution { ) { this._store.add(sessionService.registerSessionKeyComputer(Schemas.vscodeNotebookCell, { - getComparisonKey: (_editor, uri) => { + getComparisonKey: (editor, uri) => { const data = CellUri.parse(uri); if (!data) { - throw illegalState('Expected notebook'); + throw illegalState('Expected notebook cell uri'); } - for (const editor of notebookEditorService.listNotebookEditors()) { - if (isEqual(editor.textModel?.uri, data.notebook)) { - return `${editor.getId()}#${uri}`; + let fallback: string | undefined; + for (const notebookEditor of notebookEditorService.listNotebookEditors()) { + if (notebookEditor.hasModel() && isEqual(notebookEditor.textModel.uri, data.notebook)) { + + const candidate = `${notebookEditor.getId()}#${uri}`; + + if (!fallback) { + fallback = candidate; + } + + // find the code editor in the list of cell-code editors + if (notebookEditor.codeEditors.find((tuple) => tuple[1] === editor)) { + return candidate; + } + + // // reveal cell and try to find code editor again + // const cell = notebookEditor.getCellByHandle(data.handle); + // if (cell) { + // notebookEditor.revealInViewAtTop(cell); + // if (notebookEditor.codeEditors.find((tuple) => tuple[1] === editor)) { + // return candidate; + // } + // } } } - throw illegalState('Expected notebook'); + + if (fallback) { + return fallback; + } + + throw illegalState('Expected notebook editor'); } })); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts index 82effd20020f5..55658018f7394 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts @@ -3,15 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { raceCancellation } from 'vs/base/common/async'; +import { Queue, raceCancellation } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { DisposableStore, IDisposable, MutableDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, MutableDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { ITextModel } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; -import { DEFAULT_EDITOR_ASSOCIATION, SaveReason } from 'vs/workbench/common/editor'; +import { SaveReason } from 'vs/workbench/common/editor'; import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { IInlineChatSessionService } from './inlineChatSessionService'; import { InlineChatConfigKeys } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; @@ -22,8 +21,16 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IInlineChatSavingService } from './inlineChatSavingService'; import { Iterable } from 'vs/base/common/iterator'; import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; +import { Schemas } from 'vs/base/common/network'; +import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { compare } from 'vs/base/common/strings'; +import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { URI } from 'vs/base/common/uri'; +import { ILogService } from 'vs/platform/log/common/log'; interface SessionData { + readonly resourceUri: URI; readonly dispose: () => void; readonly session: Session; readonly groupCandidate: IEditorGroup; @@ -44,6 +51,8 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { @IEditorService private readonly _editorService: IEditorService, @IInlineChatSessionService private readonly _inlineChatSessionService: IInlineChatSessionService, @IConfigurationService private readonly _configService: IConfigurationService, + @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, + @ILogService private readonly _logService: ILogService, ) { this._store.add(_inlineChatSessionService.onDidEndSession(e => { this._sessionData.get(e.session)?.dispose(); @@ -58,16 +67,28 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { markChanged(session: Session): void { if (!this._sessionData.has(session)) { + let uri = session.textModelN.uri; + + // notebooks: use the notebook-uri because saving happens on the notebook-level + if (uri.scheme === Schemas.vscodeNotebookCell) { + const data = CellUri.parse(uri); + if (!data) { + return; + } + uri = data?.notebook; + } + if (this._sessionData.size === 0) { this._installSaveParticpant(); } - const saveConfig = this._fileConfigService.disableAutoSave(session.textModelN.uri); + const saveConfigOverride = this._fileConfigService.disableAutoSave(uri); this._sessionData.set(session, { + resourceUri: uri, groupCandidate: this._editorGroupService.activeGroup, session, dispose: () => { - saveConfig.dispose(); + saveConfigOverride.dispose(); this._sessionData.delete(session); if (this._sessionData.size === 0) { this._saveParticipant.clear(); @@ -78,12 +99,23 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { } private _installSaveParticpant(): void { - this._saveParticipant.value = this._textFileService.files.addSaveParticipant({ - participate: (model, context, progress, token) => this._participate(model.textEditorModel, context.reason, progress, token) + + const queue = new Queue(); + + const d1 = this._textFileService.files.addSaveParticipant({ + participate: (model, context, progress, token) => { + return queue.queue(() => this._participate(model.textEditorModel?.uri, context.reason, progress, token)); + } }); + const d2 = this._workingCopyFileService.addSaveParticipant({ + participate: (workingCopy, env, progress, token) => { + return queue.queue(() => this._participate(workingCopy.resource, env.reason, progress, token)); + } + }); + this._saveParticipant.value = combinedDisposable(d1, d2, queue); } - private async _participate(model: ITextModel | null, reason: SaveReason, progress: IProgress, token: CancellationToken): Promise { + private async _participate(uri: URI | undefined, reason: SaveReason, progress: IProgress, token: CancellationToken): Promise { if (reason !== SaveReason.EXPLICIT) { // all saves that we are concerned about are explicit @@ -98,7 +130,7 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { const sessions = new Map(); for (const [session, data] of this._sessionData) { - if (model === session.textModelN) { + if (uri?.toString() === data.resourceUri.toString()) { sessions.set(session, data); } } @@ -123,7 +155,7 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { }); // fallback: resolve when all sessions for this model have been resolved. this is independent of the editor opening - const allSessionsEnded = this._waitForSessions(Iterable.concat(groups.values(), orphans), token); + const allSessionsEnded = this._whenSessionsEnded(Iterable.concat(groups.map(tuple => tuple[1]), orphans), token); await Promise.race([allSessionsEnded, editorsOpenedAndSessionsEnded]); } @@ -138,19 +170,20 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { } } - const groups = new Map(); + const groups: [IEditorGroup, SessionData][] = []; const orphans = new Set(); for (const data of sessions) { + const editor = this._inlineChatSessionService.getCodeEditor(data.session); const group = groupByEditor.get(editor); if (group) { // there is only one session per group because all sessions have the same model // because we save one file. - groups.set(group, data); + groups.push([group, data]); } else if (this._editorGroupService.groups.includes(data.groupCandidate)) { // the group candidate is still there. use it - groups.set(data.groupCandidate, data); + groups.push([data.groupCandidate, data]); } else { orphans.add(data); } @@ -159,22 +192,61 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { } private async _openAndWait(groups: Iterable<[IEditorGroup, SessionData]>, token: CancellationToken) { - const sessions = new Set(); + + const dataByGroup = new Map(); for (const [group, data] of groups) { - const input: IResourceEditorInput = { resource: data.session.textModelN.uri, options: { override: DEFAULT_EDITOR_ASSOCIATION.id } }; - const pane = await this._editorService.openEditor(input, group); - const ctrl = pane?.getControl(); - if (!isCodeEditor(ctrl)) { - // PANIC - return; + let array = dataByGroup.get(group); + if (!array) { + array = []; + dataByGroup.set(group, array); + } + array.push(data); + } + + for (const [group, array] of dataByGroup) { + + if (token.isCancellationRequested) { + break; + } + + array.sort((a, b) => compare(a.session.textModelN.uri.toString(), b.session.textModelN.uri.toString())); + + + for (const data of array) { + + const input: IResourceEditorInput = { resource: data.resourceUri }; + const pane = await this._editorService.openEditor(input, group); + let editor: ICodeEditor | undefined; + if (data.session.textModelN.uri.scheme === Schemas.vscodeNotebookCell) { + const notebookEditor = getNotebookEditorFromEditorPane(pane); + const uriData = CellUri.parse(data.session.textModelN.uri); + if (notebookEditor && notebookEditor.hasModel() && uriData) { + const cell = notebookEditor.getCellByHandle(uriData.handle); + if (cell) { + await notebookEditor.revealRangeInCenterIfOutsideViewportAsync(cell, data.session.wholeRange.value); + } + const tuple = notebookEditor.codeEditors.find(tuple => tuple[1].getModel()?.uri.toString() === data.session.textModelN.uri.toString()); + editor = tuple?.[1]; + } + + } else { + if (isCodeEditor(pane?.getControl())) { + editor = pane.getControl(); + } + } + + if (!editor) { + // PANIC + break; + } + this._inlineChatSessionService.moveSession(data.session, editor); + this._logService.info('WAIT for session to end', editor.getId(), data.session.textModelN.uri.toString()); + await this._whenSessionsEnded(Iterable.single(data), token); } - this._inlineChatSessionService.moveSession(data.session, ctrl); - sessions.add(data); } - await this._waitForSessions(sessions, token); } - private async _waitForSessions(iterable: Iterable, token: CancellationToken) { + private async _whenSessionsEnded(iterable: Iterable, token: CancellationToken) { const sessions = new Map(); for (const item of iterable) { From 4134a5b3d0b77f4066e3961c02914c9a1aed6153 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Wed, 17 Jan 2024 14:51:44 +0100 Subject: [PATCH 062/333] Menu Bar Submenu --- src/vs/workbench/browser/actions/layoutActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 06f1875ec000d..bf10920f0ffcd 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -803,7 +803,7 @@ if (isWindows || isLinux || isWeb) { toggled: ContextKeyExpr.and(IsMacNativeContext.toNegated(), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'hidden'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'toggle'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'compact')) }, when: ContextKeyExpr.and(IsAuxiliaryWindowFocusedContext.toNegated(), ContextKeyExpr.notEquals(`config.window.titleBarStyle`, 'native')), - group: menuId === MenuId.TitleBarTitleContext ? '2_config' : undefined, + group: '2_config', order: 0 }); } From ee6babafa786fe86765cd3687a1a8339064b1819 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 17 Jan 2024 16:09:36 +0100 Subject: [PATCH 063/333] make `enableKeybindingHoldMode` return a promise that resolves when hold-mode is over --- .../standalone/browser/standaloneServices.ts | 7 +++++ .../common/abstractKeybindingService.ts | 13 ++------ .../platform/keybinding/common/keybinding.ts | 4 +-- .../common/abstractKeybindingService.test.ts | 4 +++ .../test/common/mockKeybindingService.ts | 4 +-- .../electron-sandbox/inlineChatActions.ts | 30 ++++++++----------- .../keybinding/browser/keybindingService.ts | 20 +++++++++++-- 7 files changed, 46 insertions(+), 36 deletions(-) diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index ec7e0075f74cf..b20bde39b150d 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -594,6 +594,13 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { public registerSchemaContribution(contribution: KeybindingsSchemaContribution): void { // noop } + + /** + * not yet supported + */ + public override enableKeybindingHoldMode(commandId: string): Promise | undefined { + return undefined; + } } class DomNodeListeners extends Disposable { diff --git a/src/vs/platform/keybinding/common/abstractKeybindingService.ts b/src/vs/platform/keybinding/common/abstractKeybindingService.ts index 0df56233ba139..40e5567ab00be 100644 --- a/src/vs/platform/keybinding/common/abstractKeybindingService.ts +++ b/src/vs/platform/keybinding/common/abstractKeybindingService.ts @@ -53,8 +53,7 @@ export abstract class AbstractKeybindingService extends Disposable implements IK private _ignoreSingleModifiers: KeybindingModifierSet; private _currentSingleModifier: SingleModifierChord | null; private _currentSingleModifierClearTimeout: TimeoutTimer; - private _currentlyDispatchingCommandId: string | null; - protected _isInKeybindingHoldMode: boolean; + protected _currentlyDispatchingCommandId: string | null; protected _logging: boolean; @@ -78,7 +77,6 @@ export abstract class AbstractKeybindingService extends Disposable implements IK this._currentSingleModifier = null; this._currentSingleModifierClearTimeout = new TimeoutTimer(); this._currentlyDispatchingCommandId = null; - this._isInKeybindingHoldMode = false; this._logging = false; } @@ -387,14 +385,7 @@ export abstract class AbstractKeybindingService extends Disposable implements IK } } - enableKeybindingHoldMode(commandId: string): boolean { - if (this._currentlyDispatchingCommandId !== commandId) { - return false; - } - this._isInKeybindingHoldMode = true; - this._log(`+ Enabled hold-mode for ${commandId}.`); - return true; - } + abstract enableKeybindingHoldMode(commandId: string): Promise | undefined; mightProducePrintableCharacter(event: IKeyboardEvent): boolean { if (event.ctrlKey || event.metaKey) { diff --git a/src/vs/platform/keybinding/common/keybinding.ts b/src/vs/platform/keybinding/common/keybinding.ts index 789d044a64f88..5a8c2b5dceca9 100644 --- a/src/vs/platform/keybinding/common/keybinding.ts +++ b/src/vs/platform/keybinding/common/keybinding.ts @@ -69,9 +69,9 @@ export interface IKeybindingService { * Enable hold mode for this command. This is only possible if the command is current being dispatched, meaning * we are after its keydown and before is keyup event. * - * @returns true is hold mode was enabled + * @returns A promise that resolves when hold stops, returns undefined if hold mode could not be enabled. */ - enableKeybindingHoldMode(commandId: string): boolean; + enableKeybindingHoldMode(commandId: string): Promise | undefined; dispatchByUserSettingsLabel(userSettingsLabel: string, target: IContextKeyServiceTarget): void; diff --git a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts index 246ed148a4d31..24386a8cab200 100644 --- a/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts +++ b/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts @@ -95,6 +95,10 @@ suite('AbstractKeybindingService', () => { public registerSchemaContribution() { // noop } + + public enableKeybindingHoldMode() { + return undefined; + } } let createTestKeybindingService: (items: ResolvedKeybindingItem[], contextValue?: any) => TestKeybindingService = null!; diff --git a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts index a9d352e24ea89..9991b85f24b79 100644 --- a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts +++ b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts @@ -147,8 +147,8 @@ export class MockKeybindingService implements IKeybindingService { return false; } - public enableKeybindingHoldMode(commandId: string): boolean { - return false; + public enableKeybindingHoldMode(commandId: string): undefined { + return undefined; } public mightProducePrintableCharacter(e: IKeyboardEvent): boolean { diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts index c0f8c8f5efba8..6a94b91a0d57f 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts @@ -2,7 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as dom from 'vs/base/browser/dom'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; @@ -51,31 +50,26 @@ export class StartSessionAction extends EditorAction2 { if (configService.getValue(InlineChatConfigKeys.HoldToSpeech) // enabled && speechService.hasSpeechProvider // possible - && keybindingService.enableKeybindingHoldMode(this.desc.id) // holding keys ) { + const holdMode = keybindingService.enableKeybindingHoldMode(this.desc.id); + if (holdMode) { // holding keys + let listening = false; + const handle = disposableTimeout(() => { + // start VOICE input + commandService.executeCommand(StartVoiceChatAction.ID); + listening = true; + }, 100); - let listening = false; - const handle = disposableTimeout(() => { - // start VOICE input - commandService.executeCommand(StartVoiceChatAction.ID); - listening = true; - }, 100); - - const listener = dom.addDisposableListener( - dom.getWindow(editor.getDomNode()), - dom.EventType.KEY_UP, - (_e: KeyboardEvent) => { - listener.dispose(); // Event.once + holdMode.finally(() => { if (listening) { commandService.executeCommand(StopListeningAction.ID).finally(() => { InlineChatController.get(editor)?.acceptInput(); }); - } else { - handle.dispose(); } - } - ); + handle.dispose(); + }); + } } let options: InlineChatRunOptions | undefined; diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index 3222c7dfab661..1345094497c66 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -10,7 +10,7 @@ import * as browser from 'vs/base/browser/browser'; import { BrowserFeatures, KeyboardSupport } from 'vs/base/browser/canIUse'; import * as dom from 'vs/base/browser/dom'; import { printKeyboardEvent, printStandardKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { RunOnceScheduler } from 'vs/base/common/async'; +import { DeferredPromise, RunOnceScheduler } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { parse } from 'vs/base/common/json'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; @@ -183,6 +183,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { private _cachedResolver: KeybindingResolver | null; private userKeybindings: UserKeybindings; private isComposingGlobalContextKey: IContextKey; + private _keybindingHoldMode: DeferredPromise | null; private readonly _contributions: KeybindingsSchemaContribution[] = []; private readonly kbsJsonSchema: KeybindingsJsonSchema; @@ -212,6 +213,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { this.updateResolver(); }); + this._keybindingHoldMode = null; this._cachedResolver = null; this.userKeybindings = this._register(new UserKeybindings(userDataProfileService, uriIdentityService, fileService, logService)); @@ -269,7 +271,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { // for standard keybindings disposables.add(dom.addDisposableListener(window, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { - if (this._isInKeybindingHoldMode) { + if (this._keybindingHoldMode) { return; } this.isComposingGlobalContextKey.set(e.isComposing); @@ -285,7 +287,10 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { // for single modifier chord keybindings (e.g. shift shift) disposables.add(dom.addDisposableListener(window, dom.EventType.KEY_UP, (e: KeyboardEvent) => { - this._isInKeybindingHoldMode = false; + if (this._keybindingHoldMode) { + this._keybindingHoldMode.complete(); + this._keybindingHoldMode = null; + } this.isComposingGlobalContextKey.set(e.isComposing); const keyEvent = new StandardKeyboardEvent(e); const shouldPreventDefault = this._singleModifierDispatch(keyEvent, keyEvent.target); @@ -395,6 +400,15 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { return JSON.stringify(info, null, '\t'); } + public override enableKeybindingHoldMode(commandId: string): Promise | undefined { + if (this._currentlyDispatchingCommandId !== commandId) { + return undefined; + } + this._keybindingHoldMode = new DeferredPromise(); + this._log(`+ Enabled hold-mode for ${commandId}.`); + return this._keybindingHoldMode.p; + } + public override customKeybindingsCount(): number { return this.userKeybindings.keybindings.length; } From 85d3cd0eef83f41f74311b79b5c7c46d9ce52423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 17 Jan 2024 16:54:23 +0100 Subject: [PATCH 064/333] do not create a double semi colon when adding to path (#202583) * do not create a double semi colon when adding to path fixes #202268 * parens * bad signature * hm --- build/win32/code.iss | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/build/win32/code.iss b/build/win32/code.iss index cca821e647df2..f8d231f58583f 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -1296,7 +1296,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Drive\shell\{#RegValu #define Uninstall32RootKey "HKLM32" #endif -Root: {#EnvironmentRootKey}; Subkey: "{#EnvironmentKey}"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\bin"; Tasks: addtopath; Check: NeedsAddPath(ExpandConstant('{app}\bin')) +Root: {#EnvironmentRootKey}; Subkey: "{#EnvironmentKey}"; ValueType: expandsz; ValueName: "Path"; ValueData: "{code:AddToPath|{app}\bin}"; Tasks: addtopath; Check: NeedsAddToPath(ExpandConstant('{app}\bin')) [Code] function IsBackgroundUpdate(): Boolean; @@ -1553,7 +1553,7 @@ begin until Length(Text)=0; end; -function NeedsAddPath(Param: string): boolean; +function NeedsAddToPath(VSCode: string): boolean; var OrigPath: string; begin @@ -1562,7 +1562,19 @@ begin Result := True; exit; end; - Result := Pos(';' + Param + ';', ';' + OrigPath + ';') = 0; + Result := Pos(';' + VSCode + ';', ';' + OrigPath + ';') = 0; +end; + +function AddToPath(VSCode: string): string; +var + OrigPath: string; +begin + RegQueryStringValue({#EnvironmentRootKey}, '{#EnvironmentKey}', 'Path', OrigPath) + + if (Length(OrigPath) > 0) and (OrigPath[Length(OrigPath)] = ';') then + Result := OrigPath + VSCode + else + Result := OrigPath + ';' + VSCode end; procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); From b87961277683bf962da8b18c42c851929e125e7b Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Wed, 17 Jan 2024 17:03:06 +0100 Subject: [PATCH 065/333] editor actions default --- src/vs/workbench/browser/layout.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index f276016e8bec4..3e45b0db08dd4 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1244,7 +1244,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // with the editor actions on top, we should always show - if (this.configurationService.getValue(LayoutSettings.EDITOR_ACTIONS_LOCATION) === EditorActionsLocation.TITLEBAR) { + const editorActionsLocation = this.configurationService.getValue(LayoutSettings.EDITOR_ACTIONS_LOCATION); + const editorTabsMode = this.configurationService.getValue(LayoutSettings.EDITOR_TABS_MODE); + if (editorActionsLocation === EditorActionsLocation.TITLEBAR || editorActionsLocation === EditorActionsLocation.DEFAULT && editorTabsMode === EditorTabsMode.NONE) { return true; } From 80f7da88d517bc9191c2a9c7c2018653a4f90aa0 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Wed, 17 Jan 2024 10:17:16 -0600 Subject: [PATCH 066/333] Search result tree drawing glitches (#202624) Fixes #158785 --- src/vs/workbench/contrib/search/browser/searchView.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 8286ef92bd29e..babe5ac41fcc6 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -354,6 +354,7 @@ export class SearchView extends ViewPane { // remove old model and use the new searchModel searchModel.location = SearchModelLocation.PANEL; searchModel.replaceActive = this.viewModel.isReplaceActive(); + searchModel.replaceString = this.searchWidget.getReplaceValue(); this._onSearchResultChangedDisposable?.dispose(); this._onSearchResultChangedDisposable = this._register(searchModel.onSearchResultChanged((event) => this.onSearchResultsChanged(event))); @@ -1613,8 +1614,6 @@ export class SearchView extends ViewPane { } } - this.viewModel.replaceString = this.searchWidget.getReplaceValue(); - const hasResults = !this.viewModel.searchResult.isEmpty(); if (completed?.exit === SearchCompletionExitCode.NewSearchStarted) { return; @@ -1735,6 +1734,8 @@ export class SearchView extends ViewPane { this.tree.setSelection([]); this.tree.setFocus([]); + + this.viewModel.replaceString = this.searchWidget.getReplaceValue(); const result = this.viewModel.search(query); return result.asyncResults.then((complete) => { clearTimeout(slowTimer); From f05f54e74c68bd989a3beaf5c7f8363552fdee0a Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 17 Jan 2024 08:23:55 -0800 Subject: [PATCH 067/333] Support command in inline chat followup (#202634) --- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostInlineChat.ts | 6 +++--- .../inlineChat/browser/inlineChatController.ts | 10 ++++++++-- .../contrib/inlineChat/browser/inlineChatWidget.ts | 6 +++--- .../workbench/contrib/inlineChat/common/inlineChat.ts | 4 ++-- src/vscode-dts/vscode.proposed.interactive.d.ts | 11 ++++++++++- 6 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 346c1ddd26481..e622ea5d57e5f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1233,7 +1233,7 @@ export type IInlineChatResponseDto = Dto; $provideResponse(handle: number, session: IInlineChatSession, request: IInlineChatRequest, token: CancellationToken): Promise; - $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise; + $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise; $handleFeedback(handle: number, sessionId: number, responseId: number, kind: InlineChatResponseFeedbackKind): void; $releaseSession(handle: number, sessionId: number): void; } diff --git a/src/vs/workbench/api/common/extHostInlineChat.ts b/src/vs/workbench/api/common/extHostInlineChat.ts index 7baad7113059d..72fbb41c99fe0 100644 --- a/src/vs/workbench/api/common/extHostInlineChat.ts +++ b/src/vs/workbench/api/common/extHostInlineChat.ts @@ -19,7 +19,7 @@ import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } fro import { IRange } from 'vs/editor/common/core/range'; import { IPosition } from 'vs/editor/common/core/position'; import { raceCancellation } from 'vs/base/common/async'; -import { IChatReplyFollowup } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; class ProviderWrapper { @@ -227,14 +227,14 @@ export class ExtHostInteractiveEditor implements ExtHostInlineChatShape { } } - async $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise { + async $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise { const entry = this._inputProvider.get(handle); const sessionData = this._inputSessions.get(sessionId); const response = sessionData?.responses[responseId]; if (entry && response && entry.provider.provideFollowups) { const task = Promise.resolve(entry.provider.provideFollowups(sessionData.session, response, token)); const followups = await raceCancellation(task, token); - return followups?.map(typeConvert.ChatReplyFollowup.from); + return followups?.map(typeConvert.ChatFollowup.from); } return undefined; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index d4aab93914912..be3aa770913e6 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -45,6 +45,7 @@ import { IInlineChatSessionService } from './inlineChatSessionService'; import { EditModeStrategy, LivePreviewStrategy, LiveStrategy, PreviewStrategy, ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies'; import { IInlineChatMessageAppender, InlineChatZoneWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_RESPONSE_TYPES, CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING, CTX_INLINE_CHAT_USER_DID_EDIT, EditMode, IInlineChatProgressItem, IInlineChatRequest, IInlineChatResponse, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseFeedbackKind, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { ICommandService } from 'vs/platform/commands/common/commands'; export const enum State { CREATE_SESSION = 'CREATE_SESSION', @@ -143,6 +144,7 @@ export class InlineChatController implements IEditorContribution { @IChatAgentService private readonly _chatAgentService: IChatAgentService, @IBulkEditService private readonly _bulkEditService: IBulkEditService, @IStorageService private readonly _storageService: IStorageService, + @ICommandService private readonly _commandService: ICommandService, ) { this._ctxHasActiveRequest = CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST.bindTo(contextKeyService); this._ctxDidEdit = CTX_INLINE_CHAT_DID_EDIT.bindTo(contextKeyService); @@ -788,8 +790,12 @@ export class InlineChatController implements IEditorContribution { if (followupReply && this._session) { this._log('followup request received', this._session.provider.debugName, this._session.session, followupReply); this._zone.value.widget.updateFollowUps(followupReply, followup => { - this.updateInput(followup.message); - this.acceptInput(); + if (followup.kind === 'reply') { + this.updateInput(followup.message); + this.acceptInput(); + } else { + this._commandService.executeCommand(followup.commandId, ...(followup.args ?? [])); + } }); } }).finally(() => { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 98c163bf0d565..f0509750b6344 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -64,7 +64,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ChatListItemRenderer, IChatListItemRendererOptions, IChatRendererDelegate } from 'vs/workbench/contrib/chat/browser/chatListRenderer'; import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IChatReplyFollowup } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatFollowups } from 'vs/workbench/contrib/chat/browser/chatFollowups'; @@ -635,9 +635,9 @@ export class InlineChatWidget { return resultingAppender; } - updateFollowUps(items: IChatReplyFollowup[], onFollowup: (followup: IChatReplyFollowup) => void): void; + updateFollowUps(items: IChatFollowup[], onFollowup: (followup: IChatFollowup) => void): void; updateFollowUps(items: undefined): void; - updateFollowUps(items: IChatReplyFollowup[] | undefined, onFollowup?: ((followup: IChatReplyFollowup) => void)) { + updateFollowUps(items: IChatFollowup[] | undefined, onFollowup?: ((followup: IChatFollowup) => void)) { this._followUpDisposables.clear(); this._elements.followUps.classList.toggle('hidden', !items || items.length === 0); reset(this._elements.followUps); diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index 1ac180612069f..64370f4950c95 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -20,7 +20,7 @@ import { IProgress } from 'vs/platform/progress/common/progress'; import { Registry } from 'vs/platform/registry/common/platform'; import { diffInserted, diffRemoved, editorHoverHighlight, editorWidgetBackground, editorWidgetBorder, focusBorder, inputBackground, inputPlaceholderForeground, registerColor, transparent, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { Extensions as ExtensionsMigration, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; -import { IChatReplyFollowup } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; export interface IInlineChatSlashCommand { command: string; @@ -106,7 +106,7 @@ export interface IInlineChatSessionProvider { provideResponse(item: IInlineChatSession, request: IInlineChatRequest, progress: IProgress, token: CancellationToken): ProviderResult; - provideFollowups?(session: IInlineChatSession, response: IInlineChatResponse, token: CancellationToken): ProviderResult; + provideFollowups?(session: IInlineChatSession, response: IInlineChatResponse, token: CancellationToken): ProviderResult; handleInlineChatResponseFeedback?(session: IInlineChatSession, response: IInlineChatResponse, kind: InlineChatResponseFeedbackKind): void; } diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index 98955ea4dd23d..e233f52fa5c21 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -83,6 +83,15 @@ declare module 'vscode' { title?: string; } + export interface InteractiveEditorCommandFollowup { + commandId: string; + args?: any[]; + title: string; + when?: string; + } + + export type InteractiveEditorFollowup = InteractiveEditorReplyFollowup | InteractiveEditorCommandFollowup; + export interface InteractiveEditorSessionProvider { // Create a session. The lifetime of this session is the duration of the editing session with the input mode widget. @@ -90,7 +99,7 @@ declare module 'vscode' { provideInteractiveEditorResponse(session: S, request: InteractiveEditorRequest, progress: Progress, token: CancellationToken): ProviderResult; - provideFollowups?(session: S, response: R, token: CancellationToken): ProviderResult; + provideFollowups?(session: S, response: R, token: CancellationToken): ProviderResult; // eslint-disable-next-line local/vscode-dts-provider-naming handleInteractiveEditorResponseFeedback?(session: S, response: R, kind: InteractiveEditorResponseFeedbackKind): void; From 1deb0e602ee679737d9abcc77c40aa9d7532ce22 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 17 Jan 2024 09:09:09 -0800 Subject: [PATCH 068/333] move above cursor --- src/vs/workbench/contrib/terminal/browser/media/terminal.css | 4 +--- src/vs/workbench/contrib/terminal/browser/terminal.ts | 3 ++- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 4 ++-- .../contrib/terminal/browser/terminalSpeechToText.ts | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index abd6805fa4ad2..f044e6b04f448 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -566,6 +566,4 @@ visibility: hidden; } -.terminal-speech-to-text { - padding-left: 5px; -} + diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index f5dfc7d63a0cc..68fc67ea4e1c8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -789,8 +789,9 @@ export interface ITerminalInstance extends IBaseTerminalInstance { /** * Registers and returns a marker + * @param offset The offset from the cursor */ - registerMarker(): IMarker | undefined; + registerMarker(offset?: number): IMarker | undefined; /** * Adds a marker to the buffer, mapping it to an ID if provided. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 5ac44dc27182e..9bd13171c8fe0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -1440,8 +1440,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } } - public registerMarker(): IMarker | undefined { - return this.xterm?.raw.registerMarker(); + public registerMarker(offset?: number): IMarker | undefined { + return this.xterm?.raw.registerMarker(offset); } public addBufferMarker(properties: IMarkProperties): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts index a7bc31c3b5d7c..78001da19a535 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts @@ -146,7 +146,7 @@ export class TerminalSpeechToTextSession extends Disposable { if (!xterm) { return; } - this._marker = activeInstance.registerMarker(); + this._marker = activeInstance.registerMarker(-1); if (!this._marker) { return; } @@ -157,7 +157,7 @@ export class TerminalSpeechToTextSession extends Disposable { }); this._decoration?.onRender((e: HTMLElement) => { e.classList.add(...ThemeIcon.asClassNameArray(Codicon.mic)); - e.classList.add('terminal-speech-to-text'); + e.style.transform = 'translate(-5px, -5px)'; }); } } From af44afef6d786bfe5500b0c32cf839f81e81a0cc Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 17 Jan 2024 09:42:32 -0800 Subject: [PATCH 069/333] Add terminalTextToSpeech.css for speech-to-text feature --- .../terminal/browser/media/terminal.css | 2 - .../browser/media/terminalTextToSpeech.css | 40 +++++++++++++++++++ .../terminal/browser/terminal.contribution.ts | 1 + .../terminal/browser/terminalSpeechToText.ts | 7 +++- 4 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/browser/media/terminalTextToSpeech.css diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index f044e6b04f448..488815cf2589b 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -565,5 +565,3 @@ .monaco-workbench .xterm.terminal.hide { visibility: hidden; } - - diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminalTextToSpeech.css b/src/vs/workbench/contrib/terminal/browser/media/terminalTextToSpeech.css new file mode 100644 index 0000000000000..bbdfa4fed2726 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/media/terminalTextToSpeech.css @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.terminal-speech-to-text { + background-color: var(--vscode-terminal-background, var(--vscode-panel-background)); + padding: 2px; + border-radius: 8px; + display: flex; + align-items: center; + white-space: nowrap; + z-index: 1000; +} + +.terminal-speech-to-text.codicon.codicon-mic-filled { + display: flex; + align-items: center; + width: 16px; + height: 16px; +} + +.terminal-speech-to-text.recording.codicon.codicon-mic-filled { + color: var(--vscode-activityBarBadge-background); + animation: ani-terminal-speech 1s infinite; +} + +@keyframes ani-terminal-speech { + 0% { + color: var(--vscode-terminalCursor-background); + } + + 50% { + color: var(--vscode-activityBarBadge-background); + } + + 100% { + color: var(--vscode-terminalCursor-background); + } +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 76e54cc361342..f73a9daeafd79 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -8,6 +8,7 @@ import 'vs/css!./media/scrollbar'; import 'vs/css!./media/widgets'; import 'vs/css!./media/xterm'; import 'vs/css!./media/terminal'; +import 'vs/css!./media/terminalTextToSpeech'; import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts index 78001da19a535..8a1d27ac406eb 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts @@ -88,6 +88,7 @@ export class TerminalSpeechToTextSession extends Disposable { })); } stop(send?: boolean): void { + this._setInactive(); if (send) { this._acceptTranscriptionScheduler!.cancel(); this._terminalService.activeInstance?.sendText(this._input, false); @@ -156,10 +157,14 @@ export class TerminalSpeechToTextSession extends Disposable { x: xterm.buffer.active.cursorX ?? 0, }); this._decoration?.onRender((e: HTMLElement) => { - e.classList.add(...ThemeIcon.asClassNameArray(Codicon.mic)); + e.classList.add(...ThemeIcon.asClassNameArray(Codicon.micFilled), 'terminal-speech-to-text', 'recording'); e.style.transform = 'translate(-5px, -5px)'; }); } + + private _setInactive(): void { + this._decoration?.element?.classList.remove('recording'); + } } From 5d941e436f3c38893bd3725418ecdd1476fff2f3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 17 Jan 2024 09:46:03 -0800 Subject: [PATCH 070/333] fix css issue --- .../{terminalTextToSpeech.css => terminalSpeechToText.css} | 0 .../workbench/contrib/terminal/browser/terminal.contribution.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/vs/workbench/contrib/terminal/browser/media/{terminalTextToSpeech.css => terminalSpeechToText.css} (100%) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminalTextToSpeech.css b/src/vs/workbench/contrib/terminal/browser/media/terminalSpeechToText.css similarity index 100% rename from src/vs/workbench/contrib/terminal/browser/media/terminalTextToSpeech.css rename to src/vs/workbench/contrib/terminal/browser/media/terminalSpeechToText.css diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index f73a9daeafd79..87443272043c3 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -8,7 +8,7 @@ import 'vs/css!./media/scrollbar'; import 'vs/css!./media/widgets'; import 'vs/css!./media/xterm'; import 'vs/css!./media/terminal'; -import 'vs/css!./media/terminalTextToSpeech'; +import 'vs/css!./media/terminalSpeechToText'; import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; From c4e96bcada3bded7fb683010e70a209ec68a40b5 Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 17 Jan 2024 18:49:29 +0100 Subject: [PATCH 071/333] Added incompressible element check in CompressibleStickyScrollDelegate (#202675) Added check for incompressible element in CompressibleStickyScrollDelegate --- src/vs/base/browser/ui/tree/objectTree.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index 41903e3281311..a174d01151186 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -199,6 +199,9 @@ class CompressibleStickyScrollDelegate implements IStickyScrollD const compressedNode = this.modelProvider().getCompressedTreeNode(stickyNode.node.element); if (compressedNode.element) { + if (compressedNode.element.incompressible) { + break; + } elements.push(...compressedNode.element.elements); } } From e3af4ea1ee86fe49ddfa121c535231959ae68672 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 17 Jan 2024 10:23:30 -0800 Subject: [PATCH 072/333] add ghost text --- .../browser/media/terminalSpeechToText.css | 7 ++++ .../terminal/browser/terminalSpeechToText.ts | 33 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminalSpeechToText.css b/src/vs/workbench/contrib/terminal/browser/media/terminalSpeechToText.css index bbdfa4fed2726..e227a4c3321f9 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminalSpeechToText.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminalSpeechToText.css @@ -38,3 +38,10 @@ color: var(--vscode-terminalCursor-background); } } + +.terminal-speech-progress-text { + font-style: italic; + color: var(--vscode-editorGhostText-foreground) !important; + border: 1px solid var(--vscode-editorGhostText-border); + z-index: 1000; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts index 8a1d27ac406eb..a548acc987772 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts @@ -19,8 +19,10 @@ import { Codicon } from 'vs/base/common/codicons'; export class TerminalSpeechToTextSession extends Disposable { private _input: string = ''; + private _ghostText: IDecoration | undefined; private _decoration: IDecoration | undefined; private _marker: IXtermMarker | undefined; + private _ghostTextMarker: IXtermMarker | undefined; private static _instance: TerminalSpeechToTextSession | undefined = undefined; private _acceptTranscriptionScheduler: RunOnceScheduler | undefined; static getInstance(instantiationService: IInstantiationService): TerminalSpeechToTextSession { @@ -69,6 +71,7 @@ export class TerminalSpeechToTextSession extends Disposable { break; case SpeechToTextStatus.Recognizing: { this._updateInput(e); + this._renderGhostText(e); if (voiceTimeout > 0) { this._acceptTranscriptionScheduler!.cancel(); } @@ -94,6 +97,9 @@ export class TerminalSpeechToTextSession extends Disposable { this._terminalService.activeInstance?.sendText(this._input, false); } this._marker?.dispose(); + this._ghostTextMarker?.dispose(); + this._ghostText?.dispose(); + this._ghostText = undefined; this._decoration?.dispose(); this._decoration = undefined; this._cancellationTokenSource?.cancel(); @@ -165,6 +171,33 @@ export class TerminalSpeechToTextSession extends Disposable { private _setInactive(): void { this._decoration?.element?.classList.remove('recording'); } + + private _renderGhostText(e: ISpeechToTextEvent): void { + this._ghostText?.dispose(); + const text = e.text; + if (!text) { + return; + } + const activeInstance = this._terminalService.activeInstance; + const xterm = activeInstance?.xterm?.raw; + if (!xterm) { + return; + } + this._ghostTextMarker = activeInstance.registerMarker(); + if (!this._ghostTextMarker) { + return; + } + this._ghostText = xterm.registerDecoration({ + marker: this._ghostTextMarker, + layer: 'top', + x: xterm.buffer.active.cursorX + 1 ?? 0, + }); + this._ghostText?.onRender((e: HTMLElement) => { + e.classList.add('terminal-speech-progress-text'); + e.textContent = text; + e.style.width = 'fit-content'; + }); + } } From 2e3df8a8b11fd6b7887c4df8d91227ef3c7e5413 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 17 Jan 2024 11:15:21 -0800 Subject: [PATCH 073/333] testing: coverage decorations in high contrast, better default behavior for position-only exts (#202676) * testing: coverage decorations in high contrast, better default behavior for position-only exts * update snapshot --- .../lib/stylelint/vscode-known-variables.json | 4 ++- .../browser/codeCoverageDecorations.ts | 2 +- .../contrib/testing/browser/media/testing.css | 16 +++++++++ .../contrib/testing/browser/theme.ts | 34 ++++++++++++------- ..._Decorations_CoverageDetailsModel_4.0.snap | 16 ++++++--- 5 files changed, 53 insertions(+), 19 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index e0fcdff3c6dc6..cbdea6a9ea20f 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -680,8 +680,9 @@ "--vscode-terminalOverviewRuler-findMatchForeground", "--vscode-terminalStickyScroll-background", "--vscode-terminalStickyScrollHover-background", - "--vscode-testing-coveredBackground", "--vscode-testing-coverage-lineHeight", + "--vscode-testing-coveredBackground", + "--vscode-testing-coveredBorder", "--vscode-testing-coveredGutterBackground", "--vscode-testing-iconErrored", "--vscode-testing-iconFailed", @@ -697,6 +698,7 @@ "--vscode-testing-peekHeaderBackground", "--vscode-testing-runAction", "--vscode-testing-uncoveredBackground", + "--vscode-testing-uncoveredBorder", "--vscode-testing-uncoveredGutterBackground", "--vscode-textBlockQuote-background", "--vscode-textBlockQuote-border", diff --git a/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts b/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts index c981e552a9738..ac801660ccde9 100644 --- a/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts @@ -452,7 +452,7 @@ export class CoverageDetailsModel { // and trailing whitespace. function tidyLocation(location: Range | Position): Range { if (location instanceof Position) { - return Range.fromPositions(location); + return Range.fromPositions(location, new Position(location.lineNumber, 0x7FFFFFFF)); } return location; diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index f0cf2ea8a4944..86b858a5d1e30 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -430,10 +430,19 @@ .coverage-deco-gutter.coverage-deco-hit::before { background: var(--vscode-testing-coveredGutterBackground); + border-color: var(--vscode-testing-coveredGutterBackground); } .coverage-deco-gutter.coverage-deco-miss::before { background: var(--vscode-testing-uncoveredGutterBackground); + border-color: var(--vscode-testing-uncoveredGutterBackground); +} + +.hc-light .coverage-deco-gutter::before, +.hc-black .coverage-deco-gutter::before { + border-width: 3px 0 3px 5px; + border-style: solid; + background: none; } .coverage-deco-gutter.coverage-deco-miss.coverage-deco-hit::before { @@ -456,10 +465,17 @@ .coverage-deco-inline.coverage-deco-hit { background: var(--vscode-testing-coveredBackground); + outline: 1px solid var(--vscode-testing-coveredBorder); } .coverage-deco-inline.coverage-deco-miss { background: var(--vscode-testing-uncoveredBackground); + outline: 1px solid var(--vscode-testing-uncoveredBorder); +} + +.hc-light .coverage-deco-inline.coverage-deco-hit, +.hc-black .coverage-deco-inline.coverage-deco-hit { + outline-style: dashed; } .coverage-deco-branch-miss-indicator { diff --git a/src/vs/workbench/contrib/testing/browser/theme.ts b/src/vs/workbench/contrib/testing/browser/theme.ts index a7c8cd7c553f9..fe155816c0d81 100644 --- a/src/vs/workbench/contrib/testing/browser/theme.ts +++ b/src/vs/workbench/contrib/testing/browser/theme.ts @@ -5,7 +5,7 @@ import { Color, RGBA } from 'vs/base/common/color'; import { localize } from 'vs/nls'; -import { contrastBorder, diffInserted, diffRemoved, editorBackground, editorErrorForeground, editorForeground, editorInfoForeground, opaque, registerColor, transparent } from 'vs/platform/theme/common/colorRegistry'; +import { chartsGreen, chartsRed, contrastBorder, diffInserted, diffRemoved, editorBackground, editorErrorForeground, editorForeground, editorInfoForeground, opaque, registerColor, transparent } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { TestMessageType, TestResultState } from 'vs/workbench/contrib/testing/common/testTypes'; @@ -93,11 +93,18 @@ export const testingCoveredBackground = registerColor('testing.coveredBackground hcLight: null }, localize('testing.coveredBackground', 'Background color of text that was covered.')); +export const testingCoveredBorder = registerColor('testing.coveredBorder', { + dark: transparent(testingCoveredBackground, 0.75), + light: transparent(testingCoveredBackground, 0.75), + hcDark: contrastBorder, + hcLight: contrastBorder +}, localize('testing.coveredBorder', 'Border color of text that was covered.')); + export const testingCoveredGutterBackground = registerColor('testing.coveredGutterBackground', { dark: transparent(diffInserted, 0.6), light: transparent(diffInserted, 0.6), - hcDark: null, - hcLight: null + hcDark: chartsGreen, + hcLight: chartsGreen }, localize('testing.coveredGutterBackground', 'Gutter color of regions where code was covered.')); export const testingUncoveredBranchBackground = registerColor('testing.uncoveredBranchBackground', { @@ -114,11 +121,18 @@ export const testingUncoveredBackground = registerColor('testing.uncoveredBackgr hcLight: null }, localize('testing.uncoveredBackground', 'Background color of text that was not covered.')); +export const testingUncoveredBorder = registerColor('testing.uncoveredBorder', { + dark: transparent(testingUncoveredBackground, 0.75), + light: transparent(testingUncoveredBackground, 0.75), + hcDark: contrastBorder, + hcLight: contrastBorder +}, localize('testing.uncoveredBorder', 'Border color of text that was not covered.')); + export const testingUncoveredGutterBackground = registerColor('testing.uncoveredGutterBackground', { dark: transparent(diffRemoved, 1.5), light: transparent(diffRemoved, 1.5), - hcDark: null, - hcLight: null + hcDark: chartsRed, + hcLight: chartsRed }, localize('testing.uncoveredGutterBackground', 'Gutter color of regions where code not covered.')); export const testMessageSeverityColors: { @@ -168,19 +182,13 @@ registerThemingParticipant((theme, collector) => { const missBadgeBackground = editorBg && theme.getColor(testingUncoveredBackground)?.transparent(2).makeOpaque(editorBg); collector.addRule(` - .coverage-deco-inline.coverage-deco-hit { - outline: 1px solid ${theme.getColor(testingCoveredBackground)?.transparent(0.75)}; - } .coverage-deco-inline.coverage-deco-hit.coverage-deco-hovered { background: ${theme.getColor(testingCoveredBackground)?.transparent(1.3)}; - outline: 1px solid ${theme.getColor(testingCoveredBackground)?.transparent(2)}; - } - .coverage-deco-inline.coverage-deco-miss { - outline: 1px solid ${theme.getColor(testingUncoveredBackground)?.transparent(0.75)}; + outline-color: ${theme.getColor(testingCoveredBorder)?.transparent(2)}; } .coverage-deco-inline.coverage-deco-miss.coverage-deco-hovered { background: ${theme.getColor(testingUncoveredBackground)?.transparent(1.3)}; - outline: 1px solid ${theme.getColor(testingUncoveredBackground)?.transparent(2)}; + outline-color: ${theme.getColor(testingUncoveredBorder)?.transparent(2)}; } .coverage-deco-branch-miss-indicator::before { border-color: ${missBadgeBackground?.transparent(1.3)}; diff --git a/src/vs/workbench/contrib/testing/test/browser/__snapshots__/Code_Coverage_Decorations_CoverageDetailsModel_4.0.snap b/src/vs/workbench/contrib/testing/test/browser/__snapshots__/Code_Coverage_Decorations_CoverageDetailsModel_4.0.snap index a7523744be835..f894d982979a5 100644 --- a/src/vs/workbench/contrib/testing/test/browser/__snapshots__/Code_Coverage_Decorations_CoverageDetailsModel_4.0.snap +++ b/src/vs/workbench/contrib/testing/test/browser/__snapshots__/Code_Coverage_Decorations_CoverageDetailsModel_4.0.snap @@ -1,18 +1,26 @@ [ { - range: "[2,0 -> 2,0]", + range: "[1,0 -> 2,0]", + count: 1 + }, + { + range: "[2,0 -> 2,2147483647]", count: 2 }, { - range: "[1,0 -> 4,0]", + range: "[2,2147483647 -> 4,0]", count: 1 }, { - range: "[4,3 -> 4,3]", + range: "[4,0 -> 4,3]", + count: 3 + }, + { + range: "[4,3 -> 4,2147483647]", count: 4 }, { - range: "[4,0 -> 5,0]", + range: "[4,2147483647 -> 5,0]", count: 3 } ] \ No newline at end of file From fe7233fdfb73dc63f3753e6c25795ea8c08a678f Mon Sep 17 00:00:00 2001 From: Robo Date: Thu, 18 Jan 2024 04:26:54 +0900 Subject: [PATCH 074/333] fix: avoid using libc as an executable test (#202680) --- resources/server/bin/helpers/check-requirements-linux.sh | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/resources/server/bin/helpers/check-requirements-linux.sh b/resources/server/bin/helpers/check-requirements-linux.sh index 2322093018964..b366a190abf26 100644 --- a/resources/server/bin/helpers/check-requirements-linux.sh +++ b/resources/server/bin/helpers/check-requirements-linux.sh @@ -81,13 +81,7 @@ if [ -n "$(ldd --version | grep -v musl)" ]; then # Rather than trusting the output of ldd --version (which is not always accurate) # we instead use the version of the cached libc.so.6 file itself. libc_real_path=$(readlink -f "$libc_path") - if [ -x "$libc_real_path" ]; then - # get version from executable - libc_version=$($libc_real_path --version | sed -n 's/.*release version \([0-9]\+\.[0-9]\+\).*/\1/p') - else - # .so is not executable on this host; try getting from strings - libc_version=$(cat "$libc_real_path" | sed -n 's/.*release version \([0-9]\+\.[0-9]\+\).*/\1/p') - fi + libc_version=$(cat "$libc_real_path" | sed -n 's/.*release version \([0-9]\+\.[0-9]\+\).*/\1/p') if [ "$(printf '%s\n' "2.28" "$libc_version" | sort -V | head -n1)" = "2.28" ]; then found_required_glibc=1 else From 48905201c0aeea80012eec4e22bae2bfe743d7dc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 17 Jan 2024 20:58:54 +0100 Subject: [PATCH 075/333] Sanity Bounds for "window.zoomLevel" (fix #152028) (#202682) --- src/vs/platform/window/electron-sandbox/window.ts | 5 +++++ src/vs/workbench/electron-sandbox/actions/windowActions.ts | 7 ++----- src/vs/workbench/electron-sandbox/desktop.contribution.ts | 5 +++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/window/electron-sandbox/window.ts b/src/vs/platform/window/electron-sandbox/window.ts index d8a3301ff3cd7..ab0e479d1a3af 100644 --- a/src/vs/platform/window/electron-sandbox/window.ts +++ b/src/vs/platform/window/electron-sandbox/window.ts @@ -14,11 +14,16 @@ export enum ApplyZoomTarget { ALL_WINDOWS } +export const MAX_ZOOM_LEVEL = 8; +export const MIN_ZOOM_LEVEL = -8; + /** * Apply a zoom level to the window. Also sets it in our in-memory * browser helper so that it can be accessed in non-electron layers. */ export function applyZoom(zoomLevel: number, target: ApplyZoomTarget | Window): void { + zoomLevel = Math.min(Math.max(zoomLevel, MIN_ZOOM_LEVEL), MAX_ZOOM_LEVEL); // cap zoom levels between -8 and 8 + const targetWindows: Window[] = []; if (target === ApplyZoomTarget.ACTIVE_WINDOW) { targetWindows.push(getActiveWindow()); diff --git a/src/vs/workbench/electron-sandbox/actions/windowActions.ts b/src/vs/workbench/electron-sandbox/actions/windowActions.ts index 8f5a4a5f0f07b..2d49534034ce3 100644 --- a/src/vs/workbench/electron-sandbox/actions/windowActions.ts +++ b/src/vs/workbench/electron-sandbox/actions/windowActions.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/actions'; import { URI } from 'vs/base/common/uri'; import { localize, localize2 } from 'vs/nls'; -import { ApplyZoomTarget, applyZoom } from 'vs/platform/window/electron-sandbox/window'; +import { ApplyZoomTarget, MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL, applyZoom } from 'vs/platform/window/electron-sandbox/window'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { getZoomLevel } from 'vs/base/browser/browser'; import { FileKind } from 'vs/platform/files/common/files'; @@ -68,9 +68,6 @@ abstract class BaseZoomAction extends Action2 { private static readonly ZOOM_LEVEL_SETTING_KEY = 'window.zoomLevel'; private static readonly ZOOM_PER_WINDOW_SETTING_KEY = 'window.zoomPerWindow'; - private static readonly MAX_ZOOM_LEVEL = 8; - private static readonly MIN_ZOOM_LEVEL = -8; - constructor(desc: Readonly) { super(desc); } @@ -108,7 +105,7 @@ abstract class BaseZoomAction extends Action2 { level = Math.round(level); // when reaching smallest zoom, prevent fractional zoom levels - if (level > BaseZoomAction.MAX_ZOOM_LEVEL || level < BaseZoomAction.MIN_ZOOM_LEVEL) { + if (level > MAX_ZOOM_LEVEL || level < MIN_ZOOM_LEVEL) { return; // https://github.com/microsoft/vscode/issues/48357 } diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index f5f5772fccd26..3887dd8856a9d 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -27,6 +27,7 @@ import { ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle import { NativeWindow } from 'vs/workbench/electron-sandbox/window'; import { ModifierKeyEmitter } from 'vs/base/browser/dom'; import { applicationConfigurationNodeBase, securityConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sandbox/window'; // Actions (function registerActions(): void { @@ -190,8 +191,8 @@ import { applicationConfigurationNodeBase, securityConfigurationNodeBase } from 'window.zoomLevel': { 'type': 'number', 'default': 0, - 'minimum': -8, - 'maximum': 8, + 'minimum': MIN_ZOOM_LEVEL, + 'maximum': MAX_ZOOM_LEVEL, 'markdownDescription': localize({ comment: ['{0} will be a setting name rendered as a link'], key: 'zoomLevel' }, "Adjust the default zoom level for all windows. Each increment above `0` (e.g. `1`) or below (e.g. `-1`) represents zooming `20%` larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity. See {0} for configuring if the 'Zoom In' and 'Zoom Out' commands apply the zoom level to all windows or only the active window.", '`#window.zoomPerWindow#`'), ignoreSync: true, tags: ['accessibility'] From b0b0c2b0de7b7b179a438ec71171c3fca643898e Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 17 Jan 2024 13:08:21 -0800 Subject: [PATCH 076/333] undo change --- src/vs/workbench/workbench.desktop.main.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 4e56e19903cdf..6ddcd72d1f20b 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -170,9 +170,7 @@ import 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.contributio import 'vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution'; // Chat -import 'vs/workbench/contrib/chat/electron-sandbox/chat.contribution'; -import 'vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution'; - +import 'vs/workbench/contrib/chat/electron-sandbox/voice.contribution'; //#endregion From 27dd62ac4d7b9183884c9a043fe5e315b55131d6 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 17 Jan 2024 13:09:51 -0800 Subject: [PATCH 077/333] undo another change --- .../{voice.contribution.ts => chat.contribution.ts} | 0 src/vs/workbench/workbench.desktop.main.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/vs/workbench/contrib/chat/electron-sandbox/{voice.contribution.ts => chat.contribution.ts} (100%) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/voice.contribution.ts b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts similarity index 100% rename from src/vs/workbench/contrib/chat/electron-sandbox/voice.contribution.ts rename to src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 6ddcd72d1f20b..28715bf1613f1 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -170,7 +170,7 @@ import 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.contributio import 'vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution'; // Chat -import 'vs/workbench/contrib/chat/electron-sandbox/voice.contribution'; +import 'vs/workbench/contrib/chat/electron-sandbox/chat.contribution'; //#endregion From 051c5d0107cd88236834e0e71f0970da4b4adc3c Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 17 Jan 2024 13:14:46 -0800 Subject: [PATCH 078/333] Update src/vs/workbench/contrib/terminal/browser/terminal.ts --- src/vs/workbench/contrib/terminal/browser/terminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 68fc67ea4e1c8..9a1bd1ab1b77c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -789,7 +789,7 @@ export interface ITerminalInstance extends IBaseTerminalInstance { /** * Registers and returns a marker - * @param offset The offset from the cursor + * @param the y offset from the cursor */ registerMarker(offset?: number): IMarker | undefined; From edf9855973766d62516f102a8d56b9d595f2c8a7 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 17 Jan 2024 13:15:50 -0800 Subject: [PATCH 079/333] Update src/vs/workbench/workbench.desktop.main.ts --- src/vs/workbench/workbench.desktop.main.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 28715bf1613f1..4e56e19903cdf 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -171,6 +171,8 @@ import 'vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribu // Chat import 'vs/workbench/contrib/chat/electron-sandbox/chat.contribution'; +import 'vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution'; + //#endregion From 675afadae9044af02ad5eebe181490b187983adc Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 17 Jan 2024 13:44:45 -0800 Subject: [PATCH 080/333] try something --- .../contrib/chat/browser/chatListRenderer.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 3377ae546d0cf..fc10d019a1d29 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -781,8 +781,17 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { + const selection = list.getSelection(); + if (selection.length === 1) { + const selectionElement = selection[0]; + if (selectionElement) { + list.setFocus([selectionElement]); + } + } + }); listDisposables.add(list.onDidOpen((e) => { if (e.element) { this.editorService.openEditor({ From 8807e092d4648896c931fe2d0f94dd732fbf9304 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 17 Jan 2024 14:02:06 -0800 Subject: [PATCH 081/333] try something --- .../contrib/chat/browser/chatListRenderer.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index fc10d019a1d29..efecc0b8a7db9 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -783,15 +783,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { - const selection = list.getSelection(); - if (selection.length === 1) { - const selectionElement = selection[0]; - if (selectionElement) { - list.setFocus([selectionElement]); - } - } - }); listDisposables.add(list.onDidOpen((e) => { if (e.element) { this.editorService.openEditor({ @@ -1243,6 +1234,8 @@ class ContentReferencesListRenderer implements IListRenderer Date: Wed, 17 Jan 2024 14:02:50 -0800 Subject: [PATCH 082/333] try something --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index efecc0b8a7db9..29b1f19538a55 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -1235,7 +1235,7 @@ class ContentReferencesListRenderer implements IListRenderer Date: Wed, 17 Jan 2024 14:07:57 -0800 Subject: [PATCH 083/333] try something --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 29b1f19538a55..43c71b3cdeca8 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -1180,6 +1180,10 @@ class ContentReferencesListPool extends Disposable { [new ContentReferencesListRenderer(resourceLabels)], { alwaysConsumeMouseWheel: false, + accessibilityProvider: { + getAriaLabel: (element: IChatResponseProgressFileTreeData) => element.label, + getWidgetAriaLabel: () => localize('usedReferences', "Used References") + }, }); return list; From 8f5c0d5324565044104566c6b996217425d88603 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 17 Jan 2024 14:26:13 -0800 Subject: [PATCH 084/333] Update src/vs/workbench/contrib/chat/browser/chatListRenderer.ts --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 18c3a6a92f447..ed78c17b82fc4 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -835,7 +835,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer Date: Wed, 17 Jan 2024 15:51:27 -0800 Subject: [PATCH 085/333] Fix markdown smart paste setting check (#202688) --- .../src/languageFeatures/copyFiles/pasteUrlProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts index c051683ce962d..86fa146f1af92 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts @@ -68,7 +68,7 @@ class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider { workspaceEdit.set(document.uri, edit.edits); pasteEdit.additionalEdit = workspaceEdit; - if (!shouldInsertMarkdownLinkByDefault(this._parser, document, pasteUrlSetting, ranges, token)) { + if (!(await shouldInsertMarkdownLinkByDefault(this._parser, document, pasteUrlSetting, ranges, token))) { pasteEdit.yieldTo = [{ mimeType: Mime.textPlain }]; } From 895e218ac87fa536ca104eede0ba408b554354ab Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 17 Jan 2024 19:59:16 -0800 Subject: [PATCH 086/333] register disposables --- .../contrib/terminal/browser/terminalSpeechToText.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts index a548acc987772..81b8fe489eae9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts @@ -33,7 +33,7 @@ export class TerminalSpeechToTextSession extends Disposable { return TerminalSpeechToTextSession._instance; } private _cancellationTokenSource: CancellationTokenSource | undefined; - private _disposables = new DisposableStore(); + private readonly _disposables: DisposableStore; constructor( @ISpeechService private readonly _speechService: ISpeechService, @ITerminalService readonly _terminalService: ITerminalService, @@ -43,6 +43,7 @@ export class TerminalSpeechToTextSession extends Disposable { super(); this._register(this._terminalService.onDidChangeActiveInstance(() => this.stop())); this._register(this._terminalService.onDidDisposeInstance(() => this.stop())); + this._disposables = this._register(new DisposableStore()); } start(): void { @@ -55,7 +56,7 @@ export class TerminalSpeechToTextSession extends Disposable { this._terminalService.activeInstance?.sendText(this._input, false); this.stop(); }, voiceTimeout)); - this._cancellationTokenSource = new CancellationTokenSource(); + this._cancellationTokenSource = this._register(new CancellationTokenSource()); const session = this._disposables.add(this._speechService.createSpeechToTextSession(this._cancellationTokenSource!.token)); this._disposables.add(session.onDidChange((e) => { From b67e7f7b992c5663695e75424abb42b7aaacc805 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 17 Jan 2024 20:03:18 -0800 Subject: [PATCH 087/333] address feedback --- .../terminal/browser/terminalSpeechToText.ts | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts index 81b8fe489eae9..fcdfdce86b7ea 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts @@ -17,6 +17,35 @@ import { IXtermMarker } from 'vs/platform/terminal/common/capabilities/capabilit import { ThemeIcon } from 'vs/base/common/themables'; import { Codicon } from 'vs/base/common/codicons'; +const symbolMap: { [key: string]: string } = { + 'Ampersand': '&', + 'ampersand': '&', + 'Dollar': '$', + 'dollar': '$', + 'Percent': '%', + 'percent': '%', + 'Asterisk': '*', + 'asterisk': '*', + 'Plus': '+', + 'plus': '+', + 'Equals': '=', + 'equals': '=', + 'Exclamation': '!', + 'exclamation': '!', + 'Slash': '/', + 'slash': '/', + 'Backslash': '\\', + 'backslash': '\\', + 'Dot': '.', + 'dot': '.', + 'Period': '.', + 'period': '.', + 'Quote': '\'', + 'quote': '\'', + 'double quote': '"', + 'Double quote': '"', +}; + export class TerminalSpeechToTextSession extends Disposable { private _input: string = ''; private _ghostText: IDecoration | undefined; @@ -111,37 +140,9 @@ export class TerminalSpeechToTextSession extends Disposable { private _updateInput(e: ISpeechToTextEvent): void { if (e.text) { let input = e.text.replaceAll(/[.,?;!]/g, ''); - const symbolMap: { [key: string]: string } = { - 'Ampersand': '&', - 'ampersand': '&', - 'Dollar': '$', - 'dollar': '$', - 'Percent': '%', - 'percent': '%', - 'Asterisk': '*', - 'asterisk': '*', - 'Plus': '+', - 'plus': '+', - 'Equals': '=', - 'equals': '=', - 'Exclamation': '!', - 'exclamation': '!', - 'Slash': '/', - 'slash': '/', - 'Backslash': '\\', - 'backslash': '\\', - 'Dot': '.', - 'dot': '.', - 'Period': '.', - 'period': '.', - 'Quote': '\'', - 'quote': '\'', - 'double quote': '"', - 'Double quote': '"', - }; - for (const symbol in symbolMap) { - const regex: RegExp = new RegExp(symbol); + for (const symbol of Object.values(symbolMap)) { + const regex: RegExp = new RegExp(symbol + '\b'); input = input.replace(regex, symbolMap[symbol]); } this._input = ' ' + input; From 74f9854bca7dccacae890e28fe2242d775f62cbf Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 17 Jan 2024 20:12:27 -0800 Subject: [PATCH 088/333] Refactor symbol replacement logic in TerminalSpeechToTextSession --- .../contrib/terminal/browser/terminalSpeechToText.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts index fcdfdce86b7ea..ec8c71ee36399 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts @@ -140,10 +140,8 @@ export class TerminalSpeechToTextSession extends Disposable { private _updateInput(e: ISpeechToTextEvent): void { if (e.text) { let input = e.text.replaceAll(/[.,?;!]/g, ''); - - for (const symbol of Object.values(symbolMap)) { - const regex: RegExp = new RegExp(symbol + '\b'); - input = input.replace(regex, symbolMap[symbol]); + for (const symbol of Object.entries(symbolMap)) { + input = input.replace(new RegExp('\\b' + symbol[0] + '\\b'), symbol[1]); } this._input = ' ' + input; } From 0b571a26e213bf1ec6266e37152ced80c006bd1e Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 18 Jan 2024 01:14:36 -0300 Subject: [PATCH 089/333] Change ChatAgent history to include real requests, response data, variables, etc. (#202541) * Change ChatAgent history to include real requests, response data, variables, etc. For #199908 * Tweak * Update snapshots * Clean up * Update addCompleteRequest * Filter out other progress types * Fix chat slash command data in history * Don't include slashCommand in history request at all, since it's deprecated anyway * undefined --- .../api/browser/mainThreadChatAgents2.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 13 +++- .../api/common/extHostChatAgents2.ts | 50 ++++++++----- .../api/common/extHostTypeConverters.ts | 73 ++++++++++++++++++- .../contrib/chat/browser/chatQuick.ts | 1 + .../contrib/chat/browser/chatVariables.ts | 8 +- .../contrib/chat/browser/chatWidget.ts | 2 +- .../contrib/chat/common/chatAgents.ts | 15 +++- .../contrib/chat/common/chatModel.ts | 42 +++++++---- .../contrib/chat/common/chatService.ts | 4 +- .../contrib/chat/common/chatServiceImpl.ts | 38 +++++----- .../contrib/chat/common/chatVariables.ts | 9 +-- .../chat/test/browser/chatVariables.test.ts | 8 +- .../__snapshots__/Chat_can_deserialize.0.snap | 4 + .../__snapshots__/Chat_can_serialize.1.snap | 4 + .../chat/test/common/chatModel.test.ts | 2 +- .../chat/test/common/chatService.test.ts | 6 +- .../chat/test/common/mockChatVariables.ts | 8 +- .../browser/inlineChatController.ts | 2 +- .../inlineChat/browser/inlineChatWidget.ts | 2 +- .../vscode.proposed.chatAgents2.d.ts | 19 ++++- .../vscode.proposed.chatAgents2Additions.d.ts | 2 +- 22 files changed, 226 insertions(+), 88 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts index 14f12dc57198b..fc7271c151653 100644 --- a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts +++ b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts @@ -86,7 +86,7 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA invoke: async (request, progress, history, token) => { this._pendingProgress.set(request.requestId, progress); try { - return await this._proxy.$invokeAgent(handle, request.sessionId, request.requestId, request, { history }, token) ?? {}; + return await this._proxy.$invokeAgent(handle, request, { history }, token) ?? {}; } finally { this._pendingProgress.delete(request.requestId); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e622ea5d57e5f..4f789f46598eb 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -51,6 +51,7 @@ import { SaveReason } from 'vs/workbench/common/editor'; import { IRevealOptions, ITreeItem, IViewBadge } from 'vs/workbench/common/views'; import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { IChatAgentCommand, IChatAgentMetadata, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel'; import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } from 'vs/workbench/contrib/chat/common/chatProvider'; import { IChatAsyncContent, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatReplyFollowup, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; @@ -1201,8 +1202,18 @@ export interface IChatAgentCompletionItem { documentation?: string | IMarkdownString; } +export type IChatContentProgressDto = + | Dto> + | IChatAsyncContentDto; + +export type IChatAgentHistoryEntryDto = { + request: IChatAgentRequest; + response: ReadonlyArray; + result: IChatAgentResult; +}; + export interface ExtHostChatAgentsShape2 { - $invokeAgent(handle: number, sessionId: string, requestId: string, request: IChatAgentRequest, context: { history: IChatMessage[] }, token: CancellationToken): Promise; + $invokeAgent(handle: number, request: IChatAgentRequest, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise; $provideSlashCommands(handle: number, token: CancellationToken): Promise; $provideFollowups(handle: number, sessionId: string, token: CancellationToken): Promise; $acceptFeedback(handle: number, sessionId: string, requestId: string, vote: InteractiveSessionVoteDirection, reportIssue?: boolean): void; diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index fe23645281461..7832e02cbb17b 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { coalesce } from 'vs/base/common/arrays'; import { DeferredPromise, raceCancellation } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -14,12 +15,11 @@ import { localize } from 'vs/nls'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { Progress } from 'vs/platform/progress/common/progress'; -import { ExtHostChatAgentsShape2, IChatAgentCompletionItem, IMainContext, MainContext, MainThreadChatAgentsShape2 } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostChatAgentsShape2, IChatAgentCompletionItem, IChatAgentHistoryEntryDto, IMainContext, MainContext, MainThreadChatAgentsShape2 } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider'; import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { IChatAgentCommand, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents'; -import { IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; import { IChatFollowup, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import type * as vscode from 'vscode'; @@ -51,10 +51,10 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { return agent.apiAgent; } - async $invokeAgent(handle: number, sessionId: string, requestId: string, request: IChatAgentRequest, context: { history: IChatMessage[] }, token: CancellationToken): Promise { + async $invokeAgent(handle: number, request: IChatAgentRequest, context: { history: IChatAgentHistoryEntryDto[] }, 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); + this._previousResultMap.delete(request.sessionId); const agent = this._agents.get(handle); if (!agent) { @@ -79,13 +79,10 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { const stopWatch = StopWatch.create(false); let firstProgress: number | undefined; try { + const convertedHistory = await this.prepareHistory(agent, request, context); const task = agent.invoke( - { - prompt: request.message, - variables: typeConvert.ChatVariable.objectTo(request.variables), - slashCommand - }, - { history: context.history.map(typeConvert.ChatMessage.to) }, + typeConvert.ChatAgentRequest.to(request, slashCommand), + { history: convertedHistory }, new Progress(progress => { throwIfDone(); @@ -101,7 +98,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { } if ('placeholder' in progress && 'resolvedContent' in progress) { - const resolvedContent = Promise.all([this._proxy.$handleProgressChunk(requestId, convertedProgress), progress.resolvedContent]); + const resolvedContent = Promise.all([this._proxy.$handleProgressChunk(request.requestId, convertedProgress), progress.resolvedContent]); raceCancellation(resolvedContent, token).then(res => { if (!res) { return; /* Cancelled */ @@ -113,10 +110,10 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { return; } - this._proxy.$handleProgressChunk(requestId, convertedContent, progressHandle ?? undefined); + this._proxy.$handleProgressChunk(request.requestId, convertedContent, progressHandle ?? undefined); }); } else { - this._proxy.$handleProgressChunk(requestId, convertedProgress); + this._proxy.$handleProgressChunk(request.requestId, convertedProgress); } }), token @@ -124,18 +121,18 @@ 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); + this._previousResultMap.set(request.sessionId, result); + let sessionResults = this._resultsBySessionAndRequestId.get(request.sessionId); if (!sessionResults) { sessionResults = new Map(); - this._resultsBySessionAndRequestId.set(sessionId, sessionResults); + this._resultsBySessionAndRequestId.set(request.sessionId, sessionResults); } - sessionResults.set(requestId, result); + sessionResults.set(request.requestId, result); const timings = { firstProgress: firstProgress, totalElapsed: stopWatch.elapsed() }; return { errorDetails: result.errorDetails, timings }; } else { - this._previousResultMap.delete(sessionId); + this._previousResultMap.delete(request.sessionId); } return undefined; @@ -151,6 +148,19 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { } } + private async prepareHistory(agent: ExtHostChatAgent, request: IChatAgentRequest, context: { history: IChatAgentHistoryEntryDto[] }): Promise { + return coalesce(await Promise.all(context.history + .map(async h => { + const result = request.agentId === h.request.agentId && this._resultsBySessionAndRequestId.get(request.sessionId)?.get(h.request.requestId) + || h.result; + return { + request: typeConvert.ChatAgentRequest.to(h.request, undefined), + response: coalesce(h.response.map(r => typeConvert.ChatResponseProgress.toProgressContent(r))), + result + } satisfies vscode.ChatAgentHistoryEntry; + }))); + } + $releaseSession(sessionId: string): void { this._previousResultMap.delete(sessionId); this._resultsBySessionAndRequestId.delete(sessionId); @@ -248,7 +258,7 @@ class ExtHostChatAgent { constructor( public readonly extension: IExtensionDescription, - private readonly _id: string, + public readonly id: string, private readonly _proxy: MainThreadChatAgentsShape2, private readonly _handle: number, private readonly _callback: vscode.ChatAgentExtendedHandler, @@ -352,7 +362,7 @@ class ExtHostChatAgent { const that = this; return { get name() { - return that._id; + return that.id; }, get description() { return that._description ?? ''; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 9a12f53fb5312..2170b91a05399 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -11,7 +11,7 @@ import * as htmlContent from 'vs/base/common/htmlContent'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { ResourceMap, ResourceSet } from 'vs/base/common/map'; import { marked } from 'vs/base/common/marked/marked'; -import { parse } from 'vs/base/common/marshalling'; +import { parse, revive } from 'vs/base/common/marshalling'; import { Mimes } from 'vs/base/common/mime'; import { cloneAndChange } from 'vs/base/common/objects'; import { isEmptyObject, isNumber, isString, isUndefinedOrNull } from 'vs/base/common/types'; @@ -34,6 +34,7 @@ import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol'; import { getPrivateApiFor } from 'vs/workbench/api/common/extHostTestingPrivateApi'; import { DEFAULT_EDITOR_ASSOCIATION, SaveReason } from 'vs/workbench/common/editor'; import { IViewBadge } from 'vs/workbench/common/views'; +import { IChatAgentRequest } from 'vs/workbench/contrib/chat/common/chatAgents'; import * as chatProvider from 'vs/workbench/contrib/chat/common/chatProvider'; import { IChatFollowup, IChatReplyFollowup, IChatResponseCommandFollowup } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; @@ -46,6 +47,7 @@ import { CoverageDetails, DetailType, ICoveredCount, IFileCoverage, ISerializedT import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import type * as vscode from 'vscode'; import * as types from './extHostTypes'; @@ -120,6 +122,12 @@ export namespace Range { } } +export namespace Location { + export function to(location: Dto): vscode.Location { + return new types.Location(URI.revive(location.uri), Range.to(location.range)); + } +} + export namespace TokenType { export function to(type: encodedTokenAttributes.StandardTokenType): types.StandardTokenType { switch (type) { @@ -2368,6 +2376,69 @@ export namespace ChatResponseProgress { return undefined; } } + + export function to(progress: extHostProtocol.IChatProgressDto): vscode.ChatAgentProgress | undefined { + switch (progress.kind) { + case 'markdownContent': + case 'inlineReference': + case 'treeData': + return ChatResponseProgress.to(progress); + case 'content': + return { content: progress.content }; + case 'usedContext': + return { documents: progress.documents.map(d => ({ uri: URI.revive(d.uri), version: d.version, ranges: d.ranges.map(r => Range.to(r)) })) }; + case 'reference': + return { + reference: + isUriComponents(progress.reference) ? + URI.revive(progress.reference) : + Location.to(progress.reference) + }; + case 'agentDetection': + // For simplicity, don't sent back the 'extended' types + return undefined; + case 'progressMessage': + return { message: progress.content.value }; + case 'vulnerability': + return { content: progress.content, vulnerabilities: progress.vulnerabilities }; + default: + // Unknown type, eg something in history that was removed? Ignore + return undefined; + } + } + + export function toProgressContent(progress: extHostProtocol.IChatContentProgressDto): vscode.ChatAgentContentProgress | undefined { + switch (progress.kind) { + case 'markdownContent': + // For simplicity, don't sent back the 'extended' types, so downgrade markdown to just some text + return { content: progress.content.value }; + case 'inlineReference': + return { + inlineReference: + isUriComponents(progress.inlineReference) ? + URI.revive(progress.inlineReference) : + Location.to(progress.inlineReference), + title: progress.name + }; + case 'treeData': + return { treeData: revive(progress.treeData) }; + default: + // Unknown type, eg something in history that was removed? Ignore + return undefined; + } + } +} + +export namespace ChatAgentRequest { + export function to(request: IChatAgentRequest, slashCommand: vscode.ChatAgentSlashCommand | undefined): vscode.ChatAgentRequest { + return { + prompt: request.message, + variables: ChatVariable.objectTo(request.variables), + slashCommand, + subCommand: request.command, + agentId: request.agentId, + }; + } } export namespace ChatAgentCompletionItem { diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index c4e06196df285..a23257610dc32 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -292,6 +292,7 @@ class QuickChat extends Disposable { if (request.response?.response.value || request.response?.errorDetails) { this.chatService.addCompleteRequest(widget.viewModel.sessionId, request.message as IParsedChatRequest, + request.variableData, { message: request.response.response.value, errorDetails: request.response.errorDetails, diff --git a/src/vs/workbench/contrib/chat/browser/chatVariables.ts b/src/vs/workbench/contrib/chat/browser/chatVariables.ts index 578d53696673b..3bb6fa6b7cabf 100644 --- a/src/vs/workbench/contrib/chat/browser/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/browser/chatVariables.ts @@ -9,9 +9,9 @@ import { Iterable } from 'vs/base/common/iterator'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { ChatDynamicVariableModel } from 'vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables'; -import { IChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; +import { IChatModel, IChatRequestVariableData } from 'vs/workbench/contrib/chat/common/chatModel'; import { IParsedChatRequest, ChatRequestVariablePart, ChatRequestDynamicVariablePart } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChatVariablesService, IChatRequestVariableValue, IChatVariableData, IChatVariableResolver, IChatVariableResolveResult, IDynamicVariable } from 'vs/workbench/contrib/chat/common/chatVariables'; +import { IChatVariablesService, IChatRequestVariableValue, IChatVariableData, IChatVariableResolver, IDynamicVariable } from 'vs/workbench/contrib/chat/common/chatVariables'; interface IChatData { data: IChatVariableData; @@ -28,7 +28,7 @@ export class ChatVariablesService implements IChatVariablesService { ) { } - async resolveVariables(prompt: IParsedChatRequest, model: IChatModel, token: CancellationToken): Promise { + async resolveVariables(prompt: IParsedChatRequest, model: IChatModel, token: CancellationToken): Promise { const resolvedVariables: Record = {}; const jobs: Promise[] = []; @@ -62,7 +62,7 @@ export class ChatVariablesService implements IChatVariablesService { return { variables: resolvedVariables, - prompt: parsedPrompt.join('').trim() + message: parsedPrompt.join('').trim() }; } diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index b4c02dc0b01a4..745ec7e1c0472 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -539,7 +539,7 @@ export class ChatWidget extends Disposable implements IChatWidget { const input = !opts ? editorValue : 'query' in opts ? opts.query : `${opts.prefix} ${editorValue}`; - const isUserQuery = !opts || 'query' in opts; + const isUserQuery = !opts || 'prefix' in opts; const result = await this.chatService.sendRequest(this.viewModel.sessionId, input); if (result) { diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index 157e4be57a264..bab428dd38c7a 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -11,7 +11,7 @@ import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle' import { ThemeIcon } from 'vs/base/common/themables'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; +import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel'; import { IChatFollowup, IChatProgress, IChatResponseErrorDetails } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; @@ -22,8 +22,14 @@ export interface IChatAgentData { metadata: IChatAgentMetadata; } +export interface IChatAgentHistoryEntry { + request: IChatAgentRequest; + response: ReadonlyArray; + result: IChatAgentResult; +} + export interface IChatAgent extends IChatAgentData { - invoke(request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatMessage[], token: CancellationToken): Promise; + invoke(request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise; provideFollowups?(sessionId: string, token: CancellationToken): Promise; provideSlashCommands(token: CancellationToken): Promise; } @@ -72,6 +78,7 @@ export interface IChatAgentMetadata { export interface IChatAgentRequest { sessionId: string; requestId: string; + agentId: string; command?: string; message: string; variables: Record; @@ -93,7 +100,7 @@ export interface IChatAgentService { _serviceBrand: undefined; readonly onDidChangeAgents: Event; registerAgent(agent: IChatAgent): IDisposable; - invokeAgent(id: string, request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatMessage[], token: CancellationToken): Promise; + invokeAgent(id: string, request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise; getFollowups(id: string, sessionId: string, token: CancellationToken): Promise; getAgents(): Array; getAgent(id: string): IChatAgent | undefined; @@ -163,7 +170,7 @@ export class ChatAgentService extends Disposable implements IChatAgentService { return data?.agent; } - async invokeAgent(id: string, request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatMessage[], token: CancellationToken): Promise { + async invokeAgent(id: string, request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise { const data = this._agents.get(id); if (!data) { throw new Error(`No agent with id ${id}`); diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 503cdb3b762c4..0a3ebbaa0c857 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -17,6 +17,16 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IChatAgentCommand, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { IChat, IChatAgentMarkdownContentWithVulnerability, IChatAsyncContent, IChatContent, IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatProgress, IChatProgressMessage, IChatReplyFollowup, IChatResponse, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatTreeData, IChatUsedContext, InteractiveSessionVoteDirection, isIUsedContext } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; + +export interface IChatRequestVariableData { + /** + * The user's message with variable references for extension API. + */ + message: string; + + variables: Record; +} export interface IChatRequestModel { readonly id: string; @@ -24,7 +34,8 @@ export interface IChatRequestModel { readonly avatarIconUri?: URI; readonly session: IChatModel; readonly message: IParsedChatRequest | IChatReplyFollowup; - readonly response: IChatResponseModel | undefined; + readonly variableData: IChatRequestVariableData; + readonly response?: IChatResponseModel; } export type IChatProgressResponseContent = @@ -84,7 +95,8 @@ export class ChatRequestModel implements IChatRequestModel { constructor( public readonly session: ChatModel, - public readonly message: IParsedChatRequest) { + public readonly message: IParsedChatRequest, + public readonly variableData: IChatRequestVariableData) { this._id = 'request_' + ChatRequestModel.nextId++; } } @@ -239,12 +251,10 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel private _followups?: IChatFollowup[]; - private _agent: IChatAgentData | undefined; public get agent(): IChatAgentData | undefined { return this._agent; } - private _slashCommand: IChatAgentCommand | undefined; public get slashCommand(): IChatAgentCommand | undefined { return this._slashCommand; } @@ -267,7 +277,8 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel constructor( _response: IMarkdownString | ReadonlyArray, public readonly session: ChatModel, - agent: IChatAgentData | undefined, + private _agent: IChatAgentData | undefined, + private _slashCommand: IChatAgentCommand | undefined, public readonly requestId: string, private _isComplete: boolean = false, private _isCanceled = false, @@ -276,7 +287,6 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel followups?: ReadonlyArray ) { super(); - this._agent = agent; this._followups = followups ? [...followups] : undefined; this._response = new Response(_response); this._register(this._response.onDidChangeValue(() => this._onDidChange.fire())); @@ -364,7 +374,8 @@ export interface ISerializableChatsData { export type ISerializableChatAgentData = UriDto; export interface ISerializableChatRequestData { - message: string | IParsedChatRequest; + message: string | IParsedChatRequest; // string => old format + variableData: IChatRequestVariableData; // make optional response: ReadonlyArray | undefined; agent?: ISerializableChatAgentData; slashCommand?: IChatAgentCommand; @@ -549,11 +560,13 @@ export class ChatModel extends Disposable implements IChatModel { typeof raw.message === 'string' ? this.getParsedRequestFromString(raw.message) : reviveParsedChatRequest(raw.message); - const request = new ChatRequestModel(this, parsedRequest); + // Only old messages don't have variableData + const variableData: IChatRequestVariableData = raw.variableData ?? { message: parsedRequest.text, variables: {} }; + const request = new ChatRequestModel(this, parsedRequest, variableData); if (raw.response || raw.responseErrorDetails) { const agent = (raw.agent && 'metadata' in raw.agent) ? // Check for the new format, ignore entries in the old format revive(raw.agent) : undefined; - request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, request.id, true, raw.isCanceled, raw.vote, raw.responseErrorDetails, raw.followups); + request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, raw.slashCommand, request.id, true, raw.isCanceled, raw.vote, raw.responseErrorDetails, raw.followups); if (raw.usedContext) { // @ulugbekna: if this's a new vscode sessions, doc versions are incorrect anyway? request.response.applyReference(raw.usedContext); } @@ -627,13 +640,13 @@ export class ChatModel extends Disposable implements IChatModel { return this._requests; } - addRequest(message: IParsedChatRequest, chatAgent?: IChatAgentData): ChatRequestModel { + addRequest(message: IParsedChatRequest, variableData: IChatRequestVariableData, chatAgent?: IChatAgentData, slashCommand?: IChatAgentCommand): ChatRequestModel { if (!this._session) { throw new Error('addRequest: No session'); } - const request = new ChatRequestModel(this, message); - request.response = new ChatResponseModel([], this, chatAgent, request.id); + const request = new ChatRequestModel(this, message, variableData); + request.response = new ChatResponseModel([], this, chatAgent, slashCommand, request.id); this._requests.push(request); this._onDidChange.fire({ kind: 'addRequest', request }); @@ -646,7 +659,7 @@ export class ChatModel extends Disposable implements IChatModel { } if (!request.response) { - request.response = new ChatResponseModel([], this, undefined, request.id); + request.response = new ChatResponseModel([], this, undefined, undefined, request.id); } if (request.response.isComplete) { @@ -693,7 +706,7 @@ export class ChatModel extends Disposable implements IChatModel { } if (!request.response) { - request.response = new ChatResponseModel([], this, undefined, request.id); + request.response = new ChatResponseModel([], this, undefined, undefined, request.id); } request.response.setErrorDetails(rawResponse.errorDetails); @@ -737,6 +750,7 @@ export class ChatModel extends Disposable implements IChatModel { requests: this._requests.map((r): ISerializableChatRequestData => { return { message: r.message, + variableData: r.variableData, response: r.response ? r.response.response.value.map(item => { // Keeping the shape of the persisted data the same for back compat diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 8239ec32294f8..f16e5f1ce39f2 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -13,7 +13,7 @@ import { Location, ProviderResult } from 'vs/editor/common/languages'; import { FileType } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IChatAgentCommand, IChatAgentData } from 'vs/workbench/contrib/chat/common/chatAgents'; -import { ChatModel, IChatModel, ISerializableChatData } from 'vs/workbench/contrib/chat/common/chatModel'; +import { ChatModel, IChatModel, IChatRequestVariableData, ISerializableChatData } from 'vs/workbench/contrib/chat/common/chatModel'; import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; @@ -311,7 +311,7 @@ export interface IChatService { removeRequest(sessionid: string, requestId: string): Promise; cancelCurrentRequestForSession(sessionId: string): void; clearSession(sessionId: string): void; - addCompleteRequest(sessionId: string, message: IParsedChatRequest | string, response: IChatCompleteResponse): void; + addCompleteRequest(sessionId: string, message: IParsedChatRequest | string, variableData: IChatRequestVariableData | undefined, response: IChatCompleteResponse): void; sendRequestToProvider(sessionId: string, message: IChatDynamicRequest): void; getHistory(): IChatDetail[]; clearAllHistoryEntries(): void; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 1de767f97a06c..77db6de1d0729 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -20,13 +20,13 @@ import { Progress } from 'vs/platform/progress/common/progress'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IChatAgentCommand, IChatAgentData, IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatAgentCommand, IChatAgentData, IChatAgentHistoryEntry, IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; -import { ChatModel, ChatModelInitState, ChatRequestModel, ChatWelcomeMessageModel, IChatModel, ISerializableChatData, ISerializableChatsData } from 'vs/workbench/contrib/chat/common/chatModel'; +import { ChatModel, ChatModelInitState, ChatRequestModel, ChatWelcomeMessageModel, IChatModel, IChatRequestVariableData, ISerializableChatData, ISerializableChatsData } from 'vs/workbench/contrib/chat/common/chatModel'; import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { ChatMessageRole, IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser'; -import { IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatProvider, IChatProviderInfo, IChatResponse, IChatService, IChatTransferredSessionData, IChatUserActionEvent, ChatAgentCopyKind, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; +import { ChatAgentCopyKind, IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatProvider, IChatProviderInfo, IChatResponse, IChatService, IChatTransferredSessionData, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -514,29 +514,33 @@ export class ChatService extends Disposable implements IChatService { const defaultAgent = this.chatAgentService.getDefaultAgent(); if (agentPart || (defaultAgent && !commandPart)) { const agent = (agentPart?.agent ?? defaultAgent)!; - const history: IChatMessage[] = []; + const history: IChatAgentHistoryEntry[] = []; for (const request of model.getRequests()) { if (!request.response) { continue; } - history.push({ role: ChatMessageRole.User, content: request.message.text }); - history.push({ role: ChatMessageRole.Assistant, content: request.response.response.asString() }); + const historyRequest: IChatAgentRequest = { + sessionId, + requestId: request.id, + agentId: request.response.agent?.id ?? '', + message: request.variableData.message, + variables: request.variableData.variables, + command: request.response.slashCommand?.name + }; + history.push({ request: historyRequest, response: request.response.response.value, result: { errorDetails: request.response.errorDetails } }); } - request = model.addRequest(parsedRequest, agent); + const variableData = await this.chatVariablesService.resolveVariables(parsedRequest, model, token); + request = model.addRequest(parsedRequest, variableData, agent, agentSlashCommandPart?.command); const requestProps: IChatAgentRequest = { sessionId, requestId: request.id, - message, - variables: {}, + agentId: agent.id, + message: variableData.message, + variables: variableData.variables, command: agentSlashCommandPart?.command.name ?? '', }; - if ('parts' in parsedRequest) { - const varResult = await this.chatVariablesService.resolveVariables(parsedRequest, model, token); - requestProps.variables = varResult.variables; - requestProps.message = varResult.prompt; - } const agentResult = await this.chatAgentService.invokeAgent(agent.id, requestProps, progressCallback, history, token); rawResponse = { @@ -547,7 +551,7 @@ export class ChatService extends Disposable implements IChatService { agentOrCommandFollowups = agentResult?.followUp ? Promise.resolve(agentResult.followUp) : this.chatAgentService.getFollowups(agent.id, sessionId, CancellationToken.None); } else if (commandPart && this.chatSlashCommandService.hasCommand(commandPart.slashCommand.command)) { - request = model.addRequest(parsedRequest); + request = model.addRequest(parsedRequest, { message, variables: {} }); // contributed slash commands // TODO: spell this out in the UI const history: IChatMessage[] = []; @@ -639,7 +643,7 @@ export class ChatService extends Disposable implements IChatService { return Array.from(this._providers.keys()); } - async addCompleteRequest(sessionId: string, message: IParsedChatRequest | string, response: IChatCompleteResponse): Promise { + async addCompleteRequest(sessionId: string, message: IParsedChatRequest | string, variableData: IChatRequestVariableData | undefined, response: IChatCompleteResponse): Promise { this.trace('addCompleteRequest', `message: ${message}`); const model = this._sessionModels.get(sessionId); @@ -651,7 +655,7 @@ export class ChatService extends Disposable implements IChatService { const parsedRequest = typeof message === 'string' ? await this.instantiationService.createInstance(ChatRequestParser).parseChatRequest(sessionId, message) : message; - const request = model.addRequest(parsedRequest); + const request = model.addRequest(parsedRequest, variableData || { message: parsedRequest.text, variables: {} }); if (typeof response.message === 'string') { model.acceptResponseProgress(request, { content: response.message, kind: 'content' }); } else { diff --git a/src/vs/workbench/contrib/chat/common/chatVariables.ts b/src/vs/workbench/contrib/chat/common/chatVariables.ts index 951e5bcf16137..f95b9da120e79 100644 --- a/src/vs/workbench/contrib/chat/common/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/common/chatVariables.ts @@ -8,7 +8,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IRange } from 'vs/editor/common/core/range'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; +import { IChatModel, IChatRequestVariableData } from 'vs/workbench/contrib/chat/common/chatModel'; import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; export interface IChatVariableData { @@ -42,12 +42,7 @@ export interface IChatVariablesService { /** * Resolves all variables that occur in `prompt` */ - resolveVariables(prompt: IParsedChatRequest, model: IChatModel, token: CancellationToken): Promise; -} - -export interface IChatVariableResolveResult { - variables: Record; - prompt: string; + resolveVariables(prompt: IParsedChatRequest, model: IChatModel, token: CancellationToken): Promise; } export interface IDynamicVariable { diff --git a/src/vs/workbench/contrib/chat/test/browser/chatVariables.test.ts b/src/vs/workbench/contrib/chat/test/browser/chatVariables.test.ts index 81517985bde8a..bda3f40baac91 100644 --- a/src/vs/workbench/contrib/chat/test/browser/chatVariables.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/chatVariables.test.ts @@ -49,13 +49,13 @@ suite('ChatVariables', function () { const data = await resolveVariables('Hello #foo and#far'); assert.strictEqual(Object.keys(data.variables).length, 1); assert.deepEqual(Object.keys(data.variables).sort(), ['foo']); - assert.strictEqual(data.prompt, 'Hello [#foo](values:foo) and#far'); + assert.strictEqual(data.message, 'Hello [#foo](values:foo) and#far'); } { const data = await resolveVariables('#foo Hello'); assert.strictEqual(Object.keys(data.variables).length, 1); assert.deepEqual(Object.keys(data.variables).sort(), ['foo']); - assert.strictEqual(data.prompt, '[#foo](values:foo) Hello'); + assert.strictEqual(data.message, '[#foo](values:foo) Hello'); } { const data = await resolveVariables('Hello #foo'); @@ -66,7 +66,7 @@ suite('ChatVariables', function () { const data = await resolveVariables('Hello #foo?'); assert.strictEqual(Object.keys(data.variables).length, 1); assert.deepEqual(Object.keys(data.variables).sort(), ['foo']); - assert.strictEqual(data.prompt, 'Hello [#foo](values:foo)?'); + assert.strictEqual(data.message, 'Hello [#foo](values:foo)?'); } { const data = await resolveVariables('Hello #foo and#far #foo'); @@ -82,7 +82,7 @@ suite('ChatVariables', function () { const data = await resolveVariables('Hello #foo and #far #foo #unknown'); assert.strictEqual(Object.keys(data.variables).length, 2); assert.deepEqual(Object.keys(data.variables).sort(), ['far', 'foo']); - assert.strictEqual(data.prompt, 'Hello [#foo](values:foo) and [#far](values:far) [#foo](values:foo) #unknown'); + assert.strictEqual(data.message, 'Hello [#foo](values:foo) and [#far](values:far) [#foo](values:foo) #unknown'); } v1.dispose(); diff --git a/src/vs/workbench/contrib/chat/test/common/__snapshots__/Chat_can_deserialize.0.snap b/src/vs/workbench/contrib/chat/test/common/__snapshots__/Chat_can_deserialize.0.snap index 223b9763bfe62..e9ee90ae0210f 100644 --- a/src/vs/workbench/contrib/chat/test/common/__snapshots__/Chat_can_deserialize.0.snap +++ b/src/vs/workbench/contrib/chat/test/common/__snapshots__/Chat_can_deserialize.0.snap @@ -44,6 +44,10 @@ } ] }, + variableData: { + message: "@ChatProviderWithUsedContext test request", + variables: { } + }, response: [ ], responseErrorDetails: undefined, followups: [ ], diff --git a/src/vs/workbench/contrib/chat/test/common/__snapshots__/Chat_can_serialize.1.snap b/src/vs/workbench/contrib/chat/test/common/__snapshots__/Chat_can_serialize.1.snap index fbeb0fa885f84..a332c9b3af709 100644 --- a/src/vs/workbench/contrib/chat/test/common/__snapshots__/Chat_can_serialize.1.snap +++ b/src/vs/workbench/contrib/chat/test/common/__snapshots__/Chat_can_serialize.1.snap @@ -44,6 +44,10 @@ ], text: "@ChatProviderWithUsedContext test request" }, + variableData: { + message: "@ChatProviderWithUsedContext test request", + variables: { } + }, response: [ ], responseErrorDetails: undefined, followups: [ ], diff --git a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts index 601973329451f..7ffcef4448a58 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts @@ -112,7 +112,7 @@ suite('ChatModel', () => { model.startInitialize(); model.initialize({} as any, undefined); const text = 'hello'; - model.addRequest({ text, parts: [new ChatRequestTextPart(new OffsetRange(0, text.length), new Range(1, text.length, 1, text.length), text)] }); + model.addRequest({ text, parts: [new ChatRequestTextPart(new OffsetRange(0, text.length), new Range(1, text.length, 1, text.length), text)] }, { message: text, variables: {} }); const requests = model.getRequests(); assert.strictEqual(requests.length, 1); diff --git a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts index a69148ce36091..4e69d04418eb2 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts @@ -123,11 +123,11 @@ suite('Chat', () => { const session1 = testDisposables.add(testService.startSession('provider1', CancellationToken.None)); await session1.waitForInitialization(); - session1!.addRequest({ parts: [], text: 'request 1' }); + session1!.addRequest({ parts: [], text: 'request 1' }, { message: 'request 1', variables: {} }); const session2 = testDisposables.add(testService.startSession('provider2', CancellationToken.None)); await session2.waitForInitialization(); - session2!.addRequest({ parts: [], text: 'request 2' }); + session2!.addRequest({ parts: [], text: 'request 2' }, { message: 'request 2', variables: {} }); storageService.flush(); const testService2 = testDisposables.add(instantiationService.createInstance(ChatService)); @@ -208,7 +208,7 @@ suite('Chat', () => { const model = testDisposables.add(testService.startSession('testProvider', CancellationToken.None)); assert.strictEqual(model.getRequests().length, 0); - await testService.addCompleteRequest(model.sessionId, 'test request', { message: 'test response' }); + await testService.addCompleteRequest(model.sessionId, 'test request', undefined, { message: 'test response' }); assert.strictEqual(model.getRequests().length, 1); assert.ok(model.getRequests()[0].response); assert.strictEqual(model.getRequests()[0].response?.response.asString(), 'test response'); diff --git a/src/vs/workbench/contrib/chat/test/common/mockChatVariables.ts b/src/vs/workbench/contrib/chat/test/common/mockChatVariables.ts index f13d1154145fb..73d64ca339b94 100644 --- a/src/vs/workbench/contrib/chat/test/common/mockChatVariables.ts +++ b/src/vs/workbench/contrib/chat/test/common/mockChatVariables.ts @@ -5,9 +5,9 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; +import { IChatModel, IChatRequestVariableData } from 'vs/workbench/contrib/chat/common/chatModel'; import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChatVariableData, IChatVariableResolveResult, IChatVariableResolver, IChatVariablesService, IDynamicVariable } from 'vs/workbench/contrib/chat/common/chatVariables'; +import { IChatVariableData, IChatVariableResolver, IChatVariablesService, IDynamicVariable } from 'vs/workbench/contrib/chat/common/chatVariables'; export class MockChatVariablesService implements IChatVariablesService { _serviceBrand: undefined; @@ -27,9 +27,9 @@ export class MockChatVariablesService implements IChatVariablesService { return []; } - async resolveVariables(prompt: IParsedChatRequest, model: IChatModel, token: CancellationToken): Promise { + async resolveVariables(prompt: IParsedChatRequest, model: IChatModel, token: CancellationToken): Promise { return { - prompt: prompt.text, + message: prompt.text, variables: {} }; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index be3aa770913e6..4a7fce76f9623 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -1190,7 +1190,7 @@ async function showMessageResponse(accessor: ServicesAccessor, query: string, re const chatWidgetService = accessor.get(IChatWidgetService); const widget = await chatWidgetService.revealViewForProvider(providerId); if (widget && widget.viewModel) { - chatService.addCompleteRequest(widget.viewModel.sessionId, query, { message: response }); + chatService.addCompleteRequest(widget.viewModel.sessionId, query, undefined, { message: response }); widget.focusLastMessage(); } } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index f0509750b6344..fa5e8dc762ce4 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -597,7 +597,7 @@ export class InlineChatWidget { expansionState = ExpansionState.NOT_CROPPED; } else { const sessionModel = this._chatMessageDisposables.add(new ChatModel(message.providerId, undefined, this._logService, this._chatAgentService)); - const responseModel = this._chatMessageDisposables.add(new ChatResponseModel(message.message, sessionModel, undefined, message.requestId, !isIncomplete, false, undefined)); + const responseModel = this._chatMessageDisposables.add(new ChatResponseModel(message.message, sessionModel, undefined, undefined, message.requestId, !isIncomplete, false, undefined)); const viewModel = this._chatMessageDisposables.add(new ChatResponseViewModel(responseModel, this._logService)); const renderOptions: IChatListItemRendererOptions = { renderStyle: 'compact', noHeader: true, noPadding: true }; const chatRendererDelegate: IChatRendererDelegate = { getListLength() { return 1; } }; diff --git a/src/vscode-dts/vscode.proposed.chatAgents2.d.ts b/src/vscode-dts/vscode.proposed.chatAgents2.d.ts index 172f4df6d676b..bcc565500c9d7 100644 --- a/src/vscode-dts/vscode.proposed.chatAgents2.d.ts +++ b/src/vscode-dts/vscode.proposed.chatAgents2.d.ts @@ -5,11 +5,17 @@ declare module 'vscode' { + export interface ChatAgentHistoryEntry { + request: ChatAgentRequest; + response: ChatAgentContentProgress[]; + result: ChatAgentResult2; + } + export interface ChatAgentContext { /** * All of the chat messages so far in the current chat session. */ - history: ChatMessage[]; + history: ChatAgentHistoryEntry[]; } /** @@ -241,12 +247,23 @@ declare module 'vscode' { */ prompt: string; + /** + * The ID of the chat agent to which this request was directed. + */ + agentId: string; + /** * The {@link ChatAgentSlashCommand slash command} that was selected for this request. It is guaranteed that the passed slash * command is an instance that was previously returned from the {@link ChatAgentSlashCommandProvider.provideSlashCommands slash command provider}. + * @deprecated this will be replaced by `subCommand` */ slashCommand?: ChatAgentSlashCommand; + /** + * The name of the {@link ChatAgentSlashCommand slash command} that was selected for this request. + */ + subCommand?: string; + variables: Record; } diff --git a/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts b/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts index 146ba3bbe92fa..2794d273dd864 100644 --- a/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts +++ b/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts @@ -111,7 +111,7 @@ declare module 'vscode' { export interface ChatAgentCommandAction { // eslint-disable-next-line local/vscode-dts-string-type-literals kind: 'command'; - command: any; // ChatAgentCommandButton; + command: ChatAgentCommandFollowup; } export interface ChatAgentSessionFollowupAction { From 3695bca69f984867060244fb058c46d89865d942 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 18 Jan 2024 11:55:15 +0530 Subject: [PATCH 090/333] fix #199736 (#202703) --- .../configuration/browser/configurationService.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 58761151288e1..4d27af9fb2d0c 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -104,7 +104,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat private readonly configurationRegistry: IConfigurationRegistry; private instantiationService: IInstantiationService | undefined; - private configurationEditing: ConfigurationEditing | undefined; + private configurationEditing: Promise | undefined; constructor( { remoteAuthority, configurationCache }: { remoteAuthority?: string; configurationCache: IConfigurationCache }, @@ -1030,8 +1030,8 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat } // Use same instance of ConfigurationEditing to make sure all writes go through the same queue - this.configurationEditing = this.configurationEditing ?? this.instantiationService.createInstance(ConfigurationEditing, (await this.remoteAgentService.getEnvironment())?.settingsPath ?? null); - await this.configurationEditing.writeConfiguration(editableConfigurationTarget, { key, value }, { scopes: overrides, ...options }); + this.configurationEditing = this.configurationEditing ?? this.createConfigurationEditingService(this.instantiationService); + await (await this.configurationEditing).writeConfiguration(editableConfigurationTarget, { key, value }, { scopes: overrides, ...options }); switch (editableConfigurationTarget) { case EditableConfigurationTarget.USER_LOCAL: if (this.applicationConfiguration && this.isSettingAppliedForAllProfiles(key)) { @@ -1053,6 +1053,11 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat } } + private async createConfigurationEditingService(instantiationService: IInstantiationService): Promise { + const remoteSettingsResource = (await this.remoteAgentService.getEnvironment())?.settingsPath ?? null; + return instantiationService.createInstance(ConfigurationEditing, remoteSettingsResource); + } + private getConfigurationModelForEditableConfigurationTarget(target: EditableConfigurationTarget, resource?: URI | null): ConfigurationModel | undefined { switch (target) { case EditableConfigurationTarget.USER_LOCAL: return this._configuration.localUserConfiguration; From 5eac27c2ed726889ebb227f0dcc9ceb578fe6d94 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 18 Jan 2024 13:01:49 +0530 Subject: [PATCH 091/333] report single event (#202705) --- .../browser/configurationService.ts | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 4d27af9fb2d0c..6b842ab9400fe 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -15,7 +15,7 @@ import { ConfigurationModel, ConfigurationChangeEvent, mergeChanges } from 'vs/p import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange, ConfigurationTargetToString, IConfigurationUpdateOverrides, isConfigurationUpdateOverrides, IConfigurationService, IConfigurationUpdateOptions } from 'vs/platform/configuration/common/configuration'; import { IPolicyConfiguration, NullPolicyConfiguration, PolicyConfiguration } from 'vs/platform/configuration/common/configurations'; import { Configuration } from 'vs/workbench/services/configuration/common/configurationModels'; -import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService, RestrictedSettings, PROFILE_SCOPES, LOCAL_MACHINE_PROFILE_SCOPES, profileSettingsSchemaId, APPLY_ALL_PROFILES_SETTING } from 'vs/workbench/services/configuration/common/configuration'; +import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService, RestrictedSettings, PROFILE_SCOPES, LOCAL_MACHINE_PROFILE_SCOPES, profileSettingsSchemaId, APPLY_ALL_PROFILES_SETTING, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings, ConfigurationScope, IConfigurationPropertySchema, keyFromOverrideIdentifiers, OVERRIDE_PROPERTY_PATTERN, resourceLanguageSettingsSchemaId, configurationDefaultsSchemaId } from 'vs/platform/configuration/common/configurationRegistry'; import { IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, getStoredWorkspaceFolder, toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces'; @@ -1331,39 +1331,37 @@ class ConfigurationTelemetryContribution extends Disposable implements IWorkbenc constructor( @IConfigurationService private readonly configurationService: WorkspaceService, - @ITelemetryService private readonly telemetryService: ITelemetryService, + @ITelemetryService telemetryService: ITelemetryService, ) { super(); - const { user, workspace } = configurationService.keys(); - for (const key of user) { - this.reportConfiguration(key, ConfigurationTarget.USER_LOCAL); + const updatedUserSettings: { [key: string]: any } = {}; + for (const k of user) { + if (!k.startsWith(`${TASKS_CONFIGURATION_KEY}.`) && !k.startsWith(`${LAUNCH_CONFIGURATION_KEY}.`)) { + updatedUserSettings[k] = this.getValueToReport(k, ConfigurationTarget.USER_LOCAL) ?? ''; + } } - for (const key of workspace) { - this.reportConfiguration(key, ConfigurationTarget.WORKSPACE); + const updatedWorkspaceSettings: { [key: string]: any } = {}; + for (const k of workspace) { + if (!k.startsWith(`${TASKS_CONFIGURATION_KEY}.`) && !k.startsWith(`${LAUNCH_CONFIGURATION_KEY}.`)) { + updatedWorkspaceSettings[k] = this.getValueToReport(k, ConfigurationTarget.WORKSPACE) ?? ''; + } } - } - - private reportConfiguration(key: string, target: ConfigurationTarget): void { - type UpdateConfigurationClassification = { + type UpdatedSettingsClassification = { owner: 'sandy081'; - comment: 'Event which fires for updated configurations'; - source: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'What configuration file was updated i.e user or workspace' }; - key: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'What configuration key was updated' }; - value?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Value of the key that was updated' }; + comment: 'Event reporting updated settings'; + settings: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'stringified updated settings object' }; }; - type UpdateConfigurationEvent = { - source: string; - key: string; - value?: any; + type UpdatedSettingsEvent = { + settings: string; }; - this.telemetryService.publicLog2('updateConfiguration', { - source: ConfigurationTargetToString(target), - key, - value: this.getValueToReport(key, target) - }); + telemetryService.publicLog2('updatedsettings:user', { settings: JSON.stringify(updatedUserSettings) }); + telemetryService.publicLog2('updatedsettings:workspace', { settings: JSON.stringify(updatedWorkspaceSettings) }); } + /** + * Report value of a setting only if it is an enum, boolean, or number or an array of those. + */ private getValueToReport(key: string, target: ConfigurationTarget): any { const schema = this.configurationRegistry.getConfigurationProperties()[key]; if (!schema) { From b0f0dcb54454760d475efee8192de588c1ddd320 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 18 Jan 2024 12:19:45 +0100 Subject: [PATCH 092/333] window - fix fullscreen detection (#202714) Only use `detectFullScreen` in web as it seems to return bad results on desktop. --- .../auxiliaryWindow/electron-main/auxiliaryWindows.ts | 2 +- .../electron-main/auxiliaryWindowsMainService.ts | 6 +++--- src/vs/platform/native/common/native.ts | 2 +- .../native/electron-main/nativeHostMainService.ts | 4 ++-- src/vs/platform/windows/electron-main/windows.ts | 2 +- .../windows/electron-main/windowsMainService.ts | 6 +++--- .../browser/parts/editor/auxiliaryEditorPart.ts | 6 +++--- src/vs/workbench/browser/window.ts | 6 +++--- .../services/host/browser/browserHostService.ts | 10 +++++----- src/vs/workbench/services/host/browser/host.ts | 2 +- .../host/electron-sandbox/nativeHostService.ts | 2 +- src/vs/workbench/test/browser/workbenchTestServices.ts | 2 +- 12 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows.ts b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows.ts index c67d3eac6fa5e..d437cc7f6ba0f 100644 --- a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows.ts +++ b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows.ts @@ -17,7 +17,7 @@ export interface IAuxiliaryWindowsMainService { readonly onDidMaximizeWindow: Event; readonly onDidUnmaximizeWindow: Event; - readonly onDidChangeFullScreen: Event; + readonly onDidChangeFullScreen: Event<{ window: IAuxiliaryWindow; fullscreen: boolean }>; readonly onDidTriggerSystemContextMenu: Event<{ readonly window: IAuxiliaryWindow; readonly x: number; readonly y: number }>; createWindow(): BrowserWindowConstructorOptions; diff --git a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.ts b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.ts index f87d8078a26bc..d0afe918cf478 100644 --- a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.ts +++ b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.ts @@ -24,7 +24,7 @@ export class AuxiliaryWindowsMainService extends Disposable implements IAuxiliar private readonly _onDidUnmaximizeWindow = this._register(new Emitter()); readonly onDidUnmaximizeWindow = this._onDidUnmaximizeWindow.event; - private readonly _onDidChangeFullScreen = this._register(new Emitter()); + private readonly _onDidChangeFullScreen = this._register(new Emitter<{ window: IAuxiliaryWindow; fullscreen: boolean }>()); readonly onDidChangeFullScreen = this._onDidChangeFullScreen.event; private readonly _onDidTriggerSystemContextMenu = this._register(new Emitter<{ window: IAuxiliaryWindow; x: number; y: number }>()); @@ -88,8 +88,8 @@ export class AuxiliaryWindowsMainService extends Disposable implements IAuxiliar disposables.add(auxiliaryWindow.onDidMaximize(() => this._onDidMaximizeWindow.fire(auxiliaryWindow))); disposables.add(auxiliaryWindow.onDidUnmaximize(() => this._onDidUnmaximizeWindow.fire(auxiliaryWindow))); - disposables.add(auxiliaryWindow.onDidEnterFullScreen(() => this._onDidChangeFullScreen.fire(auxiliaryWindow))); - disposables.add(auxiliaryWindow.onDidLeaveFullScreen(() => this._onDidChangeFullScreen.fire(auxiliaryWindow))); + disposables.add(auxiliaryWindow.onDidEnterFullScreen(() => this._onDidChangeFullScreen.fire({ window: auxiliaryWindow, fullscreen: true }))); + disposables.add(auxiliaryWindow.onDidLeaveFullScreen(() => this._onDidChangeFullScreen.fire({ window: auxiliaryWindow, fullscreen: false }))); disposables.add(auxiliaryWindow.onDidTriggerSystemContextMenu(({ x, y }) => this._onDidTriggerSystemContextMenu.fire({ window: auxiliaryWindow, x, y }))); Event.once(auxiliaryWindow.onDidClose)(() => disposables.dispose()); diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index 106505e908a92..239dcad93226d 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -53,7 +53,7 @@ export interface ICommonNativeHostService { readonly onDidFocusMainWindow: Event; readonly onDidBlurMainWindow: Event; - readonly onDidChangeWindowFullScreen: Event; + readonly onDidChangeWindowFullScreen: Event<{ windowId: number; fullscreen: boolean }>; readonly onDidFocusMainOrAuxiliaryWindow: Event; readonly onDidBlurMainOrAuxiliaryWindow: Event; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 5d73705288bfc..a9f4b9ed735aa 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -94,8 +94,8 @@ export class NativeHostMainService extends Disposable implements INativeHostMain ); readonly onDidChangeWindowFullScreen = Event.any( - Event.map(this.windowsMainService.onDidChangeFullScreen, window => window.id), - Event.map(this.auxiliaryWindowsMainService.onDidChangeFullScreen, window => window.id) + Event.map(this.windowsMainService.onDidChangeFullScreen, e => ({ windowId: e.window.id, fullscreen: e.fullscreen })), + Event.map(this.auxiliaryWindowsMainService.onDidChangeFullScreen, e => ({ windowId: e.window.id, fullscreen: e.fullscreen })) ); readonly onDidBlurMainWindow = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-blur', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index a658f3fa5ba27..5fe00f09595f6 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -32,7 +32,7 @@ export interface IWindowsMainService { readonly onDidSignalReadyWindow: Event; readonly onDidMaximizeWindow: Event; readonly onDidUnmaximizeWindow: Event; - readonly onDidChangeFullScreen: Event; + readonly onDidChangeFullScreen: Event<{ window: ICodeWindow; fullscreen: boolean }>; readonly onDidTriggerSystemContextMenu: Event<{ readonly window: ICodeWindow; readonly x: number; readonly y: number }>; readonly onDidDestroyWindow: Event; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 32019b8b42c88..11ea40f132534 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -198,7 +198,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private readonly _onDidUnmaximizeWindow = this._register(new Emitter()); readonly onDidUnmaximizeWindow = this._onDidUnmaximizeWindow.event; - private readonly _onDidChangeFullScreen = this._register(new Emitter()); + private readonly _onDidChangeFullScreen = this._register(new Emitter<{ window: ICodeWindow; fullscreen: boolean }>()); readonly onDidChangeFullScreen = this._onDidChangeFullScreen.event; private readonly _onDidTriggerSystemContextMenu = this._register(new Emitter<{ window: ICodeWindow; x: number; y: number }>()); @@ -1500,8 +1500,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic disposables.add(Event.once(createdWindow.onDidDestroy)(() => this.onWindowDestroyed(createdWindow))); disposables.add(createdWindow.onDidMaximize(() => this._onDidMaximizeWindow.fire(createdWindow))); disposables.add(createdWindow.onDidUnmaximize(() => this._onDidUnmaximizeWindow.fire(createdWindow))); - disposables.add(createdWindow.onDidEnterFullScreen(() => this._onDidChangeFullScreen.fire(createdWindow))); - disposables.add(createdWindow.onDidLeaveFullScreen(() => this._onDidChangeFullScreen.fire(createdWindow))); + disposables.add(createdWindow.onDidEnterFullScreen(() => this._onDidChangeFullScreen.fire({ window: createdWindow, fullscreen: true }))); + disposables.add(createdWindow.onDidLeaveFullScreen(() => this._onDidChangeFullScreen.fire({ window: createdWindow, fullscreen: false }))); disposables.add(createdWindow.onDidTriggerSystemContextMenu(({ x, y }) => this._onDidTriggerSystemContextMenu.fire({ window: createdWindow, x, y }))); const webContents = assertIsDefined(createdWindow.win?.webContents); diff --git a/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts b/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts index d50e62f60952d..03596d07bfa01 100644 --- a/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts +++ b/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { onDidChangeFullscreen, onDidChangeZoomLevel } from 'vs/base/browser/browser'; -import { detectFullscreen, hide, show } from 'vs/base/browser/dom'; +import { isFullscreen, onDidChangeFullscreen, onDidChangeZoomLevel } from 'vs/base/browser/browser'; +import { hide, show } from 'vs/base/browser/dom'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { isNative } from 'vs/base/common/platform'; @@ -134,7 +134,7 @@ export class AuxiliaryEditorPart { // Make sure to hide the custom title when we enter // fullscren mode and show it when we lave it. - const fullscreen = detectFullscreen(auxiliaryWindow.window); + const fullscreen = isFullscreen(auxiliaryWindow.window); const oldTitlebarPartVisible = titlebarPartVisible; titlebarPartVisible = !fullscreen; if (titlebarPart && oldTitlebarPartVisible !== titlebarPartVisible) { diff --git a/src/vs/workbench/browser/window.ts b/src/vs/workbench/browser/window.ts index 9d5416d007c9a..17354e5f403a0 100644 --- a/src/vs/workbench/browser/window.ts +++ b/src/vs/workbench/browser/window.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { isSafari, setFullscreen } from 'vs/base/browser/browser'; -import { addDisposableListener, detectFullscreen, EventHelper, EventType, getActiveWindow, getWindow, getWindowById, getWindows, getWindowsCount, windowOpenNoOpener, windowOpenPopup, windowOpenWithSuccess } from 'vs/base/browser/dom'; +import { addDisposableListener, EventHelper, EventType, getActiveWindow, getWindow, getWindowById, getWindows, getWindowsCount, windowOpenNoOpener, windowOpenPopup, windowOpenWithSuccess } from 'vs/base/browser/dom'; import { DomEmitter } from 'vs/base/browser/event'; import { HidDeviceData, requestHidDevice, requestSerialPort, requestUsbDevice, SerialPortData, UsbDeviceData } from 'vs/base/browser/deviceAccess'; import { timeout } from 'vs/base/common/async'; @@ -136,11 +136,11 @@ export abstract class BaseWindow extends Disposable { //#endregion private registerFullScreenListeners(targetWindowId: number): void { - this._register(this.hostService.onDidChangeFullScreen(windowId => { + this._register(this.hostService.onDidChangeFullScreen(({ windowId, fullscreen }) => { if (windowId === targetWindowId) { const targetWindow = getWindowById(targetWindowId); if (targetWindow) { - setFullscreen(!!detectFullscreen(targetWindow.window), targetWindow.window); + setFullscreen(fullscreen, targetWindow.window); } } })); diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 982ba91d8ce87..79c91e10dba3e 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -15,7 +15,7 @@ import { whenEditorClosed } from 'vs/workbench/browser/editor'; import { IWorkspace, IWorkspaceProvider } from 'vs/workbench/browser/web.api'; import { IFileService } from 'vs/platform/files/common/files'; import { ILabelService, Verbosity } from 'vs/platform/label/common/label'; -import { EventType, ModifierKeyEmitter, addDisposableListener, addDisposableThrottledListener, disposableWindowInterval, getActiveDocument, getWindowId, onDidRegisterWindow, trackFocus } from 'vs/base/browser/dom'; +import { EventType, ModifierKeyEmitter, addDisposableListener, addDisposableThrottledListener, detectFullscreen, disposableWindowInterval, getActiveDocument, getWindowId, onDidRegisterWindow, trackFocus } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { memoize } from 'vs/base/common/decorators'; @@ -206,8 +206,8 @@ export class BrowserHostService extends Disposable implements IHostService { } @memoize - get onDidChangeFullScreen(): Event { - const emitter = this._register(new Emitter()); + get onDidChangeFullScreen(): Event<{ windowId: number; fullscreen: boolean }> { + const emitter = this._register(new Emitter<{ windowId: number; fullscreen: boolean }>()); this._register(Event.runAndSubscribe(onDidRegisterWindow, ({ window, disposables }) => { const windowId = getWindowId(window); @@ -215,11 +215,11 @@ export class BrowserHostService extends Disposable implements IHostService { // Fullscreen (Browser) for (const event of [EventType.FULLSCREEN_CHANGE, EventType.WK_FULLSCREEN_CHANGE]) { - disposables.add(addDisposableListener(window.document, event, () => emitter.fire(windowId))); + disposables.add(addDisposableListener(window.document, event, () => emitter.fire({ windowId, fullscreen: !!detectFullscreen(window) }))); } // Fullscreen (Native) - disposables.add(addDisposableThrottledListener(viewport, EventType.RESIZE, () => emitter.fire(windowId), undefined, isMacintosh ? 2000 /* adjust for macOS animation */ : 800 /* can be throttled */)); + disposables.add(addDisposableThrottledListener(viewport, EventType.RESIZE, () => emitter.fire({ windowId, fullscreen: !!detectFullscreen(window) }), undefined, isMacintosh ? 2000 /* adjust for macOS animation */ : 800 /* can be throttled */)); }, { window: mainWindow, disposables: this._store })); return emitter.event; diff --git a/src/vs/workbench/services/host/browser/host.ts b/src/vs/workbench/services/host/browser/host.ts index 02d7fcea201d4..f1586f4b9c42c 100644 --- a/src/vs/workbench/services/host/browser/host.ts +++ b/src/vs/workbench/services/host/browser/host.ts @@ -69,7 +69,7 @@ export interface IHostService { * Emitted when the window with the given identifier changes * its fullscreen state. */ - readonly onDidChangeFullScreen: Event; + readonly onDidChangeFullScreen: Event<{ windowId: number; fullscreen: boolean }>; /** * Opens an empty window. The optional parameter allows to define if diff --git a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts index 4cd56f09edaf5..d3fbff27c39c2 100644 --- a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts +++ b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts @@ -94,7 +94,7 @@ class WorkbenchHostService extends Disposable implements IHostService { return Event.latch(emitter.event, undefined, this._store); } - readonly onDidChangeFullScreen = Event.filter(this.nativeHostService.onDidChangeWindowFullScreen, id => hasWindow(id), this._store); + readonly onDidChangeFullScreen = Event.filter(this.nativeHostService.onDidChangeWindowFullScreen, e => hasWindow(e.windowId), this._store); openWindow(options?: IOpenEmptyWindowOptions): Promise; openWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 4ba2d0fb3abe2..0eacb21061ce3 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -1493,7 +1493,7 @@ export class TestHostService implements IHostService { private _onDidChangeWindow = new Emitter(); readonly onDidChangeActiveWindow = this._onDidChangeWindow.event; - readonly onDidChangeFullScreen: Event = Event.None; + readonly onDidChangeFullScreen: Event<{ windowId: number; fullscreen: boolean }> = Event.None; setFocus(focus: boolean) { this._hasFocus = focus; From c005fc38a92b84826e5d56b7ce37c2c8183ca0f2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 18 Jan 2024 16:58:40 +0530 Subject: [PATCH 093/333] fix #197345 (#202715) --- .../lib/stylelint/vscode-known-variables.json | 2 ++ .../auxiliarybar/media/auxiliaryBarPart.css | 5 ++++ .../browser/parts/media/paneCompositePart.css | 4 --- .../browser/parts/panel/media/panelpart.css | 5 ++++ .../parts/sidebar/media/sidebarpart.css | 10 +++++++ .../browser/parts/sidebar/sidebarPart.ts | 10 +++---- src/vs/workbench/common/theme.ts | 30 ++++++++++++++++++- 7 files changed, 56 insertions(+), 10 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index cbdea6a9ea20f..cbb1a5e14dd10 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -461,6 +461,8 @@ "--vscode-notificationsWarningIcon-foreground", "--vscode-outputView-background", "--vscode-outputViewStickyScroll-background", + "--vscode-activityBarTop-activeBorder", + "--vscode-activityBarTop-foreground", "--vscode-panel-background", "--vscode-panel-border", "--vscode-panel-dropBorder", diff --git a/src/vs/workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css b/src/vs/workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css index 0b2a5c807409e..3d382b3e39d88 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css +++ b/src/vs/workbench/browser/parts/auxiliarybar/media/auxiliaryBarPart.css @@ -18,6 +18,11 @@ flex: 1; } +.monaco-workbench .part.auxiliarybar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked:not(:focus) .active-item-indicator:before, +.monaco-workbench .part.auxiliarybar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked.clicked:focus .active-item-indicator:before { + border-top-color: var(--vscode-panelTitle-activeBorder) !important; +} + .monaco-workbench .part.auxiliarybar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label, .monaco-workbench .part.auxiliarybar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus .action-label { color: var(--vscode-sideBarTitle-foreground) !important; diff --git a/src/vs/workbench/browser/parts/media/paneCompositePart.css b/src/vs/workbench/browser/parts/media/paneCompositePart.css index 11145c9629982..e3799464f4d84 100644 --- a/src/vs/workbench/browser/parts/media/paneCompositePart.css +++ b/src/vs/workbench/browser/parts/media/paneCompositePart.css @@ -232,10 +232,6 @@ .monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus .active-item-indicator:before { border-top-color: var(--vscode-focusBorder) !important; } -.monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked:not(:focus) .active-item-indicator:before, -.monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked.clicked:focus .active-item-indicator:before { - border-top-color: var(--vscode-panelTitle-activeBorder) !important; -} .monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked .action-label, .monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label { diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css index e1f42582e7848..4ccb53b0c804c 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -47,6 +47,11 @@ background-color: inherit; } +.monaco-workbench .part.panel > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked:not(:focus) .active-item-indicator:before, +.monaco-workbench .part.panel > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked.clicked:focus .active-item-indicator:before { + border-top-color: var(--vscode-panelTitle-activeBorder) !important; +} + .monaco-workbench .part.panel > .title > .composite-bar-container >.composite-bar > .monaco-action-bar .action-item:focus .action-label, .monaco-workbench .part.panel > .title > .composite-bar-container >.composite-bar > .monaco-action-bar .action-item:hover .action-label { color: var(--vscode-panelTitle-activeForeground) !important; diff --git a/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css b/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css index 5d0a0da2e635e..65f962e824199 100644 --- a/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css +++ b/src/vs/workbench/browser/parts/sidebar/media/sidebarpart.css @@ -60,6 +60,16 @@ height: 16px; } +.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked:not(:focus) .active-item-indicator:before, +.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.checked.clicked:focus .active-item-indicator:before { + border-top-color: var(--vscode-activityBarTop-activeBorder) !important; +} + +.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:hover .action-label, +.monaco-workbench .part.sidebar > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:focus .action-label { + color: var(--vscode-activityBarTop-foreground) !important; +} + .monaco-workbench .sidebar.pane-composite-part > .title > .composite-bar-container { flex: 1; } diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index e0b21f325173e..abadf927fd96c 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -13,7 +13,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; -import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SIDE_BAR_BORDER, SIDE_BAR_DRAG_AND_DROP_BACKGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_DRAG_AND_DROP_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND } from 'vs/workbench/common/theme'; +import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SIDE_BAR_BORDER, SIDE_BAR_DRAG_AND_DROP_BACKGROUND, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_TOP_FOREGROUND, ACTIVITY_BAR_TOP_ACTIVE_BORDER, ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND, ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER } from 'vs/workbench/common/theme'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; @@ -183,12 +183,12 @@ export class SidebarPart extends AbstractPaneCompositePart { colors: theme => ({ activeBackgroundColor: theme.getColor(SIDE_BAR_BACKGROUND), inactiveBackgroundColor: theme.getColor(SIDE_BAR_BACKGROUND), - activeBorderBottomColor: theme.getColor(PANEL_ACTIVE_TITLE_BORDER), - activeForegroundColor: theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND), - inactiveForegroundColor: theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND), + activeBorderBottomColor: theme.getColor(ACTIVITY_BAR_TOP_ACTIVE_BORDER), + activeForegroundColor: theme.getColor(ACTIVITY_BAR_TOP_FOREGROUND), + inactiveForegroundColor: theme.getColor(ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND), badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND), badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND), - dragAndDropBorder: theme.getColor(PANEL_DRAG_AND_DROP_BORDER) + dragAndDropBorder: theme.getColor(ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER) }), compact: true }; diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 1c56711b8d0b1..4977a08dbcef2 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -365,7 +365,6 @@ export const PANEL_DRAG_AND_DROP_BORDER = registerColor('panel.dropBorder', { hcLight: PANEL_ACTIVE_TITLE_FOREGROUND }, localize('panelDragAndDropBorder', "Drag and drop feedback color for the panel titles. Panels are shown below the editor area and contain views like output and integrated terminal.")); - export const PANEL_SECTION_DRAG_AND_DROP_BACKGROUND = registerColor('panelSection.dropBackground', { dark: EDITOR_DRAG_AND_DROP_BACKGROUND, light: EDITOR_DRAG_AND_DROP_BACKGROUND, @@ -685,6 +684,35 @@ export const ACTIVITY_BAR_BADGE_FOREGROUND = registerColor('activityBarBadge.for hcLight: Color.white }, localize('activityBarBadgeForeground', "Activity notification badge foreground color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +export const ACTIVITY_BAR_TOP_FOREGROUND = registerColor('activityBarTop.foreground', { + dark: '#E7E7E7', + light: '#424242', + hcDark: Color.white, + hcLight: editorForeground +}, localize('activityBarTop', "Active foreground color of the item in the Activity bar when it is on top. The activity allows to switch between views of the side bar.")); + +export const ACTIVITY_BAR_TOP_ACTIVE_BORDER = registerColor('activityBarTop.activeBorder', { + dark: ACTIVITY_BAR_TOP_FOREGROUND, + light: ACTIVITY_BAR_TOP_FOREGROUND, + hcDark: contrastBorder, + hcLight: '#B5200D' +}, localize('activityBarTopActiveFocusBorder', "Focus border color for the active item in the Activity bar when it is on top. The activity allows to switch between views of the side bar.")); + +export const ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND = registerColor('activityBarTop.inactiveForeground', { + dark: transparent(ACTIVITY_BAR_TOP_FOREGROUND, 0.6), + light: transparent(ACTIVITY_BAR_TOP_FOREGROUND, 0.75), + hcDark: Color.white, + hcLight: editorForeground +}, localize('activityBarTopInActiveForeground', "Inactive foreground color of the item in the Activity bar when it is on top. The activity allows to switch between views of the side bar.")); + +export const ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER = registerColor('activityBarTop.dropBorder', { + dark: ACTIVITY_BAR_TOP_FOREGROUND, + light: ACTIVITY_BAR_TOP_FOREGROUND, + hcDark: ACTIVITY_BAR_TOP_FOREGROUND, + hcLight: ACTIVITY_BAR_TOP_FOREGROUND +}, localize('activityBarTopDragAndDropBorder', "Drag and drop feedback color for the items in the Activity bar when it is on top. The activity allows to switch between views of the side bar.")); + + // < --- Profiles --- > export const PROFILE_BADGE_BACKGROUND = registerColor('profileBadge.background', { From 42ffc9c0d22e177a29896cb71cafedbc4e1a9f46 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Thu, 18 Jan 2024 12:29:12 +0100 Subject: [PATCH 094/333] Rename setting --- src/vs/platform/window/common/window.ts | 11 ++++++++++ src/vs/workbench/browser/layout.ts | 11 +++++----- .../browser/parts/titlebar/menubarControl.ts | 4 ++-- .../browser/parts/titlebar/titlebarActions.ts | 2 +- .../browser/parts/titlebar/titlebarPart.ts | 12 +++++------ .../browser/relauncher.contribution.ts | 4 ++-- .../contrib/splash/browser/partsSplash.ts | 3 ++- .../windowIgnoreMenuShortcutsManager.ts | 3 ++- .../electron-sandbox/desktop.contribution.ts | 20 ++++++++++++------- 9 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index 0a55eaa67e463..14f3914639ca4 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -165,11 +165,22 @@ export interface IDensitySettings { readonly editorTabHeight: 'default' | 'compact'; } +export const enum TitleBarSetting { + TITLE_BAR_STYLE = 'window.titleBarStyle', + CUSTOM_TITLE_BAR_VISIBILITY = 'window.customTitleBarVisibility', +} + export const enum TitlebarStyle { NATIVE = 'native', CUSTOM = 'custom', } +export const enum CustomTitleBarVisibility { + AUTO = 'auto', + WINDOWED = 'windowed', + NEVER = 'never', +} + export function hasCustomTitlebar(configurationService: IConfigurationService, titleBarStyle?: TitlebarStyle): boolean { // Does not imply that the title bar is visible diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 3e45b0db08dd4..972d0771b0893 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -19,7 +19,7 @@ import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/co import { ITitleService } from 'vs/workbench/services/title/browser/titleService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { StartupKind, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { getMenuBarVisibility, IPath, hasNativeTitlebar, hasCustomTitlebar } from 'vs/platform/window/common/window'; +import { getMenuBarVisibility, IPath, hasNativeTitlebar, hasCustomTitlebar, TitleBarSetting } from 'vs/platform/window/common/window'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -48,6 +48,7 @@ import { AuxiliaryBarPart } from 'vs/workbench/browser/parts/auxiliarybar/auxili import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IAuxiliaryWindowService } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService'; import { mainWindow } from 'vs/base/browser/window'; +import { CustomTitleBarVisibility } from '../../platform/window/common/window'; //#region Layout Implementation @@ -349,8 +350,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION, LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE, 'window.menuBarVisibility', - 'window.titleBarStyle', - 'window.showCustomToolBar', + TitleBarSetting.TITLE_BAR_STYLE, + TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, ].some(setting => e.affectsConfiguration(setting))) { this.doUpdateLayoutConfiguration(); } @@ -1228,8 +1229,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return false; } - const showCustomToolBar = this.configurationService.getValue('window.showCustomToolBar'); - if (showCustomToolBar === 'never' || showCustomToolBar === 'windowed' && this.state.runtime.mainWindowFullscreen) { + const showCustomTitleBar = this.configurationService.getValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY); + if (showCustomTitleBar === CustomTitleBarVisibility.NEVER || showCustomTitleBar === CustomTitleBarVisibility.WINDOWED && this.state.runtime.mainWindowFullscreen) { return false; } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 2923bcc75bb3a..eeec3dbcc5e62 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/menubarControl'; import { localize, localize2 } from 'vs/nls'; import { IMenuService, MenuId, IMenu, SubmenuItemAction, registerAction2, Action2, MenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; -import { MenuBarVisibility, IWindowOpenable, getMenuBarVisibility, hasNativeTitlebar } from 'vs/platform/window/common/window'; +import { MenuBarVisibility, IWindowOpenable, getMenuBarVisibility, hasNativeTitlebar, TitleBarSetting } from 'vs/platform/window/common/window'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IAction, Action, SubmenuAction, Separator, IActionRunner, ActionRunner, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification, toAction } from 'vs/base/common/actions'; import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom'; @@ -362,7 +362,7 @@ export abstract class MenubarControl extends Disposable { { label: localize('goToSetting', "Open Settings"), run: () => { - return this.preferencesService.openUserSettings({ query: 'window.titleBarStyle' }); + return this.preferencesService.openUserSettings({ query: TitleBarSetting.TITLE_BAR_STYLE }); } } ]); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index 6fdc5df5b092f..4ac42e76029e7 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -63,7 +63,7 @@ registerAction2(class ToggleLayoutControl extends ToggleConfigAction { }); registerAction2(class ToggleCustomToolBar extends Action2 { - static readonly settingsID = `window.showCustomToolBar`; + static readonly settingsID = `window.showCustomTitleBar`; constructor() { super({ diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index bb83d9199fcda..32361f3be144e 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -24,7 +24,7 @@ import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menuba import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Emitter, Event } from 'vs/base/common/event'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { Parts, IWorkbenchLayoutService, ActivityBarPosition, LayoutSettings } from 'vs/workbench/services/layout/browser/layoutService'; +import { Parts, IWorkbenchLayoutService, ActivityBarPosition, LayoutSettings, EditorActionsLocation, EditorTabsMode } from 'vs/workbench/services/layout/browser/layoutService'; import { createActionViewItem, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { Action2, IMenu, IMenuService, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -487,7 +487,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { // Text Title if (!this.isCommandCenterVisible) { - if (this.titleBarStyle === 'custom') { + if (!hasNativeTitlebar(this.configurationService, this.titleBarStyle)) { this.title.innerText = this.windowTitle.value; this.titleDisposables.add(this.windowTitle.onDidChange(() => { @@ -707,15 +707,15 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { } private get editorActionsEnabled(): boolean { - return this.editorGroupService.partOptions.editorActionsLocation === 'titleBar' || + return this.editorGroupService.partOptions.editorActionsLocation === EditorActionsLocation.TITLEBAR || ( - this.editorGroupService.partOptions.editorActionsLocation === 'default' && - this.editorGroupService.partOptions.showTabs === 'none' + this.editorGroupService.partOptions.editorActionsLocation === EditorActionsLocation.DEFAULT && + this.editorGroupService.partOptions.showTabs === EditorTabsMode.MULTIPLE ); } private get activityActionsEnabled(): boolean { - return !this.isAuxiliary && this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.TOP; + return !this.isAuxiliary && this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.TOP; } get hasZoomableElements(): boolean { diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index c3f413e8b0901..a75a1b4b09bf1 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -6,7 +6,7 @@ import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IWindowsConfiguration, IWindowSettings, TitlebarStyle } from 'vs/platform/window/common/window'; +import { IWindowsConfiguration, IWindowSettings, TitleBarSetting, TitlebarStyle } from 'vs/platform/window/common/window'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ConfigurationTarget, IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { localize } from 'vs/nls'; @@ -34,7 +34,7 @@ interface IConfiguration extends IWindowsConfiguration { export class SettingsChangeRelauncher extends Disposable implements IWorkbenchContribution { private static SETTINGS = [ - 'window.titleBarStyle', + TitleBarSetting.TITLE_BAR_STYLE, 'window.nativeTabs', 'window.nativeFullScreen', 'window.clickThroughInactive', diff --git a/src/vs/workbench/contrib/splash/browser/partsSplash.ts b/src/vs/workbench/contrib/splash/browser/partsSplash.ts index 63f2d79012d5b..5d637c4489333 100644 --- a/src/vs/workbench/contrib/splash/browser/partsSplash.ts +++ b/src/vs/workbench/contrib/splash/browser/partsSplash.ts @@ -21,6 +21,7 @@ import { assertIsDefined } from 'vs/base/common/types'; import { ISplashStorageService } from 'vs/workbench/contrib/splash/browser/splash'; import { mainWindow } from 'vs/base/browser/window'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { TitleBarSetting } from 'vs/platform/window/common/window'; export class PartsSplash { @@ -54,7 +55,7 @@ export class PartsSplash { }); _configService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('window.titleBarStyle')) { + if (e.affectsConfiguration(TitleBarSetting.TITLE_BAR_STYLE)) { this._didChangeTitleBarStyle = true; this._savePartsSplash(); } diff --git a/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts b/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts index 7ae47c23f5d59..49345645524ad 100644 --- a/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts +++ b/src/vs/workbench/contrib/webview/electron-sandbox/windowIgnoreMenuShortcutsManager.ts @@ -9,6 +9,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IMainProcessService } from 'vs/platform/ipc/common/mainProcessService'; import { INativeHostService } from 'vs/platform/native/common/native'; import { IWebviewManagerService } from 'vs/platform/webview/common/webviewManagerService'; +import { hasNativeTitlebar } from 'vs/platform/window/common/window'; export class WindowIgnoreMenuShortcutsManager { @@ -21,7 +22,7 @@ export class WindowIgnoreMenuShortcutsManager { mainProcessService: IMainProcessService, private readonly _nativeHostService: INativeHostService ) { - this._isUsingNativeTitleBars = configurationService.getValue('window.titleBarStyle') !== 'custom'; + this._isUsingNativeTitleBars = hasNativeTitlebar(configurationService); this._webviewMainService = ProxyChannel.toService(mainProcessService.getChannel('webview')); } diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 9cf09c66a85bc..7c7dc5250e7f6 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -27,6 +27,7 @@ import { ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle import { NativeWindow } from 'vs/workbench/electron-sandbox/window'; import { ModifierKeyEmitter } from 'vs/base/browser/dom'; import { applicationConfigurationNodeBase, securityConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { CustomTitleBarVisibility, TitleBarSetting, TitlebarStyle } from 'vs/platform/window/common/window'; // Actions (function registerActions(): void { @@ -227,19 +228,24 @@ import { applicationConfigurationNodeBase, securityConfigurationNodeBase } from 'scope': ConfigurationScope.APPLICATION, 'markdownDescription': localize('window.doubleClickIconToClose', "If enabled, this setting will close the window when the application icon in the title bar is double-clicked. The window will not be able to be dragged by the icon. This setting is effective only if `#window.titleBarStyle#` is set to `custom`.") }, - 'window.titleBarStyle': { + [TitleBarSetting.TITLE_BAR_STYLE]: { 'type': 'string', - 'enum': ['native', 'custom'], - 'default': isLinux ? 'native' : 'custom', + 'enum': [TitlebarStyle.NATIVE, TitlebarStyle.CUSTOM], + 'default': isLinux ? TitlebarStyle.NATIVE : TitlebarStyle.CUSTOM, 'scope': ConfigurationScope.APPLICATION, 'description': localize('titleBarStyle', "Adjust the appearance of the window title bar to be native by the OS or custom. On Linux and Windows, this setting also affects the application and context menu appearances. Changes require a full restart to apply.") }, - 'window.showCustomToolBar': { + [TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY]: { 'type': 'string', - 'enum': ['default', 'never', 'windowed'], - 'default': 'default', + 'enum': [CustomTitleBarVisibility.AUTO, CustomTitleBarVisibility.WINDOWED, CustomTitleBarVisibility.NEVER], + 'markdownEnumDescriptions': [ + localize(`${TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY}.${CustomTitleBarVisibility.AUTO}`, "Automatically changes custom titlebar visibility."), + localize(`${TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY}.${CustomTitleBarVisibility.WINDOWED}`, "Hide custom titlebar in full screen. Automatically changes custom titlebar visibility in windowed."), + localize(`${TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY}.${CustomTitleBarVisibility.NEVER}`, "Hide custom titlebar when `#window.titleBarStyle#` is set to `native`."), + ], + 'default': isLinux ? CustomTitleBarVisibility.NEVER : CustomTitleBarVisibility.AUTO, 'scope': ConfigurationScope.APPLICATION, - 'description': localize('showCustomToolBar', "") // TODO@Ben + 'description': localize(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, "Adjust when the custom title bar should be shown."), }, 'window.dialogStyle': { 'type': 'string', From 4fe28027f77749bd3d530f6949c9e36f7bab6276 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Thu, 18 Jan 2024 12:30:08 +0100 Subject: [PATCH 095/333] =?UTF-8?q?don=C3=9Ft=20affect=20none=20native?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/vs/workbench/browser/layout.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 972d0771b0893..02d84822c0dca 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1229,8 +1229,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return false; } + const nativeTitleBarVisible = hasNativeTitlebar(this.configurationService); const showCustomTitleBar = this.configurationService.getValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY); - if (showCustomTitleBar === CustomTitleBarVisibility.NEVER || showCustomTitleBar === CustomTitleBarVisibility.WINDOWED && this.state.runtime.mainWindowFullscreen) { + if (showCustomTitleBar === CustomTitleBarVisibility.NEVER && nativeTitleBarVisible || showCustomTitleBar === CustomTitleBarVisibility.WINDOWED && this.state.runtime.mainWindowFullscreen) { return false; } @@ -1252,7 +1253,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // Hide custom title bar when native title bar and custom title bar is empty - if (hasNativeTitlebar(this.configurationService)) { + if (nativeTitleBarVisible) { return false; } From 105180ac7b05eb605b0a84b473e000f093791d69 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Thu, 18 Jan 2024 13:00:26 +0100 Subject: [PATCH 096/333] cleanup actions --- .../browser/actions/layoutActions.ts | 27 ++++++------- .../browser/parts/titlebar/titlebarActions.ts | 39 ++++++++++++++----- 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index bf10920f0ffcd..ca73beb2038de 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -7,7 +7,7 @@ import { localize, localize2 } from 'vs/nls'; import { MenuId, MenuRegistry, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { EditorTabsMode, IWorkbenchLayoutService, LayoutSettings, Parts, Position, ZenModeSettings, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; +import { EditorActionsLocation, EditorTabsMode, IWorkbenchLayoutService, LayoutSettings, Parts, Position, ZenModeSettings, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isWeb, isMacintosh, isNative } from 'vs/base/common/platform'; @@ -30,6 +30,7 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { ICommandActionTitle } from 'vs/platform/action/common/action'; import { mainWindow } from 'vs/base/browser/window'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { TitleBarSetting, TitlebarStyle } from 'vs/platform/window/common/window'; // Register Icons const menubarIcon = registerIcon('menuBar', Codicon.layoutMenubar, localize('menuBarIcon', "Represents the menu bar")); @@ -587,8 +588,8 @@ export class EditorActionsTitleBarAction extends Action2 { }, category: Categories.View, precondition: ContextKeyExpr.and( - ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'titleBar').negate(), - ContextKeyExpr.equals('config.window.style', 'native').negate(), + ContextKeyExpr.equals(`config.${LayoutSettings.EDITOR_ACTIONS_LOCATION}`, EditorActionsLocation.TITLEBAR).negate(), + ContextKeyExpr.equals(`config.${TitleBarSetting.TITLE_BAR_STYLE}`, TitlebarStyle.NATIVE).negate(), ), f1: true }); @@ -596,7 +597,7 @@ export class EditorActionsTitleBarAction extends Action2 { run(accessor: ServicesAccessor): Promise { const configurationService = accessor.get(IConfigurationService); - return configurationService.updateValue('workbench.editor.editorActionsLocation', 'titleBar'); + return configurationService.updateValue(LayoutSettings.EDITOR_ACTIONS_LOCATION, EditorActionsLocation.TITLEBAR); } } registerAction2(EditorActionsTitleBarAction); @@ -616,8 +617,8 @@ export class EditorActionsDefaultAction extends Action2 { }, category: Categories.View, precondition: ContextKeyExpr.and( - ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'default').negate(), - ContextKeyExpr.equals('config.workbench.editor.showTabs', 'none').negate(), + ContextKeyExpr.equals(`config.${LayoutSettings.EDITOR_ACTIONS_LOCATION}`, EditorActionsLocation.DEFAULT).negate(), + ContextKeyExpr.equals(`config.${LayoutSettings.EDITOR_TABS_MODE}`, EditorTabsMode.NONE).negate(), ), f1: true }); @@ -625,7 +626,7 @@ export class EditorActionsDefaultAction extends Action2 { run(accessor: ServicesAccessor): Promise { const configurationService = accessor.get(IConfigurationService); - return configurationService.updateValue('workbench.editor.editorActionsLocation', 'default'); + return configurationService.updateValue(LayoutSettings.EDITOR_ACTIONS_LOCATION, EditorActionsLocation.DEFAULT); } } registerAction2(EditorActionsDefaultAction); @@ -644,14 +645,14 @@ export class HideEditorActionsAction extends Action2 { original: 'Hide Editor Actions' }, category: Categories.View, - precondition: ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'hidden').negate(), + precondition: ContextKeyExpr.equals(`config.${LayoutSettings.EDITOR_ACTIONS_LOCATION}`, EditorActionsLocation.HIDDEN).negate(), f1: true }); } run(accessor: ServicesAccessor): Promise { const configurationService = accessor.get(IConfigurationService); - return configurationService.updateValue('workbench.editor.editorActionsLocation', 'hidden'); + return configurationService.updateValue(LayoutSettings.EDITOR_ACTIONS_LOCATION, EditorActionsLocation.HIDDEN); } } registerAction2(HideEditorActionsAction); @@ -670,14 +671,14 @@ export class ShowEditorActionsAction extends Action2 { original: 'Show Editor Actions' }, category: Categories.View, - precondition: ContextKeyExpr.equals('config.workbench.editor.editorActionsLocation', 'hidden'), + precondition: ContextKeyExpr.equals(`config.${LayoutSettings.EDITOR_ACTIONS_LOCATION}`, EditorActionsLocation.HIDDEN), f1: true }); } run(accessor: ServicesAccessor): Promise { const configurationService = accessor.get(IConfigurationService); - return configurationService.updateValue('workbench.editor.editorActionsLocation', 'default'); + return configurationService.updateValue(LayoutSettings.EDITOR_ACTIONS_LOCATION, EditorActionsLocation.DEFAULT); } } registerAction2(ShowEditorActionsAction); @@ -703,7 +704,7 @@ registerAction2(class extends Action2 { original: 'Separate Pinned Editor Tabs' }, category: Categories.View, - precondition: ContextKeyExpr.equals('config.workbench.editor.showTabs', 'multiple'), + precondition: ContextKeyExpr.equals(`config.${LayoutSettings.EDITOR_TABS_MODE}`, EditorTabsMode.MULTIPLE), f1: true }); } @@ -802,7 +803,7 @@ if (isWindows || isLinux || isWeb) { title: localize('miMenuBarNoMnemonic', "Menu Bar"), toggled: ContextKeyExpr.and(IsMacNativeContext.toNegated(), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'hidden'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'toggle'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'compact')) }, - when: ContextKeyExpr.and(IsAuxiliaryWindowFocusedContext.toNegated(), ContextKeyExpr.notEquals(`config.window.titleBarStyle`, 'native')), + when: ContextKeyExpr.and(IsAuxiliaryWindowFocusedContext.toNegated(), ContextKeyExpr.notEquals(TitleBarSetting.TITLE_BAR_STYLE, TitlebarStyle.NATIVE)), group: '2_config', order: 0 }); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index 4ac42e76029e7..27937ac6396db 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -12,15 +12,16 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ACCOUNTS_ACTIVITY_ID, GLOBAL_ACTIVITY_ID } from 'vs/workbench/common/activity'; import { IAction } from 'vs/base/common/actions'; -import { IsAuxiliaryWindowFocusedContext } from 'vs/workbench/common/contextkeys'; +import { IsAuxiliaryWindowFocusedContext, IsMainWindowFullscreenContext } from 'vs/workbench/common/contextkeys'; +import { CustomTitleBarVisibility, TitleBarSetting, TitlebarStyle } from 'vs/platform/window/common/window'; // --- Context Menu Actions --- // class ToggleConfigAction extends Action2 { - constructor(private readonly section: string, title: string, order: number, mainWindowOnly: boolean, showInCustomToolBar: boolean) { + constructor(private readonly section: string, title: string, order: number, mainWindowOnly: boolean, showInCustomTitleBarWhenNativeTitle: boolean) { let when = mainWindowOnly ? IsAuxiliaryWindowFocusedContext.toNegated() : ContextKeyExpr.true(); - when = showInCustomToolBar ? when : ContextKeyExpr.and(when, ContextKeyExpr.equals(`config.window.titleBarStyle`, 'native').negate())!; + when = showInCustomTitleBarWhenNativeTitle ? when : ContextKeyExpr.and(when, ContextKeyExpr.equals(TitleBarSetting.TITLE_BAR_STYLE, TitlebarStyle.NATIVE).negate())!; super({ id: `toggle.${section}`, @@ -62,23 +63,43 @@ registerAction2(class ToggleLayoutControl extends ToggleConfigAction { } }); -registerAction2(class ToggleCustomToolBar extends Action2 { - static readonly settingsID = `window.showCustomTitleBar`; +registerAction2(class ToggleCustomTitleBar extends Action2 { + static readonly settingsID = TitleBarSetting.TITLE_BAR_STYLE; constructor() { super({ - id: `toggle.${ToggleCustomToolBar.settingsID}`, - title: localize('toggle.toolBar', 'Custom Title Bar'), + id: `toggle.${ToggleCustomTitleBar.settingsID}`, + title: localize('toggle.customTitleBar', 'Custom Title Bar'), toggled: ContextKeyExpr.true(), menu: [ - { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.equals(`config.window.titleBarStyle`, 'native'), group: '3_toggle' }, + { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.equals(TitleBarSetting.TITLE_BAR_STYLE, TitlebarStyle.NATIVE), group: '3_toggle' }, ] }); } run(accessor: ServicesAccessor, ...args: any[]): void { const configService = accessor.get(IConfigurationService); - configService.updateValue(ToggleCustomToolBar.settingsID, 'never'); + configService.updateValue(ToggleCustomTitleBar.settingsID, CustomTitleBarVisibility.NEVER); + } +}); + +registerAction2(class ToggleCustomTitleBarWindowed extends Action2 { + static readonly settingsID = TitleBarSetting.TITLE_BAR_STYLE; + + constructor() { + super({ + id: `toggle.${ToggleCustomTitleBarWindowed.settingsID}.windowed`, + title: localize('toggle.customTitleBarWindowed', 'Custom Title Bar In Full Screen'), + toggled: ContextKeyExpr.true(), + menu: [ + { id: MenuId.TitleBarContext, order: 0, when: IsMainWindowFullscreenContext, group: '3_toggle' }, + ] + }); + } + + run(accessor: ServicesAccessor, ...args: any[]): void { + const configService = accessor.get(IConfigurationService); + configService.updateValue(ToggleCustomTitleBarWindowed.settingsID, CustomTitleBarVisibility.WINDOWED); } }); From e5a7abbc6ab7e690994c807b8154b2b2b9643829 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 18 Jan 2024 14:51:52 +0100 Subject: [PATCH 097/333] Mirror user edits into base model (#202723) --- .../browser/inlineChatController.ts | 36 ++--- .../inlineChat/browser/inlineChatSession.ts | 114 ++++++++++++- .../browser/inlineChatStrategies.ts | 36 +++-- .../test/browser/inlineChatSession.test.ts | 150 ++++++++++++++++-- 4 files changed, 291 insertions(+), 45 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 4a7fce76f9623..dc8433eaf9b49 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -42,7 +42,7 @@ import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IInlineChatSavingService } from './inlineChatSavingService'; import { EmptyResponse, ErrorResponse, ExpansionState, ReplyResponse, Session, SessionExchange, SessionPrompt } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { IInlineChatSessionService } from './inlineChatSessionService'; -import { EditModeStrategy, LivePreviewStrategy, LiveStrategy, PreviewStrategy, ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies'; +import { EditModeStrategy, IEditObserver, LivePreviewStrategy, LiveStrategy, PreviewStrategy, ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies'; import { IInlineChatMessageAppender, InlineChatZoneWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_RESPONSE_TYPES, CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING, CTX_INLINE_CHAT_USER_DID_EDIT, EditMode, IInlineChatProgressItem, IInlineChatRequest, IInlineChatResponse, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseFeedbackKind, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -127,7 +127,6 @@ export class InlineChatController implements IEditorContribution { private _session?: Session; private _strategy?: EditModeStrategy; - private _ignoreModelContentChanged = false; constructor( private readonly _editor: ICodeEditor, @@ -390,11 +389,11 @@ export class InlineChatController implements IEditorContribution { this._sessionStore.add(this._editor.onDidChangeModelContent(e => { - if (!this._ignoreModelContentChanged && this._strategy?.hasFocus()) { + if (!this._session?.hunkData.ignoreTextModelNChanges && this._strategy?.hasFocus()) { this._ctxUserDidEdit.set(altVersionNow !== this._editor.getModel()?.getAlternativeVersionId()); } - if (this._ignoreModelContentChanged || this._strategy?.hasFocus()) { + if (this._session?.hunkData.ignoreTextModelNChanges || this._strategy?.hasFocus()) { return; } @@ -479,10 +478,10 @@ export class InlineChatController implements IEditorContribution { } if (lastExchange.response instanceof ReplyResponse) { try { - this._ignoreModelContentChanged = true; + this._session.hunkData.ignoreTextModelNChanges = true; await this._strategy.undoChanges(lastExchange.response.modelAltVersionId); } finally { - this._ignoreModelContentChanged = false; + this._session.hunkData.ignoreTextModelNChanges = false; } } return State.MAKE_REQUEST; @@ -933,19 +932,20 @@ export class InlineChatController implements IEditorContribution { const actualEdits = !opts && moreMinimalEdits ? moreMinimalEdits : edits; const editOperations = actualEdits.map(TextEdit.asEditOperation); - try { - this._ignoreModelContentChanged = true; - this._inlineChatSavingService.markChanged(this._session); - this._session.wholeRange.trackEdits(editOperations); - if (opts) { - await this._strategy.makeProgressiveChanges(editOperations, opts); - } else { - await this._strategy.makeChanges(editOperations); - } - this._ctxDidEdit.set(this._session.hasChangedText); - } finally { - this._ignoreModelContentChanged = false; + const editsObserver: IEditObserver = { + start: () => this._session!.hunkData.ignoreTextModelNChanges = true, + stop: () => this._session!.hunkData.ignoreTextModelNChanges = false, + }; + + this._inlineChatSavingService.markChanged(this._session); + this._session.wholeRange.trackEdits(editOperations); + if (opts) { + await this._strategy.makeProgressiveChanges(editOperations, editsObserver, opts); + } else { + await this._strategy.makeChanges(editOperations, editsObserver); } + this._ctxDidEdit.set(this._session.hasChangedText); + } private _forcedPlaceholder: string | undefined = undefined; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index 97c7d2cab7c6b..63d31a12445d3 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -7,7 +7,7 @@ import { URI } from 'vs/base/common/uri'; import { Emitter, Event } from 'vs/base/common/event'; import { ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { IWorkspaceTextEdit, TextEdit, WorkspaceEdit } from 'vs/editor/common/languages'; -import { IModelDecorationOptions, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { IIdentifiedSingleEditOperation, IModelDecorationOptions, IModelDeltaDecoration, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; import { EditMode, IInlineChatSessionProvider, IInlineChatSession, IInlineChatBulkEditResponse, IInlineChatEditResponse, InlineChatResponseType, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; @@ -28,6 +28,8 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { asRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; import { coalesceInPlace } from 'vs/base/common/arrays'; import { Iterable } from 'vs/base/common/iterator'; +import { IModelContentChangedEvent } from 'vs/editor/common/textModelEvents'; +import { DisposableStore } from 'vs/base/common/lifecycle'; export type TelemetryData = { @@ -409,17 +411,27 @@ export class HunkData { private static readonly _HUNK_TRACKED_RANGE = ModelDecorationOptions.register({ description: 'inline-chat-hunk-tracked-range', + stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges }); private static readonly _HUNK_THRESHOLD = 8; + private readonly _store = new DisposableStore(); private readonly _data = new Map(); + private _ignoreChanges: boolean = false; constructor( @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, private readonly _textModel0: ITextModel, private readonly _textModelN: ITextModel, - ) { } + ) { + + this._store.add(_textModelN.onDidChangeContent(e => { + if (!this._ignoreChanges) { + this._mirrorChanges(e); + } + })); + } dispose(): void { if (!this._textModelN.isDisposed()) { @@ -436,6 +448,104 @@ export class HunkData { } }); } + this._data.clear(); + this._store.dispose(); + } + + set ignoreTextModelNChanges(value: boolean) { + this._ignoreChanges = value; + } + + get ignoreTextModelNChanges(): boolean { + return this._ignoreChanges; + } + + private _mirrorChanges(event: IModelContentChangedEvent) { + + // mirror textModelN changes to textModel0 execept for those that + // overlap with a hunk + + type HunkRangePair = { rangeN: Range; range0: Range }; + const hunkRanges: HunkRangePair[] = []; + + const ranges0: Range[] = []; + + for (const { textModelNDecorations, textModel0Decorations, state } of this._data.values()) { + + if (state === HunkState.Pending) { + // pending means the hunk's changes aren't "sync'd" yet + for (let i = 1; i < textModelNDecorations.length; i++) { + const rangeN = this._textModelN.getDecorationRange(textModelNDecorations[i]); + const range0 = this._textModel0.getDecorationRange(textModel0Decorations[i]); + if (rangeN && range0) { + hunkRanges.push({ rangeN, range0 }); + } + } + + } else if (state === HunkState.Accepted) { + // accepted means the hunk's changes are also in textModel0 + for (let i = 1; i < textModel0Decorations.length; i++) { + const range = this._textModel0.getDecorationRange(textModel0Decorations[i]); + if (range) { + ranges0.push(range); + } + } + } + } + + hunkRanges.sort((a, b) => Range.compareRangesUsingStarts(a.rangeN, b.rangeN)); + ranges0.sort(Range.compareRangesUsingStarts); + + const edits: IIdentifiedSingleEditOperation[] = []; + + for (const change of event.changes) { + + let isOverlapping = false; + + let pendingChangesLen = 0; + + for (const { rangeN, range0 } of hunkRanges) { + if (rangeN.getEndPosition().isBefore(Range.getStartPosition(change.range))) { + // pending hunk _before_ this change. When projecting into textModel0 we need to + // subtract that. Because diffing is relaxed it might include changes that are not + // actual insertions/deletions. Therefore we need to take the length of the original + // range into account. + pendingChangesLen += this._textModelN.getValueLengthInRange(rangeN); + pendingChangesLen -= this._textModel0.getValueLengthInRange(range0); + + } else if (Range.areIntersectingOrTouching(rangeN, change.range)) { + isOverlapping = true; + break; + + } else { + // hunks past this change aren't relevant + break; + } + } + + if (isOverlapping) { + // hunk overlaps, it grew + continue; + } + + const offset0 = change.rangeOffset - pendingChangesLen; + const start0 = this._textModel0.getPositionAt(offset0); + + let acceptedChangesLen = 0; + for (const range of ranges0) { + if (range.getEndPosition().isBefore(start0)) { + // accepted hunk _before_ this projected change. When projecting into textModel0 + // we need to add that + acceptedChangesLen += this._textModel0.getValueLengthInRange(range); + } + } + + const start = this._textModel0.getPositionAt(offset0 + acceptedChangesLen); + const end = this._textModel0.getPositionAt(offset0 + acceptedChangesLen + change.rangeLength); + edits.push(EditOperation.replace(Range.fromPositions(start, end), change.text)); + } + + this._textModel0.pushEditOperations(null, edits, () => null); } async recompute() { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index f118144130e2d..9ece4b6796725 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -39,6 +39,11 @@ import { CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX import { HunkState } from './inlineChatSession'; import { assertType } from 'vs/base/common/types'; +export interface IEditObserver { + start(): void; + stop(): void; +} + export abstract class EditModeStrategy { protected static _decoBlock = ModelDecorationOptions.register({ @@ -80,9 +85,9 @@ export abstract class EditModeStrategy { this._onDidDiscard.fire(); } - abstract makeProgressiveChanges(edits: ISingleEditOperation[], timings: ProgressingEditsOptions): Promise; + abstract makeProgressiveChanges(edits: ISingleEditOperation[], obs: IEditObserver, timings: ProgressingEditsOptions): Promise; - abstract makeChanges(edits: ISingleEditOperation[]): Promise; + abstract makeChanges(edits: ISingleEditOperation[], obs: IEditObserver): Promise; abstract undoChanges(altVersionId: number): Promise; @@ -239,7 +244,7 @@ export class LivePreviewStrategy extends EditModeStrategy { await undoModelUntil(modelN, targetAltVersion); } - override async makeChanges(edits: ISingleEditOperation[]): Promise { + override async makeChanges(edits: ISingleEditOperation[], obs: IEditObserver): Promise { const cursorStateComputerAndInlineDiffCollection: ICursorStateComputer = (undoEdits) => { let last: Position | null = null; for (const edit of undoEdits) { @@ -252,7 +257,9 @@ export class LivePreviewStrategy extends EditModeStrategy { if (++this._editCount === 1) { this._editor.pushUndoStop(); } + obs.start(); this._editor.executeEdits('inline-chat-live', edits, cursorStateComputerAndInlineDiffCollection); + obs.stop(); } override async undoChanges(altVersionId: number): Promise { @@ -261,7 +268,7 @@ export class LivePreviewStrategy extends EditModeStrategy { await this._updateDiffZones(); } - override async makeProgressiveChanges(edits: ISingleEditOperation[], opts: ProgressingEditsOptions): Promise { + override async makeProgressiveChanges(edits: ISingleEditOperation[], obs: IEditObserver, opts: ProgressingEditsOptions): Promise { // push undo stop before first edit if (++this._editCount === 1) { @@ -280,7 +287,7 @@ export class LivePreviewStrategy extends EditModeStrategy { const wordCount = countWords(edit.text ?? ''); const speed = wordCount / durationInSec; // console.log({ durationInSec, wordCount, speed: wordCount / durationInSec }); - await performAsyncTextEdit(this._session.textModelN, asProgressiveEdit(new WindowIntervalTimer(this._zone.domNode), edit, speed, opts.token)); + await performAsyncTextEdit(this._session.textModelN, asProgressiveEdit(new WindowIntervalTimer(this._zone.domNode), edit, speed, opts.token), undefined, obs); } await renderTask; @@ -398,7 +405,7 @@ export interface AsyncTextEdit { readonly newText: AsyncIterable; } -export async function performAsyncTextEdit(model: ITextModel, edit: AsyncTextEdit, progress?: IProgress) { +export async function performAsyncTextEdit(model: ITextModel, edit: AsyncTextEdit, progress?: IProgress, obs?: IEditObserver) { const [id] = model.deltaDecorations([], [{ range: edit.range, @@ -423,11 +430,12 @@ export async function performAsyncTextEdit(model: ITextModel, edit: AsyncTextEdi const edit = first ? EditOperation.replace(range, part) // first edit needs to override the "anchor" : EditOperation.insert(range.getEndPosition(), part); - + obs?.start(); model.pushEditOperations(null, [edit], (undoEdits) => { progress?.report(undoEdits); return null; }); + obs?.stop(); first = false; } } @@ -578,15 +586,15 @@ export class LiveStrategy extends EditModeStrategy { await undoModelUntil(textModelN, altVersionId); } - override async makeChanges(edits: ISingleEditOperation[]): Promise { - return this._makeChanges(edits, undefined); + override async makeChanges(edits: ISingleEditOperation[], obs: IEditObserver): Promise { + return this._makeChanges(edits, obs, undefined); } - override async makeProgressiveChanges(edits: ISingleEditOperation[], opts: ProgressingEditsOptions): Promise { - return this._makeChanges(edits, opts); + override async makeProgressiveChanges(edits: ISingleEditOperation[], obs: IEditObserver, opts: ProgressingEditsOptions): Promise { + return this._makeChanges(edits, obs, opts); } - private async _makeChanges(edits: ISingleEditOperation[], opts: ProgressingEditsOptions | undefined): Promise { + private async _makeChanges(edits: ISingleEditOperation[], obs: IEditObserver, opts: ProgressingEditsOptions | undefined): Promise { // push undo stop before first edit if (++this._editCount === 1) { @@ -619,15 +627,17 @@ export class LiveStrategy extends EditModeStrategy { const wordCount = countWords(edit.text ?? ''); const speed = wordCount / durationInSec; // console.log({ durationInSec, wordCount, speed: wordCount / durationInSec }); - await performAsyncTextEdit(this._session.textModelN, asProgressiveEdit(new WindowIntervalTimer(this._zone.domNode), edit, speed, opts.token), progress); + await performAsyncTextEdit(this._session.textModelN, asProgressiveEdit(new WindowIntervalTimer(this._zone.domNode), edit, speed, opts.token), progress, obs); } } else { // SYNC + obs.start(); this._editor.executeEdits('inline-chat-live', edits, undoEdits => { progress.report(undoEdits); return null; }); + obs.stop(); } } diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts index f5f42a2bac866..e21cc59da32ed 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts @@ -231,6 +231,22 @@ suite('InlineChatSession', function () { ensureNoDisposablesAreLeakedInTestSuite(); + async function makeEditAsAi(edit: EditOperation | EditOperation[]) { + const session = inlineChatSessionService.getSession(editor, editor.getModel()!.uri); + assertType(session); + session.hunkData.ignoreTextModelNChanges = true; + try { + editor.executeEdits('test', Array.isArray(edit) ? edit : [edit]); + } finally { + session.hunkData.ignoreTextModelNChanges = false; + } + await session.hunkData.recompute(); + } + + function makeEdit(edit: EditOperation | EditOperation[]) { + editor.executeEdits('test', Array.isArray(edit) ? edit : [edit]); + } + test('Create, release', async function () { const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); @@ -246,18 +262,19 @@ suite('InlineChatSession', function () { assertType(session); assert.ok(session.textModelN === model); - editor.executeEdits('test', [EditOperation.insert(new Position(1, 1), 'AI_EDIT\n')]); + await makeEditAsAi(EditOperation.insert(new Position(1, 1), 'AI_EDIT\n')); + - await session.hunkData.recompute(); assert.strictEqual(session.hunkData.size, 1); - const [hunk] = session.hunkData.getInfo(); + let [hunk] = session.hunkData.getInfo(); assertType(hunk); assert.ok(!session.textModel0.equalsTextBuffer(session.textModelN.getTextBuffer())); assert.strictEqual(hunk.getState(), HunkState.Pending); assert.ok(hunk.getRangesN()[0].equalsRange({ startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 7 })); - editor.executeEdits('test', [EditOperation.insert(new Position(1, 3), 'foobar')]); + await makeEditAsAi(EditOperation.insert(new Position(1, 3), 'foobar')); + [hunk] = session.hunkData.getInfo(); assert.ok(hunk.getRangesN()[0].equalsRange({ startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 13 })); inlineChatSessionService.releaseSession(session); @@ -270,9 +287,8 @@ suite('InlineChatSession', function () { const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); assertType(session); - editor.executeEdits('test', [EditOperation.insert(new Position(1, 1), 'AI_EDIT\n'), EditOperation.insert(new Position(10, 1), 'AI_EDIT\n')]); + await makeEditAsAi([EditOperation.insert(new Position(1, 1), 'AI_EDIT\n'), EditOperation.insert(new Position(10, 1), 'AI_EDIT\n')]); - await session.hunkData.recompute(); assert.strictEqual(session.hunkData.size, 2); assert.ok(!session.textModel0.equalsTextBuffer(session.textModelN.getTextBuffer())); @@ -292,9 +308,8 @@ suite('InlineChatSession', function () { const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); assertType(session); - editor.executeEdits('test', [EditOperation.insert(new Position(1, 1), 'AI_EDIT\n'), EditOperation.insert(new Position(10, 1), 'AI_EDIT\n')]); + await makeEditAsAi([EditOperation.insert(new Position(1, 1), 'AI_EDIT\n'), EditOperation.insert(new Position(10, 1), 'AI_EDIT\n')]); - await session.hunkData.recompute(); assert.strictEqual(session.hunkData.size, 2); assert.ok(!session.textModel0.equalsTextBuffer(session.textModelN.getTextBuffer())); @@ -321,13 +336,12 @@ suite('InlineChatSession', function () { assert.strictEqual(session.hunkData.size, 0); // ROUND #1 - editor.executeEdits('test', [ + await makeEditAsAi([ EditOperation.insert(new Position(1, 1), 'AI1'), EditOperation.insert(new Position(4, 1), 'AI2'), EditOperation.insert(new Position(19, 1), 'AI3') ]); - await session.hunkData.recompute(); assert.strictEqual(session.hunkData.size, 2); // AI1, AI2 are merged into one hunk, AI3 is a separate hunk let [first, second] = session.hunkData.getInfo(); @@ -347,10 +361,9 @@ suite('InlineChatSession', function () { // ROUND #2 - editor.executeEdits('test', [ + await makeEditAsAi([ EditOperation.insert(new Position(7, 1), 'AI4'), ]); - await session.hunkData.recompute(); assert.strictEqual(session.hunkData.size, 2); [first, second] = session.hunkData.getInfo(); @@ -359,4 +372,117 @@ suite('InlineChatSession', function () { inlineChatSessionService.releaseSession(session); }); + + test('HunkData, (mirror) edit before', async function () { + + const lines = ['one', 'two', 'three']; + model.setValue(lines.join('\n')); + const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); + assertType(session); + + await makeEditAsAi([EditOperation.insert(new Position(3, 1), 'AI WAS HERE\n')]); + assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'AI WAS HERE', 'three'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), lines.join('\n')); + + makeEdit([EditOperation.replace(new Range(1, 1, 1, 4), 'ONE')]); + assert.strictEqual(session.textModelN.getValue(), ['ONE', 'two', 'AI WAS HERE', 'three'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), ['ONE', 'two', 'three'].join('\n')); + }); + + test('HunkData, (mirror) edit after', async function () { + + const lines = ['one', 'two', 'three', 'four', 'five']; + model.setValue(lines.join('\n')); + + const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); + assertType(session); + + await makeEditAsAi([EditOperation.insert(new Position(3, 1), 'AI_EDIT\n')]); + + assert.strictEqual(session.hunkData.size, 1); + const [hunk] = session.hunkData.getInfo(); + + makeEdit([EditOperation.insert(new Position(1, 1), 'USER1')]); + assert.strictEqual(session.textModelN.getValue(), ['USER1one', 'two', 'AI_EDIT', 'three', 'four', 'five'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), ['USER1one', 'two', 'three', 'four', 'five'].join('\n')); + + makeEdit([EditOperation.insert(new Position(5, 1), 'USER2')]); + assert.strictEqual(session.textModelN.getValue(), ['USER1one', 'two', 'AI_EDIT', 'three', 'USER2four', 'five'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), ['USER1one', 'two', 'three', 'USER2four', 'five'].join('\n')); + + hunk.acceptChanges(); + assert.strictEqual(session.textModelN.getValue(), ['USER1one', 'two', 'AI_EDIT', 'three', 'USER2four', 'five'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), ['USER1one', 'two', 'AI_EDIT', 'three', 'USER2four', 'five'].join('\n')); + }); + + test('HunkData, (mirror) edit inside ', async function () { + + const lines = ['one', 'two', 'three']; + model.setValue(lines.join('\n')); + const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); + assertType(session); + + await makeEditAsAi([EditOperation.insert(new Position(3, 1), 'AI WAS HERE\n')]); + assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'AI WAS HERE', 'three'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), lines.join('\n')); + + makeEdit([EditOperation.replace(new Range(3, 4, 3, 7), 'wwaaassss')]); + assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'AI wwaaassss HERE', 'three'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three'].join('\n')); + }); + + test('HunkData, (mirror) edit after, multi turn', async function () { + + const lines = ['one', 'two', 'three', 'four', 'five']; + model.setValue(lines.join('\n')); + + const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); + assertType(session); + + await makeEditAsAi([EditOperation.insert(new Position(3, 1), 'AI_EDIT\n')]); + + assert.strictEqual(session.hunkData.size, 1); + + makeEdit([EditOperation.insert(new Position(5, 1), 'FOO')]); + assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'AI_EDIT', 'three', 'FOOfour', 'five'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three', 'FOOfour', 'five'].join('\n')); + + await makeEditAsAi([EditOperation.insert(new Position(2, 4), ' zwei')]); + assert.strictEqual(session.hunkData.size, 1); + + assert.strictEqual(session.textModelN.getValue(), ['one', 'two zwei', 'AI_EDIT', 'three', 'FOOfour', 'five'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three', 'FOOfour', 'five'].join('\n')); + + makeEdit([EditOperation.replace(new Range(6, 3, 6, 5), 'vefivefi')]); + assert.strictEqual(session.textModelN.getValue(), ['one', 'two zwei', 'AI_EDIT', 'three', 'FOOfour', 'fivefivefi'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three', 'FOOfour', 'fivefivefi'].join('\n')); + }); + + test('HunkData, (mirror) edit after, multi turn 2', async function () { + + const lines = ['one', 'two', 'three', 'four', 'five']; + model.setValue(lines.join('\n')); + + const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); + assertType(session); + + await makeEditAsAi([EditOperation.insert(new Position(3, 1), 'AI_EDIT\n')]); + + assert.strictEqual(session.hunkData.size, 1); + + makeEdit([EditOperation.insert(new Position(5, 1), 'FOO')]); + assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'AI_EDIT', 'three', 'FOOfour', 'five'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three', 'FOOfour', 'five'].join('\n')); + + await makeEditAsAi([EditOperation.insert(new Position(2, 4), 'zwei')]); + assert.strictEqual(session.hunkData.size, 1); + + assert.strictEqual(session.textModelN.getValue(), ['one', 'twozwei', 'AI_EDIT', 'three', 'FOOfour', 'five'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three', 'FOOfour', 'five'].join('\n')); + + makeEdit([EditOperation.replace(new Range(6, 3, 6, 5), 'vefivefi')]); + assert.strictEqual(session.textModelN.getValue(), ['one', 'twozwei', 'AI_EDIT', 'three', 'FOOfour', 'fivefivefi'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three', 'FOOfour', 'fivefivefi'].join('\n')); + }); + }); From 3530c3bedce5bd03e0ccf59610a53eb73f218fdd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 18 Jan 2024 14:57:26 +0100 Subject: [PATCH 098/333] update WorkspaceEdit#set signature to how it is implemented. Bulk setting allows for optional metadata (#202725) --- src/vs/workbench/api/common/extHostTypes.ts | 6 +++--- .../api/test/browser/extHostTypes.test.ts | 16 ++++++++++++++++ src/vscode-dts/vscode.d.ts | 4 ++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index cd84f1b167569..12fe0b4779d5e 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -862,11 +862,11 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { } set(uri: URI, edits: ReadonlyArray): void; - set(uri: URI, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, vscode.WorkspaceEditEntryMetadata]>): void; + set(uri: URI, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, vscode.WorkspaceEditEntryMetadata | undefined]>): void; set(uri: URI, edits: readonly NotebookEdit[]): void; - set(uri: URI, edits: ReadonlyArray<[NotebookEdit, vscode.WorkspaceEditEntryMetadata]>): void; + set(uri: URI, edits: ReadonlyArray<[NotebookEdit, vscode.WorkspaceEditEntryMetadata | undefined]>): void; - set(uri: URI, edits: null | undefined | ReadonlyArray): void { + set(uri: URI, edits: null | undefined | ReadonlyArray): void { if (!edits) { // remove all text, snippet, or notebook edits for `uri` for (let i = 0; i < this._edits.length; i++) { diff --git a/src/vs/workbench/api/test/browser/extHostTypes.test.ts b/src/vs/workbench/api/test/browser/extHostTypes.test.ts index 2586aaf331c93..8afe5e312371d 100644 --- a/src/vs/workbench/api/test/browser/extHostTypes.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTypes.test.ts @@ -435,6 +435,22 @@ suite('ExtHostTypes', function () { assert.strictEqual(second.edit.newText, 'Foo'); }); + test('WorkspaceEdit - set with metadata accepts undefined', function () { + const edit = new types.WorkspaceEdit(); + const uri = URI.parse('foo:bar'); + + edit.set(uri, [ + [types.TextEdit.insert(new types.Position(0, 0), 'Hello'), { needsConfirmation: true, label: 'foo' }], + [types.TextEdit.insert(new types.Position(0, 0), 'Hello'), undefined], + ]); + + const all = edit._allEntries(); + assert.strictEqual(all.length, 2); + const [first, second] = all; + assert.ok(first.metadata); + assert.ok(!second.metadata); + }); + test('DocumentLink', () => { assert.throws(() => new types.DocumentLink(null!, null!)); assert.throws(() => new types.DocumentLink(new types.Range(1, 1, 1, 1), null!)); diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 5a7ea8aec469a..da13bcf7a35d2 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -3952,7 +3952,7 @@ declare module 'vscode' { * @param uri A resource identifier. * @param edits An array of edits. */ - set(uri: Uri, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, WorkspaceEditEntryMetadata]>): void; + set(uri: Uri, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, WorkspaceEditEntryMetadata | undefined]>): void; /** * Set (and replace) notebook edits for a resource. @@ -3968,7 +3968,7 @@ declare module 'vscode' { * @param uri A resource identifier. * @param edits An array of edits. */ - set(uri: Uri, edits: ReadonlyArray<[NotebookEdit, WorkspaceEditEntryMetadata]>): void; + set(uri: Uri, edits: ReadonlyArray<[NotebookEdit, WorkspaceEditEntryMetadata | undefined]>): void; /** * Get the text edits for a resource. From 04f554b0af1abb516c022a87f568a5b91548bb52 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 18 Jan 2024 14:59:47 +0100 Subject: [PATCH 099/333] speech - scaffold keyword activation (#202643) * speech - scaffold keyword activation * first cut settting for keyword activation * handle lifecycle better * . * . * . * tweaks * show a status bar entry * add in context option * cleanup --- src/vs/base/common/event.ts | 21 -- src/vs/base/test/common/event.test.ts | 32 +-- .../workbench/api/browser/mainThreadSpeech.ts | 45 +++- .../workbench/api/common/extHost.api.impl.ts | 3 +- .../workbench/api/common/extHost.protocol.ts | 6 +- src/vs/workbench/api/common/extHostSpeech.ts | 28 +++ src/vs/workbench/api/common/extHostTypes.ts | 5 + .../browser/accessibility.contribution.ts | 3 +- .../browser/accessibilityConfiguration.ts | 60 +++-- .../actions/voiceChatActions.ts | 223 +++++++++++++++++- .../electron-sandbox/chat.contribution.ts | 7 +- .../services/notebookEditorServiceImpl.ts | 4 +- .../contrib/speech/common/speechService.ts | 146 +++++++++++- .../editor/common/editorGroupsService.ts | 12 +- .../test/browser/workbenchTestServices.ts | 2 + src/vscode-dts/vscode.proposed.speech.d.ts | 20 +- 16 files changed, 514 insertions(+), 103 deletions(-) diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index e0070330e1d5a..3686b27d8e8a9 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -622,27 +622,6 @@ export namespace Event { return event(e => handler(e)); } - /** - * Adds a listener to an event and calls the listener immediately with undefined as the event object. A new - * {@link DisposableStore} is passed to the listener which is disposed when the returned disposable is disposed. - */ - export function runAndSubscribeWithStore(event: Event, handler: (e: T | undefined, disposableStore: DisposableStore) => any): IDisposable { - let store: DisposableStore | null = null; - - function run(e: T | undefined) { - store?.dispose(); - store = new DisposableStore(); - handler(e, store); - } - - run(undefined); - const disposable = event(e => run(e)); - return toDisposable(() => { - disposable.dispose(); - store?.dispose(); - }); - } - class EmitterObserver implements IObserver { readonly emitter: Emitter; diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index df7aac4363d75..49962c89d5ac5 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -8,7 +8,7 @@ import { DeferredPromise, timeout } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { errorHandler, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { AsyncEmitter, DebounceEmitter, DynamicListEventMultiplexer, Emitter, Event, EventBufferer, EventMultiplexer, IWaitUntil, MicrotaskEmitter, PauseableEmitter, Relay, createEventDeliveryQueue } from 'vs/base/common/event'; -import { DisposableStore, IDisposable, isDisposable, setDisposableTracker, toDisposable, DisposableTracker } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, isDisposable, setDisposableTracker, DisposableTracker } from 'vs/base/common/lifecycle'; import { observableValue, transaction } from 'vs/base/common/observable'; import { MicrotaskDelay } from 'vs/base/common/symbols'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; @@ -1272,36 +1272,6 @@ suite('Event utils', () => { }); }); - test('runAndSubscribeWithStore', () => { - const eventEmitter = ds.add(new Emitter()); - const event = eventEmitter.event; - - let i = 0; - const log = new Array(); - const disposable = Event.runAndSubscribeWithStore(event, (e, disposables) => { - const idx = i++; - log.push({ label: 'handleEvent', data: e || null, idx }); - disposables.add(toDisposable(() => { - log.push({ label: 'dispose', idx }); - })); - }); - - log.push({ label: 'fire' }); - eventEmitter.fire('someEventData'); - - log.push({ label: 'disposeAll' }); - disposable.dispose(); - - assert.deepStrictEqual(log, [ - { label: 'handleEvent', data: null, idx: 0 }, - { label: 'fire' }, - { label: 'dispose', idx: 0 }, - { label: 'handleEvent', data: 'someEventData', idx: 1 }, - { label: 'disposeAll' }, - { label: 'dispose', idx: 1 }, - ]); - }); - suite('accumulate', () => { test('should not fire after a listener is disposed with undefined or []', async () => { const eventEmitter = ds.add(new Emitter()); diff --git a/src/vs/workbench/api/browser/mainThreadSpeech.ts b/src/vs/workbench/api/browser/mainThreadSpeech.ts index d0c7acdc2b699..fa52270cdf038 100644 --- a/src/vs/workbench/api/browser/mainThreadSpeech.ts +++ b/src/vs/workbench/api/browser/mainThreadSpeech.ts @@ -8,20 +8,26 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostContext, ExtHostSpeechShape, MainContext, MainThreadSpeechShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ISpeechProviderMetadata, ISpeechService, ISpeechToTextEvent } from 'vs/workbench/contrib/speech/common/speechService'; +import { IKeywordRecognitionEvent, ISpeechProviderMetadata, ISpeechService, ISpeechToTextEvent } from 'vs/workbench/contrib/speech/common/speechService'; import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; type SpeechToTextSession = { readonly onDidChange: Emitter; }; +type KeywordRecognitionSession = { + readonly onDidChange: Emitter; +}; + @extHostNamedCustomer(MainContext.MainThreadSpeech) export class MainThreadSpeech extends Disposable implements MainThreadSpeechShape { private readonly proxy: ExtHostSpeechShape; private readonly providerRegistrations = new Map(); - private readonly providerSessions = new Map(); + + private readonly speechToTextSessions = new Map(); + private readonly keywordRecognitionSessions = new Map(); constructor( extHostContext: IExtHostContext, @@ -47,13 +53,33 @@ export class MainThreadSpeech extends Disposable implements MainThreadSpeechShap disposables.add(token.onCancellationRequested(() => this.proxy.$cancelSpeechToTextSession(session))); const onDidChange = disposables.add(new Emitter()); - this.providerSessions.set(session, { onDidChange }); + this.speechToTextSessions.set(session, { onDidChange }); return { onDidChange: onDidChange.event, dispose: () => { cts.dispose(true); - this.providerSessions.delete(session); + this.speechToTextSessions.delete(session); + disposables.dispose(); + } + }; + }, + createKeywordRecognitionSession: token => { + const disposables = new DisposableStore(); + const cts = new CancellationTokenSource(token); + const session = Math.random(); + + this.proxy.$createKeywordRecognitionSession(handle, session); + disposables.add(token.onCancellationRequested(() => this.proxy.$cancelKeywordRecognitionSession(session))); + + const onDidChange = disposables.add(new Emitter()); + this.keywordRecognitionSessions.set(session, { onDidChange }); + + return { + onDidChange: onDidChange.event, + dispose: () => { + cts.dispose(true); + this.keywordRecognitionSessions.delete(session); disposables.dispose(); } }; @@ -75,9 +101,12 @@ export class MainThreadSpeech extends Disposable implements MainThreadSpeechShap } $emitSpeechToTextEvent(session: number, event: ISpeechToTextEvent): void { - const providerSession = this.providerSessions.get(session); - if (providerSession) { - providerSession.onDidChange.fire(event); - } + const providerSession = this.speechToTextSessions.get(session); + providerSession?.onDidChange.fire(event); + } + + $emitKeywordRecognitionEvent(session: number, event: IKeywordRecognitionEvent): void { + const providerSession = this.keywordRecognitionSessions.get(session); + providerSession?.onDidChange.fire(event); } } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index ee65e81f97f8e..dd36b43f34f41 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1627,7 +1627,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I StackFrameFocus: extHostTypes.StackFrameFocus, ThreadFocus: extHostTypes.ThreadFocus, RelatedInformationType: extHostTypes.RelatedInformationType, - SpeechToTextStatus: extHostTypes.SpeechToTextStatus + SpeechToTextStatus: extHostTypes.SpeechToTextStatus, + KeywordRecognitionStatus: extHostTypes.KeywordRecognitionStatus }; }; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 4f789f46598eb..978e0179e80e1 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -64,7 +64,7 @@ import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { InputValidationType } from 'vs/workbench/contrib/scm/common/scm'; import { IWorkspaceSymbol, NotebookPriorityInfo } from 'vs/workbench/contrib/search/common/search'; import { IRawClosedNotebookFileMatch } from 'vs/workbench/contrib/search/common/searchNotebookHelpers'; -import { ISpeechProviderMetadata, ISpeechToTextEvent } from 'vs/workbench/contrib/speech/common/speechService'; +import { IKeywordRecognitionEvent, ISpeechProviderMetadata, ISpeechToTextEvent } from 'vs/workbench/contrib/speech/common/speechService'; import { CoverageDetails, ExtensionRunTestsRequest, ICallProfileRunHandler, IFileCoverage, ISerializedTestResults, IStartControllerTests, ITestItem, ITestMessage, ITestRunProfile, ITestRunTask, ResolvedTestRunRequest, TestResultState, TestsDiffOp } from 'vs/workbench/contrib/testing/common/testTypes'; import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline'; import { TypeHierarchyItem } from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy'; @@ -1159,11 +1159,15 @@ export interface MainThreadSpeechShape extends IDisposable { $unregisterProvider(handle: number): void; $emitSpeechToTextEvent(session: number, event: ISpeechToTextEvent): void; + $emitKeywordRecognitionEvent(session: number, event: IKeywordRecognitionEvent): void; } export interface ExtHostSpeechShape { $createSpeechToTextSession(handle: number, session: number): Promise; $cancelSpeechToTextSession(session: number): Promise; + + $createKeywordRecognitionSession(handle: number, session: number): Promise; + $cancelKeywordRecognitionSession(session: number): Promise; } export interface MainThreadChatProviderShape extends IDisposable { diff --git a/src/vs/workbench/api/common/extHostSpeech.ts b/src/vs/workbench/api/common/extHostSpeech.ts index 8207ab47a4797..8d230fd19f2f9 100644 --- a/src/vs/workbench/api/common/extHostSpeech.ts +++ b/src/vs/workbench/api/common/extHostSpeech.ts @@ -52,6 +52,34 @@ export class ExtHostSpeech implements ExtHostSpeechShape { this.sessions.delete(session); } + async $createKeywordRecognitionSession(handle: number, session: number): Promise { + const provider = this.providers.get(handle); + if (!provider) { + return; + } + + const disposables = new DisposableStore(); + + const cts = new CancellationTokenSource(); + this.sessions.set(session, cts); + + const keywordRecognitionSession = disposables.add(provider.provideKeywordRecognitionSession(cts.token)); + disposables.add(keywordRecognitionSession.onDidChange(e => { + if (cts.token.isCancellationRequested) { + return; + } + + this.proxy.$emitKeywordRecognitionEvent(session, e); + })); + + disposables.add(cts.token.onCancellationRequested(() => disposables.dispose())); + } + + async $cancelKeywordRecognitionSession(session: number): Promise { + this.sessions.get(session)?.dispose(true); + this.sessions.delete(session); + } + registerProvider(extension: ExtensionIdentifier, identifier: string, provider: vscode.SpeechProvider): IDisposable { const handle = ExtHostSpeech.ID_POOL++; diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 12fe0b4779d5e..4c45b385fa867 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4205,4 +4205,9 @@ export enum SpeechToTextStatus { Stopped = 4 } +export enum KeywordRecognitionStatus { + Recognized = 1, + Stopped = 2 +} + //#endregion diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts index 5df70bb0761a7..ac3d45e7a7094 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { registerAccessibilityConfiguration } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { DynamicSpeechAccessibilityConfiguration, registerAccessibilityConfiguration } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -30,3 +30,4 @@ workbenchContributionsRegistry.registerWorkbenchContribution(NotificationAccessi workbenchContributionsRegistry.registerWorkbenchContribution(InlineCompletionsAccessibleViewContribution, LifecyclePhase.Eventually); workbenchContributionsRegistry.registerWorkbenchContribution(AccessibilityStatus, LifecyclePhase.Ready); workbenchContributionsRegistry.registerWorkbenchContribution(SaveAudioCueContribution, LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(DynamicSpeechAccessibilityConfiguration, LifecyclePhase.Ready); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 16b03b216cd7d..5c37fed8c8c5d 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -9,6 +9,10 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; import { AccessibilityAlertSettingId } from 'vs/platform/audioCues/browser/audioCueService'; +import { ISpeechService } from 'vs/workbench/contrib/speech/common/speechService'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { Event } from 'vs/base/common/event'; export const accessibilityHelpIsShown = new RawContextKey('accessibilityHelpIsShown', false, true); export const accessibleViewIsShown = new RawContextKey('accessibleViewIsShown', false, true); @@ -35,11 +39,6 @@ export const enum ViewDimUnfocusedOpacityProperties { Maximum = 1 } -export const enum AccessibilityVoiceSettingId { - SpeechTimeout = 'accessibility.voice.speechTimeout', -} -export const SpeechTimeoutDefault = 1200; - export const enum AccessibilityVerbositySettingId { Terminal = 'accessibility.verbosity.terminal', DiffEditor = 'accessibility.verbosity.diffEditor', @@ -77,10 +76,14 @@ const baseProperty: object = { tags: ['accessibility'] }; -const configuration: IConfigurationNode = { +export const accessibilityConfigurationNodeBase = Object.freeze({ id: 'accessibility', title: localize('accessibilityConfigurationTitle', "Accessibility"), - type: 'object', + type: 'object' +}); + +const configuration: IConfigurationNode = { + ...accessibilityConfigurationNodeBase, properties: { [AccessibilityVerbositySettingId.Terminal]: { description: localize('verbosity.terminal.description', 'Provide information about how to access the terminal accessibility help menu when the terminal is focused.'), @@ -251,13 +254,6 @@ const configuration: IConfigurationNode = { 'default': true, tags: ['accessibility'] }, - [AccessibilityVoiceSettingId.SpeechTimeout]: { - 'markdownDescription': localize('voice.speechTimeout', "The duration in milliseconds that voice speech recognition remains active after you stop speaking. For example in a chat session, the transcribed text is submitted automatically after the timeout is met. Set to `0` to disable this feature."), - 'type': 'number', - 'default': SpeechTimeoutDefault, - 'minimum': 0, - 'tags': ['accessibility'] - }, [AccessibilityWorkbenchSettingId.AccessibleViewCloseOnKeyPress]: { markdownDescription: localize('terminal.integrated.accessibleView.closeOnKeyPress', "On keypress, close the Accessible View and focus the element from which it was invoked."), type: 'boolean', @@ -298,3 +294,39 @@ export function registerAccessibilityConfiguration() { } }); } + +export const enum AccessibilityVoiceSettingId { + SpeechTimeout = 'accessibility.voice.speechTimeout' +} +export const SpeechTimeoutDefault = 1200; + +export class DynamicSpeechAccessibilityConfiguration extends Disposable implements IWorkbenchContribution { + + constructor( + @ISpeechService private readonly speechService: ISpeechService + ) { + super(); + + this._register(Event.runAndSubscribe(speechService.onDidRegisterSpeechProvider, () => this.updateConfiguration())); + } + + private updateConfiguration(): void { + if (!this.speechService.hasSpeechProvider) { + return; // these settings require a speech provider + } + + const registry = Registry.as(Extensions.Configuration); + registry.registerConfiguration({ + ...accessibilityConfigurationNodeBase, + properties: { + [AccessibilityVoiceSettingId.SpeechTimeout]: { + 'markdownDescription': localize('voice.speechTimeout', "The duration in milliseconds that voice speech recognition remains active after you stop speaking. For example in a chat session, the transcribed text is submitted automatically after the timeout is met. Set to `0` to disable this feature."), + 'type': 'number', + 'default': SpeechTimeoutDefault, + 'minimum': 0, + 'tags': ['accessibility'] + } + } + }); + } +} diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 2a684fff64b63..11dcef4882af4 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -8,7 +8,7 @@ import { Event } from 'vs/base/common/event'; import { firstOrDefault } from 'vs/base/common/arrays'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; import { Action2, MenuId } from 'vs/platform/actions/common/actions'; @@ -23,14 +23,14 @@ import { CONTEXT_CHAT_REQUEST_IN_PROGRESS, CONTEXT_PROVIDER_EXISTS } from 'vs/wo import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { ICommandService } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { ActiveEditorContext } from 'vs/workbench/common/contextkeys'; import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; -import { HasSpeechProvider, ISpeechService, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; +import { HasSpeechProvider, ISpeechService, KeywordRecognitionStatus, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; import { RunOnceScheduler } from 'vs/base/common/async'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ACTIVITY_BAR_BADGE_BACKGROUND } from 'vs/workbench/common/theme'; @@ -39,8 +39,13 @@ import { Color } from 'vs/base/common/color'; import { contrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { isNumber } from 'vs/base/common/types'; -import { AccessibilityVoiceSettingId, SpeechTimeoutDefault } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { AccessibilityVoiceSettingId, SpeechTimeoutDefault, accessibilityConfigurationNodeBase } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { IChatExecuteActionContext } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when getting ready for receiving voice input from the microphone for voice chat.") }); const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when voice recording from microphone is in progress for voice chat.") }); @@ -747,3 +752,213 @@ registerThemingParticipant((theme, collector) => { } `); }); + +export class KeywordActivationContribution extends Disposable implements IWorkbenchContribution { + + static SETTINGS_ID = 'accessibility.voice.keywordActivation'; + + static SETTINGS_VALUE = { + OFF: 'off', + INLINE_CHAT: 'inlineChat', + QUICK_CHAT: 'quickChat', + VIEW_CHAT: 'chatInView', + CHAT_IN_CONTEXT: 'chatInContext' + }; + + private activeSession: CancellationTokenSource | undefined = undefined; + + constructor( + @ISpeechService private readonly speechService: ISpeechService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @ICommandService private readonly commandService: ICommandService, + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(); + + this._register(instantiationService.createInstance(KeywordActivationStatusEntry)); + + this.registerListeners(); + } + + private registerListeners(): void { + this._register(Event.runAndSubscribe(this.speechService.onDidRegisterSpeechProvider, () => { + this.updateConfiguration(); + this.handleKeywordActivation(); + })); + + this._register(this.speechService.onDidStartSpeechToTextSession(() => this.handleKeywordActivation())); + this._register(this.speechService.onDidEndSpeechToTextSession(() => this.handleKeywordActivation())); + + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(KeywordActivationContribution.SETTINGS_ID)) { + this.handleKeywordActivation(); + } + })); + + this._register(this.editorGroupService.onDidCreateAuxiliaryEditorPart(({ instantiationService, disposables }) => { + disposables.add(instantiationService.createInstance(KeywordActivationStatusEntry)); + })); + } + + private updateConfiguration(): void { + if (!this.speechService.hasSpeechProvider) { + return; // these settings require a speech provider + } + + const registry = Registry.as(Extensions.Configuration); + registry.registerConfiguration({ + ...accessibilityConfigurationNodeBase, + properties: { + [KeywordActivationContribution.SETTINGS_ID]: { + 'type': 'string', + 'enum': [ + KeywordActivationContribution.SETTINGS_VALUE.OFF, + KeywordActivationContribution.SETTINGS_VALUE.VIEW_CHAT, + KeywordActivationContribution.SETTINGS_VALUE.QUICK_CHAT, + KeywordActivationContribution.SETTINGS_VALUE.INLINE_CHAT, + KeywordActivationContribution.SETTINGS_VALUE.CHAT_IN_CONTEXT + ], + 'enumDescriptions': [ + localize('voice.keywordActivation.off', "Keyword activation is disabled."), + localize('voice.keywordActivation.chatInView', "Keyword activation is enabled and listening for 'Hey Code' to start a voice chat session in the chat view."), + localize('voice.keywordActivation.quickChat', "Keyword activation is enabled and listening for 'Hey Code' to start a voice chat session in the quick chat."), + localize('voice.keywordActivation.inlineChat', "Keyword activation is enabled and listening for 'Hey Code' to start a voice chat session in the active editor."), + localize('voice.keywordActivation.chatInContext', "Keyword activation is enabled and listening for 'Hey Code' to start a voice chat session in the active editor or view depending on keyboard focus.") + ], + 'description': localize('voice.keywordActivation', "Controls whether the phrase 'Hey Code' should be speech recognized to start a voice chat session."), + 'default': 'off', + 'tags': ['accessibility'] + } + } + }); + } + + private handleKeywordActivation(): void { + const enabled = + this.speechService.hasSpeechProvider && + this.configurationService.getValue(KeywordActivationContribution.SETTINGS_ID) !== KeywordActivationContribution.SETTINGS_VALUE.OFF && + !this.speechService.hasActiveSpeechToTextSession; + if ( + (enabled && this.activeSession) || + (!enabled && !this.activeSession) + ) { + return; // already running or stopped + } + + // Start keyword activation + if (enabled) { + this.enableKeywordActivation(); + } + + // Stop keyword activation + else { + this.disableKeywordActivation(); + } + } + + private async enableKeywordActivation(): Promise { + const session = this.activeSession = new CancellationTokenSource(); + const result = await this.speechService.recognizeKeyword(session.token); + if (session.token.isCancellationRequested || session !== this.activeSession) { + return; // cancelled + } + + this.activeSession = undefined; + + if (result === KeywordRecognitionStatus.Recognized) { + this.commandService.executeCommand(this.getKeywordCommand()); + } + } + + private getKeywordCommand(): string { + const setting = this.configurationService.getValue(KeywordActivationContribution.SETTINGS_ID); + switch (setting) { + case KeywordActivationContribution.SETTINGS_VALUE.INLINE_CHAT: + return InlineVoiceChatAction.ID; + case KeywordActivationContribution.SETTINGS_VALUE.QUICK_CHAT: + return QuickVoiceChatAction.ID; + case KeywordActivationContribution.SETTINGS_VALUE.CHAT_IN_CONTEXT: + return StartVoiceChatAction.ID; + default: + return VoiceChatInChatViewAction.ID; + } + } + + private disableKeywordActivation(): void { + this.activeSession?.dispose(true); + this.activeSession = undefined; + } + + override dispose(): void { + this.activeSession?.dispose(); + + super.dispose(); + } +} + +class KeywordActivationStatusEntry extends Disposable { + + private readonly entry = this._register(new MutableDisposable()); + + private static STATUS_NAME = localize('keywordActivation.status.name', "Voice Keyword Activation"); + private static STATUS_COMMAND = 'keywordActivation.status.command'; + private static STATUS_ACTIVE = localize('keywordActivation.status.active', "Voice Keyword Activation: Active"); + private static STATUS_INACTIVE = localize('keywordActivation.status.inactive', "Voice Keyword Activation: Inactive"); + + constructor( + @ISpeechService private readonly speechService: ISpeechService, + @IStatusbarService private readonly statusbarService: IStatusbarService, + @ICommandService private readonly commandService: ICommandService, + @IConfigurationService private readonly configurationService: IConfigurationService, + ) { + super(); + + CommandsRegistry.registerCommand(KeywordActivationStatusEntry.STATUS_COMMAND, () => this.commandService.executeCommand('workbench.action.openSettings', KeywordActivationContribution.SETTINGS_ID)); + + this.registerListeners(); + this.updateStatusEntry(); + } + + private registerListeners(): void { + this._register(this.speechService.onDidStartKeywordRecognition(() => this.updateStatusEntry())); + this._register(this.speechService.onDidEndKeywordRecognition(() => this.updateStatusEntry())); + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(KeywordActivationContribution.SETTINGS_ID)) { + this.updateStatusEntry(); + } + })); + } + + private updateStatusEntry(): void { + const visible = this.configurationService.getValue(KeywordActivationContribution.SETTINGS_ID) !== KeywordActivationContribution.SETTINGS_VALUE.OFF; + if (visible) { + if (!this.entry.value) { + this.createStatusEntry(); + } + + this.updateStatusLabel(); + } else { + this.entry.clear(); + } + } + + private createStatusEntry() { + this.entry.value = this.statusbarService.addEntry(this.getStatusEntryProperties(), 'status.voiceKeywordActivation', StatusbarAlignment.RIGHT, 103); + } + + private getStatusEntryProperties(): IStatusbarEntry { + return { + name: KeywordActivationStatusEntry.STATUS_NAME, + text: '$(mic)', + tooltip: this.speechService.hasActiveKeywordRecognition ? KeywordActivationStatusEntry.STATUS_ACTIVE : KeywordActivationStatusEntry.STATUS_INACTIVE, + ariaLabel: this.speechService.hasActiveKeywordRecognition ? KeywordActivationStatusEntry.STATUS_ACTIVE : KeywordActivationStatusEntry.STATUS_INACTIVE, + command: KeywordActivationStatusEntry.STATUS_COMMAND, + kind: this.speechService.hasActiveKeywordRecognition ? 'prominent' : 'standard' + }; + } + + private updateStatusLabel(): void { + this.entry.value?.update(this.getStatusEntryProperties()); + } +} 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 9ba5430737e4a..4d260fce9177b 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts @@ -3,8 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { InlineVoiceChatAction, QuickVoiceChatAction, StartVoiceChatAction, StopListeningInInlineChatAction, StopListeningInQuickChatAction, StopListeningInChatEditorAction, StopListeningInChatViewAction, VoiceChatInChatViewAction, StopListeningAction, StopListeningAndSubmitAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; +import { InlineVoiceChatAction, QuickVoiceChatAction, StartVoiceChatAction, StopListeningInInlineChatAction, StopListeningInQuickChatAction, StopListeningInChatEditorAction, StopListeningInChatViewAction, VoiceChatInChatViewAction, StopListeningAction, StopListeningAndSubmitAction, KeywordActivationContribution } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; import { registerAction2 } from 'vs/platform/actions/common/actions'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; registerAction2(StartVoiceChatAction); @@ -20,3 +23,5 @@ registerAction2(StopListeningInChatEditorAction); registerAction2(StopListeningInQuickChatAction); registerAction2(StopListeningInInlineChatAction); +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(KeywordActivationContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts index 46f2bd5e3bc11..fddd0c63854e1 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts @@ -7,7 +7,7 @@ import { CodeWindow } from 'vs/base/browser/window'; import { ResourceMap } from 'vs/base/common/map'; import { getDefaultNotebookCreationOptions, NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { IEditorGroupsService, IEditorGroup, IAuxiliaryEditorPart } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { isCompositeNotebookEditorInput, NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { IBorrowValue, INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; @@ -130,7 +130,7 @@ export class NotebookEditorWidgetService implements INotebookEditorService { const sourcePart = this.editorGroupService.getPart(sourceID); const targetPart = this.editorGroupService.getPart(targetID); - if ((sourcePart as IAuxiliaryEditorPart).windowId !== (targetPart as IAuxiliaryEditorPart).windowId) { + if (sourcePart.windowId !== targetPart.windowId) { return; } diff --git a/src/vs/workbench/contrib/speech/common/speechService.ts b/src/vs/workbench/contrib/speech/common/speechService.ts index 13286662bb553..807dc5f7a952f 100644 --- a/src/vs/workbench/contrib/speech/common/speechService.ts +++ b/src/vs/workbench/contrib/speech/common/speechService.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { firstOrDefault } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -34,14 +34,29 @@ export interface ISpeechToTextEvent { readonly text?: string; } +export interface ISpeechToTextSession extends IDisposable { + readonly onDidChange: Event; +} + +export enum KeywordRecognitionStatus { + Recognized = 1, + Stopped = 2 +} + +export interface IKeywordRecognitionEvent { + readonly status: KeywordRecognitionStatus; + readonly text?: string; +} + +export interface IKeywordRecognitionSession extends IDisposable { + readonly onDidChange: Event; +} + export interface ISpeechProvider { readonly metadata: ISpeechProviderMetadata; createSpeechToTextSession(token: CancellationToken): ISpeechToTextSession; -} - -export interface ISpeechToTextSession extends IDisposable { - readonly onDidChange: Event; + createKeywordRecognitionSession(token: CancellationToken): IKeywordRecognitionSession; } export interface ISpeechService { @@ -55,20 +70,41 @@ export interface ISpeechService { registerSpeechProvider(identifier: string, provider: ISpeechProvider): IDisposable; + readonly onDidStartSpeechToTextSession: Event; + readonly onDidEndSpeechToTextSession: Event; + + readonly hasActiveSpeechToTextSession: boolean; + + /** + * Starts to transcribe speech from the default microphone. The returned + * session object provides an event to subscribe for transcribed text. + */ createSpeechToTextSession(token: CancellationToken): ISpeechToTextSession; + + readonly onDidStartKeywordRecognition: Event; + readonly onDidEndKeywordRecognition: Event; + + readonly hasActiveKeywordRecognition: boolean; + + /** + * Starts to recognize a keyword from the default microphone. The returned + * status indicates if the keyword was recognized or if the session was + * stopped. + */ + recognizeKeyword(token: CancellationToken): Promise; } -export class SpeechService implements ISpeechService { +export class SpeechService extends Disposable implements ISpeechService { readonly _serviceBrand: undefined; - private readonly _onDidRegisterSpeechProvider = new Emitter(); + private readonly _onDidRegisterSpeechProvider = this._register(new Emitter()); readonly onDidRegisterSpeechProvider = this._onDidRegisterSpeechProvider.event; - private readonly _onDidUnregisterSpeechProvider = new Emitter(); + private readonly _onDidUnregisterSpeechProvider = this._register(new Emitter()); readonly onDidUnregisterSpeechProvider = this._onDidUnregisterSpeechProvider.event; - get hasSpeechProvider(): boolean { return this.providers.size > 0; } + get hasSpeechProvider() { return this.providers.size > 0; } private readonly providers = new Map(); @@ -78,6 +114,7 @@ export class SpeechService implements ISpeechService { @ILogService private readonly logService: ILogService, @IContextKeyService private readonly contextKeyService: IContextKeyService ) { + super(); } registerSpeechProvider(identifier: string, provider: ISpeechProvider): IDisposable { @@ -100,6 +137,15 @@ export class SpeechService implements ISpeechService { }); } + private readonly _onDidStartSpeechToTextSession = this._register(new Emitter()); + readonly onDidStartSpeechToTextSession = this._onDidStartSpeechToTextSession.event; + + private readonly _onDidEndSpeechToTextSession = this._register(new Emitter()); + readonly onDidEndSpeechToTextSession = this._onDidEndSpeechToTextSession.event; + + private _activeSpeechToTextSession: ISpeechToTextSession | undefined = undefined; + get hasActiveSpeechToTextSession() { return !!this._activeSpeechToTextSession; } + createSpeechToTextSession(token: CancellationToken): ISpeechToTextSession { const provider = firstOrDefault(Array.from(this.providers.values())); if (!provider) { @@ -108,6 +154,86 @@ export class SpeechService implements ISpeechService { this.logService.warn(`Multiple speech providers registered. Picking first one: ${provider.metadata.displayName}`); } - return provider.createSpeechToTextSession(token); + const session = this._activeSpeechToTextSession = provider.createSpeechToTextSession(token); + + const disposables = new DisposableStore(); + + const onSessionStoppedOrCanceled = () => { + if (session === this._activeSpeechToTextSession) { + this._activeSpeechToTextSession = undefined; + this._onDidEndSpeechToTextSession.fire(); + } + + disposables.dispose(); + }; + + disposables.add(token.onCancellationRequested(() => onSessionStoppedOrCanceled())); + if (token.isCancellationRequested) { + onSessionStoppedOrCanceled(); + } + + disposables.add(session.onDidChange(e => { + switch (e.status) { + case SpeechToTextStatus.Started: + if (session === this._activeSpeechToTextSession) { + this._onDidStartSpeechToTextSession.fire(); + } + break; + case SpeechToTextStatus.Stopped: + onSessionStoppedOrCanceled(); + break; + } + })); + + return session; + } + + private readonly _onDidStartKeywordRecognition = this._register(new Emitter()); + readonly onDidStartKeywordRecognition = this._onDidStartKeywordRecognition.event; + + private readonly _onDidEndKeywordRecognition = this._register(new Emitter()); + readonly onDidEndKeywordRecognition = this._onDidEndKeywordRecognition.event; + + private _activeKeywordRecognitionSession: IKeywordRecognitionSession | undefined = undefined; + get hasActiveKeywordRecognition() { return !!this._activeKeywordRecognitionSession; } + + async recognizeKeyword(token: CancellationToken): Promise { + const provider = firstOrDefault(Array.from(this.providers.values())); + if (!provider) { + throw new Error(`No Speech provider is registered.`); + } else if (this.providers.size > 1) { + this.logService.warn(`Multiple speech providers registered. Picking first one: ${provider.metadata.displayName}`); + } + + const session = this._activeKeywordRecognitionSession = provider.createKeywordRecognitionSession(token); + this._onDidStartKeywordRecognition.fire(); + + const disposables = new DisposableStore(); + + const onSessionStoppedOrCanceled = () => { + if (session === this._activeKeywordRecognitionSession) { + this._activeKeywordRecognitionSession = undefined; + this._onDidEndKeywordRecognition.fire(); + } + + disposables.dispose(); + }; + + disposables.add(token.onCancellationRequested(() => onSessionStoppedOrCanceled())); + if (token.isCancellationRequested) { + onSessionStoppedOrCanceled(); + } + + disposables.add(session.onDidChange(e => { + if (e.status === KeywordRecognitionStatus.Stopped) { + onSessionStoppedOrCanceled(); + } + })); + + try { + return (await Event.toPromise(session.onDidChange)).status; + } finally { + onSessionStoppedOrCanceled(); + } } } diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index bd39f81d3cca4..0dd568153f3f0 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -433,6 +433,11 @@ export interface IEditorPart extends IEditorGroupsContainer { */ readonly onDidScroll: Event; + /** + * The identifier of the window the editor part is contained in. + */ + readonly windowId: number; + /** * The size of the editor part. */ @@ -461,11 +466,6 @@ export interface IEditorPart extends IEditorGroupsContainer { export interface IAuxiliaryEditorPart extends IEditorPart { - /** - * The identifier of the window the auxiliary editor part is contained in. - */ - readonly windowId: number; - /** * Close this auxiliary editor part after moving all * editors of all groups back to the main editor part. @@ -590,7 +590,7 @@ export interface IEditorGroup { readonly id: GroupIdentifier; /** - * The identifier of the `CodeWindow` this editor group is part of. + * The identifier of the window this editor group is part of. */ readonly windowId: number; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 0eacb21061ce3..dac55f97d95c5 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -828,6 +828,8 @@ export class TestEditorGroupsService implements IEditorGroupsService { readonly parts: readonly IEditorPart[] = [this]; + windowId = mainWindow.vscodeWindowId; + onDidCreateAuxiliaryEditorPart: Event = Event.None; onDidChangeActiveGroup: Event = Event.None; onDidActivateGroup: Event = Event.None; diff --git a/src/vscode-dts/vscode.proposed.speech.d.ts b/src/vscode-dts/vscode.proposed.speech.d.ts index 81c90e39bac0f..42fbbb033205a 100644 --- a/src/vscode-dts/vscode.proposed.speech.d.ts +++ b/src/vscode-dts/vscode.proposed.speech.d.ts @@ -5,6 +5,8 @@ declare module 'vscode' { + // todo@bpasero work in progress speech API + export enum SpeechToTextStatus { Started = 1, Recognizing = 2, @@ -21,15 +23,27 @@ declare module 'vscode' { readonly onDidChange: Event; } + export enum KeywordRecognitionStatus { + Recognized = 1, + Stopped = 2 + } + + export interface KeywordRecognitionEvent { + readonly status: KeywordRecognitionStatus; + readonly text?: string; + } + + export interface KeywordRecognitionSession extends Disposable { + readonly onDidChange: Event; + } + export interface SpeechProvider { provideSpeechToTextSession(token: CancellationToken): SpeechToTextSession; + provideKeywordRecognitionSession(token: CancellationToken): KeywordRecognitionSession; } export namespace speech { - /** - * TODO@bpasero work in progress speech provider API - */ export function registerSpeechProvider(id: string, provider: SpeechProvider): Disposable; } } From 284417d02a70b5230e74549539a444d56f980c04 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 18 Jan 2024 15:08:22 +0100 Subject: [PATCH 100/333] Feature: change font family of inline completions (#202671) Feature: change font family of inline completions --------- Co-authored-by: Henning Dieterichs --- src/vs/base/browser/dom.ts | 32 +++++++++++++++++++ src/vs/editor/common/config/editorOptions.ts | 12 +++++++ .../browser/inlineCompletionsController.ts | 13 ++++++++ src/vs/monaco.d.ts | 4 +++ 4 files changed, 61 insertions(+) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 18e56736dd26b..c5fea2d0c7ba2 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -927,6 +927,38 @@ export function isGlobalStylesheet(node: Node): boolean { return globalStylesheets.has(node as HTMLStyleElement); } +/** + * A version of createStyleSheet which has a unified API to initialize/set the style content. + */ +export function createStyleSheet2(): WrappedStyleElement { + return new WrappedStyleElement(); +} + +class WrappedStyleElement { + private _currentCssStyle = ''; + private _styleSheet: HTMLStyleElement | undefined = undefined; + + public setStyle(cssStyle: string): void { + if (cssStyle !== this._currentCssStyle) { + return; + } + this._currentCssStyle = cssStyle; + + if (!this._styleSheet) { + this._styleSheet = createStyleSheet(mainWindow.document.head, (s) => s.innerText = cssStyle); + } else { + this._styleSheet.innerText = cssStyle; + } + } + + public dispose(): void { + if (this._styleSheet) { + clearNode(this._styleSheet); + this._styleSheet = undefined; + } + } +} + export function createStyleSheet(container: HTMLElement = mainWindow.document.head, beforeAppend?: (style: HTMLStyleElement) => void, disposableStore?: DisposableStore): HTMLStyleElement { const style = document.createElement('style'); style.type = 'text/css'; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 2890c08e76376..944041b7fc39f 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -3993,6 +3993,11 @@ export interface IInlineSuggestOptions { * Does not clear active inline suggestions when the editor loses focus. */ keepOnBlur?: boolean; + + /** + * Font family for inline suggestions. + */ + fontFamily?: string | 'default'; } /** @@ -4011,6 +4016,7 @@ class InlineEditorSuggest extends BaseEditorOption this.editor.getOption(EditorOption.inlineSuggest).enabled); + private readonly _fontFamily = observableFromEvent(this.editor.onDidChangeConfiguration, () => this.editor.getOption(EditorOption.inlineSuggest).fontFamily); private _ghostTextWidget = this._register(this._instantiationService.createInstance(GhostTextWidget, this.editor, { ghostText: this.model.map((v, reader) => /** ghostText */ v?.ghostText.read(reader)), @@ -112,6 +114,17 @@ export class InlineCompletionsController extends Disposable { }); })); + const styleElement = this._register(createStyleSheet2()); + this._register(autorun(reader => { + const fontFamily = this._fontFamily.read(reader); + styleElement.setStyle(fontFamily === '' || fontFamily === 'default' ? `` : ` +.monaco-editor .ghost-text-decoration, +.monaco-editor .ghost-text-decoration-preview, +.monaco-editor .ghost-text { + font-family: ${fontFamily}; +}`); + })); + const getReason = (e: IModelContentChangedEvent): VersionIdChangeReason => { if (e.isUndoing) { return VersionIdChangeReason.Undo; } if (e.isRedoing) { return VersionIdChangeReason.Redo; } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 51a4fa5d458a0..cea289bcfda20 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4502,6 +4502,10 @@ declare namespace monaco.editor { * Does not clear active inline suggestions when the editor loses focus. */ keepOnBlur?: boolean; + /** + * Font family for inline suggestions. + */ + fontFamily?: string | 'default'; } export interface IBracketPairColorizationOptions { From 4a5169c7e03a7e86138192303958167996dd468c Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 18 Jan 2024 15:25:31 +0100 Subject: [PATCH 101/333] =?UTF-8?q?Git=20-=20=F0=9F=92=84=20remove=20unuse?= =?UTF-8?q?d=20event=20handlers=20(#202727)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/git/src/repository.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 8ac566827a690..b0d6ef6235a86 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -681,12 +681,6 @@ export class Repository implements Disposable { private _onDidChangeBranchProtection = new EventEmitter(); readonly onDidChangeBranchProtection: Event = this._onDidChangeBranchProtection.event; - private _onDidStartCommitMessageGeneration = new EventEmitter(); - readonly onDidStartCommitMessageGeneration: Event = this._onDidStartCommitMessageGeneration.event; - - private _onDidEndCommitMessageGeneration = new EventEmitter(); - readonly onDidEndCommitMessageGeneration: Event = this._onDidEndCommitMessageGeneration.event; - @memoize get onDidChangeOperations(): Event { return anyEvent(this.onRunOperation as Event, this.onDidRunOperation as Event); From fba98d1cdfb2470955a294cf42622d2c4e09e4bb Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Thu, 18 Jan 2024 15:39:03 +0100 Subject: [PATCH 102/333] :lipstick: --- .../browser/actions/layoutActions.ts | 2 +- src/vs/workbench/browser/layout.ts | 48 ++++++++++++------- .../browser/parts/titlebar/titlebarActions.ts | 8 ++-- .../browser/parts/titlebar/titlebarPart.ts | 6 +-- .../browser/workbench.contribution.ts | 2 +- .../services/layout/browser/layoutService.ts | 1 + 6 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index ca73beb2038de..d6e446d04f1b6 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -803,7 +803,7 @@ if (isWindows || isLinux || isWeb) { title: localize('miMenuBarNoMnemonic', "Menu Bar"), toggled: ContextKeyExpr.and(IsMacNativeContext.toNegated(), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'hidden'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'toggle'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'compact')) }, - when: ContextKeyExpr.and(IsAuxiliaryWindowFocusedContext.toNegated(), ContextKeyExpr.notEquals(TitleBarSetting.TITLE_BAR_STYLE, TitlebarStyle.NATIVE)), + when: ContextKeyExpr.and(IsAuxiliaryWindowFocusedContext.toNegated(), ContextKeyExpr.notEquals(TitleBarSetting.TITLE_BAR_STYLE, TitlebarStyle.NATIVE), IsMainWindowFullscreenContext.negate()), group: '2_config', order: 0 }); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 02d84822c0dca..f40144d8cc662 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -347,6 +347,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi LayoutSettings.ACTIVITY_BAR_LOCATION, LayoutSettings.COMMAND_CENTER, LayoutSettings.EDITOR_ACTIONS_LOCATION, + LayoutSettings.LAYOUT_ACTIONS, LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION, LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE, 'window.menuBarVisibility', @@ -1229,31 +1230,18 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return false; } - const nativeTitleBarVisible = hasNativeTitlebar(this.configurationService); + const nativeTitleBarEnabled = hasNativeTitlebar(this.configurationService); const showCustomTitleBar = this.configurationService.getValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY); - if (showCustomTitleBar === CustomTitleBarVisibility.NEVER && nativeTitleBarVisible || showCustomTitleBar === CustomTitleBarVisibility.WINDOWED && this.state.runtime.mainWindowFullscreen) { + if (showCustomTitleBar === CustomTitleBarVisibility.NEVER && nativeTitleBarEnabled || showCustomTitleBar === CustomTitleBarVisibility.WINDOWED && this.state.runtime.mainWindowFullscreen) { return false; } - // with the command center enabled, we should always show - if (this.configurationService.getValue(LayoutSettings.COMMAND_CENTER)) { - return true; - } - - // with the activity bar on top, we should always show - if (this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.TOP) { - return true; - } - - // with the editor actions on top, we should always show - const editorActionsLocation = this.configurationService.getValue(LayoutSettings.EDITOR_ACTIONS_LOCATION); - const editorTabsMode = this.configurationService.getValue(LayoutSettings.EDITOR_TABS_MODE); - if (editorActionsLocation === EditorActionsLocation.TITLEBAR || editorActionsLocation === EditorActionsLocation.DEFAULT && editorTabsMode === EditorTabsMode.NONE) { + if (!this.isTitleBarEmpty(nativeTitleBarEnabled)) { return true; } // Hide custom title bar when native title bar and custom title bar is empty - if (nativeTitleBarVisible) { + if (nativeTitleBarEnabled) { return false; } @@ -1288,6 +1276,32 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } + private isTitleBarEmpty(nativeTitleBarEnabled: boolean): boolean { + // with the command center enabled, we should always show + if (this.configurationService.getValue(LayoutSettings.COMMAND_CENTER)) { + return false; + } + + // with the activity bar on top, we should always show + if (this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.TOP) { + return false; + } + + // with the editor actions on top, we should always show + const editorActionsLocation = this.configurationService.getValue(LayoutSettings.EDITOR_ACTIONS_LOCATION); + const editorTabsMode = this.configurationService.getValue(LayoutSettings.EDITOR_TABS_MODE); + if (editorActionsLocation === EditorActionsLocation.TITLEBAR || editorActionsLocation === EditorActionsLocation.DEFAULT && editorTabsMode === EditorTabsMode.NONE) { + return false; + } + + // Layout don't show with native title bar + if (!nativeTitleBarEnabled && this.configurationService.getValue(LayoutSettings.LAYOUT_ACTIONS)) { + return false; + } + + return true; + } + private shouldShowBannerFirst(): boolean { return isWeb && !isWCOEnabled(); } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index 27937ac6396db..e2d88e50c31dc 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -69,8 +69,7 @@ registerAction2(class ToggleCustomTitleBar extends Action2 { constructor() { super({ id: `toggle.${ToggleCustomTitleBar.settingsID}`, - title: localize('toggle.customTitleBar', 'Custom Title Bar'), - toggled: ContextKeyExpr.true(), + title: localize('toggle.hideCustomTitleBar', 'Hide Custom Title Bar'), menu: [ { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.equals(TitleBarSetting.TITLE_BAR_STYLE, TitlebarStyle.NATIVE), group: '3_toggle' }, ] @@ -84,13 +83,12 @@ registerAction2(class ToggleCustomTitleBar extends Action2 { }); registerAction2(class ToggleCustomTitleBarWindowed extends Action2 { - static readonly settingsID = TitleBarSetting.TITLE_BAR_STYLE; + static readonly settingsID = TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY; constructor() { super({ id: `toggle.${ToggleCustomTitleBarWindowed.settingsID}.windowed`, - title: localize('toggle.customTitleBarWindowed', 'Custom Title Bar In Full Screen'), - toggled: ContextKeyExpr.true(), + title: localize('toggle.hideCustomTitleBarInFullScreen', 'Hide Custom Title Bar In Full Screen'), menu: [ { id: MenuId.TitleBarContext, order: 0, when: IsMainWindowFullscreenContext, group: '3_toggle' }, ] diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 32361f3be144e..2f90f198a3664 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -322,7 +322,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { // Actions if (hasCustomTitlebar(this.configurationService, this.titleBarStyle) && this.actionToolBar) { - const affectsLayoutControl = event.affectsConfiguration('workbench.layoutControl.enabled'); + const affectsLayoutControl = event.affectsConfiguration(LayoutSettings.LAYOUT_ACTIONS); const affectsActivityControl = event.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION); if (affectsLayoutControl || affectsActivityControl) { @@ -699,7 +699,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { } private get layoutControlEnabled(): boolean { - return !this.isAuxiliary && this.configurationService.getValue('workbench.layoutControl.enabled') !== false && !hasNativeTitlebar(this.configurationService, this.titleBarStyle); + return !this.isAuxiliary && this.configurationService.getValue(LayoutSettings.LAYOUT_ACTIONS) !== false && !hasNativeTitlebar(this.configurationService, this.titleBarStyle); } protected get isCommandCenterVisible() { @@ -710,7 +710,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { return this.editorGroupService.partOptions.editorActionsLocation === EditorActionsLocation.TITLEBAR || ( this.editorGroupService.partOptions.editorActionsLocation === EditorActionsLocation.DEFAULT && - this.editorGroupService.partOptions.showTabs === EditorTabsMode.MULTIPLE + this.editorGroupService.partOptions.showTabs === EditorTabsMode.NONE ); } diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 869b35f957259..6153d527c98da 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -566,7 +566,7 @@ const registry = Registry.as(ConfigurationExtensions.Con tags: ['accessibility'], enum: ['on', 'off', 'auto'] }, - 'workbench.layoutControl.enabled': { + [LayoutSettings.LAYOUT_ACTIONS]: { 'type': 'boolean', 'default': true, 'markdownDescription': isWeb ? diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index c64f875ded06b..c1091f8e7a618 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -39,6 +39,7 @@ export const enum LayoutSettings { EDITOR_TABS_MODE = 'workbench.editor.showTabs', EDITOR_ACTIONS_LOCATION = 'workbench.editor.editorActionsLocation', COMMAND_CENTER = 'window.commandCenter', + LAYOUT_ACTIONS = 'workbench.layoutControl.enabled', } export const enum ActivityBarPosition { From 272d8f096e507e1239d6094025d123407bd366a7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 18 Jan 2024 15:43:34 +0100 Subject: [PATCH 103/333] Tweak inline chat voice gestures (#202731) - special rendering for recognized text - enable hold more for quick inline voice chat - increase/adjust hold mode timeouts --- .../electron-sandbox/inlineChatActions.ts | 4 +- .../electron-sandbox/inlineChatQuickVoice.css | 4 ++ .../electron-sandbox/inlineChatQuickVoice.ts | 59 +++++++++++++------ 3 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts index 6a94b91a0d57f..f3becd537a90f 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts @@ -49,7 +49,7 @@ export class StartSessionAction extends EditorAction2 { const commandService = accessor.get(ICommandService); if (configService.getValue(InlineChatConfigKeys.HoldToSpeech) // enabled - && speechService.hasSpeechProvider // possible + && speechService.hasSpeechProvider // possible ) { const holdMode = keybindingService.enableKeybindingHoldMode(this.desc.id); @@ -59,7 +59,7 @@ export class StartSessionAction extends EditorAction2 { // start VOICE input commandService.executeCommand(StartVoiceChatAction.ID); listening = true; - }, 100); + }, 250); holdMode.finally(() => { if (listening) { diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css index 784d716d80fc1..64ff58649ee1d 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css @@ -37,3 +37,7 @@ color: var(--vscode-editorCursor-background); } } + +.monaco-editor .inline-chat-quick-voice .message.preview { + opacity: .4; +} diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts index 1e47eb5d485ef..4713733f0f3bf 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts @@ -6,7 +6,7 @@ import 'vs/css!./inlineChatQuickVoice'; import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { Codicon } from 'vs/base/common/codicons'; -import { KeyCode } from 'vs/base/common/keyCodes'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; @@ -17,10 +17,11 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { HasSpeechProvider, ISpeechService, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; -import { h, reset } from 'vs/base/browser/dom'; +import { getWindow, h, reset, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; import { IDimension } from 'vs/editor/common/core/dimension'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { AbstractInlineChatAction } from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; const CTX_QUICK_CHAT_IN_PROGRESS = new RawContextKey('inlineChat.quickChatInProgress', false); @@ -33,14 +34,29 @@ export class StartAction extends EditorAction2 { category: AbstractInlineChatAction.category, precondition: ContextKeyExpr.and(HasSpeechProvider, CTX_QUICK_CHAT_IN_PROGRESS.toNegated()), f1: true, - // keybinding: { - // primary: KeyChord(KeyCode.F12, KeyCode.F12), - // weight: KeybindingWeight.WorkbenchContrib - // } + keybinding: { + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyI), + weight: KeybindingWeight.WorkbenchContrib + 100 + } }); } - override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]) { + override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor) { + const keybindingService = accessor.get(IKeybindingService); + + const holdMode = keybindingService.enableKeybindingHoldMode(this.desc.id); + if (holdMode) { + let shouldCallStop = false; + const handle = setTimeout(() => { + shouldCallStop = true; + }, 500); + holdMode.finally(() => { + clearTimeout(handle); + if (shouldCallStop) { + InlineChatQuickVoice.get(editor)?.stop(); + } + }); + } InlineChatQuickVoice.get(editor)?.start(); } } @@ -55,13 +71,13 @@ export class StopAction extends EditorAction2 { precondition: ContextKeyExpr.and(HasSpeechProvider, CTX_QUICK_CHAT_IN_PROGRESS), f1: true, keybinding: { - primary: KeyCode.Escape, - weight: KeybindingWeight.WorkbenchContrib + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyI), + weight: KeybindingWeight.WorkbenchContrib + 100 } }); } - override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]) { + override runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor) { InlineChatQuickVoice.get(editor)?.stop(); } } @@ -93,12 +109,14 @@ class QuickVoiceWidget implements IContentWidget { private readonly _domNode = document.createElement('div'); private readonly _elements = h('.inline-chat-quick-voice@main', [ h('span@mic'), - h('span@message'), + h('span.message@message'), ]); constructor(private readonly _editor: ICodeEditor) { this._domNode.appendChild(this._elements.root); this._domNode.style.zIndex = '1000'; + this._domNode.tabIndex = -1; + this._domNode.style.outline = 'none'; reset(this._elements.mic, renderIcon(Codicon.micFilled)); } @@ -134,17 +152,22 @@ class QuickVoiceWidget implements IContentWidget { // --- - updateInput(input: string | undefined): void { + updateInput(input: string | undefined, isDefinite: boolean): void { + this._elements.message.classList.toggle('preview', !isDefinite); this._elements.message.textContent = input ?? ''; } + focus(): void { + this._domNode.focus(); + } + active(): void { this._elements.main.classList.add('recording'); } reset(): void { this._elements.main.classList.remove('recording'); - this.updateInput(undefined); + this.updateInput(undefined, true); } } @@ -179,8 +202,11 @@ export class InlineChatQuickVoice implements IEditorContribution { this._editor.addContentWidget(this._widget); this._ctxQuickChatInProgress.set(true); - let message: string | undefined; + runAtThisOrScheduleAtNextAnimationFrame(getWindow(this._widget.getDomNode()), () => { + this._widget.focus(); // requires RAF because... + }); + let message: string | undefined; const session = this._speechService.createSpeechToTextSession(cts.token); const listener = session.onDidChange(e => { @@ -195,12 +221,11 @@ export class InlineChatQuickVoice implements IEditorContribution { case SpeechToTextStatus.Stopped: break; case SpeechToTextStatus.Recognizing: - // TODO@jrieken special rendering for "in-flight" message? - this._widget.updateInput(!message ? e.text : `${message} ${e.text}`); + this._widget.updateInput(!message ? e.text : `${message} ${e.text}`, false); break; case SpeechToTextStatus.Recognized: message = !message ? e.text : `${message} ${e.text}`; - this._widget.updateInput(message); + this._widget.updateInput(message, true); break; } }); From 1a386828ac254074af29ebe968bd690a90ad46c4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 18 Jan 2024 15:59:10 +0100 Subject: [PATCH 104/333] voice - support any focused code editor (including notebooks) (#202733) --- .../browser/contrib/chatInputEditorContrib.ts | 4 ++-- .../electron-sandbox/actions/voiceChatActions.ts | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index ccfa0c7a23da8..c83c4e211d8fd 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -662,8 +662,8 @@ class ChatTokenDeleter extends Disposable { const change = e.changes[0]; // If this was a simple delete, try to find out whether it was inside a token - if (!change.text) { - parser.parseChatRequest(this.widget.viewModel!.sessionId, previousInputValue).then(previousParsedValue => { + if (!change.text && this.widget.viewModel) { + parser.parseChatRequest(this.widget.viewModel.sessionId, previousInputValue).then(previousParsedValue => { // For dynamic variables, this has to happen in ChatDynamicVariableModel with the other bookkeeping const deletableTokens = previousParsedValue.parts.filter(p => p instanceof ChatRequestAgentPart || p instanceof ChatRequestAgentSubcommandPart || p instanceof ChatRequestSlashCommandPart || p instanceof ChatRequestVariablePart); deletableTokens.forEach(token => { diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 11dcef4882af4..42a627887049f 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -21,8 +21,6 @@ import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, MENU_INLINE_CHAT_INPUT } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { CONTEXT_CHAT_REQUEST_IN_PROGRESS, CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { ActiveEditorContext } from 'vs/workbench/common/contextkeys'; import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; @@ -46,6 +44,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when getting ready for receiving voice input from the microphone for voice chat.") }); const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when voice recording from microphone is in progress for voice chat.") }); @@ -84,7 +83,7 @@ class VoiceChatSessionControllerFactory { const chatService = accessor.get(IChatService); const viewsService = accessor.get(IViewsService); const chatContributionService = accessor.get(IChatContributionService); - const editorService = accessor.get(IEditorService); + const codeEditorService = accessor.get(ICodeEditorService); const quickChatService = accessor.get(IQuickChatService); const layoutService = accessor.get(IWorkbenchLayoutService); @@ -114,7 +113,7 @@ class VoiceChatSessionControllerFactory { } // Try with the inline chat - const activeCodeEditor = getCodeEditor(editorService.activeTextEditorControl); + const activeCodeEditor = codeEditorService.getFocusedCodeEditor(); if (activeCodeEditor) { const inlineChat = InlineChatController.get(activeCodeEditor); if (inlineChat?.hasFocus()) { @@ -136,7 +135,7 @@ class VoiceChatSessionControllerFactory { // Inline Chat if (context === 'inline') { - const activeCodeEditor = getCodeEditor(editorService.activeTextEditorControl); + const activeCodeEditor = codeEditorService.getFocusedCodeEditor(); if (activeCodeEditor) { const inlineChat = InlineChatController.get(activeCodeEditor); if (inlineChat) { @@ -772,7 +771,8 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe @IConfigurationService private readonly configurationService: IConfigurationService, @ICommandService private readonly commandService: ICommandService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @ICodeEditorService private readonly codeEditorService: ICodeEditorService ) { super(); @@ -879,7 +879,9 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe case KeywordActivationContribution.SETTINGS_VALUE.QUICK_CHAT: return QuickVoiceChatAction.ID; case KeywordActivationContribution.SETTINGS_VALUE.CHAT_IN_CONTEXT: - return StartVoiceChatAction.ID; + if (this.codeEditorService.getFocusedCodeEditor()) { + return InlineVoiceChatAction.ID; + } default: return VoiceChatInChatViewAction.ID; } From 2cec7a8d8872ea6a614de756b5f0ba3f5922b1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Thu, 18 Jan 2024 16:07:13 +0100 Subject: [PATCH 105/333] fix: make sure showKeybindings stills works in screencast mode (#202735) fixes #201689 --- src/vs/workbench/browser/actions/developerActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index 7b40f8df1cfa3..61d008d640401 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -350,7 +350,7 @@ class ToggleScreencastModeAction extends Action2 { append(keyboardMarker, $('span.title', {}, `${commandAndGroupLabel} `)); } - if ((options.showKeys ?? true) || (commandDetails && (options.showKeybindings ?? true))) { + if ((options.showKeys ?? true) || ((options.showKeybindings ?? true) && this._isKbFound(shortcut))) { // Fix label for arrow keys keyLabel = keyLabel?.replace('UpArrow', '↑') ?.replace('DownArrow', '↓') From 98c253817043f784e7ffbe05f098b5f0acf43a45 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 18 Jan 2024 16:09:38 +0100 Subject: [PATCH 106/333] voice - tweak status bar entry (#202736) --- .../chat/electron-sandbox/actions/voiceChatActions.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 42a627887049f..03f10c273ce4b 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -905,8 +905,8 @@ class KeywordActivationStatusEntry extends Disposable { private static STATUS_NAME = localize('keywordActivation.status.name', "Voice Keyword Activation"); private static STATUS_COMMAND = 'keywordActivation.status.command'; - private static STATUS_ACTIVE = localize('keywordActivation.status.active', "Voice Keyword Activation: Active"); - private static STATUS_INACTIVE = localize('keywordActivation.status.inactive', "Voice Keyword Activation: Inactive"); + private static STATUS_ACTIVE = localize('keywordActivation.status.active', "Listening to 'Hey Code'..."); + private static STATUS_INACTIVE = localize('keywordActivation.status.inactive', "Waiting for voice chat to end..."); constructor( @ISpeechService private readonly speechService: ISpeechService, @@ -952,11 +952,11 @@ class KeywordActivationStatusEntry extends Disposable { private getStatusEntryProperties(): IStatusbarEntry { return { name: KeywordActivationStatusEntry.STATUS_NAME, - text: '$(mic)', + text: this.speechService.hasActiveKeywordRecognition ? '$(mic-filled)' : '$(mic)', tooltip: this.speechService.hasActiveKeywordRecognition ? KeywordActivationStatusEntry.STATUS_ACTIVE : KeywordActivationStatusEntry.STATUS_INACTIVE, ariaLabel: this.speechService.hasActiveKeywordRecognition ? KeywordActivationStatusEntry.STATUS_ACTIVE : KeywordActivationStatusEntry.STATUS_INACTIVE, command: KeywordActivationStatusEntry.STATUS_COMMAND, - kind: this.speechService.hasActiveKeywordRecognition ? 'prominent' : 'standard' + kind: 'prominent' }; } From ad240ae2d26bf560406c63ac31b8186f53870867 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Thu, 18 Jan 2024 16:20:11 +0100 Subject: [PATCH 107/333] :lipstick: --- src/vs/workbench/browser/parts/titlebar/titlebarActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index e2d88e50c31dc..db34e4f1d465f 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -21,7 +21,7 @@ class ToggleConfigAction extends Action2 { constructor(private readonly section: string, title: string, order: number, mainWindowOnly: boolean, showInCustomTitleBarWhenNativeTitle: boolean) { let when = mainWindowOnly ? IsAuxiliaryWindowFocusedContext.toNegated() : ContextKeyExpr.true(); - when = showInCustomTitleBarWhenNativeTitle ? when : ContextKeyExpr.and(when, ContextKeyExpr.equals(TitleBarSetting.TITLE_BAR_STYLE, TitlebarStyle.NATIVE).negate())!; + when = showInCustomTitleBarWhenNativeTitle ? when : ContextKeyExpr.and(when, ContextKeyExpr.equals(`config.${TitleBarSetting.TITLE_BAR_STYLE}`, TitlebarStyle.NATIVE).negate())!; super({ id: `toggle.${section}`, From a0628b674edf6186ef7012747204521c62713c7a Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Thu, 18 Jan 2024 16:24:14 +0100 Subject: [PATCH 108/333] :lipstick: --- src/vs/workbench/browser/parts/titlebar/titlebarActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index db34e4f1d465f..f336a9f25fab2 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -71,7 +71,7 @@ registerAction2(class ToggleCustomTitleBar extends Action2 { id: `toggle.${ToggleCustomTitleBar.settingsID}`, title: localize('toggle.hideCustomTitleBar', 'Hide Custom Title Bar'), menu: [ - { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.equals(TitleBarSetting.TITLE_BAR_STYLE, TitlebarStyle.NATIVE), group: '3_toggle' }, + { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.equals(`config.${TitleBarSetting.TITLE_BAR_STYLE}`, TitlebarStyle.NATIVE), group: '3_toggle' }, ] }); } From 2df8c8428b9981fe3704e0e1cf945e725c692d4a Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Thu, 18 Jan 2024 16:27:02 +0100 Subject: [PATCH 109/333] :lipstick: --- src/vs/workbench/browser/parts/titlebar/titlebarActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index f336a9f25fab2..ef0c27873c34b 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -64,7 +64,7 @@ registerAction2(class ToggleLayoutControl extends ToggleConfigAction { }); registerAction2(class ToggleCustomTitleBar extends Action2 { - static readonly settingsID = TitleBarSetting.TITLE_BAR_STYLE; + static readonly settingsID = TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY; constructor() { super({ From 18b601beed5c87c2a6d54664d25ca144c51e2c5b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 18 Jan 2024 16:27:57 +0100 Subject: [PATCH 110/333] voice - trigger keyword only when window has focus (#202737) --- .../chat/electron-sandbox/actions/voiceChatActions.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 03f10c273ce4b..f18e22447b98b 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -45,6 +45,7 @@ import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/co import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when getting ready for receiving voice input from the microphone for voice chat.") }); const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when voice recording from microphone is in progress for voice chat.") }); @@ -772,7 +773,8 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe @ICommandService private readonly commandService: ICommandService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IInstantiationService instantiationService: IInstantiationService, - @ICodeEditorService private readonly codeEditorService: ICodeEditorService + @ICodeEditorService private readonly codeEditorService: ICodeEditorService, + @IHostService private readonly hostService: IHostService ) { super(); @@ -867,7 +869,11 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe this.activeSession = undefined; if (result === KeywordRecognitionStatus.Recognized) { - this.commandService.executeCommand(this.getKeywordCommand()); + if (this.hostService.hasFocus) { + this.commandService.executeCommand(this.getKeywordCommand()); + } else { + this.handleKeywordActivation(); + } } } From a7f0c4953a1c869ca93dcbd4e22ff4d0b38b4359 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Thu, 18 Jan 2024 16:34:25 +0100 Subject: [PATCH 111/333] :lipstick: --- .../browser/parts/titlebar/titlebarActions.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index ef0c27873c34b..25df6adaaf376 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -64,40 +64,38 @@ registerAction2(class ToggleLayoutControl extends ToggleConfigAction { }); registerAction2(class ToggleCustomTitleBar extends Action2 { - static readonly settingsID = TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY; - constructor() { super({ - id: `toggle.${ToggleCustomTitleBar.settingsID}`, + id: `toggle.${TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY}`, title: localize('toggle.hideCustomTitleBar', 'Hide Custom Title Bar'), menu: [ { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.equals(`config.${TitleBarSetting.TITLE_BAR_STYLE}`, TitlebarStyle.NATIVE), group: '3_toggle' }, + { id: MenuId.TitleBarTitleContext, order: 0, when: ContextKeyExpr.equals(`config.${TitleBarSetting.TITLE_BAR_STYLE}`, TitlebarStyle.NATIVE), group: '3_toggle' }, ] }); } run(accessor: ServicesAccessor, ...args: any[]): void { const configService = accessor.get(IConfigurationService); - configService.updateValue(ToggleCustomTitleBar.settingsID, CustomTitleBarVisibility.NEVER); + configService.updateValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, CustomTitleBarVisibility.NEVER); } }); registerAction2(class ToggleCustomTitleBarWindowed extends Action2 { - static readonly settingsID = TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY; - constructor() { super({ - id: `toggle.${ToggleCustomTitleBarWindowed.settingsID}.windowed`, + id: `toggle.${TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY}.windowed`, title: localize('toggle.hideCustomTitleBarInFullScreen', 'Hide Custom Title Bar In Full Screen'), menu: [ { id: MenuId.TitleBarContext, order: 0, when: IsMainWindowFullscreenContext, group: '3_toggle' }, + { id: MenuId.TitleBarTitleContext, order: 0, when: IsMainWindowFullscreenContext, group: '3_toggle' }, ] }); } run(accessor: ServicesAccessor, ...args: any[]): void { const configService = accessor.get(IConfigurationService); - configService.updateValue(ToggleCustomTitleBarWindowed.settingsID, CustomTitleBarVisibility.WINDOWED); + configService.updateValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, CustomTitleBarVisibility.WINDOWED); } }); From 37740ecbb044e581d328392b42677fba4fe39092 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 18 Jan 2024 16:52:56 +0100 Subject: [PATCH 112/333] add hold for speech command to inline chat which is for when a session already started (#202741) * - cancel quick voice when loosing focus * add hold for speech command to inline chat which is for when a session already started --- .../inlineChat.contribution.ts | 5 +- .../electron-sandbox/inlineChatActions.ts | 74 +++++++++++++------ .../electron-sandbox/inlineChatQuickVoice.ts | 55 +++++++++----- 3 files changed, 92 insertions(+), 42 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts index d5233317f5529..4a543a8ea54cd 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChat.contribution.ts @@ -6,11 +6,12 @@ import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { registerAction2 } from 'vs/platform/actions/common/actions'; import { CancelAction, InlineChatQuickVoice, StartAction, StopAction } from 'vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice'; -import * as StartSessionAction from './inlineChatActions'; +import { StartSessionAction, HoldToSpeak } from './inlineChatActions'; // start and hold for voice -registerAction2(StartSessionAction.StartSessionAction); +registerAction2(StartSessionAction); +registerAction2(HoldToSpeak); // quick voice diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts index f3becd537a90f..a6b3bd9a496a3 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts @@ -16,9 +16,10 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { StartVoiceChatAction, StopListeningAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; -import { CTX_INLINE_CHAT_HAS_PROVIDER, InlineChatConfigKeys } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { CTX_INLINE_CHAT_HAS_PROVIDER, CTX_INLINE_CHAT_VISIBLE, InlineChatConfigKeys } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ISpeechService } from 'vs/workbench/contrib/speech/common/speechService'; +import { HasSpeechProvider, ISpeechService } from 'vs/workbench/contrib/speech/common/speechService'; +import { localize2 } from 'vs/nls'; export class StartSessionAction extends EditorAction2 { @@ -45,31 +46,11 @@ export class StartSessionAction extends EditorAction2 { const configService = accessor.get(IConfigurationService); const speechService = accessor.get(ISpeechService); - const keybindingService = accessor.get(IKeybindingService); - const commandService = accessor.get(ICommandService); if (configService.getValue(InlineChatConfigKeys.HoldToSpeech) // enabled && speechService.hasSpeechProvider // possible ) { - - const holdMode = keybindingService.enableKeybindingHoldMode(this.desc.id); - if (holdMode) { // holding keys - let listening = false; - const handle = disposableTimeout(() => { - // start VOICE input - commandService.executeCommand(StartVoiceChatAction.ID); - listening = true; - }, 250); - - holdMode.finally(() => { - if (listening) { - commandService.executeCommand(StopListeningAction.ID).finally(() => { - InlineChatController.get(editor)?.acceptInput(); - }); - } - handle.dispose(); - }); - } + holdForSpeech(accessor, InlineChatController.get(editor), this.desc.id); } let options: InlineChatRunOptions | undefined; @@ -80,3 +61,50 @@ export class StartSessionAction extends EditorAction2 { InlineChatController.get(editor)?.run({ ...options }); } } + +export class HoldToSpeak extends AbstractInlineChatAction { + + constructor() { + super({ + id: 'inlineChat.holdForSpeech', + precondition: ContextKeyExpr.and(HasSpeechProvider, CTX_INLINE_CHAT_VISIBLE), + title: localize2('holdForSpeech', "Hold for Speech"), + keybinding: { + when: EditorContextKeys.textInputFocus, + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyCode.KeyI, + }, + }); + } + + override runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController, editor: ICodeEditor, ...args: any[]): void { + holdForSpeech(accessor, ctrl, this.desc.id); + } +} + +function holdForSpeech(accessor: ServicesAccessor, ctrl: InlineChatController | null, commandId: string): void { + const keybindingService = accessor.get(IKeybindingService); + const commandService = accessor.get(ICommandService); + if (!ctrl) { + return; + } + const holdMode = keybindingService.enableKeybindingHoldMode(commandId); + if (!holdMode) { + return; + } + let listening = false; + const handle = disposableTimeout(() => { + // start VOICE input + commandService.executeCommand(StartVoiceChatAction.ID); + listening = true; + }, 250); + + holdMode.finally(() => { + if (listening) { + commandService.executeCommand(StopListeningAction.ID).finally(() => { + ctrl!.acceptInput(); + }); + } + handle.dispose(); + }); +} diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts index 4713733f0f3bf..8c7f9e05b67d5 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts @@ -17,11 +17,13 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { HasSpeechProvider, ISpeechService, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; -import { getWindow, h, reset, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import * as dom from 'vs/base/browser/dom'; import { IDimension } from 'vs/editor/common/core/dimension'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { AbstractInlineChatAction } from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { Emitter, Event } from 'vs/base/common/event'; +import { DisposableStore } from 'vs/base/common/lifecycle'; const CTX_QUICK_CHAT_IN_PROGRESS = new RawContextKey('inlineChat.quickChatInProgress', false); @@ -107,17 +109,27 @@ class QuickVoiceWidget implements IContentWidget { readonly suppressMouseDown = true; private readonly _domNode = document.createElement('div'); - private readonly _elements = h('.inline-chat-quick-voice@main', [ - h('span@mic'), - h('span.message@message'), + private readonly _elements = dom.h('.inline-chat-quick-voice@main', [ + dom.h('span@mic'), + dom.h('span.message@message'), ]); + private _focusTracker: dom.IFocusTracker | undefined; + + private readonly _onDidBlur = new Emitter(); + readonly onDidBlur: Event = this._onDidBlur.event; + constructor(private readonly _editor: ICodeEditor) { this._domNode.appendChild(this._elements.root); this._domNode.style.zIndex = '1000'; this._domNode.tabIndex = -1; this._domNode.style.outline = 'none'; - reset(this._elements.mic, renderIcon(Codicon.micFilled)); + dom.reset(this._elements.mic, renderIcon(Codicon.micFilled)); + } + + dispose(): void { + this._focusTracker?.dispose(); + this._onDidBlur.dispose(); } getId(): string { @@ -150,6 +162,13 @@ class QuickVoiceWidget implements IContentWidget { return null; } + afterRender(): void { + this._domNode.focus(); + this._focusTracker?.dispose(); + this._focusTracker = dom.trackFocus(this._domNode); + this._focusTracker.onDidBlur(() => this._onDidBlur.fire()); + } + // --- updateInput(input: string | undefined, isDefinite: boolean): void { @@ -157,17 +176,19 @@ class QuickVoiceWidget implements IContentWidget { this._elements.message.textContent = input ?? ''; } - focus(): void { - this._domNode.focus(); + show() { + this._editor.addContentWidget(this); } active(): void { this._elements.main.classList.add('recording'); } - reset(): void { + hide() { this._elements.main.classList.remove('recording'); this.updateInput(undefined, true); + this._editor.removeContentWidget(this); + this._focusTracker?.dispose(); } } @@ -179,6 +200,7 @@ export class InlineChatQuickVoice implements IEditorContribution { return editor.getContribution(InlineChatQuickVoice.ID); } + private readonly _store = new DisposableStore(); private readonly _ctxQuickChatInProgress: IContextKey; private readonly _widget: QuickVoiceWidget; private _finishCallback?: (abort: boolean) => void; @@ -188,24 +210,25 @@ export class InlineChatQuickVoice implements IEditorContribution { @ISpeechService private readonly _speechService: ISpeechService, @IContextKeyService contextKeyService: IContextKeyService, ) { - this._widget = new QuickVoiceWidget(this._editor); + this._widget = this._store.add(new QuickVoiceWidget(this._editor)); + this._widget.onDidBlur(() => this._finishCallback?.(true), undefined, this._store); this._ctxQuickChatInProgress = CTX_QUICK_CHAT_IN_PROGRESS.bindTo(contextKeyService); } dispose(): void { this._finishCallback?.(true); + this._ctxQuickChatInProgress.reset(); + this._store.dispose(); } start() { + this._finishCallback?.(true); + const cts = new CancellationTokenSource(); - this._editor.addContentWidget(this._widget); + this._widget.show(); this._ctxQuickChatInProgress.set(true); - runAtThisOrScheduleAtNextAnimationFrame(getWindow(this._widget.getDomNode()), () => { - this._widget.focus(); // requires RAF because... - }); - let message: string | undefined; const session = this._speechService.createSpeechToTextSession(cts.token); const listener = session.onDidChange(e => { @@ -233,8 +256,7 @@ export class InlineChatQuickVoice implements IEditorContribution { const done = (abort: boolean) => { cts.dispose(true); listener.dispose(); - this._widget.reset(); - this._editor.removeContentWidget(this._widget); + this._widget.hide(); this._ctxQuickChatInProgress.reset(); if (!abort && message) { @@ -252,5 +274,4 @@ export class InlineChatQuickVoice implements IEditorContribution { cancel(): void { this._finishCallback?.(true); } - } From 4057a58c76b286a40b9b30c16cb4ad2d539bc4f0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 18 Jan 2024 17:10:33 +0100 Subject: [PATCH 113/333] don't count changes but make sure error'd edits don't make it into buffer (#202745) fixes https://github.com/microsoft/vscode/issues/202702 --- .../test/browser/inlineChatController.test.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) 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 d2b9fe55c3474..1648debcf8c1c 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -382,6 +382,8 @@ suite('InteractiveChatController', function () { assert.strictEqual(editor.getModel().getValue(), valueThen); }); + + test('UI is streaming edits minutes after the response is finished #3345', async function () { configurationService.setUserConfiguration(InlineChatConfigKeys.Mode, EditMode.Live); @@ -411,8 +413,8 @@ suite('InteractiveChatController', function () { }); - let modelChangeCounter = 0; - store.add(editor.getModel().onDidChangeContent(() => { modelChangeCounter++; })); + // let modelChangeCounter = 0; + // store.add(editor.getModel().onDidChangeContent(() => { modelChangeCounter++; })); store.add(d); ctrl = instaService.createInstance(TestController, editor); @@ -420,17 +422,18 @@ suite('InteractiveChatController', function () { const r = ctrl.run({ message: 'Hello', autoSend: true }); await p; - assert.ok(modelChangeCounter > 0); // some changes have been made - - const modelChangeCounterNow = modelChangeCounter; + // assert.ok(modelChangeCounter > 0, modelChangeCounter.toString()); // some changes have been made + // const modelChangeCounterNow = modelChangeCounter; + assert.ok(!editor.getModel().getValue().includes('DONE')); await timeout(10); - assert.strictEqual(modelChangeCounterNow, modelChangeCounter); + // assert.strictEqual(modelChangeCounterNow, modelChangeCounter); assert.ok(!editor.getModel().getValue().includes('DONE')); await ctrl.cancelSession(); await r; }); }); + }); From 1404ba5d83abb0e84fba1e00b4ae217da1e4fab0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 18 Jan 2024 17:24:17 +0100 Subject: [PATCH 114/333] voice - keep keyword activation alive after keyword is recognized (#202746) --- .../chat/electron-sandbox/actions/voiceChatActions.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index f18e22447b98b..17e6514a1b4c2 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -871,9 +871,13 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe if (result === KeywordRecognitionStatus.Recognized) { if (this.hostService.hasFocus) { this.commandService.executeCommand(this.getKeywordCommand()); - } else { - this.handleKeywordActivation(); } + + // Immediately start another keyboard activation session + // because we cannot assume that the command we execute + // will trigger a speech recognition session. + + this.handleKeywordActivation(); } } From c42c4ab61f72192819b40032ee1a5636090b769f Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Thu, 18 Jan 2024 17:31:44 +0100 Subject: [PATCH 115/333] :lipstick: --- src/vs/workbench/browser/parts/titlebar/titlebarActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index 25df6adaaf376..eff2013a056e8 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -87,8 +87,8 @@ registerAction2(class ToggleCustomTitleBarWindowed extends Action2 { id: `toggle.${TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY}.windowed`, title: localize('toggle.hideCustomTitleBarInFullScreen', 'Hide Custom Title Bar In Full Screen'), menu: [ - { id: MenuId.TitleBarContext, order: 0, when: IsMainWindowFullscreenContext, group: '3_toggle' }, - { id: MenuId.TitleBarTitleContext, order: 0, when: IsMainWindowFullscreenContext, group: '3_toggle' }, + { id: MenuId.TitleBarContext, order: 1, when: IsMainWindowFullscreenContext, group: '3_toggle' }, + { id: MenuId.TitleBarTitleContext, order: 1, when: IsMainWindowFullscreenContext, group: '3_toggle' }, ] }); } From 504aeb7da3cecbb81c927f6ed9cc23a30c0c5798 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Thu, 18 Jan 2024 17:35:34 +0100 Subject: [PATCH 116/333] :lipstick: --- src/vs/workbench/browser/parts/titlebar/titlebarActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index eff2013a056e8..c66ddf2cffb7b 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -69,8 +69,8 @@ registerAction2(class ToggleCustomTitleBar extends Action2 { id: `toggle.${TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY}`, title: localize('toggle.hideCustomTitleBar', 'Hide Custom Title Bar'), menu: [ - { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.equals(`config.${TitleBarSetting.TITLE_BAR_STYLE}`, TitlebarStyle.NATIVE), group: '3_toggle' }, - { id: MenuId.TitleBarTitleContext, order: 0, when: ContextKeyExpr.equals(`config.${TitleBarSetting.TITLE_BAR_STYLE}`, TitlebarStyle.NATIVE), group: '3_toggle' }, + { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.equals(`config.window.titleBarStyle`, TitlebarStyle.NATIVE), group: '3_toggle' }, + { id: MenuId.TitleBarTitleContext, order: 0, when: ContextKeyExpr.equals(`config.window.titleBarStyle`, TitlebarStyle.NATIVE), group: '3_toggle' }, ] }); } From 4bc23ab5d23ec4e312f82caa1e1105ea67e46927 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Thu, 18 Jan 2024 17:40:55 +0100 Subject: [PATCH 117/333] :lipstick: --- src/vs/workbench/browser/parts/titlebar/titlebarActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index c66ddf2cffb7b..c010eac936a84 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -69,8 +69,8 @@ registerAction2(class ToggleCustomTitleBar extends Action2 { id: `toggle.${TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY}`, title: localize('toggle.hideCustomTitleBar', 'Hide Custom Title Bar'), menu: [ - { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.equals(`config.window.titleBarStyle`, TitlebarStyle.NATIVE), group: '3_toggle' }, - { id: MenuId.TitleBarTitleContext, order: 0, when: ContextKeyExpr.equals(`config.window.titleBarStyle`, TitlebarStyle.NATIVE), group: '3_toggle' }, + { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.or(ContextKeyExpr.equals(`config.${TitleBarSetting.TITLE_BAR_STYLE}`, TitlebarStyle.NATIVE), ContextKeyExpr.equals(`config.window.nativeTabs`, ContextKeyExpr.true())), group: '3_toggle' }, + { id: MenuId.TitleBarTitleContext, order: 0, when: ContextKeyExpr.or(ContextKeyExpr.equals(`config.${TitleBarSetting.TITLE_BAR_STYLE}`, TitlebarStyle.NATIVE), ContextKeyExpr.equals(`config.window.nativeTabs`, ContextKeyExpr.true())), group: '3_toggle' }, ] }); } From 373ed50b0db434ec860be6713a2c6a4bfcaf49f2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 18 Jan 2024 17:47:01 +0100 Subject: [PATCH 118/333] voice - do not treat embedded editor as text editor (#202748) --- .../actions/voiceChatActions.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 17e6514a1b4c2..5874c2c37135c 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -46,6 +46,9 @@ import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarA import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { ICodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when getting ready for receiving voice input from the microphone for voice chat.") }); const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when voice recording from microphone is in progress for voice chat.") }); @@ -73,6 +76,15 @@ interface IVoiceChatSessionController { clearInputPlaceholder(): void; } +function getFocusedCodeEditor(editorService: IEditorService, codeEditorService: ICodeEditorService): ICodeEditor | null { + const codeEditor = getCodeEditor(codeEditorService.getFocusedCodeEditor()); + if (codeEditor && !(codeEditor instanceof EmbeddedCodeEditorWidget)) { + return codeEditor; + } + + return getCodeEditor(editorService.activeTextEditorControl); +} + class VoiceChatSessionControllerFactory { static create(accessor: ServicesAccessor, context: 'inline'): Promise; @@ -87,6 +99,7 @@ class VoiceChatSessionControllerFactory { const codeEditorService = accessor.get(ICodeEditorService); const quickChatService = accessor.get(IQuickChatService); const layoutService = accessor.get(IWorkbenchLayoutService); + const editorService = accessor.get(IEditorService); // Currently Focused Context if (context === 'focused') { @@ -114,7 +127,7 @@ class VoiceChatSessionControllerFactory { } // Try with the inline chat - const activeCodeEditor = codeEditorService.getFocusedCodeEditor(); + const activeCodeEditor = getFocusedCodeEditor(editorService, codeEditorService); if (activeCodeEditor) { const inlineChat = InlineChatController.get(activeCodeEditor); if (inlineChat?.hasFocus()) { @@ -136,7 +149,7 @@ class VoiceChatSessionControllerFactory { // Inline Chat if (context === 'inline') { - const activeCodeEditor = codeEditorService.getFocusedCodeEditor(); + const activeCodeEditor = getFocusedCodeEditor(editorService, codeEditorService); if (activeCodeEditor) { const inlineChat = InlineChatController.get(activeCodeEditor); if (inlineChat) { @@ -774,6 +787,7 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IInstantiationService instantiationService: IInstantiationService, @ICodeEditorService private readonly codeEditorService: ICodeEditorService, + @IEditorService private readonly editorService: IEditorService, @IHostService private readonly hostService: IHostService ) { super(); @@ -889,7 +903,7 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe case KeywordActivationContribution.SETTINGS_VALUE.QUICK_CHAT: return QuickVoiceChatAction.ID; case KeywordActivationContribution.SETTINGS_VALUE.CHAT_IN_CONTEXT: - if (this.codeEditorService.getFocusedCodeEditor()) { + if (getFocusedCodeEditor(this.editorService, this.codeEditorService)) { return InlineVoiceChatAction.ID; } default: From b50d5f44ea78f84b11d7d7d4cc3f9f37e90d1ebf Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 18 Jan 2024 17:48:54 +0100 Subject: [PATCH 119/333] enable experimentation for `inlineChat.mode` (#202750) --- src/vs/workbench/contrib/inlineChat/common/inlineChat.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index 64370f4950c95..f7becc10ec49f 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -217,7 +217,8 @@ Registry.as(Extensions.Configuration).registerConfigurat localize('mode.livePreview', "Changes are applied directly to the document and are highlighted visually via inline or side-by-side diffs. Ending a session will keep the changes."), localize('mode.preview', "Changes are previewed only and need to be accepted via the apply button. Ending a session will discard the changes."), localize('mode.live', "Changes are applied directly to the document, can be highlighted via inline diffs, and accepted/discarded by hunks. Ending a session will keep the changes."), - ] + ], + tags: ['experimental'] }, [InlineChatConfigKeys.FinishOnType]: { description: localize('finishOnType', "Whether to finish an inline chat session when typing outside of changed regions."), From 3473bac9638fc3fc5fa51209a7086ea0a3412597 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Thu, 18 Jan 2024 18:10:50 +0100 Subject: [PATCH 120/333] :lipstick: --- src/vs/platform/window/common/window.ts | 5 +++-- src/vs/platform/windows/electron-main/windows.ts | 4 ++-- src/vs/workbench/browser/layout.ts | 5 +++-- src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 1 + 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index 14f3914639ca4..2b991c2b2039f 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -127,10 +127,10 @@ export function isFileToOpen(uriToOpen: IWindowOpenable): uriToOpen is IFileToOp export type MenuBarVisibility = 'classic' | 'visible' | 'toggle' | 'hidden' | 'compact'; export function getMenuBarVisibility(configurationService: IConfigurationService): MenuBarVisibility { - const titleBarStyle = getTitleBarStyle(configurationService); + const nativeTitleBarEnabled = hasNativeTitlebar(configurationService); const menuBarVisibility = configurationService.getValue('window.menuBarVisibility'); - if (menuBarVisibility === 'default' || (titleBarStyle !== TitlebarStyle.CUSTOM && menuBarVisibility === 'compact') || (isMacintosh && isNative)) { + if (menuBarVisibility === 'default' || (nativeTitleBarEnabled && menuBarVisibility === 'compact') || (isMacintosh && isNative)) { return 'classic'; } else { return menuBarVisibility; @@ -182,6 +182,7 @@ export const enum CustomTitleBarVisibility { } export function hasCustomTitlebar(configurationService: IConfigurationService, titleBarStyle?: TitlebarStyle): boolean { + // Returns if it possible to have a custom title bar in the curren session // Does not imply that the title bar is visible return true; diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 7cf53969e95e0..5985e2f96917d 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -173,8 +173,8 @@ export function defaultBrowserWindowOptions(accessor: ServicesAccessor, windowSt options.tabbingIdentifier = productService.nameShort; // this opts in to sierra tabs } - const useCustomTitleStyle = !hasNativeTitlebar(configurationService); - if (useCustomTitleStyle) { + const hideNativeTitleBar = !hasNativeTitlebar(configurationService); + if (hideNativeTitleBar) { options.titleBarStyle = 'hidden'; if (!isMacintosh) { options.frame = false; diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index f40144d8cc662..6eb24dbdeca5f 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -370,7 +370,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this._register(addDisposableListener(this.mainContainer, EventType.SCROLL, () => this.mainContainer.scrollTop = 0)); // Menubar visibility changes - if ((isWindows || isLinux || isWeb) && !hasNativeTitlebar(this.configurationService)) { + const showingCustomMenu = (isWindows || isLinux || isWeb) && !hasNativeTitlebar(this.configurationService); + if (showingCustomMenu) { this._register(this.titleService.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible))); } @@ -1240,7 +1241,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return true; } - // Hide custom title bar when native title bar and custom title bar is empty + // Hide custom title bar when native title bar enabled and custom title bar is empty if (nativeTitleBarEnabled) { return false; } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 2f90f198a3664..13549aec7d3c9 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -487,6 +487,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { // Text Title if (!this.isCommandCenterVisible) { + // Don't show title in custom titlebar when native title is shown if (!hasNativeTitlebar(this.configurationService, this.titleBarStyle)) { this.title.innerText = this.windowTitle.value; From 6c462f37de22e7be4b47963a6596e08629b92827 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 18 Jan 2024 10:35:46 -0800 Subject: [PATCH 121/333] speech -> voice --- ...minalSpeechToText.css => terminalVoice.css} | 8 ++++---- .../terminal/browser/terminal.contribution.ts | 2 +- .../terminal/browser/terminalActions.ts | 18 +++++++++--------- ...erminalSpeechToText.ts => terminalVoice.ts} | 16 ++++++++-------- .../contrib/terminal/common/terminal.ts | 4 ++-- 5 files changed, 24 insertions(+), 24 deletions(-) rename src/vs/workbench/contrib/terminal/browser/media/{terminalSpeechToText.css => terminalVoice.css} (86%) rename src/vs/workbench/contrib/terminal/browser/{terminalSpeechToText.ts => terminalVoice.ts} (93%) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminalSpeechToText.css b/src/vs/workbench/contrib/terminal/browser/media/terminalVoice.css similarity index 86% rename from src/vs/workbench/contrib/terminal/browser/media/terminalSpeechToText.css rename to src/vs/workbench/contrib/terminal/browser/media/terminalVoice.css index e227a4c3321f9..fda61dd435ade 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminalSpeechToText.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminalVoice.css @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.terminal-speech-to-text { +.terminal-voice { background-color: var(--vscode-terminal-background, var(--vscode-panel-background)); padding: 2px; border-radius: 8px; @@ -13,14 +13,14 @@ z-index: 1000; } -.terminal-speech-to-text.codicon.codicon-mic-filled { +.terminal-voice.codicon.codicon-mic-filled { display: flex; align-items: center; width: 16px; height: 16px; } -.terminal-speech-to-text.recording.codicon.codicon-mic-filled { +.terminal-voice.recording.codicon.codicon-mic-filled { color: var(--vscode-activityBarBadge-background); animation: ani-terminal-speech 1s infinite; } @@ -39,7 +39,7 @@ } } -.terminal-speech-progress-text { +.terminal-voice-progress-text { font-style: italic; color: var(--vscode-editorGhostText-foreground) !important; border: 1px solid var(--vscode-editorGhostText-border); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 87443272043c3..fba9c78e785e3 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -8,7 +8,7 @@ import 'vs/css!./media/scrollbar'; import 'vs/css!./media/widgets'; import 'vs/css!./media/xterm'; import 'vs/css!./media/terminal'; -import 'vs/css!./media/terminalSpeechToText'; +import 'vs/css!./media/terminalVoice'; import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 8be2aa0dce69a..05e73b607ceda 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -64,7 +64,7 @@ import { AccessibleViewProviderId, accessibleViewCurrentProviderId, accessibleVi import { isKeyboardEvent, isMouseEvent, isPointerEvent } from 'vs/base/browser/dom'; import { editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { InstanceContext } from 'vs/workbench/contrib/terminal/browser/terminalContextMenu'; -import { TerminalSpeechToTextSession } from 'vs/workbench/contrib/terminal/browser/terminalSpeechToText'; +import { TerminalVoiceSession } from 'vs/workbench/contrib/terminal/browser/terminalVoice'; import { HasSpeechProvider } from 'vs/workbench/contrib/speech/common/speechService'; export const switchTerminalActionViewItemSeparator = '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500'; @@ -1645,30 +1645,30 @@ export function registerTerminalActions() { }); registerActiveInstanceAction({ - id: TerminalCommandId.StartSpeechToText, + id: TerminalCommandId.StartVoice, title: { - value: localize('workbench.action.startTerminalSpeechToText', "Start Terminal Speech To Text"), - original: 'Start Terminal Speech To Text' + value: localize('workbench.action.startTerminalVoice', "Start Terminal Voice"), + original: 'Start Terminal Voice' }, precondition: ContextKeyExpr.and(HasSpeechProvider, sharedWhenClause.terminalAvailable), f1: true, run: (activeInstance, c, accessor) => { const instantiationService = accessor.get(IInstantiationService); - TerminalSpeechToTextSession.getInstance(instantiationService).start(); + TerminalVoiceSession.getInstance(instantiationService).start(); } }); registerActiveInstanceAction({ - id: TerminalCommandId.StopSpeechToText, + id: TerminalCommandId.StopVoice, title: { - value: localize('workbench.action.stopTerminalSpeechToText', "Stop Terminal Speech To Text"), - original: 'Stop Terminal Speech To Text' + value: localize('workbench.action.stopTerminalVoice', "Stop Terminal Voice"), + original: 'Stop Terminal Voice' }, precondition: ContextKeyExpr.and(HasSpeechProvider, sharedWhenClause.terminalAvailable), f1: true, run: (activeInstance, c, accessor) => { const instantiationService = accessor.get(IInstantiationService); - TerminalSpeechToTextSession.getInstance(instantiationService).stop(true); + TerminalVoiceSession.getInstance(instantiationService).stop(true); } }); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts b/src/vs/workbench/contrib/terminal/browser/terminalVoice.ts similarity index 93% rename from src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts rename to src/vs/workbench/contrib/terminal/browser/terminalVoice.ts index ec8c71ee36399..e2db1158ed468 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalSpeechToText.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalVoice.ts @@ -46,20 +46,20 @@ const symbolMap: { [key: string]: string } = { 'Double quote': '"', }; -export class TerminalSpeechToTextSession extends Disposable { +export class TerminalVoiceSession extends Disposable { private _input: string = ''; private _ghostText: IDecoration | undefined; private _decoration: IDecoration | undefined; private _marker: IXtermMarker | undefined; private _ghostTextMarker: IXtermMarker | undefined; - private static _instance: TerminalSpeechToTextSession | undefined = undefined; + private static _instance: TerminalVoiceSession | undefined = undefined; private _acceptTranscriptionScheduler: RunOnceScheduler | undefined; - static getInstance(instantiationService: IInstantiationService): TerminalSpeechToTextSession { - if (!TerminalSpeechToTextSession._instance) { - TerminalSpeechToTextSession._instance = instantiationService.createInstance(TerminalSpeechToTextSession); + static getInstance(instantiationService: IInstantiationService): TerminalVoiceSession { + if (!TerminalVoiceSession._instance) { + TerminalVoiceSession._instance = instantiationService.createInstance(TerminalVoiceSession); } - return TerminalSpeechToTextSession._instance; + return TerminalVoiceSession._instance; } private _cancellationTokenSource: CancellationTokenSource | undefined; private readonly _disposables: DisposableStore; @@ -163,7 +163,7 @@ export class TerminalSpeechToTextSession extends Disposable { x: xterm.buffer.active.cursorX ?? 0, }); this._decoration?.onRender((e: HTMLElement) => { - e.classList.add(...ThemeIcon.asClassNameArray(Codicon.micFilled), 'terminal-speech-to-text', 'recording'); + e.classList.add(...ThemeIcon.asClassNameArray(Codicon.micFilled), 'terminal-voice', 'recording'); e.style.transform = 'translate(-5px, -5px)'; }); } @@ -193,7 +193,7 @@ export class TerminalSpeechToTextSession extends Disposable { x: xterm.buffer.active.cursorX + 1 ?? 0, }); this._ghostText?.onRender((e: HTMLElement) => { - e.classList.add('terminal-speech-progress-text'); + e.classList.add('terminal-voice-progress-text'); e.textContent = text; e.style.width = 'fit-content'; }); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 5ff4e94366420..80c91d5199278 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -495,8 +495,8 @@ export const enum TerminalCommandId { FocusHover = 'workbench.action.terminal.focusHover', ShowEnvironmentContributions = 'workbench.action.terminal.showEnvironmentContributions', ToggleStickyScroll = 'workbench.action.terminal.toggleStickyScroll', - StartSpeechToText = 'workbench.action.startTerminalSpeechToText', - StopSpeechToText = 'workbench.action.stopTerminalSpeechToText', + StartVoice = 'workbench.action.startTerminalVoice', + StopVoice = 'workbench.action.stopTerminalVoice', // Developer commands From 0cd38c74e69a76905ea6a90eb21844ff9c19567f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 18 Jan 2024 10:46:25 -0800 Subject: [PATCH 122/333] Add more `ensureNoDisposablesAreLeakedInTestSuite` (#202292) For #200091 --- .eslintrc.json | 5 ----- src/vs/base/test/browser/comparers.test.ts | 3 ++- src/vs/base/test/browser/hash.test.ts | 3 +++ .../test/browser/editSort.test.ts | 4 ++++ .../test/common/instantiationService.test.ts | 3 +++ .../api/test/browser/extHostTextEditor.test.ts | 16 ++++++++++------ 6 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 3b616d8a44e38..733bcd3e03670 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -149,13 +149,10 @@ // Files should (only) be removed from the list they adopt the leak detector "exclude": [ "src/vs/base/test/browser/browser.test.ts", - "src/vs/base/test/browser/comparers.test.ts", - "src/vs/base/test/browser/hash.test.ts", "src/vs/base/test/browser/ui/scrollbar/scrollableElement.test.ts", "src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts", "src/vs/editor/contrib/codeAction/test/browser/codeActionKeybindingResolver.test.ts", "src/vs/editor/contrib/codeAction/test/browser/codeActionModel.test.ts", - "src/vs/editor/contrib/dropOrPasteInto/test/browser/editSort.test.ts", "src/vs/editor/contrib/gotoSymbol/test/browser/referencesModel.test.ts", "src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts", "src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts", @@ -173,7 +170,6 @@ "src/vs/platform/contextkey/test/common/scanner.test.ts", "src/vs/platform/extensions/test/common/extensionValidator.test.ts", "src/vs/platform/instantiation/test/common/graph.test.ts", - "src/vs/platform/instantiation/test/common/instantiationService.test.ts", "src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts", "src/vs/platform/keybinding/test/common/keybindingLabels.test.ts", "src/vs/platform/keybinding/test/common/keybindingResolver.test.ts", @@ -188,7 +184,6 @@ "src/vs/workbench/api/test/browser/extHostApiCommands.test.ts", "src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts", "src/vs/workbench/api/test/browser/extHostDocumentSaveParticipant.test.ts", - "src/vs/workbench/api/test/browser/extHostTextEditor.test.ts", "src/vs/workbench/api/test/browser/extHostTypeConverter.test.ts", "src/vs/workbench/api/test/browser/extHostWorkspace.test.ts", "src/vs/workbench/api/test/browser/mainThreadConfiguration.test.ts", diff --git a/src/vs/base/test/browser/comparers.test.ts b/src/vs/base/test/browser/comparers.test.ts index 6e7047c8699a1..8848b04a6e87b 100644 --- a/src/vs/base/test/browser/comparers.test.ts +++ b/src/vs/base/test/browser/comparers.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { compareFileExtensions, compareFileExtensionsDefault, compareFileExtensionsLower, compareFileExtensionsUnicode, compareFileExtensionsUpper, compareFileNames, compareFileNamesDefault, compareFileNamesLower, compareFileNamesUnicode, compareFileNamesUpper } from 'vs/base/common/comparers'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; const compareLocale = (a: string, b: string) => a.localeCompare(b); const compareLocaleNumeric = (a: string, b: string) => a.localeCompare(b, undefined, { numeric: true }); @@ -694,7 +695,7 @@ suite('Comparers', () => { assert(compareFileExtensionsUnicode('txt.abc01', 'txt.abc1') < 0, 'extensions with equivalent numbers sort in unicode order'); assert(compareFileExtensionsUnicode('a.ext1', 'b.Ext1') < 0, 'if extensions with numbers are equal except for case, unicode full filenames should be compared'); assert(compareFileExtensionsUnicode('a.ext1', 'a.Ext1') > 0, 'if extensions with numbers are equal except for case, unicode full filenames should be compared'); - }); + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/base/test/browser/hash.test.ts b/src/vs/base/test/browser/hash.test.ts index b029d748f1994..e613a1913f18b 100644 --- a/src/vs/base/test/browser/hash.test.ts +++ b/src/vs/base/test/browser/hash.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { sha1Hex } from 'vs/base/browser/hash'; import { hash, StringSHA1 } from 'vs/base/common/hash'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Hash', () => { test('string', () => { @@ -101,4 +102,6 @@ suite('Hash', () => { test('sha1-4', () => { return checkSHA1('hello', 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d'); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/editor/contrib/dropOrPasteInto/test/browser/editSort.test.ts b/src/vs/editor/contrib/dropOrPasteInto/test/browser/editSort.test.ts index 44939652de36d..f41f6866982b7 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/test/browser/editSort.test.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/test/browser/editSort.test.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { DocumentOnDropEdit } from 'vs/editor/common/languages'; import { sortEditsByYieldTo } from 'vs/editor/contrib/dropOrPasteInto/browser/edit'; @@ -18,6 +19,7 @@ function createTestEdit(providerId: string, args?: Partial): DropEdit } suite('sortEditsByYieldTo', () => { + test('Should noop for empty edits', () => { const edits: DropEdit[] = []; @@ -62,4 +64,6 @@ suite('sortEditsByYieldTo', () => { assert.deepStrictEqual(sortEditsByYieldTo(edits).map(x => x.providerId), ['c', 'a', 'b']); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/platform/instantiation/test/common/instantiationService.test.ts b/src/vs/platform/instantiation/test/common/instantiationService.test.ts index 349e31056ffad..d3fa8924baa19 100644 --- a/src/vs/platform/instantiation/test/common/instantiationService.test.ts +++ b/src/vs/platform/instantiation/test/common/instantiationService.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { Emitter, Event } from 'vs/base/common/event'; import { dispose } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { createDecorator, IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; @@ -653,4 +654,6 @@ suite('Instantiation Service', () => { c.a.doIt(); assert.strictEqual(eventCount, 1); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/api/test/browser/extHostTextEditor.test.ts b/src/vs/workbench/api/test/browser/extHostTextEditor.test.ts index b6db2fb92fe4d..106a30df4f8b0 100644 --- a/src/vs/workbench/api/test/browser/extHostTextEditor.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTextEditor.test.ts @@ -3,15 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TextEditorLineNumbersStyle, Range } from 'vs/workbench/api/common/extHostTypes'; -import { TextEditorCursorStyle, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions'; -import { MainThreadTextEditorsShape, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostTextEditorOptions, ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor'; -import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData'; +import { Lazy } from 'vs/base/common/lazy'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { RenderLineNumbersType, TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; import { NullLogService } from 'vs/platform/log/common/log'; -import { Lazy } from 'vs/base/common/lazy'; +import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, MainThreadTextEditorsShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData'; +import { ExtHostTextEditor, ExtHostTextEditorOptions } from 'vs/workbench/api/common/extHostTextEditor'; +import { Range, TextEditorLineNumbersStyle } from 'vs/workbench/api/common/extHostTypes'; suite('ExtHostTextEditor', () => { @@ -59,6 +60,8 @@ suite('ExtHostTextEditor', () => { await editor.value.edit(edit => { edit.delete(new Range(0, 0, 1, 1)); }); assert.strictEqual(applyCount, 2); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); suite('ExtHostTextEditorOptions', () => { @@ -513,4 +516,5 @@ suite('ExtHostTextEditorOptions', () => { assert.deepStrictEqual(calls, [{ cursorStyle: TextEditorCursorStyle.Block, lineNumbers: RenderLineNumbersType.Relative }]); }); + ensureNoDisposablesAreLeakedInTestSuite(); }); From 2df662e02b2fd3de1575d4e3507de1d90881d570 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 19 Jan 2024 00:21:40 +0530 Subject: [PATCH 123/333] fix #197215 (#202756) --- .../workbench/services/views/browser/viewDescriptorService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/views/browser/viewDescriptorService.ts b/src/vs/workbench/services/views/browser/viewDescriptorService.ts index d841d165c09d2..16e775ba0282d 100644 --- a/src/vs/workbench/services/views/browser/viewDescriptorService.ts +++ b/src/vs/workbench/services/views/browser/viewDescriptorService.ts @@ -795,7 +795,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor }, { id: MenuId.ViewTitleContext, when: ContextKeyExpr.and( - viewContainerModel.visibleViewDescriptors.length > 1 ? ContextKeyExpr.or(...viewContainerModel.visibleViewDescriptors.map(v => ContextKeyExpr.equals('view', v.id))) : ContextKeyExpr.false() + ContextKeyExpr.or(...viewContainerModel.visibleViewDescriptors.map(v => ContextKeyExpr.equals('view', v.id))) ), order: index, group: '2_toggleVisibility' From 309915ba0c819bc77baba19ea8b7afdb611b26a1 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Thu, 18 Jan 2024 11:05:54 -0800 Subject: [PATCH 124/333] Nb Sticky Scroll z-index & css fixes (#201837) * no absolute positioning, scrolltop compute based on sticky lines. * remove z-index var * compute re-write, remove init, reduce pop-in * dispose delayer * remove debounce * edge case for cell 0 header, next animation frame instead of debounce * add delayer back, further improve pop in * remove unused param, update testing snapshots --- .../lib/stylelint/vscode-known-variables.json | 1 - .../media/notebookEditorStickyScroll.css | 15 - .../notebook/browser/notebookEditorWidget.ts | 24 +- .../view/cellParts/cellToolbarStickyScroll.ts | 3 +- .../viewParts/notebookEditorStickyScroll.ts | 339 +++++++----------- ...ing_against_equivalent_level_header.0.snap | 1 - ...ing_against_equivalent_level_header.0.snap | 1 + ...___scrolltop_halfway_through_cell_2.0.snap | 2 +- .../test/browser/notebookStickyScroll.test.ts | 15 +- 9 files changed, 167 insertions(+), 234 deletions(-) delete mode 100644 src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_equivalent_level_header.0.snap create mode 100644 src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_2___collapsing_against_equivalent_level_header.0.snap diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index cbb1a5e14dd10..96124fe8a87b7 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -807,7 +807,6 @@ "--z-index-notebook-progress-bar", "--z-index-notebook-scrollbar", "--z-index-run-button-container", - "--z-index-notebook-sticky-scroll", "--zoom-factor", "--test-bar-width" ] diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css b/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css index ce91cb5091028..4b4078aecbe8b 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css @@ -5,9 +5,7 @@ .monaco-workbench .notebookOverlay .notebook-sticky-scroll-container { display: none; - position: absolute; background-color: var(--vscode-notebook-editorBackground); - z-index: var(--z-index-notebook-sticky-scroll); width: 100%; } @@ -17,7 +15,6 @@ .notebook-sticky-scroll-line { background-color: var(--vscode-notebook-editorBackground); position: relative; - z-index: 0; padding-left: 12px; /* transition: margin-top 0.2s ease-in-out; */ } @@ -35,15 +32,3 @@ background-color: var(--vscode-editorStickyScrollHover-background); cursor: pointer; } - -.monaco-workbench - .notebookOverlay - .notebook-sticky-scroll-container - .notebook-shadow { - display: block; - top: 0; - left: 3px; - height: 3px; - width: 100%; - box-shadow: var(--vscode-scrollbar-shadow) 0 6px 6px -6px inset; -} diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 1a0b859cb60f5..e70b4b62870d5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1070,6 +1070,27 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD private _registerNotebookStickyScroll() { this._notebookStickyScroll = this._register(this.instantiationService.createInstance(NotebookStickyScroll, this._notebookStickyScrollContainer, this, this._notebookOutline, this._list)); + + const localDisposableStore = this._register(new DisposableStore()); + + this._register(this._notebookStickyScroll.onDidChangeNotebookStickyScroll((sizeDelta) => { + const d = localDisposableStore.add(DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), () => { + if (this.isDisposed) { + return; + } + + if (this._dimension) { + if (sizeDelta > 0) { // delta > 0 ==> sticky is growing, cell list shrinking + this.layout(this._dimension); + this.setScrollTop(this.scrollTop + sizeDelta); + } else if (sizeDelta < 0) { // delta < 0 ==> sticky is shrinking, cell list growing + this.setScrollTop(this.scrollTop + sizeDelta); + this.layout(this._dimension); + } + } + localDisposableStore.delete(d); + })); + })); } private _updateOutputRenderers() { @@ -1823,7 +1844,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._dimension = dimension; this._position = position; - const newBodyHeight = this.getBodyHeight(dimension.height); + const newBodyHeight = this.getBodyHeight(dimension.height) - this.getLayoutInfo().stickyHeight; DOM.size(this._body, dimension.width, newBodyHeight); const topInserToolbarHeight = this._notebookOptions.computeTopInsertToolbarHeight(this.viewModel?.viewType); @@ -3152,7 +3173,6 @@ registerZIndex(ZIndex.Base, 28, 'notebook-cell-bottom-toolbar-container'); registerZIndex(ZIndex.Base, 29, 'notebook-run-button-container'); registerZIndex(ZIndex.Base, 29, 'notebook-input-collapse-condicon'); registerZIndex(ZIndex.Base, 30, 'notebook-cell-output-toolbar'); -registerZIndex(ZIndex.Base, 31, 'notebook-sticky-scroll'); registerZIndex(ZIndex.Sash, 1, 'notebook-cell-expand-part-button'); registerZIndex(ZIndex.Sash, 2, 'notebook-cell-toolbar'); registerZIndex(ZIndex.Sash, 3, 'notebook-cell-toolbar-dropdown-active'); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll.ts index 55d4de6eb4983..f425ea10dcbfe 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbarStickyScroll.ts @@ -15,10 +15,9 @@ export function registerCellToolbarStickyScroll(notebookEditor: INotebookEditor, if (cell.isInputCollapsed) { element.style.top = ''; } else { - const stickyHeight = notebookEditor.getLayoutInfo().stickyHeight; const scrollTop = notebookEditor.scrollTop; const elementTop = notebookEditor.getAbsoluteTopOfElement(cell); - const diff = scrollTop - elementTop + extraOffset + stickyHeight; + const diff = scrollTop - elementTop + extraOffset; const maxTop = cell.layoutInfo.editorHeight + cell.layoutInfo.statusBarHeight - 45; // subtract roughly the height of the execution order label plus padding const top = maxTop > 20 ? // Don't move the run button if it can only move a very short distance clamp(min, diff, maxTop) : diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts index f7b02b2e4657b..0c812230c030b 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts @@ -6,6 +6,7 @@ import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; @@ -15,10 +16,10 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { INotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; +import { OutlineEntry } from 'vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry'; import { NotebookCellOutlineProvider } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProvider'; - import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { OutlineEntry } from 'vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry'; +import { Delayer } from 'vs/base/common/async'; export class ToggleNotebookStickyScroll extends Action2 { @@ -81,32 +82,48 @@ export class NotebookStickyLine extends Disposable { } } -// TODO @Yoyokrazy: -// BEHAVIOR -// - [ ] bug with some popping around the cell transition -// - [ ] bug with only bottom most sticky being partially transitioned -// - partial rendering/transition only occuring when the headers shrink against a new section -// - **and only for BOTTOM of that initial sticky tree** -// - issues with HC themes -// UX -// - [ ] render symbols instead of #'s? -// - maybe 'Hx >' where x is the level export class NotebookStickyScroll extends Disposable { private readonly _disposables = new DisposableStore(); private currentStickyLines = new Map(); + private filteredOutlineEntries: OutlineEntry[] = []; + + private readonly _onDidChangeNotebookStickyScroll = this._register(new Emitter()); + readonly onDidChangeNotebookStickyScroll: Event = this._onDidChangeNotebookStickyScroll.event; + getDomNode(): HTMLElement { return this.domNode; } getCurrentStickyHeight() { - return this.currentStickyLines.size * 22; + let height = 0; + this.currentStickyLines.forEach((value) => { + if (value.rendered) { + height += 22; + } + }); + return height; } private setCurrentStickyLines(newStickyLines: Map) { this.currentStickyLines = newStickyLines; } + private compareStickyLineMaps(mapA: Map, mapB: Map): boolean { + if (mapA.size !== mapB.size) { + return false; + } + + for (const [key, value] of mapA) { + const otherValue = mapB.get(key); + if (!otherValue || value.rendered !== otherValue.rendered) { + return false; + } + } + + return true; + } + constructor( private readonly domNode: HTMLElement, private readonly notebookEditor: INotebookEditor, @@ -124,9 +141,6 @@ export class NotebookStickyScroll extends Disposable { if (e.stickyScroll) { this.updateConfig(); } - if (e.globalToolbar) { - this.setTop(); - } })); this._register(DOM.addDisposableListener(this.domNode, DOM.EventType.CONTEXT_MENU, async (event: MouseEvent) => { @@ -153,36 +167,36 @@ export class NotebookStickyScroll extends Disposable { } } - private setTop() { - if (this.notebookEditor.notebookOptions.getDisplayOptions().globalToolbar) { - this.domNode.style.top = '26px'; - } else { - this.domNode.style.top = '0px'; - } - } - private init() { this.notebookOutline.init(); - this.initializeContent(); + this.filteredOutlineEntries = this.notebookOutline.entries.filter(entry => entry.level !== 7); this._disposables.add(this.notebookOutline.onDidChange(() => { - DOM.clearNode(this.domNode); - this.disposeCurrentStickyLines(); - this.updateContent(computeContent(this.domNode, this.notebookEditor, this.notebookCellList, this.notebookOutline.entries)); + this.filteredOutlineEntries = this.notebookOutline.entries.filter(entry => entry.level !== 7); + const recompute = computeContent(this.notebookEditor, this.notebookCellList, this.filteredOutlineEntries, this.getCurrentStickyHeight()); + if (!this.compareStickyLineMaps(recompute, this.currentStickyLines)) { + this.updateContent(recompute); + } })); this._disposables.add(this.notebookEditor.onDidAttachViewModel(() => { this.notebookOutline.init(); - this.initializeContent(); + this.updateContent(computeContent(this.notebookEditor, this.notebookCellList, this.filteredOutlineEntries, this.getCurrentStickyHeight())); })); this._disposables.add(this.notebookEditor.onDidScroll(() => { - DOM.clearNode(this.domNode); - this.disposeCurrentStickyLines(); - this.updateContent(computeContent(this.domNode, this.notebookEditor, this.notebookCellList, this.notebookOutline.entries)); + const d = new Delayer(100); + d.trigger(() => { + d.dispose(); + const recompute = computeContent(this.notebookEditor, this.notebookCellList, this.filteredOutlineEntries, this.getCurrentStickyHeight()); + if (!this.compareStickyLineMaps(recompute, this.currentStickyLines)) { + this.updateContent(recompute); + } + }); })); } + // take in an cell index, and get the corresponding outline entry static getVisibleOutlineEntry(visibleIndex: number, notebookOutlineEntries: OutlineEntry[]): OutlineEntry | undefined { let left = 0; let right = notebookOutlineEntries.length - 1; @@ -210,91 +224,35 @@ export class NotebookStickyScroll extends Disposable { return undefined; } - private initializeContent() { - - // find last code cell of section, store bottom scroll position in sectionBottom - const visibleRange = this.notebookEditor.visibleRanges[0]; - if (!visibleRange) { - return; - } - + private updateContent(newMap: Map) { DOM.clearNode(this.domNode); - const editorScrollTop = this.notebookEditor.scrollTop; - - let trackedEntry = undefined; - let sectionBottom = 0; - for (let i = visibleRange.start; i < visibleRange.end; i++) { - if (i === 0) { // don't show headers when you're viewing the top cell - this.updateDisplay(); - this.setCurrentStickyLines(new Map()); - return; - } - const cell = this.notebookEditor.cellAt(i); - if (!cell) { - return; - } - - // if we are here, the cell is a code cell. - // check next cell, if markdown, that means this is the end of the section - // check if cell is within visible range - const nextCell = this.notebookEditor.cellAt(i + 1); - if (nextCell && i + 1 < visibleRange.end) { - if (nextCell.cellKind === CellKind.Markup) { - // this is the end of the section - // store the bottom scroll position of this cell - sectionBottom = this.notebookCellList.getCellViewScrollBottom(cell); - // compute sticky scroll height - const entry = NotebookStickyScroll.getVisibleOutlineEntry(i, this.notebookOutline.entries); - if (!entry) { - return; - } - // using 22 instead of stickyscrollheight, as we don't necessarily render each line. 22 starts rendering sticky when we have space for at least 1 of them - const newStickyHeight = NotebookStickyScroll.computeStickyHeight(entry!); - if (editorScrollTop + newStickyHeight < sectionBottom) { - trackedEntry = entry; - break; - } else { - // if (editorScrollTop + stickyScrollHeight > sectionBottom), then continue to next section - continue; - } - } - } else { - // there is no next cell, so use the bottom of the editor as the sectionBottom, using scrolltop + height - sectionBottom = this.notebookEditor.scrollTop + this.notebookEditor.getLayoutInfo().scrollHeight; - trackedEntry = NotebookStickyScroll.getVisibleOutlineEntry(i, this.notebookOutline.entries); - break; - } - } // cell loop close - - // ------------------------------------------------------------------------------------- - // we now know the cell which the sticky is determined by, and the sectionBottom value to determine how many sticky lines to render - // compute the space available for sticky lines, and render sticky lines + this.disposeCurrentStickyLines(); + this.renderStickyLines(newMap, this.domNode); - const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); - let newMap: Map = new Map(); - newMap = NotebookStickyScroll.renderStickyLines(trackedEntry?.parent, this.domNode, linesToRender, newMap, this.notebookEditor); + const oldStickyHeight = this.getCurrentStickyHeight(); this.setCurrentStickyLines(newMap); - this.updateDisplay(); - } - private updateContent(newMap: Map) { - this.setCurrentStickyLines(newMap); + // (+) = sticky height increased + // (-) = sticky height decreased + const sizeDelta = this.getCurrentStickyHeight() - oldStickyHeight; + if (sizeDelta !== 0) { + this._onDidChangeNotebookStickyScroll.fire(sizeDelta); + } this.updateDisplay(); } private updateDisplay() { - const hasSticky = this.currentStickyLines.size > 0; + const hasSticky = this.getCurrentStickyHeight() > 0; if (!hasSticky) { this.domNode.style.display = 'none'; } else { this.domNode.style.display = 'block'; } - this.setTop(); } static computeStickyHeight(entry: OutlineEntry) { let height = 0; - if (entry.cell.cellKind === CellKind.Markup) { + if (entry.cell.cellKind === CellKind.Markup && entry.level !== 7) { height += 22; } while (entry.parent) { @@ -304,8 +262,9 @@ export class NotebookStickyScroll extends Disposable { return height; } - static renderStickyLines(entry: OutlineEntry | undefined, containerElement: HTMLElement, numLinesToRender: number, newMap: Map, notebookEditor: INotebookEditor) { + static checkCollapsedStickyLines(entry: OutlineEntry | undefined, numLinesToRender: number, notebookEditor: INotebookEditor) { let currentEntry = entry; + const newMap = new Map(); const elementsToRender = []; while (currentEntry) { @@ -320,32 +279,27 @@ export class NotebookStickyScroll extends Disposable { currentEntry = currentEntry.parent; } - // TODO: clean up partial cell animation - // [ ] slight pop as lines finish disappearing - // [ ] only actually works when shrunk against new section. **and only for BOTTOM of that initial sticky tree** - // [ ] issues with HC themes - // use negative margins to render the bottom sticky line as a partial element - // todo: partial render logic here - // if (numLinesToRender % 1 !== 0) { - // const partialHeight = 22 - Math.floor((numLinesToRender % 1) * 22); - // elementsToRender[elementsToRender.length - 1].element.style.zIndex = '-1'; - // elementsToRender[elementsToRender.length - 1].element.style.marginTop = `-${partialHeight}px`; - // } - // iterate over elements to render, and append to container // break when we reach numLinesToRender for (let i = 0; i < elementsToRender.length; i++) { if (i >= numLinesToRender) { break; } - containerElement.append(elementsToRender[i].element); newMap.set(elementsToRender[i].entry, { line: elementsToRender[i], rendered: true }); } - - containerElement.append(DOM.$('div', { class: 'notebook-shadow' })); // ensure we have dropShadow at base of sticky scroll return newMap; } + private renderStickyLines(stickyMap: Map, containerElement: HTMLElement) { + const reversedEntries = Array.from(stickyMap.entries()).reverse(); + for (const [, value] of reversedEntries) { + if (!value.rendered) { + continue; + } + containerElement.append(value.line.element); + } + } + static createStickyElement(entry: OutlineEntry, notebookEditor: INotebookEditor) { const stickyElement = document.createElement('div'); stickyElement.classList.add('notebook-sticky-scroll-line'); @@ -366,111 +320,92 @@ export class NotebookStickyScroll extends Disposable { } } -export function computeContent(domNode: HTMLElement, notebookEditor: INotebookEditor, notebookCellList: INotebookCellList, notebookOutlineEntries: OutlineEntry[]): Map { - // find first code cell in visible range. this marks the start of the first section - // find the last code cell in the first section of the visible range, store the bottom scroll position in a const sectionBottom - // compute sticky scroll height, and check if editorScrolltop + stickyScrollHeight < sectionBottom - // if that condition is true, break out of the loop with that cell as the tracked cell - // if that condition is false, continue to next cell - - const editorScrollTop = notebookEditor.scrollTop; - - // find last code cell of section, store bottom scroll position in sectionBottom +export function computeContent(notebookEditor: INotebookEditor, notebookCellList: INotebookCellList, notebookOutlineEntries: OutlineEntry[], renderedStickyHeight: number): Map { + // get data about the cell list within viewport ---------------------------------------------------------------------------------------- + const editorScrollTop = notebookEditor.scrollTop - renderedStickyHeight; const visibleRange = notebookEditor.visibleRanges[0]; if (!visibleRange) { return new Map(); } - let trackedEntry = undefined; - let sectionBottom = 0; - for (let i = visibleRange.start; i < visibleRange.end; i++) { - const cell = notebookEditor.cellAt(i); + // edge case for cell 0 in the notebook is a header ------------------------------------------------------------------------------------ + if (visibleRange.start === 0) { + const firstCell = notebookEditor.cellAt(0); + const firstCellEntry = NotebookStickyScroll.getVisibleOutlineEntry(0, notebookOutlineEntries); + if (firstCell && firstCellEntry && firstCell.cellKind === CellKind.Markup && firstCellEntry.level !== 7) { + if (notebookEditor.scrollTop > 22) { + const newMap = NotebookStickyScroll.checkCollapsedStickyLines(firstCellEntry, 100, notebookEditor); + return newMap; + } + } + } + + // iterate over cells in viewport ------------------------------------------------------------------------------------------------------ + let cell; + let cellEntry; + const startIndex = visibleRange.start - 1; // -1 to account for cells hidden "under" sticky lines. + for (let currentIndex = startIndex; currentIndex < visibleRange.end; currentIndex++) { + // store data for current cell, and next cell + cell = notebookEditor.cellAt(currentIndex); if (!cell) { return new Map(); } + cellEntry = NotebookStickyScroll.getVisibleOutlineEntry(currentIndex, notebookOutlineEntries); + if (!cellEntry) { + return new Map(); + } - const nextCell = notebookEditor.cellAt(i + 1); + const nextCell = notebookEditor.cellAt(currentIndex + 1); + if (!nextCell) { + const sectionBottom = notebookEditor.getLayoutInfo().scrollHeight; + const linesToRender = Math.floor((sectionBottom) / 22); + const newMap = NotebookStickyScroll.checkCollapsedStickyLines(cellEntry, linesToRender, notebookEditor); + return newMap; + } + const nextCellEntry = NotebookStickyScroll.getVisibleOutlineEntry(currentIndex + 1, notebookOutlineEntries); + if (!nextCellEntry) { + return new Map(); + } - // account for transitions between top level headers - if (cell.cellKind === CellKind.Markup) { - sectionBottom = notebookCellList.getCellViewScrollBottom(cell); - const entry = NotebookStickyScroll.getVisibleOutlineEntry(i, notebookOutlineEntries); - if (!entry) { - return new Map(); + // check next cell, if markdown with non level 7 entry, that means this is the end of the section (new header) --------------------- + if (nextCell.cellKind === CellKind.Markup && nextCellEntry.level !== 7) { + const sectionBottom = notebookCellList.getCellViewScrollTop(nextCell); + const currentSectionStickyHeight = NotebookStickyScroll.computeStickyHeight(cellEntry); + const nextSectionStickyHeight = NotebookStickyScroll.computeStickyHeight(nextCellEntry); + + // case: we can render the all sticky lines for the current section ------------------------------------------------------------ + if (editorScrollTop + currentSectionStickyHeight < sectionBottom) { + const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); + const newMap = NotebookStickyScroll.checkCollapsedStickyLines(cellEntry, linesToRender, notebookEditor); + return newMap; } - if (!entry.parent) { - // if the cell is a top level header, only render once we have scrolled past the bottom of the cell - // todo: (polish) figure out what padding value to use here. need to account properly for bottom insert cell toolbar, cell toolbar, and md cell bottom padding - if (sectionBottom > editorScrollTop) { - return new Map(); - } + // case: next section is the same size or bigger, render next entry ----------------------------------------------------------- + else if (nextSectionStickyHeight >= currentSectionStickyHeight) { + const newMap = NotebookStickyScroll.checkCollapsedStickyLines(nextCellEntry, 100, notebookEditor); + return newMap; } - } + // case: next section is the smaller, shrink until next section height is greater than the available space --------------------- + else if (nextSectionStickyHeight < currentSectionStickyHeight) { + const availableSpace = sectionBottom - editorScrollTop; - // if we are here, the cell is a code cell. - // check next cell, if markdown, that means this is the end of the section - if (nextCell && i + 1 < visibleRange.end) { - if (nextCell.cellKind === CellKind.Markup) { - // this is the end of the section - // store the bottom scroll position of this cell - sectionBottom = notebookCellList.getCellViewScrollBottom(cell); - // compute sticky scroll height - const entry = NotebookStickyScroll.getVisibleOutlineEntry(i, notebookOutlineEntries); - if (!entry) { - return new Map(); - } - // check if we can render this section of sticky - const currentSectionStickyHeight = NotebookStickyScroll.computeStickyHeight(entry!); - if (editorScrollTop + currentSectionStickyHeight < sectionBottom) { - const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); - let newMap: Map = new Map(); - newMap = NotebookStickyScroll.renderStickyLines(entry, domNode, linesToRender, newMap, notebookEditor); + if (availableSpace >= nextSectionStickyHeight) { + const linesToRender = Math.floor((availableSpace) / 22); + const newMap = NotebookStickyScroll.checkCollapsedStickyLines(cellEntry, linesToRender, notebookEditor); return newMap; - } - - let nextSectionEntry = undefined; - for (let j = 1; j < visibleRange.end - i; j++) { - // find next section after this one - const cellCheck = notebookEditor.cellAt(i + j); - if (cellCheck) { - nextSectionEntry = NotebookStickyScroll.getVisibleOutlineEntry(i + j, notebookOutlineEntries); - if (nextSectionEntry) { - break; - } - } - } - const nextSectionStickyHeight = NotebookStickyScroll.computeStickyHeight(nextSectionEntry!); - - // recompute section bottom based on the top of the next section - sectionBottom = notebookCellList.getCellViewScrollTop(nextSectionEntry!.cell) - 10; - - // this block of logic cleans transitions between two sections that share a parent. - // if the current section and the next section share a parent, then we can render the next section's sticky lines to avoid pop-in between - if (entry?.parent?.parent === nextSectionEntry?.parent) { - const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22) + 100; - let newMap: Map = new Map(); - newMap = NotebookStickyScroll.renderStickyLines(nextSectionEntry?.parent, domNode, linesToRender, newMap, notebookEditor); - return newMap; - } else if (Math.abs(currentSectionStickyHeight - nextSectionStickyHeight) > 22) { // only shrink sticky - const linesToRender = (sectionBottom - editorScrollTop) / 22; - let newMap: Map = new Map(); - newMap = NotebookStickyScroll.renderStickyLines(entry?.parent, domNode, linesToRender, newMap, notebookEditor); + } else { + const newMap = NotebookStickyScroll.checkCollapsedStickyLines(nextCellEntry, 100, notebookEditor); return newMap; } } - } else { - // there is no next visible cell, so use the bottom of the editor as the sectionBottom, using scrolltop + height - sectionBottom = notebookEditor.getLayoutInfo().scrollHeight; - trackedEntry = NotebookStickyScroll.getVisibleOutlineEntry(i, notebookOutlineEntries); - const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); - - let newMap: Map = new Map(); - newMap = NotebookStickyScroll.renderStickyLines(trackedEntry?.parent, domNode, linesToRender, newMap, notebookEditor); - return newMap; } - } // for cell loop close - return new Map(); + } // visible range loop close + + // case: all visible cells were non-header cells, so render any headers relevant to their section -------------------------------------- + const sectionBottom = notebookEditor.getLayoutInfo().scrollHeight; + const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); + const newMap = NotebookStickyScroll.checkCollapsedStickyLines(cellEntry, linesToRender, notebookEditor); + return newMap; } registerAction2(ToggleNotebookStickyScroll); diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_equivalent_level_header.0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_equivalent_level_header.0.snap deleted file mode 100644 index 1bcf0a58d432c..0000000000000 --- a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_equivalent_level_header.0.snap +++ /dev/null @@ -1 +0,0 @@ -[ "# header a", "## header aa" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_2___collapsing_against_equivalent_level_header.0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_2___collapsing_against_equivalent_level_header.0.snap new file mode 100644 index 0000000000000..3b3d5999fd8f6 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_2___collapsing_against_equivalent_level_header.0.snap @@ -0,0 +1 @@ +[ "# header a", "## header aa", "### header aab" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test5__should_render_0-_2___scrolltop_halfway_through_cell_2.0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test5__should_render_0-_2___scrolltop_halfway_through_cell_2.0.snap index cf5583b0ab9e4..2e13ea43822b9 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test5__should_render_0-_2___scrolltop_halfway_through_cell_2.0.snap +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test5__should_render_0-_2___scrolltop_halfway_through_cell_2.0.snap @@ -1 +1 @@ -[ "# header a", "## header aa", "### header aaa" ] +[ "# header a", "## header aa", "### header aaa" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts index 5c9682417a99d..0cb027d1beb7f 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts @@ -50,7 +50,7 @@ suite('NotebookEditorStickyScroll', () => { } function nbStickyTestHelper(domNode: HTMLElement, notebookEditor: INotebookEditor, notebookCellList: INotebookCellList, notebookOutlineEntries: OutlineEntry[], disposables: Pick) { - const output = computeContent(domNode, notebookEditor, notebookCellList, notebookOutlineEntries); + const output = computeContent(notebookEditor, notebookCellList, notebookOutlineEntries, 0); for (const stickyLine of output.values()) { disposables.add(stickyLine.line); } @@ -181,7 +181,7 @@ suite('NotebookEditorStickyScroll', () => { }); }); - test('test3: should render 0->1, collapsing against equivalent level header', async function () { + test('test3: should render 0->2, collapsing against equivalent level header', async function () { await withTestNotebook( [ ['# header a', 'markdown', CellKind.Markup, [], {}], // 0 @@ -222,7 +222,7 @@ suite('NotebookEditorStickyScroll', () => { }); // outdated/improper behavior - test.skip('test4: should render 0, scrolltop halfway through cell 0', async function () { + test('test4: should render 0, scrolltop halfway through cell 0', async function () { await withTestNotebook( [ ['# header a', 'markdown', CellKind.Markup, [], {}], @@ -260,8 +260,7 @@ suite('NotebookEditorStickyScroll', () => { }); }); - // outdated/improper behavior - test.skip('test5: should render 0->2, scrolltop halfway through cell 2', async function () { + test('test5: should render 0->2, scrolltop halfway through cell 2', async function () { await withTestNotebook( [ ['# header a', 'markdown', CellKind.Markup, [], {}], @@ -301,8 +300,7 @@ suite('NotebookEditorStickyScroll', () => { }); }); - // outdated/improper behavior - test.skip('test6: should render 6->7, scrolltop halfway through cell 7', async function () { + test('test6: should render 6->7, scrolltop halfway through cell 7', async function () { await withTestNotebook( [ ['# header a', 'markdown', CellKind.Markup, [], {}], @@ -342,7 +340,6 @@ suite('NotebookEditorStickyScroll', () => { }); }); - // waiting on behavior push to fix this. test('test7: should render 0->1, collapsing against next section', async function () { await withTestNotebook( [ @@ -384,6 +381,4 @@ suite('NotebookEditorStickyScroll', () => { outline.dispose(); }); }); - - }); From dc545a6242c994e33676352d2c8648f303e944ee Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Thu, 18 Jan 2024 20:06:09 +0100 Subject: [PATCH 125/333] Update icons ttf --- .../browser/ui/codicons/codicon/codicon.ttf | Bin 78244 -> 79504 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index cf782c9395efaad94ff7dbe7d6abd7e23f4c6e4c..4894dfa316d4a98f4804f67b7840b00e0e20051a 100644 GIT binary patch delta 8341 zcmai(33ycH)yMy5GT9)Rglv<9?2}|5Kp-K>gnbW$HSDV)fg~U%1j4T9AR1WpjDKG2QVU&kvB?xlN0gC-%5oC6)Kt5Cf#WN= zmA@zR{;c`6P4$8HKfF8w(*77P{*p&J_6yrWcQSYif zawL!;dmP`&-jh--j~9eUpde&TpfDthT-tlqx!4gXj5xH(5fS(%BHo|c`Z$~k2x&d* zOl<>%rotKWC0`teSYB~h>#PmdyVg-V(jFZw{8p}90jwHpy>*Xu)H-IjwX5_RpZMoL z;q$Z5fHOEFRdOa|6MqcBIT*oaao!0kAOzoEa} zh2=<+{n&~-q>m&^jKs=z9L5uPMYhU~STA?VB3Uh~WU<^P>*aR21qWn>+yMui2xVv5 zA_~!PA%fg5oXR^n#df>pQ`w_y#|Vjb?p-M9yv5Wr^Khx@Sw58y%k6c6JOJc@024BN2- zPhlsX<~`5gSv-e**pFB78s5Yqyn}cBID(^i4=3>}oWieh8o%MNy^kRNjBoHAzQ+&v z5r4(saalwh5+V`OM%qf0xFk;EB}v?pD(xjl^2IA1q@#3_&eBB+rK@z4QYjOkluHlk zDZQk(^p*ZHPzK2msgPk(DZ|A-LPpAXnIIEol1!GVTs$+SMrKL9%#j9SDrq&ud{&oM|saTi|4B}tSLbQF)Ip$tA$%H6U@o{^pMG@h4H zT#O00LEe&g~81U|$IP^pck}(oL!wWJ6BV@YF!!IRV9+pQ@Aq{v^UPd>`l4uE&ei)0d zWvE<_)$mJ;)Z!()h@Z==@(KpaSQ(A+*tPduRJ8aP=m#lB%onTh8-!J>;2f1E%^-uR zvlV`!u<8`-rPeD_L~T&y9_k!LGO2SFF?zi+Ng{bNPvLhC>jnj*sEvxeMr~4L8r9rK z#5AB;p=E%@&KR&UaMIAoowF%@l4#5Bgt4iPgf8x=7PxK|PL4q9Y`+>WlW z0xS?(P*|H4ZHINr3_A}r^RRxc(BQ*5t(!A1=jBstfGFR(C5JVRH65Q^_fCH1nYB!9tqYL z3Vjo-KPdE4u>PpfU%~p5fh(A<3)YtkofxdI6uK%{mO{q{%U0;(U3!unpJM}_r+Lf;DO zM}=M%)?XF+TUZwrdR|z6Q(8NAiRZ*mcMR+A3LP`7%L-jItSbtgHQ4!9=(g#C6gqI& z4u!59c8Ef!4%?~Fy~7Sw=;&dGDd6I^!&yHMp!0_vp>P`jJ5u3>0Cro2TLaip3O5O` zqZMu!V7nA<9AL*N+(N*PRk)de-A>`Q0(P8&_c{M|yu!T(>;#3o4%mqb_aCs66z)V| zyA|$9U?(fwoxn~}xKDwds&K~wyS>7_3+yxn9A!J5_<0aBoEeIk;mlOT3}==iW^}R@ zX+zCX#Egzd5i>fuikK0}Q^fQ(UlG$;uOjS}-|oNy5!0)VikQ|HC}LXQNfA?NXGP2l zyC`B_Sg443UspxU`?@J&-ch88c}H<8eu#NVcSX!gN)$0KDOJS0q)ZWW&8LXDR<4M- z)Q!VQ&j%NG_b zP^T-hggQf!rPS*bS!TX}(?TN4siuWQZlanN5?M*DRpe%>X)F;xb+%%HM!QatHB{3H zB5SD)imaoW4iUMRI@iFL$R?g#ugFiS^AvfAYI;rN5vu7mk!@7dYa)+P=PU9!)rD45nb4IVTLOU~f{8ABaeE7dctFPhqeH`+kLC7wjzx-l9IB z$gV(Dn#W(ql360ect-nSMN+6{xeysceN-_?6nmS(;0?A}Is`9Mw<~fT^>IaJP4C7j|H=i2xCClI~7c&KCLh=g#C2Tf0jn8v)Kh-vVv z3WHPF2Nak}UsGfh)ijDo74;2;K`iWp3T~jjX-0(yF_UH1ERlDprcC5*>Q!Vj&&_W{ z%;X`xRX1YigDW+tSV`T1PI zLFyNZWCuRU^!P8b^reD$>Q{>VjA|*&Bfz#5W)xrt73LOTf2}af0Q;Q6d;{!1E6hB= z{zidm+P4a`5wOpj6~Y4(6R^Kin4*AvL1D52_FojHEnt7IFo6O42L)zKepHy$fc;km zW)+yu5hgld|4m`a1NJ3_$&Y|!7gqiW2#Ufi2!alU`49v{6lO*cbSmJY4TdUw48br3 zR|3nkyZT46)K+2o1i>f;FHoZurc@AgDHuVGQDizbR$;;gL5{ouT)DwGMZ&3^DT6#r zMassMgDD@TI#M%IOHvzB*QM@C{Ur6r_RHJvY=1m0 zEv;u-P1>ro9cgFNE~clYk4(SCpZ-kxvGfZWSsBAJR%LvUIW2QT=98IcvwCOUlJ!}3 z-|VH?M{>I5EYEp8=d!2Hv)1#X=dkCq+=kpWx!ZCN2Te6GvfLPz16!b62;x~6sQ-?g#puCAxL z`MY(i>2|2w<)Y3-Ek&n_9mQG2^NY6^AMRe#eSP=C-M=pxTym+juyk|jrLwBBO=Snm zJ}CRf=kk^LrukO-w)*z_&iKxiN0sN64=kTn-cr7+{L3Cadz|i>(6hYf)}Al)Jl?CW z*UDZG^*Y|WxOY|W?Y)opzSL)6pSgY3_1V_vSfBHKbNqc*_kF1E`FDc6m$(tu%p0a$(!KqnO@1J^nTEw(j)6Ps!o4#&F z?2I!rE?gH^jq2j+Vb$}i_g7z@nK83!=H{B*nu?lLH3w_HsEw^HuAN(Zc~RCJd zvuDg+TNhS0ux?k~>3UcFn)-tc&W4JH)eT4HB+O}?vvJOcb6e)VH}Ad1RgJqEPd4># zs%zTVw6p2V{0{Rc&EMHv(Y&g8WAnD=JCJC5zI&p_N8BXwD&bUbcw+ZbYN;pD-Q8E}t&We+ z8!;k(MC%{ry7>4{KP7Q-5`N-9e0*GdE63xit4}Ok7#DXU&SZmGX972>VSD!vnG?ZA z@Ra7a19Ru_#faDE_PP5?X-T1Ya>DaHW!;N2Bq1!kEIg-BO8Po794-kj?koH~$0Z3F z;(n^DD=j*)LuF)}xQw{ysOT7XhTH2-NeHcHIW8kNqn+@??M+JZ7Mg>5W4WZc61#PZ zOpcE8WW|B^TA&gz_D@l(!!@`_? zXK1L?ocsq3jUOC6A$p=Ks=VBpSCkjhqelp(vAwswqStvH~SwAWDCc4A9SQ5iaeO`W|`7X(jGG9?yX^FQe@cgmFhSs&@dvw{> zwX^5B9vxJ;HZD6k-_@>TVQfr%L}Hid_;%4@5t8J|hzScF?ul*}6_wt#$~7l(>cCy$ z^ITmLGpF`(zj#ne1DlQ& zhiQd@6UTDH$K<*A31$Ya94iP*%!}vg-kXmt80gnE6wYuhl>7Ef(9y?p?Ua@zdh>H) z_;1BbDJ%6QrAtD%uP^`iT9)q(=fs8Obd~&)ePcRw-r$G`i4GCxW1%jmv&89?Q0JFP zneFvz+_22-USV73IYOdBB11NZhJ6|tc_K10#S!6r!WkjACi>G7TYuw#wvK8z*E07P zzA$foSGihCKk??5^pmpUOkoo;IjZU6DGhgX3DNu<3kwSgsVQi!FC;fNgpG-C6wGo) zIE$Um-j$VoLY>}_5OGY&(ne(Guq9C;j;yJ?m=Yxs&KU#BeT9|I2**C#CG*?Jk$uxr zWOazk@s{Il$2*S0j&~hL97p$klqS(3krA_->*~8SFIu$at4w*o6qnYw)Xu5jcWahp zw{6>|rG7zkmxlVqv*&Kv-bwE47TdC9ewW(D#xB=x48M9I(&()E#^z;F=Iq+f`}%d3 dkDczArHfiCx_+9-U7KLefOInev`yYv_j=lf@ delta 7271 zcmY+}33v`y-v;pepH(6u@?>9RkDV;CAPFJ}(jd0jlMrhX#IEX5RaH_&6jfDKRa7;V zzNo6wx7Ai#RaMK@zeeVq*c@uE;z_iNp$)#I%4&%1pQ06p# z;WpOI#m~nnaa#4fMgE@u+~WTDowrBTtV!j+yQla82?2on%Ifk(v)wMqz5II^w~wtU zudb|*{NtrTkkDTI^5*PWbLZWD>)I=j@N6J#`iA2P{-+?^mb~l7?BC!@l~eVtrNPbB zw(*=ykoq+DzZ%m#JUr_2o4r===i_jCG;I&}+i-9C)RE~6czVFY{XF*+!2jFq7nu(A2ik}2XD+!Wd36pS%l|)IBWN9Pqq`h>Ij?zgw zOS*KG49S!%$(C-?U2-H>dP*#jplM0z4 zQ)QaWkeM=D=18r~l?Ad;7ReG>Do(jq?vpxsNH)tBc~l;g?Xp9jke%|R?2^YVfmk|T0dUXo+-vb-uM@e8iv8h*ogCnlg8-B5#aS&1sl#tf7q8EwQ3Yw$2m z$uqqBLQ#RABvb~;0}_uTQYxb)QmQ0DQZY&z&_~>{me0;DaY!24Nt{F=6Im#f2jy9L zTAq^KcuodNkpyD89G4UFiaadqWrH|3O1*57b!dg55`}H@FHFU|_yiy0A_k)vcOeMx z;Q$U|Br4@DJc&sN#w$1>F^EAdZo(l4q$MunQ+$T6@g=^(x44Aw@gx3&FYvjn#oLmC z2jB&7G=mR*;6wi%BJmn-xVT2+yNIhGC$LluEoKp0ZW8p|6xB5|%WDRqwl4#~MC1xMfl~cU=VTKa(j zd||zz*bKuut=J&LdQ-7!hV`~$BMs|a#q)yoo?^od>#SlE4(olz#vIl;#bzDW2Z{|m zEcRhevtU~f>qEs>AJzrM_8-sMu-2`j5dKvj2j0S+OUB^^; z{UNNYiajH&UlsdEK0(*GAa<9qepBo?VO>}3LSg-`*qOq*p|F$rhhhf{>!xB?3+qqC zP8ZfK#qJl@e^t|o{l(AZWSXBKAz6v3g*HlfvQE1#S4hm+wo~Sn+HSAJoK*)UrZ63qm;-fEVh+?< zi8)XgCFVdWO3c1fm6&~}HE}}B&eN5cop)7YcAlZc>^xJ6*)~gw**05=*|wV!vu$@J zX4@W0%(gj7%(l7a`secO&Bl33%mzJ`@Naf6CFZ-{O3ZipN=)`qVzNL}F9myD`*^Xo^ zbC|jQT*!E?m>-dpF-It=V2)HWg*i&eRA#A?X-snvk{L{M5R#cpa}bi*%yCNQFwOBu zYR&UMfh#0)nG=;PV48xEEM%4|S;RDjB3Z(mq+}^`vJ!JiE0q}^+NR(n_c2Yu$&68L zQ}AirSRUes=}I;;%~_CaVVbibd6ZeDnU~f<; zt?wL_km|;@M-<0Yu+3#Aj;>&v%S<@M+^Xc6`V~#e80ggV~@sWQ1*Q7eYIxxm`%&n2kyz>Q6@} zG&NY}gh(D_n)`?3S*E#vNX&_t`-jAwths-PLr~bJGz3$+SCkB7o={T6H1`;Bs0#a} z!gMAtk<%>1?Bq2i=7;}QVs`kt5_4_eP-5}*yj{S)v!NM9ACpeuQ<|%{h{KR8}c3JUxhy9ZhQ!Gny z_5il6IFA6^r8uJi`)9?u1=v>Nv11MFWF=OAETGj|LZoQ{C~o8rU- z?CXkC6tI6+oUDL-Lvh*y_8$tS?#xLLr!rvwsbKDwTZ+>gu>Y$#(Ea^tjFg ze&FPTD6l$RZi=%YaJehahrs2bI5Ps5r{Ww5TwV%)*YA((;vB@a=8BUhaQP`TF#Q!L zQs8Q#z~{@w8a9L%-4&oX%>q{|g?E^NO3aaYr3~51|Pr z{egsq&W3=TfN25i0$i<@=-IHmuo+=X!w!cdJSBW!cy0K)@ZI4T!*51JMGT3U z7I8T;EHW>$E^>e5xyY+gK~cp~GotFEjz?XIj)=~2Mo*4jAAL0XYU{$*XIlRi6B1J# zb0FqQY)S0F*dODv<95bfj_({_6MrH8MnYmjW@XIe^4%J!5?srjk3sq0g}N()TOOxvAyD?KxPO8SdktGe#U@W`mj zIGO2|nUYzUS(dphb5G`(Ojnj~R@IJ5+uPl@ zdwlo2?iaeddieFo>#?iH$(+EPyqv0>Lph)1+{_Kg&B?9E-I#kV&(E1xk+(bVX3zMZ z6MAm$d9jyouQ9#qdL8R^y?0>mk-eAYd*tWkAMNAT=f{H91w#rB7kt*YZQnJ0FZFBP zZ*sq*{Zsm{?0>Ppt1zmtqHtT`p~BA!Zw(kTVBNs5feQwHGRSw(m_e%sxr(BS@{6_= z9WHwPu4Q+9U)-#CMe&)z-3RX+>>5%q#92FJ*U+?~bwe+gQ%6o8In!@u_RMiJ-$P&pAEkMr~p3 zlG?*_{pJ?W-8T2wypHo$&D%5Y`}qO$XUt!{plHGIg%cKDTokcrBpN%l35#ibsb6ig z#*YUtneSwS#>df{v(9ly2OR04!QKfO=^0Ie(}NS@y`m*3q=y72cqM0LBxlA*Rr!P6Zo5~c%GIc;Fq;mGO8~T{W z&=H)Fk-$P}3FE!Jyn|S{;E>R!^2N7FZj+oVU9&Q?GJA-3MyT18yt&NsHft<*MoZzT z1t(m0>^tr|e9%F6H&1tWPd9f@PcJWjPd7JDe=n!kk=L^w`frPW;(|rV{uvFSp4pxf z5armM&(jchPj@$8{>WVd%`dzT@yA|n2iAHx9DxqU$fono@LwI3oej3h|5w3Dd`C;? zd3JVh z-}k#Y9Da8Wn&WUtxTD1(ZWq{8)uzsq1AV>?m9-BzMrmDVv8MtgKd8vnmT zE|8Sbc<`;cp7kG`-P+jd+_?NBiyczsE2ob97%eN@k1U82fA=kQiL&)brzDAMzGZZZ itPS+8onO Date: Thu, 18 Jan 2024 13:42:45 -0600 Subject: [PATCH 126/333] Implement preview on highlighting quickpick in quick search (#202306) --- src/vs/workbench/browser/quickaccess.ts | 45 ++++++++++++++ .../search/browser/anythingQuickAccess.ts | 54 ++++------------- .../quickTextSearch/textSearchQuickAccess.ts | 60 +++++++++++++++---- 3 files changed, 106 insertions(+), 53 deletions(-) diff --git a/src/vs/workbench/browser/quickaccess.ts b/src/vs/workbench/browser/quickaccess.ts index afc3f54369b77..a73c3d7072c5c 100644 --- a/src/vs/workbench/browser/quickaccess.ts +++ b/src/vs/workbench/browser/quickaccess.ts @@ -8,6 +8,12 @@ import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/con import { ICommandHandler } from 'vs/platform/commands/common/commands'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { getIEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditorViewState, IDiffEditorViewState } from 'vs/editor/common/editorCommon'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; export const inQuickPickContextKeyValue = 'inQuickOpen'; export const InQuickPickContextKey = new RawContextKey(inQuickPickContextKeyValue, false, localize('inQuickOpen', "Whether keyboard focus is inside the quick open control")); @@ -45,3 +51,42 @@ export function getQuickNavigateHandler(id: string, next?: boolean): ICommandHan quickInputService.navigate(!!next, quickNavigate); }; } +export class EditorViewState { + private _editorViewState: { + editor: EditorInput; + group: IEditorGroup; + state: ICodeEditorViewState | IDiffEditorViewState | undefined; + } | undefined = undefined; + + constructor(private readonly editorService: IEditorService) { } + + set(): void { + if (this._editorViewState) { + return; // return early if already done + } + + const activeEditorPane = this.editorService.activeEditorPane; + if (activeEditorPane) { + this._editorViewState = { + group: activeEditorPane.group, + editor: activeEditorPane.input, + state: getIEditor(activeEditorPane.getControl())?.saveViewState() ?? undefined, + }; + } + } + + async restore(): Promise { + if (this._editorViewState) { + const options: IEditorOptions = { + viewState: this._editorViewState.state, + preserveFocus: true /* import to not close the picker as a result */ + }; + + await this._editorViewState.group.openEditor(this._editorViewState.editor, options); + } + } + + reset() { + this._editorViewState = undefined; + } +} diff --git a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts index 00c019964888b..03a37d5384ed7 100644 --- a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts @@ -35,19 +35,17 @@ import { ThrottledDelayer } from 'vs/base/common/async'; import { top } from 'vs/base/common/arrays'; import { FileQueryCacheState } from 'vs/workbench/contrib/search/common/cacheState'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { IEditorOptions, IResourceEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { IResourceEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { Schemas } from 'vs/base/common/network'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { ResourceMap } from 'vs/base/common/map'; import { SymbolsQuickAccessProvider } from 'vs/workbench/contrib/search/browser/symbolsQuickAccess'; import { AnythingQuickAccessProviderRunOptions, DefaultQuickAccessFilterValue, Extensions, IQuickAccessRegistry } from 'vs/platform/quickinput/common/quickAccess'; -import { IWorkbenchQuickAccessConfiguration } from 'vs/workbench/browser/quickaccess'; +import { EditorViewState, IWorkbenchQuickAccessConfiguration } from 'vs/workbench/browser/quickaccess'; import { GotoSymbolQuickAccessProvider } from 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { ScrollType, IEditor, ICodeEditorViewState, IDiffEditorViewState } from 'vs/editor/common/editorCommon'; +import { ScrollType, IEditor } from 'vs/editor/common/editorCommon'; import { Event } from 'vs/base/common/event'; -import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { getIEditor } from 'vs/editor/browser/editorBrowser'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; @@ -89,11 +87,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider | undefined = undefined; - editorViewState: { - editor: EditorInput; - group: IEditorGroup; - state: ICodeEditorViewState | IDiffEditorViewState | undefined; - } | undefined = undefined; + editorViewState: EditorViewState; scorerCache: FuzzyScorerCache = Object.create(null); fileQueryCache: FileQueryCacheState | undefined = undefined; @@ -106,7 +100,9 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider): void { @@ -131,33 +127,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider { - if (this.editorViewState) { - const options: IEditorOptions = { - viewState: this.editorViewState.state, - preserveFocus: true /* import to not close the picker as a result */ - }; - - await this.editorViewState.group.openEditor(this.editorViewState.editor, options); - } + this.editorViewState.reset(); } }(this, this.editorService); @@ -237,7 +207,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider { if (reason === QuickInputHideReason.Gesture) { - this.pickState.restoreEditorViewState(); + this.pickState.editorViewState.restore(); } })); @@ -259,7 +229,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider { +interface ITextSearchQuickAccessItem extends IPickerQuickAccessItem { + match?: Match; +} +export class TextSearchQuickAccess extends PickerQuickAccessProvider { private queryBuilder: QueryBuilder; private searchModel: SearchModel; private currentAsyncSearch: Promise = Promise.resolve({ results: [], messages: [] }); + private storedOriginalLocation = false; + private readonly editorViewState = new EditorViewState( + this._editorService + ); private _getTextQueryBuilderOptions(charsPerLine: number): ITextQueryBuilderOptions { return { @@ -72,7 +81,7 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): IDisposable { + override provide(picker: IQuickPick, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): IDisposable { const disposables = new DisposableStore(); if (TEXT_SEARCH_QUICK_ACCESS_PREFIX.length < picker.value.length) { picker.valueSelection = [TEXT_SEARCH_QUICK_ACCESS_PREFIX.length, picker.value.length]; } picker.customButton = true; picker.customLabel = '$(link-external)'; - picker.onDidCustom(() => { + disposables.add(picker.onDidCustom(() => { if (this.searchModel.searchResult.count() > 0) { this.moveToSearchViewlet(undefined); } else { this._viewsService.openView(VIEW_ID, true); } picker.hide(); - }); + })); + disposables.add(picker.onDidChangeActive(() => { + const [item] = picker.activeItems; + + if (item?.match) { + // only store location once, or else it will store new state every time we change active pick + if (!this.storedOriginalLocation) { + // we must remember our curret view state to be able to restore + this.editorViewState.set(); + this.storedOriginalLocation = true; + } + // open it + this._editorService.openEditor({ + resource: item.match.parent().resource, + options: { preserveFocus: true, revealIfOpened: true, ignoreError: true, selection: item.match.range() } + }); + } + })); + + disposables.add(Event.once(picker.onDidHide)(({ reason }) => { + // Restore view state upon cancellation if we changed it + // but only when the picker was closed via explicit user + // gesture and not e.g. when focus was lost because that + // could mean the user clicked into the editor directly. + if (reason === QuickInputHideReason.Gesture) { + this.editorViewState.restore(); + } + this.searchModel.searchResult.toggleHighlights(false); + })); + disposables.add(super.provide(picker, token, runOptions)); - disposables.add(picker.onDidHide(() => this.searchModel.searchResult.toggleHighlights(false))); disposables.add(picker.onDidAccept(() => this.searchModel.searchResult.toggleHighlights(false))); return disposables; } @@ -177,11 +214,11 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider limit ? matches.slice(0, limit) : matches; - const picks: Array = []; + const picks: Array = []; for (let fileIndex = 0; fileIndex < matches.length; fileIndex++) { if (fileIndex === limit) { @@ -258,7 +295,8 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { this.moveToSearchViewlet(element); return TriggerAction.CLOSE_PICKER; - } + }, + match: element }); } } From fa989a17f1985a641e8338229e7d8ce6980a4d5c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 18 Jan 2024 11:48:54 -0800 Subject: [PATCH 127/333] Switch to non-deprecated overload for rename (#202755) --- build/lib/mangle/renameWorker.js | 6 ++++-- build/lib/mangle/renameWorker.ts | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/build/lib/mangle/renameWorker.js b/build/lib/mangle/renameWorker.js index 428a9626a1fd6..6cd429b8c9ae8 100644 --- a/build/lib/mangle/renameWorker.js +++ b/build/lib/mangle/renameWorker.js @@ -7,12 +7,14 @@ Object.defineProperty(exports, "__esModule", { value: true }); const ts = require("typescript"); const workerpool = require("workerpool"); const staticLanguageServiceHost_1 = require("./staticLanguageServiceHost"); -let service; // = ts.createLanguageService(new StaticLanguageServiceHost(projectPath)); +let service; function findRenameLocations(projectPath, fileName, position) { if (!service) { service = ts.createLanguageService(new staticLanguageServiceHost_1.StaticLanguageServiceHost(projectPath)); } - return service.findRenameLocations(fileName, position, false, false, true) ?? []; + return service.findRenameLocations(fileName, position, false, false, { + providePrefixAndSuffixTextForRename: true, + }) ?? []; } workerpool.worker({ findRenameLocations diff --git a/build/lib/mangle/renameWorker.ts b/build/lib/mangle/renameWorker.ts index b5d6bcd5bc931..29b34e8c51479 100644 --- a/build/lib/mangle/renameWorker.ts +++ b/build/lib/mangle/renameWorker.ts @@ -7,7 +7,7 @@ import * as ts from 'typescript'; import * as workerpool from 'workerpool'; import { StaticLanguageServiceHost } from './staticLanguageServiceHost'; -let service: ts.LanguageService | undefined;// = ts.createLanguageService(new StaticLanguageServiceHost(projectPath)); +let service: ts.LanguageService | undefined; function findRenameLocations( projectPath: string, @@ -18,7 +18,9 @@ function findRenameLocations( service = ts.createLanguageService(new StaticLanguageServiceHost(projectPath)); } - return service.findRenameLocations(fileName, position, false, false, true) ?? []; + return service.findRenameLocations(fileName, position, false, false, { + providePrefixAndSuffixTextForRename: true, + }) ?? []; } workerpool.worker({ From 63349889d932a10d03c25dd48fe9cd7a0d3fb301 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 18 Jan 2024 17:05:28 -0300 Subject: [PATCH 128/333] Avoid "slash command" name in agent API (#202729) * Avoid "slash command" name in agent API * Fix reference * Fix --- .../src/singlefolder-tests/chat.test.ts | 4 +- .../api/common/extHostChatAgents2.ts | 10 ++--- .../api/common/extHostTypeConverters.ts | 2 +- .../vscode.proposed.chatAgents2.d.ts | 38 +++++++++---------- .../vscode.proposed.chatAgents2Additions.d.ts | 2 +- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/chat.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/chat.test.ts index 1d1da2658c3bf..d88eea64c9625 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/chat.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/chat.test.ts @@ -35,8 +35,8 @@ suite('chat', () => { deferred.complete(request); return null; }); - agent.slashCommandProvider = { - provideSlashCommands: (_token) => { + agent.subCommandProvider = { + provideSubCommands: (_token) => { return [{ name: 'hello', description: 'Hello' }]; } }; diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 7832e02cbb17b..5a80b2edbf67c 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -240,8 +240,8 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { class ExtHostChatAgent { - private _slashCommandProvider: vscode.ChatAgentSlashCommandProvider | undefined; - private _lastSlashCommands: vscode.ChatAgentSlashCommand[] | undefined; + private _slashCommandProvider: vscode.ChatAgentSubCommandProvider | undefined; + private _lastSlashCommands: vscode.ChatAgentSubCommand[] | undefined; private _followupProvider: vscode.FollowupProvider | undefined; private _description: string | undefined; private _fullName: string | undefined; @@ -297,7 +297,7 @@ class ExtHostChatAgent { if (!this._slashCommandProvider) { return []; } - const result = await this._slashCommandProvider.provideSlashCommands(token); + const result = await this._slashCommandProvider.provideSubCommands(token); if (!result) { return []; } @@ -385,10 +385,10 @@ class ExtHostChatAgent { that._iconPath = v; updateMetadataSoon(); }, - get slashCommandProvider() { + get subCommandProvider() { return that._slashCommandProvider; }, - set slashCommandProvider(v) { + set subCommandProvider(v) { that._slashCommandProvider = v; updateMetadataSoon(); }, diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 2170b91a05399..1a07e6b23f21a 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2430,7 +2430,7 @@ export namespace ChatResponseProgress { } export namespace ChatAgentRequest { - export function to(request: IChatAgentRequest, slashCommand: vscode.ChatAgentSlashCommand | undefined): vscode.ChatAgentRequest { + export function to(request: IChatAgentRequest, slashCommand: vscode.ChatAgentSubCommand | undefined): vscode.ChatAgentRequest { return { prompt: request.message, variables: ChatVariable.objectTo(request.variables), diff --git a/src/vscode-dts/vscode.proposed.chatAgents2.d.ts b/src/vscode-dts/vscode.proposed.chatAgents2.d.ts index bcc565500c9d7..12166a4f50c6a 100644 --- a/src/vscode-dts/vscode.proposed.chatAgents2.d.ts +++ b/src/vscode-dts/vscode.proposed.chatAgents2.d.ts @@ -83,12 +83,12 @@ declare module 'vscode' { readonly kind: ChatAgentResultFeedbackKind; } - export interface ChatAgentSlashCommand { + export interface ChatAgentSubCommand { /** * A short name by which this command is referred to in the UI, e.g. `fix` or * `explain` for commands that fix an issue or explain code. * - * **Note**: The name should be unique among the slash commands provided by this agent. + * **Note**: The name should be unique among the subCommands provided by this agent. */ readonly name: string; @@ -98,39 +98,39 @@ declare module 'vscode' { readonly description: string; /** - * When the user clicks this slash command in `/help`, this text will be submitted to this slash command + * When the user clicks this subCommand in `/help`, this text will be submitted to this subCommand */ readonly sampleRequest?: string; /** * Whether executing the command puts the * chat into a persistent mode, where the - * slash command is prepended to the chat input. + * subCommand is prepended to the chat input. */ readonly shouldRepopulate?: boolean; /** * Placeholder text to render in the chat input - * when the slash command has been repopulated. + * when the subCommand has been repopulated. * Has no effect if `shouldRepopulate` is `false`. */ // TODO@API merge this with shouldRepopulate? so that invalid state cannot be represented? readonly followupPlaceholder?: string; } - export interface ChatAgentSlashCommandProvider { + export interface ChatAgentSubCommandProvider { /** - * Returns a list of slash commands that its agent is capable of handling. A slash command + * Returns a list of subCommands that its agent is capable of handling. A subCommand * can be selected by the user and will then be passed to the {@link ChatAgentHandler handler} - * via the {@link ChatAgentRequest.slashCommand slashCommand} property. + * via the {@link ChatAgentRequest.subCommand subCommand} property. * * * @param token A cancellation token. - * @returns A list of slash commands. The lack of a result can be signaled by returning `undefined`, `null`, or + * @returns A list of subCommands. The lack of a result can be signaled by returning `undefined`, `null`, or * an empty array. */ - provideSlashCommands(token: CancellationToken): ProviderResult; + provideSubCommands(token: CancellationToken): ProviderResult; } // TODO@API This should become a progress type, and use vscode.Command @@ -208,9 +208,9 @@ declare module 'vscode' { } | ThemeIcon; /** - * This provider will be called to retrieve the agent's slash commands. + * This provider will be called to retrieve the agent's subCommands. */ - slashCommandProvider?: ChatAgentSlashCommandProvider; + subCommandProvider?: ChatAgentSubCommandProvider; /** * This provider will be called once after each request to retrieve suggested followup questions. @@ -218,7 +218,7 @@ declare module 'vscode' { followupProvider?: FollowupProvider; /** - * When the user clicks this agent in `/help`, this text will be submitted to this slash command + * When the user clicks this agent in `/help`, this text will be submitted to this subCommand */ sampleRequest?: string; @@ -240,10 +240,10 @@ declare module 'vscode' { export interface ChatAgentRequest { /** - * The prompt entered by the user. The {@link ChatAgent2.name name} of the agent or the {@link ChatAgentSlashCommand.name slash command} + * The prompt entered by the user. The {@link ChatAgent2.name name} of the agent or the {@link ChatAgentSubCommand.name subCommand} * are not part of the prompt. * - * @see {@link ChatAgentRequest.slashCommand} + * @see {@link ChatAgentRequest.subCommand} */ prompt: string; @@ -253,14 +253,14 @@ declare module 'vscode' { agentId: string; /** - * The {@link ChatAgentSlashCommand slash command} that was selected for this request. It is guaranteed that the passed slash - * command is an instance that was previously returned from the {@link ChatAgentSlashCommandProvider.provideSlashCommands slash command provider}. + * The {@link ChatAgentSubCommand subCommand} that was selected for this request. It is guaranteed that the passed subCommand + * is an instance that was previously returned from the {@link ChatAgentSubCommandProvider.provideSubCommands subCommand provider}. * @deprecated this will be replaced by `subCommand` */ - slashCommand?: ChatAgentSlashCommand; + slashCommand?: ChatAgentSubCommand; /** - * The name of the {@link ChatAgentSlashCommand slash command} that was selected for this request. + * The name of the {@link ChatAgentSubCommand subCommand} that was selected for this request. */ subCommand?: string; diff --git a/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts b/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts index 2794d273dd864..0f9de5a05bd4e 100644 --- a/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts +++ b/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts @@ -26,7 +26,7 @@ declare module 'vscode' { export interface ChatAgentDetectedAgent { agentName: string; - command?: ChatAgentSlashCommand; + command?: ChatAgentSubCommand; } export interface ChatAgentVulnerability { From 216a8e66d3d3b42ebfc4fb02c36403d260f53b09 Mon Sep 17 00:00:00 2001 From: "Sixia \"Leask\" Huang" Date: Thu, 18 Jan 2024 15:05:56 -0500 Subject: [PATCH 129/333] fix: tweak check-requirements for calling ldconfig (#202645) * tweak check-requirements for calling ldconfig * apply patch --- resources/server/bin/helpers/check-requirements-linux.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/server/bin/helpers/check-requirements-linux.sh b/resources/server/bin/helpers/check-requirements-linux.sh index b366a190abf26..ac1840d61d8a5 100644 --- a/resources/server/bin/helpers/check-requirements-linux.sh +++ b/resources/server/bin/helpers/check-requirements-linux.sh @@ -32,7 +32,7 @@ elif [ -f /usr/lib/libstdc++.so.6 ]; then libstdcpp_path='/usr/lib/libstdc++.so.6' elif [ -f /sbin/ldconfig ]; then # Look up path - libstdcpp_paths=$(ldconfig -p | grep 'libstdc++.so.6') + libstdcpp_paths=$(/sbin/ldconfig -p | grep 'libstdc++.so.6') if [ "$(echo "$libstdcpp_paths" | wc -l)" -gt 1 ]; then libstdcpp_path=$(echo "$libstdcpp_paths" | grep "$LDCONFIG_ARCH" | awk '{print $NF}') @@ -66,7 +66,7 @@ if [ -n "$(ldd --version | grep -v musl)" ]; then libc_path='/usr/lib/libc.so.6' elif [ -f /sbin/ldconfig ]; then # Look up path - libc_paths=$(ldconfig -p | grep 'libc.so.6') + libc_paths=$(/sbin/ldconfig -p | grep 'libc.so.6') if [ "$(echo "$libc_paths" | wc -l)" -gt 1 ]; then libc_path=$(echo "$libc_paths" | grep "$LDCONFIG_ARCH" | awk '{print $NF}') From 9ed02ced9256e9ff0571a479e82c8fa00ed805a2 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 18 Jan 2024 21:39:40 +0100 Subject: [PATCH 130/333] =?UTF-8?q?SCM=20-=20=F0=9F=92=84=20polish=20incom?= =?UTF-8?q?ing/outgoing=20proposed=20API=20(#202766)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/git/src/historyProvider.ts | 112 +++++++++--------- src/vs/workbench/api/browser/mainThreadSCM.ts | 45 +------ .../workbench/api/common/extHost.protocol.ts | 8 +- src/vs/workbench/api/common/extHostSCM.ts | 7 +- .../contrib/scm/browser/scmViewPane.ts | 67 +++++++---- .../workbench/contrib/scm/common/history.ts | 26 +--- .../vscode.proposed.scmHistoryProvider.d.ts | 5 +- 7 files changed, 112 insertions(+), 158 deletions(-) diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index b5c735586ccec..9560aef5e8ea2 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -8,10 +8,18 @@ import { Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider import { Repository, Resource } from './repository'; import { IDisposable, filterEvent } from './util'; import { toGitUri } from './uri'; -import { Branch, RefType, Status } from './api/git'; +import { Branch, RefType, UpstreamRef } from './api/git'; import { emojify, ensureEmojis } from './emoji'; import { Operation } from './operation'; +function isBranchRefEqual(brach1: Branch | undefined, branch2: Branch | undefined): boolean { + return brach1?.name === branch2?.name && brach1?.commit === branch2?.commit; +} + +function isUpstreamRefEqual(upstream1: UpstreamRef | undefined, upstream2: UpstreamRef | undefined): boolean { + return upstream1?.name === upstream2?.name && upstream1?.remote === upstream2?.remote && upstream1?.commit === upstream2?.commit; +} + export class GitHistoryProvider implements SourceControlHistoryProvider, FileDecorationProvider, IDisposable { private readonly _onDidChangeCurrentHistoryItemGroup = new EventEmitter(); @@ -21,10 +29,15 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec readonly onDidChangeFileDecorations: Event = this._onDidChangeDecorations.event; private _HEAD: Branch | undefined; + private _HEADBase: UpstreamRef | undefined; private _currentHistoryItemGroup: SourceControlHistoryItemGroup | undefined; get currentHistoryItemGroup(): SourceControlHistoryItemGroup | undefined { return this._currentHistoryItemGroup; } set currentHistoryItemGroup(value: SourceControlHistoryItemGroup | undefined) { + if (this._currentHistoryItemGroup === undefined && value === undefined) { + return; + } + this._currentHistoryItemGroup = value; this._onDidChangeCurrentHistoryItemGroup.fire(); } @@ -41,30 +54,31 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec } private async onDidRunGitStatus(): Promise { - // Check if HEAD has changed - if (this._HEAD?.name === this.repository.HEAD?.name && - this._HEAD?.commit === this.repository.HEAD?.commit && - this._HEAD?.upstream?.name === this.repository.HEAD?.upstream?.name && - this._HEAD?.upstream?.remote === this.repository.HEAD?.upstream?.remote && - this._HEAD?.upstream?.commit === this.repository.HEAD?.upstream?.commit) { + // Check if HEAD does not support incoming/outgoing (detached commit, tag) + if (!this.repository.HEAD?.name || !this.repository.HEAD?.commit || this.repository.HEAD.type === RefType.Tag) { + this._HEAD = this._HEADBase = undefined; + this.currentHistoryItemGroup = undefined; return; } - this._HEAD = this.repository.HEAD; + // Resolve HEAD base + const HEADBase = await this.resolveHEADBase(this.repository.HEAD); - // Check if HEAD supports incoming/outgoing (not a tag, not detached) - if (!this._HEAD?.name || !this._HEAD?.commit || this._HEAD.type === RefType.Tag) { - this.currentHistoryItemGroup = undefined; + // Check if HEAD or HEADBase has changed + if (isBranchRefEqual(this._HEAD, this.repository.HEAD) && isUpstreamRefEqual(this._HEADBase, HEADBase)) { return; } + this._HEAD = this.repository.HEAD; + this._HEADBase = HEADBase; + this.currentHistoryItemGroup = { - id: `refs/heads/${this._HEAD.name}`, - label: this._HEAD.name, - upstream: this._HEAD.upstream ? + id: `refs/heads/${this._HEAD.name ?? ''}`, + label: this._HEAD.name ?? '', + base: this._HEADBase ? { - id: `refs/remotes/${this._HEAD.upstream.remote}/${this._HEAD.upstream.name}`, - label: `${this._HEAD.upstream.remote}/${this._HEAD.upstream.name}`, + id: `refs/remotes/${this._HEADBase.remote}/${this._HEADBase.name}`, + label: `${this._HEADBase.remote}/${this._HEADBase.name}`, } : undefined }; } @@ -138,7 +152,10 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec }); // History item change decoration - const fileDecoration = this.getHistoryItemChangeFileDecoration(change.status); + const letter = Resource.getStatusLetter(change.status); + const tooltip = Resource.getStatusText(change.status); + const color = Resource.getStatusColor(change.status); + const fileDecoration = new FileDecoration(letter, tooltip, color); this.historyItemDecorations.set(historyItemUri.toString(), fileDecoration); historyItemChangesUri.push(historyItemUri); @@ -148,40 +165,6 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec return historyItemChanges; } - async resolveHistoryItemGroupBase(historyItemGroupId: string): Promise { - // TODO - support for all history item groups - if (historyItemGroupId !== this.currentHistoryItemGroup?.id) { - return undefined; - } - - if (this.currentHistoryItemGroup?.upstream) { - return this.currentHistoryItemGroup.upstream; - } - - // Branch base - try { - const branchBase = await this.repository.getBranchBase(historyItemGroupId); - - if (branchBase?.name && branchBase?.type === RefType.Head) { - return { - id: `refs/heads/${branchBase.name}`, - label: branchBase.name - }; - } - if (branchBase?.name && branchBase.remote && branchBase?.type === RefType.RemoteHead) { - return { - id: `refs/remotes/${branchBase.remote}/${branchBase.name}`, - label: `${branchBase.remote}/${branchBase.name}` - }; - } - } - catch (err) { - this.logger.error(`Failed to get branch base for '${historyItemGroupId}': ${err.message}`); - } - - return undefined; - } - async resolveHistoryItemGroupCommonAncestor(refId1: string, refId2: string): Promise<{ id: string; ahead: number; behind: number } | undefined> { const ancestor = await this.repository.getMergeBase(refId1, refId2); if (!ancestor) { @@ -202,12 +185,29 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec return this.historyItemDecorations.get(uri.toString()); } - private getHistoryItemChangeFileDecoration(status: Status): FileDecoration { - const letter = Resource.getStatusLetter(status); - const tooltip = Resource.getStatusText(status); - const color = Resource.getStatusColor(status); + private async resolveHEADBase(HEAD: Branch): Promise { + // Upstream + if (HEAD.upstream) { + return HEAD.upstream; + } - return new FileDecoration(letter, tooltip, color); + try { + const remoteBranch = await this.repository.getBranchBase(HEAD.name ?? ''); + if (!remoteBranch?.remote || !remoteBranch?.name || !remoteBranch?.commit || remoteBranch?.type !== RefType.RemoteHead) { + return undefined; + } + + return { + name: remoteBranch.name, + remote: remoteBranch.remote, + commit: remoteBranch.commit + }; + } + catch (err) { + this.logger.error(`Failed to get branch base for '${HEAD.name}': ${err.message}`); + } + + return undefined; } dispose(): void { diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 81a43a35387e3..2c82aa6e2ada1 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -15,10 +15,9 @@ import { MarshalledId } from 'vs/base/common/marshallingIds'; import { ThemeIcon } from 'vs/base/common/themables'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IQuickDiffService, QuickDiffProvider } from 'vs/workbench/contrib/scm/common/quickDiff'; -import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryItemGroup, ISCMHistoryItemGroupDetails, ISCMHistoryItemGroupEntry, ISCMHistoryOptions, ISCMHistoryProvider } from 'vs/workbench/contrib/scm/common/history'; +import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryItemGroup, ISCMHistoryOptions, ISCMHistoryProvider } from 'vs/workbench/contrib/scm/common/history'; import { ResourceTree } from 'vs/base/common/resourceTree'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { Codicon } from 'vs/base/common/codicons'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { basename } from 'vs/base/common/resources'; @@ -141,48 +140,6 @@ class MainThreadSCMHistoryProvider implements ISCMHistoryProvider { constructor(private readonly proxy: ExtHostSCMShape, private readonly handle: number) { } - async resolveHistoryItemGroupDetails(historyItemGroup: ISCMHistoryItemGroup): Promise { - // History item group base - const historyItemGroupBase = await this.resolveHistoryItemGroupBase(historyItemGroup.id); - - if (!historyItemGroupBase) { - return undefined; - } - - // Common ancestor, ahead, behind - const ancestor = await this.resolveHistoryItemGroupCommonAncestor(historyItemGroup.id, historyItemGroupBase.id); - - if (!ancestor) { - return undefined; - } - - // Incoming - const incoming: ISCMHistoryItemGroupEntry = { - id: historyItemGroupBase.id, - label: historyItemGroupBase.label, - icon: Codicon.arrowCircleDown, - direction: 'incoming', - ancestor: ancestor.id, - count: ancestor.behind, - }; - - // Outgoing - const outgoing: ISCMHistoryItemGroupEntry = { - id: historyItemGroup.id, - label: historyItemGroup.label, - icon: Codicon.arrowCircleUp, - direction: 'outgoing', - ancestor: ancestor.id, - count: ancestor.ahead, - }; - - return { incoming, outgoing }; - } - - async resolveHistoryItemGroupBase(historyItemGroupId: string): Promise { - return this.proxy.$resolveHistoryItemGroupBase(this.handle, historyItemGroupId, CancellationToken.None); - } - async resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string): Promise<{ id: string; ahead: number; behind: number } | undefined> { return this.proxy.$resolveHistoryItemGroupCommonAncestor(this.handle, historyItemGroupId1, historyItemGroupId2, CancellationToken.None); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 978e0179e80e1..f34388f48758d 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1477,12 +1477,7 @@ export type SCMRawResourceSplices = [ export interface SCMHistoryItemGroupDto { readonly id: string; readonly label: string; - readonly upstream?: SCMRemoteHistoryItemGroupDto; -} - -export interface SCMRemoteHistoryItemGroupDto { - readonly id: string; - readonly label: string; + readonly base?: Omit; } export interface SCMHistoryItemDto { @@ -2241,7 +2236,6 @@ export interface ExtHostSCMShape { $provideHistoryItems(sourceControlHandle: number, historyItemGroupId: string, options: any, token: CancellationToken): Promise; $provideHistoryItemSummary(sourceControlHandle: number, historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): Promise; $provideHistoryItemChanges(sourceControlHandle: number, historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): Promise; - $resolveHistoryItemGroupBase(sourceControlHandle: number, historyItemGroupId: string, token: CancellationToken): Promise; $resolveHistoryItemGroupCommonAncestor(sourceControlHandle: number, historyItemGroupId1: string, historyItemGroupId2: string, token: CancellationToken): Promise<{ id: string; ahead: number; behind: number } | undefined>; } diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 3e40a0917aed5..da1e67ab60440 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -11,7 +11,7 @@ import { debounce } from 'vs/base/common/decorators'; import { DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { asPromise } from 'vs/base/common/async'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; -import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto, MainThreadTelemetryShape, SCMGroupFeatures, SCMHistoryItemDto, SCMHistoryItemChangeDto, SCMHistoryItemGroupDto } from './extHost.protocol'; +import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto, MainThreadTelemetryShape, SCMGroupFeatures, SCMHistoryItemDto, SCMHistoryItemChangeDto } from './extHost.protocol'; import { sortedDiff, equals } from 'vs/base/common/arrays'; import { comparePaths } from 'vs/base/common/comparers'; import type * as vscode from 'vscode'; @@ -960,11 +960,6 @@ export class ExtHostSCM implements ExtHostSCMShape { return Promise.resolve(undefined); } - async $resolveHistoryItemGroupBase(sourceControlHandle: number, historyItemGroupId: string, token: CancellationToken): Promise { - const historyProvider = this._sourceControls.get(sourceControlHandle)?.historyProvider; - return await historyProvider?.resolveHistoryItemGroupBase(historyItemGroupId, token) ?? undefined; - } - async $resolveHistoryItemGroupCommonAncestor(sourceControlHandle: number, historyItemGroupId1: string, historyItemGroupId2: string, token: CancellationToken): Promise<{ id: string; ahead: number; behind: number } | undefined> { const historyProvider = this._sourceControls.get(sourceControlHandle)?.historyProvider; return await historyProvider?.resolveHistoryItemGroupCommonAncestor(historyItemGroupId1, historyItemGroupId2, token) ?? undefined; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 1f4a37bcc0702..5f53e86c9f40c 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -3329,44 +3329,66 @@ class SCMTreeDataSource implements IAsyncDataSource 0))) { - children.push({ - ...historyItemGroupDetails.incoming, - ariaLabel: localize('incomingChangesAriaLabel', "Incoming changes from {0}", historyItemGroupDetails.incoming.label), - repository: element, - type: 'historyItemGroup' - }); + (showIncomingChanges === 'auto' && (incomingHistoryItemGroup.count ?? 0) > 0))) { + children.push(incomingHistoryItemGroup); } // Outgoing - if (historyItemGroupDetails?.outgoing && + if (outgoingHistoryItemGroup && (showOutgoingChanges === 'always' || - (showOutgoingChanges === 'auto' && (historyItemGroupDetails.outgoing.count ?? 0) > 0))) { - children.push({ - ...historyItemGroupDetails.outgoing, - ariaLabel: localize('outgoingChangesAriaLabel', "Outgoing changes from {0}", historyItemGroupDetails.outgoing.label), - repository: element, - type: 'historyItemGroup' - }); + (showOutgoingChanges === 'auto' && (outgoingHistoryItemGroup.count ?? 0) > 0))) { + children.push(outgoingHistoryItemGroup); } return children; @@ -3540,7 +3562,8 @@ class SCMTreeDataSource implements IAsyncDataSource(), historyItemChanges: new Map() }; diff --git a/src/vs/workbench/contrib/scm/common/history.ts b/src/vs/workbench/contrib/scm/common/history.ts index 385019d9ede77..b9ac2eed9944b 100644 --- a/src/vs/workbench/contrib/scm/common/history.ts +++ b/src/vs/workbench/contrib/scm/common/history.ts @@ -23,13 +23,12 @@ export interface ISCMHistoryProvider { provideHistoryItems(historyItemGroupId: string, options: ISCMHistoryOptions): Promise; provideHistoryItemSummary(historyItemId: string, historyItemParentId: string | undefined): Promise; provideHistoryItemChanges(historyItemId: string, historyItemParentId: string | undefined): Promise; - resolveHistoryItemGroupBase(historyItemGroupId: string): Promise; resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string): Promise<{ id: string; ahead: number; behind: number } | undefined>; - resolveHistoryItemGroupDetails(historyItemGroup: ISCMHistoryItemGroup): Promise; } export interface ISCMHistoryProviderCacheEntry { - readonly historyItemGroupDetails?: ISCMHistoryItemGroupDetails; + readonly incomingHistoryItemGroup: SCMHistoryItemGroupTreeElement | undefined; + readonly outgoingHistoryItemGroup: SCMHistoryItemGroupTreeElement | undefined; readonly historyItems: Map; readonly historyItemChanges: Map; } @@ -39,34 +38,21 @@ export interface ISCMHistoryOptions { readonly limit?: number | { id?: string }; } -export interface ISCMRemoteHistoryItemGroup { - readonly id: string; - readonly label: string; -} - export interface ISCMHistoryItemGroup { readonly id: string; readonly label: string; - readonly upstream?: ISCMRemoteHistoryItemGroup; -} - -export interface ISCMHistoryItemGroupDetails { - readonly incoming?: ISCMHistoryItemGroupEntry; - readonly outgoing: ISCMHistoryItemGroupEntry; + readonly base?: Omit; } -export interface ISCMHistoryItemGroupEntry { +export interface SCMHistoryItemGroupTreeElement { readonly id: string; readonly label: string; - readonly direction: 'incoming' | 'outgoing'; + readonly ariaLabel?: string; readonly icon?: URI | { light: URI; dark: URI } | ThemeIcon; readonly description?: string; + readonly direction: 'incoming' | 'outgoing'; readonly ancestor?: string; readonly count?: number; -} - -export interface SCMHistoryItemGroupTreeElement extends ISCMHistoryItemGroupEntry { - readonly ariaLabel?: string; readonly repository: ISCMRepository; readonly type: 'historyItemGroup'; } diff --git a/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts b/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts index b030e44ee98da..24f4dcd8453a6 100644 --- a/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts @@ -28,8 +28,7 @@ declare module 'vscode' { provideHistoryItemSummary?(historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): ProviderResult; provideHistoryItemChanges(historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): ProviderResult; - resolveHistoryItemGroupBase(historyItemGroupId: string, token: CancellationToken): ProviderResult; - resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId: string, token: CancellationToken): ProviderResult<{ id: string; ahead: number; behind: number }>; + resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string, token: CancellationToken): ProviderResult<{ id: string; ahead: number; behind: number }>; } export interface SourceControlHistoryOptions { @@ -40,7 +39,7 @@ declare module 'vscode' { export interface SourceControlHistoryItemGroup { readonly id: string; readonly label: string; - readonly upstream?: SourceControlRemoteHistoryItemGroup; + readonly base?: Omit; } export interface SourceControlRemoteHistoryItemGroup { From 8d63804f0874686aa6c797b5d49e5f6b35264f2a Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Thu, 18 Jan 2024 13:25:31 -0800 Subject: [PATCH 131/333] get long collections in chunks (#202758) * stateless Datasource, improve initialization * retreive long collections in chunks --- .../notebookVariables/notebookVariables.ts | 21 +++--- .../notebookVariablesDataSource.ts | 72 +++++++++++++------ .../notebookVariablesView.ts | 26 ++++--- 3 files changed, 80 insertions(+), 39 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts index 03c9c7b819e9f..524f868562689 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts @@ -16,25 +16,30 @@ import { NOTEBOOK_KERNEL } from 'vs/workbench/contrib/notebook/common/notebookCo import { variablesViewIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; export class NotebookVariables extends Disposable implements IWorkbenchContribution { - private listener: IDisposable | undefined; + private listeners: IDisposable[] = []; constructor( @IEditorService private readonly editorService: IEditorService, @IConfigurationService configurationService: IConfigurationService, + @INotebookExecutionStateService private readonly notebookExecutionStateService: INotebookExecutionStateService ) { super(); - this.listener = this.editorService.onDidEditorsChange(() => { - if (configurationService.getValue('notebook.experimental.notebookVariablesView') - && this.editorService.activeEditorPane?.getId() === 'workbench.editor.notebook') { - if (this.initializeView()) { - this.listener?.dispose(); - } + this.listeners.push(this.editorService.onDidEditorsChange(() => this.handleInitEvent(configurationService))); + this.listeners.push(this.notebookExecutionStateService.onDidChangeExecution(() => this.handleInitEvent(configurationService))); + } + + private handleInitEvent(configurationService: IConfigurationService) { + if (configurationService.getValue('notebook.experimental.notebookVariablesView') + && this.editorService.activeEditorPane?.getId() === 'workbench.editor.notebook') { + if (this.initializeView()) { + this.listeners.forEach(listener => listener.dispose()); } - }); + } } private initializeView() { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts index bc64f09d6e770..70511741f6598 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts @@ -6,11 +6,11 @@ import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { CancellationToken } from 'vs/base/common/cancellation'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { INotebookKernelService, VariablesResult } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService, VariablesResult } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; export interface INotebookScope { type: 'root'; - readonly notebook: NotebookTextModel | undefined; + readonly notebook: NotebookTextModel; } export interface INotebookVariableElement { @@ -19,13 +19,13 @@ export interface INotebookVariableElement { readonly name: string; readonly value: string; readonly indexedChildrenCount: number; + readonly indexStart?: number; readonly hasNamedChildren: boolean; + readonly notebook: NotebookTextModel; } export class NotebookVariableDataSource implements IAsyncDataSource { - private notebook: NotebookTextModel | undefined = undefined; - constructor(private readonly notebookKernelService: INotebookKernelService) { } hasChildren(element: INotebookScope | INotebookVariableElement): boolean { @@ -34,33 +34,26 @@ export class NotebookVariableDataSource implements IAsyncDataSource> { if (element.type === 'root') { - this.notebook = element.notebook; - return this.getRootVariables(); + return this.getRootVariables(element.notebook); } else { return this.getVariables(element); } } async getVariables(parent: INotebookVariableElement): Promise { - if (!this.notebook) { - return []; - } - const selectedKernel = this.notebookKernelService.getMatchingKernel(this.notebook).selected; + const selectedKernel = this.notebookKernelService.getMatchingKernel(parent.notebook).selected; if (selectedKernel && selectedKernel.hasVariableProvider) { let children: INotebookVariableElement[] = []; if (parent.hasNamedChildren) { - const variables = selectedKernel.provideVariables(this.notebook.uri, parent.id, 'named', 0, CancellationToken.None); + const variables = selectedKernel.provideVariables(parent.notebook.uri, parent.id, 'named', 0, CancellationToken.None); const childNodes = await variables - .map(variable => { return this.createVariableElement(variable); }) + .map(variable => { return this.createVariableElement(variable, parent.notebook); }) .toPromise(); children = children.concat(childNodes); } if (parent.indexedChildrenCount > 0) { - const variables = selectedKernel.provideVariables(this.notebook.uri, parent.id, 'indexed', 0, CancellationToken.None); - const childNodes = await variables - .map(variable => { return this.createVariableElement(variable); }) - .toPromise(); + const childNodes = await this.getIndexedChildren(parent, selectedKernel); children = children.concat(childNodes); } @@ -69,25 +62,58 @@ export class NotebookVariableDataSource implements IAsyncDataSource { - if (!this.notebook) { - return []; + async getIndexedChildren(parent: INotebookVariableElement, kernel: INotebookKernel) { + const childNodes: INotebookVariableElement[] = []; + + if (parent.indexedChildrenCount > 100) { + for (let start = 0; start < parent.indexedChildrenCount; start += 100) { + let end = start + 100; + if (end > parent.indexedChildrenCount) { + end = parent.indexedChildrenCount; + } + + childNodes.push({ + type: 'variable', + notebook: parent.notebook, + id: parent.id, + name: `[${start}..${end - 1}]`, + value: '', + indexedChildrenCount: end - start, + indexStart: start, + hasNamedChildren: false + }); + } + } + else if (parent.indexedChildrenCount > 0) { + const variables = kernel.provideVariables(parent.notebook.uri, parent.id, 'indexed', parent.indexStart ?? 0, CancellationToken.None); + + for await (const variable of variables) { + childNodes.push(this.createVariableElement(variable, parent.notebook)); + if (childNodes.length >= 100) { + break; + } + } + } + return childNodes; + } - const selectedKernel = this.notebookKernelService.getMatchingKernel(this.notebook).selected; + async getRootVariables(notebook: NotebookTextModel): Promise { + const selectedKernel = this.notebookKernelService.getMatchingKernel(notebook).selected; if (selectedKernel && selectedKernel.hasVariableProvider) { - const variables = selectedKernel.provideVariables(this.notebook.uri, undefined, 'named', 0, CancellationToken.None); + const variables = selectedKernel.provideVariables(notebook.uri, undefined, 'named', 0, CancellationToken.None); return await variables - .map(variable => { return this.createVariableElement(variable); }) + .map(variable => { return this.createVariableElement(variable, notebook); }) .toPromise(); } return []; } - private createVariableElement(variable: VariablesResult): INotebookVariableElement { + private createVariableElement(variable: VariablesResult, notebook: NotebookTextModel): INotebookVariableElement { return { type: 'variable', + notebook, ...variable }; } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index c2b00fd9f172a..698f39e668c68 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -55,8 +55,10 @@ export class NotebookVariablesView extends ViewPane { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); this._register(this.editorService.onDidActiveEditorChange(this.handleActiveEditorChange.bind(this))); - this._register(this.notebookExecutionStateService.onDidChangeExecution(this.handleExecutionStateChange.bind(this))); this._register(this.notebookKernelService.onDidNotebookVariablesUpdate(this.handleVariablesChanged.bind(this))); + this._register(this.notebookExecutionStateService.onDidChangeExecution(this.handleExecutionStateChange.bind(this))); + + this.setActiveNotebook(); } protected override renderBody(container: HTMLElement): void { @@ -75,7 +77,9 @@ export class NotebookVariablesView extends ViewPane { }); this.tree.layout(); - this.tree.setInput({ type: 'root', notebook: this.activeNotebook }); + if (this.activeNotebook) { + this.tree.setInput({ type: 'root', notebook: this.activeNotebook }); + } } protected override layoutBody(height: number, width: number): void { @@ -83,15 +87,21 @@ export class NotebookVariablesView extends ViewPane { this.tree?.layout(height, width); } - private handleActiveEditorChange() { + setActiveNotebook() { + const current = this.activeNotebook; const activeEditorPane = this.editorService.activeEditorPane; if (activeEditorPane && activeEditorPane.getId() === 'workbench.editor.notebook') { const notebookDocument = getNotebookEditorFromEditorPane(activeEditorPane)?.getViewModel()?.notebookDocument; - if (notebookDocument && notebookDocument !== this.activeNotebook) { - this.activeNotebook = notebookDocument; - this.tree?.setInput({ type: 'root', notebook: this.activeNotebook }); - this.tree?.updateChildren(); - } + this.activeNotebook = notebookDocument; + } + + return current !== this.activeNotebook; + } + + private handleActiveEditorChange() { + if (this.setActiveNotebook() && this.activeNotebook) { + this.tree?.setInput({ type: 'root', notebook: this.activeNotebook }); + this.tree?.updateChildren(); } } From 1db9db32a49a2dc76fe6ebe334ea03a76273f707 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 18 Jan 2024 22:25:44 +0100 Subject: [PATCH 132/333] SCM - add incoming/outgoing menu proposal (#202772) --- src/vs/platform/actions/common/actions.ts | 2 + .../contrib/scm/browser/media/scm.css | 5 ++ src/vs/workbench/contrib/scm/browser/menus.ts | 14 +++- .../contrib/scm/browser/scmViewPane.ts | 65 +++++++++++++++++-- .../workbench/contrib/scm/common/history.ts | 3 + .../actions/common/menusExtensionPoint.ts | 12 ++++ .../common/extensionsApiProposals.ts | 1 + ...tribSourceControlHistoryItemGroupMenu.d.ts | 8 +++ 8 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemGroupMenu.d.ts diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 5af2f709c137d..0e39f772a7804 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -107,6 +107,8 @@ export class MenuId { static readonly OpenEditorsContextShare = new MenuId('OpenEditorsContextShare'); static readonly ProblemsPanelContext = new MenuId('ProblemsPanelContext'); static readonly SCMInputBox = new MenuId('SCMInputBox'); + static readonly SCMIncomingChanges = new MenuId('SCMIncomingChanges'); + static readonly SCMOutgoingChanges = new MenuId('SCMOutgoingChanges'); static readonly SCMIncomingChangesAllChangesContext = new MenuId('SCMIncomingChangesAllChangesContext'); static readonly SCMIncomingChangesHistoryItemContext = new MenuId('SCMIncomingChangesHistoryItemContext'); static readonly SCMOutgoingChangesAllChangesContext = new MenuId('SCMOutgoingChangesAllChangesContext'); diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index 335b67c2cad7a..13eb48d7df4fc 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -255,6 +255,7 @@ .scm-view .monaco-list .monaco-list-row .resource-group > .actions, .scm-view .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions, +.scm-view .monaco-list .monaco-list-row .history-item-group > .actions, .scm-view .monaco-list .monaco-list-row .history-item > .actions { display: none; max-width: fit-content; @@ -267,6 +268,9 @@ .scm-view .monaco-list .monaco-list-row.selected .resource > .name > .monaco-icon-label > .actions, .scm-view .monaco-list .monaco-list-row.focused .resource > .name > .monaco-icon-label > .actions, .scm-view .monaco-list:not(.selection-multiple) .monaco-list-row .resource:hover > .actions, +.scm-view .monaco-list .monaco-list-row:hover .history-item-group > .actions, +.scm-view .monaco-list .monaco-list-row.selected .history-item-group > .actions, +.scm-view .monaco-list .monaco-list-row.focused .history-item-group > .actions, .scm-view .monaco-list .monaco-list-row:hover .history-item > .actions, .scm-view .monaco-list .monaco-list-row.selected .history-item > .actions, .scm-view .monaco-list .monaco-list-row.focused .history-item > .actions { @@ -288,6 +292,7 @@ .scm-view.show-actions > .monaco-list .monaco-list-row .scm-input > .scm-editor > .actions, .scm-view.show-actions > .monaco-list .monaco-list-row .resource-group > .actions, .scm-view.show-actions > .monaco-list .monaco-list-row .resource > .name > .monaco-icon-label > .actions, +.scm-view.show-actions > .monaco-list .monaco-list-row .history-item-group > .actions, .scm-view.show-actions > .monaco-list .monaco-list-row .history-item > .actions { display: block; } diff --git a/src/vs/workbench/contrib/scm/browser/menus.ts b/src/vs/workbench/contrib/scm/browser/menus.ts index 1cf5d9b417418..968788eb6f53d 100644 --- a/src/vs/workbench/contrib/scm/browser/menus.ts +++ b/src/vs/workbench/contrib/scm/browser/menus.ts @@ -258,9 +258,21 @@ export class SCMHistoryProviderMenus implements ISCMHistoryProviderMenus, IDispo private readonly historyItemMenus = new Map(); private readonly disposables = new DisposableStore(); + private _incomingHistoryItemGroupMenu: IMenu; + get incomingHistoryItemGroupMenu(): IMenu { return this._incomingHistoryItemGroupMenu; } + + private _outgoingHistoryItemGroupMenu: IMenu; + get outgoingHistoryItemGroupMenu(): IMenu { return this._outgoingHistoryItemGroupMenu; } + constructor( @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IMenuService private readonly menuService: IMenuService) { } + @IMenuService private readonly menuService: IMenuService) { + this._incomingHistoryItemGroupMenu = this.menuService.createMenu(MenuId.SCMIncomingChanges, this.contextKeyService); + this.disposables.add(this._incomingHistoryItemGroupMenu); + + this._outgoingHistoryItemGroupMenu = this.menuService.createMenu(MenuId.SCMOutgoingChanges, this.contextKeyService); + this.disposables.add(this._outgoingHistoryItemGroupMenu); + } getHistoryItemMenu(historyItem: SCMHistoryItemTreeElement): IMenu { return this.getOrCreateHistoryItemMenu(historyItem); diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 5f53e86c9f40c..7938ec4815663 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -24,7 +24,7 @@ import { MenuItemAction, IMenuService, registerAction2, MenuId, IAction2Options, import { IAction, ActionRunner, Action, Separator, IActionRunner } from 'vs/base/common/actions'; import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { IThemeService, IFileIconTheme } from 'vs/platform/theme/common/themeService'; -import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, collectContextMenuActions, getActionViewItemProvider, isSCMActionButton, isSCMViewService, isSCMHistoryItemGroupTreeElement, isSCMHistoryItemTreeElement, isSCMHistoryItemChangeTreeElement, toDiffEditorArguments, isSCMResourceNode, isSCMHistoryItemChangeNode, isSCMViewSeparator } from './util'; +import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, collectContextMenuActions, getActionViewItemProvider, isSCMActionButton, isSCMViewService, isSCMHistoryItemGroupTreeElement, isSCMHistoryItemTreeElement, isSCMHistoryItemChangeTreeElement, toDiffEditorArguments, isSCMResourceNode, isSCMHistoryItemChangeNode, isSCMViewSeparator, connectPrimaryMenu } from './util'; import { WorkbenchCompressibleAsyncDataTree, IOpenEvent } from 'vs/platform/list/browser/listService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { disposableTimeout, Sequencer, ThrottledDelayer, Throttler } from 'vs/base/common/async'; @@ -761,11 +761,25 @@ class ResourceRenderer implements ICompressibleTreeRenderer { + if (!(action instanceof MenuItemAction)) { + return super.runAction(action, context); + } + + return action.run(context.repository.provider, context.id); + } +} + interface HistoryItemGroupTemplate { readonly iconContainer: HTMLElement; readonly label: IconLabel; + readonly toolBar: WorkbenchToolBar; readonly count: CountBadge; - readonly disposables: IDisposable; + readonly elementDisposables: DisposableStore; + readonly templateDisposables: DisposableStore; } class HistoryItemGroupRenderer implements ICompressibleTreeRenderer { @@ -773,6 +787,16 @@ class HistoryItemGroupRenderer implements ICompressibleTreeRenderer, index: number, templateData: HistoryItemGroupTemplate, height: number | undefined): void { @@ -798,13 +826,36 @@ class HistoryItemGroupRenderer implements ICompressibleTreeRenderer { + templateData.toolBar.setActions(primary, secondary, [menuId]); + })); + + templateData.toolBar.context = historyItemGroup; + } else { + templateData.toolBar.setActions([], []); + templateData.toolBar.context = undefined; + } } renderCompressedElements(node: ITreeNode, void>, index: number, templateData: HistoryItemGroupTemplate, height: number | undefined): void { throw new Error('Should never happen since node is incompressible'); } + + disposeElement(node: ITreeNode, index: number, templateData: HistoryItemGroupTemplate, height: number | undefined): void { + templateData.elementDisposables.clear(); + } + disposeTemplate(templateData: HistoryItemGroupTemplate): void { - templateData.disposables.dispose(); + templateData.elementDisposables.dispose(); + templateData.templateDisposables.dispose(); } } @@ -2709,6 +2760,10 @@ export class SCMViewPane extends ViewPane { resourceActionRunner.onWillRun(() => this.tree.domFocus(), this, this.disposables); this.disposables.add(resourceActionRunner); + const historyItemGroupActionRunner = new HistoryItemGroupActionRunner(); + historyItemGroupActionRunner.onWillRun(() => this.tree.domFocus(), this, this.disposables); + this.disposables.add(historyItemGroupActionRunner); + const historyItemActionRunner = new HistoryItemActionRunner(); historyItemActionRunner.onWillRun(() => this.tree.domFocus(), this, this.disposables); this.disposables.add(historyItemActionRunner); @@ -2728,7 +2783,7 @@ export class SCMViewPane extends ViewPane { this.instantiationService.createInstance(RepositoryRenderer, MenuId.SCMTitle, getActionViewItemProvider(this.instantiationService)), this.instantiationService.createInstance(ResourceGroupRenderer, getActionViewItemProvider(this.instantiationService)), this.instantiationService.createInstance(ResourceRenderer, () => this.viewMode, this.listLabels, getActionViewItemProvider(this.instantiationService), resourceActionRunner), - this.instantiationService.createInstance(HistoryItemGroupRenderer), + this.instantiationService.createInstance(HistoryItemGroupRenderer, historyItemGroupActionRunner), this.instantiationService.createInstance(HistoryItemRenderer, historyItemActionRunner, getActionViewItemProvider(this.instantiationService)), this.instantiationService.createInstance(HistoryItemChangeRenderer, () => this.viewMode, this.listLabels), this.instantiationService.createInstance(SeparatorRenderer) diff --git a/src/vs/workbench/contrib/scm/common/history.ts b/src/vs/workbench/contrib/scm/common/history.ts index b9ac2eed9944b..5037cc03e3ffa 100644 --- a/src/vs/workbench/contrib/scm/common/history.ts +++ b/src/vs/workbench/contrib/scm/common/history.ts @@ -10,6 +10,9 @@ import { IMenu } from 'vs/platform/actions/common/actions'; import { ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; export interface ISCMHistoryProviderMenus { + readonly incomingHistoryItemGroupMenu: IMenu; + readonly outgoingHistoryItemGroupMenu: IMenu; + getHistoryItemMenu(historyItem: SCMHistoryItemTreeElement): IMenu; } diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index df25f3c2008e5..ead9769777214 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -150,6 +150,18 @@ const apiMenus: IAPIMenu[] = [ description: localize('menus.input', "The Source Control input box menu"), proposed: 'contribSourceControlInputBoxMenu' }, + { + key: 'scm/incomingChanges', + id: MenuId.SCMIncomingChanges, + description: localize('menus.incomingChanges', "The Source Control incoming changes menu"), + proposed: 'contribSourceControlHistoryItemGroupMenu' + }, + { + key: 'scm/outgoingChanges', + id: MenuId.SCMOutgoingChanges, + description: localize('menus.outgoingChanges', "The Source Control outgoing changes menu"), + proposed: 'contribSourceControlHistoryItemGroupMenu' + }, { key: 'scm/incomingChanges/allChanges/context', id: MenuId.SCMIncomingChangesAllChangesContext, diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 225fd88658980..ce32f5bf3099f 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -32,6 +32,7 @@ export const allApiProposals = Object.freeze({ contribNotebookStaticPreloads: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribNotebookStaticPreloads.d.ts', contribRemoteHelp: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribRemoteHelp.d.ts', contribShareMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribShareMenu.d.ts', + contribSourceControlHistoryItemGroupMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemGroupMenu.d.ts', contribSourceControlHistoryItemMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemMenu.d.ts', contribSourceControlInputBoxMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlInputBoxMenu.d.ts', contribStatusBarItems: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribStatusBarItems.d.ts', diff --git a/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemGroupMenu.d.ts b/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemGroupMenu.d.ts new file mode 100644 index 0000000000000..a67c208736e55 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemGroupMenu.d.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for the `scm/incomingChanges`-menu contribution point +// empty placeholder declaration for the `scm/outgoingChanges`-menu contribution point +// https://github.com/microsoft/vscode/issues/201997 From 834e74297f8d607f2104be32ae79316862834aca Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:40:02 -0800 Subject: [PATCH 133/333] allow contributed range of higlight for code actions (#202197) * groundwork for segttings * laying groundowrk for new API * more groundwork, api fixed * cleanup * more cleanup * added updates * fixed spacing --- src/vs/editor/common/languages.ts | 1 + .../codeAction/browser/codeActionController.ts | 7 ++++++- src/vs/monaco.d.ts | 1 + src/vs/workbench/api/common/extHost.protocol.ts | 1 + .../api/common/extHostLanguageFeatures.ts | 4 ++++ .../extensions/common/extensionsApiProposals.ts | 1 + .../vscode.proposed.codeActionRanges.d.ts | 17 +++++++++++++++++ 7 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/vscode-dts/vscode.proposed.codeActionRanges.d.ts diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 9b3613efc6689..43d17fb060973 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -749,6 +749,7 @@ export interface CodeAction { isPreferred?: boolean; isAI?: boolean; disabled?: string; + ranges?: IRange[]; } export const enum CodeActionTriggerType { diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts index 81b765253ab3a..57aa30ff33f0c 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts @@ -279,7 +279,12 @@ export class CodeActionController extends Disposable implements IEditorContribut return { canPreview: !!action.action.edit?.edits.length }; }, onFocus: (action: CodeActionItem | undefined) => { - if (action && action.action.diagnostics) { + // If provider contributes ranges, then highlight contributed range over diagnostic range. + if (action && action.action.ranges) { + currentDecorations.clear(); + const decorations: IModelDeltaDecoration[] = action.action.ranges.map(range => ({ range, options: CodeActionController.DECORATION })); + currentDecorations.set(decorations); + } else if (action && action.action.diagnostics) { currentDecorations.clear(); const decorations: IModelDeltaDecoration[] = action.action.diagnostics.map(diagnostic => ({ range: diagnostic, options: CodeActionController.DECORATION })); currentDecorations.set(decorations); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index cea289bcfda20..ed1b548900fe5 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -7102,6 +7102,7 @@ declare namespace monaco.languages { isPreferred?: boolean; isAI?: boolean; disabled?: string; + ranges?: IRange[]; } export enum CodeActionTriggerType { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index f34388f48758d..b345c62cce887 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1992,6 +1992,7 @@ export interface ICodeActionDto { isPreferred?: boolean; isAI?: boolean; disabled?: string; + ranges?: IRange[]; } export interface ICodeActionListDto { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 3190f3eed8452..9e35d5c766edf 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -474,6 +474,9 @@ class CodeActionAdapter { } } + // Ensures that this is either a Range[] or an empty array so we don't get Array + const range = candidate.ranges ?? []; + // new school: convert code action actions.push({ cacheId: [cacheId, i], @@ -484,6 +487,7 @@ class CodeActionAdapter { kind: candidate.kind && candidate.kind.value, isPreferred: candidate.isPreferred, isAI: isProposedApiEnabled(this._extension, 'codeActionAI') ? candidate.isAI : false, + ranges: isProposedApiEnabled(this._extension, 'codeActionRanges') ? coalesce(range.map(typeConvert.Range.from)) : undefined, disabled: candidate.disabled?.reason }); } diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index ce32f5bf3099f..669bff233f97b 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -17,6 +17,7 @@ export const allApiProposals = Object.freeze({ chatRequestAccess: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatRequestAccess.d.ts', chatTab: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatTab.d.ts', codeActionAI: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codeActionAI.d.ts', + codeActionRanges: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codeActionRanges.d.ts', codiconDecoration: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codiconDecoration.d.ts', commentReactor: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.commentReactor.d.ts', commentsDraftState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.commentsDraftState.d.ts', diff --git a/src/vscode-dts/vscode.proposed.codeActionRanges.d.ts b/src/vscode-dts/vscode.proposed.codeActionRanges.d.ts new file mode 100644 index 0000000000000..704208454d7cd --- /dev/null +++ b/src/vscode-dts/vscode.proposed.codeActionRanges.d.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + export interface CodeAction { + /** + * + * The range to which this Code Action applies to, which will be highlighted. + * + * Ex: A refactoring action will highlight the range of text that will be affected. + */ + ranges?: Range[]; + } +} From 3c8e0fc16d21da7bb43abffd37720d93bdd644a3 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 18 Jan 2024 19:26:36 -0300 Subject: [PATCH 134/333] Get rid of chat progress Task (#202777) --- .../api/common/extHostChatAgents2.ts | 15 +------------ .../api/common/extHostTypeConverters.ts | 4 +--- .../vscode.proposed.chatAgents2.d.ts | 21 +------------------ 3 files changed, 3 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 5a80b2edbf67c..0a835a965e1a9 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -98,20 +98,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { } if ('placeholder' in progress && 'resolvedContent' in progress) { - const resolvedContent = Promise.all([this._proxy.$handleProgressChunk(request.requestId, convertedProgress), progress.resolvedContent]); - raceCancellation(resolvedContent, token).then(res => { - if (!res) { - return; /* Cancelled */ - } - const [progressHandle, progressContent] = res; - const convertedContent = typeConvert.ChatResponseProgress.from(agent.extension, progressContent); - if (!convertedContent) { - this._logService.error('Unknown progress type: ' + JSON.stringify(progressContent)); - return; - } - - this._proxy.$handleProgressChunk(request.requestId, convertedContent, progressHandle ?? undefined); - }); + // Ignore for now, this is the deleted Task type } else { this._proxy.$handleProgressChunk(request.requestId, convertedProgress); } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 1a07e6b23f21a..62d20bbea372f 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2320,9 +2320,7 @@ export namespace InteractiveEditorResponseFeedbackKind { export namespace ChatResponseProgress { export function from(extension: IExtensionDescription, progress: vscode.ChatAgentExtendedProgress): extHostProtocol.IChatProgressDto | undefined { - if ('placeholder' in progress && 'resolvedContent' in progress) { - return { content: progress.placeholder, kind: 'asyncContent' } satisfies extHostProtocol.IChatAsyncContentDto; - } else if ('markdownContent' in progress) { + if ('markdownContent' in progress) { checkProposedApiEnabled(extension, 'chatAgents2Additions'); return { content: MarkdownString.from(progress.markdownContent), kind: 'markdownContent' }; } else if ('content' in progress) { diff --git a/src/vscode-dts/vscode.proposed.chatAgents2.d.ts b/src/vscode-dts/vscode.proposed.chatAgents2.d.ts index 12166a4f50c6a..e303c682925c5 100644 --- a/src/vscode-dts/vscode.proposed.chatAgents2.d.ts +++ b/src/vscode-dts/vscode.proposed.chatAgents2.d.ts @@ -270,8 +270,7 @@ declare module 'vscode' { export type ChatAgentContentProgress = | ChatAgentContent | ChatAgentFileTree - | ChatAgentInlineContentReference - | ChatAgentTask; + | ChatAgentInlineContentReference; export type ChatAgentMetadataProgress = | ChatAgentUsedContext @@ -322,24 +321,6 @@ declare module 'vscode' { content: string; } - /** - * Represents a piece of the chat response's content that is resolved asynchronously. It is rendered immediately with a placeholder, - * which is replaced once the full content is available. - */ - export interface ChatAgentTask { - /** - * The markdown string to be rendered immediately. - */ - placeholder: string; - - /** - * A Thenable resolving to the real content. The placeholder will be replaced with this content once it's available. - */ - // TODO@API Should this be an async iterable or progress instance instead - // TODO@API Should this include more inline-renderable items like `ChatAgentInlineContentReference` - resolvedContent: Thenable; - } - /** * Represents a tree, such as a file and directory structure, rendered in the chat response. */ From 0e51e7161908bfc78efacadf7cb01db911fc7d59 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 18 Jan 2024 15:18:39 -0800 Subject: [PATCH 135/333] Pick up latest katex (#202776) --- extensions/markdown-math/package.json | 2 +- extensions/markdown-math/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/markdown-math/package.json b/extensions/markdown-math/package.json index 85d09cbc9dcd4..44b442b3df46b 100644 --- a/extensions/markdown-math/package.json +++ b/extensions/markdown-math/package.json @@ -99,7 +99,7 @@ "build-notebook": "node ./esbuild" }, "dependencies": { - "@vscode/markdown-it-katex": "^1.0.2" + "@vscode/markdown-it-katex": "^1.0.3" }, "devDependencies": { "@types/markdown-it": "^0.0.0", diff --git a/extensions/markdown-math/yarn.lock b/extensions/markdown-math/yarn.lock index e49c27e7bf859..38e50260d03e3 100644 --- a/extensions/markdown-math/yarn.lock +++ b/extensions/markdown-math/yarn.lock @@ -12,10 +12,10 @@ resolved "https://registry.yarnpkg.com/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.72.0.tgz#8943dc3cef0ced2dfb1e04c0a933bd289e7d5199" integrity sha512-5iTjb39DpLn03ULUwrDR3L2Dy59RV4blSUHy0oLdQuIY11PhgWO4mXIcoFS0VxY1GZQ4IcjSf3ooT2Jrrcahnw== -"@vscode/markdown-it-katex@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@vscode/markdown-it-katex/-/markdown-it-katex-1.0.2.tgz#27ba579fa3896b2944b71209dd30d0f983983f11" - integrity sha512-QY/OnOHPTqc8tQoCoAjVblILX4yE6xGZHKODtiTKqA328OXra+lSpeJO5Ouo9AAvrs9AwcCLz6xvW3zwcsPBQg== +"@vscode/markdown-it-katex@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@vscode/markdown-it-katex/-/markdown-it-katex-1.0.3.tgz#5364e4dbcb0f7e7fd2fdab3847ba5d6b0c3ce9d9" + integrity sha512-a8ppdac0CG2lAQC6E6lT8dxmXkUk9gRtYNtILx31FyrPEwj875AAHc6tpRGeJBpWMpiMtcvz7ymWYBwYgxuFmw== dependencies: katex "^0.16.4" From 42dea84bda99d42f018a21fa3f969216e0c4c262 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 18 Jan 2024 15:30:26 -0800 Subject: [PATCH 136/333] testing: close coverage -> clear coverage, clear if run with no coverage (#202783) Fixes #202713 Fixes #202712 --- .../contrib/testing/browser/testExplorerActions.ts | 8 ++++---- src/vs/workbench/contrib/testing/common/constants.ts | 2 +- .../contrib/testing/common/testCoverageService.ts | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts index e932cb450df8b..b3cd0a1f306cd 100644 --- a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts +++ b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts @@ -1611,11 +1611,11 @@ export class CancelTestRefreshAction extends Action2 { } } -export class CloseCoverage extends Action2 { +export class CleareCoverage extends Action2 { constructor() { super({ - id: TestCommandId.CoverageClose, - title: localize2('testing.closeCoverage', 'Close Coverage'), + id: TestCommandId.CoverageClear, + title: localize2('testing.clearCoverage', 'Clear Coverage'), icon: widgetClose, category, menu: [{ @@ -1665,7 +1665,7 @@ export const allTestActions = [ CancelTestRefreshAction, CancelTestRunAction, ClearTestResultsAction, - CloseCoverage, + CleareCoverage, CollapseAllAction, ConfigureTestProfilesAction, ContinuousRunTestAction, diff --git a/src/vs/workbench/contrib/testing/common/constants.ts b/src/vs/workbench/contrib/testing/common/constants.ts index d05d75c4d6592..35d9eb5e5521e 100644 --- a/src/vs/workbench/contrib/testing/common/constants.ts +++ b/src/vs/workbench/contrib/testing/common/constants.ts @@ -62,7 +62,7 @@ export const enum TestCommandId { CoverageAtCursor = 'testing.coverageAtCursor', CoverageByUri = 'testing.coverage.uri', CoverageViewChangeSorting = 'testing.coverageViewChangeSorting', - CoverageClose = 'testing.coverage.close', + CoverageClear = 'testing.coverage.close', CoverageCurrentFile = 'testing.coverageCurrentFile', CoverageLastRun = 'testing.coverageLastRun', CoverageSelectedAction = 'testing.coverageSelected', diff --git a/src/vs/workbench/contrib/testing/common/testCoverageService.ts b/src/vs/workbench/contrib/testing/common/testCoverageService.ts index e9278f7739f02..57c0832fdfdea 100644 --- a/src/vs/workbench/contrib/testing/common/testCoverageService.ts +++ b/src/vs/workbench/contrib/testing/common/testCoverageService.ts @@ -60,6 +60,8 @@ export class TestCoverageService extends Disposable implements ITestCoverageServ const coverage = evt.completed.tasks.find(t => t.coverage.get()); if (coverage) { this.openCoverage(coverage, false); + } else { + this.closeCoverage(); } } else if ('removed' in evt && this.selected.get()) { const taskId = this.selected.get()?.fromTaskId; From 76e1ccf4c073b7aacafa24494452a2065f37914b Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 18 Jan 2024 15:31:44 -0800 Subject: [PATCH 137/333] debug: initial visualization extension points (#202775) wip --- .../platform/extensions/common/extensions.ts | 6 + .../api/browser/mainThreadDebugService.ts | 28 ++- .../workbench/api/common/extHost.api.impl.ts | 5 + .../workbench/api/common/extHost.protocol.ts | 8 +- .../api/common/extHostDebugService.ts | 154 ++++++++++++- src/vs/workbench/api/common/extHostTypes.ts | 7 + .../workbench/api/node/extHostDebugService.ts | 4 +- .../contrib/debug/browser/baseDebugView.ts | 12 +- .../debug/browser/debug.contribution.ts | 2 + .../contrib/debug/browser/variablesView.ts | 75 ++++-- .../workbench/contrib/debug/common/debug.ts | 50 +++- .../contrib/debug/common/debugContext.ts | 26 +++ .../contrib/debug/common/debugModel.ts | 6 +- .../contrib/debug/common/debugVisualizers.ts | 213 ++++++++++++++++++ .../common/extensionsApiProposals.ts | 1 + .../vscode.proposed.debugVisualization.d.ts | 100 ++++++++ 16 files changed, 658 insertions(+), 39 deletions(-) create mode 100644 src/vs/workbench/contrib/debug/common/debugContext.ts create mode 100644 src/vs/workbench/contrib/debug/common/debugVisualizers.ts create mode 100644 src/vscode-dts/vscode.proposed.debugVisualization.d.ts diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 222f6b91861d8..413c1db06f189 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -162,6 +162,11 @@ export interface INotebookRendererContribution { readonly mimeTypes: string[]; } +export interface IDebugVisualizationContribution { + readonly id: string; + readonly when: string; +} + export interface ITranslation { id: string; path: string; @@ -199,6 +204,7 @@ export interface IExtensionContributions { startEntries?: IStartEntry[]; readonly notebooks?: INotebookEntry[]; readonly notebookRenderer?: INotebookRendererContribution[]; + readonly debugVisualizers?: IDebugVisualizationContribution[]; } export interface IExtensionCapabilities { diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index c98dd8af07217..283c0e783728e 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DisposableMap, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI as uri, UriComponents } from 'vs/base/common/uri'; -import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint, IDebugSessionOptions, IInstructionBreakpoint, DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint, IDebugSessionOptions, IInstructionBreakpoint, DebugConfigurationProviderTriggerKind, IDebugVisualization } from 'vs/workbench/contrib/debug/common/debug'; import { ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto, IDataBreakpointDto, IStartDebuggingOptions, IDebugConfiguration, IThreadFocusDto, IStackFrameFocusDto @@ -16,6 +16,8 @@ import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstract import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { convertToVSCPaths, convertToDAPaths, isSessionAttach } from 'vs/workbench/contrib/debug/common/debugUtils'; import { ErrorNoTelemetry } from 'vs/base/common/errors'; +import { IDebugVisualizerService } from 'vs/workbench/contrib/debug/common/debugVisualizers'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @extHostNamedCustomer(MainContext.MainThreadDebugService) export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterFactory { @@ -27,10 +29,12 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb private readonly _debugConfigurationProviders: Map; private readonly _debugAdapterDescriptorFactories: Map; private readonly _extHostKnownSessions: Set; + private readonly _visualizerHandles = new Map(); constructor( extHostContext: IExtHostContext, - @IDebugService private readonly debugService: IDebugService + @IDebugService private readonly debugService: IDebugService, + @IDebugVisualizerService private readonly visualizerService: IDebugVisualizerService, ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDebugService); @@ -108,6 +112,24 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb this.sendBreakpointsAndListen(); } + $registerDebugVisualizer(extensionId: string, id: string): void { + const handle = this.visualizerService.register({ + extensionId: new ExtensionIdentifier(extensionId), + id, + disposeDebugVisualizers: ids => this._proxy.$disposeDebugVisualizers(ids), + executeDebugVisualizerCommand: id => this._proxy.$executeDebugVisualizerCommand(id), + provideDebugVisualizers: (context, token) => this._proxy.$provideDebugVisualizers(extensionId, id, context, token).then(r => r.map(IDebugVisualization.deserialize)), + resolveDebugVisualizer: (viz, token) => this._proxy.$resolveDebugVisualizer(viz.id, token), + }); + this._visualizerHandles.set(`${extensionId}/${id}`, handle); + } + + $unregisterDebugVisualizer(extensionId: string, id: string): void { + const key = `${extensionId}/${id}`; + this._visualizerHandles.get(key)?.dispose(); + this._visualizerHandles.delete(key); + } + private sendBreakpointsAndListen(): void { // set up a handler to send more this._toDispose.add(this.debugService.getModel().onDidChangeBreakpoints(e => { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index dd36b43f34f41..d3982e71c82bf 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1211,6 +1211,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get stackFrameFocus() { return extHostDebugService.stackFrameFocus; }, + registerDebugVisualizationProvider(id, provider) { + checkProposedApiEnabled(extension, 'debugVisualization'); + return extHostDebugService.registerDebugVisualizationProvider(extension, id, provider); + }, onDidStartDebugSession(listener, thisArg?, disposables?) { return _asExtensionEvent(extHostDebugService.onDidStartDebugSession)(listener, thisArg, disposables); }, @@ -1465,6 +1469,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I DebugAdapterServer: extHostTypes.DebugAdapterServer, DebugConfigurationProviderTriggerKind: DebugConfigurationProviderTriggerKind, DebugConsoleMode: extHostTypes.DebugConsoleMode, + DebugVisualization: extHostTypes.DebugVisualization, DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior, Diagnostic: extHostTypes.Diagnostic, DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index b345c62cce887..21261315689cc 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -55,7 +55,7 @@ import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/c import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } from 'vs/workbench/contrib/chat/common/chatProvider'; import { IChatAsyncContent, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatReplyFollowup, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; -import { DebugConfigurationProviderTriggerKind, IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug'; +import { DebugConfigurationProviderTriggerKind, MainThreadDebugVisualization, IAdapterDescriptor, IConfig, IDebugSessionReplMode, IDebugVisualization, IDebugVisualizationContext } from 'vs/workbench/contrib/debug/common/debug'; import { IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatProgressItem, IInlineChatRequest, IInlineChatSession, InlineChatResponseFeedbackKind } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import * as notebookCommon from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CellExecutionUpdateType } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; @@ -1562,6 +1562,8 @@ export interface MainThreadDebugServiceShape extends IDisposable { $appendDebugConsole(value: string): void; $registerBreakpoints(breakpoints: Array): Promise; $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[], dataBreakpointIds: string[]): Promise; + $registerDebugVisualizer(extensionId: string, id: string): void; + $unregisterDebugVisualizer(extensionId: string, id: string): void; } export interface IOpenUriOptions { @@ -2352,6 +2354,10 @@ export interface ExtHostDebugServiceShape { $acceptBreakpointsDelta(delta: IBreakpointsDeltaDto): void; $acceptDebugSessionNameChanged(session: IDebugSessionDto, name: string): void; $acceptStackFrameFocus(focus: IThreadFocusDto | IStackFrameFocusDto | undefined): void; + $provideDebugVisualizers(extensionId: string, id: string, context: IDebugVisualizationContext, token: CancellationToken): Promise; + $resolveDebugVisualizer(id: number, token: CancellationToken): Promise; + $executeDebugVisualizerCommand(id: number): Promise; + $disposeDebugVisualizers(ids: number[]): void; } diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 8eacd6f83b24f..ae9ba7e1b42a5 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -7,7 +7,7 @@ import { asPromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ISignService } from 'vs/platform/sign/common/sign'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -15,16 +15,19 @@ import { DebugSessionUUID, ExtHostDebugServiceShape, IBreakpointsDeltaDto, IThre import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs'; import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -import { Breakpoint, DataBreakpoint, DebugAdapterExecutable, DebugAdapterInlineImplementation, DebugAdapterNamedPipeServer, DebugAdapterServer, DebugConsoleMode, Disposable, FunctionBreakpoint, Location, Position, setBreakpointId, SourceBreakpoint, ThreadFocus, StackFrameFocus } from 'vs/workbench/api/common/extHostTypes'; +import { Breakpoint, DataBreakpoint, DebugAdapterExecutable, DebugAdapterInlineImplementation, DebugAdapterNamedPipeServer, DebugAdapterServer, DebugConsoleMode, Disposable, FunctionBreakpoint, Location, Position, setBreakpointId, SourceBreakpoint, ThreadFocus, StackFrameFocus, ThemeIcon } from 'vs/workbench/api/common/extHostTypes'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter'; -import { IAdapterDescriptor, IConfig, IDebugAdapter, IDebugAdapterExecutable, IDebugAdapterNamedPipeServer, IDebugAdapterServer, IDebuggerContribution } from 'vs/workbench/contrib/debug/common/debug'; +import { MainThreadDebugVisualization, IAdapterDescriptor, IConfig, IDebugAdapter, IDebugAdapterExecutable, IDebugAdapterNamedPipeServer, IDebugAdapterServer, IDebugVisualization, IDebugVisualizationContext, IDebuggerContribution, DebugVisualizationType } from 'vs/workbench/contrib/debug/common/debug'; import { convertToDAPaths, convertToVSCPaths, isDebuggerMainContribution } from 'vs/workbench/contrib/debug/common/debugUtils'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import type * as vscode from 'vscode'; import { IExtHostConfiguration } from '../common/extHostConfiguration'; import { IExtHostVariableResolverProvider } from './extHostVariableResolverService'; +import { toDisposable } from 'vs/base/common/lifecycle'; +import { ThemeIcon as ThemeIconUtils } from 'vs/base/common/themables'; +import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; export const IExtHostDebugService = createDecorator('IExtHostDebugService'); @@ -50,6 +53,7 @@ export interface IExtHostDebugService extends ExtHostDebugServiceShape { registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider, trigger: vscode.DebugConfigurationProviderTriggerKind): vscode.Disposable; registerDebugAdapterDescriptorFactory(extension: IExtensionDescription, type: string, factory: vscode.DebugAdapterDescriptorFactory): vscode.Disposable; registerDebugAdapterTrackerFactory(type: string, factory: vscode.DebugAdapterTrackerFactory): vscode.Disposable; + registerDebugVisualizationProvider(extension: IExtensionDescription, id: string, provider: vscode.DebugVisualizationProvider): vscode.Disposable; asDebugSourceUri(source: vscode.DebugProtocolSource, session?: vscode.DebugSession): vscode.Uri; } @@ -96,9 +100,13 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E private _debugAdapters: Map; private _debugAdaptersTrackers: Map; + private readonly _debugVisualizationProviders = new Map(); private _signService: ISignService | undefined; + private readonly _visualizers = new Map(); + private _visualizerIdCounter = 0; + constructor( @IExtHostRpcService extHostRpcService: IExtHostRpcService, @IExtHostWorkspace protected _workspaceService: IExtHostWorkspace, @@ -106,6 +114,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E @IExtHostConfiguration protected _configurationService: IExtHostConfiguration, @IExtHostEditorTabs protected _editorTabs: IExtHostEditorTabs, @IExtHostVariableResolverProvider private _variableResolver: IExtHostVariableResolverProvider, + @IExtHostCommands private _commands: IExtHostCommands, ) { this._configProviderHandleCounter = 0; this._configProviders = []; @@ -209,6 +218,96 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E return result; } + public async $resolveDebugVisualizer(id: number, token: CancellationToken): Promise { + const visualizer = this._visualizers.get(id); + if (!visualizer) { + throw new Error(`No debug visualizer found with id '${id}'`); + } + + let { v, provider } = visualizer; + if (!v.visualization) { + v = await provider.resolveDebugVisualization?.(v, token) || v; + visualizer.v = v; + } + + if (!v.visualization) { + throw new Error(`No visualization returned from resolveDebugVisualization in '${provider}'`); + } + + return this.serializeVisualization(v.visualization)!; + } + + public async $executeDebugVisualizerCommand(id: number): Promise { + const visualizer = this._visualizers.get(id); + if (!visualizer) { + throw new Error(`No debug visualizer found with id '${id}'`); + } + + const command = visualizer.v.visualization; + if (command && 'command' in command) { + this._commands.executeCommand(command.command, ...(command.arguments || [])); + } + } + + public async $provideDebugVisualizers(extensionId: string, id: string, context: IDebugVisualizationContext, token: CancellationToken): Promise { + const session = this._debugSessions.get(context.sessionId); + const key = this.extensionVisKey(extensionId, id); + const provider = this._debugVisualizationProviders.get(key); + if (!session || !provider) { + return []; // probably ended in the meantime + } + + const visualizations = await provider.provideDebugVisualization({ + session, + variable: context.variable, + containerId: context.containerId, + frameId: context.frameId, + threadId: context.threadId, + }, token); + + if (!visualizations) { + return []; + } + + return visualizations.map(v => { + const id = ++this._visualizerIdCounter; + this._visualizers.set(id, { v, provider }); + const icon = v.iconPath ? this.getIconPathOrClass(v.iconPath) : undefined; + return { + id, + name: v.name, + iconClass: icon?.iconClass, + iconPath: icon?.iconPath, + visualization: this.serializeVisualization(v.visualization), + }; + }); + } + + public $disposeDebugVisualizers(ids: number[]): void { + for (const id of ids) { + this._visualizers.delete(id); + } + } + + public registerDebugVisualizationProvider(manifest: IExtensionDescription, id: string, provider: vscode.DebugVisualizationProvider): vscode.Disposable { + if (!manifest.contributes?.debugVisualizers?.some(r => r.id === id)) { + throw new Error(`Extensions may only call registerDebugVisualizationProvider() for renderers they contribute (got ${id})`); + } + + const extensionId = ExtensionIdentifier.toKey(manifest.identifier); + const key = this.extensionVisKey(extensionId, id); + if (this._debugVisualizationProviders.has(key)) { + throw new Error(`A debug visualization provider with id '${id}' is already registered`); + } + + this._debugVisualizationProviders.set(key, provider); + this._debugServiceProxy.$registerDebugVisualizer(extensionId, id); + return toDisposable(() => { + this._debugServiceProxy.$unregisterDebugVisualizer(extensionId, id); + this._debugVisualizationProviders.delete(id); + }); + } + public addBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise { // filter only new breakpoints const breakpoints = breakpoints0.filter(bp => { @@ -858,6 +957,50 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E } return Promise.resolve(undefined); } + + private extensionVisKey(extensionId: string, id: string) { + return `${extensionId}\0${id}`; + } + + private serializeVisualization(viz: vscode.DebugVisualization['visualization']): MainThreadDebugVisualization | undefined { + if (!viz) { + return undefined; + } + + if ('title' in viz && 'command' in viz) { + return { type: DebugVisualizationType.Command }; + } + + throw new Error('Unsupported debug visualization type'); + } + + private getIconPathOrClass(icon: vscode.DebugVisualization['iconPath']) { + const iconPathOrIconClass = this.getIconUris(icon); + let iconPath: { dark: URI; light?: URI | undefined } | undefined; + let iconClass: string | undefined; + if ('id' in iconPathOrIconClass) { + iconClass = ThemeIconUtils.asClassName(iconPathOrIconClass); + } else { + iconPath = iconPathOrIconClass; + } + + return { + iconPath, + iconClass + }; + } + + private getIconUris(iconPath: vscode.DebugVisualization['iconPath']): { dark: URI; light?: URI } | { id: string } { + if (iconPath instanceof ThemeIcon) { + return { id: iconPath.id }; + } + const dark = typeof iconPath === 'object' && 'dark' in iconPath ? iconPath.dark : iconPath; + const light = typeof iconPath === 'object' && 'light' in iconPath ? iconPath.light : iconPath; + return { + dark: (typeof dark === 'string' ? URI.file(dark) : dark) as URI, + light: (typeof light === 'string' ? URI.file(light) : light) as URI, + }; + } } export class ExtHostDebugSession implements vscode.DebugSession { @@ -1013,8 +1156,9 @@ export class WorkerExtHostDebugService extends ExtHostDebugServiceBase { @IExtHostExtensionService extensionService: IExtHostExtensionService, @IExtHostConfiguration configurationService: IExtHostConfiguration, @IExtHostEditorTabs editorTabs: IExtHostEditorTabs, - @IExtHostVariableResolverProvider variableResolver: IExtHostVariableResolverProvider + @IExtHostVariableResolverProvider variableResolver: IExtHostVariableResolverProvider, + @IExtHostCommands commands: IExtHostCommands ) { - super(extHostRpcService, workspaceService, extensionService, configurationService, editorTabs, variableResolver); + super(extHostRpcService, workspaceService, extensionService, configurationService, editorTabs, variableResolver, commands); } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 4c45b385fa867..1307d23e7056d 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3448,6 +3448,13 @@ export enum DebugConsoleMode { MergeWithParent = 1 } +export class DebugVisualization { + iconPath?: URI | { light: URI; dark: URI } | ThemeIcon; + visualization?: vscode.Command | vscode.TreeDataProvider; + + constructor(public name: string) { } +} + //#endregion @es5ClassCompat diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 55cd060d3646a..07bb7582839f2 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -26,6 +26,7 @@ import { hasChildProcesses, prepareCommand } from 'vs/workbench/contrib/debug/no import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import type * as vscode from 'vscode'; import { ExtHostConfigProvider, IExtHostConfiguration } from '../common/extHostConfiguration'; +import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; export class ExtHostDebugService extends ExtHostDebugServiceBase { @@ -42,8 +43,9 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { @IExtHostTerminalService private _terminalService: IExtHostTerminalService, @IExtHostEditorTabs editorTabs: IExtHostEditorTabs, @IExtHostVariableResolverProvider variableResolver: IExtHostVariableResolverProvider, + @IExtHostCommands commands: IExtHostCommands, ) { - super(extHostRpcService, workspaceService, extensionService, configurationService, editorTabs, variableResolver); + super(extHostRpcService, workspaceService, extensionService, configurationService, editorTabs, variableResolver, commands); } protected override createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined { diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index a3e99830ac38e..058167334d83a 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -138,7 +138,7 @@ export interface IExpressionTemplateData { value: HTMLSpanElement; inputBoxContainer: HTMLElement; actionBar?: ActionBar; - elementDisposable: IDisposable[]; + elementDisposable: DisposableStore; templateDisposable: IDisposable; label: HighlightedLabel; lazyButton: HTMLElement; @@ -174,7 +174,7 @@ export abstract class AbstractExpressionsRenderer implements IT actionBar = templateDisposable.add(new ActionBar(expression)); } - const template: IExpressionTemplateData = { expression, name, value, label, inputBoxContainer, actionBar, elementDisposable: [], templateDisposable, lazyButton, currentElement: undefined }; + const template: IExpressionTemplateData = { expression, name, value, label, inputBoxContainer, actionBar, elementDisposable: new DisposableStore(), templateDisposable, lazyButton, currentElement: undefined }; templateDisposable.add(dom.addDisposableListener(lazyButton, dom.EventType.CLICK, () => { if (template.currentElement) { @@ -188,6 +188,7 @@ export abstract class AbstractExpressionsRenderer implements IT public abstract renderElement(node: ITreeNode, index: number, data: IExpressionTemplateData): void; protected renderExpressionElement(element: IExpression, node: ITreeNode, data: IExpressionTemplateData): void { + data.elementDisposable.clear(); data.currentElement = element; this.renderExpression(node.element, data, createMatches(node.filterData)); if (data.actionBar) { @@ -197,7 +198,7 @@ export abstract class AbstractExpressionsRenderer implements IT if (element === selectedExpression?.expression || (element instanceof Variable && element.errorMessage)) { const options = this.getInputBoxOptions(element, !!selectedExpression?.settingWatch); if (options) { - data.elementDisposable.push(this.renderInputBox(data.name, data.value, data.inputBoxContainer, options)); + data.elementDisposable.add(this.renderInputBox(data.name, data.value, data.inputBoxContainer, options)); } } } @@ -259,12 +260,11 @@ export abstract class AbstractExpressionsRenderer implements IT protected renderActionBar?(actionBar: ActionBar, expression: IExpression, data: IExpressionTemplateData): void; disposeElement(node: ITreeNode, index: number, templateData: IExpressionTemplateData): void { - dispose(templateData.elementDisposable); - templateData.elementDisposable = []; + templateData.elementDisposable.clear(); } disposeTemplate(templateData: IExpressionTemplateData): void { - dispose(templateData.elementDisposable); + templateData.elementDisposable.dispose(); templateData.templateDisposable.dispose(); } } diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 7324f4bedfe58..76ca42eeadd35 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -51,6 +51,7 @@ import { WelcomeView } from 'vs/workbench/contrib/debug/browser/welcomeView'; import { BREAKPOINTS_VIEW_ID, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CALLSTACK_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE, CONTEXT_DEBUG_UX, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_FOCUSED_SESSION_IS_NO_DEBUG, CONTEXT_HAS_DEBUGGED, CONTEXT_IN_DEBUG_MODE, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_SET_EXPRESSION_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_WATCH_ITEM_TYPE, DEBUG_PANEL_ID, DISASSEMBLY_VIEW_ID, EDITOR_CONTRIBUTION_ID, IDebugService, INTERNAL_CONSOLE_OPTIONS_SCHEMA, LOADED_SCRIPTS_VIEW_ID, REPL_VIEW_ID, State, VARIABLES_VIEW_ID, VIEWLET_ID, WATCH_VIEW_ID, getStateLabel } from 'vs/workbench/contrib/debug/common/debug'; import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; import { DebugLifecycle } from 'vs/workbench/contrib/debug/common/debugLifecycle'; +import { DebugVisualizerService, IDebugVisualizerService } from 'vs/workbench/contrib/debug/common/debugVisualizers'; import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -58,6 +59,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle const debugCategory = nls.localize('debugCategory', "Debug"); registerColors(); registerSingleton(IDebugService, DebugService, InstantiationType.Delayed); +registerSingleton(IDebugVisualizerService, DebugVisualizerService, InstantiationType.Delayed); // Register Debug Workbench Contributions Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugStatusContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index a0f887673ae1c..df67a66260a13 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -10,12 +10,14 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; import { IAsyncDataSource, ITreeContextMenuEvent, ITreeMouseEvent, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; -import { IAction } from 'vs/base/common/actions'; +import { Action, IAction } from 'vs/base/common/actions'; import { coalesce } from 'vs/base/common/arrays'; import { RunOnceScheduler, timeout } from 'vs/base/common/async'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; -import { createMatches, FuzzyScore } from 'vs/base/common/filters'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { FuzzyScore, createMatches } from 'vs/base/common/filters'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { ThemeIcon } from 'vs/base/common/themables'; import { localize } from 'vs/nls'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuService, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; @@ -37,8 +39,10 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl import { IViewDescriptorService } from 'vs/workbench/common/views'; import { AbstractExpressionsRenderer, IExpressionTemplateData, IInputBoxOptions, renderVariable, renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; -import { CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT, CONTEXT_VARIABLES_FOCUSED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, CONTEXT_VARIABLE_IS_READONLY, IDataBreakpointInfoResponse, IDebugService, IExpression, IScope, IStackFrame, VARIABLES_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; -import { ErrorScope, Expression, getUriForDebugMemory, Scope, StackFrame, Variable } from 'vs/workbench/contrib/debug/common/debugModel'; +import { CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_VARIABLES_FOCUSED, DebugVisualizationType, IDataBreakpointInfoResponse, IDebugService, IExpression, IScope, IStackFrame, VARIABLES_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; +import { getContextForVariable } from 'vs/workbench/contrib/debug/common/debugContext'; +import { ErrorScope, Expression, Scope, StackFrame, Variable, getUriForDebugMemory } from 'vs/workbench/contrib/debug/common/debugModel'; +import { DebugVisualizer, IDebugVisualizerService } from 'vs/workbench/contrib/debug/common/debugVisualizers'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -254,7 +258,7 @@ const getVariablesContext = (variable: Variable): IVariablesContext => ({ async function getContextForVariableMenuWithDataAccess(parentContext: IContextKeyService, variable: Variable) { const session = variable.getSession(); if (!session || !session.capabilities.supportsDataBreakpoints) { - return getContextForVariableMenu(parentContext, variable); + return getContextForVariableMenuBase(parentContext, variable); } const contextKeys: [string, unknown][] = []; @@ -280,25 +284,15 @@ async function getContextForVariableMenuWithDataAccess(parentContext: IContextKe } } - return getContextForVariableMenu(parentContext, variable, contextKeys); + return getContextForVariableMenuBase(parentContext, variable, contextKeys); } /** * Gets a context key overlay that has context for the given variable. */ -function getContextForVariableMenu(parentContext: IContextKeyService, variable: Variable, additionalContext: [string, unknown][] = []) { - const session = variable.getSession(); - const contextKeys: [string, unknown][] = [ - [CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT.key, variable.variableMenuContext || ''], - [CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT.key, !!variable.evaluateName], - [CONTEXT_CAN_VIEW_MEMORY.key, !!session?.capabilities.supportsReadMemoryRequest && variable.memoryReference !== undefined], - [CONTEXT_VARIABLE_IS_READONLY.key, !!variable.presentationHint?.attributes?.includes('readOnly') || variable.presentationHint?.lazy], - ...additionalContext, - ]; - +function getContextForVariableMenuBase(parentContext: IContextKeyService, variable: Variable, additionalContext: [string, unknown][] = []) { variableInternalContext = variable; - - return parentContext.createOverlay(contextKeys); + return getContextForVariable(parentContext, variable, additionalContext); } function isStackFrame(obj: any): obj is IStackFrame { @@ -410,6 +404,8 @@ export class VariablesRenderer extends AbstractExpressionsRenderer { private readonly linkDetector: LinkDetector, @IMenuService private readonly menuService: IMenuService, @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IDebugVisualizerService private readonly visualization: IDebugVisualizerService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, @IDebugService debugService: IDebugService, @IContextViewService contextViewService: IContextViewService, ) { @@ -452,9 +448,9 @@ export class VariablesRenderer extends AbstractExpressionsRenderer { }; } - protected override renderActionBar(actionBar: ActionBar, expression: IExpression) { + protected override renderActionBar(actionBar: ActionBar, expression: IExpression, data: IExpressionTemplateData) { const variable = expression as Variable; - const contextKeyService = getContextForVariableMenu(this.contextKeyService, variable); + const contextKeyService = getContextForVariableMenuBase(this.contextKeyService, variable); const menu = this.menuService.createMenu(MenuId.DebugVariablesContext, contextKeyService); const primary: IAction[] = []; @@ -464,6 +460,43 @@ export class VariablesRenderer extends AbstractExpressionsRenderer { actionBar.clear(); actionBar.context = context; actionBar.push(primary, { icon: true, label: false }); + + const cts = new CancellationTokenSource(); + data.elementDisposable.add(toDisposable(() => cts.dispose(true))); + this.visualization.getApplicableFor(expression, cts.token).then(result => { + data.elementDisposable.add(result); + + const actions = result.object.map(v => new Action('debugViz', v.name, v.iconClass || 'debug-viz-icon', undefined, this.useVisualizer(v, cts.token))); + if (actions.length === 0) { + // no-op + } else if (actions.length === 1) { + actionBar.push(actions[0], { icon: true, label: false }); + } else { + actionBar.push(new Action('debugViz', localize('useVisualizer', 'Visualize Variable...'), ThemeIcon.asClassName(Codicon.eye), undefined, () => this.pickVisualizer(actions, expression, data)), { icon: true, label: false }); + } + }); + } + + private pickVisualizer(actions: IAction[], expression: IExpression, data: IExpressionTemplateData) { + this.contextMenuService.showContextMenu({ + getAnchor: () => data.actionBar!.getContainer(), + getActions: () => actions, + }); + } + + private useVisualizer(viz: DebugVisualizer, token: CancellationToken) { + return async () => { + const resolved = await viz.resolve(token); + if (token.isCancellationRequested) { + return; + } + + if (resolved.type === DebugVisualizationType.Command) { + viz.execute(); + } else { + throw new Error('not implemented, yet'); + } + }; } } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 308b0ac9bc0b9..2bdd7fa756c46 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -11,7 +11,7 @@ import { Event } from 'vs/base/common/event'; import { IJSONSchemaSnippet } from 'vs/base/common/jsonSchema'; import { IDisposable } from 'vs/base/common/lifecycle'; import severity from 'vs/base/common/severity'; -import { URI as uri } from 'vs/base/common/uri'; +import { URI, UriComponents, URI as uri } from 'vs/base/common/uri'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; @@ -84,6 +84,9 @@ export const CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED = new RawContextKey(' export const CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED = new RawContextKey('suspendDebuggeeSupported', false, { type: 'boolean', description: nls.localize('suspendDebuggeeSupported', "True when the focused session supports the suspend debuggee capability.") }); export const CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT = new RawContextKey('variableEvaluateNamePresent', false, { type: 'boolean', description: nls.localize('variableEvaluateNamePresent', "True when the focused variable has an 'evalauteName' field set.") }); export const CONTEXT_VARIABLE_IS_READONLY = new RawContextKey('variableIsReadonly', false, { type: 'boolean', description: nls.localize('variableIsReadonly', "True when the focused variable is read-only.") }); +export const CONTEXT_VARIABLE_VALUE = new RawContextKey('variableValue', false, { type: 'string', description: nls.localize('variableValue', "Value of the variable, present for debug visualization clauses.") }); +export const CONTEXT_VARIABLE_TYPE = new RawContextKey('variableType', false, { type: 'string', description: nls.localize('variableType', "Type of the variable, present for debug visualization clauses.") }); +export const CONTEXT_VARIABLE_NAME = new RawContextKey('variableName', false, { type: 'string', description: nls.localize('variableName', "Name of the variable, present for debug visualization clauses.") }); export const CONTEXT_EXCEPTION_WIDGET_VISIBLE = new RawContextKey('exceptionWidgetVisible', false, { type: 'boolean', description: nls.localize('exceptionWidgetVisible', "True when the exception widget is visible.") }); export const CONTEXT_MULTI_SESSION_REPL = new RawContextKey('multiSessionRepl', false, { type: 'boolean', description: nls.localize('multiSessionRepl', "True when there is more than 1 debug console.") }); export const CONTEXT_MULTI_SESSION_DEBUG = new RawContextKey('multiSessionDebug', false, { type: 'boolean', description: nls.localize('multiSessionDebug', "True when there is more than 1 active debug session.") }); @@ -1247,3 +1250,48 @@ export interface IReplConfiguration { export interface IReplOptions { readonly replConfiguration: IReplConfiguration; } + +export interface IDebugVisualizationContext { + variable: DebugProtocol.Variable; + containerId?: string; + frameId?: number; + threadId: number; + sessionId: string; +} + +export const enum DebugVisualizationType { + Command, + Tree, +} + +export type MainThreadDebugVisualization = { + type: DebugVisualizationType.Command; +}; // todo: tree + +export interface IDebugVisualization { + id: number; + name: string; + iconPath: { light?: URI; dark: URI } | undefined; + iconClass: string | undefined; + visualization: MainThreadDebugVisualization | undefined; +} + +export namespace IDebugVisualization { + export interface Serialized { + id: number; + name: string; + iconPath?: { light?: UriComponents; dark: UriComponents }; + iconClass?: string; + visualization?: MainThreadDebugVisualization; + } + + export const deserialize = (v: Serialized): IDebugVisualization => ({ + id: v.id, + name: v.name, + iconPath: v.iconPath && { light: URI.revive(v.iconPath.light), dark: URI.revive(v.iconPath.dark) }, + iconClass: v.iconClass, + visualization: v.visualization, + }); + + export const serialize = (visualizer: IDebugVisualization): Serialized => visualizer; +} diff --git a/src/vs/workbench/contrib/debug/common/debugContext.ts b/src/vs/workbench/contrib/debug/common/debugContext.ts new file mode 100644 index 0000000000000..26a6953310701 --- /dev/null +++ b/src/vs/workbench/contrib/debug/common/debugContext.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_DEBUG_TYPE } from 'vs/workbench/contrib/debug/common/debug'; +import { Variable } from 'vs/workbench/contrib/debug/common/debugModel'; + + +/** + * Gets a context key overlay that has context for the given variable. + */ +export function getContextForVariable(parentContext: IContextKeyService, variable: Variable, additionalContext: [string, unknown][] = []) { + const session = variable.getSession(); + const contextKeys: [string, unknown][] = [ + [CONTEXT_DEBUG_PROTOCOL_VARIABLE_MENU_CONTEXT.key, variable.variableMenuContext || ''], + [CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT.key, !!variable.evaluateName], + [CONTEXT_CAN_VIEW_MEMORY.key, !!session?.capabilities.supportsReadMemoryRequest && variable.memoryReference !== undefined], + [CONTEXT_VARIABLE_IS_READONLY.key, !!variable.presentationHint?.attributes?.includes('readOnly') || variable.presentationHint?.lazy], + [CONTEXT_DEBUG_TYPE.key, session?.configuration.type], + ...additionalContext, + ]; + + return parentContext.createOverlay(contextKeys); +} diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index c4f5c811018da..5a33aecfbebbf 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -305,6 +305,10 @@ export class Variable extends ExpressionContainer implements IExpression { this.type = type; } + getThreadId() { + return this.threadId; + } + async setVariable(value: string, stackFrame: IStackFrame): Promise { if (!this.session) { return; @@ -354,7 +358,7 @@ export class Variable extends ExpressionContainer implements IExpression { export class Scope extends ExpressionContainer implements IScope { constructor( - stackFrame: IStackFrame, + public readonly stackFrame: IStackFrame, id: number, public readonly name: string, reference: number, diff --git a/src/vs/workbench/contrib/debug/common/debugVisualizers.ts b/src/vs/workbench/contrib/debug/common/debugVisualizers.ts new file mode 100644 index 0000000000000..9a5cf6885c29e --- /dev/null +++ b/src/vs/workbench/contrib/debug/common/debugVisualizers.ts @@ -0,0 +1,213 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IDisposable, IReference, toDisposable } from 'vs/base/common/lifecycle'; +import { isDefined } from 'vs/base/common/types'; +import { ContextKeyExpr, ContextKeyExpression, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ExtensionIdentifier, IRelaxedExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; +import { CONTEXT_VARIABLE_NAME, CONTEXT_VARIABLE_TYPE, CONTEXT_VARIABLE_VALUE, MainThreadDebugVisualization, IDebugVisualization, IDebugVisualizationContext, IExpression, IExpressionContainer } from 'vs/workbench/contrib/debug/common/debug'; +import { getContextForVariable } from 'vs/workbench/contrib/debug/common/debugContext'; +import { Scope, Variable } from 'vs/workbench/contrib/debug/common/debugModel'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; + +export const IDebugVisualizerService = createDecorator('debugVisualizerService'); + +interface VisualizerHandle { + id: string; + extensionId: ExtensionIdentifier; + provideDebugVisualizers(context: IDebugVisualizationContext, token: CancellationToken): Promise; + resolveDebugVisualizer(viz: IDebugVisualization, token: CancellationToken): Promise; + executeDebugVisualizerCommand(id: number): Promise; + disposeDebugVisualizers(ids: number[]): void; +} + +export class DebugVisualizer { + public get name() { + return this.viz.name; + } + + public get iconPath() { + return this.viz.iconPath; + } + + public get iconClass() { + return this.viz.iconClass; + } + + constructor(private readonly handle: VisualizerHandle, private readonly viz: IDebugVisualization) { } + + public async resolve(token: CancellationToken) { + return this.viz.visualization ??= await this.handle.resolveDebugVisualizer(this.viz, token); + } + + public async execute() { + await this.handle.executeDebugVisualizerCommand(this.viz.id); + } +} + +export interface IDebugVisualizerService { + _serviceBrand: undefined; + + /** + * Gets visualizers applicable for the given Expression. + */ + getApplicableFor(expression: IExpression, token: CancellationToken): Promise>; + + /** + * Registers a new visualizer (called from the main thread debug service) + */ + register(handle: VisualizerHandle): IDisposable; +} + +export class DebugVisualizerService implements IDebugVisualizerService { + declare public readonly _serviceBrand: undefined; + + private readonly handles = new Map(); + private readonly didActivate = new Map>(); + private registrations: { expr: ContextKeyExpression; id: string; extensionId: ExtensionIdentifier }[] = []; + + constructor( + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IExtensionService private readonly extensionService: IExtensionService, + @ILogService private readonly logService: ILogService, + ) { + visualizersExtensionPoint.setHandler((_, { added, removed }) => { + this.registrations = this.registrations.filter(r => + !removed.some(e => ExtensionIdentifier.equals(e.description.identifier, r.extensionId))); + added.forEach(e => this.processExtensionRegistration(e.description)); + }); + } + + /** @inheritdoc */ + public async getApplicableFor(variable: Variable, token: CancellationToken): Promise> { + const threadId = variable.getThreadId(); + if (threadId === undefined) { // an expression, not a variable + return { object: [], dispose: () => { } }; + } + + const context: IDebugVisualizationContext = { + sessionId: variable.getSession()?.getId() || '', + containerId: variable.parent.getId(), + threadId, + variable: { + name: variable.name, + value: variable.value, + type: variable.type, + evaluateName: variable.evaluateName, + variablesReference: variable.reference || 0, + indexedVariables: variable.indexedVariables, + memoryReference: variable.memoryReference, + namedVariables: variable.namedVariables, + presentationHint: variable.presentationHint, + } + }; + + for (let p: IExpressionContainer = variable; p instanceof Variable; p = p.parent) { + if (p.parent instanceof Scope) { + context.frameId = p.parent.stackFrame.frameId; + } + } + + const overlay = getContextForVariable(this.contextKeyService, variable, [ + [CONTEXT_VARIABLE_NAME.key, variable.name], + [CONTEXT_VARIABLE_VALUE.key, variable.value], + [CONTEXT_VARIABLE_TYPE.key, variable.type], + ]); + + const maybeVisualizers = await Promise.all(this.registrations.map(async registration => { + if (!overlay.contextMatchesRules(registration.expr)) { + return; + } + + let prom = this.didActivate.get(registration.id); + if (!prom) { + prom = this.extensionService.activateByEvent(`onDebugVisualizer:${registration.id}`); + this.didActivate.set(registration.id, prom); + } + + await prom; + if (token.isCancellationRequested) { + return; + } + + const handle = this.handles.get(toKey(registration.extensionId, registration.id)); + return handle && { handle, result: await handle.provideDebugVisualizers(context, token) }; + })); + + const ref = { + object: maybeVisualizers.filter(isDefined).flatMap(v => v.result.map(r => new DebugVisualizer(v.handle, r))), + dispose: () => { + for (const viz of maybeVisualizers) { + viz?.handle.disposeDebugVisualizers(viz.result.map(r => r.id)); + } + }, + }; + + if (token.isCancellationRequested) { + ref.dispose(); + } + + return ref; + } + + /** @inheritdoc */ + public register(handle: VisualizerHandle): IDisposable { + const key = toKey(handle.extensionId, handle.id); + this.handles.set(key, handle); + return toDisposable(() => this.handles.delete(key)); + } + + private processExtensionRegistration(ext: Readonly) { + const viz = ext.contributes?.debugVisualizers; + if (!(viz instanceof Array)) { + return; + } + + for (const { when, id } of viz) { + try { + const expr = ContextKeyExpr.deserialize(when); + if (expr) { + this.registrations.push({ expr, id, extensionId: ext.identifier }); + } + } catch (e) { + this.logService.error(`Error processing debug visualizer registration from extension '${ext.identifier.value}'`, e); + } + } + } +} + +const toKey = (extensionId: ExtensionIdentifier, id: string) => `${ExtensionIdentifier.toKey(extensionId)}\0${id}`; + +const visualizersExtensionPoint = ExtensionsRegistry.registerExtensionPoint<{ id: string; when: string }[]>({ + extensionPoint: 'debugVisualizers', + jsonSchema: { + type: 'array', + items: { + type: 'object', + properties: { + id: { + type: 'string', + description: 'Name of the debug visualizer' + }, + when: { + type: 'string', + description: 'Condition when the debug visualizer is applicable' + } + }, + required: ['id', 'when'] + } + }, + activationEventsGenerator: (contribs, result: { push(item: string): void }) => { + for (const contrib of contribs) { + if (contrib.id) { + result.push(`onDebugVisualizer:${contrib.id}`); + } + } + } +}); diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 669bff233f97b..c8d09cf0969e8 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -42,6 +42,7 @@ export const allApiProposals = Object.freeze({ createFileSystemWatcher: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.createFileSystemWatcher.d.ts', customEditorMove: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.customEditorMove.d.ts', debugFocus: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.debugFocus.d.ts', + debugVisualization: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.debugVisualization.d.ts', defaultChatAgent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.defaultChatAgent.d.ts', diffCommand: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffCommand.d.ts', diffContentOptions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffContentOptions.d.ts', diff --git a/src/vscode-dts/vscode.proposed.debugVisualization.d.ts b/src/vscode-dts/vscode.proposed.debugVisualization.d.ts new file mode 100644 index 0000000000000..29a3c22d83240 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.debugVisualization.d.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +declare module 'vscode' { + export namespace debug { + /** + * Registers a custom data visualization for variables when debugging. + * + * @param id The corresponding ID in the package.json `debugVisualizers` contribution point. + * @param provider The {@link DebugVisualizationProvider} to register + */ + export function registerDebugVisualizationProvider( + id: string, + provider: DebugVisualizationProvider + ): Disposable; + } + + export class DebugVisualization { + /** + * The name of the visualization to show to the user. + */ + name: string; + + /** + * An icon for the view when it's show in inline actions. + */ + iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; + + /** + * Visualization to use for the variable. This may be either: + * - A command to run when the visualization is selected for a variable. + * - A {@link TreeDataProvider} which is used to display the data in-line + * where the variable is shown. If a single root item is returned from + * the data provider, it will replace the variable in its tree. + * Otherwise, the items will be shown as children of the variable. + */ + visualization?: Command | TreeDataProvider; + + /** + * Creates a new debug visualization object. + * @param name Name of the visualization to show to the user. + */ + constructor(name: string); + } + + export interface DebugVisualizationProvider { + /** + * Called for each variable when the debug session stops. It should return + * any visualizations the extension wishes to show to the user. + * + * Note that this is only called when its `when` clause defined under the + * `debugVisualizers` contribution point in the `package.json` evaluates + * to true. + */ + provideDebugVisualization(context: DebugVisualizationContext, token: CancellationToken): ProviderResult; + + /** + * Invoked for a variable when a user picks the visualizer. + * + * It may return a {@link TreeView} that's shown in the Debug Console or + * inline in a hover. A visualizer may choose to return `undefined` from + * this function and instead trigger other actions in the UI, such as opening + * a custom {@link WebviewView}. + */ + resolveDebugVisualization?(visualization: T, token: CancellationToken): ProviderResult; + } + + export interface DebugVisualizationContext { + /** + * The Debug Adapter Protocol Variable to be visualized. + * @see https://microsoft.github.io/debug-adapter-protocol/specification#Types_Variable + */ + variable: any; + /** + * The Debug Adapter Protocol variable reference the type (such as a scope + * or another variable) that contained this one. Empty for variables + * that came from user evaluations in the Debug Console. + * @see https://microsoft.github.io/debug-adapter-protocol/specification#Types_Variable + */ + containerId?: string; + /** + * The ID of the Debug Adapter Protocol StackFrame in which the variable was found, + * for variables that came from scopes in a stack frame. + * @see https://microsoft.github.io/debug-adapter-protocol/specification#Types_StackFrame + */ + frameId?: number; + /** + * The ID of the Debug Adapter Protocol Thread in which the variable was found. + * @see https://microsoft.github.io/debug-adapter-protocol/specification#Types_StackFrame + */ + threadId: number; + /** + * The debug session the variable belongs to. + */ + session: DebugSession; + } +} From 271fb7fbd599b49a1482ea7284a50b3229317f96 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Thu, 18 Jan 2024 15:48:15 -0800 Subject: [PATCH 138/333] dont ask for all children for large collections (#202784) * limit number of children retreived * share const for page size --- src/vs/workbench/api/common/extHostNotebookKernels.ts | 8 +++++++- .../notebookVariables/notebookVariablesDataSource.ts | 10 +++++----- .../contrib/notebook/common/notebookKernelService.ts | 2 ++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index a83caeb5c84fd..8a087a434e7be 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -25,6 +25,7 @@ import { CellExecutionUpdateType } from 'vs/workbench/contrib/notebook/common/no import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import * as vscode from 'vscode'; +import { variablePageSize } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; interface IKernelData { extensionId: ExtensionIdentifier; @@ -431,7 +432,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { } let parent: vscode.Variable | undefined = undefined; - if (parentId) { + if (parentId !== undefined) { parent = this.variableStore[parentId]; if (!parent) { // request for unknown parent @@ -446,6 +447,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { const requestKind = kind === 'named' ? NotebookVariablesRequestKind.Named : NotebookVariablesRequestKind.Indexed; const variableResults = variableProvider.provideVariables(document.apiNotebook, parent, requestKind, start, token); + let resultCount = 0; for await (const result of variableResults) { if (token.isCancellationRequested) { return; @@ -459,6 +461,10 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { }; this.variableStore[variable.id] = result.variable; this._proxy.$receiveVariable(requestId, variable); + + if (resultCount++ >= variablePageSize) { + return; + } } } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts index 70511741f6598..5a61cec80ffe2 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts @@ -6,7 +6,7 @@ import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { CancellationToken } from 'vs/base/common/cancellation'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { INotebookKernel, INotebookKernelService, VariablesResult } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService, VariablesResult, variablePageSize } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; export interface INotebookScope { type: 'root'; @@ -65,9 +65,9 @@ export class NotebookVariableDataSource implements IAsyncDataSource 100) { - for (let start = 0; start < parent.indexedChildrenCount; start += 100) { - let end = start + 100; + if (parent.indexedChildrenCount > variablePageSize) { + for (let start = 0; start < parent.indexedChildrenCount; start += variablePageSize) { + let end = start + variablePageSize; if (end > parent.indexedChildrenCount) { end = parent.indexedChildrenCount; } @@ -89,7 +89,7 @@ export class NotebookVariableDataSource implements IAsyncDataSource= 100) { + if (childNodes.length >= variablePageSize) { break; } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index 212fe0ef0babd..ca3e3f7bf3aac 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -46,6 +46,8 @@ export interface VariablesResult { indexedChildrenCount: number; } +export const variablePageSize = 100; + export interface INotebookKernel { readonly id: string; readonly viewType: string; From e5bf9dac9048c0517bc1f6f15fc4f0fdeda63cdc Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Fri, 19 Jan 2024 09:18:28 +0100 Subject: [PATCH 139/333] fix error --- .../electron-sandbox/desktop.contribution.ts | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index f9c5c09c16dd6..19549fc876aff 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -27,7 +27,6 @@ import { ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle import { NativeWindow } from 'vs/workbench/electron-sandbox/window'; import { ModifierKeyEmitter } from 'vs/base/browser/dom'; import { applicationConfigurationNodeBase, securityConfigurationNodeBase } from 'vs/workbench/common/configuration'; -import { CustomTitleBarVisibility, TitleBarSetting, TitlebarStyle } from 'vs/platform/window/common/window'; import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sandbox/window'; // Actions @@ -229,24 +228,24 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand 'scope': ConfigurationScope.APPLICATION, 'markdownDescription': localize('window.doubleClickIconToClose', "If enabled, this setting will close the window when the application icon in the title bar is double-clicked. The window will not be able to be dragged by the icon. This setting is effective only if `#window.titleBarStyle#` is set to `custom`.") }, - [TitleBarSetting.TITLE_BAR_STYLE]: { + 'window.titleBarStyle': { 'type': 'string', - 'enum': [TitlebarStyle.NATIVE, TitlebarStyle.CUSTOM], - 'default': isLinux ? TitlebarStyle.NATIVE : TitlebarStyle.CUSTOM, + 'enum': ['native', 'custom'], + 'default': isLinux ? 'native' : 'custom', 'scope': ConfigurationScope.APPLICATION, 'description': localize('titleBarStyle', "Adjust the appearance of the window title bar to be native by the OS or custom. On Linux and Windows, this setting also affects the application and context menu appearances. Changes require a full restart to apply.") }, - [TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY]: { + 'window.customTitleBarVisibility': { 'type': 'string', - 'enum': [CustomTitleBarVisibility.AUTO, CustomTitleBarVisibility.WINDOWED, CustomTitleBarVisibility.NEVER], + 'enum': ['auto', 'windowed', 'never'], 'markdownEnumDescriptions': [ - localize(`${TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY}.${CustomTitleBarVisibility.AUTO}`, "Automatically changes custom titlebar visibility."), - localize(`${TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY}.${CustomTitleBarVisibility.WINDOWED}`, "Hide custom titlebar in full screen. Automatically changes custom titlebar visibility in windowed."), - localize(`${TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY}.${CustomTitleBarVisibility.NEVER}`, "Hide custom titlebar when `#window.titleBarStyle#` is set to `native`."), + localize(`window.customTitleBarVisibility.auto`, "Automatically changes custom titlebar visibility."), + localize(`window.customTitleBarVisibility.windowed`, "Hide custom titlebar in full screen. Automatically changes custom titlebar visibility in windowed."), + localize(`window.customTitleBarVisibility.never`, "Hide custom titlebar when `#window.titleBarStyle#` is set to `native`."), ], - 'default': isLinux ? CustomTitleBarVisibility.NEVER : CustomTitleBarVisibility.AUTO, + 'default': isLinux ? 'never' : 'auto', 'scope': ConfigurationScope.APPLICATION, - 'description': localize(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, "Adjust when the custom title bar should be shown."), + 'description': localize('window.customTitleBarVisibility', "Adjust when the custom title bar should be shown."), }, 'window.dialogStyle': { 'type': 'string', From 77f94e28d21ebd043e76181aae9cce456777436e Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 19 Jan 2024 10:23:19 +0100 Subject: [PATCH 140/333] comments.contribution.ts should only be included once (#202803) --- .../browser/accessibility.contribution.ts | 2 +- .../browser/editorAccessibilityHelp.ts | 2 +- .../comments/browser/comments.contribution.ts | 78 +---------------- .../comments/browser/commentsAccessibility.ts | 86 +++++++++++++++++++ 4 files changed, 89 insertions(+), 79 deletions(-) create mode 100644 src/vs/workbench/contrib/comments/browser/commentsAccessibility.ts diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts index ac3d45e7a7094..ffdf8d006763a 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts @@ -12,9 +12,9 @@ import { IAccessibleViewService, AccessibleViewService } from 'vs/workbench/cont import { UnfocusedViewDimmingContribution } from 'vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution'; import { HoverAccessibleViewContribution, InlineCompletionsAccessibleViewContribution, NotificationAccessibleViewContribution } from 'vs/workbench/contrib/accessibility/browser/accessibilityContributions'; import { AccessibilityStatus } from 'vs/workbench/contrib/accessibility/browser/accessibilityStatus'; -import { CommentsAccessibilityHelpContribution } from 'vs/workbench/contrib/comments/browser/comments.contribution'; import { EditorAccessibilityHelpContribution } from 'vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp'; import { SaveAudioCueContribution } from 'vs/workbench/contrib/accessibility/browser/saveAudioCue'; +import { CommentsAccessibilityHelpContribution } from 'vs/workbench/contrib/comments/browser/commentsAccessibility'; registerAccessibilityConfiguration(); registerSingleton(IAccessibleViewService, AccessibleViewService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts b/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts index d4bde61062fca..2eafab6402d76 100644 --- a/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts @@ -18,7 +18,7 @@ import { AccessibleViewProviderId, AccessibilityVerbositySettingId } from 'vs/wo import { descriptionForCommand } from 'vs/workbench/contrib/accessibility/browser/accessibilityContributions'; import { IAccessibleViewService, IAccessibleContentProvider, IAccessibleViewOptions, AccessibleViewType } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; -import { CommentAccessibilityHelpNLS } from 'vs/workbench/contrib/comments/browser/comments.contribution'; +import { CommentAccessibilityHelpNLS } from 'vs/workbench/contrib/comments/browser/commentsAccessibility'; import { CommentCommandId } from 'vs/workbench/contrib/comments/common/commentCommandIds'; import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys'; import { NEW_UNTITLED_FILE_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileConstants'; diff --git a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts index eb7c912fb6d9a..6545fa6c4441a 100644 --- a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts +++ b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts @@ -9,19 +9,9 @@ import { Registry } from 'vs/platform/registry/common/platform'; import 'vs/workbench/contrib/comments/browser/commentsEditorContribution'; import { ICommentService, CommentService, IWorkspaceCommentThreadsEvent } from 'vs/workbench/contrib/comments/browser/commentService'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { ctxCommentEditorFocused } from 'vs/workbench/contrib/comments/browser/simpleCommentEditor'; -import * as strings from 'vs/base/common/strings'; -import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; -import { AccessibleViewType, IAccessibleContentProvider, IAccessibleViewOptions, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; -import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { Disposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys'; -import { CommentCommandId } from 'vs/workbench/contrib/comments/common/commentCommandIds'; -import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode'; -import { getActiveElement } from 'vs/base/browser/dom'; import { Extensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { COMMENTS_VIEW_ID } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; @@ -129,72 +119,6 @@ Registry.as(ConfigurationExtensions.Configuration).regis registerSingleton(ICommentService, CommentService, InstantiationType.Delayed); -export namespace CommentAccessibilityHelpNLS { - export const intro = nls.localize('intro', "The editor contains commentable range(s). Some useful commands include:"); - export const introWidget = nls.localize('introWidget', "This widget contains a text area, for composition of new comments, and actions, that can be tabbed to once tab moves focus mode has been enabled ({0})."); - export const introWidgetNoKb = nls.localize('introWidgetNoKb', "This widget contains a text area, for composition of new comments, and actions, that can be tabbed to once tab moves focus mode has been enabled with the command Toggle Tab Key Moves Focus, which is currently not triggerable via keybinding."); - export const commentCommands = nls.localize('commentCommands', "Some useful comment commands include:"); - export const escape = nls.localize('escape', "- Dismiss Comment (Escape)"); - export const nextRange = nls.localize('next', "- Go to Next Commenting Range ({0})"); - export const nextRangeNoKb = nls.localize('nextNoKb', "- Go to Next Commenting Range, which is currently not triggerable via keybinding."); - export const previousRange = nls.localize('previous', "- Go to Previous Commenting Range ({0})"); - export const previousRangeNoKb = nls.localize('previousNoKb', "- Go to Previous Commenting Range, which is currently not triggerable via keybinding."); - export const nextCommentThreadKb = nls.localize('nextCommentThreadKb', "- Go to Next Comment Thread ({0})"); - export const nextCommentThreadNoKb = nls.localize('nextCommentThreadNoKb', "- Go to Next Comment Thread, which is currently not triggerable via keybinding."); - export const previousCommentThreadKb = nls.localize('previousCommentThreadKb', "- Go to Previous Comment Thread ({0})"); - export const previousCommentThreadNoKb = nls.localize('previousCommentThreadNoKb', "- Go to Previous Comment Thread, which is currently not triggerable via keybinding."); - export const addComment = nls.localize('addComment', "- Add Comment ({0})"); - export const addCommentNoKb = nls.localize('addCommentNoKb', "- Add Comment on Current Selection, which is currently not triggerable via keybinding."); - export const submitComment = nls.localize('submitComment', "- Submit Comment ({0})"); - export const submitCommentNoKb = nls.localize('submitCommentNoKb', "- Submit Comment, accessible via tabbing, as it's currently not triggerable with a keybinding."); -} - -export class CommentsAccessibilityHelpContribution extends Disposable { - static ID: 'commentsAccessibilityHelpContribution'; - constructor() { - super(); - this._register(AccessibilityHelpAction.addImplementation(110, 'comments', accessor => { - const instantiationService = accessor.get(IInstantiationService); - const accessibleViewService = accessor.get(IAccessibleViewService); - accessibleViewService.show(instantiationService.createInstance(CommentsAccessibilityHelpProvider)); - return true; - }, ContextKeyExpr.or(ctxCommentEditorFocused, CommentContextKeys.commentFocused))); - } -} -export class CommentsAccessibilityHelpProvider implements IAccessibleContentProvider { - id = AccessibleViewProviderId.Comments; - verbositySettingKey: AccessibilityVerbositySettingId = AccessibilityVerbositySettingId.Comments; - options: IAccessibleViewOptions = { type: AccessibleViewType.Help }; - private _element: HTMLElement | undefined; - constructor( - @IKeybindingService private readonly _keybindingService: IKeybindingService - ) { - - } - private _descriptionForCommand(commandId: string, msg: string, noKbMsg: string): string { - const kb = this._keybindingService.lookupKeybinding(commandId); - if (kb) { - return strings.format(msg, kb.getAriaLabel()); - } - return strings.format(noKbMsg, commandId); - } - provideContent(): string { - this._element = getActiveElement() as HTMLElement; - const content: string[] = []; - content.push(this._descriptionForCommand(ToggleTabFocusModeAction.ID, CommentAccessibilityHelpNLS.introWidget, CommentAccessibilityHelpNLS.introWidgetNoKb) + '\n'); - content.push(CommentAccessibilityHelpNLS.commentCommands); - content.push(CommentAccessibilityHelpNLS.escape); - content.push(this._descriptionForCommand(CommentCommandId.Add, CommentAccessibilityHelpNLS.addComment, CommentAccessibilityHelpNLS.addCommentNoKb)); - content.push(this._descriptionForCommand(CommentCommandId.Submit, CommentAccessibilityHelpNLS.submitComment, CommentAccessibilityHelpNLS.submitCommentNoKb)); - content.push(this._descriptionForCommand(CommentCommandId.NextRange, CommentAccessibilityHelpNLS.nextRange, CommentAccessibilityHelpNLS.nextRangeNoKb)); - content.push(this._descriptionForCommand(CommentCommandId.PreviousRange, CommentAccessibilityHelpNLS.previousRange, CommentAccessibilityHelpNLS.previousRangeNoKb)); - return content.join('\n'); - } - onClose(): void { - this._element?.focus(); - } -} - export class UnresolvedCommentsBadge extends Disposable implements IWorkbenchContribution { private readonly activity = this._register(new MutableDisposable()); private totalUnresolved = 0; diff --git a/src/vs/workbench/contrib/comments/browser/commentsAccessibility.ts b/src/vs/workbench/contrib/comments/browser/commentsAccessibility.ts new file mode 100644 index 0000000000000..27ba7d7e66379 --- /dev/null +++ b/src/vs/workbench/contrib/comments/browser/commentsAccessibility.ts @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { AccessibleViewType, IAccessibleContentProvider, IAccessibleViewOptions, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; +import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; +import { ctxCommentEditorFocused } from 'vs/workbench/contrib/comments/browser/simpleCommentEditor'; +import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys'; +import * as nls from 'vs/nls'; +import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import * as strings from 'vs/base/common/strings'; +import { getActiveElement } from 'vs/base/browser/dom'; +import { CommentCommandId } from 'vs/workbench/contrib/comments/common/commentCommandIds'; +import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode'; + +export namespace CommentAccessibilityHelpNLS { + export const intro = nls.localize('intro', "The editor contains commentable range(s). Some useful commands include:"); + export const introWidget = nls.localize('introWidget', "This widget contains a text area, for composition of new comments, and actions, that can be tabbed to once tab moves focus mode has been enabled ({0})."); + export const introWidgetNoKb = nls.localize('introWidgetNoKb', "This widget contains a text area, for composition of new comments, and actions, that can be tabbed to once tab moves focus mode has been enabled with the command Toggle Tab Key Moves Focus, which is currently not triggerable via keybinding."); + export const commentCommands = nls.localize('commentCommands', "Some useful comment commands include:"); + export const escape = nls.localize('escape', "- Dismiss Comment (Escape)"); + export const nextRange = nls.localize('next', "- Go to Next Commenting Range ({0})"); + export const nextRangeNoKb = nls.localize('nextNoKb', "- Go to Next Commenting Range, which is currently not triggerable via keybinding."); + export const previousRange = nls.localize('previous', "- Go to Previous Commenting Range ({0})"); + export const previousRangeNoKb = nls.localize('previousNoKb', "- Go to Previous Commenting Range, which is currently not triggerable via keybinding."); + export const nextCommentThreadKb = nls.localize('nextCommentThreadKb', "- Go to Next Comment Thread ({0})"); + export const nextCommentThreadNoKb = nls.localize('nextCommentThreadNoKb', "- Go to Next Comment Thread, which is currently not triggerable via keybinding."); + export const previousCommentThreadKb = nls.localize('previousCommentThreadKb', "- Go to Previous Comment Thread ({0})"); + export const previousCommentThreadNoKb = nls.localize('previousCommentThreadNoKb', "- Go to Previous Comment Thread, which is currently not triggerable via keybinding."); + export const addComment = nls.localize('addComment', "- Add Comment ({0})"); + export const addCommentNoKb = nls.localize('addCommentNoKb', "- Add Comment on Current Selection, which is currently not triggerable via keybinding."); + export const submitComment = nls.localize('submitComment', "- Submit Comment ({0})"); + export const submitCommentNoKb = nls.localize('submitCommentNoKb', "- Submit Comment, accessible via tabbing, as it's currently not triggerable with a keybinding."); +} + +export class CommentsAccessibilityHelpProvider implements IAccessibleContentProvider { + id = AccessibleViewProviderId.Comments; + verbositySettingKey: AccessibilityVerbositySettingId = AccessibilityVerbositySettingId.Comments; + options: IAccessibleViewOptions = { type: AccessibleViewType.Help }; + private _element: HTMLElement | undefined; + constructor( + @IKeybindingService private readonly _keybindingService: IKeybindingService + ) { + + } + private _descriptionForCommand(commandId: string, msg: string, noKbMsg: string): string { + const kb = this._keybindingService.lookupKeybinding(commandId); + if (kb) { + return strings.format(msg, kb.getAriaLabel()); + } + return strings.format(noKbMsg, commandId); + } + provideContent(): string { + this._element = getActiveElement() as HTMLElement; + const content: string[] = []; + content.push(this._descriptionForCommand(ToggleTabFocusModeAction.ID, CommentAccessibilityHelpNLS.introWidget, CommentAccessibilityHelpNLS.introWidgetNoKb) + '\n'); + content.push(CommentAccessibilityHelpNLS.commentCommands); + content.push(CommentAccessibilityHelpNLS.escape); + content.push(this._descriptionForCommand(CommentCommandId.Add, CommentAccessibilityHelpNLS.addComment, CommentAccessibilityHelpNLS.addCommentNoKb)); + content.push(this._descriptionForCommand(CommentCommandId.Submit, CommentAccessibilityHelpNLS.submitComment, CommentAccessibilityHelpNLS.submitCommentNoKb)); + content.push(this._descriptionForCommand(CommentCommandId.NextRange, CommentAccessibilityHelpNLS.nextRange, CommentAccessibilityHelpNLS.nextRangeNoKb)); + content.push(this._descriptionForCommand(CommentCommandId.PreviousRange, CommentAccessibilityHelpNLS.previousRange, CommentAccessibilityHelpNLS.previousRangeNoKb)); + return content.join('\n'); + } + onClose(): void { + this._element?.focus(); + } +} + +export class CommentsAccessibilityHelpContribution extends Disposable { + static ID: 'commentsAccessibilityHelpContribution'; + constructor() { + super(); + this._register(AccessibilityHelpAction.addImplementation(110, 'comments', accessor => { + const instantiationService = accessor.get(IInstantiationService); + const accessibleViewService = accessor.get(IAccessibleViewService); + accessibleViewService.show(instantiationService.createInstance(CommentsAccessibilityHelpProvider)); + return true; + }, ContextKeyExpr.or(ctxCommentEditorFocused, CommentContextKeys.commentFocused))); + } +} From f964dcc08a106d3ace3af2d98bd0ad633bf1583b Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 19 Jan 2024 10:38:51 +0100 Subject: [PATCH 141/333] Fix for 'Can't compress when there are no elements to compress' error (#202804) fix #202800 --- src/vs/base/browser/ui/tree/objectTree.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index a174d01151186..894f74ce92191 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -189,6 +189,10 @@ class CompressibleStickyScrollDelegate implements IStickyScrollD private compressStickyNodes(stickyNodes: StickyScrollNode[]): StickyScrollNode { + if (stickyNodes.length === 0) { + throw new Error('Can\'t compress empty sticky nodes'); + } + if (!this.modelProvider().isCompressionEnabled()) { return stickyNodes[0]; } @@ -206,11 +210,7 @@ class CompressibleStickyScrollDelegate implements IStickyScrollD } } - if (elements.length === 0) { - throw new Error('Can\'t compress when there are no elements to compress'); - } - - if (elements.length === 1) { + if (elements.length < 2) { return stickyNodes[0]; } From 3b9f4a340c4433b4cff3635d53ad20125eb028e9 Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 19 Jan 2024 10:53:53 +0100 Subject: [PATCH 142/333] Fix for incorrect file icon margins with compact pinned tabs and left tab action location (#202808) fix #202505 --- .../browser/parts/editor/media/multieditortabscontrol.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css index 9d470d6c1606a..c826a4f8725cf 100644 --- a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css @@ -228,7 +228,7 @@ pointer-events: none; /* prevents cursor flickering (fixes https://github.com/microsoft/vscode/issues/38753) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-left { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-left:not(.sticky-compact) { flex-direction: row-reverse; padding-left: 0; padding-right: 10px; From 0287cb9fe78a006e02488892182b0791cfe8988b Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 19 Jan 2024 11:25:59 +0100 Subject: [PATCH 143/333] Git - add fetch, pull, push commands to incoming/outgoing (#202809) --- extensions/git/package.json | 73 +++++++++++++++++++++++---- extensions/git/src/commands.ts | 29 +++++++++++ extensions/git/src/historyProvider.ts | 30 +++++------ 3 files changed, 107 insertions(+), 25 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 0a9c690455568..e909d5f503049 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -10,25 +10,26 @@ }, "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "enabledApiProposals": [ - "diffCommand", - "contribEditorContentMenu", - "contribEditSessions", "canonicalUriProvider", + "contribEditSessions", + "contribEditorContentMenu", + "contribMergeEditorMenus", + "contribMultiDiffEditorMenus", + "contribSourceControlHistoryItemGroupMenu", + "contribSourceControlHistoryItemMenu", + "contribSourceControlInputBoxMenu", "contribViewsWelcome", + "diffCommand", "editSessionIdentityProvider", "quickDiffProvider", + "quickPickSortByLabel", "scmActionButton", "scmHistoryProvider", + "scmMultiDiffEditor", "scmSelectedProvider", "scmValidation", - "scmMultiDiffEditor", "tabInputTextMerge", - "timeline", - "contribMergeEditorMenus", - "contribMultiDiffEditorMenus", - "contribSourceControlInputBoxMenu", - "quickPickSortByLabel", - "contribSourceControlHistoryItemMenu" + "timeline" ], "categories": [ "Other" @@ -511,6 +512,13 @@ "category": "Git", "enablement": "!operationInProgress" }, + { + "command": "git.fetchRef", + "title": "%command.fetch%", + "icon": "$(git-fetch)", + "category": "Git", + "enablement": "!operationInProgress" + }, { "command": "git.pull", "title": "%command.pull%", @@ -529,6 +537,13 @@ "category": "Git", "enablement": "!operationInProgress" }, + { + "command": "git.pullRef", + "title": "%command.pull%", + "icon": "$(repo-pull)", + "category": "Git", + "enablement": "!operationInProgress" + }, { "command": "git.push", "title": "%command.push%", @@ -571,6 +586,13 @@ "category": "Git", "enablement": "!operationInProgress" }, + { + "command": "git.pushRef", + "title": "%command.push%", + "icon": "$(repo-push)", + "category": "Git", + "enablement": "!operationInProgress" + }, { "command": "git.cherryPick", "title": "%command.cherryPick%", @@ -1335,6 +1357,18 @@ { "command": "git.unstageFile", "when": "false" + }, + { + "command": "git.fetchRef", + "when": "false" + }, + { + "command": "git.pullRef", + "when": "false" + }, + { + "command": "git.pushRef", + "when": "false" } ], "scm/title": [ @@ -1802,6 +1836,18 @@ "group": "1_modification@3" } ], + "scm/incomingChanges": [ + { + "command": "git.fetchRef", + "group": "navigation", + "when": "scmProvider == git" + }, + { + "command": "git.pullRef", + "group": "navigation", + "when": "scmProvider == git" + } + ], "scm/incomingChanges/allChanges/context": [ { "command": "git.viewAllChanges", @@ -1816,6 +1862,13 @@ "group": "inline@1" } ], + "scm/outgoingChanges": [ + { + "command": "git.pushRef", + "group": "navigation", + "when": "scmProvider == git" + } + ], "scm/outgoingChanges/allChanges/context": [ { "command": "git.viewAllChanges", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 81abbd94aa049..6a15bd94455f5 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2961,6 +2961,16 @@ export class CommandCenter { await repository.fetchAll(); } + @command('git.fetchRef', { repository: true }) + async fetchRef(repository: Repository, ref: string): Promise { + if (!repository || !ref) { + return; + } + + const branch = await repository.getBranch(ref); + await repository.fetch({ remote: branch.remote, ref: branch.name }); + } + @command('git.pullFrom', { repository: true }) async pullFrom(repository: Repository): Promise { const remotes = repository.remotes; @@ -3023,6 +3033,16 @@ export class CommandCenter { await repository.pullWithRebase(repository.HEAD); } + @command('git.pullRef', { repository: true }) + async pullRef(repository: Repository, ref: string): Promise { + if (!repository || !ref) { + return; + } + + const branch = await repository.getBranch(ref); + await repository.pullFrom(false, branch.remote, branch.name); + } + private async _push(repository: Repository, pushOptions: PushOptions) { const remotes = repository.remotes; @@ -3160,6 +3180,15 @@ export class CommandCenter { await this._push(repository, { pushType: PushType.PushFollowTags, forcePush: true }); } + @command('git.pushRef', { repository: true }) + async pushRef(repository: Repository, ref: string): Promise { + if (!repository || !ref) { + return; + } + + await this._push(repository, { pushType: PushType.Push }); + } + @command('git.cherryPick', { repository: true }) async cherryPick(repository: Repository): Promise { const hash = await window.showInputBox({ diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index 9560aef5e8ea2..5d395c502c192 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -191,21 +191,21 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec return HEAD.upstream; } - try { - const remoteBranch = await this.repository.getBranchBase(HEAD.name ?? ''); - if (!remoteBranch?.remote || !remoteBranch?.name || !remoteBranch?.commit || remoteBranch?.type !== RefType.RemoteHead) { - return undefined; - } - - return { - name: remoteBranch.name, - remote: remoteBranch.remote, - commit: remoteBranch.commit - }; - } - catch (err) { - this.logger.error(`Failed to get branch base for '${HEAD.name}': ${err.message}`); - } + // try { + // const remoteBranch = await this.repository.getBranchBase(HEAD.name ?? ''); + // if (!remoteBranch?.remote || !remoteBranch?.name || !remoteBranch?.commit || remoteBranch?.type !== RefType.RemoteHead) { + // return undefined; + // } + + // return { + // name: remoteBranch.name, + // remote: remoteBranch.remote, + // commit: remoteBranch.commit + // }; + // } + // catch (err) { + // this.logger.error(`Failed to get branch base for '${HEAD.name}': ${err.message}`); + // } return undefined; } From 99a2fd53bd4a8cfd9eed7751c8b269af005eec3d Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 18 Jan 2024 17:12:01 +0100 Subject: [PATCH 144/333] Adds substring --- src/vs/editor/common/core/offsetRange.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/editor/common/core/offsetRange.ts b/src/vs/editor/common/core/offsetRange.ts index e35a27c56bc70..17857dd9488ca 100644 --- a/src/vs/editor/common/core/offsetRange.ts +++ b/src/vs/editor/common/core/offsetRange.ts @@ -136,6 +136,10 @@ export class OffsetRange implements IOffsetRange { return arr.slice(this.start, this.endExclusive); } + public substring(str: string): string { + return str.substring(this.start, this.endExclusive); + } + /** * Returns the given value if it is contained in this instance, otherwise the closest value that is contained. * The range must not be empty. From 600e284b848cfd9cb3b94552611c309a0645d551 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 19 Jan 2024 12:47:12 +0100 Subject: [PATCH 145/333] "Voice Keyword Activation" appeared in status bar with latest Insiders update (fix #202838) (#202841) --- .../electron-sandbox/actions/voiceChatActions.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 5874c2c37135c..f83d6f8ff4946 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -766,6 +766,16 @@ registerThemingParticipant((theme, collector) => { `); }); +function supportsKeywordActivation(configurationService: IConfigurationService, speechService: ISpeechService): boolean { + if (!speechService.hasSpeechProvider) { + return false; + } + + const value = configurationService.getValue(KeywordActivationContribution.SETTINGS_ID); + + return typeof value === 'string' && value !== KeywordActivationContribution.SETTINGS_VALUE.OFF; +} + export class KeywordActivationContribution extends Disposable implements IWorkbenchContribution { static SETTINGS_ID = 'accessibility.voice.keywordActivation'; @@ -852,8 +862,7 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe private handleKeywordActivation(): void { const enabled = - this.speechService.hasSpeechProvider && - this.configurationService.getValue(KeywordActivationContribution.SETTINGS_ID) !== KeywordActivationContribution.SETTINGS_VALUE.OFF && + supportsKeywordActivation(this.configurationService, this.speechService) && !this.speechService.hasActiveSpeechToTextSession; if ( (enabled && this.activeSession) || @@ -957,7 +966,7 @@ class KeywordActivationStatusEntry extends Disposable { } private updateStatusEntry(): void { - const visible = this.configurationService.getValue(KeywordActivationContribution.SETTINGS_ID) !== KeywordActivationContribution.SETTINGS_VALUE.OFF; + const visible = supportsKeywordActivation(this.configurationService, this.speechService); if (visible) { if (!this.entry.value) { this.createStatusEntry(); From 945bbd01aa9c90c3dcc6ae51595e834fde5adbad Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 19 Jan 2024 12:56:34 +0100 Subject: [PATCH 146/333] Git - only show "Incoming changes" node for upstream (#202836) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Git - only show "Incoming changes" node for upstream * 💄 Revert some of the changes now that base is limited to upstream * Fixed a bug * 💄 More clean-up --- extensions/git/src/historyProvider.ts | 92 +++++++++---------- src/vs/workbench/api/browser/mainThreadSCM.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostSCM.ts | 2 +- .../contrib/scm/browser/scmViewPane.ts | 18 ++-- .../workbench/contrib/scm/common/history.ts | 2 +- .../vscode.proposed.scmHistoryProvider.d.ts | 2 +- 7 files changed, 57 insertions(+), 63 deletions(-) diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index 5d395c502c192..88dc4e0a668aa 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -12,14 +12,6 @@ import { Branch, RefType, UpstreamRef } from './api/git'; import { emojify, ensureEmojis } from './emoji'; import { Operation } from './operation'; -function isBranchRefEqual(brach1: Branch | undefined, branch2: Branch | undefined): boolean { - return brach1?.name === branch2?.name && brach1?.commit === branch2?.commit; -} - -function isUpstreamRefEqual(upstream1: UpstreamRef | undefined, upstream2: UpstreamRef | undefined): boolean { - return upstream1?.name === upstream2?.name && upstream1?.remote === upstream2?.remote && upstream1?.commit === upstream2?.commit; -} - export class GitHistoryProvider implements SourceControlHistoryProvider, FileDecorationProvider, IDisposable { private readonly _onDidChangeCurrentHistoryItemGroup = new EventEmitter(); @@ -29,15 +21,10 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec readonly onDidChangeFileDecorations: Event = this._onDidChangeDecorations.event; private _HEAD: Branch | undefined; - private _HEADBase: UpstreamRef | undefined; private _currentHistoryItemGroup: SourceControlHistoryItemGroup | undefined; get currentHistoryItemGroup(): SourceControlHistoryItemGroup | undefined { return this._currentHistoryItemGroup; } set currentHistoryItemGroup(value: SourceControlHistoryItemGroup | undefined) { - if (this._currentHistoryItemGroup === undefined && value === undefined) { - return; - } - this._currentHistoryItemGroup = value; this._onDidChangeCurrentHistoryItemGroup.fire(); } @@ -54,31 +41,30 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec } private async onDidRunGitStatus(): Promise { - // Check if HEAD does not support incoming/outgoing (detached commit, tag) - if (!this.repository.HEAD?.name || !this.repository.HEAD?.commit || this.repository.HEAD.type === RefType.Tag) { - this._HEAD = this._HEADBase = undefined; - this.currentHistoryItemGroup = undefined; + // Check if HEAD has changed + if (this._HEAD?.name === this.repository.HEAD?.name && + this._HEAD?.commit === this.repository.HEAD?.commit && + this._HEAD?.upstream?.name === this.repository.HEAD?.upstream?.name && + this._HEAD?.upstream?.remote === this.repository.HEAD?.upstream?.remote && + this._HEAD?.upstream?.commit === this.repository.HEAD?.upstream?.commit) { return; } - // Resolve HEAD base - const HEADBase = await this.resolveHEADBase(this.repository.HEAD); + this._HEAD = this.repository.HEAD; - // Check if HEAD or HEADBase has changed - if (isBranchRefEqual(this._HEAD, this.repository.HEAD) && isUpstreamRefEqual(this._HEADBase, HEADBase)) { + // Check if HEAD does not support incoming/outgoing (detached commit, tag) + if (!this._HEAD?.name || !this._HEAD?.commit || this._HEAD.type === RefType.Tag) { + this.currentHistoryItemGroup = undefined; return; } - this._HEAD = this.repository.HEAD; - this._HEADBase = HEADBase; - this.currentHistoryItemGroup = { id: `refs/heads/${this._HEAD.name ?? ''}`, label: this._HEAD.name ?? '', - base: this._HEADBase ? + base: this._HEAD.upstream ? { - id: `refs/remotes/${this._HEADBase.remote}/${this._HEADBase.name}`, - label: `${this._HEADBase.remote}/${this._HEADBase.name}`, + id: `refs/remotes/${this._HEAD.upstream.remote}/${this._HEAD.upstream.name}`, + label: `${this._HEAD.upstream.remote}/${this._HEAD.upstream.name}`, } : undefined }; } @@ -165,17 +151,26 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec return historyItemChanges; } - async resolveHistoryItemGroupCommonAncestor(refId1: string, refId2: string): Promise<{ id: string; ahead: number; behind: number } | undefined> { - const ancestor = await this.repository.getMergeBase(refId1, refId2); + async resolveHistoryItemGroupCommonAncestor(historyItemId1: string, historyItemId2: string | undefined): Promise<{ id: string; ahead: number; behind: number } | undefined> { + if (!historyItemId2) { + const upstreamRef = await this.resolveHistoryItemGroupBase(historyItemId1); + if (!upstreamRef) { + return undefined; + } + + historyItemId2 = `refs/remotes/${upstreamRef.remote}/${upstreamRef.name}`; + } + + const ancestor = await this.repository.getMergeBase(historyItemId1, historyItemId2); if (!ancestor) { return undefined; } try { - const commitCount = await this.repository.getCommitCount(`${refId1}...${refId2}`); + const commitCount = await this.repository.getCommitCount(`${historyItemId1}...${historyItemId2}`); return { id: ancestor, ahead: commitCount.ahead, behind: commitCount.behind }; } catch (err) { - this.logger.error(`Failed to get ahead/behind for '${refId1}...${refId2}': ${err.message}`); + this.logger.error(`Failed to get ahead/behind for '${historyItemId1}...${historyItemId2}': ${err.message}`); } return undefined; @@ -185,27 +180,22 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec return this.historyItemDecorations.get(uri.toString()); } - private async resolveHEADBase(HEAD: Branch): Promise { - // Upstream - if (HEAD.upstream) { - return HEAD.upstream; - } + private async resolveHistoryItemGroupBase(historyItemId: string): Promise { + try { + const remoteBranch = await this.repository.getBranchBase(historyItemId); + if (!remoteBranch?.remote || !remoteBranch?.name || !remoteBranch?.commit || remoteBranch?.type !== RefType.RemoteHead) { + return undefined; + } - // try { - // const remoteBranch = await this.repository.getBranchBase(HEAD.name ?? ''); - // if (!remoteBranch?.remote || !remoteBranch?.name || !remoteBranch?.commit || remoteBranch?.type !== RefType.RemoteHead) { - // return undefined; - // } - - // return { - // name: remoteBranch.name, - // remote: remoteBranch.remote, - // commit: remoteBranch.commit - // }; - // } - // catch (err) { - // this.logger.error(`Failed to get branch base for '${HEAD.name}': ${err.message}`); - // } + return { + name: remoteBranch.name, + remote: remoteBranch.remote, + commit: remoteBranch.commit + }; + } + catch (err) { + this.logger.error(`Failed to get branch base for '${historyItemId}': ${err.message}`); + } return undefined; } diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 2c82aa6e2ada1..0ae7ac97e961a 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -140,7 +140,7 @@ class MainThreadSCMHistoryProvider implements ISCMHistoryProvider { constructor(private readonly proxy: ExtHostSCMShape, private readonly handle: number) { } - async resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string): Promise<{ id: string; ahead: number; behind: number } | undefined> { + async resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string | undefined): Promise<{ id: string; ahead: number; behind: number } | undefined> { return this.proxy.$resolveHistoryItemGroupCommonAncestor(this.handle, historyItemGroupId1, historyItemGroupId2, CancellationToken.None); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 21261315689cc..68ac001fd367e 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -2239,7 +2239,7 @@ export interface ExtHostSCMShape { $provideHistoryItems(sourceControlHandle: number, historyItemGroupId: string, options: any, token: CancellationToken): Promise; $provideHistoryItemSummary(sourceControlHandle: number, historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): Promise; $provideHistoryItemChanges(sourceControlHandle: number, historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): Promise; - $resolveHistoryItemGroupCommonAncestor(sourceControlHandle: number, historyItemGroupId1: string, historyItemGroupId2: string, token: CancellationToken): Promise<{ id: string; ahead: number; behind: number } | undefined>; + $resolveHistoryItemGroupCommonAncestor(sourceControlHandle: number, historyItemGroupId1: string, historyItemGroupId2: string | undefined, token: CancellationToken): Promise<{ id: string; ahead: number; behind: number } | undefined>; } export interface ExtHostQuickDiffShape { diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index da1e67ab60440..ee2be0b1bcc24 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -960,7 +960,7 @@ export class ExtHostSCM implements ExtHostSCMShape { return Promise.resolve(undefined); } - async $resolveHistoryItemGroupCommonAncestor(sourceControlHandle: number, historyItemGroupId1: string, historyItemGroupId2: string, token: CancellationToken): Promise<{ id: string; ahead: number; behind: number } | undefined> { + async $resolveHistoryItemGroupCommonAncestor(sourceControlHandle: number, historyItemGroupId1: string, historyItemGroupId2: string | undefined, token: CancellationToken): Promise<{ id: string; ahead: number; behind: number } | undefined> { const historyProvider = this._sourceControls.get(sourceControlHandle)?.historyProvider; return await historyProvider?.resolveHistoryItemGroupCommonAncestor(historyItemGroupId1, historyItemGroupId2, token) ?? undefined; } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 7938ec4815663..7ce51f64928df 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -3291,7 +3291,7 @@ class SCMTreeDataSource implements IAsyncDataSource> { - const { alwaysShowRepositories, showActionButton, showIncomingChanges, showOutgoingChanges } = this.getConfiguration(); + const { alwaysShowRepositories, showActionButton } = this.getConfiguration(); const repositoryCount = this.scmViewService.visibleRepositories.length; if (isSCMViewService(inputOrElement) && (repositoryCount > 1 || alwaysShowRepositories)) { @@ -3331,10 +3331,13 @@ class SCMTreeDataSource implements IAsyncDataSource g.direction === 'incoming'); + const outgoingHistoryItems = historyItemGroups.find(g => g.direction === 'outgoing'); + + if (incomingHistoryItems && !outgoingHistoryItems) { label = localize('syncIncomingSeparatorHeader', "Incoming"); ariaLabel = localize('syncIncomingSeparatorHeaderAriaLabel', "Incoming changes"); - } else if (showIncomingChanges === 'never' && showOutgoingChanges !== 'never') { + } else if (!incomingHistoryItems && outgoingHistoryItems) { label = localize('syncOutgoingSeparatorHeader', "Outgoing"); ariaLabel = localize('syncOutgoingSeparatorHeaderAriaLabel', "Outgoing changes"); } @@ -3384,7 +3387,7 @@ class SCMTreeDataSource implements IAsyncDataSource; provideHistoryItemSummary(historyItemId: string, historyItemParentId: string | undefined): Promise; provideHistoryItemChanges(historyItemId: string, historyItemParentId: string | undefined): Promise; - resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string): Promise<{ id: string; ahead: number; behind: number } | undefined>; + resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string | undefined): Promise<{ id: string; ahead: number; behind: number } | undefined>; } export interface ISCMHistoryProviderCacheEntry { diff --git a/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts b/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts index 24f4dcd8453a6..3f1509d0cc6f4 100644 --- a/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts @@ -28,7 +28,7 @@ declare module 'vscode' { provideHistoryItemSummary?(historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): ProviderResult; provideHistoryItemChanges(historyItemId: string, historyItemParentId: string | undefined, token: CancellationToken): ProviderResult; - resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string, token: CancellationToken): ProviderResult<{ id: string; ahead: number; behind: number }>; + resolveHistoryItemGroupCommonAncestor(historyItemGroupId1: string, historyItemGroupId2: string | undefined, token: CancellationToken): ProviderResult<{ id: string; ahead: number; behind: number }>; } export interface SourceControlHistoryOptions { From 053bbeb47cab4c13f26e93b6f779ee0d58b650c5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 19 Jan 2024 13:14:28 +0100 Subject: [PATCH 147/333] Use hunks also for livePreview and preview mode (#202843) * more test coverage * - Session has textModelN and targetUri where they must not always be equal - for preview mode create textModelN copy so that AI edits can always be applied - let preview mode work with hunk information * - live preview uses hunk information - tweak utils * fix live-mode toggle diff --- .../contrib/inlineChat/browser/inlineChat.css | 18 +- .../browser/inlineChatController.ts | 2 +- .../browser/inlineChatLivePreviewWidget.ts | 44 +- .../browser/inlineChatSavingServiceImpl.ts | 12 +- .../inlineChat/browser/inlineChatSession.ts | 13 + .../browser/inlineChatSessionServiceImpl.ts | 57 ++- .../browser/inlineChatStrategies.ts | 421 +++++++----------- .../inlineChat/browser/inlineChatWidget.ts | 44 +- .../contrib/inlineChat/browser/utils.ts | 88 +++- .../test/browser/inlineChatSession.test.ts | 28 ++ .../test/browser/inlineChatStrategies.test.ts | 2 +- .../view/cellParts/chat/cellChatController.ts | 3 +- 12 files changed, 365 insertions(+), 367 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css index 9f50942dafc1f..ee46f6df25e52 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css @@ -295,28 +295,18 @@ display: none; } -.monaco-editor .inline-chat .previewDiff { +.monaco-editor .inline-chat .previewDiff, +.monaco-editor .inline-chat .previewCreate { display: inherit; - padding: 6px; border: 1px solid var(--vscode-inlineChat-border); - border-top: none; - border-bottom-left-radius: 2px; - border-bottom-right-radius: 2px; - margin: 0 2px 6px 2px; + border-radius: 2px; + margin: 6px 0px; } .monaco-editor .inline-chat .previewCreateTitle { padding-top: 6px; } -.monaco-editor .inline-chat .previewCreate { - display: inherit; - padding: 6px; - border: 1px solid var(--vscode-inlineChat-border); - border-radius: 2px; - margin: 0 2px 6px 2px; -} - .monaco-editor .inline-chat .previewDiff.hidden, .monaco-editor .inline-chat .previewCreate.hidden, .monaco-editor .inline-chat .previewCreateTitle.hidden { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index dc8433eaf9b49..2318b0bdd06e7 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -330,7 +330,7 @@ export class InlineChatController implements IEditorContribution { this._strategy = this._instaService.createInstance(LiveStrategy, session, this._editor, this._zone.value); break; case EditMode.Preview: - this._strategy = this._instaService.createInstance(PreviewStrategy, session, this._zone.value); + this._strategy = this._instaService.createInstance(PreviewStrategy, session, this._editor, this._zone.value); break; case EditMode.LivePreview: default: diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts index f4badff7d4d7b..1af9ecfa2f14c 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts @@ -18,15 +18,14 @@ import * as editorColorRegistry from 'vs/editor/common/core/editorColorRegistry' import { IThemeService } from 'vs/platform/theme/common/themeService'; import { INLINE_CHAT_ID, inlineChatDiffInserted, inlineChatDiffRemoved, inlineChatRegionHighlight } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { LineRange } from 'vs/editor/common/core/lineRange'; -import { LineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { Position } from 'vs/editor/common/core/position'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; import { IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; import { ILogService } from 'vs/platform/log/common/log'; -import { lineRangeAsRange, invertLineRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; +import { invertLineRange, asRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; import { ResourceLabel } from 'vs/workbench/browser/labels'; import { FileKind } from 'vs/platform/files/common/files'; -import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { HunkInformation, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { FoldingController } from 'vs/editor/contrib/folding/browser/folding'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { generateUuid } from 'vs/base/common/uuid'; @@ -170,22 +169,22 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { throw new Error('use showForChanges'); } - showForChanges(changes: readonly LineRangeMapping[]): void { + showForChanges(hunk: HunkInformation): void { const hasFocus = this._diffEditor.hasTextFocus(); this._isVisible = true; - const onlyInserts = changes.every(change => change.original.isEmpty); + const onlyInserts = hunk.isInsertion(); - if (onlyInserts || changes.length === 0 || this._session.textModel0.getValueLength() === 0) { + if (onlyInserts || this._session.textModel0.getValueLength() === 0) { // no change or changes to an empty file this._logService.debug('[IE] livePreview-mode: no diff'); this._cleanupFullDiff(); - this._renderInsertWithHighlight(changes); + this._renderInsertWithHighlight(hunk); } else { // complex changes this._logService.debug('[IE] livePreview-mode: full diff'); this._decorationCollection.clear(); - this._renderChangesWithFullDiff(changes); + this._renderChangesWithFullDiff(hunk); } // TODO@jrieken find a better fix for this. this is the challenge: @@ -197,7 +196,7 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { } } - private _renderInsertWithHighlight(changes: readonly LineRangeMapping[]) { + private _renderInsertWithHighlight(hunk: HunkInformation) { assertType(this.editor.hasModel()); const options: IModelDecorationOptions = { @@ -207,21 +206,18 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { className: 'inline-chat-lines-inserted-range', }; - this._decorationCollection.set(changes.map(change => { - return { - range: lineRangeAsRange(change.modified), - options, - }; - })); + this._decorationCollection.set([{ + range: hunk.getRangesN()[0], + options + }]); } // --- full diff - private _renderChangesWithFullDiff(changes: readonly LineRangeMapping[]) { + private _renderChangesWithFullDiff(hunk: HunkInformation) { assertType(this.editor.hasModel()); - const modified = this.editor.getModel(); - const ranges = this._computeHiddenRanges(modified, changes); + const ranges = this._computeHiddenRanges(this._session.textModelN, hunk); this._hideEditorRanges(this.editor, [ranges.modifiedHidden]); this._hideEditorRanges(this._diffEditor.getOriginalEditor(), ranges.originalDiffHidden); @@ -246,15 +242,11 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { this._isVisible = false; } - private _computeHiddenRanges(model: ITextModel, changes: readonly LineRangeMapping[]) { + private _computeHiddenRanges(model: ITextModel, hunk: HunkInformation) { - 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); - } + const modifiedLineRange = LineRange.fromRangeInclusive(hunk.getRangesN()[0]); + let originalLineRange = LineRange.fromRangeInclusive(hunk.getRanges0()[0]); if (originalLineRange.isEmpty) { originalLineRange = new LineRange(originalLineRange.startLineNumber, originalLineRange.endLineNumberExclusive + 1); } @@ -287,7 +279,7 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { // TODO: not every line can be hidden, keep the first line around hiddenRanges = [editor.getModel().getFullModelRange().delta(1)]; } else { - hiddenRanges = lineRanges.map(lineRangeAsRange); + hiddenRanges = lineRanges.map(lr => asRange(lr, editor.getModel())); } editor.setHiddenAreas(hiddenRanges, this._hideId); this._logService.debug(`[IE] diff HIDING ${hiddenRanges} for ${editor.getId()} with ${String(editor.getModel()?.uri)}`); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts index 55658018f7394..bbd97b97408a7 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts @@ -67,7 +67,7 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { markChanged(session: Session): void { if (!this._sessionData.has(session)) { - let uri = session.textModelN.uri; + let uri = session.targetUri; // notebooks: use the notebook-uri because saving happens on the notebook-level if (uri.scheme === Schemas.vscodeNotebookCell) { @@ -209,7 +209,7 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { break; } - array.sort((a, b) => compare(a.session.textModelN.uri.toString(), b.session.textModelN.uri.toString())); + array.sort((a, b) => compare(a.session.targetUri.toString(), b.session.targetUri.toString())); for (const data of array) { @@ -217,15 +217,15 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { const input: IResourceEditorInput = { resource: data.resourceUri }; const pane = await this._editorService.openEditor(input, group); let editor: ICodeEditor | undefined; - if (data.session.textModelN.uri.scheme === Schemas.vscodeNotebookCell) { + if (data.session.targetUri.scheme === Schemas.vscodeNotebookCell) { const notebookEditor = getNotebookEditorFromEditorPane(pane); - const uriData = CellUri.parse(data.session.textModelN.uri); + const uriData = CellUri.parse(data.session.targetUri); if (notebookEditor && notebookEditor.hasModel() && uriData) { const cell = notebookEditor.getCellByHandle(uriData.handle); if (cell) { await notebookEditor.revealRangeInCenterIfOutsideViewportAsync(cell, data.session.wholeRange.value); } - const tuple = notebookEditor.codeEditors.find(tuple => tuple[1].getModel()?.uri.toString() === data.session.textModelN.uri.toString()); + const tuple = notebookEditor.codeEditors.find(tuple => tuple[1].getModel()?.uri.toString() === data.session.targetUri.toString()); editor = tuple?.[1]; } @@ -240,7 +240,7 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { break; } this._inlineChatSessionService.moveSession(data.session, editor); - this._logService.info('WAIT for session to end', editor.getId(), data.session.textModelN.uri.toString()); + this._logService.info('WAIT for session to end', editor.getId(), data.session.targetUri.toString()); await this._whenSessionsEnded(Iterable.single(data), token); } } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index 63d31a12445d3..fb2cdf8e54ca5 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -142,7 +142,17 @@ export class Session { constructor( readonly editMode: EditMode, + /** + * The URI of the document which is being EditorEdit + */ + readonly targetUri: URI, + /** + * A copy of the document at the time the session was started + */ readonly textModel0: ITextModel, + /** + * The document into which AI edits went, when live this is `targetUri` otherwise it is a temporary document + */ readonly textModelN: ITextModel, readonly provider: IInlineChatSessionProvider, readonly session: IInlineChatSession, @@ -707,6 +717,9 @@ export interface HunkInformation { discardChanges(): void; + /** + * Accept the hunk. Applies the corresponding edits into textModel0 + */ acceptChanges(): void; getState(): HunkState; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts index 57e865322f9a4..acd05d3527d3b 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts @@ -19,6 +19,8 @@ import { raceCancellation } from 'vs/base/common/async'; import { Recording, IInlineChatSessionService, ISessionKeyComputer } from './inlineChatSessionService'; import { HunkData, Session, SessionWholeRange, TelemetryData, TelemetryDataClassification } from './inlineChatSession'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; +import { ITextModel } from 'vs/editor/common/model'; +import { Schemas } from 'vs/base/common/network'; type SessionData = { editor: ICodeEditor; @@ -71,9 +73,9 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { const textModel = editor.getModel(); const selection = editor.getSelection(); - let raw: IInlineChatSession | undefined | null; + let rawSession: IInlineChatSession | undefined | null; try { - raw = await raceCancellation( + rawSession = await raceCancellation( Promise.resolve(provider.prepareInlineChatSession(textModel, selection, token)), token ); @@ -82,7 +84,7 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { this._logService.error(error); return undefined; } - if (!raw) { + if (!rawSession) { this._logService.trace('[IE] NO session', provider.debugName); return undefined; } @@ -91,35 +93,46 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { this._logService.trace(`[IE] creating NEW session for ${editor.getId()}, ${provider.debugName}`); const store = new DisposableStore(); - // create: keep a reference to prevent disposal of the "actual" model - const refTextModelN = await this._textModelService.createModelReference(textModel.uri); - store.add(refTextModelN); + const targetUri = textModel.uri; + + let textModelN: ITextModel; + if (options.editMode === EditMode.Preview) { + // AI edits happen in a copy + textModelN = store.add(this._modelService.createModel( + createTextBufferFactoryFromSnapshot(textModel.createSnapshot()), + { languageId: textModel.getLanguageId(), onDidChange: Event.None }, + targetUri.with({ scheme: Schemas.inMemory, query: 'inline-chat-textModelN' }), true + )); + } else { + // AI edits happen in the actual model, keep a reference but make no copy + store.add((await this._textModelService.createModelReference(textModel.uri))); + textModelN = textModel; + } // create: keep a snapshot of the "actual" model - const textModel0 = this._modelService.createModel( + const textModel0 = store.add(this._modelService.createModel( createTextBufferFactoryFromSnapshot(textModel.createSnapshot()), { languageId: textModel.getLanguageId(), onDidChange: Event.None }, - undefined, true - ); - store.add(textModel0); + targetUri.with({ scheme: Schemas.inMemory, query: 'inline-chat-textModel0' }), true + )); let wholeRange = options.wholeRange; if (!wholeRange) { - wholeRange = raw.wholeRange ? Range.lift(raw.wholeRange) : editor.getSelection(); + wholeRange = rawSession.wholeRange ? Range.lift(rawSession.wholeRange) : editor.getSelection(); } - - // install managed-marker for the decoration range - const wholeRangeMgr = new SessionWholeRange(textModel, wholeRange); - store.add(wholeRangeMgr); - - const hunkData = new HunkData(this._editorWorkerService, textModel0, textModel); - store.add(hunkData); - - const session = new Session(options.editMode, textModel0, textModel, provider, raw, wholeRangeMgr, hunkData); + const session = new Session( + options.editMode, + targetUri, + textModel0, + textModelN, + provider, rawSession, + store.add(new SessionWholeRange(textModelN, wholeRange)), + store.add(new HunkData(this._editorWorkerService, textModel0, textModelN)) + ); // store: key -> session - const key = this._key(editor, textModel.uri); + const key = this._key(editor, session.targetUri); if (this._sessions.has(key)) { store.dispose(); throw new Error(`Session already stored for ${key}`); @@ -129,7 +142,7 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { } moveSession(session: Session, target: ICodeEditor): void { - const newKey = this._key(target, session.textModelN.uri); + const newKey = this._key(target, session.targetUri); const existing = this._sessions.get(newKey); if (existing) { if (existing.session !== session) { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 9ece4b6796725..b7009c9184d33 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -4,12 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { WindowIntervalTimer } from 'vs/base/browser/dom'; -import { coalesceInPlace, equals, tail } from 'vs/base/common/arrays'; -import { AsyncIterableSource, IntervalTimer } from 'vs/base/common/async'; +import { coalesceInPlace } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { Lazy } from 'vs/base/common/lazy'; -import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { themeColorFromId } from 'vs/base/common/themables'; import { ICodeEditor, IViewZone, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll'; @@ -17,27 +16,26 @@ import { LineSource, RenderOptions, renderLines } from 'vs/editor/browser/widget import { EditOperation, ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; -import { IRange, Range } from 'vs/editor/common/core/range'; -import { Selection } from 'vs/editor/common/core/selection'; -import { LineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { Range } from 'vs/editor/common/core/range'; import { IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; -import { TextEdit } from 'vs/editor/common/languages'; -import { ICursorStateComputer, IIdentifiedSingleEditOperation, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel, IValidEditOperation, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel, IValidEditOperation, OverviewRulerLane } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel'; import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IProgress, Progress } from 'vs/platform/progress/common/progress'; +import { Progress } from 'vs/platform/progress/common/progress'; import { SaveReason } from 'vs/workbench/common/editor'; -import { countWords, getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; +import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; import { InlineChatFileCreatePreviewWidget, InlineChatLivePreviewWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget'; import { HunkInformation, ReplyResponse, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { InlineChatZoneWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_DOCUMENT_CHANGED, overviewRulerInlineChatDiffInserted } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { HunkState } from './inlineChatSession'; import { assertType } from 'vs/base/common/types'; +import { IModelService } from 'vs/editor/common/services/model'; +import { performAsyncTextEdit, asProgressiveEdit } from './utils'; export interface IEditObserver { start(): void; @@ -53,9 +51,11 @@ export abstract class EditModeStrategy { className: 'inline-chat-block-selection', }); + protected readonly _store = new DisposableStore(); + protected readonly _onDidAccept = this._store.add(new Emitter()); + protected readonly _onDidDiscard = this._store.add(new Emitter()); - protected readonly _onDidAccept = new Emitter(); - protected readonly _onDidDiscard = new Emitter(); + protected _editCount: number = 0; readonly onDidAccept: Event = this._onDidAccept.event; readonly onDidDiscard: Event = this._onDidDiscard.event; @@ -65,12 +65,12 @@ export abstract class EditModeStrategy { constructor( protected readonly _session: Session, + protected readonly _editor: ICodeEditor, protected readonly _zone: InlineChatZoneWidget, ) { } dispose(): void { - this._onDidAccept.dispose(); - this._onDidDiscard.dispose(); + this._store.dispose(); } abstract apply(): Promise; @@ -89,6 +89,35 @@ export abstract class EditModeStrategy { abstract makeChanges(edits: ISingleEditOperation[], obs: IEditObserver): Promise; + protected async _makeChanges(edits: ISingleEditOperation[], obs: IEditObserver, opts: ProgressingEditsOptions | undefined, progress: Progress | undefined): Promise { + + // push undo stop before first edit + if (++this._editCount === 1) { + this._editor.pushUndoStop(); + } + + if (opts) { + // ASYNC + const durationInSec = opts.duration / 1000; + for (const edit of edits) { + const wordCount = countWords(edit.text ?? ''); + const speed = wordCount / durationInSec; + // console.log({ durationInSec, wordCount, speed: wordCount / durationInSec }); + const asyncEdit = asProgressiveEdit(new WindowIntervalTimer(this._zone.domNode), edit, speed, opts.token); + await performAsyncTextEdit(this._session.textModelN, asyncEdit, progress, obs); + } + + } else { + // SYNC + obs.start(); + this._session.textModelN.pushEditOperations(null, edits, (undoEdits) => { + progress?.report(undoEdits); + return null; + }); + obs.stop(); + } + } + abstract undoChanges(altVersionId: number): Promise; abstract renderChanges(response: ReplyResponse): Promise; @@ -106,48 +135,54 @@ export abstract class EditModeStrategy { export class PreviewStrategy extends EditModeStrategy { private readonly _ctxDocumentChanged: IContextKey; - private readonly _listener: IDisposable; constructor( session: Session, + editor: ICodeEditor, zone: InlineChatZoneWidget, + @IModelService modelService: IModelService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super(session, zone); + super(session, editor, zone); this._ctxDocumentChanged = CTX_INLINE_CHAT_DOCUMENT_CHANGED.bindTo(contextKeyService); - this._listener = Event.debounce(session.textModelN.onDidChangeContent.bind(session.textModelN), () => { }, 350)(_ => { - if (!session.textModelN.isDisposed() && !session.textModel0.isDisposed()) { + + const baseModel = modelService.getModel(session.targetUri)!; + Event.debounce(baseModel.onDidChangeContent.bind(baseModel), () => { }, 350)(_ => { + if (!baseModel.isDisposed() && !session.textModel0.isDisposed()) { this._ctxDocumentChanged.set(session.hasChangedText); } - }); + }, undefined, this._store); } override dispose(): void { - this._listener.dispose(); this._ctxDocumentChanged.reset(); super.dispose(); } async apply() { - if (!(this._session.lastExchange?.response instanceof ReplyResponse)) { - return; - } - const editResponse = this._session.lastExchange?.response; - const { textModelN: modelN } = this._session; + // (1) ensure the editor still shows the original text + // (2) accept all pending hunks (moves changes from N to 0) + // (3) replace editor model with textModel0 + const textModel = this._editor.getModel(); + if (textModel?.equalsTextBuffer(this._session.textModel0.getTextBuffer())) { - if (modelN.equalsTextBuffer(this._session.textModel0.getTextBuffer())) { - modelN.pushStackElement(); - for (const edits of editResponse.allLocalEdits) { - modelN.pushEditOperations(null, edits.map(TextEdit.asEditOperation), () => null); - } - modelN.pushStackElement(); + this._session.hunkData.getInfo().forEach(item => item.acceptChanges()); + + const newText = this._session.textModel0.getValue(); + const range = textModel.getFullModelRange(); + + textModel.pushStackElement(); + textModel.pushEditOperations(null, [EditOperation.replace(range, newText)], () => null); + textModel.pushStackElement(); } - const { untitledTextModel } = this._session.lastExchange.response; - if (untitledTextModel && !untitledTextModel.isDisposed() && untitledTextModel.isDirty()) { - await untitledTextModel.save({ reason: SaveReason.EXPLICIT }); + if (this._session.lastExchange?.response instanceof ReplyResponse) { + const { untitledTextModel } = this._session.lastExchange.response; + if (untitledTextModel && !untitledTextModel.isDisposed() && untitledTextModel.isDirty()) { + await untitledTextModel.save({ reason: SaveReason.EXPLICIT }); + } } } @@ -155,22 +190,22 @@ export class PreviewStrategy extends EditModeStrategy { // nothing to do } - override async makeChanges(_edits: ISingleEditOperation[]): Promise { - // nothing to do + override async makeChanges(edits: ISingleEditOperation[], obs: IEditObserver): Promise { + return this._makeChanges(edits, obs, undefined, undefined); } - override async undoChanges(_altVersionId: number): Promise { - // nothing to do + override async makeProgressiveChanges(edits: ISingleEditOperation[], obs: IEditObserver, opts: ProgressingEditsOptions): Promise { + return this._makeChanges(edits, obs, opts, undefined); } - override async makeProgressiveChanges(): Promise { - // nothing to do + override async undoChanges(altVersionId: number): Promise { + const { textModelN } = this._session; + await undoModelUntil(textModelN, altVersionId); } override async renderChanges(response: ReplyResponse): Promise { if (response.allLocalEdits.length > 0) { - const allEditOperation = response.allLocalEdits.map(edits => edits.map(TextEdit.asEditOperation)); - await this._zone.widget.showEditsPreview(this._session.textModel0, this._session.textModelN, allEditOperation); + await this._zone.widget.showEditsPreview(this._session.textModel0, this._session.textModelN); } else { this._zone.widget.hideEditsPreview(); } @@ -197,19 +232,16 @@ export class LivePreviewStrategy extends EditModeStrategy { private readonly _previewZone: Lazy; private readonly _diffZonePool: InlineChatLivePreviewWidget[] = []; - private _currentLineRangeGroups: LineRangeMapping[][] = []; - private _editCount: number = 0; constructor( session: Session, - private readonly _editor: ICodeEditor, + editor: ICodeEditor, zone: InlineChatZoneWidget, - @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, @IInstantiationService private readonly _instaService: IInstantiationService, ) { - super(session, zone); + super(session, editor, zone); - this._previewZone = new Lazy(() => _instaService.createInstance(InlineChatFileCreatePreviewWidget, _editor)); + this._previewZone = new Lazy(() => _instaService.createInstance(InlineChatFileCreatePreviewWidget, editor)); } override dispose(): void { @@ -244,153 +276,119 @@ export class LivePreviewStrategy extends EditModeStrategy { await undoModelUntil(modelN, targetAltVersion); } - override async makeChanges(edits: ISingleEditOperation[], obs: IEditObserver): Promise { - const cursorStateComputerAndInlineDiffCollection: ICursorStateComputer = (undoEdits) => { - let last: Position | null = null; - for (const edit of undoEdits) { - last = !last || last.isBefore(edit.range.getEndPosition()) ? edit.range.getEndPosition() : last; - } - return last && [Selection.fromPositions(last)]; - }; - - // push undo stop before first edit - if (++this._editCount === 1) { - this._editor.pushUndoStop(); - } - obs.start(); - this._editor.executeEdits('inline-chat-live', edits, cursorStateComputerAndInlineDiffCollection); - obs.stop(); - } - override async undoChanges(altVersionId: number): Promise { const { textModelN } = this._session; await undoModelUntil(textModelN, altVersionId); - await this._updateDiffZones(); + this._updateDiffZones(); } - override async makeProgressiveChanges(edits: ISingleEditOperation[], obs: IEditObserver, opts: ProgressingEditsOptions): Promise { - - // push undo stop before first edit - if (++this._editCount === 1) { - this._editor.pushUndoStop(); - } - - //add a listener that shows the diff zones as soon as the first edit is applied - let renderTask = Promise.resolve(); - const changeListener = this._session.textModelN.onDidChangeContent(() => { - changeListener.dispose(); - renderTask = this._updateDiffZones(); - }); - - const durationInSec = opts.duration / 1000; - for (const edit of edits) { - const wordCount = countWords(edit.text ?? ''); - const speed = wordCount / durationInSec; - // console.log({ durationInSec, wordCount, speed: wordCount / durationInSec }); - await performAsyncTextEdit(this._session.textModelN, asProgressiveEdit(new WindowIntervalTimer(this._zone.domNode), edit, speed, opts.token), undefined, obs); - } - - await renderTask; - changeListener.dispose(); + override async makeChanges(edits: ISingleEditOperation[], obs: IEditObserver): Promise { + return this._makeChanges(edits, obs, undefined, undefined); } - override async renderChanges(response: ReplyResponse): Promise { + override async makeProgressiveChanges(edits: ISingleEditOperation[], obs: IEditObserver, opts: ProgressingEditsOptions): Promise { + await this._makeChanges(edits, obs, opts, new Progress(() => { + this._updateDiffZones(); + })); + } - await this._updateDiffZones(); + override async renderChanges(response: ReplyResponse): Promise { if (response.untitledTextModel && !response.untitledTextModel.isDisposed()) { this._previewZone.value.showCreation(this._session.wholeRange.value.getStartPosition().delta(-1), response.untitledTextModel); } else { this._previewZone.value.hide(); } + + return this._updateDiffZones(); } - protected _updateSummaryMessage(mappings: readonly LineRangeMapping[]) { - let linesChanged = 0; - for (const change of mappings) { - linesChanged += change.changedLineCount; - } + + protected _updateSummaryMessage(hunkCount: number) { let message: string; - if (linesChanged === 0) { - message = localize('lines.0', "Nothing changed"); - } else if (linesChanged === 1) { - message = localize('lines.1', "Changed 1 line"); + if (hunkCount === 0) { + message = localize('change.0', "Nothing changed"); + } else if (hunkCount === 1) { + message = localize('change.1', "1 change"); } else { - message = localize('lines.N', "Changed {0} lines", linesChanged); + message = localize('lines.NM', "{0} changes", hunkCount); } this._zone.widget.updateStatus(message); } - private async _updateDiffZones() { - 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) { - for (const zone of this._diffZonePool) { - zone.hide(); - } - return; - } - - const originalStartLineNumber = this._session.session.wholeRange?.startLineNumber ?? 1; - const mainGroup: LineRangeMapping[] = []; - let lastGroup: LineRangeMapping[] | undefined; - const groups: LineRangeMapping[][] = [mainGroup]; + private _updateDiffZones(): Position | undefined { - for (let i = 0; i < diff.changes.length; i++) { - const change = diff.changes[i]; + const { hunkData } = this._session; + const hunks = hunkData.getInfo().filter(hunk => hunk.getState() === HunkState.Pending); - // everything below the original start line is one group - if (change.original.startLineNumber >= originalStartLineNumber || 'true') { // TODO@jrieken be smarter and fix this - mainGroup.push(change); - continue; - } - - if (!lastGroup) { - lastGroup = [change]; - groups.push(lastGroup); - continue; + if (hunks.length === 0) { + for (const zone of this._diffZonePool) { + zone.hide(); } - // 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 last = tail(lastGroup); - const treshold = Math.ceil((change.modified.length + last.modified.length) * .75); - if (change.modified.startLineNumber - last.modified.endLineNumberExclusive <= treshold) { - lastGroup.push(change); + if (hunkData.getInfo().find(hunk => hunk.getState() === HunkState.Accepted)) { + this._onDidAccept.fire(); } else { - lastGroup = [change]; - groups.push(lastGroup); + this._onDidDiscard.fire(); } - } - - 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._updateSummaryMessage(diff.changes); - this._currentLineRangeGroups = groups; + this._updateSummaryMessage(hunks.length); - const handleDiff = () => { - this._updateDiffZones(); - }; + // create enough zones + const handleDiff = () => this._updateDiffZones(); + + type Data = { position: Position; distance: number; accept: Function; discard: Function }; + let nearest: Data | undefined; // create enough zones - while (groups.length > this._diffZonePool.length) { + while (hunks.length > this._diffZonePool.length) { this._diffZonePool.push(this._instaService.createInstance(InlineChatLivePreviewWidget, this._editor, this._session, {}, this._diffZonePool.length === 0 ? handleDiff : undefined)); } - for (let i = 0; i < groups.length; i++) { - this._diffZonePool[i].showForChanges(groups[i]); + + for (let i = 0; i < hunks.length; i++) { + const hunk = hunks[i]; + this._diffZonePool[i].showForChanges(hunk); + + const modifiedRange = hunk.getRangesN()[0]; + const zoneLineNumber = this._zone.position!.lineNumber; + const distance = zoneLineNumber <= modifiedRange.startLineNumber + ? modifiedRange.startLineNumber - zoneLineNumber + : zoneLineNumber - modifiedRange.endLineNumber; + + if (!nearest || nearest.distance > distance) { + nearest = { + position: modifiedRange.getStartPosition().delta(-1), + distance, + accept: () => { + hunk.acceptChanges(); + handleDiff(); + }, + discard: () => { + hunk.discardChanges(); + handleDiff(); + } + }; + } + } // hide unused zones - for (let i = groups.length; i < this._diffZonePool.length; i++) { + for (let i = hunks.length; i < this._diffZonePool.length; i++) { this._diffZonePool[i].hide(); } + + this.acceptHunk = async () => nearest?.accept(); + this.discardHunk = async () => nearest?.discard(); + + if (nearest) { + this._zone.updatePositionAndHeight(nearest.position); + this._editor.revealPositionInCenterIfOutsideViewport(nearest.position); + } + + return nearest?.position; } override hasFocus(): boolean { @@ -400,79 +398,6 @@ export class LivePreviewStrategy extends EditModeStrategy { } } -export interface AsyncTextEdit { - readonly range: IRange; - readonly newText: AsyncIterable; -} - -export async function performAsyncTextEdit(model: ITextModel, edit: AsyncTextEdit, progress?: IProgress, obs?: IEditObserver) { - - const [id] = model.deltaDecorations([], [{ - range: edit.range, - options: { - description: 'asyncTextEdit', - stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges - } - }]); - - let first = true; - for await (const part of edit.newText) { - - if (model.isDisposed()) { - break; - } - - const range = model.getDecorationRange(id); - if (!range) { - throw new Error('FAILED to perform async replace edit because the anchor decoration was removed'); - } - - const edit = first - ? EditOperation.replace(range, part) // first edit needs to override the "anchor" - : EditOperation.insert(range.getEndPosition(), part); - obs?.start(); - model.pushEditOperations(null, [edit], (undoEdits) => { - progress?.report(undoEdits); - return null; - }); - obs?.stop(); - first = false; - } -} - -export function asProgressiveEdit(interval: IntervalTimer, edit: IIdentifiedSingleEditOperation, wordsPerSec: number, token: CancellationToken): AsyncTextEdit { - - wordsPerSec = Math.max(10, wordsPerSec); - - const stream = new AsyncIterableSource(); - let newText = edit.text ?? ''; - - interval.cancelAndSet(() => { - const r = getNWords(newText, 1); - stream.emitOne(r.value); - newText = newText.substring(r.value.length); - if (r.isFullString) { - interval.cancel(); - stream.resolve(); - d.dispose(); - } - - }, 1000 / wordsPerSec); - - // cancel ASAP - const d = token.onCancellationRequested(() => { - interval.cancel(); - stream.resolve(); - d.dispose(); - }); - - return { - range: edit.range, - newText: stream.asyncIterable - }; -} - - type HunkDisplayData = { decorationIds: string[]; @@ -506,7 +431,6 @@ export class LiveStrategy extends EditModeStrategy { className: 'inline-chat-inserted-range', }); - private readonly _store = new DisposableStore(); private readonly _previewZone: Lazy; private readonly _ctxCurrentChangeHasDiff: IContextKey; @@ -514,32 +438,30 @@ export class LiveStrategy extends EditModeStrategy { private readonly _progressiveEditingDecorations: IEditorDecorationsCollection; - private _editCount: number = 0; override acceptHunk: () => Promise = () => super.acceptHunk(); override discardHunk: () => Promise = () => super.discardHunk(); constructor( session: Session, - protected readonly _editor: ICodeEditor, + editor: ICodeEditor, zone: InlineChatZoneWidget, @IContextKeyService contextKeyService: IContextKeyService, @IEditorWorkerService protected readonly _editorWorkerService: IEditorWorkerService, @IInstantiationService protected readonly _instaService: IInstantiationService, ) { - super(session, zone); + super(session, editor, zone); this._ctxCurrentChangeHasDiff = CTX_INLINE_CHAT_CHANGE_HAS_DIFF.bindTo(contextKeyService); this._ctxCurrentChangeShowsDiff = CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF.bindTo(contextKeyService); this._progressiveEditingDecorations = this._editor.createDecorationsCollection(); - this._previewZone = new Lazy(() => _instaService.createInstance(InlineChatFileCreatePreviewWidget, _editor)); + this._previewZone = new Lazy(() => _instaService.createInstance(InlineChatFileCreatePreviewWidget, editor)); } override dispose(): void { this._resetDiff(); this._previewZone.rawValue?.dispose(); - this._store.dispose(); super.dispose(); } @@ -581,25 +503,15 @@ export class LiveStrategy extends EditModeStrategy { } override async undoChanges(altVersionId: number): Promise { - const { textModelN } = this._session; await undoModelUntil(textModelN, altVersionId); } override async makeChanges(edits: ISingleEditOperation[], obs: IEditObserver): Promise { - return this._makeChanges(edits, obs, undefined); + return this._makeChanges(edits, obs, undefined, undefined); } override async makeProgressiveChanges(edits: ISingleEditOperation[], obs: IEditObserver, opts: ProgressingEditsOptions): Promise { - return this._makeChanges(edits, obs, opts); - } - - private async _makeChanges(edits: ISingleEditOperation[], obs: IEditObserver, opts: ProgressingEditsOptions | undefined): Promise { - - // push undo stop before first edit - if (++this._editCount === 1) { - this._editor.pushUndoStop(); - } // add decorations once per line that got edited const progress = new Progress(edits => { @@ -619,26 +531,7 @@ export class LiveStrategy extends EditModeStrategy { this._progressiveEditingDecorations.append(newDecorations); }); - - if (opts) { - // ASYNC - const durationInSec = opts.duration / 1000; - for (const edit of edits) { - const wordCount = countWords(edit.text ?? ''); - const speed = wordCount / durationInSec; - // console.log({ durationInSec, wordCount, speed: wordCount / durationInSec }); - await performAsyncTextEdit(this._session.textModelN, asProgressiveEdit(new WindowIntervalTimer(this._zone.domNode), edit, speed, opts.token), progress, obs); - } - - } else { - // SYNC - obs.start(); - this._editor.executeEdits('inline-chat-live', edits, undoEdits => { - progress.report(undoEdits); - return null; - }); - obs.stop(); - } + return this._makeChanges(edits, obs, opts, progress); } private readonly _hunkDisplayData = new Map(); @@ -712,7 +605,7 @@ export class LiveStrategy extends EditModeStrategy { const scrollState = StableEditorScrollState.capture(this._editor); changeDecorationsAndViewZones(this._editor, (_decorationsAccessor, viewZoneAccessor) => { assertType(data); - if (!data.viewZone) { + if (!data.viewZoneId) { const [hunkRange] = hunkData.getRangesN(); viewZoneData.afterLineNumber = hunkRange.startLineNumber - 1; data.viewZoneId = viewZoneAccessor.addZone(viewZoneData); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index fa5e8dc762ce4..ae291d9d4625d 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -29,14 +29,11 @@ import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestCont import { Position } from 'vs/editor/common/core/position'; import { DEFAULT_FONT_FAMILY } from 'vs/workbench/browser/style'; import { CompletionItem, CompletionItemInsertTextRule, CompletionItemKind, CompletionItemProvider, CompletionList, ProviderResult } from 'vs/editor/common/languages'; -import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; -import { ILanguageSelection } from 'vs/editor/common/languages/language'; import { ResourceLabel } from 'vs/workbench/browser/labels'; 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 { invertLineRange, lineRangeAsRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; +import { asRange, invertLineRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; @@ -388,10 +385,6 @@ export class InlineChatWidget { toolbarOptions: { primaryGroup: 'main' } })); - this._progressBar = new ProgressBar(this._elements.progress); - this._store.add(this._progressBar); - - const workbenchMenubarOptions: IWorkbenchButtonBarOptions = { telemetrySource: 'interactiveEditorWidget-toolbar', buttonConfigProvider: action => { @@ -423,6 +416,7 @@ export class InlineChatWidget { // preview editors this._previewDiffEditor = new Lazy(() => this._store.add(_instantiationService.createInstance(EmbeddedDiffEditorWidget, this._elements.previewDiff, { + useInlineViewWhenSpaceIsLimited: false, ..._previewEditorEditorOptions, onlyShowAccessibleDiffViewer: this._accessibilityService.isScreenReaderOptimized(), }, { modifiedEditor: codeEditorWidgetOptions, originalEditor: codeEditorWidgetOptions }, parentEditor))); @@ -472,21 +466,23 @@ export class InlineChatWidget { return this._elements.root; } - layout(dim: Dimension) { + layout(_dim: Dimension) { this._isLayouting = true; try { const widgetToolbarWidth = getTotalWidth(this._elements.widgetToolbar); - const innerEditorWidth = dim.width - (getTotalWidth(this._elements.editorToolbar) + 8 /* L/R-padding */) - (widgetToolbarWidth); - dim = new Dimension(innerEditorWidth, dim.height); + const editorToolbarWidth = getTotalWidth(this._elements.editorToolbar) + 8 /* L/R-padding */; + const innerEditorWidth = _dim.width - editorToolbarWidth - widgetToolbarWidth; + const dim = new Dimension(innerEditorWidth, _dim.height); if (!this._lastDim || !Dimension.equals(this._lastDim, dim)) { this._lastDim = dim; this._inputEditor.layout(new Dimension(innerEditorWidth, this._inputEditor.getContentHeight())); this._elements.placeholder.style.width = `${innerEditorWidth /* input-padding*/}px`; if (this._previewDiffEditor.hasValue) { - const previewDiffDim = new Dimension(dim.width, Math.min(300, Math.max(0, this._previewDiffEditor.value.getContentHeight()))); - this._previewDiffEditor.value.layout(previewDiffDim); + const previewDiffDim = new Dimension(_dim.width - 12, Math.min(300, Math.max(0, this._previewDiffEditor.value.getContentHeight()))); + this._elements.previewDiff.style.width = `${previewDiffDim.width}px`; this._elements.previewDiff.style.height = `${previewDiffDim.height}px`; + this._previewDiffEditor.value.layout(previewDiffDim); } if (this._previewCreateEditor.hasValue) { @@ -521,9 +517,11 @@ export class InlineChatWidget { updateProgress(show: boolean) { if (show) { + this._progressBar.show(); this._progressBar.infinite(); } else { this._progressBar.stop(); + this._progressBar.hide(); } } @@ -751,23 +749,17 @@ export class InlineChatWidget { // --- preview - async showEditsPreview(textModel0: ITextModel, textModelN: ITextModel, allEdits: ISingleEditOperation[][]) { + async showEditsPreview(textModel0: ITextModel, textModelN: ITextModel) { this._elements.previewDiff.classList.remove('hidden'); - 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); - } - - const diff = await this._editorWorkerService.computeDiff(textModel0.uri, modified.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: 5000, computeMoves: false }, 'advanced'); + 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._previewDiffEditor.value.setModel({ original: textModel0, modified }); + this._previewDiffEditor.value.setModel({ original: textModel0, modified: textModelN }); // joined ranges let originalLineRange = diff.changes[0].original; @@ -783,15 +775,15 @@ export class InlineChatWidget { modifiedLineRange = new LineRange(newStartLine, modifiedLineRange.endLineNumberExclusive); originalLineRange = new LineRange(newStartLine, originalLineRange.endLineNumberExclusive); - const newEndLineModified = Math.min(modifiedLineRange.endLineNumberExclusive + pad, modified.getLineCount()); + const newEndLineModified = Math.min(modifiedLineRange.endLineNumberExclusive + pad, textModelN.getLineCount()); modifiedLineRange = new LineRange(modifiedLineRange.startLineNumber, newEndLineModified); const newEndLineOriginal = Math.min(originalLineRange.endLineNumberExclusive + pad, textModel0.getLineCount()); originalLineRange = new LineRange(originalLineRange.startLineNumber, newEndLineOriginal); 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'); + const hiddenModified = invertLineRange(modifiedLineRange, textModelN); + this._previewDiffEditor.value.getOriginalEditor().setHiddenAreas(hiddenOriginal.map(lr => asRange(lr, textModel0)), 'diff-hidden'); + this._previewDiffEditor.value.getModifiedEditor().setHiddenAreas(hiddenModified.map(lr => asRange(lr, textModelN)), 'diff-hidden'); this._previewDiffEditor.value.revealLine(modifiedLineRange.startLineNumber, ScrollType.Immediate); this._onDidChangeHeight.fire(); diff --git a/src/vs/workbench/contrib/inlineChat/browser/utils.ts b/src/vs/workbench/contrib/inlineChat/browser/utils.ts index 43364755deb63..fbd21af7b650f 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/utils.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/utils.ts @@ -3,9 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { EditOperation } from 'vs/editor/common/core/editOperation'; import { LineRange } from 'vs/editor/common/core/lineRange'; -import { Range } from 'vs/editor/common/core/range'; -import { ITextModel } from 'vs/editor/common/model'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { IIdentifiedSingleEditOperation, ITextModel, IValidEditOperation, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { IEditObserver } from './inlineChatStrategies'; +import { IProgress } from 'vs/platform/progress/common/progress'; +import { IntervalTimer, AsyncIterableSource } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; export function invertLineRange(range: LineRange, model: ITextModel): LineRange[] { if (range.isEmpty) { @@ -21,12 +27,82 @@ export function invertLineRange(range: LineRange, model: ITextModel): LineRange[ return result.filter(r => !r.isEmpty); } -export function lineRangeAsRange(r: LineRange): Range { - return new Range(r.startLineNumber, 1, r.endLineNumberExclusive - 1, 1); -} - export function asRange(lineRange: LineRange, model: ITextModel): Range { return lineRange.isEmpty ? new Range(lineRange.startLineNumber, 1, lineRange.startLineNumber, model.getLineLength(lineRange.startLineNumber)) : new Range(lineRange.startLineNumber, 1, lineRange.endLineNumberExclusive - 1, model.getLineLength(lineRange.endLineNumberExclusive - 1)); } + +// --- async edit + +export interface AsyncTextEdit { + readonly range: IRange; + readonly newText: AsyncIterable; +} + +export async function performAsyncTextEdit(model: ITextModel, edit: AsyncTextEdit, progress?: IProgress, obs?: IEditObserver) { + + const [id] = model.deltaDecorations([], [{ + range: edit.range, + options: { + description: 'asyncTextEdit', + stickiness: TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges + } + }]); + + let first = true; + for await (const part of edit.newText) { + + if (model.isDisposed()) { + break; + } + + const range = model.getDecorationRange(id); + if (!range) { + throw new Error('FAILED to perform async replace edit because the anchor decoration was removed'); + } + + const edit = first + ? EditOperation.replace(range, part) // first edit needs to override the "anchor" + : EditOperation.insert(range.getEndPosition(), part); + obs?.start(); + model.pushEditOperations(null, [edit], (undoEdits) => { + progress?.report(undoEdits); + return null; + }); + obs?.stop(); + first = false; + } +} + +export function asProgressiveEdit(interval: IntervalTimer, edit: IIdentifiedSingleEditOperation, wordsPerSec: number, token: CancellationToken): AsyncTextEdit { + + wordsPerSec = Math.max(10, wordsPerSec); + + const stream = new AsyncIterableSource(); + let newText = edit.text ?? ''; + + interval.cancelAndSet(() => { + const r = getNWords(newText, 1); + stream.emitOne(r.value); + newText = newText.substring(r.value.length); + if (r.isFullString) { + interval.cancel(); + stream.resolve(); + d.dispose(); + } + + }, 1000 / wordsPerSec); + + // cancel ASAP + const d = token.onCancellationRequested(() => { + interval.cancel(); + stream.resolve(); + d.dispose(); + }); + + return { + range: edit.range, + newText: stream.asyncIterable + }; +} diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts index e21cc59da32ed..0c6a58b40365c 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts @@ -431,6 +431,28 @@ suite('InlineChatSession', function () { assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three'].join('\n')); }); + test('HunkData, (mirror) edit after dicard ', async function () { + + const lines = ['one', 'two', 'three']; + model.setValue(lines.join('\n')); + const session = await inlineChatSessionService.createSession(editor, { editMode: EditMode.Live }, CancellationToken.None); + assertType(session); + + await makeEditAsAi([EditOperation.insert(new Position(3, 1), 'AI WAS HERE\n')]); + assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'AI WAS HERE', 'three'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), lines.join('\n')); + + assert.strictEqual(session.hunkData.size, 1); + const [hunk] = session.hunkData.getInfo(); + hunk.discardChanges(); + assert.strictEqual(session.textModelN.getValue(), lines.join('\n')); + assert.strictEqual(session.textModel0.getValue(), lines.join('\n')); + + makeEdit([EditOperation.replace(new Range(3, 4, 3, 6), '3333')]); + assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'thr3333'].join('\n')); + assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'thr3333'].join('\n')); + }); + test('HunkData, (mirror) edit after, multi turn', async function () { const lines = ['one', 'two', 'three', 'four', 'five']; @@ -483,6 +505,12 @@ suite('InlineChatSession', function () { makeEdit([EditOperation.replace(new Range(6, 3, 6, 5), 'vefivefi')]); assert.strictEqual(session.textModelN.getValue(), ['one', 'twozwei', 'AI_EDIT', 'three', 'FOOfour', 'fivefivefi'].join('\n')); assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three', 'FOOfour', 'fivefivefi'].join('\n')); + + session.hunkData.getInfo()[0].acceptChanges(); + assert.strictEqual(session.textModelN.getValue(), session.textModel0.getValue()); + + makeEdit([EditOperation.replace(new Range(1, 1, 1, 1), 'done')]); + assert.strictEqual(session.textModelN.getValue(), session.textModel0.getValue()); }); }); diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatStrategies.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatStrategies.test.ts index d91aa0201908f..e8d229d81fcf3 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatStrategies.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatStrategies.test.ts @@ -6,7 +6,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IntervalTimer } from 'vs/base/common/async'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; -import { asProgressiveEdit } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies'; +import { asProgressiveEdit } from '../../browser/utils'; import * as assert from 'assert'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts index cfc2b2fc2d375..145bd1772e8a1 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts @@ -31,7 +31,8 @@ import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; import { EmptyResponse, ErrorResponse, ReplyResponse, Session, SessionExchange, SessionPrompt } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { IInlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionService'; -import { ProgressingEditsOptions, asProgressiveEdit, performAsyncTextEdit } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies'; +import { ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies'; +import { asProgressiveEdit, performAsyncTextEdit } from 'vs/workbench/contrib/inlineChat/browser/utils'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_LAST_RESPONSE_TYPE, CTX_INLINE_CHAT_VISIBLE, EditMode, IInlineChatProgressItem, IInlineChatRequest, InlineChatResponseFeedbackKind, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; From b921c3efc15afff6a1387448756d2cdb6c66ad76 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 19 Jan 2024 18:21:31 +0530 Subject: [PATCH 148/333] report telemetry only for opted in settings (#202844) --- .../browser/workbench.contribution.ts | 3 +- .../browser/extensions.contribution.ts | 2 +- .../browser/configurationService.ts | 57 ++++++++++--------- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 6153d527c98da..bca80d33bb9dd 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -504,7 +504,8 @@ const registry = Registry.as(ConfigurationExtensions.Con localize('workbench.activityBar.location.side', "Show the Activity Bar to the side of the Primary Side Bar."), localize('workbench.activityBar.location.top', "Show the Activity Bar on top of the Primary Side Bar."), localize('workbench.activityBar.location.hide', "Hide the Activity Bar.") - ] + ], + tags: ['FeatureInsight'] }, 'workbench.activityBar.iconClickBehavior': { 'type': 'string', diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index b8f44f8df23fa..de4e96c1c251f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -146,7 +146,7 @@ Registry.as(ConfigurationExtensions.Configuration) description: localize('extensions.autoUpdate', "Controls the automatic update behavior of extensions. The updates are fetched from a Microsoft online service."), default: true, scope: ConfigurationScope.APPLICATION, - tags: ['usesOnlineServices'] + tags: ['usesOnlineServices', 'FeatureInsight'] }, 'extensions.autoCheckUpdates': { type: 'boolean', diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 6b842ab9400fe..4057b5bc7cc30 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -15,9 +15,9 @@ import { ConfigurationModel, ConfigurationChangeEvent, mergeChanges } from 'vs/p import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange, ConfigurationTargetToString, IConfigurationUpdateOverrides, isConfigurationUpdateOverrides, IConfigurationService, IConfigurationUpdateOptions } from 'vs/platform/configuration/common/configuration'; import { IPolicyConfiguration, NullPolicyConfiguration, PolicyConfiguration } from 'vs/platform/configuration/common/configurations'; import { Configuration } from 'vs/workbench/services/configuration/common/configurationModels'; -import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService, RestrictedSettings, PROFILE_SCOPES, LOCAL_MACHINE_PROFILE_SCOPES, profileSettingsSchemaId, APPLY_ALL_PROFILES_SETTING, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration'; +import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService, RestrictedSettings, PROFILE_SCOPES, LOCAL_MACHINE_PROFILE_SCOPES, profileSettingsSchemaId, APPLY_ALL_PROFILES_SETTING } from 'vs/workbench/services/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings, ConfigurationScope, IConfigurationPropertySchema, keyFromOverrideIdentifiers, OVERRIDE_PROPERTY_PATTERN, resourceLanguageSettingsSchemaId, configurationDefaultsSchemaId } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings, ConfigurationScope, IConfigurationPropertySchema, keyFromOverrideIdentifiers, OVERRIDE_PROPERTY_PATTERN, resourceLanguageSettingsSchemaId, configurationDefaultsSchemaId, IRegisteredConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; import { IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, getStoredWorkspaceFolder, toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ConfigurationEditing, EditableConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; @@ -1325,48 +1325,49 @@ class ResetConfigurationDefaultsOverridesCache extends Disposable implements IWo } } -class ConfigurationTelemetryContribution extends Disposable implements IWorkbenchContribution { +class ConfigurationTelemetryContribution implements IWorkbenchContribution { private readonly configurationRegistry = Registry.as(Extensions.Configuration); constructor( @IConfigurationService private readonly configurationService: WorkspaceService, - @ITelemetryService telemetryService: ITelemetryService, + @ITelemetryService private readonly telemetryService: ITelemetryService, ) { - super(); + type UpdatedSettingClassification = { + owner: 'sandy081'; + comment: 'Event reporting the updated setting'; + setting: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'name of the setting' }; + value: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'value of the setting' }; + source: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'source of the setting' }; + }; + type UpdatedSettingEvent = { + setting: string; + value: string; + source: string; + }; const { user, workspace } = configurationService.keys(); - const updatedUserSettings: { [key: string]: any } = {}; - for (const k of user) { - if (!k.startsWith(`${TASKS_CONFIGURATION_KEY}.`) && !k.startsWith(`${LAUNCH_CONFIGURATION_KEY}.`)) { - updatedUserSettings[k] = this.getValueToReport(k, ConfigurationTarget.USER_LOCAL) ?? ''; + const userSource = ConfigurationTargetToString(ConfigurationTarget.USER_LOCAL); + for (const setting of user) { + const schema = this.configurationRegistry.getConfigurationProperties()[setting]; + if (schema.tags?.includes('FeatureInsight')) { + const value = this.getValueToReport(setting, ConfigurationTarget.USER_LOCAL, schema); + this.telemetryService.publicLog2('updatedsetting', { setting, value, source: userSource }); } } - const updatedWorkspaceSettings: { [key: string]: any } = {}; - for (const k of workspace) { - if (!k.startsWith(`${TASKS_CONFIGURATION_KEY}.`) && !k.startsWith(`${LAUNCH_CONFIGURATION_KEY}.`)) { - updatedWorkspaceSettings[k] = this.getValueToReport(k, ConfigurationTarget.WORKSPACE) ?? ''; + const worskpaceSource = ConfigurationTargetToString(ConfigurationTarget.WORKSPACE); + for (const setting of workspace) { + const schema = this.configurationRegistry.getConfigurationProperties()[setting]; + if (schema.tags?.includes('FeatureInsight')) { + const value = this.getValueToReport(setting, ConfigurationTarget.WORKSPACE, schema); + this.telemetryService.publicLog2('updatedsetting', { setting, value, source: worskpaceSource }); } } - type UpdatedSettingsClassification = { - owner: 'sandy081'; - comment: 'Event reporting updated settings'; - settings: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'stringified updated settings object' }; - }; - type UpdatedSettingsEvent = { - settings: string; - }; - telemetryService.publicLog2('updatedsettings:user', { settings: JSON.stringify(updatedUserSettings) }); - telemetryService.publicLog2('updatedsettings:workspace', { settings: JSON.stringify(updatedWorkspaceSettings) }); } /** * Report value of a setting only if it is an enum, boolean, or number or an array of those. */ - private getValueToReport(key: string, target: ConfigurationTarget): any { - const schema = this.configurationRegistry.getConfigurationProperties()[key]; - if (!schema) { - return undefined; - } + private getValueToReport(key: string, target: ConfigurationTarget, schema: IRegisteredConfigurationPropertySchema): any { const configurationModel = this.configurationService.getConfigurationModel(target); const value = configurationModel?.getValue(key); if (isNumber(value) || isBoolean(value)) { From 53661a149511a8ba4afd9e0fd9bfbedbc777664a Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 19 Jan 2024 14:40:06 +0100 Subject: [PATCH 149/333] focus editor back when quick voice ends --- .../contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts index 8c7f9e05b67d5..8be1350ac9353 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts @@ -258,6 +258,7 @@ export class InlineChatQuickVoice implements IEditorContribution { listener.dispose(); this._widget.hide(); this._ctxQuickChatInProgress.reset(); + this._editor.focus(); if (!abort && message) { InlineChatController.get(this._editor)?.run({ message, autoSend: true }); From 53e467c211f370ca4f38fadece02ec664efbf8da Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 19 Jan 2024 15:14:39 +0100 Subject: [PATCH 150/333] fix https://github.com/microsoft/vscode-copilot/issues/315 --- .../browser/inlineChatController.ts | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 2318b0bdd06e7..9c89e11d7ba78 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -106,6 +106,7 @@ export class InlineChatController implements IEditorContribution { private _historyCandidate: string = ''; private _historyUpdate: (prompt: string) => void; + private _isDisposed: boolean = false; private readonly _store = new DisposableStore(); private readonly _zone: Lazy; private readonly _ctxHasActiveRequest: IContextKey; @@ -189,12 +190,15 @@ export class InlineChatController implements IEditorContribution { }; } - dispose(): void { - if (this._session) { - this._inlineChatSessionService.releaseSession(this._session); + async dispose(): Promise { + if (this._currentRun) { + this._messages.fire((this._session?.lastExchange + ? Message.PAUSE_SESSION + : Message.CANCEL_SESSION) + ); } - this._strategy?.dispose(); this._store.dispose(); + this._isDisposed = true; this._log('DISPOSED controller'); } @@ -262,7 +266,7 @@ export class InlineChatController implements IEditorContribution { protected async _nextState(state: State, options: InlineChatRunOptions): Promise { let nextState: State | void = state; - while (nextState) { + while (nextState && !this._isDisposed) { this._log('setState to ', nextState); nextState = await this[nextState](options); } @@ -457,20 +461,20 @@ export class InlineChatController implements IEditorContribution { store.dispose(); } - this._zone.value.widget.selectAll(false); if (message & (Message.CANCEL_INPUT | Message.CANCEL_SESSION)) { return State.CANCEL; } - if (message & Message.ACCEPT_SESSION) { - return State.ACCEPT; - } - if (message & Message.PAUSE_SESSION) { return State.PAUSE; } + if (message & Message.ACCEPT_SESSION) { + this._zone.value.widget.selectAll(false); + return State.ACCEPT; + } + if (message & Message.RERUN_INPUT && this._session.lastExchange) { const { lastExchange } = this._session; if (options.withIntentDetection === undefined) { // @ulugbekna: if we're re-running with intent detection turned off, no need to update `attempt` # From 335b2d5fd4dfc9909b36d10ad6cede7243152f4a Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 19 Jan 2024 15:29:42 +0100 Subject: [PATCH 151/333] quick voice should wrap when getting too long --- build/lib/stylelint/vscode-known-variables.json | 2 ++ .../inlineChat/electron-sandbox/inlineChatQuickVoice.css | 4 +++- .../inlineChat/electron-sandbox/inlineChatQuickVoice.ts | 7 +++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 96124fe8a87b7..0b1767fba21ed 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -778,6 +778,8 @@ "--vscode-hover-whiteSpace", "--vscode-inline-chat-cropped", "--vscode-inline-chat-expanded", + "--vscode-inline-chat-quick-voice-height", + "--vscode-inline-chat-quick-voice-width", "--vscode-interactive-session-foreground", "--vscode-interactive-result-editor-background-color", "--vscode-repl-font-family", diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css index 64ff58649ee1d..f62e5b2852b4a 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css @@ -10,8 +10,10 @@ display: flex; align-items: center; box-shadow: 0 4px 8px var(--vscode-inlineChat-shadow); - white-space: nowrap; z-index: 1000; + min-height: var(--vscode-inline-chat-quick-voice-height); + line-height: var(--vscode-inline-chat-quick-voice-height); + max-width: var(--vscode-inline-chat-quick-voice-width); } .monaco-editor .inline-chat-quick-voice .codicon.codicon-mic-filled { diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts index 8be1350ac9353..ec0e5c60c80bd 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts @@ -107,6 +107,7 @@ export class CancelAction extends EditorAction2 { class QuickVoiceWidget implements IContentWidget { readonly suppressMouseDown = true; + readonly allowEditorOverflow = true; private readonly _domNode = document.createElement('div'); private readonly _elements = dom.h('.inline-chat-quick-voice@main', [ @@ -157,8 +158,10 @@ class QuickVoiceWidget implements IContentWidget { beforeRender(): IDimension | null { const lineHeight = this._editor.getOption(EditorOption.lineHeight); - this._elements.main.style.lineHeight = `${lineHeight}px`; - this._elements.main.style.height = `${lineHeight}px`; + const width = this._editor.getLayoutInfo().contentWidth * 0.7; + + this._elements.main.style.setProperty('--vscode-inline-chat-quick-voice-height', `${lineHeight}px`); + this._elements.main.style.setProperty('--vscode-inline-chat-quick-voice-width', `${width}px`); return null; } From 84477e958ea604dc280f825d4002aabe9e62318b Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 19 Jan 2024 15:48:42 +0100 Subject: [PATCH 152/333] render recognizing and recognized text differently --- .../electron-sandbox/inlineChatQuickVoice.css | 6 +++++- .../electron-sandbox/inlineChatQuickVoice.ts | 21 ++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css index f62e5b2852b4a..61d96056a006f 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.css @@ -40,6 +40,10 @@ } } -.monaco-editor .inline-chat-quick-voice .message.preview { +.monaco-editor .inline-chat-quick-voice .preview { opacity: .4; } + +.monaco-editor .inline-chat-quick-voice .message:not(:empty) { + margin-right: 0.4ch; +} diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts index ec0e5c60c80bd..1e33dc7e5cdaa 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts @@ -112,7 +112,10 @@ class QuickVoiceWidget implements IContentWidget { private readonly _domNode = document.createElement('div'); private readonly _elements = dom.h('.inline-chat-quick-voice@main', [ dom.h('span@mic'), - dom.h('span.message@message'), + dom.h('span', [ + dom.h('span.message@message'), + dom.h('span.preview@preview'), + ]) ]); private _focusTracker: dom.IFocusTracker | undefined; @@ -174,9 +177,9 @@ class QuickVoiceWidget implements IContentWidget { // --- - updateInput(input: string | undefined, isDefinite: boolean): void { - this._elements.message.classList.toggle('preview', !isDefinite); - this._elements.message.textContent = input ?? ''; + updateInput(data: { message?: string; preview?: string }): void { + this._elements.message.textContent = data.message ?? ''; + this._elements.preview.textContent = data.preview ?? ''; } show() { @@ -189,7 +192,8 @@ class QuickVoiceWidget implements IContentWidget { hide() { this._elements.main.classList.remove('recording'); - this.updateInput(undefined, true); + this._elements.message.textContent = ''; + this._elements.preview.textContent = ''; this._editor.removeContentWidget(this); this._focusTracker?.dispose(); } @@ -233,6 +237,7 @@ export class InlineChatQuickVoice implements IEditorContribution { this._ctxQuickChatInProgress.set(true); let message: string | undefined; + let preview: string | undefined; const session = this._speechService.createSpeechToTextSession(cts.token); const listener = session.onDidChange(e => { @@ -247,11 +252,13 @@ export class InlineChatQuickVoice implements IEditorContribution { case SpeechToTextStatus.Stopped: break; case SpeechToTextStatus.Recognizing: - this._widget.updateInput(!message ? e.text : `${message} ${e.text}`, false); + preview = e.text; + this._widget.updateInput({ message, preview }); break; case SpeechToTextStatus.Recognized: message = !message ? e.text : `${message} ${e.text}`; - this._widget.updateInput(message, true); + preview = ''; + this._widget.updateInput({ message, preview }); break; } }); From ceb5e559bde77f42daf8b1ac8262939390ffb335 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 19 Jan 2024 16:10:55 +0100 Subject: [PATCH 153/333] fix compilo --- .../contrib/inlineChat/browser/inlineChatController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 9c89e11d7ba78..262736553ab80 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -190,7 +190,7 @@ export class InlineChatController implements IEditorContribution { }; } - async dispose(): Promise { + dispose(): void { if (this._currentRun) { this._messages.fire((this._session?.lastExchange ? Message.PAUSE_SESSION From 41ec830a62fffa2596c07f043ec2a240cfab7053 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 19 Jan 2024 08:19:09 -0700 Subject: [PATCH 154/333] Add extra test case for log error and common properties (#202858) * Add an additional test for common properties * Add one more test --- .../api/test/browser/extHostTelemetry.test.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts b/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts index d38fb955373e4..0f79b399d9da7 100644 --- a/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTelemetry.test.ts @@ -215,6 +215,34 @@ suite('ExtHostTelemetry', function () { }); + test('Log error should get common properties #193205', function () { + const functionSpy: TelemetryLoggerSpy = { dataArr: [], exceptionArr: [], flushCalled: false }; + + const logger = createLogger(functionSpy, undefined, { additionalCommonProperties: { 'common.foo': 'bar' } }); + logger.logError(new Error('Test error')); + assert.strictEqual(functionSpy.exceptionArr.length, 1); + assert.strictEqual(functionSpy.exceptionArr[0].data['common.foo'], 'bar'); + assert.strictEqual(functionSpy.exceptionArr[0].data['common.product'], 'test'); + + logger.logError('test-error-event'); + assert.strictEqual(functionSpy.dataArr.length, 1); + assert.strictEqual(functionSpy.dataArr[0].data['common.foo'], 'bar'); + assert.strictEqual(functionSpy.dataArr[0].data['common.product'], 'test'); + + logger.logError('test-error-event', { 'test-data': 'test-data' }); + assert.strictEqual(functionSpy.dataArr.length, 2); + assert.strictEqual(functionSpy.dataArr[1].data['common.foo'], 'bar'); + assert.strictEqual(functionSpy.dataArr[1].data['common.product'], 'test'); + + logger.logError('test-error-event', { properties: { 'test-data': 'test-data' } }); + assert.strictEqual(functionSpy.dataArr.length, 3); + assert.strictEqual(functionSpy.dataArr[2].data.properties['common.foo'], 'bar'); + assert.strictEqual(functionSpy.dataArr[2].data.properties['common.product'], 'test'); + + logger.dispose(); + assert.strictEqual(functionSpy.flushCalled, true); + }); + test('Ensure logger properly cleans PII', function () { const functionSpy: TelemetryLoggerSpy = { dataArr: [], exceptionArr: [], flushCalled: false }; From 79cb8061ed8860f16fb660ced34a160c286b505f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 19 Jan 2024 17:04:06 +0100 Subject: [PATCH 155/333] fix: use correct max-old-space-size flag syntax, increase mem limit for extensions-ci (#202862) fixes #202720 --- .github/workflows/basic.yml | 2 +- .github/workflows/ci.yml | 6 +++--- package.json | 34 +++++++++++++++++----------------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index 92c75ebdd9aaa..b0f7d8de27c52 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -61,7 +61,7 @@ jobs: run: yarn --frozen-lockfile --network-timeout 180000 - name: Compile and Download - run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" + run: yarn npm-run-all --max-old-space-size=4095 -lp compile "electron x64" - name: Run Unit Tests id: electron-unit-tests diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b75e45b49c551..94a552b5fb8d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,7 +69,7 @@ jobs: 7z.exe a .build/node_modules_cache/cache.7z -mx3 `@.build/node_modules_list.txt - name: Compile and Download - run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions + run: yarn npm-run-all --max-old-space-size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions - name: Compile Integration Tests run: yarn --cwd test/integration/browser compile @@ -145,7 +145,7 @@ jobs: run: yarn --frozen-lockfile --network-timeout 180000 - name: Compile and Download - run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions + run: yarn npm-run-all --max-old-space-size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions - name: Compile Integration Tests run: yarn --cwd test/integration/browser compile @@ -216,7 +216,7 @@ jobs: run: yarn --frozen-lockfile --network-timeout 180000 - name: Compile and Download - run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions + run: yarn npm-run-all --max-old-space-size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions - name: Compile Integration Tests run: yarn --cwd test/integration/browser compile diff --git a/package.json b/package.json index 5a1353b031e8f..b221471572840 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "test-extension": "vscode-test", "preinstall": "node build/npm/preinstall.js", "postinstall": "node build/npm/postinstall.js", - "compile": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js compile", + "compile": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js compile", "watch": "npm-run-all -lp watch-client watch-extensions", "watchd": "deemon yarn watch", "watch-webd": "deemon yarn watch-web", @@ -24,14 +24,14 @@ "kill-watch-webd": "deemon --kill yarn watch-web", "restart-watchd": "deemon --restart yarn watch", "restart-watch-webd": "deemon --restart yarn watch-web", - "watch-client": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js watch-client", + "watch-client": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js watch-client", "watch-clientd": "deemon yarn watch-client", "kill-watch-clientd": "deemon --kill yarn watch-client", - "watch-extensions": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js watch-extensions watch-extension-media", + "watch-extensions": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js watch-extensions watch-extension-media", "watch-extensionsd": "deemon yarn watch-extensions", "kill-watch-extensionsd": "deemon --kill yarn watch-extensions", "precommit": "node build/hygiene.js", - "gulp": "node --max_old_space_size=8192 ./node_modules/gulp/bin/gulp.js", + "gulp": "node --max-old-space-size=8192 ./node_modules/gulp/bin/gulp.js", "electron": "node build/lib/electron", "7z": "7z", "update-grammars": "node build/npm/update-all-grammars.mjs", @@ -47,22 +47,22 @@ "update-distro": "node build/npm/update-distro.mjs", "web": "echo 'yarn web' is replaced by './scripts/code-server' or './scripts/code-web'", "compile-cli": "gulp compile-cli", - "compile-web": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js compile-web", - "watch-web": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js watch-web", - "watch-cli": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js watch-cli", + "compile-web": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js compile-web", + "watch-web": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js watch-web", + "watch-cli": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js watch-cli", "eslint": "node build/eslint", "stylelint": "node build/stylelint", "playwright-install": "node build/azure-pipelines/common/installPlaywright.js", - "compile-build": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js compile-build", - "compile-extensions-build": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js compile-extensions-build", - "minify-vscode": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js minify-vscode", - "minify-vscode-reh": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js minify-vscode-reh", - "minify-vscode-reh-web": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js minify-vscode-reh-web", - "hygiene": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js hygiene", - "core-ci": "node --max_old_space_size=8095 ./node_modules/gulp/bin/gulp.js core-ci", - "core-ci-pr": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js core-ci-pr", - "extensions-ci": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js extensions-ci", - "extensions-ci-pr": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js extensions-ci-pr", + "compile-build": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js compile-build", + "compile-extensions-build": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js compile-extensions-build", + "minify-vscode": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js minify-vscode", + "minify-vscode-reh": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js minify-vscode-reh", + "minify-vscode-reh-web": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js minify-vscode-reh-web", + "hygiene": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js hygiene", + "core-ci": "node --max-old-space-size=8095 ./node_modules/gulp/bin/gulp.js core-ci", + "core-ci-pr": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js core-ci-pr", + "extensions-ci": "node --max-old-space-size=8095 ./node_modules/gulp/bin/gulp.js extensions-ci", + "extensions-ci-pr": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js extensions-ci-pr", "perf": "node scripts/code-perf.js" }, "dependencies": { From 5be6890cf5f60bec243715b92f33e97a197e1413 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 19 Jan 2024 17:34:44 +0100 Subject: [PATCH 156/333] Git - add onCommit event to vscode.git extension API (#202863) --- extensions/git/src/api/api1.ts | 5 ++++- extensions/git/src/api/git.d.ts | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index c36362c282334..1a0a483409c27 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -7,11 +7,12 @@ import { Model } from '../model'; import { Repository as BaseRepository, Resource } from '../repository'; import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher, PostCommitCommandsProvider, RefQuery, BranchProtectionProvider, InitOptions } from './git'; import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands, CancellationToken } from 'vscode'; -import { combinedDisposable, mapEvent } from '../util'; +import { combinedDisposable, filterEvent, mapEvent } from '../util'; import { toGitUri } from '../uri'; import { GitExtensionImpl } from './extension'; import { GitBaseApi } from '../git-base'; import { PickRemoteSourceOptions } from './git-base'; +import { Operation, OperationResult } from '../operation'; class ApiInputBox implements InputBox { set value(value: string) { this._inputBox.value = value; } @@ -65,6 +66,8 @@ export class ApiRepository implements Repository { readonly state: RepositoryState = new ApiRepositoryState(this.repository); readonly ui: RepositoryUIState = new ApiRepositoryUIState(this.repository.sourceControl); + readonly onDidCommit: Event = mapEvent(filterEvent(this.repository.onDidRunOperation, e => e.operation === Operation.Commit), () => null); + constructor(readonly repository: BaseRepository) { } apply(patch: string, reverse?: boolean): Promise { diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 0b15542361951..6a53d89335284 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -194,6 +194,8 @@ export interface Repository { readonly state: RepositoryState; readonly ui: RepositoryUIState; + readonly onDidCommit: Event; + getConfigs(): Promise<{ key: string; value: string; }[]>; getConfig(key: string): Promise; setConfig(key: string, value: string): Promise; From e9a2a5823b50d1176ab6766c24c14e12b92fb184 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 19 Jan 2024 10:32:42 -0800 Subject: [PATCH 157/333] add terminal voice improvements --- .../contrib/terminal/browser/terminalVoice.ts | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalVoice.ts b/src/vs/workbench/contrib/terminal/browser/terminalVoice.ts index e2db1158ed468..dc1cf7c5878a0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalVoice.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalVoice.ts @@ -16,6 +16,7 @@ import type { IDecoration } from '@xterm/xterm'; import { IXtermMarker } from 'vs/platform/terminal/common/capabilities/capabilities'; import { ThemeIcon } from 'vs/base/common/themables'; import { Codicon } from 'vs/base/common/codicons'; +import { alert } from 'vs/base/browser/ui/aria/aria'; const symbolMap: { [key: string]: string } = { 'Ampersand': '&', @@ -82,7 +83,7 @@ export class TerminalVoiceSession extends Disposable { voiceTimeout = SpeechTimeoutDefault; } this._acceptTranscriptionScheduler = this._disposables.add(new RunOnceScheduler(() => { - this._terminalService.activeInstance?.sendText(this._input, false); + this._sendText(); this.stop(); }, voiceTimeout)); this._cancellationTokenSource = this._register(new CancellationTokenSource()); @@ -124,7 +125,7 @@ export class TerminalVoiceSession extends Disposable { this._setInactive(); if (send) { this._acceptTranscriptionScheduler!.cancel(); - this._terminalService.activeInstance?.sendText(this._input, false); + this._sendText(); } this._marker?.dispose(); this._ghostTextMarker?.dispose(); @@ -137,6 +138,11 @@ export class TerminalVoiceSession extends Disposable { this._input = ''; } + private _sendText(): void { + this._terminalService.activeInstance?.sendText(this._input, false); + alert(this._input + ' inserted'); + } + private _updateInput(e: ISpeechToTextEvent): void { if (e.text) { let input = e.text.replaceAll(/[.,?;!]/g, ''); @@ -153,7 +159,8 @@ export class TerminalVoiceSession extends Disposable { if (!xterm) { return; } - this._marker = activeInstance.registerMarker(-1); + const onFirstLine = xterm.buffer.active.cursorY === 0; + this._marker = activeInstance.registerMarker(onFirstLine ? 0 : -1); if (!this._marker) { return; } @@ -164,7 +171,7 @@ export class TerminalVoiceSession extends Disposable { }); this._decoration?.onRender((e: HTMLElement) => { e.classList.add(...ThemeIcon.asClassNameArray(Codicon.micFilled), 'terminal-voice', 'recording'); - e.style.transform = 'translate(-5px, -5px)'; + e.style.transform = onFirstLine ? 'translate(10px, -2px)' : 'translate(-6px, -5px)'; }); } @@ -187,15 +194,16 @@ export class TerminalVoiceSession extends Disposable { if (!this._ghostTextMarker) { return; } + const onFirstLine = xterm.buffer.active.cursorY === 0; this._ghostText = xterm.registerDecoration({ marker: this._ghostTextMarker, layer: 'top', - x: xterm.buffer.active.cursorX + 1 ?? 0, + x: onFirstLine ? xterm.buffer.active.cursorX + 4 : xterm.buffer.active.cursorX + 1 ?? 0, }); this._ghostText?.onRender((e: HTMLElement) => { e.classList.add('terminal-voice-progress-text'); e.textContent = text; - e.style.width = 'fit-content'; + e.style.width = (xterm.cols - xterm.buffer.active.cursorX) / xterm.cols * 100 + '%'; }); } } From 3565346a782fc7aed7412de34b95ff359eadb971 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 19 Jan 2024 10:34:28 -0800 Subject: [PATCH 158/333] localize --- src/vs/workbench/contrib/terminal/browser/terminalVoice.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalVoice.ts b/src/vs/workbench/contrib/terminal/browser/terminalVoice.ts index dc1cf7c5878a0..57464855e57f2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalVoice.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalVoice.ts @@ -17,6 +17,7 @@ import { IXtermMarker } from 'vs/platform/terminal/common/capabilities/capabilit import { ThemeIcon } from 'vs/base/common/themables'; import { Codicon } from 'vs/base/common/codicons'; import { alert } from 'vs/base/browser/ui/aria/aria'; +import { localize } from 'vs/nls'; const symbolMap: { [key: string]: string } = { 'Ampersand': '&', @@ -140,7 +141,7 @@ export class TerminalVoiceSession extends Disposable { private _sendText(): void { this._terminalService.activeInstance?.sendText(this._input, false); - alert(this._input + ' inserted'); + alert(localize('terminalVoiceTextInserted', '{0} inserted', this._input)); } private _updateInput(e: ISpeechToTextEvent): void { From a903fe6edde3a1d12c4a9ac90d5b7d3ee21c06a8 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 19 Jan 2024 10:54:17 -0800 Subject: [PATCH 159/333] fix #202873 --- src/vs/editor/common/standaloneStrings.ts | 4 ++++ .../browser/editorAccessibilityHelp.ts | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/vs/editor/common/standaloneStrings.ts b/src/vs/editor/common/standaloneStrings.ts index 112c5ff1caf27..c6472a818999d 100644 --- a/src/vs/editor/common/standaloneStrings.ts +++ b/src/vs/editor/common/standaloneStrings.ts @@ -27,6 +27,10 @@ export namespace AccessibilityHelpNLS { export const showAccessibilityHelpAction = nls.localize("showAccessibilityHelpAction", "Show Accessibility Help"); export const listAudioCues = nls.localize("listAudioCuesCommand", "Run the command: List Audio Cues for an overview of all audio cues and their current status."); export const listAlerts = nls.localize("listAlertsCommand", "Run the command: List Alerts for an overview of alerts and their current status."); + export const quickChat = nls.localize("quickChatCommand", "Toggle quick chat ({0}) to open or close a chat session."); + export const quickChatNoKb = nls.localize("quickChatCommandNoKb", "Toggle quick chat is not currently triggerable by a keybinding."); + export const startInlineChat = nls.localize("startInlineChatCommand", "Start inline chat ({0}) to create an in editor chat session."); + export const startInlineChatNoKb = nls.localize("startInlineChatCommandNoKb", "The command: Start inline chat is not currentlyt riggerable by a keybinding."); } export namespace InspectTokensNLS { diff --git a/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts b/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts index 2eafab6402d76..a084c6b378db6 100644 --- a/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts @@ -18,6 +18,7 @@ import { AccessibleViewProviderId, AccessibilityVerbositySettingId } from 'vs/wo import { descriptionForCommand } from 'vs/workbench/contrib/accessibility/browser/accessibilityContributions'; import { IAccessibleViewService, IAccessibleContentProvider, IAccessibleViewOptions, AccessibleViewType } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; +import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { CommentAccessibilityHelpNLS } from 'vs/workbench/contrib/comments/browser/commentsAccessibility'; import { CommentCommandId } from 'vs/workbench/contrib/comments/common/commentCommandIds'; import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys'; @@ -77,6 +78,11 @@ class EditorAccessibilityHelpProvider implements IAccessibleContentProvider { content.push(AccessibilityHelpNLS.listAudioCues); content.push(AccessibilityHelpNLS.listAlerts); + const chatCommandInfo = getChatCommandInfo(this._keybindingService, this._contextKeyService); + if (chatCommandInfo) { + content.push(chatCommandInfo); + } + const commentCommandInfo = getCommentCommandInfo(this._keybindingService, this._contextKeyService, this._editor); if (commentCommandInfo) { content.push(commentCommandInfo); @@ -109,3 +115,13 @@ export function getCommentCommandInfo(keybindingService: IKeybindingService, con } return; } + +export function getChatCommandInfo(keybindingService: IKeybindingService, contextKeyService: IContextKeyService): string | undefined { + if (CONTEXT_PROVIDER_EXISTS.getValue(contextKeyService)) { + const commentCommandInfo: string[] = []; + commentCommandInfo.push(descriptionForCommand('workbench.action.quickchat.toggle', AccessibilityHelpNLS.quickChat, AccessibilityHelpNLS.quickChatNoKb, keybindingService)); + commentCommandInfo.push(descriptionForCommand('inlineChat.start', AccessibilityHelpNLS.startInlineChat, AccessibilityHelpNLS.startInlineChatNoKb, keybindingService)); + return commentCommandInfo.join('\n'); + } + return; +} From b0c5818e205b59584093b322fb38b6f73c6bbd6f Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 19 Jan 2024 11:07:08 -0800 Subject: [PATCH 160/333] variable view styling (#202886) * tree view styling * use the render method from the debug view * no colon when value is empty --- .../notebookVariablesTree.ts | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts index 2fa75095b6ec5..be76d2647ce79 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts @@ -10,8 +10,11 @@ import { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { FuzzyScore } from 'vs/base/common/filters'; import { localize } from 'vs/nls'; import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; +import { renderExpressionValue } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { INotebookVariableElement } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource'; +const $ = dom.$; + export class NotebookVariablesTree extends WorkbenchObjectTree { } export class NotebookVariablesDelegate implements IListVirtualDelegate { @@ -25,7 +28,13 @@ export class NotebookVariablesDelegate implements IListVirtualDelegate { +export interface IVariableTemplateData { + expression: HTMLElement; + name: HTMLSpanElement; + value: HTMLSpanElement; +} + +export class NotebookVariableRenderer implements ITreeRenderer { static readonly ID = 'variableElement'; @@ -33,13 +42,21 @@ export class NotebookVariableRenderer implements ITreeRenderer, _index: number, templateData: { wrapper: HTMLElement }): void { - templateData.wrapper.innerText = `${element.element.name}: ${element.element.value}`; + renderElement(element: ITreeNode, _index: number, data: IVariableTemplateData): void { + const text = element.element.value.trim() !== '' ? `${element.element.name}:` : element.element.name; + data.name.textContent = text; + + renderExpressionValue(element.element.value, data.value, { colorize: true, showHover: true }); } disposeTemplate(): void { From 6c8cbf45d2bad0c653318830475baa08902f3b0a Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Fri, 19 Jan 2024 11:31:07 -0800 Subject: [PATCH 161/333] Enable followup for Cell Chat (#202769) * Enable followup for Cell Chat --- .../view/cellParts/chat/cellChatController.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts index 145bd1772e8a1..0c2bfa6a1ae0f 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts @@ -23,6 +23,7 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { localize } from 'vs/nls'; import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { MenuId } from 'vs/platform/actions/common/actions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { AsyncProgress } from 'vs/platform/progress/common/progress'; @@ -80,6 +81,7 @@ export class NotebookCellChatController extends Disposable { @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @INotebookExecutionStateService private readonly _notebookExecutionStateService: INotebookExecutionStateService, + @ICommandService private readonly _commandService: ICommandService, ) { super(); @@ -369,6 +371,22 @@ export class NotebookCellChatController extends Disposable { for (let i = progressEdits.length; i < replyResponse.allLocalEdits.length; i++) { await this._makeChanges(editor, replyResponse.allLocalEdits[i], undefined); } + + if (this._activeSession?.provider.provideFollowups) { + const followupCts = new CancellationTokenSource(); + const followups = await this._activeSession.provider.provideFollowups(this._activeSession.session, replyResponse.raw, followupCts.token); + if (followups && this._widget) { + const widget = this._widget; + widget.updateFollowUps(followups, followup => { + if (followup.kind === 'reply') { + widget.value = followup.message; + this.acceptInput(); + } else { + this._commandService.executeCommand(followup.commandId, ...(followup.args ?? [])); + } + }); + } + } } } catch (e) { response = new ErrorResponse(e); From 0865a604d2fd74db5b86f4587503085c9c29d8a9 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 19 Jan 2024 11:43:02 -0800 Subject: [PATCH 162/333] testing: finalize selected profiles (#202878) Closes #193160 --- src/vs/workbench/api/common/extHostTesting.ts | 6 +----- .../api/test/browser/extHostTesting.test.ts | 2 +- .../extensions/common/extensionsApiProposals.ts | 1 - src/vscode-dts/vscode.d.ts | 6 ++++++ .../vscode.proposed.testingActiveProfile.d.ts | 16 ---------------- 5 files changed, 8 insertions(+), 23 deletions(-) delete mode 100644 src/vscode-dts/vscode.proposed.testingActiveProfile.d.ts diff --git a/src/vs/workbench/api/common/extHostTesting.ts b/src/vs/workbench/api/common/extHostTesting.ts index fa062b025471b..597865e61c003 100644 --- a/src/vs/workbench/api/common/extHostTesting.ts +++ b/src/vs/workbench/api/common/extHostTesting.ts @@ -148,7 +148,7 @@ export class ExtHostTesting extends Disposable implements ExtHostTestingShape { profileId++; } - return new TestRunProfileImpl(this.proxy, profiles, extension, activeProfiles, this.defaultProfilesChangedEmitter.event, controllerId, profileId, label, group, runHandler, isDefault, tag, supportsContinuousRun); + return new TestRunProfileImpl(this.proxy, profiles, activeProfiles, this.defaultProfilesChangedEmitter.event, controllerId, profileId, label, group, runHandler, isDefault, tag, supportsContinuousRun); }, createTestItem(id, label, uri) { return new TestItemImpl(controllerId, id, label, uri); @@ -1068,7 +1068,6 @@ const updateProfile = (impl: TestRunProfileImpl, proxy: MainThreadTestingShape, export class TestRunProfileImpl implements vscode.TestRunProfile { readonly #proxy: MainThreadTestingShape; - readonly #extension: IRelaxedExtensionDescription; readonly #activeProfiles: Set; readonly #onDidChangeDefaultProfiles: Event; #initialPublish?: ITestRunProfile; @@ -1140,7 +1139,6 @@ export class TestRunProfileImpl implements vscode.TestRunProfile { } public get onDidChangeDefault() { - checkProposedApiEnabled(this.#extension, 'testingActiveProfile'); return Event.chain(this.#onDidChangeDefaultProfiles, $ => $ .map(ev => ev.get(this.controllerId)?.get(this.profileId)) .filter(isDefined) @@ -1150,7 +1148,6 @@ export class TestRunProfileImpl implements vscode.TestRunProfile { constructor( proxy: MainThreadTestingShape, profiles: Map, - extension: IRelaxedExtensionDescription, activeProfiles: Set, onDidChangeActiveProfiles: Event, public readonly controllerId: string, @@ -1164,7 +1161,6 @@ export class TestRunProfileImpl implements vscode.TestRunProfile { ) { this.#proxy = proxy; this.#profiles = profiles; - this.#extension = extension; this.#activeProfiles = activeProfiles; this.#onDidChangeDefaultProfiles = onDidChangeActiveProfiles; profiles.set(profileId, this); diff --git a/src/vs/workbench/api/test/browser/extHostTesting.test.ts b/src/vs/workbench/api/test/browser/extHostTesting.test.ts index 62df7ae029fa6..f52b523cb1c98 100644 --- a/src/vs/workbench/api/test/browser/extHostTesting.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTesting.test.ts @@ -610,7 +610,7 @@ suite('ExtHost Testing', () => { cts = new CancellationTokenSource(); c = new TestRunCoordinator(proxy, new NullLogService()); - configuration = new TestRunProfileImpl(mockObject()(), new Map(), nullExtensionDescription, new Set(), Event.None, 'ctrlId', 42, 'Do Run', TestRunProfileKind.Run, () => { }, false); + configuration = new TestRunProfileImpl(mockObject()(), new Map(), new Set(), Event.None, 'ctrlId', 42, 'Do Run', TestRunProfileKind.Run, () => { }, false); await single.expand(single.root.id, Infinity); single.collectDiff(); diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index c8d09cf0969e8..e80c095be8450 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -101,7 +101,6 @@ export const allApiProposals = Object.freeze({ terminalSelection: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalSelection.d.ts', testCoverage: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testCoverage.d.ts', testObserver: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testObserver.d.ts', - testingActiveProfile: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testingActiveProfile.d.ts', textSearchProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.textSearchProvider.d.ts', timeline: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.timeline.d.ts', tokenInformation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tokenInformation.d.ts', diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index da13bcf7a35d2..b35ff0b13f4d1 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -17149,6 +17149,12 @@ declare module 'vscode' { */ isDefault: boolean; + /** + * Fired when a user has changed whether this is a default profile. The + * event contains the new value of {@link isDefault} + */ + onDidChangeDefault: Event; + /** * Whether this profile supports continuous running of requests. If so, * then {@link TestRunRequest.continuous} may be set to `true`. Defaults diff --git a/src/vscode-dts/vscode.proposed.testingActiveProfile.d.ts b/src/vscode-dts/vscode.proposed.testingActiveProfile.d.ts deleted file mode 100644 index 474489f45a6b6..0000000000000 --- a/src/vscode-dts/vscode.proposed.testingActiveProfile.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// https://github.com/microsoft/vscode/issues/193160 @connor4312 - -declare module 'vscode' { - export interface TestRunProfile { - /** - * Fired when a user has changed whether this is a default profile. The - * event contains the new value of {@link isDefault} - */ - onDidChangeDefault: Event; - } -} From b6dc6c4a80948ea4b112d9ab616f43d624162712 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 19 Jan 2024 11:57:03 -0800 Subject: [PATCH 163/333] fix every child of a long array being highlighted when expanded (#202893) provide unique id for each tree node --- .../notebookVariablesDataSource.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts index 5a61cec80ffe2..8ddec399c2ce3 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts @@ -15,7 +15,8 @@ export interface INotebookScope { export interface INotebookVariableElement { type: 'variable'; - readonly id: number; + readonly id: string; + readonly extHostId: number; readonly name: string; readonly value: string; readonly indexedChildrenCount: number; @@ -46,7 +47,7 @@ export class NotebookVariableDataSource implements IAsyncDataSource { return this.createVariableElement(variable, parent.notebook); }) .toPromise(); @@ -75,7 +76,8 @@ export class NotebookVariableDataSource implements IAsyncDataSource 0) { - const variables = kernel.provideVariables(parent.notebook.uri, parent.id, 'indexed', parent.indexStart ?? 0, CancellationToken.None); + const variables = kernel.provideVariables(parent.notebook.uri, parent.extHostId, 'indexed', parent.indexStart ?? 0, CancellationToken.None); for await (const variable of variables) { childNodes.push(this.createVariableElement(variable, parent.notebook)); @@ -112,9 +114,11 @@ export class NotebookVariableDataSource implements IAsyncDataSource Date: Fri, 19 Jan 2024 12:04:06 -0800 Subject: [PATCH 164/333] Cell chat: disable auto save for auto generated code (#202892) * Cell chat: disable auto save for auto generated code --- .../notebook/browser/view/cellParts/chat/cellChatController.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts index 0c2bfa6a1ae0f..58aa70314cf19 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts @@ -38,6 +38,7 @@ import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inline import { CTX_INLINE_CHAT_LAST_RESPONSE_TYPE, CTX_INLINE_CHAT_VISIBLE, EditMode, IInlineChatProgressItem, IInlineChatRequest, InlineChatResponseFeedbackKind, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { INotebookExecutionStateService, NotebookExecutionType } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; +import { IInlineChatSavingService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSavingService'; export const CTX_NOTEBOOK_CELL_CHAT_FOCUSED = new RawContextKey('notebookCellChatFocused', false, localize('notebookCellChatFocused', "Whether the cell chat editor is focused")); export const CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST = new RawContextKey('notebookChatHasActiveRequest', false, localize('notebookChatHasActiveRequest', "Whether the cell chat editor has an active request")); @@ -82,6 +83,7 @@ export class NotebookCellChatController extends Disposable { @IInstantiationService private readonly _instantiationService: IInstantiationService, @INotebookExecutionStateService private readonly _notebookExecutionStateService: INotebookExecutionStateService, @ICommandService private readonly _commandService: ICommandService, + @IInlineChatSavingService private readonly _inlineChatSavingService: IInlineChatSavingService, ) { super(); @@ -466,6 +468,7 @@ export class NotebookCellChatController extends Disposable { const actualEdits = !opts && moreMinimalEdits ? moreMinimalEdits : edits; const editOperations = actualEdits.map(TextEdit.asEditOperation); + this._inlineChatSavingService.markChanged(this._activeSession); try { // this._ignoreModelContentChanged = true; this._activeSession.wholeRange.trackEdits(editOperations); From 4c316c04ca3ef6c34ad01005bb0d9a4710532303 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 19 Jan 2024 13:27:15 -0800 Subject: [PATCH 165/333] enable move to previous cell command when screen reader enabled (#202883) * remove inline chat related move between cells hotkey * remove precondition --- .../browser/contrib/navigation/arrow.ts | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/navigation/arrow.ts b/src/vs/workbench/contrib/notebook/browser/contrib/navigation/arrow.ts index 93c94c90d60a4..2436576bbce5e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/navigation/arrow.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/navigation/arrow.ts @@ -18,7 +18,6 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; -import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_LAST } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { INotebookActionContext, INotebookCellActionContext, NotebookAction, NotebookCellAction, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, findTargetCellEditor } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { CellEditState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellKind, NOTEBOOK_EDITOR_CURSOR_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -78,39 +77,6 @@ registerAction2(class FocusNextCellAction extends NotebookCellAction { mac: { primary: KeyMod.WinCtrl | KeyMod.CtrlCmd | KeyCode.DownArrow, }, weight: KeybindingWeight.WorkbenchContrib }, - { - when: ContextKeyExpr.and( - NOTEBOOK_EDITOR_FOCUSED, - CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate(), - ContextKeyExpr.equals('config.notebook.navigation.allowNavigateToSurroundingCells', true), - ContextKeyExpr.and( - ContextKeyExpr.has(InputFocusedContextKey), - NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('top'), - NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none'), - ), - CTX_INLINE_CHAT_FOCUSED, - CTX_INLINE_CHAT_INNER_CURSOR_LAST, - EditorContextKeys.isEmbeddedDiffEditor.negate() - ), - primary: KeyCode.DownArrow, - weight: KeybindingWeight.EditorCore - }, - { - when: ContextKeyExpr.and( - NOTEBOOK_EDITOR_FOCUSED, - CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate(), - ContextKeyExpr.equals('config.notebook.navigation.allowNavigateToSurroundingCells', true), - ContextKeyExpr.and( - NOTEBOOK_CELL_TYPE.isEqualTo('markup'), - NOTEBOOK_CELL_MARKDOWN_EDIT_MODE.isEqualTo(false), - NOTEBOOK_CURSOR_NAVIGATION_MODE), - CTX_INLINE_CHAT_FOCUSED, - CTX_INLINE_CHAT_INNER_CURSOR_LAST, - EditorContextKeys.isEmbeddedDiffEditor.negate() - ), - primary: KeyCode.DownArrow, - weight: KeybindingWeight.EditorCore - }, { when: NOTEBOOK_EDITOR_FOCUSED, primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageDown, @@ -155,7 +121,6 @@ registerAction2(class FocusPreviousCellAction extends NotebookCellAction { super({ id: NOTEBOOK_FOCUS_PREVIOUS_EDITOR, title: localize('cursorMoveUp', 'Focus Previous Cell Editor'), - precondition: CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate(), keybinding: [ { when: ContextKeyExpr.and( From 5b18cd75cb347b9813be48f2cffdba616924531a Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 19 Jan 2024 13:55:04 -0800 Subject: [PATCH 166/333] include variable type information (#202896) include type information --- .../workbench/api/common/extHostNotebookKernels.ts | 1 + .../notebookVariablesDataSource.ts | 13 +++++++------ .../notebookVariables/notebookVariablesTree.ts | 1 + .../notebookVariables/notebookVariablesView.ts | 6 +++--- .../notebook/common/notebookKernelService.ts | 1 + .../vscode.proposed.notebookVariableProvider.d.ts | 3 +++ 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 8a087a434e7be..265b29c33d364 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -456,6 +456,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { id: this.id++, name: result.variable.name, value: result.variable.value, + type: result.variable.type, hasNamedChildren: result.hasNamedChildren, indexedChildrenCount: result.indexedChildrenCount }; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts index 8ddec399c2ce3..bb17daadf2ded 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts @@ -9,16 +9,17 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { INotebookKernel, INotebookKernelService, VariablesResult, variablePageSize } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; export interface INotebookScope { - type: 'root'; + kind: 'root'; readonly notebook: NotebookTextModel; } export interface INotebookVariableElement { - type: 'variable'; + kind: 'variable'; readonly id: string; readonly extHostId: number; readonly name: string; readonly value: string; + readonly type?: string; readonly indexedChildrenCount: number; readonly indexStart?: number; readonly hasNamedChildren: boolean; @@ -30,11 +31,11 @@ export class NotebookVariableDataSource implements IAsyncDataSource 0; + return element.kind === 'root' || element.hasNamedChildren || element.indexedChildrenCount > 0; } async getChildren(element: INotebookScope | INotebookVariableElement): Promise> { - if (element.type === 'root') { + if (element.kind === 'root') { return this.getRootVariables(element.notebook); } else { return this.getVariables(element); @@ -74,7 +75,7 @@ export class NotebookVariableDataSource implements IAsyncDataSource, _index: number, data: IVariableTemplateData): void { const text = element.element.value.trim() !== '' ? `${element.element.name}:` : element.element.name; data.name.textContent = text; + data.name.title = element.element.type ?? ''; renderExpressionValue(element.element.value, data.value, { colorize: true, showHover: true }); } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index 698f39e668c68..3ecc61d3428dd 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -78,7 +78,7 @@ export class NotebookVariablesView extends ViewPane { this.tree.layout(); if (this.activeNotebook) { - this.tree.setInput({ type: 'root', notebook: this.activeNotebook }); + this.tree.setInput({ kind: 'root', notebook: this.activeNotebook }); } } @@ -100,7 +100,7 @@ export class NotebookVariablesView extends ViewPane { private handleActiveEditorChange() { if (this.setActiveNotebook() && this.activeNotebook) { - this.tree?.setInput({ type: 'root', notebook: this.activeNotebook }); + this.tree?.setInput({ kind: 'root', notebook: this.activeNotebook }); this.tree?.updateChildren(); } } @@ -116,7 +116,7 @@ export class NotebookVariablesView extends ViewPane { private handleVariablesChanged(notebookUri: URI) { if (this.activeNotebook && notebookUri.toString() === this.activeNotebook.uri.toString()) { - this.tree?.setInput({ type: 'root', notebook: this.activeNotebook }); + this.tree?.setInput({ kind: 'root', notebook: this.activeNotebook }); this.tree?.updateChildren(); } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index ca3e3f7bf3aac..be999203d7524 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -42,6 +42,7 @@ export interface VariablesResult { id: number; name: string; value: string; + type?: string; hasNamedChildren: boolean; indexedChildrenCount: number; } diff --git a/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts index ce3de6ebbfc80..46258e0432c49 100644 --- a/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookVariableProvider.d.ts @@ -38,6 +38,9 @@ declare module 'vscode' { An empty string can be used if no value should be shown in the UI. */ value: string; + + /** The type of the variable's value */ + type?: string; } } From 3ec9b2aed85ad6062deaa3a1ab8f797ed8d5c813 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 19 Jan 2024 15:03:23 -0800 Subject: [PATCH 167/333] type based coloring (#202901) * type based coloring * include valueChanged in value interface --- .../workbench/contrib/debug/browser/baseDebugView.ts | 6 +++--- src/vs/workbench/contrib/debug/common/debug.ts | 11 +++++++---- .../notebookVariables/notebookVariablesTree.ts | 7 ++++++- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index 058167334d83a..4399464dbc05d 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -19,7 +19,7 @@ import { localize } from 'vs/nls'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { defaultInputBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector'; -import { IDebugService, IExpression, IExpressionContainer } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IExpression, IExpressionValue } from 'vs/workbench/contrib/debug/common/debug'; import { Expression, ExpressionContainer, Variable } from 'vs/workbench/contrib/debug/common/debugModel'; import { ReplEvaluationResult } from 'vs/workbench/contrib/debug/common/replModel'; @@ -51,7 +51,7 @@ export function renderViewTree(container: HTMLElement): HTMLElement { return treeContainer; } -export function renderExpressionValue(expressionOrValue: IExpressionContainer | string, container: HTMLElement, options: IRenderValueOptions): void { +export function renderExpressionValue(expressionOrValue: IExpressionValue | string, container: HTMLElement, options: IRenderValueOptions): void { let value = typeof expressionOrValue === 'string' ? expressionOrValue : expressionOrValue.value; // remove stale classes @@ -63,7 +63,7 @@ export function renderExpressionValue(expressionOrValue: IExpressionContainer | container.classList.add('error'); } } else { - if ((expressionOrValue instanceof ExpressionContainer) && options.showChanged && expressionOrValue.valueChanged && value !== Expression.DEFAULT_VALUE) { + if (typeof expressionOrValue !== 'string' && options.showChanged && expressionOrValue.valueChanged && value !== Expression.DEFAULT_VALUE) { // value changed color has priority over other colors. container.className = 'value changed'; expressionOrValue.valueChanged = false; diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 2bdd7fa756c46..06d374cbb80e9 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -148,15 +148,18 @@ export interface IReplElementSource { readonly column: number; } -export interface IExpressionContainer extends ITreeElement { +export interface IExpressionValue { + readonly value: string; + readonly type?: string; + valueChanged?: boolean; +} + +export interface IExpressionContainer extends ITreeElement, IExpressionValue { readonly hasChildren: boolean; evaluateLazy(): Promise; getChildren(): Promise; readonly reference?: number; readonly memoryReference?: string; - readonly value: string; - readonly type?: string; - valueChanged?: boolean; readonly presentationHint?: DebugProtocol.VariablePresentationHint | undefined; } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts index 3d2355006e5bf..5ffe8b8c96f92 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree.ts @@ -14,6 +14,7 @@ import { renderExpressionValue } from 'vs/workbench/contrib/debug/browser/baseDe import { INotebookVariableElement } from 'vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource'; const $ = dom.$; +const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; export class NotebookVariablesTree extends WorkbenchObjectTree { } @@ -57,7 +58,11 @@ export class NotebookVariableRenderer implements ITreeRenderer Date: Fri, 19 Jan 2024 18:43:40 -0600 Subject: [PATCH 168/333] fix local file search in vscode.dev (#202905) Can no longer search across all the files in the workspace Fixes #201138 --- .../workbench/services/search/browser/searchService.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/search/browser/searchService.ts b/src/vs/workbench/services/search/browser/searchService.ts index 9a8bd1274762d..f9aea8ee65a31 100644 --- a/src/vs/workbench/services/search/browser/searchService.ts +++ b/src/vs/workbench/services/search/browser/searchService.ts @@ -100,9 +100,16 @@ export class LocalFileSearchWorkerClient extends Disposable implements ISearchRe return; } + // force resource to revive using URI.revive. + // TODO @andrea see why we can't just use `revive()` below. For some reason, (obj).$mid was undefined for result.resource + const reviveMatch = (result: IFileMatch): IFileMatch => ({ + resource: URI.revive(result.resource), + results: revive(result.results) + }); + queryDisposables.add(this.onDidReceiveTextSearchMatch(e => { if (e.queryId === queryId) { - onProgress?.(revive(e.match)); + onProgress?.(reviveMatch(e.match)); } })); From 68f4339e5ec96cfb8a0077f3957016fa2c083856 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 19 Jan 2024 17:11:29 -0800 Subject: [PATCH 169/333] manage scheduling of getting children to prevent too many requests (#202904) --- .../notebookVariablesDataSource.ts | 20 +++++++++---- .../notebookVariablesView.ts | 29 ++++++++++++++----- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts index bb17daadf2ded..d23184d77a5db 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookKernel, INotebookKernelService, VariablesResult, variablePageSize } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; @@ -28,12 +28,22 @@ export interface INotebookVariableElement { export class NotebookVariableDataSource implements IAsyncDataSource { - constructor(private readonly notebookKernelService: INotebookKernelService) { } + private cancellationTokenSource: CancellationTokenSource; + + constructor(private readonly notebookKernelService: INotebookKernelService) { + this.cancellationTokenSource = new CancellationTokenSource(); + } hasChildren(element: INotebookScope | INotebookVariableElement): boolean { return element.kind === 'root' || element.hasNamedChildren || element.indexedChildrenCount > 0; } + public cancel(): void { + this.cancellationTokenSource.cancel(); + this.cancellationTokenSource.dispose(); + this.cancellationTokenSource = new CancellationTokenSource(); + } + async getChildren(element: INotebookScope | INotebookVariableElement): Promise> { if (element.kind === 'root') { return this.getRootVariables(element.notebook); @@ -48,7 +58,7 @@ export class NotebookVariableDataSource implements IAsyncDataSource { return this.createVariableElement(variable, parent.notebook); }) .toPromise(); @@ -88,7 +98,7 @@ export class NotebookVariableDataSource implements IAsyncDataSource 0) { - const variables = kernel.provideVariables(parent.notebook.uri, parent.extHostId, 'indexed', parent.indexStart ?? 0, CancellationToken.None); + const variables = kernel.provideVariables(parent.notebook.uri, parent.extHostId, 'indexed', parent.indexStart ?? 0, this.cancellationTokenSource.token); for await (const variable of variables) { childNodes.push(this.createVariableElement(variable, parent.notebook)); @@ -104,7 +114,7 @@ export class NotebookVariableDataSource implements IAsyncDataSource { const selectedKernel = this.notebookKernelService.getMatchingKernel(notebook).selected; if (selectedKernel && selectedKernel.hasVariableProvider) { - const variables = selectedKernel.provideVariables(notebook.uri, undefined, 'named', 0, CancellationToken.None); + const variables = selectedKernel.provideVariables(notebook.uri, undefined, 'named', 0, this.cancellationTokenSource.token); return await variables .map(variable => { return this.createVariableElement(variable, notebook); }) .toPromise(); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index 3ecc61d3428dd..d96d49333287e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { RunOnceScheduler } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { ILocalizedString } from 'vs/platform/action/common/action'; @@ -34,6 +35,9 @@ export class NotebookVariablesView extends ViewPane { private tree: WorkbenchAsyncDataTree | undefined; private activeNotebook: NotebookTextModel | undefined; + private readonly dataSource: NotebookVariableDataSource; + + private updateScheduler: RunOnceScheduler; constructor( options: IViewPaneOptions, @@ -59,6 +63,9 @@ export class NotebookVariablesView extends ViewPane { this._register(this.notebookExecutionStateService.onDidChangeExecution(this.handleExecutionStateChange.bind(this))); this.setActiveNotebook(); + + this.dataSource = new NotebookVariableDataSource(this.notebookKernelService); + this.updateScheduler = new RunOnceScheduler(() => this.tree?.updateChildren(), 100); } protected override renderBody(container: HTMLElement): void { @@ -70,7 +77,7 @@ export class NotebookVariablesView extends ViewPane { container, new NotebookVariablesDelegate(), [new NotebookVariableRenderer()], - new NotebookVariableDataSource(this.notebookKernelService), + this.dataSource, { accessibilityProvider: new NotebookVariableAccessibilityProvider(), identityProvider: { getId: (e: INotebookVariableElement) => e.id }, @@ -87,7 +94,7 @@ export class NotebookVariablesView extends ViewPane { this.tree?.layout(height, width); } - setActiveNotebook() { + private setActiveNotebook() { const current = this.activeNotebook; const activeEditorPane = this.editorService.activeEditorPane; if (activeEditorPane && activeEditorPane.getId() === 'workbench.editor.notebook') { @@ -101,15 +108,23 @@ export class NotebookVariablesView extends ViewPane { private handleActiveEditorChange() { if (this.setActiveNotebook() && this.activeNotebook) { this.tree?.setInput({ kind: 'root', notebook: this.activeNotebook }); - this.tree?.updateChildren(); + this.updateScheduler.schedule(); } } private handleExecutionStateChange(event: ICellExecutionStateChangedEvent | IExecutionStateChangedEvent) { if (this.activeNotebook) { - // changed === undefined -> excecution ended - if (event.changed === undefined && event.affectsNotebook(this.activeNotebook?.uri)) { - this.tree?.updateChildren(); + if (event.affectsNotebook(this.activeNotebook.uri)) { + // new execution state means either new variables or the kernel is busy so we shouldn't ask + this.dataSource.cancel(); + + // changed === undefined -> excecution ended + if (event.changed === undefined) { + this.updateScheduler.schedule(); + } + else { + this.updateScheduler.cancel(); + } } } } @@ -117,7 +132,7 @@ export class NotebookVariablesView extends ViewPane { private handleVariablesChanged(notebookUri: URI) { if (this.activeNotebook && notebookUri.toString() === this.activeNotebook.uri.toString()) { this.tree?.setInput({ kind: 'root', notebook: this.activeNotebook }); - this.tree?.updateChildren(); + this.updateScheduler.schedule(); } } } From 564d6ae4c7c0e1158d3d6bd15d925a0c4430ecaf Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 20 Jan 2024 05:56:07 -0300 Subject: [PATCH 170/333] Remove internal implementation of ChatAgentTask (#202908) * Get rid of chat progress Task * Remove internal implementation of ChatAgentTask * Clean up more --- .../api/browser/mainThreadChatAgents2.ts | 31 ++----------------- .../workbench/api/common/extHost.protocol.ts | 12 +++---- .../contrib/chat/browser/chatListRenderer.ts | 26 ++-------------- .../contrib/chat/common/chatModel.ts | 31 ++----------------- .../contrib/chat/common/chatService.ts | 10 ------ .../chat/test/common/chatModel.test.ts | 17 +--------- 6 files changed, 13 insertions(+), 114 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts index fc7271c151653..7be5e2e6ea041 100644 --- a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts +++ b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts @@ -3,9 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DeferredPromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IMarkdownString } from 'vs/base/common/htmlContent'; import { Disposable, DisposableMap, IDisposable } from 'vs/base/common/lifecycle'; import { revive } from 'vs/base/common/marshalling'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; @@ -23,7 +21,7 @@ import { AddDynamicVariableAction, IAddDynamicVariableContext } from 'vs/workben import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatRequestAgentPart } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser'; -import { IChatFollowup, IChatProgress, IChatService, IChatTreeData } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatFollowup, IChatProgress, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; type AgentData = { @@ -42,9 +40,6 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA private readonly _pendingProgress = new Map void>(); private readonly _proxy: ExtHostChatAgentsShape2; - private _responsePartHandlePool = 0; - private readonly _activeResponsePartPromises = new Map>(); - constructor( extHostContext: IExtHostContext, @IChatAgentService private readonly _chatAgentService: IChatAgentService, @@ -123,29 +118,7 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA this._chatAgentService.updateAgent(data.name, revive(metadataUpdate)); } - async $handleProgressChunk(requestId: string, progress: IChatProgressDto, responsePartHandle?: number): Promise { - if (progress.kind === 'asyncContent') { - const handle = ++this._responsePartHandlePool; - const responsePartId = `${requestId}_${handle}`; - const deferredContentPromise = new DeferredPromise(); - this._activeResponsePartPromises.set(responsePartId, deferredContentPromise); - this._pendingProgress.get(requestId)?.({ ...progress, resolvedContent: deferredContentPromise.p }); - return handle; - } else if (typeof responsePartHandle === 'number') { - // Complete an existing deferred promise with resolved content - const responsePartId = `${requestId}_${responsePartHandle}`; - const deferredContentPromise = this._activeResponsePartPromises.get(responsePartId); - if (deferredContentPromise && progress.kind === 'treeData') { - const withRevivedUris = revive(progress); - deferredContentPromise.complete(withRevivedUris); - this._activeResponsePartPromises.delete(responsePartId); - } else if (deferredContentPromise && progress.kind === 'content') { - deferredContentPromise.complete(progress.content); - this._activeResponsePartPromises.delete(responsePartId); - } - return responsePartHandle; - } - + async $handleProgressChunk(requestId: string, progress: IChatProgressDto): Promise { const revivedProgress = revive(progress); this._pendingProgress.get(requestId)?.(revivedProgress as IChatProgress); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 68ac001fd367e..b4605fd211186 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -53,7 +53,7 @@ import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/cal import { IChatAgentCommand, IChatAgentMetadata, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents'; import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel'; import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } from 'vs/workbench/contrib/chat/common/chatProvider'; -import { IChatAsyncContent, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatReplyFollowup, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatDynamicRequest, IChatFollowup, IChatProgress, IChatReplyFollowup, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; import { DebugConfigurationProviderTriggerKind, MainThreadDebugVisualization, IAdapterDescriptor, IConfig, IDebugSessionReplMode, IDebugVisualization, IDebugVisualizationContext } from 'vs/workbench/contrib/debug/common/debug'; import { IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatProgressItem, IInlineChatRequest, IInlineChatSession, InlineChatResponseFeedbackKind } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; @@ -1195,7 +1195,7 @@ export interface MainThreadChatAgentsShape2 extends IDisposable { $unregisterAgentCompletionsProvider(handle: number): void; $updateAgent(handle: number, metadataUpdate: IExtensionChatAgentMetadata): void; $unregisterAgent(handle: number): void; - $handleProgressChunk(requestId: string, chunk: IChatProgressDto, responsePartHandle?: number): Promise; + $handleProgressChunk(requestId: string, chunk: IChatProgressDto): Promise; } export interface IChatAgentCompletionItem { @@ -1207,8 +1207,7 @@ export interface IChatAgentCompletionItem { } export type IChatContentProgressDto = - | Dto> - | IChatAsyncContentDto; + | Dto; export type IChatAgentHistoryEntryDto = { request: IChatAgentRequest; @@ -1293,11 +1292,8 @@ export type IDocumentContextDto = { ranges: IRange[]; }; -export type IChatAsyncContentDto = Dto>; - export type IChatProgressDto = - | Dto> - | IChatAsyncContentDto; + | Dto; export interface MainThreadChatShape extends IDisposable { $registerChatProvider(handle: number, id: string): Promise; diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index ed78c17b82fc4..3e8aa42bb21c7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -442,9 +442,8 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer content.dispose() }; - } - private renderProgressMessage(progress: IChatProgressMessage, showSpinner: boolean): IMarkdownRenderResult { if (showSpinner) { // this step is in progress, communicate it to SR users diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 0a3ebbaa0c857..8bec2aa5ec7be 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -16,7 +16,7 @@ import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { ILogService } from 'vs/platform/log/common/log'; import { IChatAgentCommand, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChat, IChatAgentMarkdownContentWithVulnerability, IChatAsyncContent, IChatContent, IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatProgress, IChatProgressMessage, IChatReplyFollowup, IChatResponse, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatTreeData, IChatUsedContext, InteractiveSessionVoteDirection, isIUsedContext } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChat, IChatAgentMarkdownContentWithVulnerability, IChatContent, IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatProgress, IChatProgressMessage, IChatReplyFollowup, IChatResponse, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatTreeData, IChatUsedContext, InteractiveSessionVoteDirection, isIUsedContext } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; export interface IChatRequestVariableData { @@ -42,7 +42,6 @@ export type IChatProgressResponseContent = | IChatMarkdownContent | IChatAgentMarkdownContentWithVulnerability | IChatTreeData - | IChatAsyncContent | IChatContentInlineReference | IChatProgressMessage; @@ -160,22 +159,6 @@ export class Response implements IResponse { } this._updateRepr(quiet); - } else if (progress.kind === 'asyncContent') { - // Add a new resolving part - const responsePosition = this._responseParts.push(progress) - 1; - this._updateRepr(quiet); - - progress.resolvedContent?.then((content) => { - // Replace the resolving part's content with the resolved response - if (typeof content === 'string') { - this._responseParts[responsePosition] = { content: new MarkdownString(content), kind: 'markdownContent' }; - } else if (isMarkdownString(content)) { - this._responseParts[responsePosition] = { content, kind: 'markdownContent' }; - } else { - this._responseParts[responsePosition] = content; - } - this._updateRepr(quiet); - }); } else if (progress.kind === 'treeData' || progress.kind === 'inlineReference' || progress.kind === 'markdownVuln' || progress.kind === 'progressMessage') { this._responseParts.push(progress); this._updateRepr(quiet); @@ -188,8 +171,6 @@ export class Response implements IResponse { return ''; } else if (part.kind === 'inlineReference') { return basename('uri' in part.inlineReference ? part.inlineReference.uri : part.inlineReference); - } else if (part.kind === 'asyncContent') { - return part.content; } else { return part.content.value; } @@ -303,15 +284,12 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel /** * Apply one of the progress updates that are not part of the actual response content. */ - applyReference(progress: IChatUsedContext | IChatContentReference | IChatProgressMessage) { + applyReference(progress: IChatUsedContext | IChatContentReference) { if (progress.kind === 'usedContext') { this._usedContext = progress; } else if (progress.kind === 'reference') { this._contentReferences.push(progress); this._onDidChange.fire(); - } else if (progress.kind === 'progressMessage') { - // this._progressMessages.push(progress); - // this._onDidChange.fire(); } } @@ -667,9 +645,8 @@ export class ChatModel extends Disposable implements IChatModel { } if (progress.kind === 'vulnerability') { - // TODO@roblourens ChatModel should just work with strings request.response.updateContent({ kind: 'markdownVuln', content: { value: progress.content }, vulnerabilities: progress.vulnerabilities }, quiet); - } else if (progress.kind === 'content' || progress.kind === 'markdownContent' || progress.kind === 'asyncContent' || progress.kind === 'treeData' || progress.kind === 'inlineReference' || progress.kind === 'markdownVuln' || progress.kind === 'progressMessage') { + } else if (progress.kind === 'content' || progress.kind === 'markdownContent' || progress.kind === 'treeData' || progress.kind === 'inlineReference' || progress.kind === 'markdownVuln' || progress.kind === 'progressMessage') { request.response.updateContent(progress, quiet); } else if (progress.kind === 'usedContext' || progress.kind === 'reference') { request.response.applyReference(progress); @@ -758,8 +735,6 @@ export class ChatModel extends Disposable implements IChatModel { return item.treeData; } else if (item.kind === 'markdownContent') { return item.content; - } else if (item.kind === 'asyncContent') { - return new MarkdownString(item.content); } else { return item as any; // TODO } diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index f16e5f1ce39f2..5b90ef33acfdf 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -119,15 +119,6 @@ export interface IChatTreeData { kind: 'treeData'; } -export interface IChatAsyncContent { - /** - * The placeholder to show while the content is loading - */ - content: string; - resolvedContent: Promise; - kind: 'asyncContent'; -} - export interface IChatProgressMessage { content: IMarkdownString; kind: 'progressMessage'; @@ -157,7 +148,6 @@ export type IChatProgress = | IChatAgentContentWithVulnerabilities | IChatAgentMarkdownContentWithVulnerability | IChatTreeData - | IChatAsyncContent | IChatUsedContext | IChatContentReference | IChatContentInlineReference diff --git a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts index 7ffcef4448a58..ed18235fe4f8c 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { DeferredPromise, timeout } from 'vs/base/common/async'; +import { timeout } from 'vs/base/common/async'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { URI } from 'vs/base/common/uri'; import { assertSnapshot } from 'vs/base/test/common/snapshot'; @@ -17,7 +17,6 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ChatAgentService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatModel, Response } from 'vs/workbench/contrib/chat/common/chatModel'; import { ChatRequestTextPart } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChatTreeData } from 'vs/workbench/contrib/chat/common/chatService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; @@ -140,20 +139,6 @@ suite('Response', () => { await assertSnapshot(response.value); }); - test('async content', async () => { - const response = new Response([]); - const deferred = new DeferredPromise(); - const deferred2 = new DeferredPromise(); - response.updateContent({ resolvedContent: deferred.p, content: 'text', kind: 'asyncContent' }); - response.updateContent({ resolvedContent: deferred2.p, content: 'text2', kind: 'asyncContent' }); - await assertSnapshot(response.value); - - await deferred2.complete({ kind: 'treeData', treeData: { label: 'label', uri: URI.parse('https://microsoft.com') } }); - await deferred.complete('resolved'); - await assertSnapshot(response.value); - }); - - test('inline reference', async () => { const response = new Response([]); response.updateContent({ content: 'text before', kind: 'content' }); From 442419c7a24beda953fe229ede17cef870c0ac9a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Jan 2024 11:06:39 -0800 Subject: [PATCH 171/333] Bump h2 from 0.3.17 to 0.3.24 in /cli (#202867) Bumps [h2](https://github.com/hyperium/h2) from 0.3.17 to 0.3.24. - [Release notes](https://github.com/hyperium/h2/releases) - [Changelog](https://github.com/hyperium/h2/blob/v0.3.24/CHANGELOG.md) - [Commits](https://github.com/hyperium/h2/compare/v0.3.17...v0.3.24) --- updated-dependencies: - dependency-name: h2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- cli/Cargo.lock | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 632ec0af17650..311614b430102 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -680,6 +680,12 @@ dependencies = [ "syn 2.0.18", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.1" @@ -917,9 +923,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.17" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", @@ -927,7 +933,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 2.1.0", "slab", "tokio", "tokio-util", @@ -940,6 +946,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "heck" version = "0.4.0" @@ -1094,7 +1106,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", ] [[package]] @@ -1529,7 +1551,7 @@ checksum = "ed41783a5bf567688eb38372f2b7a8530f5a607a4b49d38dd7573236c23ca7e2" dependencies = [ "futures-channel", "futures-util", - "indexmap", + "indexmap 1.9.1", "once_cell", "pin-project-lite", "thiserror", From cf13b387868102076947cbd010ac4a9755a06cc6 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Sat, 20 Jan 2024 22:57:51 +0100 Subject: [PATCH 172/333] changing the milestone to the correct milestone for the next endgame --- .vscode/notebooks/endgame.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 9743e6470775c..975758eb74767 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"November 2023 Recovery 1\"\n" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"December / January 2024\"\n" }, { "kind": 1, From e244acbb172c428cb219717a07bf55d2737492ca Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Sat, 20 Jan 2024 21:38:46 -0800 Subject: [PATCH 173/333] testing: avoid large hovers in test coverage, show inline counts instead (#202944) Closes #202600 I still have a hover to make the "toggle line coverage" action visible, not sure a better place to put that... --- .../lib/stylelint/vscode-known-variables.json | 2 + src/vs/platform/theme/common/colorRegistry.ts | 4 +- .../browser/codeCoverageDecorations.ts | 77 ++++++++++--------- .../contrib/testing/browser/media/testing.css | 56 ++++++++++---- .../contrib/testing/browser/theme.ts | 16 +++- 5 files changed, 100 insertions(+), 55 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 0b1767fba21ed..9fb16d8459df8 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -683,6 +683,8 @@ "--vscode-terminalStickyScroll-background", "--vscode-terminalStickyScrollHover-background", "--vscode-testing-coverage-lineHeight", + "--vscode-testing-coverCountBadgeBackground", + "--vscode-testing-coverCountBadgeForeground", "--vscode-testing-coveredBackground", "--vscode-testing-coveredBorder", "--vscode-testing-coveredGutterBackground", diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 05989bbbb4da0..3702702f5428b 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { assertNever } from 'vs/base/common/assert'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Color, RGBA } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; -import { assertNever } from 'vs/base/common/assert'; import * as nls from 'vs/nls'; -import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import * as platform from 'vs/platform/registry/common/platform'; import { IColorTheme } from 'vs/platform/theme/common/themeService'; diff --git a/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts b/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts index ac801660ccde9..831ee05e69909 100644 --- a/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts @@ -13,14 +13,13 @@ import { Lazy } from 'vs/base/common/lazy'; import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { autorun, derived, observableFromEvent, observableValue } from 'vs/base/common/observable'; import { ThemeIcon } from 'vs/base/common/themables'; -import { isDefined } from 'vs/base/common/types'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { MarkdownRenderer } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { IModelDecorationOptions, ITextModel, InjectedTextCursorStops } from 'vs/editor/common/model'; +import { IModelDecorationOptions, ITextModel, InjectedTextCursorStops, InjectedTextOptions } from 'vs/editor/common/model'; import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/browser/hoverOperation'; import { localize } from 'vs/nls'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; @@ -173,12 +172,12 @@ export class CodeCoverageDecorations extends Disposable implements IEditorContri return; } - const wasPreviouslyHovering = typeof this.hoveredSubject === 'number'; this.hoveredStore.clear(); this.hoveredSubject = lineNumber; const todo = [{ line: lineNumber, dir: 0 }]; const toEnable = new Set(); + const inlineEnabled = CodeCoverageDecorations.showInline.get(); if (!CodeCoverageDecorations.showInline.get()) { for (let i = 0; i < todo.length && i < MAX_HOVERED_LINES; i++) { const { line, dir } = todo[i]; @@ -209,7 +208,9 @@ export class CodeCoverageDecorations extends Disposable implements IEditorContri }); } - this.lineHoverWidget.value.startShowingAt(lineNumber, this.details, wasPreviouslyHovering); + if (toEnable.size || inlineEnabled) { + this.lineHoverWidget.value.startShowingAt(lineNumber); + } this.hoveredStore.add(this.editor.onMouseLeave(() => { this.hoveredStore.clear(); @@ -240,7 +241,7 @@ export class CodeCoverageDecorations extends Disposable implements IEditorContri model.changeDecorations(e => { for (const detailRange of details.ranges) { - const { metadata: { detail, description }, range } = detailRange; + const { metadata: { detail, description }, range, primary } = detailRange; if (detail.type === DetailType.Branch) { const hits = detail.detail.branches![detail.branch].count; const cls = hits ? CLASS_HIT : CLASS_MISS; @@ -263,6 +264,9 @@ export class CodeCoverageDecorations extends Disposable implements IEditorContri }; } else { target.className = `coverage-deco-inline ${cls}`; + if (primary) { + target.before = countBadge(hits); + } } }; @@ -282,6 +286,9 @@ export class CodeCoverageDecorations extends Disposable implements IEditorContri const applyHoverOptions = (target: IModelDecorationOptions) => { target.className = `coverage-deco-inline ${cls}`; target.hoverMessage = description; + if (primary) { + target.before = countBadge(detail.count); + } }; if (showInlineByDefault) { @@ -336,8 +343,21 @@ export class CodeCoverageDecorations extends Disposable implements IEditorContri } } +const countBadge = (count: number): InjectedTextOptions | undefined => { + if (count === 0) { + return undefined; + } + + return { + content: `${count > 99 ? '99+' : count}x`, + cursorStops: InjectedTextCursorStops.None, + inlineClassName: `coverage-deco-inline-count`, + inlineClassNameAffectsLetterSpacing: true, + }; +}; + type CoverageDetailsWithBranch = CoverageDetails | { type: DetailType.Branch; branch: number; detail: IStatementCoverage }; -type DetailRange = { range: Range; metadata: { detail: CoverageDetailsWithBranch; description: IMarkdownString | undefined } }; +type DetailRange = { range: Range; primary: boolean; metadata: { detail: CoverageDetailsWithBranch; description: IMarkdownString | undefined } }; export class CoverageDetailsModel { public readonly ranges: DetailRange[] = []; @@ -351,6 +371,7 @@ export class CoverageDetailsModel { // the editor without ugly overlaps. const detailRanges: DetailRange[] = details.map(detail => ({ range: tidyLocation(detail.location), + primary: true, metadata: { detail, description: this.describe(detail, textModel) } })); @@ -360,6 +381,7 @@ export class CoverageDetailsModel { const branch: CoverageDetailsWithBranch = { type: DetailType.Branch, branch: i, detail }; detailRanges.push({ range: tidyLocation(detail.branches[i].location || Range.fromPositions(range.getEndPosition())), + primary: true, metadata: { detail: branch, description: this.describe(branch, textModel), @@ -404,11 +426,13 @@ export class CoverageDetailsModel { // until after the `item.range` ends. const prev = stack[stack.length - 1]; if (prev) { + const primary = prev.primary; const si = prev.range.setEndPosition(start.lineNumber, start.column); prev.range = prev.range.setStartPosition(item.range.endLineNumber, item.range.endColumn); + prev.primary = false; // discard the previous range if it became empty, e.g. a nested statement if (prev.range.isEmpty()) { stack.pop(); } - result.push({ range: si, metadata: prev.metadata }); + result.push({ range: si, primary, metadata: prev.metadata }); } stack.push(item); @@ -460,39 +484,20 @@ function tidyLocation(location: Range | Position): Range { class LineHoverComputer implements IHoverComputer { public line = -1; - public textModel!: ITextModel; - public details!: CoverageDetailsModel; constructor(@IKeybindingService private readonly keybindingService: IKeybindingService) { } /** @inheritdoc */ public computeSync(): IMarkdownString[] { - const bestDetails: DetailRange[] = []; - let bestLine = -1; - for (const detail of this.details.ranges) { - if (detail.range.startLineNumber > this.line) { - break; - } - if (detail.range.endLineNumber < this.line) { - continue; - } - if (detail.range.startLineNumber !== bestLine) { - bestDetails.length = 0; - } - bestLine = detail.range.startLineNumber; - bestDetails.push(detail); - } + const strs: IMarkdownString[] = []; - const strs = bestDetails.map(d => d.metadata.detail.type === DetailType.Branch ? undefined : d.metadata.description).filter(isDefined); - if (strs.length) { - const s = new MarkdownString().appendMarkdown(`[${TOGGLE_INLINE_COMMAND_TEXT}](command:${TOGGLE_INLINE_COMMAND_ID})`); - s.isTrusted = true; - const binding = this.keybindingService.lookupKeybinding(TOGGLE_INLINE_COMMAND_ID); - if (binding) { - s.appendText(` (${binding.getLabel()})`); - } - strs.push(s); + const s = new MarkdownString().appendMarkdown(`[${TOGGLE_INLINE_COMMAND_TEXT}](command:${TOGGLE_INLINE_COMMAND_ID})`); + s.isTrusted = true; + const binding = this.keybindingService.lookupKeybinding(TOGGLE_INLINE_COMMAND_ID); + if (binding) { + s.appendText(` (${binding.getLabel()})`); } + strs.push(s); return strs; } @@ -556,7 +561,7 @@ class LineHoverWidget extends Disposable implements IOverlayWidget { } /** Shows the hover widget at the given line */ - public startShowingAt(lineNumber: number, details: CoverageDetailsModel, showImmediate: boolean) { + public startShowingAt(lineNumber: number) { this.hide(); const textModel = this.editor.getModel(); if (!textModel) { @@ -564,9 +569,7 @@ class LineHoverWidget extends Disposable implements IOverlayWidget { } this.computer.line = lineNumber; - this.computer.textModel = textModel; - this.computer.details = details; - this.hoverOperation.start(showImmediate ? HoverStartMode.Immediate : HoverStartMode.Delayed); + this.hoverOperation.start(HoverStartMode.Delayed); } /** Hides the hover widget */ diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index 86b858a5d1e30..9f577c776de5d 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -147,7 +147,7 @@ .test-explorer .computed-state.retired, .testing-run-glyph.retired { - opacity: 0.7 !important; + opacity: 0.7 !important; } .test-explorer .test-is-hidden { @@ -171,11 +171,7 @@ flex-grow: 1; } -.monaco-workbench - .test-explorer - .monaco-action-bar - .action-item - > .action-label { +.monaco-workbench .test-explorer .monaco-action-bar .action-item > .action-label { padding: 1px 2px; margin-right: 2px; } @@ -358,6 +354,7 @@ .monaco-editor .testing-inline-message-severity-0 { color: var(--vscode-testing-message-error-decorationForeground) !important; } + .monaco-editor .testing-inline-message-severity-1 { color: var(--vscode-testing-message-info-decorationForeground) !important; } @@ -411,8 +408,10 @@ .explorer-item-with-test-coverage .explorer-item { flex-grow: 1; } + .explorer-item-with-test-coverage .monaco-icon-label::after { - margin-right: 12px; /* slightly reduce because the bars handle the scrollbar margin */ + margin-right: 12px; + /* slightly reduce because the bars handle the scrollbar margin */ } /** -- coverage decorations */ @@ -447,14 +446,13 @@ .coverage-deco-gutter.coverage-deco-miss.coverage-deco-hit::before { background-image: linear-gradient(45deg, - var(--vscode-testing-coveredGutterBackground) 25%, - var(--vscode-testing-uncoveredGutterBackground) 25%, - var(--vscode-testing-uncoveredGutterBackground) 50%, - var(--vscode-testing-coveredGutterBackground) 50%, - 75%, - var(--vscode-testing-uncoveredGutterBackground) 75%, - var(--vscode-testing-uncoveredGutterBackground) 100% - ); + var(--vscode-testing-coveredGutterBackground) 25%, + var(--vscode-testing-uncoveredGutterBackground) 25%, + var(--vscode-testing-uncoveredGutterBackground) 50%, + var(--vscode-testing-coveredGutterBackground) 50%, + 75%, + var(--vscode-testing-uncoveredGutterBackground) 75%, + var(--vscode-testing-uncoveredGutterBackground) 100%); background-size: 6px 6px; background-color: transparent; } @@ -497,3 +495,31 @@ font: normal normal normal calc(var(--vscode-testing-coverage-lineHeight) / 2)/1 codicon; border: 1px solid; } + +.coverage-deco-inline-count { + position: relative; + background: var(--vscode-testing-coverCountBadgeBackground); + color: var(--vscode-testing-coverCountBadgeForeground); + font-size: 0.7em; + margin: 0 0.7em 0 0.4em; + padding: 0.2em 0 0.2em 0.2em; + /* display: inline-block; */ + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; +} + +.coverage-deco-inline-count::after { + content: ''; + display: block; + position: absolute; + left: 100%; + top: 0; + bottom: 0; + width: 0.5em; + background-image: + linear-gradient(to bottom left, transparent 50%, var(--vscode-testing-coverCountBadgeBackground) 0), + linear-gradient(to bottom right, var(--vscode-testing-coverCountBadgeBackground) 50%, transparent 0); + background-size: 100% 50%; + background-repeat: no-repeat; + background-position: top, bottom; +} diff --git a/src/vs/workbench/contrib/testing/browser/theme.ts b/src/vs/workbench/contrib/testing/browser/theme.ts index fe155816c0d81..4e23240541171 100644 --- a/src/vs/workbench/contrib/testing/browser/theme.ts +++ b/src/vs/workbench/contrib/testing/browser/theme.ts @@ -5,7 +5,7 @@ import { Color, RGBA } from 'vs/base/common/color'; import { localize } from 'vs/nls'; -import { chartsGreen, chartsRed, contrastBorder, diffInserted, diffRemoved, editorBackground, editorErrorForeground, editorForeground, editorInfoForeground, opaque, registerColor, transparent } from 'vs/platform/theme/common/colorRegistry'; +import { badgeBackground, badgeForeground, chartsGreen, chartsRed, contrastBorder, diffInserted, diffRemoved, editorBackground, editorErrorForeground, editorForeground, editorInfoForeground, opaque, registerColor, transparent } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { TestMessageType, TestResultState } from 'vs/workbench/contrib/testing/common/testTypes'; @@ -135,6 +135,20 @@ export const testingUncoveredGutterBackground = registerColor('testing.uncovered hcLight: chartsRed }, localize('testing.uncoveredGutterBackground', 'Gutter color of regions where code not covered.')); +export const testingCoverCountBadgeBackground = registerColor('testing.coverCountBadgeBackground', { + dark: badgeBackground, + light: badgeBackground, + hcDark: badgeBackground, + hcLight: badgeBackground +}, localize('testing.coverCountBadgeBackground', 'Background for the badge indicating execution count')); + +export const testingCoverCountBadgeForeground = registerColor('testing.coverCountBadgeForeground', { + dark: badgeForeground, + light: badgeForeground, + hcDark: badgeForeground, + hcLight: badgeForeground +}, localize('testing.coverCountBadgeForeground', 'Foreground for the badge indicating execution count')); + export const testMessageSeverityColors: { [K in TestMessageType]: { decorationForeground: string; From 1396f9c8274692e5ed6f7089a62297f89238f894 Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 22 Jan 2024 16:12:40 +0900 Subject: [PATCH 174/333] ci: fix debian packaging step failing to download sysroot (#202973) --- build/azure-pipelines/linux/product-build-linux.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index a4b7faa391852..848c47fe82328 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -288,6 +288,8 @@ steps: - script: | set -e yarn gulp "vscode-linux-$(VSCODE_ARCH)-prepare-deb" + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Prepare deb package - script: | From 7c471f6415d8cb9b5e1b77bc1663e187fe8ca5cb Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Sun, 21 Jan 2024 23:20:21 -0800 Subject: [PATCH 175/333] ensures correct diagnostic contributions to highlight code actions (#202978) ensures correct diagnostic contributions to highlight --- .../contrib/codeAction/browser/codeActionController.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts index 57aa30ff33f0c..ad34973eddf7a 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts @@ -289,8 +289,10 @@ export class CodeActionController extends Disposable implements IEditorContribut const decorations: IModelDeltaDecoration[] = action.action.diagnostics.map(diagnostic => ({ range: diagnostic, options: CodeActionController.DECORATION })); currentDecorations.set(decorations); const diagnostic = action.action.diagnostics[0]; - const selectionText = this._editor.getModel()?.getWordAtPosition({ lineNumber: diagnostic.startLineNumber, column: diagnostic.startColumn })?.word; - aria.status(localize('editingNewSelection', "Context: {0} at line {1} and column {2}.", selectionText, diagnostic.startLineNumber, diagnostic.startColumn)); + if (diagnostic.startLineNumber && diagnostic.startColumn) { + const selectionText = this._editor.getModel()?.getWordAtPosition({ lineNumber: diagnostic.startLineNumber, column: diagnostic.startColumn })?.word; + aria.status(localize('editingNewSelection', "Context: {0} at line {1} and column {2}.", selectionText, diagnostic.startLineNumber, diagnostic.startColumn)); + } } else { currentDecorations.clear(); } From ba3e2066981110b9a5e12571b506c4fd212bbd09 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 22 Jan 2024 13:10:17 +0530 Subject: [PATCH 176/333] fix #141282 (#202814) * fix #141282 * fix tests --- .../common/extensionManagement.ts | 5 +- .../common/extensionsProfileScannerService.ts | 12 +- .../node/extensionManagementService.ts | 19 ++- .../extensionsProfileScannerService.test.ts | 118 ------------------ .../extensions/browser/extensionEditor.ts | 21 ++-- .../browser/extensions.contribution.ts | 35 ++---- .../extensions/browser/extensionsActions.ts | 69 +++++----- .../extensions/browser/extensionsList.ts | 4 +- .../extensions/browser/extensionsWidgets.ts | 5 +- .../browser/extensionsWorkbenchService.ts | 26 +++- .../contrib/extensions/common/extensions.ts | 3 + .../browser/webExtensionsScannerService.ts | 4 + .../common/webExtensionManagementService.ts | 9 +- 13 files changed, 111 insertions(+), 219 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 853b9c182bae1..15907ec4882ad 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -237,6 +237,7 @@ export type Metadata = Partial; @@ -248,6 +249,7 @@ export interface ILocalExtension extends IExtension { publisherDisplayName: string | null; installedTimestamp?: number; isPreReleaseVersion: boolean; + hasPreReleaseVersion: boolean; preRelease: boolean; updated: boolean; pinned: boolean; @@ -446,14 +448,15 @@ export type InstallOptions = { pinned?: boolean; donotIncludePackAndDependencies?: boolean; installGivenVersion?: boolean; + preRelease?: boolean; installPreReleaseVersion?: boolean; donotVerifySignature?: boolean; operation?: InstallOperation; + profileLocation?: URI; /** * Context passed through to InstallExtensionResult */ context?: IStringDictionary; - profileLocation?: URI; }; export type InstallVSIXOptions = InstallOptions & { installOnlyNewlyAddedFromExtensionPack?: boolean }; export type UninstallOptions = { readonly donotIncludePack?: boolean; readonly donotCheckDependents?: boolean; readonly versionOnly?: boolean; readonly remove?: boolean; readonly profileLocation?: URI }; diff --git a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts index 40eb258430591..4fcf403d999a1 100644 --- a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts @@ -253,12 +253,8 @@ export abstract class AbstractExtensionsProfileScannerService extends Disposable // Extension in new format. No migration needed. location = this.resolveExtensionLocation(e.relativeLocation); } else if (isString(e.location)) { - // Extension in intermediate format. Migrate to new format. - location = this.resolveExtensionLocation(e.location); - migrate = true; - e.relativeLocation = e.location; - // retain old format so that old clients can read it - e.location = location.toJSON(); + this.logService.warn(`Extensions profile: Ignoring extension with invalid location: ${e.location}`); + continue; } else { location = URI.revive(e.location); const relativePath = this.toRelativePath(location); @@ -268,6 +264,10 @@ export abstract class AbstractExtensionsProfileScannerService extends Disposable e.relativeLocation = relativePath; } } + if (isUndefined(e.metadata?.hasPreReleaseVersion) && e.metadata?.preRelease) { + migrate = true; + e.metadata.hasPreReleaseVersion = true; + } extensions.push({ identifier: e.identifier, location, diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 8bcf57b346a9e..c7137910f446b 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -192,6 +192,7 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi this.logService.trace('ExtensionManagementService#updateMetadata', local.identifier.id); if (metadata.isPreReleaseVersion) { metadata.preRelease = true; + metadata.hasPreReleaseVersion = true; } // unset if false if (metadata.isMachineScoped === false) { @@ -493,7 +494,9 @@ export class ExtensionsScanner extends Disposable { exists = false; } - if (!exists) { + if (exists) { + await this.extensionsScannerService.updateMetadata(extensionLocation, metadata); + } else { try { // Extract try { @@ -700,6 +703,7 @@ export class ExtensionsScanner extends Disposable { isApplicationScoped: !!extension.metadata?.isApplicationScoped, isMachineScoped: !!extension.metadata?.isMachineScoped, isPreReleaseVersion: !!extension.metadata?.isPreReleaseVersion, + hasPreReleaseVersion: !!extension.metadata?.hasPreReleaseVersion, preRelease: !!extension.metadata?.preRelease, installedTimestamp: extension.metadata?.installedTimestamp, updated: !!extension.metadata?.updated, @@ -815,7 +819,9 @@ abstract class InstallExtensionTask extends AbstractExtensionTask { let local = await this.unsetIfUninstalled(key); - if (!local) { + if (local) { + local = await this.extensionsScanner.updateMetadata(local, metadata); + } else { this.logService.trace('Extracting extension...', key.id); local = await this.extensionsScanner.extractUserExtension(key, zipPath, metadata, removeIfExists, token); this.logService.info('Extracting extension completed.', key.id); @@ -888,12 +894,12 @@ export class InstallGalleryExtensionTask extends InstallExtensionTask { isSystem: existingExtension?.type === ExtensionType.System ? true : undefined, updated: !!existingExtension, isPreReleaseVersion: this.gallery.properties.isPreReleaseVersion, + hasPreReleaseVersion: existingExtension?.hasPreReleaseVersion || this.gallery.properties.isPreReleaseVersion, installedTimestamp: Date.now(), pinned: this.options.installGivenVersion ? true : (this.options.pinned ?? existingExtension?.pinned), - preRelease: this.gallery.properties.isPreReleaseVersion || - (isBoolean(this.options.installPreReleaseVersion) - ? this.options.installPreReleaseVersion /* Respect the passed flag */ - : existingExtension?.preRelease /* Respect the existing pre-release flag if it was set */) + preRelease: isBoolean(this.options.preRelease) + ? this.options.preRelease + : this.options.installPreReleaseVersion || this.gallery.properties.isPreReleaseVersion || existingExtension?.preRelease }; if (existingExtension?.manifest.version === this.gallery.version) { @@ -1007,6 +1013,7 @@ class InstallVSIXTask extends InstallExtensionTask { publisherDisplayName: galleryExtension.publisherDisplayName, publisherId: galleryExtension.publisherId, isPreReleaseVersion: galleryExtension.properties.isPreReleaseVersion, + hasPreReleaseVersion: extension.hasPreReleaseVersion || galleryExtension.properties.isPreReleaseVersion, preRelease: galleryExtension.properties.isPreReleaseVersion || this.options.installPreReleaseVersion }; await this.extensionsScanner.updateMetadata(extension, metadata, this.options.profileLocation); diff --git a/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts b/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts index 9636eabcf6902..5e71b85714e77 100644 --- a/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts +++ b/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts @@ -186,124 +186,6 @@ suite('ExtensionsProfileScannerService', () => { ]); }); - test('extension in intermediate format is read and migrated', async () => { - const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); - const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0')); - await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{ - identifier: extension.identifier, - location: 'pub.a-1.0.0', - version: extension.manifest.version, - }]))); - - const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation)); - - const actual = await testObject.scanProfileExtensions(extensionsManifest); - assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }]); - - const manifestContent = JSON.parse((await instantiationService.get(IFileService).readFile(extensionsManifest)).value.toString()); - assert.deepStrictEqual(manifestContent, [{ identifier: extension.identifier, location: extension.location.toJSON(), relativeLocation: 'pub.a-1.0.0', version: extension.manifest.version }]); - }); - - test('extension in intermediate format is read and migrated during write', async () => { - const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); - const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0')); - await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{ - identifier: extension.identifier, - location: 'pub.a-1.0.0', - version: extension.manifest.version, - }]))); - - const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation)); - const extension2 = aExtension('pub.b', joinPath(extensionsLocation, 'pub.b-1.0.0')); - await testObject.addExtensionsToProfile([[extension2, undefined]], extensionsManifest); - - const actual = await testObject.scanProfileExtensions(extensionsManifest); - assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [ - { identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }, - { identifier: extension2.identifier, location: extension2.location.toJSON(), version: extension2.manifest.version, metadata: undefined } - ]); - - const manifestContent = JSON.parse((await instantiationService.get(IFileService).readFile(extensionsManifest)).value.toString()); - assert.deepStrictEqual(manifestContent, [ - { identifier: extension.identifier, location: extension.location.toJSON(), relativeLocation: 'pub.a-1.0.0', version: extension.manifest.version }, - { identifier: extension2.identifier, location: extension2.location.toJSON(), relativeLocation: 'pub.b-1.0.0', version: extension2.manifest.version } - ]); - }); - - test('extensions in intermediate and new format is read and migrated', async () => { - const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); - const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0')); - const extension2 = aExtension('pub.b', joinPath(extensionsLocation, 'pub.b-1.0.0')); - await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{ - identifier: extension.identifier, - location: 'pub.a-1.0.0', - version: extension.manifest.version, - }, { - identifier: extension2.identifier, - location: extension2.location.toJSON(), - relativeLocation: 'pub.b-1.0.0', - version: extension2.manifest.version, - }]))); - - const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation)); - - const actual = await testObject.scanProfileExtensions(extensionsManifest); - assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [ - { identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }, - { identifier: extension2.identifier, location: extension2.location.toJSON(), version: extension2.manifest.version, metadata: undefined } - ]); - - const manifestContent = JSON.parse((await instantiationService.get(IFileService).readFile(extensionsManifest)).value.toString()); - assert.deepStrictEqual(manifestContent, [ - { identifier: extension.identifier, location: extension.location.toJSON(), relativeLocation: 'pub.a-1.0.0', version: extension.manifest.version }, - { identifier: extension2.identifier, location: extension2.location.toJSON(), relativeLocation: 'pub.b-1.0.0', version: extension2.manifest.version } - ]); - }); - - test('extensions in mixed format is read and migrated', async () => { - const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); - const extension1 = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0')); - const extension2 = aExtension('pub.b', joinPath(extensionsLocation, 'pub.b-1.0.0')); - const extension3 = aExtension('pub.c', joinPath(extensionsLocation, 'pub.c-1.0.0')); - const extension4 = aExtension('pub.d', joinPath(ROOT, 'pub.d-1.0.0')); - await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{ - identifier: extension1.identifier, - location: 'pub.a-1.0.0', - version: extension1.manifest.version, - }, { - identifier: extension2.identifier, - location: extension2.location.toJSON(), - version: extension2.manifest.version, - }, { - identifier: extension3.identifier, - location: extension3.location.toJSON(), - relativeLocation: 'pub.c-1.0.0', - version: extension3.manifest.version, - }, { - identifier: extension4.identifier, - location: extension4.location.toJSON(), - version: extension4.manifest.version, - }]))); - - const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation)); - - const actual = await testObject.scanProfileExtensions(extensionsManifest); - assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [ - { identifier: extension1.identifier, location: extension1.location.toJSON(), version: extension1.manifest.version, metadata: undefined }, - { identifier: extension2.identifier, location: extension2.location.toJSON(), version: extension2.manifest.version, metadata: undefined }, - { identifier: extension3.identifier, location: extension3.location.toJSON(), version: extension3.manifest.version, metadata: undefined }, - { identifier: extension4.identifier, location: extension4.location.toJSON(), version: extension4.manifest.version, metadata: undefined } - ]); - - const manifestContent = JSON.parse((await instantiationService.get(IFileService).readFile(extensionsManifest)).value.toString()); - assert.deepStrictEqual(manifestContent, [ - { identifier: extension1.identifier, location: extension1.location.toJSON(), relativeLocation: 'pub.a-1.0.0', version: extension1.manifest.version }, - { identifier: extension2.identifier, location: extension2.location.toJSON(), relativeLocation: 'pub.b-1.0.0', version: extension2.manifest.version }, - { identifier: extension3.identifier, location: extension3.location.toJSON(), relativeLocation: 'pub.c-1.0.0', version: extension3.manifest.version }, - { identifier: extension4.identifier, location: extension4.location.toJSON(), version: extension4.manifest.version } - ]); - }); - test('throws error if extension has invalid relativePath', async () => { const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0')); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index e991e2d5f8dba..a977ee1ed2f5e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -73,10 +73,10 @@ import { SetLanguageAction, SetProductIconThemeAction, ToggleAutoUpdateForExtensionAction, - SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, UninstallAction, UpdateAction, - WebInstallAction + WebInstallAction, + TogglePreReleaseExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { errorIcon, infoIcon, preReleaseIcon, warningIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; import { Delegate } from 'vs/workbench/contrib/extensions/browser/extensionsList'; @@ -218,16 +218,7 @@ class PreReleaseTextWidget extends ExtensionWithDifferentGalleryVersionWidget { this.render(); } render(): void { - this.element.style.display = this.isPreReleaseVersion() ? 'inherit' : 'none'; - } - private isPreReleaseVersion(): boolean { - if (!this.extension) { - return false; - } - if (this.gallery) { - return this.gallery.properties.isPreReleaseVersion; - } - return !!(this.extension.state === ExtensionState.Installed ? this.extension.local?.isPreReleaseVersion : this.extension.gallery?.properties.isPreReleaseVersion); + this.element.style.display = this.extension?.isPreReleaseVersion ? 'inherit' : 'none'; } } @@ -365,8 +356,7 @@ export class ExtensionEditor extends EditorPane { this.instantiationService.createInstance(InstallAnotherVersionAction), ] ]), - this.instantiationService.createInstance(SwitchToPreReleaseVersionAction, false), - this.instantiationService.createInstance(SwitchToReleasedVersionAction, false), + this.instantiationService.createInstance(TogglePreReleaseExtensionAction), this.instantiationService.createInstance(ToggleAutoUpdateForExtensionAction, false, [false, 'onlySelectedExtensions']), new ExtensionEditorManageExtensionAction(this.scopedContextKeyService || this.contextKeyService, this.instantiationService), ]; @@ -384,6 +374,9 @@ export class ExtensionEditor extends EditorPane { if (action instanceof ToggleAutoUpdateForExtensionAction) { return new CheckboxActionViewItem(undefined, action, { icon: true, label: true, checkboxStyles: defaultCheckboxStyles }); } + if (action instanceof TogglePreReleaseExtensionAction) { + return new CheckboxActionViewItem(undefined, action, { icon: true, label: true, checkboxStyles: defaultCheckboxStyles }); + } return undefined; }, focusOnlyEnabledItems: true diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index de4e96c1c251f..412cbbc3b5a6e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -14,7 +14,7 @@ import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsServi import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY, IExtension, extensionsSearchActionsMenu, UPDATE_ACTIONS_GROUP } from 'vs/workbench/contrib/extensions/common/extensions'; -import { ReinstallAction, InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, PromptExtensionInstallFailureAction, SearchExtensionsAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ClearLanguageAction, ToggleAutoUpdateForExtensionAction, ToggleAutoUpdatesForPublisherAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { ReinstallAction, InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, PromptExtensionInstallFailureAction, SearchExtensionsAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ClearLanguageAction, ToggleAutoUpdateForExtensionAction, ToggleAutoUpdatesForPublisherAction, TogglePreReleaseExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor'; import { StatusUpdater, MaliciousExtensionChecker, ExtensionsViewletViewsContribution, ExtensionsViewPaneContainer, BuiltInExtensionsContext, SearchMarketplaceExtensionsContext, RecommendedExtensionsContext, DefaultViewsContext, ExtensionsSortByContext, SearchHasTextContext } from 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; @@ -1372,39 +1372,24 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi }); this.registerExtensionAction({ - id: SwitchToPreReleaseVersionAction.ID, - title: SwitchToPreReleaseVersionAction.TITLE, + id: TogglePreReleaseExtensionAction.ID, + title: TogglePreReleaseExtensionAction.LABEL, + category: ExtensionsLocalizedLabel, menu: { id: MenuId.ExtensionContext, group: INSTALL_ACTIONS_GROUP, order: 2, - when: ContextKeyExpr.and(ContextKeyExpr.not('installedExtensionIsPreReleaseVersion'), ContextKeyExpr.not('installedExtensionIsOptedTpPreRelease'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) - }, - run: async (accessor: ServicesAccessor, id: string) => { - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); - if (extension) { - extensionWorkbenchService.open(extension, { showPreReleaseVersion: true }); - await extensionWorkbenchService.install(extension, { installPreReleaseVersion: true }); - } - } - }); - - this.registerExtensionAction({ - id: SwitchToReleasedVersionAction.ID, - title: SwitchToReleasedVersionAction.TITLE, - menu: { - id: MenuId.ExtensionContext, - group: INSTALL_ACTIONS_GROUP, - order: 3, - when: ContextKeyExpr.and(ContextKeyExpr.has('installedExtensionIsPreReleaseVersion'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.has('extensionHasReleaseVersion'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) + when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) }, + toggled: ContextKeyExpr.has('installedExtensionIsOptedToPreRelease'), run: async (accessor: ServicesAccessor, id: string) => { + const instantiationService = accessor.get(IInstantiationService); const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); if (extension) { - extensionWorkbenchService.open(extension, { showPreReleaseVersion: false }); - await extensionWorkbenchService.install(extension, { installPreReleaseVersion: false }); + const action = instantiationService.createInstance(TogglePreReleaseExtensionAction); + action.extension = extension; + return action.run(); } } }); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 853426bcdd44a..fbd480a719f67 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -55,7 +55,7 @@ import { IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/ import { ActionWithDropdownActionViewItem, IActionWithDropdownActionViewItemOptions } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { IContextMenuProvider } from 'vs/base/browser/contextmenu'; import { ILogService } from 'vs/platform/log/common/log'; -import { errorIcon, infoIcon, manageExtensionIcon, preReleaseIcon, syncEnabledIcon, syncIgnoredIcon, trustIcon, warningIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; +import { errorIcon, infoIcon, manageExtensionIcon, syncEnabledIcon, syncIgnoredIcon, trustIcon, warningIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; import { isIOS, isWeb, language } from 'vs/base/common/platform'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; @@ -1085,7 +1085,7 @@ async function getContextMenuActionsGroups(extension: IExtension | undefined | n cksOverlay.push(['extensionStatus', 'installed']); } cksOverlay.push(['installedExtensionIsPreReleaseVersion', !!extension.local?.isPreReleaseVersion]); - cksOverlay.push(['installedExtensionIsOptedTpPreRelease', !!extension.local?.preRelease]); + cksOverlay.push(['installedExtensionIsOptedToPreRelease', !!extension.local?.preRelease]); cksOverlay.push(['galleryExtensionIsPreReleaseVersion', !!extension.gallery?.properties.isPreReleaseVersion]); cksOverlay.push(['extensionHasPreReleaseVersion', extension.hasPreReleaseVersion]); cksOverlay.push(['extensionHasReleaseVersion', extension.hasReleaseVersion]); @@ -1267,55 +1267,50 @@ export class MenuItemExtensionAction extends ExtensionAction { } } -export class SwitchToPreReleaseVersionAction extends ExtensionAction { +export class TogglePreReleaseExtensionAction extends ExtensionAction { - static readonly ID = 'workbench.extensions.action.switchToPreReleaseVersion'; - static readonly TITLE = { value: localize('switch to pre-release version', "Switch to Pre-Release Version"), original: 'Switch to Pre-Release Version' }; + static readonly ID = 'workbench.extensions.action.togglePreRlease'; + static readonly LABEL = localize('togglePreRleaseLabel', "Pre-Release"); + + private static readonly EnabledClass = `${ExtensionAction.EXTENSION_ACTION_CLASS} pre-release`; + private static readonly DisabledClass = `${TogglePreReleaseExtensionAction.EnabledClass} hide`; constructor( - icon: boolean, - @ICommandService private readonly commandService: ICommandService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, ) { - super(SwitchToPreReleaseVersionAction.ID, icon ? '' : SwitchToPreReleaseVersionAction.TITLE.value, `${icon ? ExtensionAction.ICON_ACTION_CLASS + ' ' + ThemeIcon.asClassName(preReleaseIcon) : ExtensionAction.LABEL_ACTION_CLASS} hide-when-disabled switch-to-prerelease`, true); - this.tooltip = localize('switch to pre-release version tooltip', "Switch to Pre-Release version of this extension"); + super(TogglePreReleaseExtensionAction.ID, TogglePreReleaseExtensionAction.LABEL, TogglePreReleaseExtensionAction.DisabledClass); this.update(); } - update(): void { - this.enabled = !!this.extension && !this.extension.isBuiltin && !this.extension.local?.isPreReleaseVersion && !this.extension.local?.preRelease && this.extension.hasPreReleaseVersion && this.extension.state === ExtensionState.Installed; - } - - override async run(): Promise { - if (!this.enabled) { + override update() { + this.enabled = false; + this.class = TogglePreReleaseExtensionAction.DisabledClass; + if (!this.extension) { return; } - return this.commandService.executeCommand(SwitchToPreReleaseVersionAction.ID, this.extension?.identifier.id); - } -} - -export class SwitchToReleasedVersionAction extends ExtensionAction { - - static readonly ID = 'workbench.extensions.action.switchToReleaseVersion'; - static readonly TITLE = localize2('switch to release version', 'Switch to Release Version'); - - constructor( - icon: boolean, - @ICommandService private readonly commandService: ICommandService, - ) { - super(SwitchToReleasedVersionAction.ID, icon ? '' : SwitchToReleasedVersionAction.TITLE.value, `${icon ? ExtensionAction.ICON_ACTION_CLASS + ' ' + ThemeIcon.asClassName(preReleaseIcon) : ExtensionAction.LABEL_ACTION_CLASS} hide-when-disabled switch-to-released`); - this.tooltip = localize('switch to release version tooltip', "Switch to Release version of this extension"); - this.update(); - } - - update(): void { - this.enabled = !!this.extension && !this.extension.isBuiltin && this.extension.state === ExtensionState.Installed && !!this.extension.local?.isPreReleaseVersion && !!this.extension.hasReleaseVersion; + if (this.extension.isBuiltin) { + return; + } + if (this.extension.state !== ExtensionState.Installed) { + return; + } + if (!this.extension.hasPreReleaseVersion) { + return; + } + if (!this.extension.gallery) { + return; + } + this.enabled = true; + this.class = TogglePreReleaseExtensionAction.EnabledClass; + this.checked = this.extension.preRelease; } override async run(): Promise { - if (!this.enabled) { + if (!this.extension) { return; } - return this.commandService.executeCommand(SwitchToReleasedVersionAction.ID, this.extension?.identifier.id); + this.extensionsWorkbenchService.open(this.extension, { showPreReleaseVersion: !this.extension.preRelease }); + await this.extensionsWorkbenchService.togglePreRelease(this.extension); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index d021b584a1802..0f3b1b04d08e1 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -13,7 +13,7 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { Event } from 'vs/base/common/event'; import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; -import { ManageExtensionAction, ReloadAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction, UpdateAction, ToggleAutoUpdateForExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { ManageExtensionAction, ReloadAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ActionWithDropDownAction, InstallDropdownAction, InstallingLabelAction, ExtensionActionWithDropdownActionViewItem, ExtensionDropDownAction, WebInstallAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction, UpdateAction, ToggleAutoUpdateForExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget, ExtensionHoverWidget, ExtensionActivationStatusWidget, PreReleaseBookmarkWidget, extensionVerifiedPublisherIconColor, VerifiedPublisherWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; @@ -128,8 +128,6 @@ export class Renderer implements IPagedRenderer { this.instantiationService.createInstance(LocalInstallAction), this.instantiationService.createInstance(WebInstallAction), extensionStatusIconAction, - this.instantiationService.createInstance(SwitchToReleasedVersionAction, true), - this.instantiationService.createInstance(SwitchToPreReleaseVersionAction, true), this.instantiationService.createInstance(ManageExtensionAction) ]; const extensionHoverWidget = this.instantiationService.createInstance(ExtensionHoverWidget, { target: root, position: this.options.hoverOptions.position }, extensionStatusIconAction); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index 2145b7dfbeeff..8b95c60437167 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -323,10 +323,7 @@ export class PreReleaseBookmarkWidget extends ExtensionWidget { if (!this.extension) { return; } - if (!this.extension.hasPreReleaseVersion) { - return; - } - if (this.extension.state === ExtensionState.Installed && !this.extension.local?.isPreReleaseVersion) { + if (!this.extension.isPreReleaseVersion) { return; } this.element = append(this.parent, $('div.extension-bookmark')); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 65b2e09affabb..62731c2520aba 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -290,8 +290,19 @@ export class Extension implements IExtension { return this.local?.manifest.preview ?? this.gallery?.preview ?? false; } + get preRelease(): boolean { + return !!this.local?.preRelease; + } + + get isPreReleaseVersion(): boolean { + if (this.local) { + return this.local.isPreReleaseVersion; + } + return !!this.gallery?.properties.isPreReleaseVersion; + } + get hasPreReleaseVersion(): boolean { - return !!this.gallery?.hasPreReleaseVersion; + return !!this.gallery?.hasPreReleaseVersion || !!this.local?.hasPreReleaseVersion; } get hasReleaseVersion(): boolean { @@ -1637,6 +1648,8 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return false; } + + install(extension: URI | IExtension, installOptions?: InstallOptions | InstallVSIXOptions, progressLocation?: ProgressLocation): Promise { return this.doInstall(extension, async () => { if (extension instanceof URI) { @@ -1766,6 +1779,17 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension : this.extensionsSyncManagementService.hasToNeverSyncExtension(extension.identifier.id); } + async togglePreRelease(extension: IExtension): Promise { + if (!extension.local) { + return; + } + if (extension.preRelease !== extension.isPreReleaseVersion) { + await this.extensionManagementService.updateMetadata(extension.local, { preRelease: !extension.preRelease }); + return; + } + await this.install(extension, { installPreReleaseVersion: !extension.preRelease, preRelease: !extension.preRelease }); + } + async toggleExtensionIgnoredToSync(extension: IExtension): Promise { const isIgnored = this.isExtensionIgnoredToSync(extension); if (extension.local && isIgnored) { diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index 97ee589ea19f0..cd7375688b351 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -53,6 +53,8 @@ export interface IExtension { readonly publisherSponsorLink?: URI; readonly version: string; readonly latestVersion: string; + readonly preRelease: boolean; + readonly isPreReleaseVersion: boolean; readonly hasPreReleaseVersion: boolean; readonly hasReleaseVersion: boolean; readonly description: string; @@ -110,6 +112,7 @@ export interface IExtensionsWorkbenchService { uninstall(extension: IExtension): Promise; installVersion(extension: IExtension, version: string, installOptions?: InstallOptions): Promise; reinstall(extension: IExtension): Promise; + togglePreRelease(extension: IExtension): Promise; canSetLanguage(extension: IExtension): boolean; setLanguage(extension: IExtension): Promise; setEnablement(extensions: IExtension | IExtension[], enablementState: EnablementState): Promise; diff --git a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts index de40f04aeafc3..9e0eab4f88f4d 100644 --- a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts @@ -936,6 +936,10 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten update = true; webExtension.location = migratedLocation; } + if (isUndefined(webExtension.metadata?.hasPreReleaseVersion) && webExtension.metadata?.preRelease) { + update = true; + webExtension.metadata.hasPreReleaseVersion = true; + } return webExtension; })); if (update) { diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts index 9927bd3d34250..074f0d70d66c3 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts @@ -230,6 +230,7 @@ function toLocalExtension(extension: IExtension): ILocalExtension { publisherDisplayName: metadata.publisherDisplayName || null, installedTimestamp: metadata.installedTimestamp, isPreReleaseVersion: !!metadata.isPreReleaseVersion, + hasPreReleaseVersion: !!metadata.hasPreReleaseVersion, preRelease: !!metadata.preRelease, targetPlatform: TargetPlatform.WEB, updated: !!metadata.updated, @@ -280,14 +281,14 @@ class InstallExtensionTask extends AbstractExtensionTask implem metadata.publisherId = this.extension.publisherId; metadata.installedTimestamp = Date.now(); metadata.isPreReleaseVersion = this.extension.properties.isPreReleaseVersion; + metadata.hasPreReleaseVersion = metadata.hasPreReleaseVersion || this.extension.properties.isPreReleaseVersion; metadata.isBuiltin = this.options.isBuiltin || existingExtension?.isBuiltin; metadata.isSystem = existingExtension?.type === ExtensionType.System ? true : undefined; metadata.updated = !!existingExtension; metadata.isApplicationScoped = this.options.isApplicationScoped || metadata.isApplicationScoped; - metadata.preRelease = this.extension.properties.isPreReleaseVersion || - (isBoolean(this.options.installPreReleaseVersion) - ? this.options.installPreReleaseVersion /* Respect the passed flag */ - : metadata?.preRelease /* Respect the existing pre-release flag if it was set */); + metadata.preRelease = isBoolean(this.options.preRelease) + ? this.options.preRelease + : this.options.installPreReleaseVersion || this.extension.properties.isPreReleaseVersion || metadata.preRelease; } metadata.pinned = this.options.installGivenVersion ? true : (this.options.pinned ?? metadata.pinned); From 143e023867ac7520a92aa8c708e99029850491f2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 22 Jan 2024 13:24:09 +0530 Subject: [PATCH 177/333] schema can be undefined (#202980) --- .../services/configuration/browser/configurationService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 4057b5bc7cc30..509ec2874c41b 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -1349,7 +1349,7 @@ class ConfigurationTelemetryContribution implements IWorkbenchContribution { const userSource = ConfigurationTargetToString(ConfigurationTarget.USER_LOCAL); for (const setting of user) { const schema = this.configurationRegistry.getConfigurationProperties()[setting]; - if (schema.tags?.includes('FeatureInsight')) { + if (schema?.tags?.includes('FeatureInsight')) { const value = this.getValueToReport(setting, ConfigurationTarget.USER_LOCAL, schema); this.telemetryService.publicLog2('updatedsetting', { setting, value, source: userSource }); } @@ -1357,7 +1357,7 @@ class ConfigurationTelemetryContribution implements IWorkbenchContribution { const worskpaceSource = ConfigurationTargetToString(ConfigurationTarget.WORKSPACE); for (const setting of workspace) { const schema = this.configurationRegistry.getConfigurationProperties()[setting]; - if (schema.tags?.includes('FeatureInsight')) { + if (schema?.tags?.includes('FeatureInsight')) { const value = this.getValueToReport(setting, ConfigurationTarget.WORKSPACE, schema); this.telemetryService.publicLog2('updatedsetting', { setting, value, source: worskpaceSource }); } From 0d71d26b8ca9ece39c43ac513c963a5b697b51ec Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 22 Jan 2024 13:51:04 +0530 Subject: [PATCH 178/333] fix #202868 (#202981) --- src/vs/workbench/browser/parts/globalCompositeBar.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/parts/globalCompositeBar.ts b/src/vs/workbench/browser/parts/globalCompositeBar.ts index d65d239c9599c..50a63bb9e0021 100644 --- a/src/vs/workbench/browser/parts/globalCompositeBar.ts +++ b/src/vs/workbench/browser/parts/globalCompositeBar.ts @@ -584,17 +584,18 @@ export class GlobalActivityActionViewItem extends AbstractGlobalActivityActionVi return; } - if ((this.action as CompositeBarAction).activity) { + if (this.userDataProfileService.currentProfile.icon && this.userDataProfileService.currentProfile.icon !== DEFAULT_ICON.id) { return; } - if (!this.userDataProfileService.currentProfile.icon || this.userDataProfileService.currentProfile.icon === DEFAULT_ICON.id) { - this.profileBadgeContent.classList.toggle('profile-text-overlay', true); - this.profileBadgeContent.classList.toggle('profile-icon-overlay', false); - this.profileBadgeContent.textContent = this.userDataProfileService.currentProfile.name.substring(0, 2).toUpperCase(); + if ((this.action as CompositeBarAction).activity) { + return; } show(this.profileBadge); + this.profileBadgeContent.classList.toggle('profile-text-overlay', true); + this.profileBadgeContent.classList.toggle('profile-icon-overlay', false); + this.profileBadgeContent.textContent = this.userDataProfileService.currentProfile.name.substring(0, 2).toUpperCase(); } protected override updateActivity(): void { From 8baf105cbd35e538d8133a857d4b4fede89f902c Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 22 Jan 2024 17:51:44 +0900 Subject: [PATCH 179/333] fix: skip glibc requirements check on nixos (#202982) --- resources/server/bin/helpers/check-requirements-linux.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/resources/server/bin/helpers/check-requirements-linux.sh b/resources/server/bin/helpers/check-requirements-linux.sh index ac1840d61d8a5..af77f0b9d8252 100644 --- a/resources/server/bin/helpers/check-requirements-linux.sh +++ b/resources/server/bin/helpers/check-requirements-linux.sh @@ -10,6 +10,13 @@ ARCH=$(uname -m) found_required_glibc=0 found_required_glibcxx=0 +# Extract the ID value from /etc/os-release +OS_ID="$(cat /etc/os-release | grep -Eo 'ID=([^"]+)' | sed 's/ID=//')" +if [ "$OS_ID" = "nixos" ]; then + echo "Warning: NixOS detected, skipping GLIBC check" + exit 0 +fi + # Based on https://github.com/bminor/glibc/blob/520b1df08de68a3de328b65a25b86300a7ddf512/elf/cache.c#L162-L245 case $ARCH in x86_64) LDCONFIG_ARCH="x86-64";; From c17371c2c4002fbc88d57db914e348efc0a4396c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 22 Jan 2024 12:05:27 +0100 Subject: [PATCH 180/333] eng - enlist some settings to report as telemetry (#202986) * eng - enlist some settings to report as telemetry * include more custom titlebar insights --------- Co-authored-by: BeniBenj --- .../chat/electron-sandbox/actions/voiceChatActions.ts | 2 +- .../contrib/files/browser/files.contribution.ts | 3 ++- .../electron-sandbox/desktop.contribution.ts | 11 +++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index f83d6f8ff4946..a68d2a95678bb 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -854,7 +854,7 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe ], 'description': localize('voice.keywordActivation', "Controls whether the phrase 'Hey Code' should be speech recognized to start a voice chat session."), 'default': 'off', - 'tags': ['accessibility'] + 'tags': ['accessibility', 'FeatureInsight'] } } }); diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index f64fe59659d32..4f6e5691daa23 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -251,7 +251,8 @@ configurationRegistry.registerConfiguration({ ], 'default': isWeb ? AutoSaveConfiguration.AFTER_DELAY : AutoSaveConfiguration.OFF, 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSave' }, "Controls [auto save](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) of editors that have unsaved changes.", AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE, AutoSaveConfiguration.AFTER_DELAY), - scope: ConfigurationScope.LANGUAGE_OVERRIDABLE + scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, + tags: ['FeatureInsight'] }, 'files.autoSaveDelay': { 'type': 'number', diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 19549fc876aff..7246962aa7505 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -195,13 +195,13 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand 'maximum': MAX_ZOOM_LEVEL, 'markdownDescription': localize({ comment: ['{0} will be a setting name rendered as a link'], key: 'zoomLevel' }, "Adjust the default zoom level for all windows. Each increment above `0` (e.g. `1`) or below (e.g. `-1`) represents zooming `20%` larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity. See {0} for configuring if the 'Zoom In' and 'Zoom Out' commands apply the zoom level to all windows or only the active window.", '`#window.zoomPerWindow#`'), ignoreSync: true, - tags: ['accessibility'] + tags: ['accessibility', 'FeatureInsight'] }, 'window.zoomPerWindow': { 'type': 'boolean', 'default': true, 'markdownDescription': localize({ comment: ['{0} will be a setting name rendered as a link'], key: 'zoomPerWindow' }, "Controls if the 'Zoom In' and 'Zoom Out' commands apply the zoom level to all windows or only the active window. See {0} for configuring a default zoom level for all windows.", '`#window.zoomLevel#`'), - tags: ['accessibility'] + tags: ['accessibility', 'FeatureInsight'] }, 'window.newWindowDimensions': { 'type': 'string', @@ -233,7 +233,8 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand 'enum': ['native', 'custom'], 'default': isLinux ? 'native' : 'custom', 'scope': ConfigurationScope.APPLICATION, - 'description': localize('titleBarStyle', "Adjust the appearance of the window title bar to be native by the OS or custom. On Linux and Windows, this setting also affects the application and context menu appearances. Changes require a full restart to apply.") + 'description': localize('titleBarStyle', "Adjust the appearance of the window title bar to be native by the OS or custom. On Linux and Windows, this setting also affects the application and context menu appearances. Changes require a full restart to apply."), + tags: ['FeatureInsight'] }, 'window.customTitleBarVisibility': { 'type': 'string', @@ -246,6 +247,7 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand 'default': isLinux ? 'never' : 'auto', 'scope': ConfigurationScope.APPLICATION, 'description': localize('window.customTitleBarVisibility', "Adjust when the custom title bar should be shown."), + tags: ['FeatureInsight'] }, 'window.dialogStyle': { 'type': 'string', @@ -259,7 +261,8 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand 'default': false, 'scope': ConfigurationScope.APPLICATION, 'description': localize('window.nativeTabs', "Enables macOS Sierra window tabs. Note that changes require a full restart to apply and that native tabs will disable a custom title bar style if configured."), - 'included': isMacintosh + 'included': isMacintosh, + tags: ['FeatureInsight'] }, 'window.nativeFullScreen': { 'type': 'boolean', From 1d914f2bf258a005302b01542ef3df9e96826777 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 22 Jan 2024 12:12:53 +0100 Subject: [PATCH 181/333] Define default of 'editor.lightbulb.enabled' by experiment (#202989) --- src/vs/editor/common/config/editorOptions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 944041b7fc39f..8e9ef0b8ef3f0 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2748,12 +2748,13 @@ export type EditorLightbulbOptions = Readonly> class EditorLightbulb extends BaseEditorOption { constructor() { - const defaults: EditorLightbulbOptions = { enabled: ShowLightbulbIconMode.On }; + const defaults: EditorLightbulbOptions = { enabled: ShowLightbulbIconMode.OnCode }; super( EditorOption.lightbulb, 'lightbulb', defaults, { 'editor.lightbulb.enabled': { type: 'string', + tags: ['experimental'], enum: [ShowLightbulbIconMode.Off, ShowLightbulbIconMode.OnCode, ShowLightbulbIconMode.On], default: defaults.enabled, enumDescriptions: [ From e1c930d5be4e618944116b1eb09191aa99124316 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 22 Jan 2024 16:47:54 +0530 Subject: [PATCH 182/333] Enable old telemetry event (#202990) --- .../browser/configurationService.ts | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 509ec2874c41b..f3cc5a0eff647 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -1325,7 +1325,7 @@ class ResetConfigurationDefaultsOverridesCache extends Disposable implements IWo } } -class ConfigurationTelemetryContribution implements IWorkbenchContribution { +class ConfigurationTelemetryContribution extends Disposable implements IWorkbenchContribution { private readonly configurationRegistry = Registry.as(Extensions.Configuration); @@ -1333,6 +1333,33 @@ class ConfigurationTelemetryContribution implements IWorkbenchContribution { @IConfigurationService private readonly configurationService: WorkspaceService, @ITelemetryService private readonly telemetryService: ITelemetryService, ) { + super(); + + // Debounce the event by 1000 ms and merge all affected keys into one event + const debouncedConfigService = Event.debounce(configurationService.onDidChangeConfiguration, (last, cur) => { + const newAffectedKeys: ReadonlySet = last ? new Set([...last.affectedKeys, ...cur.affectedKeys]) : cur.affectedKeys; + return { ...cur, affectedKeys: newAffectedKeys }; + }, 1000, true); + + debouncedConfigService(event => { + if (event.source !== ConfigurationTarget.DEFAULT) { + type UpdateConfigurationClassification = { + owner: 'sandy081'; + comment: 'Event which fires when user updates settings'; + configurationSource: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'What configuration file was updated i.e user or workspace' }; + configurationKeys: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'What configuration keys were updated' }; + }; + type UpdateConfigurationEvent = { + configurationSource: string; + configurationKeys: string[]; + }; + telemetryService.publicLog2('updateConfiguration', { + configurationSource: ConfigurationTargetToString(event.source), + configurationKeys: Array.from(event.affectedKeys) + }); + } + }); + type UpdatedSettingClassification = { owner: 'sandy081'; comment: 'Event reporting the updated setting'; From 7d164333a7238e1e3db6449c0b494b0c4b39948d Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 22 Jan 2024 12:20:55 +0100 Subject: [PATCH 183/333] adding the experimental and the feature insight tag --- src/vs/editor/common/config/editorOptions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 8e9ef0b8ef3f0..73fc32d3b34a5 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2817,7 +2817,8 @@ class EditorStickyScroll extends BaseEditorOption Date: Mon, 22 Jan 2024 12:35:09 +0100 Subject: [PATCH 184/333] Update notebook (#202992) --- .vscode/notebooks/my-endgame.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index 5278439e57a0a..46a88544941eb 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"November 2023\"\n\n$MINE=assignee:@me\n" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"December / January 2024\"\n\n$MINE=assignee:@me\n" }, { "kind": 1, From dca6f399d32b5ead87efc32fb2ada736ce9596c3 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 22 Jan 2024 12:28:16 +0100 Subject: [PATCH 185/333] Fixes diff editor bug --- src/vs/base/common/assert.ts | 6 ++++++ .../browser/widget/diffEditor/diffEditorViewModel.ts | 11 +++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/vs/base/common/assert.ts b/src/vs/base/common/assert.ts index 5efd7244ff4c2..5c1bff2799031 100644 --- a/src/vs/base/common/assert.ts +++ b/src/vs/base/common/assert.ts @@ -35,6 +35,12 @@ export function assert(condition: boolean): void { } } +export function softAssert(condition: boolean): void { + if (!condition) { + onUnexpectedError(new BugIndicatingError('Assertion Failed')); + } +} + /** * condition must be side-effect free! */ diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts index ab824ae4c1ec5..b9a3880f0d90f 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts @@ -22,6 +22,7 @@ import { DiffEditorOptions } from './diffEditorOptions'; import { optimizeSequenceDiffs } from 'vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations'; import { isDefined } from 'vs/base/common/types'; import { groupAdjacentBy } from 'vs/base/common/arrays'; +import { softAssert } from 'vs/base/common/assert'; export class DiffEditorViewModel extends Disposable implements IDiffEditorViewModel { private readonly _isDiffUpToDate = observableValue(this, false); @@ -509,8 +510,14 @@ export class UnchangedRegion { visibleLineCountTop: number, visibleLineCountBottom: number, ) { - this._visibleLineCountTop.set(visibleLineCountTop, undefined); - this._visibleLineCountBottom.set(visibleLineCountBottom, undefined); + const visibleLineCountTop2 = Math.max(Math.min(visibleLineCountTop, this.lineCount), 0); + const visibleLineCountBottom2 = Math.max(Math.min(visibleLineCountBottom, this.lineCount - visibleLineCountTop), 0); + + softAssert(visibleLineCountTop === visibleLineCountTop2); + softAssert(visibleLineCountBottom === visibleLineCountBottom2); + + this._visibleLineCountTop.set(visibleLineCountTop2, undefined); + this._visibleLineCountBottom.set(visibleLineCountBottom2, undefined); } public setVisibleRanges(visibleRanges: LineRangeMapping[], tx: ITransaction): UnchangedRegion[] { From e5e5cb8632abd7fe6f4c522b84b3ee776b4c4f3b Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 22 Jan 2024 14:12:14 +0100 Subject: [PATCH 186/333] adding the license changes --- ThirdPartyNotices.txt | 102 ++++++--------- cglicenses.json | 88 +++++++++++++ cli/ThirdPartyNotices.txt | 253 +++++++++----------------------------- package.json | 2 +- 4 files changed, 187 insertions(+), 258 deletions(-) diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 4fd1d6a460b31..14af9ca8b8a6b 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -263,34 +263,6 @@ suitability for any purpose. --------------------------------------------------------- -better-go-syntax 1.0.0 - MIT -https://github.com/jeff-hykin/better-go-syntax/ - -MIT License - -Copyright (c) 2019 Jeff Hykin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ---------------------------------------------------------- - ---------------------------------------------------------- - Colorsublime-Themes 0.1.0 https://github.com/Colorsublime/Colorsublime-Themes @@ -545,6 +517,34 @@ to the base-name name of the original file, and an extension of txt, html, or si --------------------------------------------------------- +go-syntax 0.5.1 - MIT +https://github.com/worlpaker/go-syntax + +MIT License + +Copyright (c) 2023 Furkan Ozalp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + HTML 5.1 W3C Working Draft 08 October 2015 - W3C Document License http://www.w3.org/TR/2015/WD-html51-20151008/ @@ -1146,38 +1146,12 @@ Apache License --------------------------------------------------------- -language-less 0.34.2 - MIT -https://github.com/atom/language-less - -The MIT License (MIT) - -Copyright (c) 2014 GitHub Inc. +language-less 0.6.1 - MIT +https://github.com/radium-v/Better-Less -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -This package was derived from a TextMate bundle located at -https://github.com/textmate/less.tmbundle and distributed under the following -license, located in `LICENSE.md`: +MIT License -Copyright (c) 2010 Scott Kyle and Rasmus Andersson +Copyright (c) 2017 John Kreitlow Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -1186,16 +1160,16 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- @@ -1441,7 +1415,7 @@ SOFTWARE. --------------------------------------------------------- -microsoft/vscode-mssql 1.20.0 - MIT +microsoft/vscode-mssql 1.23.0 - MIT https://github.com/microsoft/vscode-mssql ------------------------------------------ START OF LICENSE ----------------------------------------- @@ -1532,7 +1506,7 @@ SOFTWARE. --------------------------------------------------------- -redhat-developer/vscode-java 1.22.0 - MIT +redhat-developer/vscode-java 1.26.0 - MIT https://github.com/redhat-developer/vscode-java The MIT License (MIT) diff --git a/cglicenses.json b/cglicenses.json index a5b108d4f79a8..3d4e0f804436b 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -543,5 +543,93 @@ "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", "SOFTWARE" ] + }, + { + "name": "vscode-webview-tools", + "fullLicenseText": [ + "MIT License", + "", + "Copyright (c) 2020 Connor Peet", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the 'Software'), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE." + ] + }, + { + "name": "@vscode/v8-heap-parser", + "fullLicenseText": [ + "The code in this package is built upon that in the Chrome devtools", + "(https://github.com/ChromeDevTools/devtools-frontend) which is made available", + "under the following license:", + "", + "// Copyright 2014 The Chromium Authors. All rights reserved.", + "//", + "// Redistribution and use in source and binary forms, with or without", + "// modification, are permitted provided that the following conditions are", + "// met:", + "//", + "// * Redistributions of source code must retain the above copyright", + "// notice, this list of conditions and the following disclaimer.", + "// * Redistributions in binary form must reproduce the above", + "// copyright notice, this list of conditions and the following disclaimer", + "// in the documentation and/or other materials provided with the", + "// distribution.", + "// * Neither the name of Google Inc. nor the names of its", + "// contributors may be used to endorse or promote products derived from", + "// this software without specific prior written permission.", + "//", + "// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS", + "// 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT", + "// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR", + "// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT", + "// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,", + "// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT", + "// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,", + "// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY", + "// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT", + "// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE", + "// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ] + }, + { + "name": "heap", + "fullLicenseText": [ + "The MIT License", + "", + "Copyright (c) Xueqiao (Joe) Xu", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the 'Software'), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", + "THE SOFTWARE." + ] } ] diff --git a/cli/ThirdPartyNotices.txt b/cli/ThirdPartyNotices.txt index 8903f9e531bf9..a867935f2b403 100644 --- a/cli/ThirdPartyNotices.txt +++ b/cli/ThirdPartyNotices.txt @@ -618,6 +618,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- bitflags 1.3.2 - MIT/Apache-2.0 +bitflags 2.4.1 - MIT OR Apache-2.0 https://github.com/bitflags/bitflags Copyright (c) 2014 The Rust Project Developers @@ -2368,6 +2369,38 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- +equivalent 1.0.1 - Apache-2.0 OR MIT +https://github.com/indexmap-rs/equivalent + +Copyright (c) 2016--2023 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + errno 0.3.1 - MIT OR Apache-2.0 https://github.com/lambda-fairy/rust-errno @@ -3235,7 +3268,7 @@ getrandom 0.1.16 - MIT OR Apache-2.0 getrandom 0.2.7 - MIT OR Apache-2.0 https://github.com/rust-random/getrandom -Copyright 2018 Developers of the Rand project +Copyright (c) 2018-2024 The rust-random Project Developers Copyright (c) 2014 The Rust Project Developers Permission is hereby granted, free of charge, to any @@ -3265,7 +3298,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -h2 0.3.17 - MIT +h2 0.3.24 - MIT https://github.com/hyperium/h2 The MIT License (MIT) @@ -3300,6 +3333,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- hashbrown 0.12.3 - MIT OR Apache-2.0 +hashbrown 0.14.3 - MIT OR Apache-2.0 https://github.com/rust-lang/hashbrown Copyright (c) 2016 Amanieu d'Antras @@ -3792,6 +3826,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- indexmap 1.9.1 - Apache-2.0 OR MIT +indexmap 2.1.0 - Apache-2.0 OR MIT https://github.com/bluss/indexmap Copyright (c) 2016--2017 @@ -5007,7 +5042,7 @@ OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -openssl 0.10.55 - Apache-2.0 +openssl 0.10.60 - Apache-2.0 https://github.com/sfackler/rust-openssl Copyright 2011-2017 Google Inc. @@ -5086,7 +5121,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -openssl-sys 0.9.90 - MIT +openssl-sys 0.9.96 - MIT https://github.com/sfackler/rust-openssl The MIT License (MIT) @@ -8038,10 +8073,10 @@ DEALINGS IN THE SOFTWARE. sha1 0.10.5 - MIT OR Apache-2.0 https://github.com/RustCrypto/hashes -All crates licensed under either of +All crates in this repository are licensed under either of - * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) - * [MIT license](http://opensource.org/licenses/MIT) +* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +* [MIT license](http://opensource.org/licenses/MIT) at your option. @@ -8056,9 +8091,9 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [deps-image]: https://deps.rs/repo/github/RustCrypto/hashes/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/hashes -[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg -[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg -[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg +[msrv-1.71]: https://img.shields.io/badge/rustc-1.71.0+-blue.svg +[msrv-1.72]: https://img.shields.io/badge/rustc-1.72.0+-blue.svg +[msrv-1.74]: https://img.shields.io/badge/rustc-1.74.0+-blue.svg [//]: # (crates) @@ -8131,10 +8166,10 @@ Unless you explicitly state otherwise, any contribution intentionally submitted sha2 0.10.6 - MIT OR Apache-2.0 https://github.com/RustCrypto/hashes -All crates licensed under either of +All crates in this repository are licensed under either of - * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) - * [MIT license](http://opensource.org/licenses/MIT) +* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +* [MIT license](http://opensource.org/licenses/MIT) at your option. @@ -8149,9 +8184,9 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [deps-image]: https://deps.rs/repo/github/RustCrypto/hashes/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/hashes -[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg -[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg -[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg +[msrv-1.71]: https://img.shields.io/badge/rustc-1.71.0+-blue.svg +[msrv-1.72]: https://img.shields.io/badge/rustc-1.72.0+-blue.svg +[msrv-1.74]: https://img.shields.io/badge/rustc-1.74.0+-blue.svg [//]: # (crates) @@ -8476,7 +8511,7 @@ SOFTWARE. --------------------------------------------------------- strsim 0.10.0 - MIT -https://github.com/dguo/strsim-rs +https://github.com/rapidfuzz/strsim-rs The MIT License (MIT) @@ -9273,7 +9308,7 @@ https://github.com/seanmonstar/try-lock The MIT License (MIT) -Copyright (c) 2018 Sean McArthur +Copyright (c) 2018-2023 Sean McArthur Copyright (c) 2016 Alex Crichton Permission is hereby granted, free of charge, to any person obtaining a copy @@ -10622,31 +10657,7 @@ DEALINGS IN THE SOFTWARE. xdg-home 1.0.0 - MIT https://github.com/zeenix/xdg-home -The MIT License (MIT) - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +LICENSE-MIT --------------------------------------------------------- --------------------------------------------------------- @@ -10668,31 +10679,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI zbus 3.13.1 - MIT https://github.com/dbus2/zbus/ -The MIT License (MIT) - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +LICENSE-MIT --------------------------------------------------------- --------------------------------------------------------- @@ -10700,31 +10687,7 @@ DEALINGS IN THE SOFTWARE. zbus_macros 3.13.1 - MIT https://github.com/dbus2/zbus/ -The MIT License (MIT) - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +LICENSE-MIT --------------------------------------------------------- --------------------------------------------------------- @@ -10732,31 +10695,7 @@ DEALINGS IN THE SOFTWARE. zbus_names 2.5.1 - MIT https://github.com/dbus2/zbus/ -The MIT License (MIT) - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +LICENSE-MIT --------------------------------------------------------- --------------------------------------------------------- @@ -10846,31 +10785,7 @@ SOFTWARE. zvariant 3.14.0 - MIT https://github.com/dbus2/zbus/ -The MIT License (MIT) - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +LICENSE-MIT --------------------------------------------------------- --------------------------------------------------------- @@ -10878,31 +10793,7 @@ DEALINGS IN THE SOFTWARE. zvariant_derive 3.14.0 - MIT https://github.com/dbus2/zbus/ -The MIT License (MIT) - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +LICENSE-MIT --------------------------------------------------------- --------------------------------------------------------- @@ -10910,29 +10801,5 @@ DEALINGS IN THE SOFTWARE. zvariant_utils 1.0.1 - MIT https://github.com/dbus2/zbus/ -The MIT License (MIT) - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +LICENSE-MIT --------------------------------------------------------- \ No newline at end of file diff --git a/package.json b/package.json index b221471572840..c27208c3fb811 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.86.0", - "distro": "e74e6b268c5f26bfeb37b3468500c70b4dbd98da", + "distro": "4d476863b75b35121a8d2a49c6adbdd2d9ea7572", "author": { "name": "Microsoft Corporation" }, From 4217af76967d061d673ad0d3464c1c8abbe8cd80 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 22 Jan 2024 14:31:23 +0100 Subject: [PATCH 187/333] restore stashed sessions for when accidentially dismissing a session (#203006) --- .../browser/inlineChat.contribution.ts | 2 +- .../inlineChat/browser/inlineChatActions.ts | 2 +- .../browser/inlineChatController.ts | 69 +++--------- .../browser/inlineChatSavingServiceImpl.ts | 3 +- .../inlineChat/browser/inlineChatSession.ts | 102 +++++++++++++++--- .../browser/inlineChatSessionService.ts | 17 ++- .../browser/inlineChatSessionServiceImpl.ts | 45 +++++--- .../browser/inlineChatStrategies.ts | 23 +--- 8 files changed, 156 insertions(+), 107 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts index 0d9c8cce7bef9..e86fb8b900ffe 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts @@ -32,7 +32,7 @@ registerEditorContribution(INTERACTIVE_EDITOR_ACCESSIBILITY_HELP_ID, InlineChatA registerAction2(InlineChatActions.CloseAction); registerAction2(InlineChatActions.ConfigureInlineChatAction); -// registerAction2(InlineChatActions.UnstashSessionAction); +registerAction2(InlineChatActions.UnstashSessionAction); registerAction2(InlineChatActions.MakeRequestAction); registerAction2(InlineChatActions.StopRequestAction); registerAction2(InlineChatActions.ReRunRequestAction); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index af47d8f2b764e..dd492579f8ba7 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -49,7 +49,7 @@ export class UnstashSessionAction extends EditorAction2 { }); } - override runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) { + override async runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) { const ctrl = InlineChatController.get(editor); if (ctrl) { const session = ctrl.unstashLastSession(); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 262736553ab80..2c7f73adbf5cb 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -12,7 +12,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { Lazy } from 'vs/base/common/lazy'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { MovingAverage } from 'vs/base/common/numbers'; import { StopWatch } from 'vs/base/common/stopwatch'; import { assertType } from 'vs/base/common/types'; @@ -46,6 +46,8 @@ import { EditModeStrategy, IEditObserver, LivePreviewStrategy, LiveStrategy, Pre import { IInlineChatMessageAppender, InlineChatZoneWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_RESPONSE_TYPES, CTX_INLINE_CHAT_SUPPORT_ISSUE_REPORTING, CTX_INLINE_CHAT_USER_DID_EDIT, EditMode, IInlineChatProgressItem, IInlineChatRequest, IInlineChatResponse, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseFeedbackKind, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import { StashedSession } from './inlineChatSession'; +import { IValidEditOperation } from 'vs/editor/common/model'; export const enum State { CREATE_SESSION = 'CREATE_SESSION', @@ -124,8 +126,8 @@ export class InlineChatController implements IEditorContribution { readonly onDidAcceptInput = Event.filter(this._messages.event, m => m === Message.ACCEPT_INPUT, this._store); readonly onDidCancelInput = Event.filter(this._messages.event, m => m === Message.CANCEL_INPUT || m === Message.CANCEL_SESSION, this._store); - private readonly _sessionStore: DisposableStore = this._store.add(new DisposableStore()); - + private readonly _sessionStore = this._store.add(new DisposableStore()); + private readonly _stashedSession = this._store.add(new MutableDisposable()); private _session?: Session; private _strategy?: EditModeStrategy; @@ -850,14 +852,22 @@ export class InlineChatController implements IEditorContribution { assertType(this._strategy); this._sessionStore.clear(); + let undoCancelEdits: IValidEditOperation[] = []; try { - await this._strategy.cancel(); + undoCancelEdits = this._strategy.cancel(); } catch (err) { this._dialogService.error(localize('err.discard', "Failed to discard changes.", toErrorMessage(err))); this._log('FAILED to discard changes'); this._log(err); } - this._inlineChatSessionService.releaseSession(this._session); + + this._stashedSession.clear(); + if (!this._session.isUnstashed && !!this._session.lastExchange && this._session.hunkData.size === this._session.hunkData.pending) { + // only stash sessions that were not unstashed, not "empty", and not interacted with + this._stashedSession.value = this._inlineChatSessionService.stashSession(this._session, this._editor, undoCancelEdits); + } else { + this._inlineChatSessionService.releaseSession(this._session); + } } this._resetWidget(); @@ -1132,7 +1142,7 @@ export class InlineChatController implements IEditorContribution { } unstashLastSession(): Session | undefined { - return undefined; + return this._stashedSession.value?.unstash(); } joinCurrentRun(): Promise | undefined { @@ -1140,53 +1150,6 @@ export class InlineChatController implements IEditorContribution { } } -// class StashedSession { - -// private readonly _listener: IDisposable; -// private readonly _ctxHasStashedSession: IContextKey; -// private _session: Session | undefined; - -// constructor( -// editor: ICodeEditor, -// session: Session, -// @IContextKeyService contextKeyService: IContextKeyService, -// @IInlineChatSessionService private readonly _sessionService: IInlineChatSessionService, -// @ILogService private readonly _logService: ILogService, -// ) { -// this._ctxHasStashedSession = CTX_INLINE_CHAT_HAS_STASHED_SESSION.bindTo(contextKeyService); - -// // keep session for a little bit, only release when user continues to work (type, move cursor, etc.) -// this._session = session; -// this._ctxHasStashedSession.set(true); -// this._listener = Event.once(Event.any(editor.onDidChangeCursorSelection, editor.onDidChangeModelContent, editor.onDidChangeModel))(() => { -// this._session = undefined; -// this._sessionService.releaseSession(session); -// this._ctxHasStashedSession.reset(); -// }); -// } - -// dispose() { -// this._listener.dispose(); -// this._ctxHasStashedSession.reset(); -// if (this._session) { -// this._sessionService.releaseSession(this._session); -// } -// } - -// unstash(): Session | undefined { -// if (!this._session) { -// return undefined; -// } -// this._listener.dispose(); -// const result = this._session; -// result.markUnstashed(); -// this._session = undefined; -// this._logService.debug('[IE] Unstashed session'); -// return result; -// } - -// } - async function showMessageResponse(accessor: ServicesAccessor, query: string, response: string) { const chatService = accessor.get(IChatService); const providerId = chatService.getProviderInfos()[0]?.id; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts index bbd97b97408a7..b68c999bd6cda 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts @@ -28,6 +28,7 @@ import { compare } from 'vs/base/common/strings'; import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; +import { Event } from 'vs/base/common/event'; interface SessionData { readonly resourceUri: URI; @@ -261,7 +262,7 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { let listener: IDisposable | undefined; const whenEnded = new Promise(resolve => { - listener = this._inlineChatSessionService.onDidEndSession(e => { + listener = Event.any(this._inlineChatSessionService.onDidEndSession, this._inlineChatSessionService.onDidStashSession)(e => { const data = sessions.get(e.session); if (data) { data.dispose(); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index fb2cdf8e54ca5..94f64a77e18a6 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -7,8 +7,8 @@ import { URI } from 'vs/base/common/uri'; import { Emitter, Event } from 'vs/base/common/event'; import { ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { IWorkspaceTextEdit, TextEdit, WorkspaceEdit } from 'vs/editor/common/languages'; -import { IIdentifiedSingleEditOperation, IModelDecorationOptions, IModelDeltaDecoration, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; -import { EditMode, IInlineChatSessionProvider, IInlineChatSession, IInlineChatBulkEditResponse, IInlineChatEditResponse, InlineChatResponseType, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { IIdentifiedSingleEditOperation, IModelDecorationOptions, IModelDeltaDecoration, ITextModel, IValidEditOperation, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { EditMode, IInlineChatSessionProvider, IInlineChatSession, IInlineChatBulkEditResponse, IInlineChatEditResponse, InlineChatResponseType, InlineChatResponseTypes, CTX_INLINE_CHAT_HAS_STASHED_SESSION } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -22,14 +22,17 @@ import { ILanguageService } from 'vs/editor/common/languages/language'; import { ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; -import { Recording } from './inlineChatSessionService'; +import { IInlineChatSessionService, Recording } from './inlineChatSessionService'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { asRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; import { coalesceInPlace } from 'vs/base/common/arrays'; import { Iterable } from 'vs/base/common/iterator'; import { IModelContentChangedEvent } from 'vs/editor/common/textModelEvents'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ILogService } from 'vs/platform/log/common/log'; export type TelemetryData = { @@ -37,6 +40,7 @@ export type TelemetryData = { rounds: string; undos: string; edits: boolean; + unstashed: number; finishedByEdit: boolean; startTime: string; endTime: string; @@ -50,6 +54,7 @@ export type TelemetryDataClassification = { rounds: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Number of request that were made' }; undos: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Requests that have been undone' }; edits: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Did edits happen while the session was active' }; + unstashed: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'How often did this session become stashed and resumed' }; finishedByEdit: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Did edits cause the session to terminate' }; startTime: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'When the session started' }; endTime: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'When the session ended' }; @@ -167,7 +172,8 @@ export class Session { finishedByEdit: false, rounds: '', undos: '', - editMode + editMode, + unstashed: 0 }; } @@ -184,6 +190,7 @@ export class Session { } markUnstashed() { + this._teldata.unstashed! += 1; this._isUnstashed = true; } @@ -415,6 +422,56 @@ export class ReplyResponse { } } +export class StashedSession { + + private readonly _listener: IDisposable; + private readonly _ctxHasStashedSession: IContextKey; + private _session: Session | undefined; + + constructor( + editor: ICodeEditor, + session: Session, + private readonly _undoCancelEdits: IValidEditOperation[], + @IContextKeyService contextKeyService: IContextKeyService, + @IInlineChatSessionService private readonly _sessionService: IInlineChatSessionService, + @ILogService private readonly _logService: ILogService + ) { + this._ctxHasStashedSession = CTX_INLINE_CHAT_HAS_STASHED_SESSION.bindTo(contextKeyService); + + // keep session for a little bit, only release when user continues to work (type, move cursor, etc.) + this._session = session; + this._ctxHasStashedSession.set(true); + this._listener = Event.once(Event.any(editor.onDidChangeCursorSelection, editor.onDidChangeModelContent, editor.onDidChangeModel))(() => { + this._session = undefined; + this._sessionService.releaseSession(session); + this._ctxHasStashedSession.reset(); + }); + } + + dispose() { + this._listener.dispose(); + this._ctxHasStashedSession.reset(); + if (this._session) { + this._sessionService.releaseSession(this._session); + } + } + + unstash(): Session | undefined { + if (!this._session) { + return undefined; + } + this._listener.dispose(); + const result = this._session; + result.markUnstashed(); + result.hunkData.ignoreTextModelNChanges = true; + result.textModelN.pushEditOperations(null, this._undoCancelEdits, () => null); + result.hunkData.ignoreTextModelNChanges = false; + this._session = undefined; + this._logService.debug('[IE] Unstashed session'); + return result; + } +} + // --- export class HunkData { @@ -629,6 +686,32 @@ export class HunkData { return Iterable.reduce(this._data.values(), (r, { state }) => r + (state === HunkState.Pending ? 1 : 0), 0); } + private _discardEdits(item: HunkInformation): ISingleEditOperation[] { + const edits: ISingleEditOperation[] = []; + const rangesN = item.getRangesN(); + const ranges0 = item.getRanges0(); + for (let i = 1; i < rangesN.length; i++) { + const modifiedRange = rangesN[i]; + + const originalValue = this._textModel0.getValueInRange(ranges0[i]); + edits.push(EditOperation.replace(modifiedRange, originalValue)); + } + return edits; + } + + discardAll() { + const edits: ISingleEditOperation[][] = []; + for (const item of this.getInfo()) { + edits.push(this._discardEdits(item)); + } + const undoEdits: IValidEditOperation[][] = []; + this._textModelN.pushEditOperations(null, edits.flat(), (_undoEdits) => { + undoEdits.push(_undoEdits); + return null; + }); + return undoEdits.flat(); + } + getInfo(): HunkInformation[] { const result: HunkInformation[] = []; @@ -655,14 +738,7 @@ export class HunkData { // DISCARD: replace modified range with original value. The modified range is retrieved from a decoration // which was created above so that typing in the editor keeps discard working. if (data.state === HunkState.Pending) { - const edits: ISingleEditOperation[] = []; - const rangesN = item.getRangesN(); - const ranges0 = item.getRanges0(); - for (let i = 1; i < rangesN.length; i++) { - const modifiedRange = rangesN[i]; - const originalValue = this._textModel0.getValueInRange(ranges0[i]); - edits.push(EditOperation.replace(modifiedRange, originalValue)); - } + const edits = this._discardEdits(item); this._textModelN.pushEditOperations(null, edits, () => null); data.state = HunkState.Rejected; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionService.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionService.ts index ee8de25c12d6e..0f2f0fb414233 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionService.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionService.ts @@ -10,7 +10,8 @@ import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser' import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable } from 'vs/base/common/lifecycle'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { Session } from './inlineChatSession'; +import { Session, StashedSession } from './inlineChatSession'; +import { IValidEditOperation } from 'vs/editor/common/model'; export type Recording = { @@ -25,14 +26,18 @@ export interface ISessionKeyComputer { export const IInlineChatSessionService = createDecorator('IInlineChatSessionService'); +export interface IInlineChatSessionEvent { + readonly editor: ICodeEditor; + readonly session: Session; +} + export interface IInlineChatSessionService { _serviceBrand: undefined; onWillStartSession: Event; - - onDidMoveSession: Event<{ editor: ICodeEditor; session: Session }>; - - onDidEndSession: Event<{ editor: ICodeEditor; session: Session }>; + onDidMoveSession: Event; + onDidStashSession: Event; + onDidEndSession: Event; createSession(editor: IActiveCodeEditor, options: { editMode: EditMode; wholeRange?: IRange }, token: CancellationToken): Promise; @@ -44,6 +49,8 @@ export interface IInlineChatSessionService { releaseSession(session: Session): void; + stashSession(session: Session, editor: ICodeEditor, undoCancelEdits: IValidEditOperation[]): StashedSession; + registerSessionKeyComputer(scheme: string, value: ISessionKeyComputer): IDisposable; // diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts index acd05d3527d3b..ab3d899aed63c 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts @@ -16,11 +16,12 @@ import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Iterable } from 'vs/base/common/iterator'; import { raceCancellation } from 'vs/base/common/async'; -import { Recording, IInlineChatSessionService, ISessionKeyComputer } from './inlineChatSessionService'; -import { HunkData, Session, SessionWholeRange, TelemetryData, TelemetryDataClassification } from './inlineChatSession'; +import { Recording, IInlineChatSessionService, ISessionKeyComputer, IInlineChatSessionEvent } from './inlineChatSessionService'; +import { HunkData, Session, SessionWholeRange, StashedSession, TelemetryData, TelemetryDataClassification } from './inlineChatSession'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; -import { ITextModel } from 'vs/editor/common/model'; +import { ITextModel, IValidEditOperation } from 'vs/editor/common/model'; import { Schemas } from 'vs/base/common/network'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; type SessionData = { editor: ICodeEditor; @@ -35,11 +36,14 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { private readonly _onWillStartSession = new Emitter(); readonly onWillStartSession: Event = this._onWillStartSession.event; - private readonly _onDidMoveSession = new Emitter<{ session: Session; editor: ICodeEditor }>(); - readonly onDidMoveSession: Event<{ session: Session; editor: ICodeEditor }> = this._onDidMoveSession.event; + private readonly _onDidMoveSession = new Emitter(); + readonly onDidMoveSession: Event = this._onDidMoveSession.event; - private readonly _onDidEndSession = new Emitter<{ editor: ICodeEditor; session: Session }>(); - readonly onDidEndSession: Event<{ editor: ICodeEditor; session: Session }> = this._onDidEndSession.event; + private readonly _onDidEndSession = new Emitter(); + readonly onDidEndSession: Event = this._onDidEndSession.event; + + private readonly _onDidStashSession = new Emitter(); + readonly onDidStashSession: Event = this._onDidStashSession.event; private readonly _sessions = new Map(); private readonly _keyComputers = new Map(); @@ -51,7 +55,8 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { @IModelService private readonly _modelService: IModelService, @ITextModelService private readonly _textModelService: ITextModelService, @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, - @ILogService private readonly _logService: ILogService + @ILogService private readonly _logService: ILogService, + @IInstantiationService private readonly _instaService: IInstantiationService, ) { } dispose() { @@ -189,18 +194,20 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { return; } - // keep recording - const newLen = this._recordings.unshift(session.asRecording()); - if (newLen > 5) { - this._recordings.pop(); - } - - // send telemetry + this._keepRecording(session); this._telemetryService.publicLog2('interactiveEditor/session', session.asTelemetryData()); this._onDidEndSession.fire({ editor: data.editor, session }); } + stashSession(session: Session, editor: ICodeEditor, undoCancelEdits: IValidEditOperation[]): StashedSession { + this._keepRecording(session); + const result = this._instaService.createInstance(StashedSession, editor, session, undoCancelEdits); + this._onDidStashSession.fire({ editor, session }); + this._logService.trace(`[IE] did STASH session for ${editor.getId()}, ${session.provider.debugName}`); + return result; + } + getCodeEditor(session: Session): ICodeEditor { for (const [, data] of this._sessions) { if (data.session === session) { @@ -229,6 +236,14 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { } // --- debug + + private _keepRecording(session: Session) { + const newLen = this._recordings.unshift(session.asRecording()); + if (newLen > 5) { + this._recordings.pop(); + } + } + recordings(): readonly Recording[] { return this._recordings; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index b7009c9184d33..dab83d92c176d 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -75,7 +75,9 @@ export abstract class EditModeStrategy { abstract apply(): Promise; - abstract cancel(): Promise; + cancel() { + return this._session.hunkData.discardAll(); + } async acceptHunk(): Promise { this._onDidAccept.fire(); @@ -186,10 +188,6 @@ export class PreviewStrategy extends EditModeStrategy { } } - async cancel(): Promise { - // nothing to do - } - override async makeChanges(edits: ISingleEditOperation[], obs: IEditObserver): Promise { return this._makeChanges(edits, obs, undefined, undefined); } @@ -267,15 +265,6 @@ export class LivePreviewStrategy extends EditModeStrategy { } } - async cancel() { - const { textModelN: modelN, textModelNAltVersion, textModelNSnapshotAltVersion } = this._session; - if (modelN.isDisposed()) { - return; - } - const targetAltVersion = textModelNSnapshotAltVersion ?? textModelNAltVersion; - await undoModelUntil(modelN, targetAltVersion); - } - override async undoChanges(altVersionId: number): Promise { const { textModelN } = this._session; await undoModelUntil(textModelN, altVersionId); @@ -495,11 +484,9 @@ export class LiveStrategy extends EditModeStrategy { } } - async cancel() { - for (const item of this._session.hunkData.getInfo()) { - item.discardChanges(); - } + override cancel() { this._resetDiff(); + return super.cancel(); } override async undoChanges(altVersionId: number): Promise { From cc593a387ec2f7f22d67492633ae638953bbb2e3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 22 Jan 2024 14:39:08 +0100 Subject: [PATCH 188/333] inline chat tweaks and tests (#202984) * update action ids * detect user edits inside and outside of chat UI, add test for https://github.com/microsoft/vscode-copilot/issues/3523 * fix tests --- .../inlineChat/browser/inlineChatActions.ts | 2 + .../browser/inlineChatController.ts | 2 +- .../contrib/inlineChat/common/inlineChat.ts | 2 +- .../test/browser/inlineChatController.test.ts | 44 +++++++++++++++++-- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index dd492579f8ba7..c1c0679e305db 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -32,6 +32,8 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; CommandsRegistry.registerCommandAlias('interactiveEditor.start', 'inlineChat.start'); +CommandsRegistry.registerCommandAlias('interactive.acceptChanges', ACTION_ACCEPT_CHANGES); + export const LOCALIZED_START_INLINE_CHAT_STRING = localize('run', 'Start Inline Chat'); export const START_INLINE_CHAT = registerIcon('start-inline-chat', Codicon.sparkle, localize('startInlineChat', 'Icon which spawns the inline chat from the editor toolbar.')); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 2c7f73adbf5cb..977f9b71bdf84 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -395,7 +395,7 @@ export class InlineChatController implements IEditorContribution { this._sessionStore.add(this._editor.onDidChangeModelContent(e => { - if (!this._session?.hunkData.ignoreTextModelNChanges && this._strategy?.hasFocus()) { + if (!this._session?.hunkData.ignoreTextModelNChanges) { this._ctxUserDidEdit.set(altVersionNow !== this._editor.getModel()?.getAlternativeVersionId()); } diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index f7becc10ec49f..1b5712c36e2cf 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -150,7 +150,7 @@ export const CTX_INLINE_CHAT_EDIT_MODE = new RawContextKey('config.inl // --- (select) action identifier -export const ACTION_ACCEPT_CHANGES = 'interactive.acceptChanges'; +export const ACTION_ACCEPT_CHANGES = 'inlineChat.acceptChanges'; export const ACTION_REGENERATE_RESPONSE = 'inlineChat.regenerate'; export const ACTION_VIEW_IN_CHAT = 'inlineChat.viewInChat'; 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 1648debcf8c1c..ae238b40501e9 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -36,9 +36,10 @@ import { IInlineChatSavingService } from '../../browser/inlineChatSavingService' import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { InlineChatSessionServiceImpl } from '../../browser/inlineChatSessionServiceImpl'; import { IInlineChatSessionService } from '../../browser/inlineChatSessionService'; -import { EditMode, IInlineChatService, InlineChatConfigKeys, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { CTX_INLINE_CHAT_USER_DID_EDIT, EditMode, IInlineChatService, InlineChatConfigKeys, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { InlineChatServiceImpl } from 'vs/workbench/contrib/inlineChat/common/inlineChatServiceImpl'; import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; suite('InteractiveChatController', function () { class TestController extends InlineChatController { @@ -90,14 +91,14 @@ suite('InteractiveChatController', function () { let editor: IActiveCodeEditor; let model: ITextModel; let ctrl: TestController; - // let contextKeys: MockContextKeyService; + let contextKeyService: MockContextKeyService; let inlineChatService: InlineChatServiceImpl; let inlineChatSessionService: IInlineChatSessionService; let instaService: TestInstantiationService; setup(function () { - const contextKeyService = new MockContextKeyService(); + contextKeyService = new MockContextKeyService(); inlineChatService = new InlineChatServiceImpl(contextKeyService); configurationService = new TestConfigurationService(); @@ -436,4 +437,41 @@ suite('InteractiveChatController', function () { }); }); + test('escape doesn\'t remove code added from inline editor chat #3523 1/2', async function () { + + + // NO manual edits -> cancel + ctrl = instaService.createInstance(TestController, editor); + const p = ctrl.waitFor([...TestController.INIT_SEQUENCE, State.MAKE_REQUEST, State.APPLY_RESPONSE, State.SHOW_RESPONSE, State.WAIT_FOR_INPUT]); + const r = ctrl.run({ message: 'GENERATED', autoSend: true }); + await p; + + assert.ok(model.getValue().includes('GENERATED')); + assert.strictEqual(contextKeyService.getContextKeyValue(CTX_INLINE_CHAT_USER_DID_EDIT.key), undefined); + ctrl.cancelSession(); + await r; + assert.ok(!model.getValue().includes('GENERATED')); + + }); + + test('escape doesn\'t remove code added from inline editor chat #3523, 2/2', async function () { + + // manual edits -> finish + ctrl = instaService.createInstance(TestController, editor); + const p = ctrl.waitFor([...TestController.INIT_SEQUENCE, State.MAKE_REQUEST, State.APPLY_RESPONSE, State.SHOW_RESPONSE, State.WAIT_FOR_INPUT]); + const r = ctrl.run({ message: 'GENERATED', autoSend: true }); + await p; + + assert.ok(model.getValue().includes('GENERATED')); + + editor.executeEdits('test', [EditOperation.insert(model.getFullModelRange().getEndPosition(), 'MANUAL')]); + assert.strictEqual(contextKeyService.getContextKeyValue(CTX_INLINE_CHAT_USER_DID_EDIT.key), true); + + ctrl.finishExistingSession(); + await r; + assert.ok(model.getValue().includes('GENERATED')); + assert.ok(model.getValue().includes('MANUAL')); + + }); + }); From 236d5dc072622d511e2f8f6ddd83fd24a7165467 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 22 Jan 2024 14:40:18 +0100 Subject: [PATCH 189/333] `sha256` over `md5` (#202985) * `sha256` over `md5` * add missing JS file --- build/lib/tsb/builder.js | 4 ++-- build/lib/tsb/builder.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/lib/tsb/builder.js b/build/lib/tsb/builder.js index e041a754c4fea..e87945ea9cc43 100644 --- a/build/lib/tsb/builder.js +++ b/build/lib/tsb/builder.js @@ -89,7 +89,7 @@ function createTypeScriptBuilder(config, projectFile, cmd) { if (/\.d\.ts$/.test(fileName)) { // if it's already a d.ts file just emit it signature const snapshot = host.getScriptSnapshot(fileName); - const signature = crypto.createHash('md5') + const signature = crypto.createHash('sha256') .update(snapshot.getText(0, snapshot.getLength())) .digest('base64'); return resolve({ @@ -106,7 +106,7 @@ function createTypeScriptBuilder(config, projectFile, cmd) { continue; } if (/\.d\.ts$/.test(file.name)) { - signature = crypto.createHash('md5') + signature = crypto.createHash('sha256') .update(file.text) .digest('base64'); if (!userWantsDeclarations) { diff --git a/build/lib/tsb/builder.ts b/build/lib/tsb/builder.ts index 7d40239e7159c..9fc476ae7020b 100644 --- a/build/lib/tsb/builder.ts +++ b/build/lib/tsb/builder.ts @@ -113,7 +113,7 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str if (/\.d\.ts$/.test(fileName)) { // if it's already a d.ts file just emit it signature const snapshot = host.getScriptSnapshot(fileName); - const signature = crypto.createHash('md5') + const signature = crypto.createHash('sha256') .update(snapshot.getText(0, snapshot.getLength())) .digest('base64'); @@ -134,7 +134,7 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str } if (/\.d\.ts$/.test(file.name)) { - signature = crypto.createHash('md5') + signature = crypto.createHash('sha256') .update(file.text) .digest('base64'); From a8dfc65ebd90122ded88577df386bf78ed404ebe Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 22 Jan 2024 15:59:55 +0100 Subject: [PATCH 190/333] fix tests, use real diff (slow) (#203022) --- .../test/browser/inlineChatController.test.ts | 11 ++- .../test/browser/inlineChatSession.test.ts | 71 +--------------- .../test/browser/testWorkerService.ts | 84 +++++++++++++++++++ 3 files changed, 93 insertions(+), 73 deletions(-) create mode 100644 src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts 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 ae238b40501e9..8efc9619cc9d6 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -40,6 +40,8 @@ import { CTX_INLINE_CHAT_USER_DID_EDIT, EditMode, IInlineChatService, InlineChat import { InlineChatServiceImpl } from 'vs/workbench/contrib/inlineChat/common/inlineChatServiceImpl'; import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { TestWorkerService } from './testWorkerService'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; suite('InteractiveChatController', function () { class TestController extends InlineChatController { @@ -106,6 +108,7 @@ suite('InteractiveChatController', function () { configurationService.setUserConfiguration('editor', {}); const serviceCollection = new ServiceCollection( + [IEditorWorkerService, new SyncDescriptor(TestWorkerService)], [IContextKeyService, contextKeyService], [IInlineChatService, inlineChatService], [IDiffProviderFactoryService, new SyncDescriptor(TestDiffProviderFactoryService)], @@ -263,7 +266,7 @@ suite('InteractiveChatController', function () { const session = inlineChatSessionService.getSession(editor, editor.getModel()!.uri); assert.ok(session); - assert.deepStrictEqual(session.wholeRange.value, new Range(1, 1, 1, 6)); + assert.deepStrictEqual(session.wholeRange.value, new Range(1, 1, 1, 10 /* line length */)); editor.setSelection(new Range(2, 1, 2, 1)); editor.trigger('test', 'type', { text: 'a' }); @@ -299,19 +302,19 @@ suite('InteractiveChatController', function () { store.add(d); ctrl = instaService.createInstance(TestController, editor); const p = ctrl.waitFor(TestController.INIT_SEQUENCE); - const r = ctrl.run({ message: 'Hello', autoSend: false }); + const r = ctrl.run({ message: 'GENGEN', autoSend: false }); await p; const session = inlineChatSessionService.getSession(editor, editor.getModel()!.uri); assert.ok(session); - assert.deepStrictEqual(session.wholeRange.value, new Range(3, 1, 3, 3)); + assert.deepStrictEqual(session.wholeRange.value, new Range(3, 1, 3, 3)); // initial ctrl.acceptInput(); await ctrl.waitFor([State.MAKE_REQUEST, State.APPLY_RESPONSE, State.SHOW_RESPONSE, State.WAIT_FOR_INPUT]); - assert.deepStrictEqual(session.wholeRange.value, new Range(4, 1, 4, 3)); + assert.deepStrictEqual(session.wholeRange.value, new Range(1, 1, 4, 3)); await ctrl.cancelSession(); await r; diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts index 0c6a58b40365c..53b0cb87519a1 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts @@ -41,12 +41,8 @@ import { assertType } from 'vs/base/common/types'; import { InlineChatServiceImpl } from 'vs/workbench/contrib/inlineChat/common/inlineChatServiceImpl'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; -import { DiffAlgorithmName, IEditorWorkerService, ILineChange } from 'vs/editor/common/services/editorWorker'; -import { IDocumentDiff, IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider'; -import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker'; -import { LineRange } from 'vs/editor/common/core/lineRange'; -import { MovedText } from 'vs/editor/common/diff/linesDiffComputer'; -import { LineRangeMapping, DetailedLineRangeMapping, RangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; +import { TestWorkerService } from './testWorkerService'; suite('ReplyResponse', function () { @@ -86,69 +82,6 @@ suite('ReplyResponse', function () { }); }); -class TestWorkerService extends mock() { - - private readonly _worker = new EditorSimpleWorker(null!, null); - - constructor(@IModelService private readonly _modelService: IModelService) { - super(); - } - - override async computeDiff(original: URI, modified: URI, options: IDocumentDiffProviderOptions, algorithm: DiffAlgorithmName): Promise { - - const originalModel = this._modelService.getModel(original); - const modifiedModel = this._modelService.getModel(modified); - - assertType(originalModel); - assertType(modifiedModel); - - this._worker.acceptNewModel({ - url: originalModel.uri.toString(), - versionId: originalModel.getVersionId(), - lines: originalModel.getLinesContent(), - EOL: originalModel.getEOL(), - }); - - this._worker.acceptNewModel({ - url: modifiedModel.uri.toString(), - versionId: modifiedModel.getVersionId(), - lines: modifiedModel.getLinesContent(), - EOL: modifiedModel.getEOL(), - }); - - const result = await this._worker.computeDiff(originalModel.uri.toString(), modifiedModel.uri.toString(), options, algorithm); - if (!result) { - return result; - } - // Convert from space efficient JSON data to rich objects. - const diff: IDocumentDiff = { - identical: result.identical, - quitEarly: result.quitEarly, - changes: toLineRangeMappings(result.changes), - moves: result.moves.map(m => new MovedText( - new LineRangeMapping(new LineRange(m[0], m[1]), new LineRange(m[2], m[3])), - toLineRangeMappings(m[4]) - )) - }; - return diff; - - function toLineRangeMappings(changes: readonly ILineChange[]): readonly DetailedLineRangeMapping[] { - return changes.map( - (c) => new DetailedLineRangeMapping( - new LineRange(c[0], c[1]), - new LineRange(c[2], c[3]), - c[4]?.map( - (c) => new RangeMapping( - new Range(c[0], c[1], c[2], c[3]), - new Range(c[4], c[5], c[6], c[7]) - ) - ) - ) - ); - } - } -} - suite('InlineChatSession', function () { const store = new DisposableStore(); diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts b/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts new file mode 100644 index 0000000000000..d9b6466638483 --- /dev/null +++ b/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { URI } from 'vs/base/common/uri'; +import { mock } from 'vs/base/test/common/mock'; +import { Range } from 'vs/editor/common/core/range'; +import { IModelService } from 'vs/editor/common/services/model'; +import { assertType } from 'vs/base/common/types'; +import { DiffAlgorithmName, IEditorWorkerService, ILineChange } from 'vs/editor/common/services/editorWorker'; +import { IDocumentDiff, IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider'; +import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker'; +import { LineRange } from 'vs/editor/common/core/lineRange'; +import { MovedText } from 'vs/editor/common/diff/linesDiffComputer'; +import { LineRangeMapping, DetailedLineRangeMapping, RangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { TextEdit } from 'vs/editor/common/languages'; + + +export class TestWorkerService extends mock() { + + private readonly _worker = new EditorSimpleWorker(null!, null); + + constructor(@IModelService private readonly _modelService: IModelService) { + super(); + } + + override async computeMoreMinimalEdits(resource: URI, edits: TextEdit[] | null | undefined, pretty?: boolean | undefined): Promise { + return undefined; + } + + override async computeDiff(original: URI, modified: URI, options: IDocumentDiffProviderOptions, algorithm: DiffAlgorithmName): Promise { + + const originalModel = this._modelService.getModel(original); + const modifiedModel = this._modelService.getModel(modified); + + assertType(originalModel); + assertType(modifiedModel); + + this._worker.acceptNewModel({ + url: originalModel.uri.toString(), + versionId: originalModel.getVersionId(), + lines: originalModel.getLinesContent(), + EOL: originalModel.getEOL(), + }); + + this._worker.acceptNewModel({ + url: modifiedModel.uri.toString(), + versionId: modifiedModel.getVersionId(), + lines: modifiedModel.getLinesContent(), + EOL: modifiedModel.getEOL(), + }); + + const result = await this._worker.computeDiff(originalModel.uri.toString(), modifiedModel.uri.toString(), options, algorithm); + if (!result) { + return result; + } + // Convert from space efficient JSON data to rich objects. + const diff: IDocumentDiff = { + identical: result.identical, + quitEarly: result.quitEarly, + changes: toLineRangeMappings(result.changes), + moves: result.moves.map(m => new MovedText( + new LineRangeMapping(new LineRange(m[0], m[1]), new LineRange(m[2], m[3])), + toLineRangeMappings(m[4]) + )) + }; + return diff; + + function toLineRangeMappings(changes: readonly ILineChange[]): readonly DetailedLineRangeMapping[] { + return changes.map( + (c) => new DetailedLineRangeMapping( + new LineRange(c[0], c[1]), + new LineRange(c[2], c[3]), + c[4]?.map( + (c) => new RangeMapping( + new Range(c[0], c[1], c[2], c[3]), + new Range(c[4], c[5], c[6], c[7]) + ) + ) + ) + ); + } + } +} From 4e31c84e0fe43e0df34b39d70ba279f04240f720 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 22 Jan 2024 16:21:09 +0100 Subject: [PATCH 191/333] make sure textModel0 (and textModekN) has a unique uri (#203015) --- .../inlineChat/browser/inlineChatSessionServiceImpl.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts index ab3d899aed63c..5e211c6ac6d1a 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts @@ -22,6 +22,7 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { ITextModel, IValidEditOperation } from 'vs/editor/common/model'; import { Schemas } from 'vs/base/common/network'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { generateUuid } from 'vs/base/common/uuid'; type SessionData = { editor: ICodeEditor; @@ -98,6 +99,7 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { this._logService.trace(`[IE] creating NEW session for ${editor.getId()}, ${provider.debugName}`); const store = new DisposableStore(); + const id = generateUuid(); const targetUri = textModel.uri; let textModelN: ITextModel; @@ -106,7 +108,7 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { textModelN = store.add(this._modelService.createModel( createTextBufferFactoryFromSnapshot(textModel.createSnapshot()), { languageId: textModel.getLanguageId(), onDidChange: Event.None }, - targetUri.with({ scheme: Schemas.inMemory, query: 'inline-chat-textModelN' }), true + targetUri.with({ scheme: Schemas.inMemory, query: new URLSearchParams({ id, 'inline-chat-textModelN': '' }).toString() }), true )); } else { // AI edits happen in the actual model, keep a reference but make no copy @@ -118,7 +120,7 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { const textModel0 = store.add(this._modelService.createModel( createTextBufferFactoryFromSnapshot(textModel.createSnapshot()), { languageId: textModel.getLanguageId(), onDidChange: Event.None }, - targetUri.with({ scheme: Schemas.inMemory, query: 'inline-chat-textModel0' }), true + targetUri.with({ scheme: Schemas.inMemory, query: new URLSearchParams({ id, 'inline-chat-textModel0': '' }).toString() }), true )); let wholeRange = options.wholeRange; From e5a6595f135c2fb27ee1d83dbfb6d4bc2115b55f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 22 Jan 2024 16:25:01 +0100 Subject: [PATCH 192/333] fix https://github.com/microsoft/vscode/issues/203008 (#203011) --- .../inlineChat/electron-sandbox/inlineChatQuickVoice.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts index 1e33dc7e5cdaa..948093ae666d6 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatQuickVoice.ts @@ -24,6 +24,7 @@ import { AbstractInlineChatAction } from 'vs/workbench/contrib/inlineChat/browse import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; const CTX_QUICK_CHAT_IN_PROGRESS = new RawContextKey('inlineChat.quickChatInProgress', false); @@ -34,7 +35,7 @@ export class StartAction extends EditorAction2 { id: 'inlineChat.quickVoice.start', title: localize2('start', "Start Inline Voice Chat"), category: AbstractInlineChatAction.category, - precondition: ContextKeyExpr.and(HasSpeechProvider, CTX_QUICK_CHAT_IN_PROGRESS.toNegated()), + precondition: ContextKeyExpr.and(HasSpeechProvider, CTX_QUICK_CHAT_IN_PROGRESS.toNegated(), EditorContextKeys.focus), f1: true, keybinding: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyI), From 40ecea93e9ec15368df68d1bf795959a2f72f598 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 22 Jan 2024 16:27:42 +0100 Subject: [PATCH 193/333] debt - prevent simple code editors in results (#203019) Co-authored-by: Johannes Rieken --- .../workbench/contrib/files/browser/fileCommands.ts | 2 +- .../services/dialogs/browser/fileDialogService.ts | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 434d8623aca27..6fa04545a0ea0 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -414,7 +414,7 @@ async function saveSelectedEditors(accessor: ServicesAccessor, options?: ISaveEd // find it in our text file models. Currently, only textual editors // support embedded editors. const focusedCodeEditor = codeEditorService.getFocusedCodeEditor(); - if (focusedCodeEditor instanceof EmbeddedCodeEditorWidget) { + if (focusedCodeEditor instanceof EmbeddedCodeEditorWidget && !focusedCodeEditor.isSimpleWidget) { const resource = focusedCodeEditor.getModel()?.uri; // Check that the resource of the model was not saved already diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts index aa590e8a240ef..c55daa6fdc309 100644 --- a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -19,6 +19,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { extractFileListData } from 'vs/platform/dnd/browser/dnd'; import { Iterable } from 'vs/base/common/iterator'; import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; export class FileDialogService extends AbstractFileDialogService implements IFileDialogService { @@ -217,10 +218,13 @@ export class FileDialogService extends AbstractFileDialogService implements IFil // When saving, try to just download the contents // of the active text editor if any as a workaround if (context === 'save') { - const activeTextModel = this.codeEditorService.getActiveCodeEditor()?.getModel(); - if (activeTextModel) { - triggerDownload(VSBuffer.fromString(activeTextModel.getValue()).buffer, basename(activeTextModel.uri)); - return; + const activeCodeEditor = this.codeEditorService.getActiveCodeEditor(); + if (!(activeCodeEditor instanceof EmbeddedCodeEditorWidget)) { + const activeTextModel = activeCodeEditor?.getModel(); + if (activeTextModel) { + triggerDownload(VSBuffer.fromString(activeTextModel.getValue()).buffer, basename(activeTextModel.uri)); + return; + } } } From 13196099b60ac204be38a9b13ff04264dbfb9c30 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 22 Jan 2024 16:31:42 +0100 Subject: [PATCH 194/333] polish pre-release UX (#203021) * improve pre-releas UX * rename labels --------- Co-authored-by: Johannes Rieken --- .../extensions/browser/extensionEditor.ts | 26 ++--------------- .../browser/extensions.contribution.ts | 29 ++++++++++++++++--- .../extensions/browser/extensionsActions.ts | 7 +++-- .../extensions/browser/extensionsWidgets.ts | 23 ++++++--------- .../browser/extensionsWorkbenchService.ts | 3 +- 5 files changed, 43 insertions(+), 45 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index a977ee1ed2f5e..8b49ea2903f34 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -78,7 +78,7 @@ import { WebInstallAction, TogglePreReleaseExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; -import { errorIcon, infoIcon, preReleaseIcon, warningIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; +import { errorIcon, infoIcon, warningIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; import { Delegate } from 'vs/workbench/contrib/extensions/browser/extensionsList'; import { ExtensionData, ExtensionsGridView, ExtensionsTree, getExtensions } from 'vs/workbench/contrib/extensions/browser/extensionsViewer'; import { ExtensionRecommendationWidget, ExtensionStatusWidget, ExtensionWidget, InstallCountWidget, RatingsWidget, RemoteBadgeWidget, SponsorWidget, VerifiedPublisherWidget, onClick } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; @@ -203,22 +203,7 @@ class VersionWidget extends ExtensionWithDifferentGalleryVersionWidget { if (!this.extension || !semver.valid(this.extension.version)) { return; } - this.element.textContent = `v${this.gallery?.version ?? this.extension.version}`; - } -} - -class PreReleaseTextWidget extends ExtensionWithDifferentGalleryVersionWidget { - private readonly element: HTMLElement; - constructor(container: HTMLElement) { - super(); - this.element = append(container, $('span.pre-release')); - append(this.element, $('span' + ThemeIcon.asCSSSelector(preReleaseIcon))); - const textElement = append(this.element, $('span.pre-release-text')); - textElement.textContent = localize('preRelease', "Pre-Release"); - this.render(); - } - render(): void { - this.element.style.display = this.extension?.isPreReleaseVersion ? 'inherit' : 'none'; + this.element.textContent = `v${this.gallery?.version ?? this.extension.version}${this.extension.isPreReleaseVersion ? ' (pre-release)' : ''}`; } } @@ -295,8 +280,6 @@ export class ExtensionEditor extends EditorPane { const name = append(title, $('span.name.clickable', { title: localize('name', "Extension name"), role: 'heading', tabIndex: 0 })); const versionWidget = new VersionWidget(title); - const preReleaseWidget = new PreReleaseTextWidget(title); - const preview = append(title, $('span.preview', { title: localize('preview', "Preview") })); preview.textContent = localize('preview', "Preview"); @@ -321,7 +304,6 @@ export class ExtensionEditor extends EditorPane { const widgets: ExtensionWidget[] = [ remoteBadge, versionWidget, - preReleaseWidget, verifiedPublisherWidget, installCountWidget, ratingsWidget, @@ -374,9 +356,6 @@ export class ExtensionEditor extends EditorPane { if (action instanceof ToggleAutoUpdateForExtensionAction) { return new CheckboxActionViewItem(undefined, action, { icon: true, label: true, checkboxStyles: defaultCheckboxStyles }); } - if (action instanceof TogglePreReleaseExtensionAction) { - return new CheckboxActionViewItem(undefined, action, { icon: true, label: true, checkboxStyles: defaultCheckboxStyles }); - } return undefined; }, focusOnlyEnabledItems: true @@ -448,7 +427,6 @@ export class ExtensionEditor extends EditorPane { }, set gallery(gallery: IGalleryExtension | null) { versionWidget.gallery = gallery; - preReleaseWidget.gallery = gallery; }, set manifest(manifest: IExtensionManifest | null) { installAction.manifest = manifest; diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 412cbbc3b5a6e..38a576546dc66 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -1372,16 +1372,37 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi }); this.registerExtensionAction({ - id: TogglePreReleaseExtensionAction.ID, - title: TogglePreReleaseExtensionAction.LABEL, + id: 'workbench.extensions.action.enablePreRlease', + title: localize('enablePreRleaseLabel', "Switch to Pre-Release Version"), category: ExtensionsLocalizedLabel, menu: { id: MenuId.ExtensionContext, group: INSTALL_ACTIONS_GROUP, order: 2, - when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) + when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.not('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) + }, + run: async (accessor: ServicesAccessor, id: string) => { + const instantiationService = accessor.get(IInstantiationService); + const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); + const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); + if (extension) { + const action = instantiationService.createInstance(TogglePreReleaseExtensionAction); + action.extension = extension; + return action.run(); + } + } + }); + + this.registerExtensionAction({ + id: 'workbench.extensions.action.disablPreRlease', + title: localize('disablePreRleaseLabel', "Switch to Release Version"), + category: ExtensionsLocalizedLabel, + menu: { + id: MenuId.ExtensionContext, + group: INSTALL_ACTIONS_GROUP, + order: 2, + when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.has('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) }, - toggled: ContextKeyExpr.has('installedExtensionIsOptedToPreRelease'), run: async (accessor: ServicesAccessor, id: string) => { const instantiationService = accessor.get(IInstantiationService); const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index fbd480a719f67..8b4d951ed4e42 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1272,7 +1272,7 @@ export class TogglePreReleaseExtensionAction extends ExtensionAction { static readonly ID = 'workbench.extensions.action.togglePreRlease'; static readonly LABEL = localize('togglePreRleaseLabel', "Pre-Release"); - private static readonly EnabledClass = `${ExtensionAction.EXTENSION_ACTION_CLASS} pre-release`; + private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} pre-release`; private static readonly DisabledClass = `${TogglePreReleaseExtensionAction.EnabledClass} hide`; constructor( @@ -1302,7 +1302,10 @@ export class TogglePreReleaseExtensionAction extends ExtensionAction { } this.enabled = true; this.class = TogglePreReleaseExtensionAction.EnabledClass; - this.checked = this.extension.preRelease; + this.label = this.extension.preRelease ? localize('togglePreRleaseDisableLabel', "Switch to Release Version") : localize('togglePreRleaseEnableLabel', "Switch to Pre-Release Version"); + this.tooltip = this.extension.preRelease + ? localize('togglePreRleaseDisableTooltip1', "This will switch and enable updates to release versions") + : localize('togglePreRleaseEnableTooltip', "This will switch to pre-release version and enable updates to latest version always"); } override async run(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index 8b95c60437167..64639cd23de14 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -320,15 +320,11 @@ export class PreReleaseBookmarkWidget extends ExtensionWidget { render(): void { this.clear(); - if (!this.extension) { - return; - } - if (!this.extension.isPreReleaseVersion) { - return; + if (this.extension?.state === ExtensionState.Installed ? this.extension.preRelease : this.extension?.hasPreReleaseVersion) { + this.element = append(this.parent, $('div.extension-bookmark')); + const preRelease = append(this.element, $('.pre-release')); + append(preRelease, $('span' + ThemeIcon.asCSSSelector(preReleaseIcon))); } - this.element = append(this.parent, $('div.extension-bookmark')); - const preRelease = append(this.element, $('.pre-release')); - append(preRelease, $('span' + ThemeIcon.asCSSSelector(preReleaseIcon))); } } @@ -553,11 +549,7 @@ export class ExtensionHoverWidget extends ExtensionWidget { markdown.appendMarkdown(`**${this.extension.displayName}**`); if (semver.valid(this.extension.version)) { - markdown.appendMarkdown(` ** _v${this.extension.version}_** `); - } - if (this.extension.state === ExtensionState.Installed ? this.extension.local?.isPreReleaseVersion : this.extension.gallery?.properties.isPreReleaseVersion) { - const extensionPreReleaseIcon = this.themeService.getColorTheme().getColor(extensionPreReleaseIconColor); - markdown.appendMarkdown(`** **  $(${preReleaseIcon.id}) ${localize('pre-release-label', "Pre-Release")} `); + markdown.appendMarkdown(` ** _v${this.extension.version}${(this.extension.isPreReleaseVersion ? ' (pre-release)' : '')}_** `); } markdown.appendText(`\n`); @@ -695,7 +687,10 @@ export class ExtensionHoverWidget extends ExtensionWidget { if (extension.isBuiltin) { return undefined; } - if (extension.local?.isPreReleaseVersion || extension.gallery?.properties.isPreReleaseVersion) { + if (extension.isPreReleaseVersion) { + return undefined; + } + if (extension.preRelease) { return undefined; } const preReleaseVersionLink = `[${localize('Show prerelease version', "Pre-Release version")}](${URI.parse(`command:workbench.extensions.action.showPreReleaseVersion?${encodeURIComponent(JSON.stringify([extension.identifier.id]))}`)})`; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 62731c2520aba..bb3f66c717516 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -656,7 +656,8 @@ class Extensions extends Disposable { private async onDidUpdateExtensionMetadata(local: ILocalExtension): Promise { const extension = this.installed.find(e => areSameExtensions(e.identifier, local.identifier)); if (extension?.local) { - const hasChanged = extension.local.pinned !== local.pinned; + const hasChanged = extension.local.pinned !== local.pinned + || extension.local.preRelease !== local.preRelease; extension.local = local; if (hasChanged) { this._onChange.fire({ extension }); From 9e96f6f7398582eb19a2124bda47e04b8df6a002 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 22 Jan 2024 15:32:05 +0100 Subject: [PATCH 195/333] multiFileDiffEditorOriginalUri -> multiDiffEditorOriginalUri --- src/vs/workbench/api/browser/mainThreadSCM.ts | 10 ++--- .../browser/scmMultiDiffSourceResolver.ts | 38 +++++++++++-------- src/vs/workbench/contrib/scm/common/scm.ts | 4 +- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 0ae7ac97e961a..1300c25f4f0f7 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -108,8 +108,8 @@ class MainThreadSCMResource implements ISCMResource { readonly decorations: ISCMResourceDecorations, readonly contextValue: string | undefined, readonly command: Command | undefined, - readonly multiFileDiffEditorOriginalUri: URI | undefined, - readonly multiFileDiffEditorModifiedUri: URI | undefined, + readonly multiDiffEditorOriginalUri: URI | undefined, + readonly multiDiffEditorModifiedUri: URI | undefined, ) { } open(preserveFocus: boolean): Promise { @@ -336,7 +336,7 @@ class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider { for (const [start, deleteCount, rawResources] of groupSlices) { const resources = rawResources.map(rawResource => { - const [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue, command, multiFileDiffEditorOriginalUri, multiFileDiffEditorModifiedUri] = rawResource; + const [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue, command, multiDiffEditorOriginalUri, multiDiffEditorModifiedUri] = rawResource; const [light, dark] = icons; const icon = ThemeIcon.isThemeIcon(light) ? light : URI.revive(light); @@ -360,8 +360,8 @@ class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider { decorations, contextValue || undefined, command, - URI.revive(multiFileDiffEditorOriginalUri), - URI.revive(multiFileDiffEditorModifiedUri), + URI.revive(multiDiffEditorOriginalUri), + URI.revive(multiDiffEditorModifiedUri), ); }); diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts index 1c1a4c8fc56e7..75d8776a7853a 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts @@ -6,12 +6,13 @@ import { Codicon } from 'vs/base/common/codicons'; import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { IObservable, observableFromEvent } from 'vs/base/common/observable'; import { URI } from 'vs/base/common/uri'; import { localize, localize2 } from 'vs/nls'; import { Action2, MenuId } from 'vs/platform/actions/common/actions'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, ContextKeyValue } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IMultiDiffSourceResolver, IMultiDiffSourceResolverService, IResolvedMultiDiffSource } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffSourceResolverService'; +import { IMultiDiffSourceResolver, IMultiDiffSourceResolverService, IResolvedMultiDiffSource, MultiDiffEditorItem } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffSourceResolverService'; import { ISCMResourceGroup, ISCMService } from 'vs/workbench/contrib/scm/common/scm'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -77,19 +78,26 @@ export class ScmMultiDiffSourceResolver implements IMultiDiffSourceResolver { } ); - return { - get resources() { - return group.resources.map(e => ({ - original: e.multiFileDiffEditorOriginalUri, - modified: e.multiFileDiffEditorModifiedUri - })); - }, - onDidChange: e => group.onDidChangeResources(() => e()), - contextKeys: { - scmResourceGroup: groupId, - scmProvider: repository.provider.contextValue, - }, - }; + const resources = observableFromEvent(group.onDidChangeResources, () => group.resources.map(e => ({ + original: e.multiDiffEditorOriginalUri, + modified: e.multiDiffEditorModifiedUri + }))); + + return new ScmResolvedMultiDiffSource(resources, { + scmResourceGroup: groupId, + scmProvider: repository.provider.contextValue, + }); + } +} + +class ScmResolvedMultiDiffSource implements IResolvedMultiDiffSource { + get resources(): readonly MultiDiffEditorItem[] { return this._resources.get(); } + public readonly onDidChange = Event.fromObservableLight(this._resources); + + constructor( + private readonly _resources: IObservable, + public readonly contextKeys: Record | undefined, + ) { } } diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index 1ff1a8c59fdc4..95bb756359d85 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -39,8 +39,8 @@ export interface ISCMResource { readonly decorations: ISCMResourceDecorations; readonly contextValue: string | undefined; readonly command: Command | undefined; - readonly multiFileDiffEditorOriginalUri: URI | undefined; - readonly multiFileDiffEditorModifiedUri: URI | undefined; + readonly multiDiffEditorOriginalUri: URI | undefined; + readonly multiDiffEditorModifiedUri: URI | undefined; open(preserveFocus: boolean): Promise; } From db62e3b46c829fb15349ad6c26468b077f1ca4dc Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 22 Jan 2024 14:54:02 +0100 Subject: [PATCH 196/333] Fixes multi file diff editor bug that deleted files would be shown as added files. --- extensions/git/src/repository.ts | 59 ++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index b0d6ef6235a86..698ab8affd58f 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -154,19 +154,19 @@ export class Resource implements SourceControlResourceState { } get leftUri(): Uri | undefined { - return this.resources[0]; + return this.resources.left; } get rightUri(): Uri | undefined { - return this.resources[1]; + return this.resources.right; } get multiDiffEditorOriginalUri(): Uri | undefined { - return this.leftUri; + return this.resources.original; } get multiFileDiffEditorModifiedUri(): Uri | undefined { - return this.rightUri; + return this.resources.modified; } @memoize @@ -175,7 +175,7 @@ export class Resource implements SourceControlResourceState { } @memoize - private get resources(): [Uri | undefined, Uri | undefined] { + private get resources(): { left: Uri | undefined; right: Uri | undefined; original: Uri | undefined; modified: Uri | undefined } { return this._commandResolver.getResources(this); } @@ -534,53 +534,63 @@ class ResourceCommandResolver { } } - getResources(resource: Resource): [Uri | undefined, Uri | undefined] { + getResources(resource: Resource): { left: Uri | undefined; right: Uri | undefined; original: Uri | undefined; modified: Uri | undefined } { for (const submodule of this.repository.submodules) { if (path.join(this.repository.root, submodule.path) === resource.resourceUri.fsPath) { - return [undefined, toGitUri(resource.resourceUri, resource.resourceGroupType === ResourceGroupType.Index ? 'index' : 'wt', { submoduleOf: this.repository.root })]; + const original = undefined; + const modified = toGitUri(resource.resourceUri, resource.resourceGroupType === ResourceGroupType.Index ? 'index' : 'wt', { submoduleOf: this.repository.root }); + return { left: original, right: modified, original, modified }; } } - return [this.getLeftResource(resource), this.getRightResource(resource)]; + const left = this.getLeftResource(resource); + const right = this.getRightResource(resource); + + return { + left: left.original ?? left.modified, + right: right.original ?? right.modified, + original: left.original ?? right.original, + modified: left.modified ?? right.modified, + }; } - private getLeftResource(resource: Resource): Uri | undefined { + private getLeftResource(resource: Resource): ModifiedOrOriginal { switch (resource.type) { case Status.INDEX_MODIFIED: case Status.INDEX_RENAMED: case Status.INDEX_ADDED: case Status.INTENT_TO_RENAME: case Status.TYPE_CHANGED: - return toGitUri(resource.original, 'HEAD'); + return { original: toGitUri(resource.original, 'HEAD') }; case Status.MODIFIED: case Status.UNTRACKED: - return toGitUri(resource.resourceUri, '~'); + return { original: toGitUri(resource.resourceUri, '~') }; case Status.DELETED_BY_US: case Status.DELETED_BY_THEM: - return toGitUri(resource.resourceUri, '~1'); + return { original: toGitUri(resource.resourceUri, '~1') }; } - return undefined; + return {}; } - private getRightResource(resource: Resource): Uri | undefined { + private getRightResource(resource: Resource): ModifiedOrOriginal { switch (resource.type) { case Status.INDEX_MODIFIED: case Status.INDEX_ADDED: case Status.INDEX_COPIED: case Status.INDEX_RENAMED: - return toGitUri(resource.resourceUri, ''); + return { modified: toGitUri(resource.resourceUri, '') }; case Status.INDEX_DELETED: case Status.DELETED: - return toGitUri(resource.resourceUri, 'HEAD'); + return { original: toGitUri(resource.resourceUri, 'HEAD') }; case Status.DELETED_BY_US: - return toGitUri(resource.resourceUri, '~3'); + return { original: toGitUri(resource.resourceUri, '~3') }; case Status.DELETED_BY_THEM: - return toGitUri(resource.resourceUri, '~2'); + return { original: toGitUri(resource.resourceUri, '~2') }; case Status.MODIFIED: case Status.UNTRACKED: @@ -592,17 +602,17 @@ class ResourceCommandResolver { const [indexStatus] = this.repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); if (indexStatus && indexStatus.renameResourceUri) { - return indexStatus.renameResourceUri; + return { modified: indexStatus.renameResourceUri }; } - return resource.resourceUri; + return { modified: resource.resourceUri }; } case Status.BOTH_ADDED: case Status.BOTH_MODIFIED: - return resource.resourceUri; + return { modified: resource.resourceUri }; } - return undefined; + return {}; } private getTitle(resource: Resource): string { @@ -645,6 +655,11 @@ class ResourceCommandResolver { } } +interface ModifiedOrOriginal { + modified?: Uri | undefined; + original?: Uri | undefined; +} + interface BranchProtectionMatcher { include?: picomatch.Matcher; exclude?: picomatch.Matcher; From 615e7eccca5e6f26242f357531402b452060453c Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:38:23 +0100 Subject: [PATCH 197/333] Polish custom titlebar visibility behaviour (#203017) * polish custom titlebar visibility behaviour * cleanup --------- Co-authored-by: Johannes Rieken --- .../browser/actions/layoutActions.ts | 11 +- src/vs/workbench/browser/layout.ts | 19 +++- .../browser/parts/sidebar/sidebarPart.ts | 3 +- .../browser/parts/titlebar/titlebarActions.ts | 107 ++++++++++++++++-- .../browser/parts/titlebar/titlebarPart.ts | 13 +-- .../browser/workbench.contribution.ts | 8 +- 6 files changed, 124 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index d6e446d04f1b6..08e0fb7835796 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -22,7 +22,7 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b import { ToggleAuxiliaryBarAction } from 'vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions'; import { TogglePanelAction } from 'vs/workbench/browser/parts/panel/panelActions'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { AuxiliaryBarVisibleContext, PanelAlignmentContext, PanelVisibleContext, SideBarVisibleContext, FocusedViewContext, InEditorZenModeContext, IsCenteredLayoutContext, MainEditorAreaVisibleContext, IsMainWindowFullscreenContext, PanelPositionContext, IsAuxiliaryWindowFocusedContext } from 'vs/workbench/common/contextkeys'; +import { AuxiliaryBarVisibleContext, PanelAlignmentContext, PanelVisibleContext, SideBarVisibleContext, FocusedViewContext, InEditorZenModeContext, IsCenteredLayoutContext, MainEditorAreaVisibleContext, IsMainWindowFullscreenContext, PanelPositionContext, IsAuxiliaryWindowFocusedContext, TitleBarStyleContext } from 'vs/workbench/common/contextkeys'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -30,7 +30,7 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { ICommandActionTitle } from 'vs/platform/action/common/action'; import { mainWindow } from 'vs/base/browser/window'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { TitleBarSetting, TitlebarStyle } from 'vs/platform/window/common/window'; +import { TitlebarStyle } from 'vs/platform/window/common/window'; // Register Icons const menubarIcon = registerIcon('menuBar', Codicon.layoutMenubar, localize('menuBarIcon', "Represents the menu bar")); @@ -587,10 +587,7 @@ export class EditorActionsTitleBarAction extends Action2 { original: 'Move Editor Actions to Title Bar' }, category: Categories.View, - precondition: ContextKeyExpr.and( - ContextKeyExpr.equals(`config.${LayoutSettings.EDITOR_ACTIONS_LOCATION}`, EditorActionsLocation.TITLEBAR).negate(), - ContextKeyExpr.equals(`config.${TitleBarSetting.TITLE_BAR_STYLE}`, TitlebarStyle.NATIVE).negate(), - ), + precondition: ContextKeyExpr.equals(`config.${LayoutSettings.EDITOR_ACTIONS_LOCATION}`, EditorActionsLocation.TITLEBAR).negate(), f1: true }); } @@ -803,7 +800,7 @@ if (isWindows || isLinux || isWeb) { title: localize('miMenuBarNoMnemonic', "Menu Bar"), toggled: ContextKeyExpr.and(IsMacNativeContext.toNegated(), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'hidden'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'toggle'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'compact')) }, - when: ContextKeyExpr.and(IsAuxiliaryWindowFocusedContext.toNegated(), ContextKeyExpr.notEquals(TitleBarSetting.TITLE_BAR_STYLE, TitlebarStyle.NATIVE), IsMainWindowFullscreenContext.negate()), + when: ContextKeyExpr.and(IsAuxiliaryWindowFocusedContext.toNegated(), ContextKeyExpr.notEquals(TitleBarStyleContext.key, TitlebarStyle.NATIVE), IsMainWindowFullscreenContext.negate()), group: '2_config', order: 0 }); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 6eb24dbdeca5f..e8322cbf07d0c 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -354,6 +354,15 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi TitleBarSetting.TITLE_BAR_STYLE, TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, ].some(setting => e.affectsConfiguration(setting))) { + // Show Custom TitleBar if actions moved to the titlebar + const activityBarMovedToTop = e.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION) && this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.TOP; + const editorActionsMovedToTitlebar = e.affectsConfiguration(LayoutSettings.EDITOR_ACTIONS_LOCATION) && this.configurationService.getValue(LayoutSettings.EDITOR_ACTIONS_LOCATION) === EditorActionsLocation.TITLEBAR; + if (activityBarMovedToTop || editorActionsMovedToTitlebar) { + if (this.configurationService.getValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY) === CustomTitleBarVisibility.NEVER) { + this.configurationService.updateValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, CustomTitleBarVisibility.AUTO); + } + } + this.doUpdateLayoutConfiguration(); } })); @@ -1237,7 +1246,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return false; } - if (!this.isTitleBarEmpty(nativeTitleBarEnabled)) { + if (!this.isTitleBarEmpty()) { return true; } @@ -1277,7 +1286,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } - private isTitleBarEmpty(nativeTitleBarEnabled: boolean): boolean { + private isTitleBarEmpty(): boolean { // with the command center enabled, we should always show if (this.configurationService.getValue(LayoutSettings.COMMAND_CENTER)) { return false; @@ -1295,8 +1304,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return false; } - // Layout don't show with native title bar - if (!nativeTitleBarEnabled && this.configurationService.getValue(LayoutSettings.LAYOUT_ACTIONS)) { + // with the layout actions on top, we should always show + if (this.configurationService.getValue(LayoutSettings.LAYOUT_ACTIONS)) { return false; } @@ -1742,7 +1751,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } private canActivityBarBeHidden(): boolean { - return !(this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.TOP && !this.isVisible(Parts.TITLEBAR_PART, mainWindow)); + return this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.TOP; } private setBannerHidden(hidden: boolean): void { diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index abadf927fd96c..ec276e02f5a3b 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -31,7 +31,6 @@ import { Action2, IMenuService, registerAction2 } from 'vs/platform/actions/comm import { Separator } from 'vs/base/common/actions'; import { ToggleActivityBarVisibilityActionId } from 'vs/workbench/browser/actions/layoutActions'; import { localize } from 'vs/nls'; -import { mainWindow } from 'vs/base/browser/window'; export class SidebarPart extends AbstractPaneCompositePart { @@ -195,7 +194,7 @@ export class SidebarPart extends AbstractPaneCompositePart { } protected shouldShowCompositeBar(): boolean { - return this.layoutService.isVisible(Parts.TITLEBAR_PART, mainWindow) && this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.TOP; + return this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.TOP; } private shouldShowActivityBar(): boolean { diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index c010eac936a84..f800a48b72d1e 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -9,20 +9,18 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { LayoutSettings } from 'vs/workbench/services/layout/browser/layoutService'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ACCOUNTS_ACTIVITY_ID, GLOBAL_ACTIVITY_ID } from 'vs/workbench/common/activity'; import { IAction } from 'vs/base/common/actions'; -import { IsAuxiliaryWindowFocusedContext, IsMainWindowFullscreenContext } from 'vs/workbench/common/contextkeys'; +import { IsAuxiliaryWindowFocusedContext, IsMainWindowFullscreenContext, TitleBarStyleContext, TitleBarVisibleContext } from 'vs/workbench/common/contextkeys'; import { CustomTitleBarVisibility, TitleBarSetting, TitlebarStyle } from 'vs/platform/window/common/window'; // --- Context Menu Actions --- // class ToggleConfigAction extends Action2 { - constructor(private readonly section: string, title: string, order: number, mainWindowOnly: boolean, showInCustomTitleBarWhenNativeTitle: boolean) { - let when = mainWindowOnly ? IsAuxiliaryWindowFocusedContext.toNegated() : ContextKeyExpr.true(); - when = showInCustomTitleBarWhenNativeTitle ? when : ContextKeyExpr.and(when, ContextKeyExpr.equals(`config.${TitleBarSetting.TITLE_BAR_STYLE}`, TitlebarStyle.NATIVE).negate())!; - + constructor(private readonly section: string, title: string, order: number, mainWindowOnly: boolean) { + const when = mainWindowOnly ? IsAuxiliaryWindowFocusedContext.toNegated() : ContextKeyExpr.true(); super({ id: `toggle.${section}`, title, @@ -53,13 +51,13 @@ class ToggleConfigAction extends Action2 { registerAction2(class ToggleCommandCenter extends ToggleConfigAction { constructor() { - super(LayoutSettings.COMMAND_CENTER, localize('toggle.commandCenter', 'Command Center'), 1, false, true); + super(LayoutSettings.COMMAND_CENTER, localize('toggle.commandCenter', 'Command Center'), 1, false); } }); registerAction2(class ToggleLayoutControl extends ToggleConfigAction { constructor() { - super('workbench.layoutControl.enabled', localize('toggle.layout', 'Layout Controls'), 2, true, false); + super('workbench.layoutControl.enabled', localize('toggle.layout', 'Layout Controls'), 2, true); } }); @@ -69,8 +67,8 @@ registerAction2(class ToggleCustomTitleBar extends Action2 { id: `toggle.${TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY}`, title: localize('toggle.hideCustomTitleBar', 'Hide Custom Title Bar'), menu: [ - { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.or(ContextKeyExpr.equals(`config.${TitleBarSetting.TITLE_BAR_STYLE}`, TitlebarStyle.NATIVE), ContextKeyExpr.equals(`config.window.nativeTabs`, ContextKeyExpr.true())), group: '3_toggle' }, - { id: MenuId.TitleBarTitleContext, order: 0, when: ContextKeyExpr.or(ContextKeyExpr.equals(`config.${TitleBarSetting.TITLE_BAR_STYLE}`, TitlebarStyle.NATIVE), ContextKeyExpr.equals(`config.window.nativeTabs`, ContextKeyExpr.true())), group: '3_toggle' }, + { id: MenuId.TitleBarContext, order: 0, when: ContextKeyExpr.equals(TitleBarStyleContext.key, TitlebarStyle.NATIVE), group: '3_toggle' }, + { id: MenuId.TitleBarTitleContext, order: 0, when: ContextKeyExpr.equals(TitleBarStyleContext.key, TitlebarStyle.NATIVE), group: '3_toggle' }, ] }); } @@ -99,6 +97,95 @@ registerAction2(class ToggleCustomTitleBarWindowed extends Action2 { } }); + +class ToggleCustomTitleBar extends Action2 { + + constructor() { + super({ + id: `toggle.toggleCustomTitleBar`, + title: localize('toggle.customTitleBar', 'Custom Title Bar'), + toggled: TitleBarVisibleContext, + menu: [ + { id: MenuId.MenubarAppearanceMenu, order: 6, when: ContextKeyExpr.or(ContextKeyExpr.equals(TitleBarStyleContext.key, TitlebarStyle.NATIVE), IsMainWindowFullscreenContext), group: '2_workbench_layout' }, + ] + }); + } + + run(accessor: ServicesAccessor, ...args: any[]): void { + const configService = accessor.get(IConfigurationService); + const contextKeyService = accessor.get(IContextKeyService); + const titleBarVisibility = configService.getValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY); + switch (titleBarVisibility) { + case CustomTitleBarVisibility.NEVER: + configService.updateValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, CustomTitleBarVisibility.AUTO); + break; + case CustomTitleBarVisibility.WINDOWED: { + const isFullScreen = IsMainWindowFullscreenContext.evaluate(contextKeyService.getContext(null)); + if (isFullScreen) { + configService.updateValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, CustomTitleBarVisibility.AUTO); + } else { + configService.updateValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, CustomTitleBarVisibility.NEVER); + } + break; + } + case CustomTitleBarVisibility.AUTO: + default: + configService.updateValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, CustomTitleBarVisibility.NEVER); + break; + } + } +} +registerAction2(ToggleCustomTitleBar); + +registerAction2(class ShowCustomTitleBar extends Action2 { + constructor() { + super({ + id: `showCustomTitleBar`, + title: { value: localize('showCustomTitleBar', 'Show Custom Title Bar'), original: 'Show Custom Title Bar' }, + precondition: TitleBarVisibleContext.negate(), + f1: true + }); + } + + run(accessor: ServicesAccessor, ...args: any[]): void { + const configService = accessor.get(IConfigurationService); + configService.updateValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, CustomTitleBarVisibility.AUTO); + } +}); + + +registerAction2(class HideCustomTitleBar extends Action2 { + constructor() { + super({ + id: `hideCustomTitleBar`, + title: { value: localize('hideCustomTitleBar', 'Hide Custom Title Bar'), original: 'Hide Custom Title Bar' }, + precondition: TitleBarVisibleContext, + f1: true + }); + } + + run(accessor: ServicesAccessor, ...args: any[]): void { + const configService = accessor.get(IConfigurationService); + configService.updateValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, CustomTitleBarVisibility.NEVER); + } +}); + +registerAction2(class HideCustomTitleBar extends Action2 { + constructor() { + super({ + id: `hideCustomTitleBarInFullScreen`, + title: { value: localize('hideCustomTitleBarInFullScreen', 'Hide Custom Title Bar In Full Screen'), original: 'Hide Custom Title Bar In Full Screen' }, + precondition: ContextKeyExpr.and(TitleBarVisibleContext, IsMainWindowFullscreenContext), + f1: true + }); + } + + run(accessor: ServicesAccessor, ...args: any[]): void { + const configService = accessor.get(IConfigurationService); + configService.updateValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, CustomTitleBarVisibility.WINDOWED); + } +}); + registerAction2(class ToggleEditorActions extends Action2 { static readonly settingsID = `workbench.editor.editorActionsLocation`; constructor() { diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 13549aec7d3c9..7e481cf928d85 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -487,15 +487,10 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { // Text Title if (!this.isCommandCenterVisible) { - // Don't show title in custom titlebar when native title is shown - if (!hasNativeTitlebar(this.configurationService, this.titleBarStyle)) { - + this.title.innerText = this.windowTitle.value; + this.titleDisposables.add(this.windowTitle.onDidChange(() => { this.title.innerText = this.windowTitle.value; - this.titleDisposables.add(this.windowTitle.onDidChange(() => { - this.title.innerText = this.windowTitle.value; - })); - - } + })); } // Menu Title @@ -700,7 +695,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { } private get layoutControlEnabled(): boolean { - return !this.isAuxiliary && this.configurationService.getValue(LayoutSettings.LAYOUT_ACTIONS) !== false && !hasNativeTitlebar(this.configurationService, this.titleBarStyle); + return !this.isAuxiliary && this.configurationService.getValue(LayoutSettings.LAYOUT_ACTIONS) !== false; } protected get isCommandCenterVisible() { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index bca80d33bb9dd..43b12a63bab14 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -54,7 +54,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'enum': [EditorActionsLocation.DEFAULT, EditorActionsLocation.TITLEBAR, EditorActionsLocation.HIDDEN], 'enumDescriptions': [ localize({ comment: ['{0} will be a setting name rendered as a link'], key: 'workbench.editor.editorActionsLocation.default' }, "Show editor actions in the window title bar when {0} is set to {1}. Otherwise, editor actions are shown in the editor tab bar.", '`#workbench.editor.showTabs#`', '`none`'), - localize({ comment: ['{0} will be a setting name rendered as a link'], key: 'workbench.editor.editorActionsLocation.titleBar' }, "Show editor actions in the window title bar. If {0} is set to {1}, editor actions are hidden.", '`#window.titleBarStyle#`', '`native`'), + localize({ comment: ['{0} will be a setting name rendered as a link'], key: 'workbench.editor.editorActionsLocation.titleBar' }, "Show editor actions in the window title bar. If {0} is set to {1}, editor actions are hidden.", '`#window.customTitleBarVisibility#`', '`never`'), localize('workbench.editor.editorActionsLocation.hidden', "Editor actions are not shown."), ], 'markdownDescription': localize('editorActionsLocation', "Controls where the editor actions are shown."), @@ -499,7 +499,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'type': 'string', 'enum': ['side', 'top', 'hidden'], 'default': 'side', - 'markdownDescription': localize({ comment: ['This is the description for a setting'], key: 'activityBarLocation' }, "Controls the location of the Activity Bar. It can either show to the `side` or `top` (requires {0} set to {1}) of the Primary Side Bar or `hidden`.", '`#window.titleBarStyle#`', '`custom`'), + 'markdownDescription': localize({ comment: ['This is the description for a setting'], key: 'activityBarLocation' }, "Controls the location of the Activity Bar. It can either show to the `side` or `top` of the Primary Side Bar or `hidden`."), 'enumDescriptions': [ localize('workbench.activityBar.location.side', "Show the Activity Bar to the side of the Primary Side Bar."), localize('workbench.activityBar.location.top', "Show the Activity Bar on top of the Primary Side Bar."), @@ -572,7 +572,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'default': true, 'markdownDescription': isWeb ? localize('layoutControlEnabledWeb', "Controls whether the layout control in the title bar is shown.") : - localize({ key: 'layoutControlEnabled', comment: ['{0}, {1} is a placeholder for a setting identifier.'] }, "Controls whether the layout control is shown in the custom title bar. This setting only has an effect when {0} is set to {1}.", '`#window.titleBarStyle#`', '`custom`') + localize({ key: 'layoutControlEnabled', comment: ['{0}, {1} is a placeholder for a setting identifier.'] }, "Controls whether the layout control is shown in the custom title bar. This setting only has an effect when {0} is not set to {1}.", '`#window.customTitleBarVisibility#`', '`never`') }, 'workbench.layoutControl.type': { 'type': 'string', @@ -648,7 +648,7 @@ const registry = Registry.as(ConfigurationExtensions.Con default: true, markdownDescription: isWeb ? localize('window.commandCenterWeb', "Show command launcher together with the window title.") : - localize({ key: 'window.commandCenter', comment: ['{0}, {1} is a placeholder for a setting identifier.'] }, "Show command launcher together with the window title. This setting only has an effect when {0} is set to {1}.", '`#window.titleBarStyle#`', '`custom`') + localize({ key: 'window.commandCenter', comment: ['{0}, {1} is a placeholder for a setting identifier.'] }, "Show command launcher together with the window title. This setting only has an effect when {0} is not set to {1}.", '`#window.customTitleBarVisibility#`', '`never`') }, 'window.menuBarVisibility': { 'type': 'string', From 2d0b92d89b446713dcb123532b7e1b15cc0e9e56 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 22 Jan 2024 16:46:40 +0100 Subject: [PATCH 198/333] some `ensureNoDisposablesAreLeakedInTestSuite` (#203026) * some `ensureNoDisposablesAreLeakedInTestSuite` https://github.com/microsoft/vscode/issues/200091 * update eslint file --- .eslintrc.json | 2 -- .../contrib/snippet/test/browser/snippetParser.test.ts | 3 +++ .../contrib/snippet/test/browser/snippetVariables.test.ts | 7 ++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 733bcd3e03670..e9670fc9c83c0 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -155,9 +155,7 @@ "src/vs/editor/contrib/codeAction/test/browser/codeActionModel.test.ts", "src/vs/editor/contrib/gotoSymbol/test/browser/referencesModel.test.ts", "src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts", - "src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts", "src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts", - "src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts", "src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts", "src/vs/editor/contrib/suggest/test/browser/suggestMemory.test.ts", "src/vs/editor/test/common/services/languageService.test.ts", diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts index bc1e224b187c2..625efaf846e3a 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts @@ -3,10 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { Choice, FormatString, Marker, Placeholder, Scanner, SnippetParser, Text, TextmateSnippet, TokenType, Transform, Variable } from 'vs/editor/contrib/snippet/browser/snippetParser'; suite('SnippetParser', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('Scanner', () => { const scanner = new Scanner(); diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts index 8ab633ed81a89..53a9b272757af 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts @@ -10,6 +10,7 @@ import { isWindows } from 'vs/base/common/platform'; import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { Selection } from 'vs/editor/common/core/selection'; import { TextModel } from 'vs/editor/common/model/textModel'; import { SnippetParser, Variable, VariableResolver } from 'vs/editor/contrib/snippet/browser/snippetParser'; @@ -22,6 +23,7 @@ import { toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces'; suite('Snippet Variables Resolver', function () { + const labelService = new class extends mock() { override getUriLabel(uri: URI) { return uri.fsPath; @@ -48,6 +50,9 @@ suite('Snippet Variables Resolver', function () { model.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + + function assertVariableResolve(resolver: VariableResolver, varName: string, expected?: string) { const snippet = new SnippetParser().parse(`$${varName}`); const variable = snippet.children[0]; @@ -270,7 +275,7 @@ suite('Snippet Variables Resolver', function () { assertVariableResolve(new ClipboardBasedVariableResolver(() => 'foo', 1, 0, true), 'cLIPBOARD', undefined); }); - test('Add variable to insert value from clipboard to a snippet #40153', function () { + test('Add variable to insert value from clipboard to a snippet #40153, 2', function () { assertVariableResolve(new ClipboardBasedVariableResolver(() => 'line1', 1, 2, true), 'CLIPBOARD', 'line1'); assertVariableResolve(new ClipboardBasedVariableResolver(() => 'line1\nline2\nline3', 1, 2, true), 'CLIPBOARD', 'line1\nline2\nline3'); From 9555d9dfff5e312fd2af6ea6599b23d220a82968 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 22 Jan 2024 12:54:35 -0300 Subject: [PATCH 199/333] Properly deserialize these fields (#203029) --- src/vs/workbench/contrib/chat/common/chatModel.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 8bec2aa5ec7be..235c547b2e46b 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -546,11 +546,11 @@ export class ChatModel extends Disposable implements IChatModel { revive(raw.agent) : undefined; request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, raw.slashCommand, request.id, true, raw.isCanceled, raw.vote, raw.responseErrorDetails, raw.followups); if (raw.usedContext) { // @ulugbekna: if this's a new vscode sessions, doc versions are incorrect anyway? - request.response.applyReference(raw.usedContext); + request.response.applyReference(revive(raw.usedContext)); } if (raw.contentReferences) { - raw.contentReferences.forEach(r => request.response!.applyReference(r)); + raw.contentReferences.forEach(r => request.response!.applyReference(revive(r))); } } return request; From ecb6dd684e9cef2e4c4cd57d6ee40de7d0755838 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 22 Jan 2024 16:57:01 +0100 Subject: [PATCH 200/333] debt - silence CodeQL warnings (#203031) --- src/vs/platform/backup/electron-main/backupMainService.ts | 2 +- src/vs/platform/workspaces/node/workspaces.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index b818207243906..fd7b1ae2350b8 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -410,6 +410,6 @@ export class BackupMainService implements IBackupMainService { key = folderUri.toString().toLowerCase(); } - return createHash('md5').update(key).digest('hex'); + return createHash('md5').update(key).digest('hex'); // CodeQL [SM04514] Using MD5 to convert a file path to a fixed length } } diff --git a/src/vs/platform/workspaces/node/workspaces.ts b/src/vs/platform/workspaces/node/workspaces.ts index beffbbe27765c..86e7f3ebeee27 100644 --- a/src/vs/platform/workspaces/node/workspaces.ts +++ b/src/vs/platform/workspaces/node/workspaces.ts @@ -29,7 +29,7 @@ export function getWorkspaceIdentifier(configPath: URI): IWorkspaceIdentifier { configPathStr = configPathStr.toLowerCase(); // sanitize for platform file system } - return createHash('md5').update(configPathStr).digest('hex'); + return createHash('md5').update(configPathStr).digest('hex'); // CodeQL [SM04514] Using MD5 to convert a file path to a fixed length } return { @@ -50,7 +50,7 @@ export function getSingleFolderWorkspaceIdentifier(folderUri: URI, folderStat?: // Remote: produce a hash from the entire URI if (folderUri.scheme !== Schemas.file) { - return createHash('md5').update(folderUri.toString()).digest('hex'); + return createHash('md5').update(folderUri.toString()).digest('hex'); // CodeQL [SM04514] Using MD5 to convert a file path to a fixed length } // Local: we use the ctime as extra salt to the @@ -77,7 +77,7 @@ export function getSingleFolderWorkspaceIdentifier(folderUri: URI, folderStat?: } } - return createHash('md5').update(folderUri.fsPath).update(ctime ? String(ctime) : '').digest('hex'); + return createHash('md5').update(folderUri.fsPath).update(ctime ? String(ctime) : '').digest('hex'); // CodeQL [SM04514] Using MD5 to convert a file path to a fixed length } const folderId = getFolderId(); From 2a9a0a06f8c9cf687056d4d2634c942481c8c0dd Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 22 Jan 2024 06:17:58 -1000 Subject: [PATCH 201/333] end: use a better hash for built-in cache key (#203033) For CodeQL --- build/azure-pipelines/common/computeBuiltInDepsCacheKey.js | 2 +- build/azure-pipelines/common/computeBuiltInDepsCacheKey.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/common/computeBuiltInDepsCacheKey.js b/build/azure-pipelines/common/computeBuiltInDepsCacheKey.js index fa230bb684935..2d747f56cc736 100644 --- a/build/azure-pipelines/common/computeBuiltInDepsCacheKey.js +++ b/build/azure-pipelines/common/computeBuiltInDepsCacheKey.js @@ -8,7 +8,7 @@ const fs = require("fs"); const path = require("path"); const crypto = require("crypto"); const productjson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../../product.json'), 'utf8')); -const shasum = crypto.createHash('sha1'); +const shasum = crypto.createHash('sha256'); for (const ext of productjson.builtInExtensions) { shasum.update(`${ext.name}@${ext.version}`); } diff --git a/build/azure-pipelines/common/computeBuiltInDepsCacheKey.ts b/build/azure-pipelines/common/computeBuiltInDepsCacheKey.ts index f0554361607b0..53d6c501ea9a6 100644 --- a/build/azure-pipelines/common/computeBuiltInDepsCacheKey.ts +++ b/build/azure-pipelines/common/computeBuiltInDepsCacheKey.ts @@ -8,7 +8,7 @@ import * as path from 'path'; import * as crypto from 'crypto'; const productjson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../../product.json'), 'utf8')); -const shasum = crypto.createHash('sha1'); +const shasum = crypto.createHash('sha256'); for (const ext of productjson.builtInExtensions) { shasum.update(`${ext.name}@${ext.version}`); From 4c00663055759e03f6459f3ecc537c7d79c86c89 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 22 Jan 2024 17:20:13 +0100 Subject: [PATCH 202/333] supress CodeQL warning (#203032) --- src/vs/platform/languagePacks/node/languagePacks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/languagePacks/node/languagePacks.ts b/src/vs/platform/languagePacks/node/languagePacks.ts index d0d56e578235a..3a7df771bc2cc 100644 --- a/src/vs/platform/languagePacks/node/languagePacks.ts +++ b/src/vs/platform/languagePacks/node/languagePacks.ts @@ -169,7 +169,7 @@ class LanguagePacksCache extends Disposable { private updateHash(languagePack: ILanguagePack): void { if (languagePack) { - const md5 = createHash('md5'); + const md5 = createHash('md5'); // CodeQL [SM04514] Used to create an hash for language pack extension version, which is not a security issue for (const extension of languagePack.extensions) { md5.update(extension.extensionIdentifier.uuid || extension.extensionIdentifier.id).update(extension.version); // CodeQL [SM01510] The extension UUID is not sensitive info and is not manually created by a user } From 74411a0543a7e54b982524bdf2d4f110c1d904e4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 22 Jan 2024 08:29:33 -0800 Subject: [PATCH 203/333] use basename --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 6df202d84349a..b691ff6b461f3 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -26,6 +26,7 @@ import { ResourceMap } from 'vs/base/common/map'; import { marked } from 'vs/base/common/marked/marked'; import { FileAccess } from 'vs/base/common/network'; import { clamp } from 'vs/base/common/numbers'; +import { basename } from 'vs/base/common/path'; import { ThemeIcon } from 'vs/base/common/themables'; import { URI } from 'vs/base/common/uri'; import { IMarkdownRenderResult, MarkdownRenderer } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer'; @@ -1165,7 +1166,7 @@ class ContentReferencesListPool extends Disposable { { alwaysConsumeMouseWheel: false, accessibilityProvider: { - getAriaLabel: (element: IChatResponseProgressFileTreeData) => element.label, + getAriaLabel: (element: IChatResponseProgressFileTreeData) => basename(element.uri.path), getWidgetAriaLabel: () => localize('usedReferences', "Used References") }, }); From f6bf4c92c03ecf84266729f8d2c8aa59a8e1fa30 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Mon, 22 Jan 2024 17:37:00 +0100 Subject: [PATCH 204/333] toggle tree sticky scroll --- .../workbench/browser/actions/listCommands.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index ff6e3cbf3d4e8..1c84f2f9f3e5b 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -19,6 +19,8 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { Table } from 'vs/base/browser/ui/table/tableWidget'; import { AbstractTree, TreeFindMatchType, TreeFindMode } from 'vs/base/browser/ui/tree/abstractTree'; import { isActiveElement } from 'vs/base/browser/dom'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; function ensureDOMFocus(widget: ListWidget | undefined): void { // it can happen that one of the commands is executed while @@ -904,3 +906,20 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ focused.scrollLeft += 10; } }); + +registerAction2(class ToggleStickyScroll extends Action2 { + constructor() { + super({ + id: 'tree.toggleStickyScroll', + title: { value: 'Toggle Tree Sticky Scroll', original: 'Toggle Tree Sticky Scroll' }, + category: 'View', + f1: true + }); + } + + run(accessor: ServicesAccessor) { + const configurationService = accessor.get(IConfigurationService); + const newValue = !configurationService.getValue('workbench.tree.enableStickyScroll'); + configurationService.updateValue('workbench.tree.enableStickyScroll', newValue); + } +}); From 17fe94ae2a73cff6b1e38c91a56adb551dbb7a27 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 22 Jan 2024 09:14:36 -0800 Subject: [PATCH 205/333] fix issue --- .../contrib/chat/browser/chatListRenderer.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index b691ff6b461f3..8804cf94e303f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -1166,7 +1166,16 @@ class ContentReferencesListPool extends Disposable { { alwaysConsumeMouseWheel: false, accessibilityProvider: { - getAriaLabel: (element: IChatResponseProgressFileTreeData) => basename(element.uri.path), + getAriaLabel: (element: IChatContentReference) => { + if (URI.isUri(element.reference)) { + console.log(basename(element.reference.path)); + return basename(element.reference.path); + } else { + console.log(basename((element.reference.uri.path))); + return basename(element.reference.uri.path); + } + }, + getWidgetAriaLabel: () => localize('usedReferences', "Used References") }, }); From 27ab324c06617d20e30e6e99b9e998592e3f22c4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 22 Jan 2024 09:17:23 -0800 Subject: [PATCH 206/333] rm console.logs --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 8804cf94e303f..9e6ec93e6ce96 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -1168,10 +1168,8 @@ class ContentReferencesListPool extends Disposable { accessibilityProvider: { getAriaLabel: (element: IChatContentReference) => { if (URI.isUri(element.reference)) { - console.log(basename(element.reference.path)); return basename(element.reference.path); } else { - console.log(basename((element.reference.uri.path))); return basename(element.reference.uri.path); } }, From 5efaf9b360b9ca8df50eefc9074265e312eb34ab Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 22 Jan 2024 09:17:48 -0800 Subject: [PATCH 207/333] fix issue --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 9e6ec93e6ce96..e14185c7444ee 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -1230,8 +1230,6 @@ class ContentReferencesListRenderer implements IListRenderer Date: Mon, 22 Jan 2024 09:18:39 -0800 Subject: [PATCH 208/333] revert change --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index e14185c7444ee..6c9cc49c006f4 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -775,7 +775,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { if (e.element) { this.editorService.openEditor({ From de360da900172c91e05497d6fb45c8d0862344cd Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 22 Jan 2024 09:19:11 -0800 Subject: [PATCH 209/333] revert change --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 6c9cc49c006f4..e14185c7444ee 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -775,6 +775,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { if (e.element) { this.editorService.openEditor({ From a073a416552ac1ea2e4ae20bd21079aec05957f2 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 22 Jan 2024 09:19:45 -0800 Subject: [PATCH 210/333] undo move --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index e14185c7444ee..7d5eaf14e4a5a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -774,8 +774,8 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { if (e.element) { this.editorService.openEditor({ From 67ae7ddf0ec98929d4a8daaefc6bdec14afb8516 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 22 Jan 2024 19:01:13 +0100 Subject: [PATCH 211/333] fix #203038 (#203046) --- .../workbench/contrib/extensions/browser/extensionsActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 8b4d951ed4e42..47def38fec06e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -880,7 +880,7 @@ export class ToggleAutoUpdateForExtensionAction extends ExtensionAction { if (this.extension.isBuiltin) { return; } - if (this.enableWhenOutdated && !this.extension.outdated) { + if (this.enableWhenOutdated && (this.extension.state !== ExtensionState.Installed || !this.extension.outdated)) { return; } if (!this.enableWhenAutoUpdateValue.includes(this.extensionsWorkbenchService.getAutoUpdateValue())) { From 603bc41a0731822f92dc4110238be71d81d93583 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 22 Jan 2024 18:13:12 +0100 Subject: [PATCH 212/333] Fixes diff editor soft assert --- .../widget/diffEditor/diffEditorViewModel.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts index b9a3880f0d90f..bd065a313f78b 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts @@ -170,15 +170,20 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo .map(id => model.modified.getDecorationRange(id)) .map(r => r ? LineRange.fromRangeInclusive(r) : undefined); const updatedLastUnchangedRegions = filterWithPrevious( - lastUnchangedRegions.regions.map((r, idx) => - (!lastUnchangedRegionsOrigRanges[idx] || !lastUnchangedRegionsModRanges[idx]) ? undefined : - new UnchangedRegion( + lastUnchangedRegions.regions + .map((r, idx) => { + if (!lastUnchangedRegionsOrigRanges[idx] || !lastUnchangedRegionsModRanges[idx]) { return undefined; } + const length = lastUnchangedRegionsOrigRanges[idx]!.length; + return new UnchangedRegion( lastUnchangedRegionsOrigRanges[idx]!.startLineNumber, lastUnchangedRegionsModRanges[idx]!.startLineNumber, - lastUnchangedRegionsOrigRanges[idx]!.length, - r.visibleLineCountTop.get(), - r.visibleLineCountBottom.get(), - )).filter(isDefined), + length, + // The visible area can shrink by edits -> we have to account for this + Math.min(r.visibleLineCountTop.get(), length), + Math.min(r.visibleLineCountBottom.get(), length - r.visibleLineCountTop.get()), + ); + } + ).filter(isDefined), (cur, prev) => !prev || (cur.modifiedLineNumber >= prev.modifiedLineNumber + prev.lineCount && cur.originalLineNumber >= prev.originalLineNumber + prev.lineCount) ); From 74f4ad53b8e0e5ad3f662f34fde1d34a8238b710 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 22 Jan 2024 19:24:18 +0100 Subject: [PATCH 213/333] donot show switch action when not needed (#203043) --- .../browser/extensions.contribution.ts | 12 +++++------ .../extensions/browser/extensionsActions.ts | 20 ++++++++++++++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 38a576546dc66..31972316d6521 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -1302,7 +1302,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi id: MenuId.ExtensionContext, group: INSTALL_ACTIONS_GROUP, order: 0, - when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.not('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension')) + when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.not('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension')) }, run: async (accessor: ServicesAccessor, extensionId: string) => { const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); @@ -1318,7 +1318,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi id: MenuId.ExtensionContext, group: INSTALL_ACTIONS_GROUP, order: 1, - when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.has('extensionHasReleaseVersion'), ContextKeyExpr.has('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension')) + when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('extensionHasReleaseVersion'), ContextKeyExpr.has('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension')) }, run: async (accessor: ServicesAccessor, extensionId: string) => { const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); @@ -1372,14 +1372,14 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi }); this.registerExtensionAction({ - id: 'workbench.extensions.action.enablePreRlease', + id: 'workbench.extensions.action.switchToPreRlease', title: localize('enablePreRleaseLabel', "Switch to Pre-Release Version"), category: ExtensionsLocalizedLabel, menu: { id: MenuId.ExtensionContext, group: INSTALL_ACTIONS_GROUP, order: 2, - when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.not('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) + when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.not('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) }, run: async (accessor: ServicesAccessor, id: string) => { const instantiationService = accessor.get(IInstantiationService); @@ -1394,14 +1394,14 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi }); this.registerExtensionAction({ - id: 'workbench.extensions.action.disablPreRlease', + id: 'workbench.extensions.action.switchToRelease', title: localize('disablePreRleaseLabel', "Switch to Release Version"), category: ExtensionsLocalizedLabel, menu: { id: MenuId.ExtensionContext, group: INSTALL_ACTIONS_GROUP, order: 2, - when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.has('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) + when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) }, run: async (accessor: ServicesAccessor, id: string) => { const instantiationService = accessor.get(IInstantiationService); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 47def38fec06e..eed284ef570d7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1087,7 +1087,7 @@ async function getContextMenuActionsGroups(extension: IExtension | undefined | n cksOverlay.push(['installedExtensionIsPreReleaseVersion', !!extension.local?.isPreReleaseVersion]); cksOverlay.push(['installedExtensionIsOptedToPreRelease', !!extension.local?.preRelease]); cksOverlay.push(['galleryExtensionIsPreReleaseVersion', !!extension.gallery?.properties.isPreReleaseVersion]); - cksOverlay.push(['extensionHasPreReleaseVersion', extension.hasPreReleaseVersion]); + cksOverlay.push(['galleryExtensionHasPreReleaseVersion', extension.gallery?.hasPreReleaseVersion]); cksOverlay.push(['extensionHasReleaseVersion', extension.hasReleaseVersion]); const [colorThemes, fileIconThemes, productIconThemes] = await Promise.all([workbenchThemeService.getColorThemes(), workbenchThemeService.getFileIconThemes(), workbenchThemeService.getProductIconThemes()]); @@ -1300,12 +1300,22 @@ export class TogglePreReleaseExtensionAction extends ExtensionAction { if (!this.extension.gallery) { return; } + if (this.extension.preRelease && !this.extension.isPreReleaseVersion) { + return; + } + if (!this.extension.preRelease && !this.extension.gallery.hasPreReleaseVersion) { + return; + } this.enabled = true; this.class = TogglePreReleaseExtensionAction.EnabledClass; - this.label = this.extension.preRelease ? localize('togglePreRleaseDisableLabel', "Switch to Release Version") : localize('togglePreRleaseEnableLabel', "Switch to Pre-Release Version"); - this.tooltip = this.extension.preRelease - ? localize('togglePreRleaseDisableTooltip1', "This will switch and enable updates to release versions") - : localize('togglePreRleaseEnableTooltip', "This will switch to pre-release version and enable updates to latest version always"); + + if (this.extension.preRelease) { + this.label = localize('togglePreRleaseDisableLabel', "Switch to Release Version"); + this.tooltip = localize('togglePreRleaseDisableTooltip', "This will switch and enable updates to release versions"); + } else { + this.label = localize('switchToPreReleaseLabel', "Switch to Pre-Release Version"); + this.tooltip = localize('switchToPreReleaseTooltip', "This will switch to pre-release version and enable updates to latest version always"); + } } override async run(): Promise { From df48846f267a2fd693a6e10dd1b3af40e0537fb2 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Mon, 22 Jan 2024 10:25:15 -0800 Subject: [PATCH 214/333] Don't show account entitlements for vscode.dev (#203051) --- .../browser/accountsEntitlements.contribution.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/accountEntitlements/browser/accountsEntitlements.contribution.ts b/src/vs/workbench/contrib/accountEntitlements/browser/accountsEntitlements.contribution.ts index 8ce4726c5f38c..ecc7d689e2778 100644 --- a/src/vs/workbench/contrib/accountEntitlements/browser/accountsEntitlements.contribution.ts +++ b/src/vs/workbench/contrib/accountEntitlements/browser/accountsEntitlements.contribution.ts @@ -26,6 +26,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IRequestService, asText } from 'vs/platform/request/common/request'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { isWeb } from 'vs/base/common/platform'; const configurationKey = 'workbench.accounts.experimental.showEntitlements'; @@ -61,7 +62,7 @@ class AccountsEntitlement extends Disposable implements IWorkbenchContribution { ) { super(); - if (!this.productService.gitHubEntitlement) { + if (!this.productService.gitHubEntitlement || isWeb) { return; } From bdf1f049d3130cc332d4fcc557788fc99174da84 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 22 Jan 2024 10:58:57 -0800 Subject: [PATCH 215/333] publish setting for the notebook variables view (#203056) * publish experimental setting * grammar --- .../browser/contrib/notebookVariables/notebookVariables.ts | 3 ++- .../contrib/notebook/browser/notebook.contribution.ts | 5 +++++ src/vs/workbench/contrib/notebook/common/notebookCommon.ts | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts index 524f868562689..962f144cc7061 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables.ts @@ -17,6 +17,7 @@ import { variablesViewIcon } from 'vs/workbench/contrib/notebook/browser/noteboo import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; +import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export class NotebookVariables extends Disposable implements IWorkbenchContribution { @@ -34,7 +35,7 @@ export class NotebookVariables extends Disposable implements IWorkbenchContribut } private handleInitEvent(configurationService: IConfigurationService) { - if (configurationService.getValue('notebook.experimental.notebookVariablesView') + if (configurationService.getValue(NotebookSetting.notebookVariablesView) && this.editorService.activeEditorPane?.getId() === 'workbench.editor.notebook') { if (this.initializeView()) { this.listeners.forEach(listener => listener.dispose()); diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index b0c8c072e62a1..6a7b115bb627a 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -1064,6 +1064,11 @@ configurationRegistry.registerConfiguration({ markdownDescription: nls.localize('notebook.cellChat', "Enable experimental cell chat for notebooks."), type: 'boolean', default: false + }, + [NotebookSetting.notebookVariablesView]: { + markdownDescription: nls.localize('notebook.VariablesView.description', "Enable the experimental notebook variables view within the debug panel."), + type: 'boolean', + default: false } } }); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index a4d44cdf61e3a..d24e108e6d377 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -946,7 +946,8 @@ export const NotebookSetting = { gotoSymbolsAllSymbols: 'notebook.gotoSymbols.showAllSymbols', scrollToRevealCell: 'notebook.scrolling.revealNextCellOnExecute', anchorToFocusedCell: 'notebook.scrolling.experimental.anchorToFocusedCell', - cellChat: 'notebook.experimental.cellChat' + cellChat: 'notebook.experimental.cellChat', + notebookVariablesView: 'notebook.experimental.variablesView' } as const; export const enum CellStatusbarAlignment { From 80750ef3ebafd869c49a269b3aa0967c871adffd Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:00:31 -0800 Subject: [PATCH 216/333] chore: bump electron/get to 2.0.3 (#203055) chore: bump electron/get to ^2.0.0 --- build/package.json | 2 +- build/yarn.lock | 283 ++++++++++++++++++++------------------------- 2 files changed, 124 insertions(+), 161 deletions(-) diff --git a/build/package.json b/build/package.json index a06baecbdeb63..52b1f47a5ebc1 100644 --- a/build/package.json +++ b/build/package.json @@ -6,7 +6,7 @@ "@azure/cosmos": "^3", "@azure/identity": "^3.4.1", "@azure/storage-blob": "^12.17.0", - "@electron/get": "^1.12.4", + "@electron/get": "^2.0.0", "@types/ansi-colors": "^3.2.0", "@types/byline": "^4.2.32", "@types/cssnano": "^4.0.0", diff --git a/build/yarn.lock b/build/yarn.lock index c7d9bb3a37b2a..51ca8c276d0ac 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -223,21 +223,20 @@ events "^3.0.0" tslib "^2.2.0" -"@electron/get@^1.12.4": - version "1.12.4" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.12.4.tgz#a5971113fc1bf8fa12a8789dc20152a7359f06ab" - integrity sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg== +"@electron/get@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@electron/get/-/get-2.0.3.tgz#fba552683d387aebd9f3fcadbcafc8e12ee4f960" + integrity sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ== dependencies: debug "^4.1.1" env-paths "^2.2.0" fs-extra "^8.1.0" - got "^9.6.0" + got "^11.8.5" progress "^2.0.3" semver "^6.2.0" sumchecker "^3.0.1" optionalDependencies: - global-agent "^2.0.2" - global-tunnel-ng "^2.7.1" + global-agent "^3.0.0" "@esbuild/android-arm64@0.17.14": version "0.17.14" @@ -361,17 +360,17 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.0.3.tgz#13a12ae9e05c2a782f7b5e84c3cbfda4225eaf80" integrity sha512-puWxACExDe9nxbBB3lOymQFrLYml2dVOrd7USiVRnSbgXE+KwBu+HxFvxrzfqsiSda9IWsXJG1ef7C1O2/GmKQ== -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@sindresorhus/is@^4.0.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== dependencies: - defer-to-connect "^1.0.1" + defer-to-connect "^2.0.0" "@tootallnate/once@1": version "1.1.2" @@ -390,6 +389,16 @@ dependencies: "@types/node" "*" +"@types/cacheable-request@^6.0.1": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" + integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "^3.1.4" + "@types/node" "*" + "@types/responselike" "^1.0.0" + "@types/chokidar@*": version "1.7.5" resolved "https://registry.yarnpkg.com/@types/chokidar/-/chokidar-1.7.5.tgz#1fa78c8803e035bed6d98e6949e514b133b0c9b6" @@ -513,11 +522,23 @@ "@types/undertaker" "*" "@types/vinyl-fs" "*" +"@types/http-cache-semantics@*": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== + "@types/js-beautify@*": version "1.8.0" resolved "https://registry.yarnpkg.com/@types/js-beautify/-/js-beautify-1.8.0.tgz#0369d3d0e1f35a6aec07cb4da2ee2bcda111367c" integrity sha512-/siF86XrwDKLuHe8l7h6NhrAWgLdgqbxmjZv9NvGWmgYRZoTipkjKiWb0SQHy/jcR+ee0GvbG6uGd+LEBMGNvA== +"@types/keyv@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== + dependencies: + "@types/node" "*" + "@types/mime@0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-0.0.29.tgz#fbcfd330573b912ef59eeee14602bface630754b" @@ -577,6 +598,13 @@ dependencies: "@types/node" "*" +"@types/responselike@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" + integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== + dependencies: + "@types/node" "*" + "@types/rimraf@^2.0.4": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.4.tgz#403887b0b53c6100a6c35d2ab24f6ccc042fec46" @@ -895,18 +923,23 @@ byline@^5.0.0: resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-request@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" + integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== dependencies: clone-response "^1.0.2" get-stream "^5.1.0" http-cache-semantics "^4.0.0" - keyv "^3.0.0" + keyv "^4.0.0" lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" + normalize-url "^6.0.1" + responselike "^2.0.0" call-bind@^1.0.0: version "1.0.2" @@ -1092,19 +1125,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -config-chain@^1.1.11: - version "1.1.13" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" - integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -core-js@^3.6.5: - version "3.15.2" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61" - integrity sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q== - core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -1149,13 +1169,6 @@ debug@^2.6.8: dependencies: ms "2.0.0" -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" - decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" @@ -1168,10 +1181,10 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== define-lazy-prop@^2.0.0: version "2.0.0" @@ -1240,11 +1253,6 @@ domutils@^3.0.1: domelementtype "^2.3.0" domhandler "^5.0.1" -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - duplexify@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0" @@ -1274,11 +1282,6 @@ electron-osx-sign@^0.4.16: minimist "^1.2.0" plist "^3.0.1" -encodeurl@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -1480,13 +1483,6 @@ get-intrinsic@^1.0.2: has "^1.0.3" has-symbols "^1.0.3" -get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" @@ -1530,29 +1526,18 @@ glob@^7.1.3, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -global-agent@^2.0.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.2.0.tgz#566331b0646e6bf79429a16877685c4a1fbf76dc" - integrity sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg== +global-agent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6" + integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q== dependencies: boolean "^3.0.1" - core-js "^3.6.5" es6-error "^4.1.1" matcher "^3.0.0" roarr "^2.15.3" semver "^7.3.2" serialize-error "^7.0.1" -global-tunnel-ng@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f" - integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg== - dependencies: - encodeurl "^1.0.2" - lodash "^4.17.10" - npm-conf "^1.1.3" - tunnel "^0.0.6" - globalthis@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" @@ -1560,22 +1545,22 @@ globalthis@^1.0.1: dependencies: define-properties "^1.1.3" -got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" +got@^11.8.5: + version "11.8.6" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" + integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" graceful-fs@^4.1.2: version "4.2.10" @@ -1668,6 +1653,14 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -1694,7 +1687,7 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.4, ini@~1.3.0: +ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -1788,10 +1781,10 @@ jsbi@^3.1.3: resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.1.4.tgz#9654dd02207a66a4911b4e4bb74265bc2cbc9dd0" integrity sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg== -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== json-stringify-safe@^5.0.1: version "5.0.1" @@ -1876,12 +1869,12 @@ keytar@^7.7.0: node-addon-api "^4.3.0" prebuild-install "^7.0.1" -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== +keyv@^4.0.0: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: - json-buffer "3.0.0" + json-buffer "3.0.1" leven@^3.1.0: version "3.1.0" @@ -1920,16 +1913,11 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" -lodash@^4.17.10, lodash@^4.17.21: +lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - lowercase-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" @@ -1987,7 +1975,7 @@ mime@^1.3.4, mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mimic-response@^1.0.0, mimic-response@^1.0.1: +mimic-response@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== @@ -2090,18 +2078,10 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^4.1.0: - version "4.5.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" - integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== - -npm-conf@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== nth-check@^2.0.1: version "2.1.1" @@ -2141,10 +2121,10 @@ open@^8.0.0: is-docker "^2.1.1" is-wsl "^2.2.0" -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== parse-node-version@^1.0.0: version "1.0.1" @@ -2203,11 +2183,6 @@ pify@^2.3.0: resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - plist@^3.0.1: version "3.0.5" resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987" @@ -2253,11 +2228,6 @@ prebuild-install@^7.0.1, prebuild-install@^7.1.1: tar-fs "^2.0.0" tunnel-agent "^0.6.0" -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - priorityqueuejs@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/priorityqueuejs/-/priorityqueuejs-1.0.0.tgz#2ee4f23c2560913e08c07ce5ccdd6de3df2c5af8" @@ -2278,11 +2248,6 @@ progress@^2.0.3: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -2298,6 +2263,11 @@ qs@^6.9.1: dependencies: side-channel "^1.0.4" +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -2354,12 +2324,17 @@ replace-ext@^1.0.0: resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= +resolve-alpn@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== dependencies: - lowercase-keys "^1.0.0" + lowercase-keys "^2.0.0" rimraf@^3.0.0: version "3.0.2" @@ -2616,11 +2591,6 @@ tmp@^0.2.1: dependencies: rimraf "^3.0.0" -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -2724,13 +2694,6 @@ url-join@^4.0.1: resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" From 39fe2dab670354239b4bb457dbbf4dfdaa14669d Mon Sep 17 00:00:00 2001 From: Robo Date: Tue, 23 Jan 2024 04:25:03 +0900 Subject: [PATCH 217/333] chore: bump electron@27.2.3 (#202771) * chore: bump electron@27.2.3 * chore: bump distro --- .yarnrc | 4 +- build/checksums/electron.txt | 150 +++++++++++++++++------------------ cgmanifest.json | 4 +- package.json | 4 +- remote/.yarnrc | 2 +- yarn.lock | 8 +- 6 files changed, 86 insertions(+), 86 deletions(-) diff --git a/.yarnrc b/.yarnrc index 1614ee06ded12..12a0b977bd9fe 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,5 +1,5 @@ disturl "https://electronjs.org/headers" -target "27.2.1" -ms_build_id "26149897" +target "27.2.3" +ms_build_id "26378995" runtime "electron" build_from_source "true" diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index d5d99bdf9241f..bcd4bfe0da6d3 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -1,75 +1,75 @@ -fa257474711c65fe0f929299fc21a7171a548ef655e393246a48319ee53f702e *chromedriver-v27.2.1-darwin-arm64.zip -d3f279d281221ca87510716afda22be95f8ad66641b3d46943885de0ae94b046 *chromedriver-v27.2.1-darwin-x64.zip -fc0c512cb87c82b1d5327bb04ef7dfb2046ea884d00896bab89652d607ce1c97 *chromedriver-v27.2.1-linux-arm64.zip -4c33d1a323bc59d776a14b2ebb40229dbf1b8de30d51e5643e6584bd63cd7593 *chromedriver-v27.2.1-linux-armv7l.zip -a60c7fc2f278b7a177eb1ec3dccff6fc9c1c8d120596deb595b544a2b8018a79 *chromedriver-v27.2.1-linux-x64.zip -80bc0e0ce66028436853bb5a625f8a1c6c8456c1a8435070a6e30d0e06b9312b *chromedriver-v27.2.1-mas-arm64.zip -67fcfafbc24902d1dff455527115f4453d49308d95010214dda7a1e46a96e6e7 *chromedriver-v27.2.1-mas-x64.zip -1c33c6cadb07c62e7a80ae64de520cb3122cc8496ee73533da67770d0ab1bf5c *chromedriver-v27.2.1-win32-arm64.zip -666042a40a30ab322a66bbcf8552f777c0564bb4a7647122eb7bd260269cd1c3 *chromedriver-v27.2.1-win32-ia32.zip -af92d72b04760be18ac748c5a0072751b497cff4ebabb4bfeecc23538ea9de8e *chromedriver-v27.2.1-win32-x64.zip -9f626747797f005cfe41076bc905a11ab17ac380a2d7865c3cfba05c63ea6514 *electron-api.json -a1ea4e720b5ce964bb4aa6f723f0305ec81a0024f8e9fe474fb1b05b79402e51 *electron-v27.2.1-darwin-arm64-dsym-snapshot.zip -60f69390ca803845992dae4aecb0d360c8c3da62c842523f307a8d495bec84e7 *electron-v27.2.1-darwin-arm64-dsym.zip -33ed9025a4ac39ec597c6cdcc5f7c425a34aca4e34713732de159685a7884d52 *electron-v27.2.1-darwin-arm64-symbols.zip -a8d12908b5aad53083e1b257ba8028ea55ac099cab859260c2a8b5ce512597ca *electron-v27.2.1-darwin-arm64.zip -68b5e5a021239fc7849986a24dddb54d21f4cc9ffbceb8777b7bfd696c940c50 *electron-v27.2.1-darwin-x64-dsym-snapshot.zip -ed33f31e69925642c3b09b2c318ec66be8877bdfb4984c91ee3b90052fb1bcac *electron-v27.2.1-darwin-x64-dsym.zip -88f7b0fec77ee4e0ae9659cfee4d9cbe3a26b378cc92878236b928254568fb07 *electron-v27.2.1-darwin-x64-symbols.zip -7ea6d061520afa77483b891bb8623574d87458deb54841f740c51a1adfa88fea *electron-v27.2.1-darwin-x64.zip -e4af2089beecdcdb7625918ce1dfd6b3401b10239c1c0435951a8642ff17568f *electron-v27.2.1-linux-arm64-debug.zip -c9b009ce848f02fc31c072b546758fbccb39a6ff2779dfea794920c1a83f8f8d *electron-v27.2.1-linux-arm64-symbols.zip -32c86c89a4fdc55aac107c8807bf3181fc31cf5c6dc4e0ca00319bb7837ed695 *electron-v27.2.1-linux-arm64.zip -e4af2089beecdcdb7625918ce1dfd6b3401b10239c1c0435951a8642ff17568f *electron-v27.2.1-linux-armv7l-debug.zip -ceb3f9e7b19f8e1ca0e94858492b1b5eb8bb3c7e91ebde230bdbe463838f2a0a *electron-v27.2.1-linux-armv7l-symbols.zip -b4496ae943fa8ee19d418f9f871ce05ad2ffab35b5dfc79052c842478464b5d6 *electron-v27.2.1-linux-armv7l.zip -35bdcb99f7f63a6892dde033046b4eb315ca71a459124461b43f9ffcf9cae579 *electron-v27.2.1-linux-x64-debug.zip -5271540e93fbdb1f69f21a37a3deb51886afaf7e1b9200ed0cbb6dc91f83854c *electron-v27.2.1-linux-x64-symbols.zip -b4be96cd85797d23afd422dc7cc6bf321234001810d08b52e32b055a7e8afbd7 *electron-v27.2.1-linux-x64.zip -dc7feb71d7a65bde3ddbcc43e514940a070af9fd8b385f819ac012b9d694ce64 *electron-v27.2.1-mas-arm64-dsym-snapshot.zip -98317e6af5ddb3db98511958a9ad46a54de00c897b67c6d1e3dc3baaf3e3878a *electron-v27.2.1-mas-arm64-dsym.zip -91a78763ac1959c3b4865902e378830c7304917dc20877db7c12283c1d34e510 *electron-v27.2.1-mas-arm64-symbols.zip -01414a8e5ef3c1f7efb888d32bdfc85ba0ea8b42d4e8093ca7cf0e7a8a728415 *electron-v27.2.1-mas-arm64.zip -e082d37c20b1af792307fa2db9ff576e4345256d26dd0e0f5f2d807a45673991 *electron-v27.2.1-mas-x64-dsym-snapshot.zip -3a93a95bf05ec4d01268a64db2d58a07bff90463f3059a3dd4a30968297aab36 *electron-v27.2.1-mas-x64-dsym.zip -138ac2d1fea73d0f677fa5218a0ba2d5422d22cb6090d865c7ddce99383aa415 *electron-v27.2.1-mas-x64-symbols.zip -87457c8938858570b20a122c1cdb23c0a41374b7010253626d49e9b6b127a50a *electron-v27.2.1-mas-x64.zip -ce3638d29dfb46fc5b375ead5c0bbb98b50eec2c9e42c5b4a0a948a60ef25439 *electron-v27.2.1-win32-arm64-pdb.zip -f24636b71937e9b4ca46d046e6131a3acb87edca93be0b26d500d93c6a78abeb *electron-v27.2.1-win32-arm64-symbols.zip -c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v27.2.1-win32-arm64-toolchain-profile.zip -5b4fd051b04de97ef14a711533ecd44c99d31ce6fc83f2593c4e041e9550eeb1 *electron-v27.2.1-win32-arm64.zip -c8cd49e0ba8a87666a50d358dc3a2810d0d5f2c13e345c48febf488aed513856 *electron-v27.2.1-win32-ia32-pdb.zip -7d28badd01d7f357f0cbbff12df55b1816f2dd08bcceea00fa03b027d7c8bb9b *electron-v27.2.1-win32-ia32-symbols.zip -c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v27.2.1-win32-ia32-toolchain-profile.zip -508f29dd2d07c19b1ae6d7546772103230d601d44f33443a79a3b42a5fa9d293 *electron-v27.2.1-win32-ia32.zip -1b40186cd080f9d45bb03b1d7f6e4f3c4fc6332bbbe36453f99bd8643545f384 *electron-v27.2.1-win32-x64-pdb.zip -e2bc044c06a463d29699b5a0bedecb68714cacfdb5661a70db4047cb211a8246 *electron-v27.2.1-win32-x64-symbols.zip -c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v27.2.1-win32-x64-toolchain-profile.zip -4d0810c1bc9985e7e471efb06380bd190b5d07f46ef1938c1d3ee496e0cdcccf *electron-v27.2.1-win32-x64.zip -f48477c175fe9bc34c1541cb75bd9d3eda2c540a799e2f3b715adcdd626c3dc1 *electron.d.ts -c951898cbf3c65013a21b5b4581d24e38f568183893f785f7d3bab0aedf49d6f *ffmpeg-v27.2.1-darwin-arm64.zip -5306f41915ced8307c44858a91250a228819304c82dfdd80c9f584fe092436ad *ffmpeg-v27.2.1-darwin-x64.zip -be517ba93c5b5f36d2e55af148213f4c2fc43177563825601bee6f88dd4f7b05 *ffmpeg-v27.2.1-linux-arm64.zip -926d0da25ffcea3d05a6cbcae15e5d7729d93bc43394ae4439747669d2210e1d *ffmpeg-v27.2.1-linux-armv7l.zip -6f9c0ef52af14828ad547a80b17f8c63cac51a18b8d5769a2f33e4fa6cccfc7e *ffmpeg-v27.2.1-linux-x64.zip -bb0036c363cdc2558d9feec90cb896c66133400b499b14fa099421e1d0117bc6 *ffmpeg-v27.2.1-mas-arm64.zip -33203048fa78450cac7ae1988b708ad51d1a49933d212b505fcf272da201e6d6 *ffmpeg-v27.2.1-mas-x64.zip -954870aaa5ffa56bbe06b04308bd6a4fc937b82cc11d8780542e4a9f25790b37 *ffmpeg-v27.2.1-win32-arm64.zip -a46eab4db71ff41a0a1056b8777ec2d01f83f4b7a85687514562045f1f2b4820 *ffmpeg-v27.2.1-win32-ia32.zip -74841b03157199f2439d2182cf4da66dfdc4014d4e2d76a043055b4dc5d33a9b *ffmpeg-v27.2.1-win32-x64.zip -7f15f0f64083f093a98f9dc002d6256a3a56cc7d720cd861d820a7223d46be83 *hunspell_dictionaries.zip -6cc8cbde27c4e0ed7689f87a4c444aa50a1bee9bfec35289e26e9854864c1d69 *libcxx-objects-v27.2.1-linux-arm64.zip -ce935631fa896af9d34e8ed01a431c7f4b289d9cc872cede5bfd9e4663ac4d6e *libcxx-objects-v27.2.1-linux-armv7l.zip -979a65ea5cd9c8c44e6d9760c2cb79bec0914396b68be9270b52d09c1b1a72ee *libcxx-objects-v27.2.1-linux-x64.zip -86f961adc7015bbf8762453bc5148d381ff7c7c4580ceed9189c93fd01139fcf *libcxx_headers.zip -acb2c0d967dc14fe63784c79c58648193348252efb25a691b61d3c19142d67e9 *libcxxabi_headers.zip -07bc91f769fb7320bfb331d7e40d963e787960dec0f404b165c7df9ef49f531b *mksnapshot-v27.2.1-darwin-arm64.zip -c9127c412b6f10a706c406865a72119abf34692efd19e58231dec631af91f3c5 *mksnapshot-v27.2.1-darwin-x64.zip -d52e6deec09571b4618dab3ee1c615f2996d3d52e97f0df4c8a7ecddc443d847 *mksnapshot-v27.2.1-linux-arm64-x64.zip -23aa985b9d4431d9a92eefe8f635bcd54a50a1b02d008d9883dce1ca90670d9b *mksnapshot-v27.2.1-linux-armv7l-x64.zip -9aac8616fcfaeda69945742f4fe99309cabfcc355f5a6edae5ac6bdb5de78552 *mksnapshot-v27.2.1-linux-x64.zip -f53004a849ea0d5e19569f48561fc79b95015c3395405ef28800fa34e5a6b37c *mksnapshot-v27.2.1-mas-arm64.zip -8b7458854d6e3339220623a53927eeaac2d97e02133172f2615ee976e32a1337 *mksnapshot-v27.2.1-mas-x64.zip -5314c938447f1f24aee9007719d8d65ef4f68a3fac0e103897bc53f26685ed6a *mksnapshot-v27.2.1-win32-arm64-x64.zip -fbae6d4323d20af0a7c5d5c044e9927b6f8396e0d9c8a45c1f6945ce32250715 *mksnapshot-v27.2.1-win32-ia32.zip -08556f224fcb5dee0928dce8beda23f058258cc85e85da3fe651bf0e84469f5d *mksnapshot-v27.2.1-win32-x64.zip +5e12800023944ba203820eac76382b57ccd5dad371f70650889bd77decbc372b *chromedriver-v27.2.3-darwin-arm64.zip +d271649b18bbbe7aa2c8fe8633bbb335611d018c95ddb6b4f54e6cd037163219 *chromedriver-v27.2.3-darwin-x64.zip +1c94c33c01cb23b1e9bf6ab34093abd1a8b8cfa8b6021a70c8142022b6d8f0a0 *chromedriver-v27.2.3-linux-arm64.zip +4491b78e7fc45f27388abc022841aeecb48fd2907f7aed2be42caf3db3149284 *chromedriver-v27.2.3-linux-armv7l.zip +1ec4ddb99209017df0f3252799e87bd04f2ae6d14b1ed3dc638e8bfbc512441b *chromedriver-v27.2.3-linux-x64.zip +a9384b769f0b47eab221a0576c6eb5b6f8a390943621dac0b4b03c6c11b4db9b *chromedriver-v27.2.3-mas-arm64.zip +2dc6bb386adb60426ed9902cae118073d289bea9d2ad739162aa3534464637b0 *chromedriver-v27.2.3-mas-x64.zip +23587604cdc9aa9b434879a8615f96d023cb5f7bc1bbb1d672b232bda21f3c48 *chromedriver-v27.2.3-win32-arm64.zip +aceae2b417ae6a83bc6ddd3a762bd99e48455765b9db4d65fcfbc1fe17182bae *chromedriver-v27.2.3-win32-ia32.zip +a3797c5d58fb73b22b8d06b58250c57be94673f2c7e6862095a0bd768feee278 *chromedriver-v27.2.3-win32-x64.zip +39ec5429329af2990470a0c46ba865851863610ff46b47c4a25f7055670f1604 *electron-api.json +6cd6e53a0fdccbf3e89da8634d6fda7f04e0e521ffd3f494dfb4e9f7087c5ff2 *electron-v27.2.3-darwin-arm64-dsym-snapshot.zip +cc13dbdb4f319cbc31c8ef76560a92b695d19e1f88709dda5f2f26d87fc04469 *electron-v27.2.3-darwin-arm64-dsym.zip +949cfa456931a3f9190504a7b135a726eb75d223fd769d38c5c0258ab2328db0 *electron-v27.2.3-darwin-arm64-symbols.zip +20d6d486f6e0e428182dbbafd293f3007fc91760d37bbc3559e813d55ee15d85 *electron-v27.2.3-darwin-arm64.zip +b1b4e7d7212336fe28e8a461b920e7ca0a358a313000592d160adbf335c7e65b *electron-v27.2.3-darwin-x64-dsym-snapshot.zip +55f88e9491c237675cb1f7e3d055d404db70e4682e906ede79ff433d8794679f *electron-v27.2.3-darwin-x64-dsym.zip +75262139cb49ce5070501f6e74dade18bf9f0eb3b968d848765a937560f6338b *electron-v27.2.3-darwin-x64-symbols.zip +602b89308fb114631d90bda4bbef611aa571c57588fbb1bfcec781e22d584c22 *electron-v27.2.3-darwin-x64.zip +94989909484abff147b4334a100ace00571d810da94b717e54c82cb76d7acf11 *electron-v27.2.3-linux-arm64-debug.zip +9e57241fa5df5e09c70a99aff5099350f5b7126895f7a4ab8ca401352b4bd99a *electron-v27.2.3-linux-arm64-symbols.zip +86c98c25f1a7e02f3c8e3fc837e8785e1cd88518c4e5f005c59e039bb6e79054 *electron-v27.2.3-linux-arm64.zip +94989909484abff147b4334a100ace00571d810da94b717e54c82cb76d7acf11 *electron-v27.2.3-linux-armv7l-debug.zip +43a89997cb8acd88f37d69649ef4e8f6bbb9ffce187004e09604c410e396e788 *electron-v27.2.3-linux-armv7l-symbols.zip +7a581252c59514696f920f21af0e97f997e01c9df44d6788c457825bf398c0ea *electron-v27.2.3-linux-armv7l.zip +8a27e845f4405a44fb16297362782531aff03219a002a3e23b3b439fd610b6dd *electron-v27.2.3-linux-x64-debug.zip +b8ddc3f92f23d5351c096287b76ff106c53560bf44cdbc3ba33ed2bd9609bb63 *electron-v27.2.3-linux-x64-symbols.zip +3347f4ff220fa63b286d91fec129d7f8d636ce883dba3c53c50b7e5834c940e1 *electron-v27.2.3-linux-x64.zip +b474af6f1681e204e09906f92d26e1474bc84b35510e4454a3f6bbaaf4df387b *electron-v27.2.3-mas-arm64-dsym-snapshot.zip +3c829ad8fafd12a3ee5c633bee0ae12137a9d780b7bc003785fd1badfafe3d97 *electron-v27.2.3-mas-arm64-dsym.zip +1a0ff4e32b980aabbcc21b533b17c16dbe1eb431408e9cc61dccddbcb673c695 *electron-v27.2.3-mas-arm64-symbols.zip +dd59ca093811646c953c71aaa12bf30c773ed7a77ea1344029d87af40281a114 *electron-v27.2.3-mas-arm64.zip +70b04af2ef42f99becdf0de53efaa4cf419151ceeed1b2ed77759c1f7c1332cc *electron-v27.2.3-mas-x64-dsym-snapshot.zip +89c379f6648a1864250adb420e09bcd1ac7422958a4daf6c830c0b15cfb2babc *electron-v27.2.3-mas-x64-dsym.zip +b0215c7b1496352b92547543c2a107fcf4c70023c10043a5a6777bdf82b22287 *electron-v27.2.3-mas-x64-symbols.zip +781f618ec0b16fdee5c0a76430b617f66152aa2e02ccf953fae1c81e57fab950 *electron-v27.2.3-mas-x64.zip +d7deac3628b145c006ab935d90694a9b009fbfdce2e73f4ccc861ce739452af7 *electron-v27.2.3-win32-arm64-pdb.zip +4b767cd84c8fccdd0350fa5b82a3424779994bfcd4e3c4f864f003ea3ec4fd33 *electron-v27.2.3-win32-arm64-symbols.zip +c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v27.2.3-win32-arm64-toolchain-profile.zip +407f86a40574152ee89e4f01878e43d389876bbf23058352b77d5d3363ab98d3 *electron-v27.2.3-win32-arm64.zip +b5de17188f154b5041978b90f0471089a94d552d2c96e1f953154484f65bfe31 *electron-v27.2.3-win32-ia32-pdb.zip +f7ac401c08cdfe3d1f60d3e61c1de995f20a476f4762e3a21057a5132ff42735 *electron-v27.2.3-win32-ia32-symbols.zip +c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v27.2.3-win32-ia32-toolchain-profile.zip +f637d01467cdb808530391508ac268b397675eaa620b71d9d2f0d71d25cafe53 *electron-v27.2.3-win32-ia32.zip +a7463a6993a6d938c110dfa61c4af7c751081aac581f81d803efa9af063f4530 *electron-v27.2.3-win32-x64-pdb.zip +32fd37c76cfe8ac40738f0c9775f991971ec6a787bd81216ae2822ade8695e7a *electron-v27.2.3-win32-x64-symbols.zip +c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v27.2.3-win32-x64-toolchain-profile.zip +36b75f90dde9013e13afd47576192fba1ee1ddf6790cca285e78fd58828d5961 *electron-v27.2.3-win32-x64.zip +57900d1631e5f15f976d8aac2ce11dab56127d83358bbbc09ae8db4aa5698516 *electron.d.ts +a6863fa69d352b46e28966dc0f7ca2af08d91cc5e894421d9c8b21d9a552cf1c *ffmpeg-v27.2.3-darwin-arm64.zip +e35fa1f8f5884dc3dca29b6e7b03f30509dc20012abffcdac027ddda4d2e1602 *ffmpeg-v27.2.3-darwin-x64.zip +be517ba93c5b5f36d2e55af148213f4c2fc43177563825601bee6f88dd4f7b05 *ffmpeg-v27.2.3-linux-arm64.zip +926d0da25ffcea3d05a6cbcae15e5d7729d93bc43394ae4439747669d2210e1d *ffmpeg-v27.2.3-linux-armv7l.zip +6f9c0ef52af14828ad547a80b17f8c63cac51a18b8d5769a2f33e4fa6cccfc7e *ffmpeg-v27.2.3-linux-x64.zip +e2e099bba965b13050d65980705aca2fb26698c9b0bcf1e66725896e6a3702cf *ffmpeg-v27.2.3-mas-arm64.zip +6104f719783d7d910a520dde756555ee77a284e29c3b14d8c2e9c53d409e8efa *ffmpeg-v27.2.3-mas-x64.zip +c7c574fe9afc3a42f207b805543b56b36e3f4fe65b48a5c6a5a4dc03353a6d41 *ffmpeg-v27.2.3-win32-arm64.zip +165bb1e9b42cc8f1bc233f26e823e1f53ae7f1e278fac1ec864273ceb3bfe5eb *ffmpeg-v27.2.3-win32-ia32.zip +1ccc98816757a19f01c838bd67c73fa53e11495bf075fbc101754aeb38a09d8d *ffmpeg-v27.2.3-win32-x64.zip +27490d54e045651a7c2c3d220bb23cf76a98fd9f7566737f5b06e75809e7959e *hunspell_dictionaries.zip +b49f4a784414819807de510a3d8ace198118997c11693cd693807c31260ac47d *libcxx-objects-v27.2.3-linux-arm64.zip +c62630afb544f42749bd2b9f7d0b54d8d44e109fb115f88822a5d7102f61088c *libcxx-objects-v27.2.3-linux-armv7l.zip +f4f8c72bc6b545959a18ba2caf4db05f05ce8c70b234509f95d2826dd54273e2 *libcxx-objects-v27.2.3-linux-x64.zip +f26fd6d3216afb9e9c16be9f88d27d82bb13500eac4957ae2d83c90682a201fc *libcxx_headers.zip +ba6952490ded27c344150527543dee9693e5906ca419a6975a38cb61da6be794 *libcxxabi_headers.zip +fd1e8aed99530f94bec125a60f5b6ea34f2586deb8034f5f437e95f58c951340 *mksnapshot-v27.2.3-darwin-arm64.zip +8508fbbd71c1654b79a02336cdd042101f9596ebf4e1666d2d55ab8d3f363c8f *mksnapshot-v27.2.3-darwin-x64.zip +9c0ecb392aab08f59cddd54b0f3116f3a19442549ec3d3523208ac0807e3d504 *mksnapshot-v27.2.3-linux-arm64-x64.zip +ccc26dd636dbfc9c99c8e76fa55535dc66154c19a7de18d2f462ee4e1269ae95 *mksnapshot-v27.2.3-linux-armv7l-x64.zip +d7ceffae1aeb4022660fd2354bb4b8e3f62fd4531c1f77989039a3e47be17303 *mksnapshot-v27.2.3-linux-x64.zip +efc12f63c71b290c8f07b337098bcd0f54aa9dfa92a1ed16603db5e9f232d100 *mksnapshot-v27.2.3-mas-arm64.zip +65f9fc4c3a639d4b5279fd80b57be79099815a04bcdc504631170a15f8a7efdb *mksnapshot-v27.2.3-mas-x64.zip +b46cc5f28cfb49a2a2b5f4c66cb8d0cfc530877201d4df85b7568c1ad8d5da1c *mksnapshot-v27.2.3-win32-arm64-x64.zip +39878ef70de05b000b86c2e593a62f9d76703d46d1bd5bfdb525e2826ea85d9f *mksnapshot-v27.2.3-win32-ia32.zip +c19a90b8149d0ba9a623d1134c058f514234975dce32ca37e7e97bdf474eb9fd *mksnapshot-v27.2.3-win32-x64.zip diff --git a/cgmanifest.json b/cgmanifest.json index 139424f261334..1a676f2ac1e3c 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "8471e4750734ea33a85720ce20bca0d8d9d56985" + "commitHash": "b3ca1a1f96bae5352f420ef1f77cc9d7fd5957c6" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "27.2.1" + "version": "27.2.3" }, { "component": { diff --git a/package.json b/package.json index c27208c3fb811..c632d597fa2f5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.86.0", - "distro": "4d476863b75b35121a8d2a49c6adbdd2d9ea7572", + "distro": "be7d9acdb535bf4095cbbb28c88cfd05a2a11533", "author": { "name": "Microsoft Corporation" }, @@ -150,7 +150,7 @@ "cssnano": "^4.1.11", "debounce": "^1.0.0", "deemon": "^1.8.0", - "electron": "27.2.1", + "electron": "27.2.3", "eslint": "8.36.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^46.5.0", diff --git a/remote/.yarnrc b/remote/.yarnrc index 45af692342a67..adbe2d2a5f47f 100644 --- a/remote/.yarnrc +++ b/remote/.yarnrc @@ -1,5 +1,5 @@ disturl "https://nodejs.org/dist" target "18.17.1" -ms_build_id "241679" +ms_build_id "252256" runtime "node" build_from_source "true" diff --git a/yarn.lock b/yarn.lock index 5e1850eb69f42..0fc6389685ad2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3570,10 +3570,10 @@ electron-to-chromium@^1.4.202: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.207.tgz#9c3310ebace2952903d05dcaba8abe3a4ed44c01" integrity sha512-piH7MJDJp4rJCduWbVvmUd59AUne1AFBJ8JaRQvk0KzNTSUnZrVXHCZc+eg+CGE4OujkcLJznhGKD6tuAshj5Q== -electron@27.2.1: - version "27.2.1" - resolved "https://registry.yarnpkg.com/electron/-/electron-27.2.1.tgz#9251b151fea39f7ef2b011e45dd04fb06db58c25" - integrity sha512-bYUzyptYrUIFtPnyF2x6DnhF1E9FCthctjbNSKMqg7dG4NqSwyuZzEku3Wts55u8R+ddNFbLoLwRHHLvYTCQlA== +electron@27.2.3: + version "27.2.3" + resolved "https://registry.yarnpkg.com/electron/-/electron-27.2.3.tgz#ef8c7a33175bb9fc91ae5508c560aea719b49232" + integrity sha512-AbDL5WhoyN56Nb6Ox26eFNXo3PcoxPQGUfBMBT8WXY19uD8kI+l6XF8mI09ezMejo3JjKu4kh3zZUPcgskzIng== dependencies: "@electron/get" "^2.0.0" "@types/node" "^18.11.18" From 4a85ff3e4efe1221920a32906798cf51bd91f379 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Mon, 22 Jan 2024 11:57:45 -0800 Subject: [PATCH 218/333] add simple limit for number of children (#203061) --- .../notebookVariablesDataSource.ts | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts index d23184d77a5db..035e5b0ee7f9e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource.ts @@ -5,6 +5,7 @@ import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { localize } from 'vs/nls'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookKernel, INotebookKernelService, VariablesResult, variablePageSize } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; @@ -78,10 +79,13 @@ export class NotebookVariableDataSource implements IAsyncDataSource variablePageSize) { - for (let start = 0; start < parent.indexedChildrenCount; start += variablePageSize) { + // TODO: improve handling of large number of children + const indexedChildCountLimit = 100000; + const limit = Math.min(parent.indexedChildrenCount, indexedChildCountLimit); + for (let start = 0; start < limit; start += variablePageSize) { let end = start + variablePageSize; - if (end > parent.indexedChildrenCount) { - end = parent.indexedChildrenCount; + if (end > limit) { + end = limit; } childNodes.push({ @@ -96,6 +100,19 @@ export class NotebookVariableDataSource implements IAsyncDataSource indexedChildCountLimit) { + childNodes.push({ + kind: 'variable', + notebook: parent.notebook, + id: parent.id + `${limit + 1}`, + extHostId: parent.extHostId, + name: localize('notebook.indexedChildrenLimitReached', "Display limit reached"), + value: '', + indexedChildrenCount: 0, + hasNamedChildren: false + }); + } } else if (parent.indexedChildrenCount > 0) { const variables = kernel.provideVariables(parent.notebook.uri, parent.extHostId, 'indexed', parent.indexStart ?? 0, this.cancellationTokenSource.token); From de8957372679016ef1d2b08515844287eb9b968a Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Mon, 22 Jan 2024 12:00:40 -0800 Subject: [PATCH 219/333] wip -- Support folding notebook cells via sticky scroll (#202785) * CSS tweaks, folding icon, indentation option, high contrast background * CSS cleaning, listen to touch events, folding functionality and icons * update testing snapshots, no hashtag prefixing * foldingController nit --- .../media/notebookEditorStickyScroll.css | 51 +++++++++-- .../viewParts/notebookEditorStickyScroll.ts | 86 +++++++++++++++++-- ...ld_render_0-_1___visible_range_3-_8.0.snap | 2 +- ...ng_next_2_against_following_section.0.snap | 2 +- ...ing_against_equivalent_level_header.0.snap | 2 +- ...___scrolltop_halfway_through_cell_0.0.snap | 2 +- ...___scrolltop_halfway_through_cell_2.0.snap | 2 +- ...___scrolltop_halfway_through_cell_7.0.snap | 2 +- ...1___collapsing_against_next_section.0.snap | 2 +- 9 files changed, 127 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css b/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css index 4b4078aecbe8b..25fa90546d698 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css @@ -6,29 +6,62 @@ .monaco-workbench .notebookOverlay .notebook-sticky-scroll-container { display: none; background-color: var(--vscode-notebook-editorBackground); + padding-left: 9.5px; +} + +.monaco-workbench + .notebookOverlay + .notebook-sticky-scroll-container + .notebook-sticky-scroll-element { + display: flex; + align-items: center; +} + +.monaco-workbench + .notebookOverlay + .notebook-sticky-scroll-container + .notebook-sticky-scroll-element + .notebook-sticky-scroll-folding-icon:hover { + outline: 1px dashed var(--vscode-contrastActiveBorder); + outline-offset: -1px; +} + +.monaco-workbench + .notebookOverlay + .notebook-sticky-scroll-container + .notebook-sticky-scroll-element + .notebook-sticky-scroll-header { width: 100%; + padding-left: 6px; } .monaco-workbench .notebookOverlay .notebook-sticky-scroll-container - .notebook-sticky-scroll-line { - background-color: var(--vscode-notebook-editorBackground); - position: relative; - padding-left: 12px; - /* transition: margin-top 0.2s ease-in-out; */ + .notebook-sticky-scroll-element + .notebook-sticky-scroll-header:hover { + background-color: var(--vscode-editorStickyScrollHover-background); + width: 100%; + cursor: pointer; } .monaco-workbench.hc-light .notebookOverlay .notebook-sticky-scroll-container, .monaco-workbench.hc-black .notebookOverlay .notebook-sticky-scroll-container { background-color: var(--vscode-editorStickyScroll-background); border-bottom: 1px solid var(--vscode-contrastBorder); + padding-bottom: 3px; } -.monaco-workbench +.monaco-workbench.hc-light .notebookOverlay .notebook-sticky-scroll-container - .notebook-sticky-scroll-line:hover { - background-color: var(--vscode-editorStickyScrollHover-background); - cursor: pointer; + .notebook-sticky-scroll-element + .notebook-sticky-scroll-header:hover, +.monaco-workbench.hc-black + .notebookOverlay + .notebook-sticky-scroll-container + .notebook-sticky-scroll-element + .notebook-sticky-scroll-header:hover { + outline: 1px dashed var(--vscode-contrastActiveBorder); + outline-offset: -2px; } diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts index 0c812230c030b..e8eb6c5a7ec13 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts @@ -5,6 +5,7 @@ import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; +import { EventType as TouchEventType } from 'vs/base/browser/touch'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -14,12 +15,16 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellFoldingState, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { INotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { OutlineEntry } from 'vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry'; import { NotebookCellOutlineProvider } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProvider'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { Delayer } from 'vs/base/common/async'; +import { ThemeIcon } from 'vs/base/common/themables'; +import { foldingCollapsedIcon, foldingExpandedIcon } from 'vs/editor/contrib/folding/browser/foldingDecorations'; +import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; +import { FoldingController } from 'vs/workbench/contrib/notebook/browser/controller/foldingController'; export class ToggleNotebookStickyScroll extends Action2 { @@ -54,26 +59,56 @@ export class ToggleNotebookStickyScroll extends Action2 { export class NotebookStickyLine extends Disposable { constructor( public readonly element: HTMLElement, + public readonly foldingIcon: StickyFoldingIcon, + public readonly header: HTMLElement, public readonly entry: OutlineEntry, public readonly notebookEditor: INotebookEditor, ) { super(); - this._register(DOM.addDisposableListener(this.element, DOM.EventType.CLICK, () => { + // click the header to focus the cell + this._register(DOM.addDisposableListener(this.header, DOM.EventType.CLICK || TouchEventType.Tap, () => { this.focusCell(); })); + + // click the folding icon to fold the range covered by the header + this._register(DOM.addDisposableListener(this.foldingIcon.domNode, DOM.EventType.CLICK || TouchEventType.Tap, () => { + if (this.entry.cell.cellKind === CellKind.Markup) { + const currentFoldingState = (this.entry.cell as MarkupCellViewModel).foldingState; + this.toggleFoldRange(currentFoldingState); + } + })); + + // folding icon hovers + // this._register(DOM.addDisposableListener(this.element, DOM.EventType.MOUSE_OVER, () => { + // this.foldingIcon.setVisible(true); + // })); + // this._register(DOM.addDisposableListener(this.element, DOM.EventType.MOUSE_OUT, () => { + // this.foldingIcon.setVisible(false); + // })); + + } + + private toggleFoldRange(currentState: CellFoldingState) { + const foldingController = this.notebookEditor.getContribution(FoldingController.id); + + const index = this.entry.index; + const headerLevel = this.entry.level; + const newFoldingState = (currentState === CellFoldingState.Collapsed) ? CellFoldingState.Expanded : CellFoldingState.Collapsed; + + foldingController.setFoldingStateUp(index, newFoldingState, headerLevel); + this.focusCell(); } private focusCell() { this.notebookEditor.focusNotebookCell(this.entry.cell, 'container'); const cellScrollTop = this.notebookEditor.getAbsoluteTopOfElement(this.entry.cell); - const parentCount = this.getParentCount(); + const parentCount = NotebookStickyLine.getParentCount(this.entry); // 1.1 addresses visible cell padding, to make sure we don't focus md cell and also render its sticky line this.notebookEditor.setScrollTop(cellScrollTop - (parentCount + 1.1) * 22); } - private getParentCount() { + static getParentCount(entry: OutlineEntry) { let count = 0; - let entry = this.entry; while (entry.parent) { count++; entry = entry.parent; @@ -82,6 +117,26 @@ export class NotebookStickyLine extends Disposable { } } +class StickyFoldingIcon { + + public domNode: HTMLElement; + + constructor( + public isCollapsed: boolean, + public dimension: number + ) { + this.domNode = document.createElement('div'); + this.domNode.style.width = `${dimension}px`; + this.domNode.style.height = `${dimension}px`; + this.domNode.className = ThemeIcon.asClassName(isCollapsed ? foldingCollapsedIcon : foldingExpandedIcon); + } + + public setVisible(visible: boolean) { + this.domNode.style.cursor = visible ? 'pointer' : 'default'; + this.domNode.style.opacity = visible ? '1' : '0'; + } +} + export class NotebookStickyScroll extends Disposable { private readonly _disposables = new DisposableStore(); private currentStickyLines = new Map(); @@ -302,9 +357,24 @@ export class NotebookStickyScroll extends Disposable { static createStickyElement(entry: OutlineEntry, notebookEditor: INotebookEditor) { const stickyElement = document.createElement('div'); - stickyElement.classList.add('notebook-sticky-scroll-line'); - stickyElement.innerText = '#'.repeat(entry.level) + ' ' + entry.label; - return new NotebookStickyLine(stickyElement, entry, notebookEditor); + stickyElement.classList.add('notebook-sticky-scroll-element'); + stickyElement.style.paddingLeft = NotebookStickyLine.getParentCount(entry) * 10 + 'px'; + + let isCollapsed = false; + if (entry.cell.cellKind === CellKind.Markup) { + isCollapsed = (entry.cell as MarkupCellViewModel).foldingState === CellFoldingState.Collapsed; + } + + const stickyFoldingIcon = new StickyFoldingIcon(isCollapsed, 16); + stickyFoldingIcon.domNode.classList.add('notebook-sticky-scroll-folding-icon'); + stickyFoldingIcon.setVisible(true); + + const stickyHeader = document.createElement('div'); + stickyHeader.classList.add('notebook-sticky-scroll-header'); + stickyHeader.innerText = entry.label; + + stickyElement.append(stickyFoldingIcon.domNode, stickyHeader); + return new NotebookStickyLine(stickyElement, stickyFoldingIcon, stickyHeader, entry, notebookEditor); } private disposeCurrentStickyLines() { diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test1__should_render_0-_1___visible_range_3-_8.0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test1__should_render_0-_1___visible_range_3-_8.0.snap index 1bcf0a58d432c..81faf81e4af61 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test1__should_render_0-_1___visible_range_3-_8.0.snap +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test1__should_render_0-_1___visible_range_3-_8.0.snap @@ -1 +1 @@ -[ "# header a", "## header aa" ] \ No newline at end of file +[ "header a", "header aa" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test2__should_render_0____visible_range_6-_9_so_collapsing_next_2_against_following_section.0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test2__should_render_0____visible_range_6-_9_so_collapsing_next_2_against_following_section.0.snap index 3f23335f240c6..ff5d6d92e8865 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test2__should_render_0____visible_range_6-_9_so_collapsing_next_2_against_following_section.0.snap +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test2__should_render_0____visible_range_6-_9_so_collapsing_next_2_against_following_section.0.snap @@ -1 +1 @@ -[ "# header a" ] \ No newline at end of file +[ "header a" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_2___collapsing_against_equivalent_level_header.0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_2___collapsing_against_equivalent_level_header.0.snap index 3b3d5999fd8f6..9ba2fcd338ebb 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_2___collapsing_against_equivalent_level_header.0.snap +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_2___collapsing_against_equivalent_level_header.0.snap @@ -1 +1 @@ -[ "# header a", "## header aa", "### header aab" ] \ No newline at end of file +[ "header a", "header aa", "header aab" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test4__should_render_0____scrolltop_halfway_through_cell_0.0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test4__should_render_0____scrolltop_halfway_through_cell_0.0.snap index 3f23335f240c6..ff5d6d92e8865 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test4__should_render_0____scrolltop_halfway_through_cell_0.0.snap +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test4__should_render_0____scrolltop_halfway_through_cell_0.0.snap @@ -1 +1 @@ -[ "# header a" ] \ No newline at end of file +[ "header a" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test5__should_render_0-_2___scrolltop_halfway_through_cell_2.0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test5__should_render_0-_2___scrolltop_halfway_through_cell_2.0.snap index 2e13ea43822b9..5ebd7d5ec87e2 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test5__should_render_0-_2___scrolltop_halfway_through_cell_2.0.snap +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test5__should_render_0-_2___scrolltop_halfway_through_cell_2.0.snap @@ -1 +1 @@ -[ "# header a", "## header aa", "### header aaa" ] \ No newline at end of file +[ "header a", "header aa", "header aaa" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test6__should_render_6-_7___scrolltop_halfway_through_cell_7.0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test6__should_render_6-_7___scrolltop_halfway_through_cell_7.0.snap index 77fe21fe781da..e2a34d2a7083c 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test6__should_render_6-_7___scrolltop_halfway_through_cell_7.0.snap +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test6__should_render_6-_7___scrolltop_halfway_through_cell_7.0.snap @@ -1 +1 @@ -[ "# header b", "## header bb" ] \ No newline at end of file +[ "header b", "header bb" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test7__should_render_0-_1___collapsing_against_next_section.0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test7__should_render_0-_1___collapsing_against_next_section.0.snap index 1bcf0a58d432c..81faf81e4af61 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test7__should_render_0-_1___collapsing_against_next_section.0.snap +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test7__should_render_0-_1___collapsing_against_next_section.0.snap @@ -1 +1 @@ -[ "# header a", "## header aa" ] \ No newline at end of file +[ "header a", "header aa" ] \ No newline at end of file From e0f13622d780fc275fe8481848340389f48362b4 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Mon, 22 Jan 2024 13:09:25 -0700 Subject: [PATCH 220/333] Trim insider suffix from version when sent to ExP (#203071) * Trim insiders suffix from version * Fix comment indent * Fix more indent --- src/vs/platform/assignment/common/assignment.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/assignment/common/assignment.ts b/src/vs/platform/assignment/common/assignment.ts index 1fe2da6a87571..8a2fe3becf75c 100644 --- a/src/vs/platform/assignment/common/assignment.ts +++ b/src/vs/platform/assignment/common/assignment.ts @@ -90,10 +90,24 @@ export class AssignmentFilterProvider implements IExperimentationFilterProvider private targetPopulation: TargetPopulation ) { } + /** + * Returns a version string that can be parsed by the TAS client. + * The tas client cannot handle suffixes lke "-insider" + * Ref: https://github.com/microsoft/tas-client/blob/30340d5e1da37c2789049fcf45928b954680606f/vscode-tas-client/src/vscode-tas-client/VSCodeFilterProvider.ts#L35 + * + * @param version Version string to be trimmed. + */ + private static trimVersionSuffix(version: string): string { + const regex = /\-[a-zA-Z0-9]+$/; + const result = version.split(regex); + + return result[0]; + } + getFilterValue(filter: string): string | null { switch (filter) { case Filters.ApplicationVersion: - return this.version; // productService.version + return AssignmentFilterProvider.trimVersionSuffix(this.version); // productService.version case Filters.Build: return this.appName; // productService.nameLong case Filters.ClientId: From f9bb272b285da871e51a8d56160e988209900d45 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Mon, 22 Jan 2024 14:18:22 -0600 Subject: [PATCH 221/333] Focus in Quick Search doesn't go back to previous position after hitting ESC when zero results (#203073) Fixes #202911 --- .../browser/quickTextSearch/textSearchQuickAccess.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts index 3be24ea6f3434..b1ba725a1b3a5 100644 --- a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -54,7 +54,6 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { if (this.searchModel.searchResult.count() > 0) { this.moveToSearchViewlet(undefined); @@ -114,12 +114,8 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider folder.uri), this._getTextQueryBuilderOptions(charsPerLine)); From 0c882aa7709b3060c63f6e3ddc6ecc81c3df9005 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 22 Jan 2024 10:58:52 -1000 Subject: [PATCH 222/333] debug: update js-debug (#203076) --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index 49b692f2c2fe0..7f445fef7a297 100644 --- a/product.json +++ b/product.json @@ -50,8 +50,8 @@ }, { "name": "ms-vscode.js-debug", - "version": "1.85.0", - "sha256": "85a97d373a6f92359f5db7bfc5a2fa6c5763b22c6ad07c962dfe32c84a079f3b", + "version": "1.86.0", + "sha256": "cdd3187384c953160c3d0ad1e8ebd869cc420930a9993f977f7d739029a02a54", "repo": "https://github.com/microsoft/vscode-js-debug", "metadata": { "id": "25629058-ddac-4e17-abba-74678e126c5d", From 2d30630ab494f403bb19af922fee673255f210ee Mon Sep 17 00:00:00 2001 From: RedCMD <33529441+RedCMD@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:11:24 +1300 Subject: [PATCH 223/333] `onDidChangeEmmiter` fires constantly when it shouldn't (#202198) `onDidChangeEmmiter` fires when it shouldn't --- .../html-language-features/client/src/languageParticipants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/html-language-features/client/src/languageParticipants.ts b/extensions/html-language-features/client/src/languageParticipants.ts index 6abe49237c806..e3d5612b9b32d 100644 --- a/extensions/html-language-features/client/src/languageParticipants.ts +++ b/extensions/html-language-features/client/src/languageParticipants.ts @@ -55,7 +55,7 @@ export function getLanguageParticipants(): LanguageParticipants { } } } - return !isEqualSet(languages, oldLanguages) || !isEqualSet(oldLanguages, oldAutoInsert); + return !isEqualSet(languages, oldLanguages) || !isEqualSet(autoInsert, oldAutoInsert); } update(); From 1ddc10601dcd3d9045c8f84109783d7d9ceb0fbc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 22 Jan 2024 14:58:28 -0800 Subject: [PATCH 224/333] Add angle brackets as surrounding pairs in markdown (#203083) Add angle brackets as surrounding pairs in md This is useful for html and for autolinks --- extensions/markdown-basics/language-configuration.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/markdown-basics/language-configuration.json b/extensions/markdown-basics/language-configuration.json index 0d36cf29d7a4c..4a79f1c5c07b1 100644 --- a/extensions/markdown-basics/language-configuration.json +++ b/extensions/markdown-basics/language-configuration.json @@ -79,6 +79,10 @@ [ "\"", "\"" + ], + [ + "<", + ">" ] ], "folding": { From 0152062c6eeae231932c592edd45f9d68d0b393d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 22 Jan 2024 15:02:28 -0800 Subject: [PATCH 225/333] fix punctuation issues --- .../contrib/chat/browser/actions/chatAccessibilityHelp.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts index 302c4d0c61ba1..6be43b0fcff20 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts @@ -21,11 +21,11 @@ export function getAccessibilityHelpText(accessor: ServicesAccessor, type: 'pane if (type === 'panelChat') { content.push(localize('chat.overview', 'The chat view is comprised of an input box and a request/response list. The input box is used to make requests and the list is used to display responses.')); content.push(localize('chat.requestHistory', 'In the input box, use up and down arrows to navigate your request history. Edit input and use enter or the submit button to run a new request.')); - content.push(openAccessibleViewKeybinding ? localize('chat.inspectResponse', 'In the input box, inspect the last response in the accessible view via {0}', openAccessibleViewKeybinding) : localize('chat.inspectResponseNoKb', 'With the input box focused, inspect the last response in the accessible view via the Open Accessible View command, which is currently not triggerable by a keybinding.')); + content.push(openAccessibleViewKeybinding ? localize('chat.inspectResponse', 'In the input box, inspect the last response in the accessible view {0}', openAccessibleViewKeybinding) : localize('chat.inspectResponseNoKb', 'With the input box focused, inspect the last response in the accessible view via the Open Accessible View command, which is currently not triggerable by a keybinding.')); content.push(localize('chat.followUp', 'In the input box, navigate to the suggested follow up question (Shift+Tab) and press Enter to run it.')); content.push(localize('chat.announcement', 'Chat responses will be announced as they come in. A response will indicate the number of code blocks, if any, and then the rest of the response.')); content.push(descriptionForCommand('chat.action.focus', localize('workbench.action.chat.focus', 'To focus the chat request/response list, which can be navigated with up and down arrows, invoke the Focus Chat command ({0}).',), localize('workbench.action.chat.focusNoKb', 'To focus the chat request/response list, which can be navigated with up and down arrows, invoke The Focus Chat List command, which is currently not triggerable by a keybinding.'), keybindingService)); - content.push(descriptionForCommand('workbench.action.chat.focusInput', localize('workbench.action.chat.focusInput', 'To focus the input box for chat requests, invoke the Focus Chat Input command ({0})'), localize('workbench.action.interactiveSession.focusInputNoKb', 'To focus the input box for chat requests, invoke the Focus Chat Input command, which is currently not triggerable by a keybinding.'), keybindingService)); + content.push(descriptionForCommand('workbench.action.chat.focusInput', localize('workbench.action.chat.focusInput', 'To focus the input box for chat requests, invoke the Focus Chat Input command ({0}).'), localize('workbench.action.interactiveSession.focusInputNoKb', 'To focus the input box for chat requests, invoke the Focus Chat Input command, which is currently not triggerable by a keybinding.'), keybindingService)); content.push(descriptionForCommand('workbench.action.chat.nextCodeBlock', localize('workbench.action.chat.nextCodeBlock', 'To focus the next code block within a response, invoke the Chat: Next Code Block command ({0}).'), localize('workbench.action.chat.nextCodeBlockNoKb', 'To focus the next code block within a response, invoke the Chat: Next Code Block command, which is currently not triggerable by a keybinding.'), keybindingService)); content.push(descriptionForCommand('workbench.action.chat.nextFileTree', localize('workbench.action.chat.nextFileTree', 'To focus the next file tree within a response, invoke the Chat: Next File Tree command ({0}).'), localize('workbench.action.chat.nextFileTreeNoKb', 'To focus the next file tree within a response, invoke the Chat: Next File Tree command, which is currently not triggerable by a keybinding.'), keybindingService)); content.push(descriptionForCommand('workbench.action.chat.clear', localize('workbench.action.chat.clear', 'To clear the request/response list, invoke the Chat Clear command ({0}).'), localize('workbench.action.chat.clearNoKb', 'To clear the request/response list, invoke the Chat Clear command, which is currently not triggerable by a keybinding.'), keybindingService)); @@ -38,7 +38,7 @@ export function getAccessibilityHelpText(accessor: ServicesAccessor, type: 'pane if (upHistoryKeybinding && downHistoryKeybinding) { content.push(localize('inlineChat.requestHistory', 'In the input box, use {0} and {1} to navigate your request history. Edit input and use enter or the submit button to run a new request.', upHistoryKeybinding, downHistoryKeybinding)); } - content.push(openAccessibleViewKeybinding ? localize('inlineChat.inspectResponse', 'In the input box, inspect the response in the accessible view via {0}', openAccessibleViewKeybinding) : localize('inlineChat.inspectResponseNoKb', 'With the input box focused, inspect the response in the accessible view via the Open Accessible View command, which is currently not triggerable by a keybinding.')); + content.push(openAccessibleViewKeybinding ? localize('inlineChat.inspectResponse', 'In the input box, inspect the response in the accessible view {0}.', openAccessibleViewKeybinding) : localize('inlineChat.inspectResponseNoKb', 'With the input box focused, inspect the response in the accessible view via the Open Accessible View command, which is currently not triggerable by a keybinding.')); content.push(localize('inlineChat.contextActions', "Context menu actions may run a request prefixed with a /. Type / to discover such ready-made commands.")); content.push(localize('inlineChat.fix', "If a fix action is invoked, a response will indicate the problem with the current code. A diff editor will be rendered and can be reached by tabbing.")); const diffReviewKeybinding = keybindingService.lookupKeybinding(AccessibleDiffViewerNext.id)?.getAriaLabel(); From a6c109418c60054224a1d6a51f1d3d64b0c20db4 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Mon, 22 Jan 2024 15:29:12 -0800 Subject: [PATCH 226/333] Add setting for nb sticky indent mode (#203085) add setting for nb sticky indent mode --- .../interactive/browser/interactiveEditor.ts | 2 +- .../notebook/browser/notebook.contribution.ts | 15 ++++++-- .../notebook/browser/notebookOptions.ts | 35 ++++++++++++------ .../viewParts/notebookEditorStickyScroll.ts | 36 ++++++++++++------- .../contrib/notebook/common/notebookCommon.ts | 3 +- 5 files changed, 65 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts index b15b5a16a66da..f422393ef5f8f 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts @@ -160,7 +160,7 @@ export class InteractiveEditor extends EditorPane { this._editorOptions = this._computeEditorOptions(); } })); - this._notebookOptions = new NotebookOptions(configurationService, notebookExecutionStateService, true, { cellToolbarInteraction: 'hover', globalToolbar: true, stickyScroll: false, dragAndDropEnabled: false }); + this._notebookOptions = new NotebookOptions(configurationService, notebookExecutionStateService, true, { cellToolbarInteraction: 'hover', globalToolbar: true, stickyScrollEnabled: false, dragAndDropEnabled: false }); this._editorMemento = this.getEditorMemento(editorGroupService, textResourceConfigurationService, INTERACTIVE_EDITOR_VIEW_STATE_PREFERENCE_KEY); codeEditorService.registerDecorationType('interactive-decoration', DECORATION_KEY, {}); diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 6a7b115bb627a..ce28e166b21bb 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -872,12 +872,23 @@ configurationRegistry.registerConfiguration({ default: true, tags: ['notebookLayout'] }, - [NotebookSetting.stickyScroll]: { - description: nls.localize('notebook.stickyScroll.description', "Experimental. Control whether to render notebook Sticky Scroll headers in the notebook editor."), + [NotebookSetting.stickyScrollEnabled]: { + description: nls.localize('notebook.stickyScrollEnabled.description', "Experimental. Control whether to render notebook Sticky Scroll headers in the notebook editor."), type: 'boolean', default: false, tags: ['notebookLayout'] }, + [NotebookSetting.stickyScrollMode]: { + description: nls.localize('notebook.stickyScrollMode.description', "Control whether nested sticky lines appear to stack flat or indented."), + type: 'string', + enum: ['flat', 'indented'], + enumDescriptions: [ + nls.localize('notebook.stickyScrollMode.flat', "Nested sticky lines appear flat."), + nls.localize('notebook.stickyScrollMode.indented', "Nested sticky lines appear indented."), + ], + default: 'flat', + tags: ['notebookLayout'] + }, [NotebookSetting.consolidatedOutputButton]: { description: nls.localize('notebook.consolidatedOutputButton.description', "Control whether outputs action should be rendered in the output toolbar."), type: 'boolean', diff --git a/src/vs/workbench/contrib/notebook/browser/notebookOptions.ts b/src/vs/workbench/contrib/notebook/browser/notebookOptions.ts index eb4bbe768ac5a..7530b8e91f914 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookOptions.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookOptions.ts @@ -41,7 +41,8 @@ export interface NotebookDisplayOptions { insertToolbarPosition: 'betweenCells' | 'notebookToolbar' | 'both' | 'hidden'; insertToolbarAlignment: 'left' | 'center'; globalToolbar: boolean; - stickyScroll: boolean; + stickyScrollEnabled: boolean; + stickyScrollMode: 'flat' | 'indented'; consolidatedOutputButton: boolean; consolidatedRunButton: boolean; showFoldingControls: 'always' | 'never' | 'mouseover'; @@ -92,7 +93,8 @@ export interface NotebookOptionsChangeEvent { readonly insertToolbarPosition?: boolean; readonly insertToolbarAlignment?: boolean; readonly globalToolbar?: boolean; - readonly stickyScroll?: boolean; + readonly stickyScrollEnabled?: boolean; + readonly stickyScrollMode?: boolean; readonly showFoldingControls?: boolean; readonly consolidatedOutputButton?: boolean; readonly consolidatedRunButton?: boolean; @@ -139,12 +141,13 @@ export class NotebookOptions extends Disposable { private readonly configurationService: IConfigurationService, private readonly notebookExecutionStateService: INotebookExecutionStateService, private isReadonly: boolean, - private readonly overrides?: { cellToolbarInteraction: string; globalToolbar: boolean; stickyScroll: boolean; dragAndDropEnabled: boolean } + private readonly overrides?: { cellToolbarInteraction: string; globalToolbar: boolean; stickyScrollEnabled: boolean; dragAndDropEnabled: boolean } ) { super(); const showCellStatusBar = this.configurationService.getValue(NotebookSetting.showCellStatusBar); const globalToolbar = overrides?.globalToolbar ?? this.configurationService.getValue(NotebookSetting.globalToolbar) ?? true; - const stickyScroll = overrides?.stickyScroll ?? this.configurationService.getValue(NotebookSetting.stickyScroll) ?? false; + const stickyScrollEnabled = overrides?.stickyScrollEnabled ?? this.configurationService.getValue(NotebookSetting.stickyScrollEnabled) ?? false; + const stickyScrollMode = this._computeStickyScrollModeOption(); const consolidatedOutputButton = this.configurationService.getValue(NotebookSetting.consolidatedOutputButton) ?? true; const consolidatedRunButton = this.configurationService.getValue(NotebookSetting.consolidatedRunButton) ?? false; const dragAndDropEnabled = overrides?.dragAndDropEnabled ?? this.configurationService.getValue(NotebookSetting.dragAndDropEnabled) ?? true; @@ -220,7 +223,8 @@ export class NotebookOptions extends Disposable { collapsedIndicatorHeight: 28, showCellStatusBar, globalToolbar, - stickyScroll, + stickyScrollEnabled, + stickyScrollMode, consolidatedOutputButton, consolidatedRunButton, dragAndDropEnabled, @@ -343,7 +347,8 @@ export class NotebookOptions extends Disposable { const insertToolbarPosition = e.affectsConfiguration(NotebookSetting.insertToolbarLocation); const insertToolbarAlignment = e.affectsConfiguration(NotebookSetting.experimentalInsertToolbarAlignment); const globalToolbar = e.affectsConfiguration(NotebookSetting.globalToolbar); - const stickyScroll = e.affectsConfiguration(NotebookSetting.stickyScroll); + const stickyScrollEnabled = e.affectsConfiguration(NotebookSetting.stickyScrollEnabled); + const stickyScrollMode = e.affectsConfiguration(NotebookSetting.stickyScrollMode); const consolidatedOutputButton = e.affectsConfiguration(NotebookSetting.consolidatedOutputButton); const consolidatedRunButton = e.affectsConfiguration(NotebookSetting.consolidatedRunButton); const showFoldingControls = e.affectsConfiguration(NotebookSetting.showFoldingControls); @@ -369,7 +374,8 @@ export class NotebookOptions extends Disposable { && !insertToolbarPosition && !insertToolbarAlignment && !globalToolbar - && !stickyScroll + && !stickyScrollEnabled + && !stickyScrollMode && !consolidatedOutputButton && !consolidatedRunButton && !showFoldingControls @@ -426,8 +432,12 @@ export class NotebookOptions extends Disposable { configuration.globalToolbar = this.configurationService.getValue(NotebookSetting.globalToolbar) ?? true; } - if (stickyScroll && this.overrides?.stickyScroll === undefined) { - configuration.stickyScroll = this.configurationService.getValue(NotebookSetting.stickyScroll) ?? false; + if (stickyScrollEnabled && this.overrides?.stickyScrollEnabled === undefined) { + configuration.stickyScrollEnabled = this.configurationService.getValue(NotebookSetting.stickyScrollEnabled) ?? false; + } + + if (stickyScrollMode) { + configuration.stickyScrollMode = this.configurationService.getValue<'flat' | 'indented'>(NotebookSetting.stickyScrollMode) ?? 'flat'; } if (consolidatedOutputButton) { @@ -499,7 +509,8 @@ export class NotebookOptions extends Disposable { insertToolbarPosition, insertToolbarAlignment, globalToolbar, - stickyScroll, + stickyScrollEnabled, + stickyScrollMode, showFoldingControls, consolidatedOutputButton, consolidatedRunButton, @@ -534,6 +545,10 @@ export class NotebookOptions extends Disposable { return this.configurationService.getValue<'border' | 'gutter'>(NotebookSetting.focusIndicator) ?? 'gutter'; } + private _computeStickyScrollModeOption() { + return this.configurationService.getValue<'flat' | 'indented'>(NotebookSetting.stickyScrollMode) ?? 'flat'; + } + getCellCollapseDefault(): NotebookCellDefaultCollapseConfig { return this._layoutConfiguration.interactiveWindowCollapseCodeCells === 'never' ? { diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts index e8eb6c5a7ec13..c614ea2ef4654 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts @@ -25,6 +25,7 @@ import { ThemeIcon } from 'vs/base/common/themables'; import { foldingCollapsedIcon, foldingExpandedIcon } from 'vs/editor/contrib/folding/browser/foldingDecorations'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { FoldingController } from 'vs/workbench/contrib/notebook/browser/controller/foldingController'; +import { NotebookOptionsChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; export class ToggleNotebookStickyScroll extends Action2 { @@ -188,13 +189,13 @@ export class NotebookStickyScroll extends Disposable { ) { super(); - if (this.notebookEditor.notebookOptions.getDisplayOptions().stickyScroll) { + if (this.notebookEditor.notebookOptions.getDisplayOptions().stickyScrollEnabled) { this.init(); } this._register(this.notebookEditor.notebookOptions.onDidChangeOptions((e) => { - if (e.stickyScroll) { - this.updateConfig(); + if (e.stickyScrollEnabled || e.stickyScrollMode) { + this.updateConfig(e); } })); @@ -211,20 +212,26 @@ export class NotebookStickyScroll extends Disposable { }); } - private updateConfig() { - if (this.notebookEditor.notebookOptions.getDisplayOptions().stickyScroll) { - this.init(); - } else { - this._disposables.clear(); - this.disposeCurrentStickyLines(); - DOM.clearNode(this.domNode); - this.updateDisplay(); + private updateConfig(e: NotebookOptionsChangeEvent) { + if (e.stickyScrollEnabled) { + if (this.notebookEditor.notebookOptions.getDisplayOptions().stickyScrollEnabled) { + this.init(); + } else { + this._disposables.clear(); + this.notebookOutline.dispose(); + this.disposeCurrentStickyLines(); + DOM.clearNode(this.domNode); + this.updateDisplay(); + } + } else if (e.stickyScrollMode && this.notebookEditor.notebookOptions.getDisplayOptions().stickyScrollEnabled) { + this.updateContent(computeContent(this.notebookEditor, this.notebookCellList, this.filteredOutlineEntries, this.getCurrentStickyHeight())); } } private init() { this.notebookOutline.init(); this.filteredOutlineEntries = this.notebookOutline.entries.filter(entry => entry.level !== 7); + this.updateContent(computeContent(this.notebookEditor, this.notebookCellList, this.filteredOutlineEntries, this.getCurrentStickyHeight())); this._disposables.add(this.notebookOutline.onDidChange(() => { this.filteredOutlineEntries = this.notebookOutline.entries.filter(entry => entry.level !== 7); @@ -358,7 +365,11 @@ export class NotebookStickyScroll extends Disposable { static createStickyElement(entry: OutlineEntry, notebookEditor: INotebookEditor) { const stickyElement = document.createElement('div'); stickyElement.classList.add('notebook-sticky-scroll-element'); - stickyElement.style.paddingLeft = NotebookStickyLine.getParentCount(entry) * 10 + 'px'; + + const indentMode = notebookEditor.notebookOptions.getLayoutConfiguration().stickyScrollMode; + if (indentMode === 'indented') { + stickyElement.style.paddingLeft = NotebookStickyLine.getParentCount(entry) * 10 + 'px'; + } let isCollapsed = false; if (entry.cell.cellKind === CellKind.Markup) { @@ -386,6 +397,7 @@ export class NotebookStickyScroll extends Disposable { override dispose() { this._disposables.dispose(); this.disposeCurrentStickyLines(); + this.notebookOutline.dispose(); super.dispose(); } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index d24e108e6d377..c673ae8ad79a1 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -913,7 +913,8 @@ export const NotebookSetting = { focusIndicator: 'notebook.cellFocusIndicator', insertToolbarLocation: 'notebook.insertToolbarLocation', globalToolbar: 'notebook.globalToolbar', - stickyScroll: 'notebook.stickyScroll.enabled', + stickyScrollEnabled: 'notebook.stickyScroll.enabled', + stickyScrollMode: 'notebook.stickyScroll.mode', undoRedoPerCell: 'notebook.undoRedoPerCell', consolidatedOutputButton: 'notebook.consolidatedOutputButton', showFoldingControls: 'notebook.showFoldingControls', From fc57e0e1d44497ee0194ea6ed22cddcfbb78f28d Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Mon, 22 Jan 2024 15:40:55 -0800 Subject: [PATCH 227/333] issue reporter command enhancements (#203080) * groundwork for command, only strings for now * more groundwork for uri * added uri and clearing suport when switching extensions * cleanup --- .../issue/issueReporterService.ts | 67 ++++++++++++++++--- src/vs/platform/issue/common/issue.ts | 12 +++- 2 files changed, 68 insertions(+), 11 deletions(-) diff --git a/src/vs/code/electron-sandbox/issue/issueReporterService.ts b/src/vs/code/electron-sandbox/issue/issueReporterService.ts index 896747af2792b..1924ff872a08e 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterService.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterService.ts @@ -248,8 +248,13 @@ export class IssueReporter extends Disposable { private async updateIssueReporterUri(extension: IssueReporterExtensionData): Promise { try { - const uri = await this.issueMainService.$getIssueReporterUri(extension.id); - extension.bugsUrl = uri.toString(true); + if (extension.command?.uri) { + extension.bugsUrl = extension.command.uri; + } else { + const uri = await this.issueMainService.$getIssueReporterUri(extension.id); + extension.bugsUrl = uri.toString(true); + } + } catch (e) { extension.hasIssueUriRequestHandler = false; // The issue handler failed so fall back to old issue reporter experience. @@ -806,6 +811,13 @@ export class IssueReporter extends Disposable { show(extensionDataBlock); } + if (fileOnExtension && selectedExtension?.command?.data) { + const data = selectedExtension?.command?.data; + (extensionDataTextArea as HTMLElement).innerText = data.toString(); + (extensionDataTextArea as HTMLTextAreaElement).readOnly = true; + show(extensionDataBlock); + } + if (issueType === IssueType.Bug) { if (!fileOnMarketplace) { show(blockContainer); @@ -893,8 +905,9 @@ export class IssueReporter extends Disposable { } private async createIssue(): Promise { - const hasUri = this.issueReporterModel.getData().selectedExtension?.hasIssueUriRequestHandler; - const hasData = this.issueReporterModel.getData().selectedExtension?.hasIssueDataProviders; + const selectedExtension = this.issueReporterModel.getData().selectedExtension; + const hasUri = selectedExtension?.hasIssueUriRequestHandler; + const hasData = selectedExtension?.hasIssueDataProviders; // Short circuit if the extension provides a custom issue handler if (hasUri && !hasData) { const url = this.getExtensionBugsUrl(); @@ -939,10 +952,15 @@ export class IssueReporter extends Disposable { const issueTitle = (this.getElementById('issue-title')).value; const issueBody = this.issueReporterModel.serialize(); - const issueUrl = hasUri ? this.getExtensionBugsUrl() : this.getIssueUrl(); + let issueUrl = hasUri ? this.getExtensionBugsUrl() : this.getIssueUrl(); if (!issueUrl) { return false; } + + if (selectedExtension?.command?.uri) { + issueUrl = selectedExtension.command.uri; + } + const gitHubDetails = this.parseGitHubUrl(issueUrl); if (this.configuration.data.githubAccessToken && gitHubDetails) { return this.submitToGitHub(issueTitle, issueBody, gitHubDetails); @@ -1145,17 +1163,22 @@ export class IssueReporter extends Disposable { } this.addEventListener('extension-selector', 'change', async (e: Event) => { + this.clearExtensionData(); const selectedExtensionId = (e.target).value; const extensions = this.issueReporterModel.getData().allExtensions; const matches = extensions.filter(extension => extension.id === selectedExtensionId); if (matches.length) { + this.issueReporterModel.update({ selectedExtension: matches[0] }); + const selectedExtension = this.issueReporterModel.getData().selectedExtension; + if (selectedExtension) { + selectedExtension.command = undefined; + } this.updateExtensionStatus(matches[0]); } else { this.issueReporterModel.update({ selectedExtension: undefined }); this.clearSearchResults(); this.validateSelectedExtension(); } - }); } @@ -1164,8 +1187,36 @@ export class IssueReporter extends Disposable { }); } + private clearExtensionData(): void { + this.issueReporterModel.update({ extensionData: undefined }); + this.configuration.data.command = undefined; + } + private async updateExtensionStatus(extension: IssueReporterExtensionData) { this.issueReporterModel.update({ selectedExtension: extension }); + if (this.configuration.data.command) { + const template = this.configuration.data.command.template; + if (template) { + const descriptionTextArea = this.getElementById('description')!; + const descriptionText = (descriptionTextArea as HTMLTextAreaElement).value; + if (descriptionText === '' || !descriptionText.includes(template.toString())) { + const fullTextArea = descriptionText + (descriptionText === '' ? '' : '\n') + template.toString(); + (descriptionTextArea as HTMLTextAreaElement).value = fullTextArea; + this.issueReporterModel.update({ issueDescription: fullTextArea }); + } + } + const data = this.configuration.data.command.data; + if (data) { + const extensionDataBlock = mainWindow.document.querySelector('.block-extension-data')!; + show(extensionDataBlock); + this.issueReporterModel.update({ extensionData: data }); + } + + const uri = this.configuration.data.command.uri; + if (uri) { + this.updateIssueReporterUri(extension); + } + } // if extension does not have provider/handles, will check for either. If extension is already active, IPC will return [false, false] and will proceed as normal. if (!extension.hasIssueDataProviders && !extension.hasIssueUriRequestHandler) { @@ -1198,8 +1249,6 @@ export class IssueReporter extends Disposable { // then update this this.updateIssueReporterUri(extension); - // reset to false so issue url is updated, but won't be affected later. - // extension.hasIssueUriRequestHandler = false; } else if (extension.hasIssueUriRequestHandler) { this.updateIssueReporterUri(extension); } else if (extension.hasIssueDataProviders) { @@ -1222,7 +1271,7 @@ export class IssueReporter extends Disposable { this.removeLoading(iconElement); } else { this.validateSelectedExtension(); - this.issueReporterModel.update({ extensionData: undefined }); + this.issueReporterModel.update({ extensionData: extension.command?.data ?? undefined }); const title = (this.getElementById('issue-title')).value; this.searchExtensionIssues(title); } diff --git a/src/vs/platform/issue/common/issue.ts b/src/vs/platform/issue/common/issue.ts index 93a1f41262338..8d9adda1019e1 100644 --- a/src/vs/platform/issue/common/issue.ts +++ b/src/vs/platform/issue/common/issue.ts @@ -57,7 +57,11 @@ export interface IssueReporterExtensionData { extensionTemplate?: string; hasIssueUriRequestHandler?: boolean; hasIssueDataProviders?: boolean; - command?: boolean; + command?: { + data?: string; + template?: string; + uri?: string; + }; } export interface IssueReporterData extends WindowData { @@ -71,7 +75,11 @@ export interface IssueReporterData extends WindowData { githubAccessToken: string; readonly issueTitle?: string; readonly issueBody?: string; - readonly command?: boolean; + command?: { + data?: string; + template?: string; + uri?: string; + }; } export interface ISettingSearchResult { From 800e60abdfb1675a59520bac6a9473bc380d49ea Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Mon, 22 Jan 2024 16:38:55 -0800 Subject: [PATCH 228/333] Check if should sync model before extension host (#203096) Fix #199385 -- check if should sync model before eh --- .../editor/contrib/wordHighlighter/browser/wordHighlighter.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts index f7bd8e2eededb..10b5069c80c2d 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts @@ -24,7 +24,7 @@ import { IDiffEditor, IEditorContribution, IEditorDecorationsCollection } from ' import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; import { DocumentHighlight, DocumentHighlightKind, DocumentHighlightProvider, MultiDocumentHighlightProvider } from 'vs/editor/common/languages'; -import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { IModelDeltaDecoration, ITextModel, shouldSynchronizeModel } from 'vs/editor/common/model'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { getHighlightDecorationOptions } from 'vs/editor/contrib/wordHighlighter/browser/highlightDecorations'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -65,6 +65,8 @@ export function getOccurrencesAcrossMultipleModels(registry: LanguageFeatureRegi // (good = none empty array) return first | null | undefined>(orderedByScore.map(provider => () => { const filteredModels = otherModels.filter(otherModel => { + return shouldSynchronizeModel(otherModel); + }).filter(otherModel => { return score(provider.selector, otherModel.uri, otherModel.getLanguageId(), true, undefined, undefined) > 0; }); From c1cbd4909947fd3b3ba5212ea22f550be8d7cc67 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 23 Jan 2024 09:56:18 +0100 Subject: [PATCH 229/333] Editor scroll position jumps up when returning to editor (#203013) Fixes #202811 --- .../contrib/comments/browser/commentThreadZoneWidget.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts index 4784d1c38c566..e5ae9040d5096 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts @@ -25,6 +25,7 @@ import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { commentThreadStateBackgroundColorVar, commentThreadStateColorVar, getCommentThreadStateBorderColor } from 'vs/workbench/contrib/comments/browser/commentColors'; import { peekViewBorder } from 'vs/editor/contrib/peekView/browser/peekView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll'; function getCommentThreadWidgetStateColor(thread: languages.CommentThreadState | undefined, theme: IColorTheme): Color | undefined { return getCommentThreadStateBorderColor(thread, theme) ?? theme.getColor(peekViewBorder); @@ -453,7 +454,9 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._commentThreadWidget.focusCommentEditor(); } + const capture = StableEditorScrollState.capture(this.editor); this._relayout(computedLinesNumber); + capture.restore(this.editor); } } From 6444de2424eff9630e0e2547de8d38559a45667d Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Tue, 23 Jan 2024 12:07:15 +0100 Subject: [PATCH 230/333] mnemonicTitle --- src/vs/workbench/browser/actions/listCommands.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index 1c84f2f9f3e5b..e4482953dd8a2 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -21,6 +21,7 @@ import { AbstractTree, TreeFindMatchType, TreeFindMode } from 'vs/base/browser/u import { isActiveElement } from 'vs/base/browser/dom'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { localize } from 'vs/nls'; function ensureDOMFocus(widget: ListWidget | undefined): void { // it can happen that one of the commands is executed while @@ -911,7 +912,11 @@ registerAction2(class ToggleStickyScroll extends Action2 { constructor() { super({ id: 'tree.toggleStickyScroll', - title: { value: 'Toggle Tree Sticky Scroll', original: 'Toggle Tree Sticky Scroll' }, + title: { + value: localize('toggleTreeStickyScroll', "Toggle Tree Sticky Scroll"), + mnemonicTitle: localize({ key: 'mitoggleTreeStickyScroll', comment: ['&& denotes a mnemonic'] }, "&&Toggle Tree Sticky Scroll"), + original: 'Toggle Tree Sticky Scroll', + }, category: 'View', f1: true }); From 61a80a31d4d39fb0aff146341e2eae93fd847a88 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Tue, 23 Jan 2024 12:09:40 +0100 Subject: [PATCH 231/333] editor sticky scroll --- .../contrib/stickyScroll/browser/stickyScrollActions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts index ece8daa4e51a5..e50c0f97c5080 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts @@ -21,9 +21,9 @@ export class ToggleStickyScroll extends Action2 { super({ id: 'editor.action.toggleStickyScroll', title: { - value: localize('toggleStickyScroll', "Toggle Sticky Scroll"), - mnemonicTitle: localize({ key: 'mitoggleStickyScroll', comment: ['&& denotes a mnemonic'] }, "&&Toggle Sticky Scroll"), - original: 'Toggle Sticky Scroll', + value: localize('toggleEditorStickyScroll', "Toggle Editor Sticky Scroll"), + mnemonicTitle: localize({ key: 'mitoggleStickyScroll', comment: ['&& denotes a mnemonic'] }, "&&Toggle Editor Sticky Scroll"), + original: 'Toggle Editor Sticky Scroll', }, category: Categories.View, toggled: { From 4b0d700f7a934912e21295dfd16d82c0cb5fd13d Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 23 Jan 2024 13:00:55 +0100 Subject: [PATCH 232/333] [json/css/html] Update dependencies (#203084) * update json/css/html * fix typing * revert to vscode-languageclient@9.0.1 --- extensions/css-language-features/package.json | 2 +- .../css-language-features/server/package.json | 6 +- .../server/src/languageModelCache.ts | 2 +- .../css-language-features/server/yarn.lock | 70 +++++++++++-------- extensions/css-language-features/yarn.lock | 8 +-- .../html-language-features/package.json | 2 +- .../server/package.json | 8 +-- .../html-language-features/server/yarn.lock | 63 +++++++++-------- extensions/html-language-features/yarn.lock | 8 +-- .../json-language-features/package.json | 2 +- .../server/package.json | 8 +-- .../json-language-features/server/yarn.lock | 65 +++++++++-------- extensions/json-language-features/yarn.lock | 8 +-- 13 files changed, 137 insertions(+), 115 deletions(-) diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index e105c0090eeaa..06e58ee064d84 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -994,7 +994,7 @@ ] }, "dependencies": { - "vscode-languageclient": "^9.0.1", + "vscode-languageclient": "9.0.1", "vscode-uri": "^3.0.8" }, "devDependencies": { diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 523e66333bc0d..b5ff75c76860b 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -10,9 +10,9 @@ "main": "./out/node/cssServerMain", "browser": "./dist/browser/cssServerMain", "dependencies": { - "@vscode/l10n": "^0.0.16", - "vscode-css-languageservice": "^6.2.11", - "vscode-languageserver": "^9.0.1", + "@vscode/l10n": "^0.0.18", + "vscode-css-languageservice": "^6.2.12", + "vscode-languageserver": "^9.0.2-next.1", "vscode-uri": "^3.0.8" }, "devDependencies": { diff --git a/extensions/css-language-features/server/src/languageModelCache.ts b/extensions/css-language-features/server/src/languageModelCache.ts index f39eada6f445b..df498ed9f20e6 100644 --- a/extensions/css-language-features/server/src/languageModelCache.ts +++ b/extensions/css-language-features/server/src/languageModelCache.ts @@ -15,7 +15,7 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime let languageModels: { [uri: string]: { version: number; languageId: string; cTime: number; languageModel: T } } = {}; let nModels = 0; - let cleanupInterval: NodeJS.Timer | undefined = undefined; + let cleanupInterval: NodeJS.Timeout | undefined = undefined; if (cleanupIntervalTimeInSec > 0) { cleanupInterval = setInterval(() => { const cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000; diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 28a355c3b01e0..2e2a0c9a6ca99 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -8,37 +8,44 @@ integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== "@types/node@18.x": - version "18.15.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" - integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== - -"@vscode/l10n@^0.0.16": - version "0.0.16" - resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.16.tgz#f075db346d0b08419a12540171b230bd803c42be" - integrity sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg== - -vscode-css-languageservice@^6.2.11: - version "6.2.11" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.2.11.tgz#40de8b34adb6d68ee96795ffb34e34d99fc26801" - integrity sha512-qn49Wa6K94LnizpVxmlYrcPf1Cb36gq1nNueW0COhi4shylXBzET5wuDbH8ZWQlJD0HM5Mmnn7WE9vQVVs+ULA== + version "18.19.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.8.tgz#c1e42b165e5a526caf1f010747e0522cb2c9c36a" + integrity sha512-g1pZtPhsvGVTwmeVoexWZLTQaOvXwoSq//pTL0DHeNzUDrFnir4fgETdhjhIxjVnN+hKOuh98+E1eMLnUXstFg== dependencies: - "@vscode/l10n" "^0.0.16" + undici-types "~5.26.4" + +"@vscode/l10n@^0.0.18": + version "0.0.18" + resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.18.tgz#916d3a5e960dbab47c1c56f58a7cb5087b135c95" + integrity sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +vscode-css-languageservice@^6.2.12: + version "6.2.12" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.2.12.tgz#f8f9f335fb4b433f557c51c62e687b4f62c0c786" + integrity sha512-PS9r7HgNjqzRl3v91sXpCyZPc8UDotNo6gntFNtGCKPhGA9Frk7g/VjX1Mbv3F00pn56D+rxrFzR9ep4cawOgA== + dependencies: + "@vscode/l10n" "^0.0.18" vscode-languageserver-textdocument "^1.0.11" vscode-languageserver-types "3.17.5" vscode-uri "^3.0.8" -vscode-jsonrpc@8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" - integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== +vscode-jsonrpc@8.2.1-next.1: + version "8.2.1-next.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1-next.1.tgz#52e1091907b56759114fabac803b18c44a48f2a9" + integrity sha512-L+DYtdUtqUXGpyMgHqer6IBKvFFhl/1ToiMmCmG85LYHuuX0jllHMz77MYt0RicakoYY+Lq1yLK6Qj3YBqgzDQ== -vscode-languageserver-protocol@3.17.5: - version "3.17.5" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz#864a8b8f390835572f4e13bd9f8313d0e3ac4bea" - integrity sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg== +vscode-languageserver-protocol@3.17.6-next.1: + version "3.17.6-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.1.tgz#5d87f7f708667cf04dbefb5c860901df7d01ebc1" + integrity sha512-2npXUc8oe/fb9Bjcwm2HTWYZXyCbW4NTo7jkOrEciGO+/LfWbSMgqZ6PwKWgqUkgCbkPxQHNjoMqr9ol/Ehjgg== dependencies: - vscode-jsonrpc "8.2.0" - vscode-languageserver-types "3.17.5" + vscode-jsonrpc "8.2.1-next.1" + vscode-languageserver-types "3.17.6-next.1" vscode-languageserver-textdocument@^1.0.11: version "1.0.11" @@ -50,12 +57,17 @@ vscode-languageserver-types@3.17.5: resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a" integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== -vscode-languageserver@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz#500aef82097eb94df90d008678b0b6b5f474015b" - integrity sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g== +vscode-languageserver-types@3.17.6-next.1: + version "3.17.6-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.1.tgz#a3d2006d52f7d4026ea67668113ec16c73cd8f1d" + integrity sha512-7xVc/xLtNhKuCKX0mINT6mFUrUuRz0EinhwPGT8Gtsv2hlo+xJb5NKbiGailcWa1/T5e4dr5Pb2MfGchHreHAA== + +vscode-languageserver@^9.0.2-next.1: + version "9.0.2-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-9.0.2-next.1.tgz#cc9bbd66716346aa761e5bafa19d64559ab4e030" + integrity sha512-xySldxoHIcKXtxoI0LqRX3QcTdOVFt1SeHV0hyPq28p7xGPqWxUPcmTcfIqYdHefXG22nd8DQbGWOEe52yu08A== dependencies: - vscode-languageserver-protocol "3.17.5" + vscode-languageserver-protocol "3.17.6-next.1" vscode-uri@^3.0.8: version "3.0.8" diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index 88aff84e4ded2..794ee50acbead 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -27,9 +27,9 @@ lru-cache@^6.0.0: yallist "^4.0.0" minimatch@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" - integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" @@ -45,7 +45,7 @@ vscode-jsonrpc@8.2.0: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== -vscode-languageclient@^9.0.1: +vscode-languageclient@9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz#cdfe20267726c8d4db839dc1e9d1816e1296e854" integrity sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA== diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 58b9cdfdb2add..347af8182737c 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -259,7 +259,7 @@ }, "dependencies": { "@vscode/extension-telemetry": "^0.9.0", - "vscode-languageclient": "^9.0.1", + "vscode-languageclient": "9.0.1", "vscode-uri": "^3.0.8" }, "devDependencies": { diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 25b8024bd181c..13727939f3c8b 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,10 +9,10 @@ }, "main": "./out/node/htmlServerMain", "dependencies": { - "@vscode/l10n": "^0.0.16", - "vscode-css-languageservice": "^6.2.10", - "vscode-html-languageservice": "^5.1.1", - "vscode-languageserver": "^9.0.1", + "@vscode/l10n": "^0.0.18", + "vscode-css-languageservice": "^6.2.12", + "vscode-html-languageservice": "^5.1.2", + "vscode-languageserver": "^9.0.2-next.1", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 6c052559b50e0..ba17b14513f46 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -12,43 +12,43 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== -"@vscode/l10n@^0.0.16": - version "0.0.16" - resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.16.tgz#f075db346d0b08419a12540171b230bd803c42be" - integrity sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg== +"@vscode/l10n@^0.0.18": + version "0.0.18" + resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.18.tgz#916d3a5e960dbab47c1c56f58a7cb5087b135c95" + integrity sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ== -vscode-css-languageservice@^6.2.10: - version "6.2.10" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.2.10.tgz#ba114d92d634df7b45f572a6eaaccd29cbde5d9d" - integrity sha512-sYUZPku4mQ06AWGCbMyjv2tdR6juBW6hTbVPFwbJvNVzdtEfBioQOgkdXg7yMJNWnXkvWSU1FL2kb4Vxu5Cdyw== +vscode-css-languageservice@^6.2.12: + version "6.2.12" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.2.12.tgz#f8f9f335fb4b433f557c51c62e687b4f62c0c786" + integrity sha512-PS9r7HgNjqzRl3v91sXpCyZPc8UDotNo6gntFNtGCKPhGA9Frk7g/VjX1Mbv3F00pn56D+rxrFzR9ep4cawOgA== dependencies: - "@vscode/l10n" "^0.0.16" + "@vscode/l10n" "^0.0.18" vscode-languageserver-textdocument "^1.0.11" vscode-languageserver-types "3.17.5" vscode-uri "^3.0.8" -vscode-html-languageservice@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-5.1.1.tgz#8e56f7e11c1e3f4a9d56de0f97badea9296f4e04" - integrity sha512-JenrspIIG/Q+93R6G3L6HdK96itSisMynE0glURqHpQbL3dKAKzdm8L40lAHNkwJeBg+BBPpAshZKv/38onrTQ== +vscode-html-languageservice@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-5.1.2.tgz#8309fa7f319c873af11cf23ddbba4e68d6c42e2c" + integrity sha512-wkWfEx/IIR3s2P5yD4aTGHiOb8IAzFxgkSt1uSC3itJ4oDAm23yG7o0L29JljUdnXDDgLafPAvhv8A2I/8riHw== dependencies: - "@vscode/l10n" "^0.0.16" + "@vscode/l10n" "^0.0.18" vscode-languageserver-textdocument "^1.0.11" vscode-languageserver-types "^3.17.5" vscode-uri "^3.0.8" -vscode-jsonrpc@8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" - integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== +vscode-jsonrpc@8.2.1-next.1: + version "8.2.1-next.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1-next.1.tgz#52e1091907b56759114fabac803b18c44a48f2a9" + integrity sha512-L+DYtdUtqUXGpyMgHqer6IBKvFFhl/1ToiMmCmG85LYHuuX0jllHMz77MYt0RicakoYY+Lq1yLK6Qj3YBqgzDQ== -vscode-languageserver-protocol@3.17.5: - version "3.17.5" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz#864a8b8f390835572f4e13bd9f8313d0e3ac4bea" - integrity sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg== +vscode-languageserver-protocol@3.17.6-next.1: + version "3.17.6-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.1.tgz#5d87f7f708667cf04dbefb5c860901df7d01ebc1" + integrity sha512-2npXUc8oe/fb9Bjcwm2HTWYZXyCbW4NTo7jkOrEciGO+/LfWbSMgqZ6PwKWgqUkgCbkPxQHNjoMqr9ol/Ehjgg== dependencies: - vscode-jsonrpc "8.2.0" - vscode-languageserver-types "3.17.5" + vscode-jsonrpc "8.2.1-next.1" + vscode-languageserver-types "3.17.6-next.1" vscode-languageserver-textdocument@^1.0.11: version "1.0.11" @@ -60,12 +60,17 @@ vscode-languageserver-types@3.17.5, vscode-languageserver-types@^3.17.5: resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a" integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== -vscode-languageserver@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz#500aef82097eb94df90d008678b0b6b5f474015b" - integrity sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g== +vscode-languageserver-types@3.17.6-next.1: + version "3.17.6-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.1.tgz#a3d2006d52f7d4026ea67668113ec16c73cd8f1d" + integrity sha512-7xVc/xLtNhKuCKX0mINT6mFUrUuRz0EinhwPGT8Gtsv2hlo+xJb5NKbiGailcWa1/T5e4dr5Pb2MfGchHreHAA== + +vscode-languageserver@^9.0.2-next.1: + version "9.0.2-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-9.0.2-next.1.tgz#cc9bbd66716346aa761e5bafa19d64559ab4e030" + integrity sha512-xySldxoHIcKXtxoI0LqRX3QcTdOVFt1SeHV0hyPq28p7xGPqWxUPcmTcfIqYdHefXG22nd8DQbGWOEe52yu08A== dependencies: - vscode-languageserver-protocol "3.17.5" + vscode-languageserver-protocol "3.17.6-next.1" vscode-uri@^3.0.8: version "3.0.8" diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index 4d096a226edd0..c7e1dee98acb1 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -129,9 +129,9 @@ lru-cache@^6.0.0: yallist "^4.0.0" minimatch@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" - integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" @@ -147,7 +147,7 @@ vscode-jsonrpc@8.2.0: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== -vscode-languageclient@^9.0.1: +vscode-languageclient@9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz#cdfe20267726c8d4db839dc1e9d1816e1296e854" integrity sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA== diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 1af141703386d..46a7603818a93 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -160,7 +160,7 @@ "dependencies": { "@vscode/extension-telemetry": "^0.9.0", "request-light": "^0.7.0", - "vscode-languageclient": "^9.0.1" + "vscode-languageclient": "9.0.1" }, "devDependencies": { "@types/node": "18.x" diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 9ebfd2c94ad61..bddc40acd347f 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -12,11 +12,11 @@ }, "main": "./out/node/jsonServerMain", "dependencies": { - "@vscode/l10n": "^0.0.16", - "jsonc-parser": "^3.2.0", + "@vscode/l10n": "^0.0.18", + "jsonc-parser": "^3.2.1", "request-light": "^0.7.0", - "vscode-json-languageservice": "^5.3.7", - "vscode-languageserver": "^9.0.1", + "vscode-json-languageservice": "^5.3.9", + "vscode-languageserver": "^9.0.2-next.1", "vscode-uri": "^3.0.8" }, "devDependencies": { diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 45d68ed346884..fc1159b9160e0 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -12,61 +12,66 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== -"@vscode/l10n@^0.0.16": - version "0.0.16" - resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.16.tgz#f075db346d0b08419a12540171b230bd803c42be" - integrity sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg== +"@vscode/l10n@^0.0.18": + version "0.0.18" + resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.18.tgz#916d3a5e960dbab47c1c56f58a7cb5087b135c95" + integrity sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ== -jsonc-parser@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" - integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== +jsonc-parser@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a" + integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA== request-light@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.7.0.tgz#885628bb2f8040c26401ebf258ec51c4ae98ac2a" integrity sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q== -vscode-json-languageservice@^5.3.7: - version "5.3.7" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-5.3.7.tgz#63eec2448e5cfcde2eabce39c331681e6158f45e" - integrity sha512-jdDggN2SLMQw4C/tLr11v6/OK4cMVGy7tbyZRHQvukQ6lcflY3UV+ZMkmwHKCqXz2TmxkjQb536eJW6JMEVeew== +vscode-json-languageservice@^5.3.9: + version "5.3.9" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-5.3.9.tgz#512463ed580237d958df9280b43da9e3b5b621ce" + integrity sha512-0IcymTw0ZYX5Zcx+7KLLwTRvg0FzXUVnM1hrUH+sPhqEX0fHGg2h5UUOSp1f8ydGS7/xxzlFI3TR01yaHs6Y0Q== dependencies: - "@vscode/l10n" "^0.0.16" - jsonc-parser "^3.2.0" + "@vscode/l10n" "^0.0.18" + jsonc-parser "^3.2.1" vscode-languageserver-textdocument "^1.0.11" vscode-languageserver-types "^3.17.5" vscode-uri "^3.0.8" -vscode-jsonrpc@8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" - integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== +vscode-jsonrpc@8.2.1-next.1: + version "8.2.1-next.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1-next.1.tgz#52e1091907b56759114fabac803b18c44a48f2a9" + integrity sha512-L+DYtdUtqUXGpyMgHqer6IBKvFFhl/1ToiMmCmG85LYHuuX0jllHMz77MYt0RicakoYY+Lq1yLK6Qj3YBqgzDQ== -vscode-languageserver-protocol@3.17.5: - version "3.17.5" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz#864a8b8f390835572f4e13bd9f8313d0e3ac4bea" - integrity sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg== +vscode-languageserver-protocol@3.17.6-next.1: + version "3.17.6-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.1.tgz#5d87f7f708667cf04dbefb5c860901df7d01ebc1" + integrity sha512-2npXUc8oe/fb9Bjcwm2HTWYZXyCbW4NTo7jkOrEciGO+/LfWbSMgqZ6PwKWgqUkgCbkPxQHNjoMqr9ol/Ehjgg== dependencies: - vscode-jsonrpc "8.2.0" - vscode-languageserver-types "3.17.5" + vscode-jsonrpc "8.2.1-next.1" + vscode-languageserver-types "3.17.6-next.1" vscode-languageserver-textdocument@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz#0822a000e7d4dc083312580d7575fe9e3ba2e2bf" integrity sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA== -vscode-languageserver-types@3.17.5, vscode-languageserver-types@^3.17.5: +vscode-languageserver-types@3.17.6-next.1: + version "3.17.6-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.1.tgz#a3d2006d52f7d4026ea67668113ec16c73cd8f1d" + integrity sha512-7xVc/xLtNhKuCKX0mINT6mFUrUuRz0EinhwPGT8Gtsv2hlo+xJb5NKbiGailcWa1/T5e4dr5Pb2MfGchHreHAA== + +vscode-languageserver-types@^3.17.5: version "3.17.5" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a" integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== -vscode-languageserver@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz#500aef82097eb94df90d008678b0b6b5f474015b" - integrity sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g== +vscode-languageserver@^9.0.2-next.1: + version "9.0.2-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-9.0.2-next.1.tgz#cc9bbd66716346aa761e5bafa19d64559ab4e030" + integrity sha512-xySldxoHIcKXtxoI0LqRX3QcTdOVFt1SeHV0hyPq28p7xGPqWxUPcmTcfIqYdHefXG22nd8DQbGWOEe52yu08A== dependencies: - vscode-languageserver-protocol "3.17.5" + vscode-languageserver-protocol "3.17.6-next.1" vscode-uri@^3.0.8: version "3.0.8" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index be29a81b717ed..9f29ee2197acc 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -129,9 +129,9 @@ lru-cache@^6.0.0: yallist "^4.0.0" minimatch@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" - integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" @@ -152,7 +152,7 @@ vscode-jsonrpc@8.2.0: resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== -vscode-languageclient@^9.0.1: +vscode-languageclient@9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz#cdfe20267726c8d4db839dc1e9d1816e1296e854" integrity sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA== From 523725a022af3f539131487df0e06bfe0b784b68 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 23 Jan 2024 13:13:55 +0100 Subject: [PATCH 233/333] Run OSS Tool (#203132) --- cglicenses.json | 54 ++--------- cli/ThirdPartyNotices.txt | 182 ++++++++++++++++++++++++++++++++++++-- package.json | 2 +- 3 files changed, 182 insertions(+), 56 deletions(-) diff --git a/cglicenses.json b/cglicenses.json index 3d4e0f804436b..d61164acd3d33 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -545,32 +545,8 @@ ] }, { - "name": "vscode-webview-tools", - "fullLicenseText": [ - "MIT License", - "", - "Copyright (c) 2020 Connor Peet", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the 'Software'), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", - "SOFTWARE." - ] - }, - { + // Reason: missing repository property on package.json + // Issue: https://github.com/microsoft/vscode-v8-heap-tools/issues/14 "name": "@vscode/v8-heap-parser", "fullLicenseText": [ "The code in this package is built upon that in the Chrome devtools", @@ -607,29 +583,11 @@ ] }, { + // Reason: missing copyright statement + // Issue: https://github.com/qiao/heap.js/issues/33 "name": "heap", - "fullLicenseText": [ - "The MIT License", - "", - "Copyright (c) Xueqiao (Joe) Xu", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the 'Software'), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in", - "all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", - "THE SOFTWARE." + "prependLicenseText": [ + "Copyright (c) heap.js authors" ] } ] diff --git a/cli/ThirdPartyNotices.txt b/cli/ThirdPartyNotices.txt index a867935f2b403..6ec82c8b541ad 100644 --- a/cli/ThirdPartyNotices.txt +++ b/cli/ThirdPartyNotices.txt @@ -10657,7 +10657,31 @@ DEALINGS IN THE SOFTWARE. xdg-home 1.0.0 - MIT https://github.com/zeenix/xdg-home -LICENSE-MIT +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- @@ -10679,7 +10703,31 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI zbus 3.13.1 - MIT https://github.com/dbus2/zbus/ -LICENSE-MIT +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- @@ -10687,7 +10735,31 @@ LICENSE-MIT zbus_macros 3.13.1 - MIT https://github.com/dbus2/zbus/ -LICENSE-MIT +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- @@ -10695,7 +10767,31 @@ LICENSE-MIT zbus_names 2.5.1 - MIT https://github.com/dbus2/zbus/ -LICENSE-MIT +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- @@ -10785,7 +10881,31 @@ SOFTWARE. zvariant 3.14.0 - MIT https://github.com/dbus2/zbus/ -LICENSE-MIT +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- @@ -10793,7 +10913,31 @@ LICENSE-MIT zvariant_derive 3.14.0 - MIT https://github.com/dbus2/zbus/ -LICENSE-MIT +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- @@ -10801,5 +10945,29 @@ LICENSE-MIT zvariant_utils 1.0.1 - MIT https://github.com/dbus2/zbus/ -LICENSE-MIT +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. --------------------------------------------------------- \ No newline at end of file diff --git a/package.json b/package.json index c632d597fa2f5..e9cd097d3e846 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.86.0", - "distro": "be7d9acdb535bf4095cbbb28c88cfd05a2a11533", + "distro": "2124c72ef2dcad044b30293f7548a7cc63f113fe", "author": { "name": "Microsoft Corporation" }, From bef67be6c6a811efbbf110e91bf908a37b7d5e3c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 23 Jan 2024 11:20:38 -0300 Subject: [PATCH 234/333] Add basic tooltip to file vars (#203074) --- .../contrib/chat/browser/chatListRenderer.ts | 8 ++----- .../chatMarkdownDecorationsRenderer.ts | 23 ++++++++++++++----- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 7d5eaf14e4a5a..3336d00c7888c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -40,11 +40,9 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { FileKind, FileType } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { WorkbenchCompressibleAsyncDataTree, WorkbenchList } from 'vs/platform/list/browser/listService'; import { ILogService } from 'vs/platform/log/common/log'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IProductService } from 'vs/platform/product/common/productService'; import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles'; import { ColorScheme } from 'vs/platform/theme/common/theme'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -140,9 +138,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer this.codeBlocksByResponseId.delete(element.id))); } - walkTreeAndAnnotateReferenceLinks(result.element, this.keybindingService); + this.instantiationService.invokeFunction(acc => walkTreeAndAnnotateReferenceLinks(acc, result.element)); orderedDisposablesList.reverse().forEach(d => disposables.add(d)); return { diff --git a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts index 8c449efcafe90..6a72ca4a9510d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts @@ -10,33 +10,43 @@ import { basename } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IRange } from 'vs/editor/common/core/range'; import { Location } from 'vs/editor/common/languages'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ILabelService } from 'vs/platform/label/common/label'; import { IChatProgressRenderableResponseContent, IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel'; -import { ChatRequestTextPart, IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; +import { ChatRequestDynamicVariablePart, ChatRequestTextPart, IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { IChatAgentMarkdownContentWithVulnerability, IChatAgentVulnerabilityDetails, IChatContentInlineReference } from 'vs/workbench/contrib/chat/common/chatService'; const variableRefUrl = 'http://_vscodedecoration_'; -export function convertParsedRequestToMarkdown(parsedRequest: IParsedChatRequest): string { +export function convertParsedRequestToMarkdown(accessor: ServicesAccessor, parsedRequest: IParsedChatRequest): string { let result = ''; for (const part of parsedRequest.parts) { if (part instanceof ChatRequestTextPart) { result += part.text; } else { - result += `[${part.text}](${variableRefUrl})`; + const labelService = accessor.get(ILabelService); + const uri = part instanceof ChatRequestDynamicVariablePart && part.data.map(d => d.value).find((d): d is URI => d instanceof URI) + || undefined; + const title = uri ? labelService.getUriLabel(uri, { relative: true }) : ''; + + result += `[${part.text}](${variableRefUrl}${title})`; } } return result; } -export function walkTreeAndAnnotateReferenceLinks(element: HTMLElement, keybindingService: IKeybindingService): void { +export function walkTreeAndAnnotateReferenceLinks(accessor: ServicesAccessor, element: HTMLElement): void { + const keybindingService = accessor.get(IKeybindingService); + element.querySelectorAll('a').forEach(a => { const href = a.getAttribute('data-href'); if (href) { if (href.startsWith(variableRefUrl)) { + const title = href.slice(variableRefUrl.length); a.parentElement!.replaceChild( - renderResourceWidget(a.textContent!), + renderResourceWidget(a.textContent!, title), a); } else if (href.startsWith(contentRefUrl)) { renderFileWidget(href, a); @@ -60,9 +70,10 @@ function injectKeybindingHint(a: HTMLAnchorElement, href: string, keybindingServ } } -function renderResourceWidget(name: string): HTMLElement { +function renderResourceWidget(name: string, title: string): HTMLElement { const container = dom.$('span.chat-resource-widget'); const alias = dom.$('span', undefined, name); + alias.title = title; container.appendChild(alias); return container; } From 09f06adf6519b91ebccf537a8f5f0c1a6048a80a Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Tue, 23 Jan 2024 15:38:39 +0100 Subject: [PATCH 235/333] Don't hide activity actions at top (#203142) dont hide activity actions top --- .../browser/menuEntryActionViewItem.ts | 3 ++- .../browser/parts/titlebar/titlebarActions.ts | 22 ------------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 15abc82b4be63..8374e100d3cb6 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -45,7 +45,8 @@ export function createAndFillInActionBarActions( target: IAction[] | { primary: IAction[]; secondary: IAction[] }, primaryGroup?: string | ((actionGroup: string) => boolean), shouldInlineSubmenu?: (action: SubmenuAction, group: string, groupSize: number) => boolean, - useSeparatorsInPrimaryActions?: boolean): void { + useSeparatorsInPrimaryActions?: boolean +): void { const groups = menu.getActions(options); const isPrimaryAction = typeof primaryGroup === 'string' ? (actionGroup: string) => actionGroup === primaryGroup : primaryGroup; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index f800a48b72d1e..a528f6ab90cb9 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -235,28 +235,6 @@ registerAction2(class ToggleEditorActions extends Action2 { } }); -registerAction2(class ToggleActivityBarActions extends Action2 { - static readonly settingsID = `workbench.activityBar.location`; - constructor() { - - super({ - id: `toggle.${ToggleActivityBarActions.settingsID}`, - title: localize('toggle.activityBarActions', 'Activity Bar Actions'), - toggled: ContextKeyExpr.equals(`config.${ToggleActivityBarActions.settingsID}`, 'top'), - menu: [ - { id: MenuId.TitleBarContext, order: 4, when: ContextKeyExpr.notEquals(`config.${ToggleActivityBarActions.settingsID}`, 'side'), group: '2_config' }, - { id: MenuId.TitleBarTitleContext, order: 4, when: ContextKeyExpr.notEquals(`config.${ToggleActivityBarActions.settingsID}`, 'side'), group: '2_config' } - ] - }); - } - - run(accessor: ServicesAccessor, ...args: any[]): void { - const configService = accessor.get(IConfigurationService); - const oldLocation = configService.getValue(ToggleActivityBarActions.settingsID); - configService.updateValue(ToggleActivityBarActions.settingsID, oldLocation === 'top' ? 'hidden' : 'top'); - } -}); - // --- Toolbar actions --- // export const ACCOUNTS_ACTIVITY_TILE_ACTION: IAction = { From 24f792463434920e387fabd13d458c54b20ba497 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 23 Jan 2024 16:31:25 +0100 Subject: [PATCH 236/333] remove save-block when stashing session (#203164) also restore save-block when unstashing and fix and issues that would prevent stashing fixes https://github.com/microsoft/vscode-copilot/issues/3654 --- .../inlineChat/browser/inlineChatController.ts | 11 ++++++++--- .../inlineChat/browser/inlineChatSavingServiceImpl.ts | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 977f9b71bdf84..ca86cd75143b7 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -852,6 +852,8 @@ export class InlineChatController implements IEditorContribution { assertType(this._strategy); this._sessionStore.clear(); + // only stash sessions that were not unstashed, not "empty", and not interacted with + const shouldStash = !this._session.isUnstashed && !!this._session.lastExchange && this._session.hunkData.size === this._session.hunkData.pending; let undoCancelEdits: IValidEditOperation[] = []; try { undoCancelEdits = this._strategy.cancel(); @@ -862,8 +864,7 @@ export class InlineChatController implements IEditorContribution { } this._stashedSession.clear(); - if (!this._session.isUnstashed && !!this._session.lastExchange && this._session.hunkData.size === this._session.hunkData.pending) { - // only stash sessions that were not unstashed, not "empty", and not interacted with + if (shouldStash) { this._stashedSession.value = this._inlineChatSessionService.stashSession(this._session, this._editor, undoCancelEdits); } else { this._inlineChatSessionService.releaseSession(this._session); @@ -1142,7 +1143,11 @@ export class InlineChatController implements IEditorContribution { } unstashLastSession(): Session | undefined { - return this._stashedSession.value?.unstash(); + const result = this._stashedSession.value?.unstash(); + if (result) { + this._inlineChatSavingService.markChanged(result); + } + return result; } joinCurrentRun(): Promise | undefined { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts index b68c999bd6cda..133923b13fe9e 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts @@ -55,7 +55,7 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService, @ILogService private readonly _logService: ILogService, ) { - this._store.add(_inlineChatSessionService.onDidEndSession(e => { + this._store.add(Event.any(_inlineChatSessionService.onDidEndSession, _inlineChatSessionService.onDidStashSession)(e => { this._sessionData.get(e.session)?.dispose(); })); } From 42c4cb5036b4fe74e8d672e9501d79363a377b09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 08:40:51 -0700 Subject: [PATCH 237/333] Bump actions/cache from 3 to 4 (#202999) Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/basic.yml | 12 ++++++------ .github/workflows/ci.yml | 16 ++++++++-------- .github/workflows/monaco-editor.yml | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index b0f7d8de27c52..8448d05c3b84d 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -38,7 +38,7 @@ jobs: run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - name: Cache node modules id: cacheNodeModules - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: "**/node_modules" key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} @@ -48,7 +48,7 @@ jobs: run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - name: Cache yarn directory if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.yarnCacheDirPath.outputs.dir }} key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} @@ -90,7 +90,7 @@ jobs: run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - name: Cache node modules id: cacheNodeModules - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: "**/node_modules" key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} @@ -100,7 +100,7 @@ jobs: run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - name: Cache yarn directory if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.yarnCacheDirPath.outputs.dir }} key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} @@ -152,7 +152,7 @@ jobs: run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - name: Cache node modules id: cacheNodeModules - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: "**/node_modules" key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} @@ -162,7 +162,7 @@ jobs: run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - name: Cache yarn directory if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.yarnCacheDirPath.outputs.dir }} key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 94a552b5fb8d4..ee204799cf6c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - name: Cache node_modules archive id: cacheNodeModules - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ".build/node_modules_cache" key: "${{ runner.os }}-cacheNodeModulesArchive-${{ steps.nodeModulesCacheKey.outputs.value }}" @@ -49,7 +49,7 @@ jobs: run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - name: Cache yarn directory if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.yarnCacheDirPath.outputs.dir }} key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} @@ -122,7 +122,7 @@ jobs: run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - name: Cache node modules id: cacheNodeModules - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: "**/node_modules" key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} @@ -132,7 +132,7 @@ jobs: run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - name: Cache yarn directory if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.yarnCacheDirPath.outputs.dir }} key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} @@ -193,7 +193,7 @@ jobs: run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - name: Cache node modules id: cacheNodeModules - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: "**/node_modules" key: ${{ runner.os }}-cacheNodeModulesMacOS-${{ steps.nodeModulesCacheKey.outputs.value }} @@ -203,7 +203,7 @@ jobs: run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - name: Cache yarn directory if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.yarnCacheDirPath.outputs.dir }} key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} @@ -265,7 +265,7 @@ jobs: run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - name: Cache node modules id: cacheNodeModules - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: "**/node_modules" key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} @@ -275,7 +275,7 @@ jobs: run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - name: Cache yarn directory if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.yarnCacheDirPath.outputs.dir }} key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} diff --git a/.github/workflows/monaco-editor.yml b/.github/workflows/monaco-editor.yml index c62fd2b2697a6..8c8ae7b743d9e 100644 --- a/.github/workflows/monaco-editor.yml +++ b/.github/workflows/monaco-editor.yml @@ -29,7 +29,7 @@ jobs: run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - name: Cache node modules id: cacheNodeModules - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: "**/node_modules" key: ${{ runner.os }}-cacheNodeModules20-${{ steps.nodeModulesCacheKey.outputs.value }} @@ -40,7 +40,7 @@ jobs: run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - name: Cache yarn directory if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.yarnCacheDirPath.outputs.dir }} key: ${{ runner.os }}-yarnCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} From 39804fae310663346fd29efeb09e68a99bae5e29 Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Tue, 23 Jan 2024 16:45:08 +0100 Subject: [PATCH 238/333] Refactor Enum Value Descriptions (#203170) fix #203157 --- src/vs/workbench/electron-sandbox/desktop.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 7246962aa7505..ffcd7a7a0a5bb 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -240,8 +240,8 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand 'type': 'string', 'enum': ['auto', 'windowed', 'never'], 'markdownEnumDescriptions': [ - localize(`window.customTitleBarVisibility.auto`, "Automatically changes custom titlebar visibility."), - localize(`window.customTitleBarVisibility.windowed`, "Hide custom titlebar in full screen. Automatically changes custom titlebar visibility in windowed."), + localize(`window.customTitleBarVisibility.auto`, "Automatically changes custom title bar visibility."), + localize(`window.customTitleBarVisibility.windowed`, "Hide custom titlebar in full screen. When not in full screen, automatically change custom title bar visibility."), localize(`window.customTitleBarVisibility.never`, "Hide custom titlebar when `#window.titleBarStyle#` is set to `native`."), ], 'default': isLinux ? 'never' : 'auto', From 5bebda832a1feb3cfc4e8be0b3d4900a1e2923ab Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 23 Jan 2024 19:44:25 +0100 Subject: [PATCH 239/333] Disabling window.zoomPerWindow doesn't restore window zooms (#203209) Disabling window.zoomPerWindow doesn't restore window zooms (fix #203129) --- src/vs/workbench/electron-sandbox/window.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index cfb720593d53e..5c7cb75b085a2 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -332,7 +332,7 @@ export class NativeWindow extends BaseWindow { // Window Zoom this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('window.zoomLevel')) { + if (e.affectsConfiguration('window.zoomLevel') || (e.affectsConfiguration('window.zoomPerWindow') && this.configurationService.getValue('window.zoomPerWindow') === false)) { this.onDidChangeConfiguredWindowZoomLevel(); } else if (e.affectsConfiguration('keyboard.touchbar.enabled') || e.affectsConfiguration('keyboard.touchbar.ignored')) { this.updateTouchbarMenu(); From e523361ab832fe26deea34f242ebddfc6a1e90a3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 23 Jan 2024 20:01:52 +0100 Subject: [PATCH 240/333] Auto Save When No Errors does not work consistently (fix #203125) (#203221) --- src/vs/workbench/contrib/files/browser/files.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 4f6e5691daa23..0ea210b535de6 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -270,7 +270,7 @@ configurationRegistry.registerConfiguration({ 'files.autoSaveWhenNoErrors': { 'type': 'boolean', 'default': false, - 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSaveWhenNoErrors' }, "When enabled, will limit [auto save](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) of editors to files that have no errors reported in them. Only applies when `#files.autoSave#` is enabled."), + 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSaveWhenNoErrors' }, "When enabled, will limit [auto save](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) of editors to files that have no errors reported in them at the time the auto save is triggered. Only applies when `#files.autoSave#` is enabled."), scope: ConfigurationScope.LANGUAGE_OVERRIDABLE }, 'files.watcherExclude': { From 3aa161859aca5bcca03fddd6f1e18c19b5071e2f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 23 Jan 2024 20:09:14 +0100 Subject: [PATCH 241/333] Wording " speech recognized" (fix #203171) (#203226) --- .../contrib/chat/electron-sandbox/actions/voiceChatActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index a68d2a95678bb..1666adf085de9 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -852,7 +852,7 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe localize('voice.keywordActivation.inlineChat', "Keyword activation is enabled and listening for 'Hey Code' to start a voice chat session in the active editor."), localize('voice.keywordActivation.chatInContext', "Keyword activation is enabled and listening for 'Hey Code' to start a voice chat session in the active editor or view depending on keyboard focus.") ], - 'description': localize('voice.keywordActivation', "Controls whether the phrase 'Hey Code' should be speech recognized to start a voice chat session."), + 'description': localize('voice.keywordActivation', "Controls whether the keyword phrase 'Hey Code' is recognized to start a voice chat session. Enabling this will start recording from the microphone but the audio is processed locally and never sent to a server."), 'default': 'off', 'tags': ['accessibility', 'FeatureInsight'] } From f73a212381a509029ef73c1d3c0959e47b6f4d2a Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 22 Jan 2024 18:29:38 +0100 Subject: [PATCH 242/333] Improves rendering of deleted and renamed files in multi diff editor. --- .../diffEditorItemTemplate.ts | 40 ++++++++++++++++--- .../widget/multiDiffEditorWidget/model.ts | 11 ++++- .../multiDiffEditorViewModel.ts | 21 +++++++--- .../widget/multiDiffEditorWidget/style.css | 35 +++++++++++++++- .../workbenchUIElementFactory.ts | 6 ++- .../browser/multiDiffEditor.ts | 8 +++- .../browser/multiDiffEditorInput.ts | 23 +++++------ .../browser/scmMultiDiffSourceResolver.ts | 10 +++-- 8 files changed, 120 insertions(+), 34 deletions(-) diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts b/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts index 11fdb1e5ccc73..186fdb3576103 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/diffEditorItemTemplate.ts @@ -60,14 +60,18 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< private readonly _elements = h('div.multiDiffEntry', [ h('div.header@header', [ h('div.collapse-button@collapseButton'), - h('div.title.show-file-icons@title', [] as any), + h('div.file-path', [ + h('div.title.modified.show-file-icons@primaryPath', [] as any), + h('div.status.deleted@status', ['R']), + h('div.title.original.show-file-icons@secondaryPath', [] as any), + ]), h('div.actions@actions'), ]), h('div.editorParent', [ h('div.editorContainer@editor'), ]) - ]); + ]) as Record; public readonly editor = this._register(this._instantiationService.createInstance(DiffEditorWidget, this._elements.editor, { overflowWidgetsDomNode: this._overflowWidgetsDomNode, @@ -78,7 +82,11 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< public readonly isFocused = derived(this, reader => this.isModifedFocused.read(reader) || this.isOriginalFocused.read(reader)); private readonly _resourceLabel = this._workbenchUIElementFactory.createResourceLabel - ? this._register(this._workbenchUIElementFactory.createResourceLabel(this._elements.title)) + ? this._register(this._workbenchUIElementFactory.createResourceLabel(this._elements.primaryPath)) + : undefined; + + private readonly _resourceLabel2 = this._workbenchUIElementFactory.createResourceLabel + ? this._register(this._workbenchUIElementFactory.createResourceLabel(this._elements.secondaryPath)) : undefined; private readonly _outerEditorHeight: number; @@ -132,7 +140,7 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< this._outerEditorHeight = 38; this._register(this._instantiationService.createInstance(MenuWorkbenchToolBar, this._elements.actions, MenuId.MultiDiffEditorFileToolbar, { - actionRunner: this._register(new ActionRunnerWithContext(() => (this._viewModel.get()?.diffEditorViewModel?.model.modified.uri))), + actionRunner: this._register(new ActionRunnerWithContext(() => (this._viewModel.get()?.modifiedUri))), menuOptions: { shouldForwardArgs: true, }, @@ -178,7 +186,29 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject< })); } globalTransaction(tx => { - this._resourceLabel?.setUri(data.viewModel.diffEditorViewModel.model.modified.uri); + this._resourceLabel?.setUri(data.viewModel.modifiedUri ?? data.viewModel.originalUri!, { strikethrough: data.viewModel.modifiedUri === undefined }); + + let isRenamed = false; + let isDeleted = false; + let isAdded = false; + let flag = ''; + if (data.viewModel.modifiedUri && data.viewModel.originalUri && data.viewModel.modifiedUri.path !== data.viewModel.originalUri.path) { + flag = 'R'; + isRenamed = true; + } else if (!data.viewModel.modifiedUri) { + flag = 'D'; + isDeleted = true; + } else if (!data.viewModel.originalUri) { + flag = 'A'; + isAdded = true; + } + this._elements.status.classList.toggle('renamed', isRenamed); + this._elements.status.classList.toggle('deleted', isDeleted); + this._elements.status.classList.toggle('added', isAdded); + this._elements.status.innerText = flag; + + this._resourceLabel2?.setUri(isRenamed ? data.viewModel.originalUri : undefined, { strikethrough: true }); + this._dataStore.clear(); this._viewModel.set(data.viewModel, tx); this.editor.setModel(data.viewModel.diffEditorViewModel, tx); diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/model.ts b/src/vs/editor/browser/widget/multiDiffEditorWidget/model.ts index c6973f50cdf17..02f37e47ad202 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/model.ts +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/model.ts @@ -37,8 +37,15 @@ export class ConstLazyPromise implements LazyPromise { } export interface IDocumentDiffItem { - readonly original: ITextModel | undefined; // undefined if the file was created. - readonly modified: ITextModel | undefined; // undefined if the file was deleted. + /** + * undefined if the file was created. + */ + readonly original: ITextModel | undefined; + + /** + * undefined if the file was deleted. + */ + readonly modified: ITextModel | undefined; readonly options?: IDiffEditorOptions; readonly onOptionsDidChange?: Event; } diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts index a2d272329e359..3a26a82c526ab 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel.ts @@ -12,13 +12,15 @@ import { IDocumentDiffItem, IMultiDiffEditorModel, LazyPromise } from 'vs/editor import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Selection } from 'vs/editor/common/core/selection'; import { IDiffEditorViewModel } from 'vs/editor/common/editorCommon'; +import { IModelService } from 'vs/editor/common/services/model'; import { ContextKeyValue } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { URI } from 'vs/base/common/uri'; export class MultiDiffEditorViewModel extends Disposable { private readonly _documents = observableFromEvent(this.model.onDidChange, /** @description MultiDiffEditorViewModel.documents */() => this.model.documents); - public readonly items = mapObservableArrayCached(this, this._documents, (d, store) => store.add(new DocumentDiffItemViewModel(d, this._instantiationService))) + public readonly items = mapObservableArrayCached(this, this._documents, (d, store) => store.add(this._instantiationService.createInstance(DocumentDiffItemViewModel, d))) .recomputeInitiallyAndOnChange(this._store); public readonly activeDiffItem = observableValue(this, undefined); @@ -66,9 +68,13 @@ export class DocumentDiffItemViewModel extends Disposable { { contentHeight: 500, selections: undefined, } ); + public get originalUri(): URI | undefined { return this.entry.value!.original?.uri; } + public get modifiedUri(): URI | undefined { return this.entry.value!.modified?.uri; } + constructor( public readonly entry: LazyPromise, - private readonly _instantiationService: IInstantiationService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IModelService private readonly _modelService: IModelService, ) { super(); @@ -88,16 +94,19 @@ export class DocumentDiffItemViewModel extends Disposable { })); } + const originalTextModel = this.entry.value!.original ?? this._register(this._modelService.createModel('', null)); + const modifiedTextModel = this.entry.value!.modified ?? this._register(this._modelService.createModel('', null)); + this.diffEditorViewModel = this._register(this._instantiationService.createInstance(DiffEditorViewModel, { - original: entry.value!.original!, - modified: entry.value!.modified!, + original: originalTextModel, + modified: modifiedTextModel, }, options)); } public getKey(): string { return JSON.stringify([ - this.diffEditorViewModel.model.original.uri.toString(), - this.diffEditorViewModel.model.modified.uri.toString() + this.originalUri?.toString(), + this.modifiedUri?.toString() ]); } } diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/style.css b/src/vs/editor/browser/widget/multiDiffEditorWidget/style.css index f1ce31f78376c..9071d1f841b6a 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/style.css +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/style.css @@ -44,12 +44,45 @@ box-shadow: var(--vscode-scrollbar-shadow) 0px 6px 6px -6px; } -.monaco-component .multiDiffEntry .header .title { +.monaco-component .multiDiffEntry .header .file-path { + display: flex; flex: 1; + min-width: 0; +} + +.monaco-component .multiDiffEntry .header .file-path .title { font-size: 14px; line-height: 22px; } +.monaco-component .multiDiffEntry .header .file-path .status { + + font-weight: 600; + opacity: 0.75; + margin: 0px 10px; + line-height: 22px; +} +/* +TODO@hediet: move colors from git extension to core! + +.monaco-component .multiDiffEntry .header .file-path .status.renamed { + color: va r(--vscode-gitDecoration-renamedResourceForeground); +} + +.monaco-component .multiDiffEntry .header .file-path .status.deleted { + color: va r(--vscode-gitDecoration-deletedResourceForeground); +} + +.monaco-component .multiDiffEntry .header .file-path .status.added { + color: va r(--vscode-gitDecoration-addedResourceForeground); +} +*/ +.monaco-component .multiDiffEntry .header .file-path .title.original { + flex: 1; + min-width: 0; + text-overflow: ellipsis; +} + .monaco-component .multiDiffEntry .header .actions { padding: 0 8px; } diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory.ts b/src/vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory.ts index 54b29691c96e0..80a018dead401 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory.ts +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory.ts @@ -17,5 +17,9 @@ export interface IWorkbenchUIElementFactory { } export interface IResourceLabel extends IDisposable { - setUri(uri: URI): void; + setUri(uri: URI | undefined, options?: IResourceLabelOptions): void; +} + +export interface IResourceLabelOptions { + strikethrough?: boolean; } diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts index e100522ed4ccc..263668667598c 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts @@ -122,8 +122,12 @@ class WorkbenchUIElementFactory implements IWorkbenchUIElementFactory { createResourceLabel(element: HTMLElement): IResourceLabel { const label = this._instantiationService.createInstance(ResourceLabel, element, {}); return { - setUri(uri) { - label.element.setFile(uri, {}); + setUri(uri, options = {}) { + if (!uri) { + label.element.clear(); + } else { + label.element.setFile(uri, { strikethrough: options.strikethrough }); + } }, dispose() { label.dispose(); diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts index 0fa31d32b33d9..0507111b60dc3 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts @@ -142,9 +142,8 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor const documentsWithPromises = mapObservableArrayCached(undefined, source.resources, async (r, store) => { /** @description documentsWithPromises */ - let originalTextModel: ITextModel; - let modifiedTextModel: ITextModel; - let modifiedRef: IReference | undefined; + let original: IReference | undefined; + let modified: IReference | undefined; const store2 = new DisposableStore(); store.add(toDisposable(() => { // Mark the text model references as garbage when they get stale (don't dispose them yet) @@ -152,14 +151,12 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor })); try { - [originalTextModel, modifiedTextModel] = await Promise.all([ - r.original - ? store2.add(await this._textModelService.createModelReference(r.original)).object.textEditorModel - : store2.add(this._modelService.createModel('', null)), - r.modified - ? store2.add(modifiedRef = await this._textModelService.createModelReference(r.modified)).object.textEditorModel - : store2.add(this._modelService.createModel('', null)), + [original, modified] = await Promise.all([ + r.original ? this._textModelService.createModelReference(r.original) : undefined, + r.modified ? this._textModelService.createModelReference(r.modified) : undefined, ]); + if (original) { store.add(original); } + if (modified) { store.add(modified); } } catch (e) { // e.g. "File seems to be binary and cannot be opened as text" console.error(e); @@ -169,11 +166,11 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor const uri = (r.modified ?? r.original)!; return new ConstLazyPromise({ - original: originalTextModel, - modified: modifiedTextModel, + original: original?.object.textEditorModel, + modified: modified?.object.textEditorModel, get options() { return { - ...getReadonlyConfiguration(modifiedRef?.object.isReadonly() ?? true), + ...getReadonlyConfiguration(modified?.object.isReadonly() ?? true), ...computeOptions(textResourceConfigurationService.getValue(uri)), } satisfies IDiffEditorOptions; }, diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts index 75d8776a7853a..3326e502d820e 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts @@ -78,10 +78,12 @@ export class ScmMultiDiffSourceResolver implements IMultiDiffSourceResolver { } ); - const resources = observableFromEvent(group.onDidChangeResources, () => group.resources.map(e => ({ - original: e.multiDiffEditorOriginalUri, - modified: e.multiDiffEditorModifiedUri - }))); + const resources = observableFromEvent(group.onDidChangeResources, () => group.resources.map(e => { + return { + original: e.multiDiffEditorOriginalUri, + modified: e.multiDiffEditorModifiedUri + }; + })); return new ScmResolvedMultiDiffSource(resources, { scmResourceGroup: groupId, From 0bf3670b5257a485071219048d692da7db3d4770 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 23 Jan 2024 19:39:27 +0100 Subject: [PATCH 243/333] Fixes CI --- .../contrib/multiDiffEditor/browser/multiDiffEditorInput.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts index 0507111b60dc3..60b430fd74454 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts @@ -18,8 +18,6 @@ import { URI } from 'vs/base/common/uri'; import { ConstLazyPromise, IDocumentDiffItem, IMultiDiffEditorModel, LazyPromise } from 'vs/editor/browser/widget/multiDiffEditorWidget/model'; import { MultiDiffEditorViewModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { ITextModel } from 'vs/editor/common/model'; -import { IModelService } from 'vs/editor/common/services/model'; import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; import { localize } from 'vs/nls'; @@ -85,7 +83,6 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor @ITextModelService private readonly _textModelService: ITextModelService, @ITextResourceConfigurationService private readonly _textResourceConfigurationService: ITextResourceConfigurationService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IModelService private readonly _modelService: IModelService, @IMultiDiffSourceResolverService private readonly _multiDiffSourceResolverService: IMultiDiffSourceResolverService, @ITextFileService private readonly _textFileService: ITextFileService, ) { From 7975a455439d96a3e33a3705b97cc47165d8cdcc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 23 Jan 2024 20:32:24 +0100 Subject: [PATCH 244/333] No prompt after closing editor when piping command that doesn't terminate (fix #203166) (#203217) --- src/vs/code/node/cli.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 0ecdbb32e77de..578bf1a282c44 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -225,7 +225,24 @@ export async function main(argv: string[]): Promise { try { const readFromStdinDone = new DeferredPromise(); await readFromStdin(stdinFilePath, !!args.verbose, () => readFromStdinDone.complete()); - processCallbacks.push(() => readFromStdinDone.p); + if (!args.wait) { + + // if `--wait` is not provided, we keep this process alive + // for at least as long as the stdin stream is open to + // ensure that we read all the data. + // the downside is that the Code CLI process will then not + // terminate until stdin is closed, but users can always + // pass `--wait` to prevent that from happening (this is + // actually what we enforced until v1.85.x but then was + // changed to not enforce it anymore). + // a solution in the future would possibly be to exit, when + // the Code process exits. this would require some careful + // solution though in case Code is already running and this + // is a second instance telling the first instance what to + // open. + + processCallbacks.push(() => readFromStdinDone.p); + } // Make sure to open tmp file as editor but ignore it in the "recently open" list addArg(argv, stdinFilePath); From ca476115d31eaaf2213c3ea20fa904c10ca583d5 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 23 Jan 2024 20:31:02 +0100 Subject: [PATCH 245/333] Fixes bug in WrappedStyleElement --- src/vs/base/browser/dom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index c5fea2d0c7ba2..72caa84e06993 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -939,7 +939,7 @@ class WrappedStyleElement { private _styleSheet: HTMLStyleElement | undefined = undefined; public setStyle(cssStyle: string): void { - if (cssStyle !== this._currentCssStyle) { + if (cssStyle === this._currentCssStyle) { return; } this._currentCssStyle = cssStyle; From 3cf18155086177df108c38429454bdd8236c7c67 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 23 Jan 2024 21:18:54 +0100 Subject: [PATCH 246/333] Do not offer 'Create file' option for readonly file systems (fix #203223) (#203234) --- .../contrib/files/browser/editors/textFileEditor.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index 44dc610e9367b..c0f1c912060d6 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts @@ -36,6 +36,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; /** * An implementation of editor for file system resources. @@ -61,7 +62,8 @@ export class TextFileEditor extends AbstractTextCodeEditor @IPathService private readonly pathService: IPathService, @IConfigurationService private readonly configurationService: IConfigurationService, @IPreferencesService protected readonly preferencesService: IPreferencesService, - @IHostService private readonly hostService: IHostService + @IHostService private readonly hostService: IHostService, + @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService ) { super(TextFileEditor.ID, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorService, editorGroupService, fileService); @@ -201,8 +203,12 @@ export class TextFileEditor extends AbstractTextCodeEditor throw createTooLargeFileError(this.group, input, options, message, this.preferencesService); } - // Offer to create a file from the error if we have a file not found and the name is valid - if ((error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND && await this.pathService.hasValidBasename(input.preferredResource)) { + // Offer to create a file from the error if we have a file not found and the name is valid and not readonly + if ( + (error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND && + !this.filesConfigurationService.isReadonly(input.preferredResource) && + await this.pathService.hasValidBasename(input.preferredResource) + ) { const fileNotFoundError = createEditorOpenError(new FileOperationError(localize('unavailableResourceErrorEditorText', "The editor could not be opened because the file was not found."), FileOperationResult.FILE_NOT_FOUND), [ toAction({ id: 'workbench.files.action.createMissingFile', label: localize('createFile', "Create File"), run: async () => { From d742dffc43cccff5e20847f7e4db865f8111778f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 23 Jan 2024 21:19:10 +0100 Subject: [PATCH 247/333] Speech provider with identifier ms-vscode.vscode-speech is already registered (fix #203172) (#203233) --- .../workbench/api/browser/mainThreadSpeech.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadSpeech.ts b/src/vs/workbench/api/browser/mainThreadSpeech.ts index fa52270cdf038..d670ffd66a1f6 100644 --- a/src/vs/workbench/api/browser/mainThreadSpeech.ts +++ b/src/vs/workbench/api/browser/mainThreadSpeech.ts @@ -5,7 +5,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; -import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostContext, ExtHostSpeechShape, MainContext, MainThreadSpeechShape } from 'vs/workbench/api/common/extHost.protocol'; import { IKeywordRecognitionEvent, ISpeechProviderMetadata, ISpeechService, ISpeechToTextEvent } from 'vs/workbench/contrib/speech/common/speechService'; @@ -20,7 +20,7 @@ type KeywordRecognitionSession = { }; @extHostNamedCustomer(MainContext.MainThreadSpeech) -export class MainThreadSpeech extends Disposable implements MainThreadSpeechShape { +export class MainThreadSpeech implements MainThreadSpeechShape { private readonly proxy: ExtHostSpeechShape; @@ -34,8 +34,6 @@ export class MainThreadSpeech extends Disposable implements MainThreadSpeechShap @ISpeechService private readonly speechService: ISpeechService, @ILogService private readonly logService: ILogService ) { - super(); - this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostSpeech); } @@ -109,4 +107,15 @@ export class MainThreadSpeech extends Disposable implements MainThreadSpeechShap const providerSession = this.keywordRecognitionSessions.get(session); providerSession?.onDidChange.fire(event); } + + dispose(): void { + this.providerRegistrations.forEach(disposable => disposable.dispose()); + this.providerRegistrations.clear(); + + this.speechToTextSessions.forEach(session => session.onDidChange.dispose()); + this.speechToTextSessions.clear(); + + this.keywordRecognitionSessions.forEach(session => session.onDidChange.dispose()); + this.keywordRecognitionSessions.clear(); + } } From 1d62c50954360aec5d1247680e5fda5569d45f15 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 23 Jan 2024 21:28:24 +0100 Subject: [PATCH 248/333] Fix Expand All Diffs command order (#203244) Fix #203201 --- src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts index 921026f473192..5298c06ac45ef 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts @@ -94,6 +94,7 @@ export class ExpandAllAction extends Action2 { when: ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', MultiDiffEditor.ID), ContextKeyExpr.has('multiDiffEditorAllCollapsed')), id: MenuId.EditorTitle, group: 'navigation', + order: 100 }, f1: true, }); From d87f1716cf5c23cf51cc66cf151e90eed2fb5169 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 23 Jan 2024 17:35:19 -0300 Subject: [PATCH 249/333] Fix bad spacing in /help (#203245) --- src/vs/workbench/contrib/chat/browser/chat.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 8a2486b786e19..9dd8bf76be7f1 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -265,7 +265,7 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable { return `\t* [\`${chatSubcommandLeader}${c.name}\`](command:${SubmitAction.ID}?${urlSafeArg}) - ${c.description}`; }).join('\n'); - return agentLine + '\n' + commandText; + return (agentLine + '\n' + commandText).trim(); }))).join('\n'); progress.report({ content: new MarkdownString(agentText, { isTrusted: { enabledCommands: [SubmitAction.ID] } }), kind: 'markdownContent' }); if (defaultAgent?.metadata.helpTextPostfix) { From 856d46ed86cacc939504e0d9d77fb8dfc12f9efc Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 23 Jan 2024 22:01:09 +0100 Subject: [PATCH 250/333] SCM - fix regression related to the editor.wordWrap language setting (#203248) --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 7ce51f64928df..562bc92778030 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -2027,7 +2027,7 @@ class SCMInputWidgetEditorOptions { // editor.wordWrap const wordWrapConfig = this.configurationService.inspect('editor.wordWrap', { overrideIdentifier: 'scminput' }); - const wordWrap = wordWrapConfig.overrideIdentifiers?.includes('scminput') ? EditorOptions.wordWrap.validate(wordWrapConfig) : 'on'; + const wordWrap = wordWrapConfig.overrideIdentifiers?.includes('scminput') ? EditorOptions.wordWrap.validate(wordWrapConfig.value) : 'on'; return { rulers, wordWrap }; } From 79e6d75a4b56aa4a973820200ede94e5ba644043 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 23 Jan 2024 15:37:29 -0800 Subject: [PATCH 251/333] fix #203115 --- .../browser/accessibilityConfiguration.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 5c37fed8c8c5d..9c18fc789c89a 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -159,25 +159,25 @@ const configuration: IConfigurationNode = { tags: ['accessibility'] }, [AccessibilityAlertSettingId.Breakpoint]: { - 'markdownDescription': localize('alert.breakpoint', "Alerts when the active line has a breakpoint. Also see {0}.", '`#audioCues.breakpoint#`'), + 'markdownDescription': localize('alert.breakpoint', "Alerts when the active line has a breakpoint. Also see {0}.", '`#audioCues.onDebugBreak#`'), 'type': 'boolean', 'default': true, tags: ['accessibility'] }, [AccessibilityAlertSettingId.Error]: { - 'markdownDescription': localize('alert.error', "Alerts when the active line has an error. Also see {0}.", '`#audioCues.error#`'), + 'markdownDescription': localize('alert.error', "Alerts when the active line has an error. Also see {0}.", '`#audioCues.lineHasError#`'), 'type': 'boolean', 'default': true, tags: ['accessibility'] }, [AccessibilityAlertSettingId.Warning]: { - 'markdownDescription': localize('alert.warning', "Alerts when the active line has a warning. Also see {0}.", '`#audioCues.warning#`'), + 'markdownDescription': localize('alert.warning', "Alerts when the active line has a warning. Also see {0}.", '`#audioCues.lineHasWarning#`'), 'type': 'boolean', 'default': true, tags: ['accessibility'] }, [AccessibilityAlertSettingId.FoldedArea]: { - 'markdownDescription': localize('alert.foldedArea', "Alerts when the active line has a folded area that can be unfolded. Also see {0}.", '`#audioCues.foldedArea#`'), + 'markdownDescription': localize('alert.foldedArea', "Alerts when the active line has a folded area that can be unfolded. Also see {0}.", '`#audioCues.lineHasFoldedArea#`'), 'type': 'boolean', 'default': true, tags: ['accessibility'] @@ -189,7 +189,7 @@ const configuration: IConfigurationNode = { tags: ['accessibility'] }, [AccessibilityAlertSettingId.TerminalBell]: { - 'markdownDescription': localize('alert.terminalBell', "Alerts when the terminal bell is activated. Also see {0}.", '`#audioCues.terminalBell#`'), + 'markdownDescription': localize('alert.terminalBell', "Alerts when the terminal bell is activated."), 'type': 'boolean', 'default': true, tags: ['accessibility'] From 0955a9d9d5f2ad067b3ff1d037b6be6ca224e267 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 23 Jan 2024 20:52:56 -0300 Subject: [PATCH 252/333] Add background color to chat agent icon so it doesn't overlap copilot icon (#203256) --- build/lib/stylelint/vscode-known-variables.json | 1 + src/vs/workbench/contrib/chat/browser/chatWidget.ts | 3 +++ src/vs/workbench/contrib/chat/browser/media/chat.css | 1 + 3 files changed, 5 insertions(+) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 9fb16d8459df8..7f5b6fa289289 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -42,6 +42,7 @@ "--vscode-chat-requestBorder", "--vscode-chat-slashCommandBackground", "--vscode-chat-slashCommandForeground", + "--vscode-chat-list-background", "--vscode-checkbox-background", "--vscode-checkbox-border", "--vscode-checkbox-foreground", diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 745ec7e1c0472..11780bb247944 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -32,6 +32,7 @@ import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatC import { ChatModelInitState, IChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; import { IChatReplyFollowup, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { ChatViewModel, IChatResponseViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; const $ = dom.$; @@ -138,6 +139,7 @@ export class ChatWidget extends Disposable implements IChatWidget { @IChatAccessibilityService private readonly _chatAccessibilityService: IChatAccessibilityService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILogService private readonly _logService: ILogService, + @IThemeService private readonly _themeService: IThemeService ) { super(); CONTEXT_IN_CHAT_SESSION.bindTo(contextKeyService).set(true); @@ -439,6 +441,7 @@ export class ChatWidget extends Disposable implements IChatWidget { private onDidStyleChange(): void { this.container.style.setProperty('--vscode-interactive-result-editor-background-color', this.editorOptions.configuration.resultEditor.backgroundColor?.toString() ?? ''); this.container.style.setProperty('--vscode-interactive-session-foreground', this.editorOptions.configuration.foreground?.toString() ?? ''); + this.container.style.setProperty('--vscode-chat-list-background', this._themeService.getColorTheme().getColor(this.styles.listBackground)?.toString() ?? ''); } setModel(model: IChatModel, viewState: IChatViewState): void { diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 009e5b35f4972..7b8c230b92fba 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -115,6 +115,7 @@ width: 24px; height: 24px; border-radius: 50%; + background-color: var(--vscode-chat-list-background); } .interactive-item-container .header .avatar .codicon { From b9ee99d894ac7704e5e40ea867d971f3b7641d1d Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 23 Jan 2024 20:53:08 -0300 Subject: [PATCH 253/333] Delete unnecessary newlines (#203257) --- .vscode/notebooks/api.github-issues | 6 ++-- .vscode/notebooks/endgame.github-issues | 31 ++++++++---------- .vscode/notebooks/grooming.github-issues | 18 +++++------ .vscode/notebooks/my-endgame.github-issues | 34 ++++++++++---------- .vscode/notebooks/my-work.github-issues | 30 ++++++++--------- .vscode/notebooks/verification.github-issues | 10 +++--- .vscode/notebooks/vscode-dev.github-issues | 12 +++---- 7 files changed, 68 insertions(+), 73 deletions(-) diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index f990cff7af10f..8027d3066ef9f 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"December / January 2024\"\n" + "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"December / January 2024\"" }, { "kind": 1, @@ -17,7 +17,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPO $MILESTONE label:api-finalization\n" + "value": "$REPO $MILESTONE label:api-finalization" }, { "kind": 1, @@ -27,6 +27,6 @@ { "kind": 2, "language": "github-issues", - "value": "$REPO $MILESTONE is:open label:api-proposal sort:created-asc\n" + "value": "$REPO $MILESTONE is:open label:api-proposal sort:created-asc" } ] \ No newline at end of file diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 975758eb74767..e93d064d8de01 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"December / January 2024\"\n" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"December / January 2024\"" }, { "kind": 1, @@ -22,7 +22,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:pr is:open\n" + "value": "$REPOS $MILESTONE is:pr is:open" }, { "kind": 1, @@ -32,7 +32,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS -$MILESTONE is:issue is:closed reason:completed label:bug label:insiders-released -label:verified -label:*duplicate -label:*as-designed -label:z-author-verified -label:on-testplan\n" + "value": "$REPOS -$MILESTONE is:issue is:closed reason:completed label:bug label:insiders-released -label:verified -label:*duplicate -label:*as-designed -label:z-author-verified -label:on-testplan" }, { "kind": 1, @@ -42,7 +42,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS -$MILESTONE is:issue is:closed reason:completed label:feature-request label:insiders-released -label:on-testplan -label:verified -label:*duplicate\n" + "value": "$REPOS -$MILESTONE is:issue is:closed reason:completed label:feature-request label:insiders-released -label:on-testplan -label:verified -label:*duplicate" }, { "kind": 1, @@ -52,7 +52,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:open -label:iteration-plan -label:endgame-plan -label:testplan-item\n" + "value": "$REPOS $MILESTONE is:issue is:open -label:iteration-plan -label:endgame-plan -label:testplan-item" }, { "kind": 1, @@ -62,7 +62,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed reason:completed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate\n" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate" }, { "kind": 1, @@ -72,7 +72,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:open label:testplan-item no:milestone\n" + "value": "$REPOS $MILESTONE is:issue is:open label:testplan-item no:milestone" }, { "kind": 1, @@ -87,7 +87,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS is:issue is:open label:testplan-item\n" + "value": "$REPOS is:issue is:open label:testplan-item" }, { "kind": 1, @@ -97,7 +97,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed reason:completed label:verification-needed -label:verified\n" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed label:verification-needed -label:verified" }, { "kind": 1, @@ -112,7 +112,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:z-author-verified -label:unreleased\n" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:z-author-verified -label:unreleased" }, { "kind": 1, @@ -122,7 +122,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:z-author-verified label:unreleased\n" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:z-author-verified label:unreleased" }, { "kind": 1, @@ -132,7 +132,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:z-author-verified\n" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:z-author-verified" }, { "kind": 1, @@ -142,11 +142,6 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:open label:candidate\n" - }, - { - "kind": 2, - "language": "github-issues", - "value": "" + "value": "$REPOS $MILESTONE is:issue is:open label:candidate" } ] \ No newline at end of file diff --git a/.vscode/notebooks/grooming.github-issues b/.vscode/notebooks/grooming.github-issues index 694862806ede8..8bb9a246d5f78 100644 --- a/.vscode/notebooks/grooming.github-issues +++ b/.vscode/notebooks/grooming.github-issues @@ -2,27 +2,27 @@ { "kind": 1, "language": "markdown", - "value": "#### Config\n" + "value": "#### Config" }, { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\r\n$repos=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\r\n" + "value": "// list of repos we work in\r\n$repos=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\r" }, { "kind": 1, "language": "markdown", - "value": "#### Missing Type label\r\n" + "value": "#### Missing Type label\r" }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open type:issue -label:bug -label:\"info-needed\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream -label:polish -label:testplan-item -label:error-telemetry -label:engineering -label:endgame-plan\r\n" + "value": "$repos assignee:@me is:open type:issue -label:bug -label:\"info-needed\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream -label:polish -label:testplan-item -label:error-telemetry -label:engineering -label:endgame-plan\r" }, { "kind": 1, "language": "markdown", - "value": "#### Missing Area Label\r\n\r\nFeature area labels are light or strong blue (`1d76db` or `c5def5`) and they denote a specific feature or feature area, like `editor-clipboard` or `file-explorer`\r\n" + "value": "#### Missing Area Label\r\n\r\nFeature area labels are light or strong blue (`1d76db` or `c5def5`) and they denote a specific feature or feature area, like `editor-clipboard` or `file-explorer`\r" }, { "kind": 2, @@ -32,21 +32,21 @@ { "kind": 1, "language": "markdown", - "value": "### Missing Milestone\r\n" + "value": "### Missing Milestone\r" }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open type:issue no:milestone -label:info-needed -label:triage-needed\r\n" + "value": "$repos assignee:@me is:open type:issue no:milestone -label:info-needed -label:triage-needed\r" }, { "kind": 1, "language": "markdown", - "value": "#### Not Actionable\r\n" + "value": "#### Not Actionable\r" }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:\"info-needed\"\r\n" + "value": "$repos assignee:@me is:open label:\"info-needed\"\r" } ] \ No newline at end of file diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index 46a88544941eb..ddcb541f8ff04 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"December / January 2024\"\n\n$MINE=assignee:@me\n" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"December / January 2024\"\n\n$MINE=assignee:@me" }, { "kind": 1, @@ -22,7 +22,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:pr is:open\n" + "value": "$REPOS $MILESTONE $MINE is:pr is:open" }, { "kind": 1, @@ -32,7 +32,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue is:open -label:iteration-plan -label:endgame-plan -label:testplan-item\n" + "value": "$REPOS $MILESTONE $MINE is:issue is:open -label:iteration-plan -label:endgame-plan -label:testplan-item" }, { "kind": 1, @@ -42,7 +42,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate\n" + "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:verification-needed -label:on-testplan -label:verified -label:*duplicate" }, { "kind": 1, @@ -52,7 +52,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS is:issue is:open author:@me label:testplan-item\n" + "value": "$REPOS is:issue is:open author:@me label:testplan-item" }, { "kind": 1, @@ -62,7 +62,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request label:verification-needed -label:verified\n" + "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request label:verification-needed -label:verified" }, { "kind": 1, @@ -77,7 +77,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MINE is:issue is:open label:testplan-item\n" + "value": "$REPOS $MINE is:issue is:open label:testplan-item" }, { "kind": 1, @@ -87,7 +87,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -assignee:@me -label:verified -label:z-author-verified label:feature-request label:verification-needed -label:verification-steps-needed -label:unreleased\n" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -assignee:@me -label:verified -label:z-author-verified label:feature-request label:verification-needed -label:verification-steps-needed -label:unreleased" }, { "kind": 1, @@ -102,7 +102,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue is:open -label:endgame-plan -label:testplan-item -label:iteration-plan\n" + "value": "$REPOS $MILESTONE $MINE is:issue is:open -label:endgame-plan -label:testplan-item -label:iteration-plan" }, { "kind": 1, @@ -112,7 +112,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue is:open label:bug\n" + "value": "$REPOS $MILESTONE $MINE is:issue is:open label:bug" }, { "kind": 1, @@ -127,7 +127,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue label:bug label:verification-steps-needed\n" + "value": "$REPOS $MILESTONE $MINE is:issue label:bug label:verification-steps-needed" }, { "kind": 1, @@ -137,7 +137,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue label:bug label:verification-found\n" + "value": "$REPOS $MILESTONE $MINE is:issue label:bug label:verification-found" }, { "kind": 1, @@ -147,7 +147,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:triage-needed -label:verification-found\n" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:triage-needed -label:verification-found" }, { "kind": 1, @@ -157,7 +157,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:karrtikr -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett\n" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:karrtikr -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett" }, { "kind": 1, @@ -167,7 +167,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found\n" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found" }, { "kind": 1, @@ -177,7 +177,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue label:bug label:verification-steps-needed -label:verified\n" + "value": "$REPOS $MILESTONE -$MINE is:issue label:bug label:verification-steps-needed -label:verified" }, { "kind": 1, @@ -187,6 +187,6 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:on-release-notes\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:engineering -label:on-release-notes\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:plan-item -label:on-release-notes\n" + "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:on-release-notes\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:engineering -label:on-release-notes\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:plan-item -label:on-release-notes" } ] \ No newline at end of file diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index c6e8ee4b20eb0..cbdd18d165396 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\r\n$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\r\n\r\n// current milestone name\r\n$MILESTONE=milestone:\"December / January 2024\"\r\n" + "value": "// list of repos we work in\r\n$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\r\n\r\n// current milestone name\r\n$MILESTONE=milestone:\"December / January 2024\"\r" }, { "kind": 1, @@ -17,7 +17,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE assignee:@me is:open\r\n" + "value": "$REPOS $MILESTONE assignee:@me is:open\r" }, { "kind": 1, @@ -32,7 +32,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS assignee:@me is:open label:bug\r\n" + "value": "$REPOS assignee:@me is:open label:bug\r" }, { "kind": 1, @@ -42,7 +42,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS assignee:@me is:open label:debt,engineering\r\n" + "value": "$REPOS assignee:@me is:open label:debt,engineering\r" }, { "kind": 1, @@ -52,7 +52,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS assignee:@me is:open label:perf,perf-startup,perf-bloat,freeze-slow-crash-leak\r\n" + "value": "$REPOS assignee:@me is:open label:perf,perf-startup,perf-bloat,freeze-slow-crash-leak\r" }, { "kind": 1, @@ -62,17 +62,17 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS assignee:@me is:open label:feature-request milestone:Backlog sort:reactions-+1-desc\r\n" + "value": "$REPOS assignee:@me is:open label:feature-request milestone:Backlog sort:reactions-+1-desc\r" }, { "kind": 2, "language": "github-issues", - "value": "$REPOS assignee:@me is:open milestone:\"Backlog Candidates\"\r\n" + "value": "$REPOS assignee:@me is:open milestone:\"Backlog Candidates\"\r" }, { "kind": 1, "language": "markdown", - "value": "### Personal Inbox\n" + "value": "### Personal Inbox" }, { "kind": 1, @@ -82,7 +82,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode is:open assignee:@me label:triage-needed\r\n" + "value": "repo:microsoft/vscode is:open assignee:@me label:triage-needed\r" }, { "kind": 1, @@ -92,7 +92,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS assignee:@me is:open type:issue -label:bug -label:\"info-needed\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream -label:polish -label:testplan-item -label:error-telemetry -label:engineering\r\n" + "value": "$REPOS assignee:@me is:open type:issue -label:bug -label:\"info-needed\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream -label:polish -label:testplan-item -label:error-telemetry -label:engineering\r" }, { "kind": 1, @@ -112,7 +112,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS assignee:@me is:open type:issue no:milestone -label:info-needed -label:triage-needed\r\n" + "value": "$REPOS assignee:@me is:open type:issue no:milestone -label:info-needed -label:triage-needed\r" }, { "kind": 1, @@ -122,7 +122,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS assignee:@me is:open label:\"info-needed\"\r\n" + "value": "$REPOS assignee:@me is:open label:\"info-needed\"\r" }, { "kind": 1, @@ -137,7 +137,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS author:@me is:open is:pr review:approved\r\n" + "value": "$REPOS author:@me is:open is:pr review:approved\r" }, { "kind": 1, @@ -147,7 +147,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS author:@me is:open is:pr review:required\r\n" + "value": "$REPOS author:@me is:open is:pr review:required\r" }, { "kind": 1, @@ -157,6 +157,6 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS author:@me is:open is:pr review:changes_requested\r\n" + "value": "$REPOS author:@me is:open is:pr review:changes_requested\r" } ] \ No newline at end of file diff --git a/.vscode/notebooks/verification.github-issues b/.vscode/notebooks/verification.github-issues index 6145b43a2ecdc..9740d9557e37f 100644 --- a/.vscode/notebooks/verification.github-issues +++ b/.vscode/notebooks/verification.github-issues @@ -12,7 +12,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n$milestone=milestone:\"November 2023\"\n$closedRecently=closed:>2023-09-29\n" + "value": "$repos=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n$milestone=milestone:\"November 2023\"\n$closedRecently=closed:>2023-09-29" }, { "kind": 1, @@ -22,7 +22,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos $milestone is:closed reason:completed -assignee:@me label:bug -label:verified -label:*duplicate author:@me\n" + "value": "$repos $milestone is:closed reason:completed -assignee:@me label:bug -label:verified -label:*duplicate author:@me" }, { "kind": 1, @@ -32,7 +32,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos $milestone is:closed reason:completed -assignee:@me label:bug -label:verified -label:*duplicate -author:@me -assignee:@me label:bug -label:verified -author:@me -author:aeschli -author:alexdima -author:alexr00 -author:bpasero -author:chrisdias -author:chrmarti -author:connor4312 -author:dbaeumer -author:deepak1556 -author:eamodio -author:egamma -author:gregvanl -author:isidorn -author:JacksonKearl -author:joaomoreno -author:jrieken -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:RMacfarlane -author:roblourens -author:sana-ajani -author:sandy081 -author:sbatten -author:Tyriar -author:weinand -author:rzhao271 -author:kieferrm -author:TylerLeonhardt -author:bamurtaugh -author:hediet -author:joyceerhl -author:rchiodo\n" + "value": "$repos $milestone is:closed reason:completed -assignee:@me label:bug -label:verified -label:*duplicate -author:@me -assignee:@me label:bug -label:verified -author:@me -author:aeschli -author:alexdima -author:alexr00 -author:bpasero -author:chrisdias -author:chrmarti -author:connor4312 -author:dbaeumer -author:deepak1556 -author:eamodio -author:egamma -author:gregvanl -author:isidorn -author:JacksonKearl -author:joaomoreno -author:jrieken -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:RMacfarlane -author:roblourens -author:sana-ajani -author:sandy081 -author:sbatten -author:Tyriar -author:weinand -author:rzhao271 -author:kieferrm -author:TylerLeonhardt -author:bamurtaugh -author:hediet -author:joyceerhl -author:rchiodo" }, { "kind": 1, @@ -42,7 +42,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos $milestone is:closed reason:completed -assignee:@me label:bug -label:verified -label:*duplicate\n" + "value": "$repos $milestone is:closed reason:completed -assignee:@me label:bug -label:verified -label:*duplicate" }, { "kind": 1, @@ -52,6 +52,6 @@ { "kind": 2, "language": "github-issues", - "value": "$repos is:closed linked:pr $closedRecently no:milestone -label:verified -label:*duplicate\n" + "value": "$repos is:closed linked:pr $closedRecently no:milestone -label:verified -label:*duplicate" } ] \ No newline at end of file diff --git a/.vscode/notebooks/vscode-dev.github-issues b/.vscode/notebooks/vscode-dev.github-issues index 314a541823f8c..53fad82e6d9e4 100644 --- a/.vscode/notebooks/vscode-dev.github-issues +++ b/.vscode/notebooks/vscode-dev.github-issues @@ -2,7 +2,7 @@ { "kind": 2, "language": "github-issues", - "value": "$milestone=milestone:\"November 2023\"\n" + "value": "$milestone=milestone:\"November 2023\"" }, { "kind": 1, @@ -12,12 +12,12 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode-dev $milestone is:open\n" + "value": "repo:microsoft/vscode-dev $milestone is:open" }, { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode-dev milestone:\"Backlog\" is:open\n" + "value": "repo:microsoft/vscode-dev milestone:\"Backlog\" is:open" }, { "kind": 1, @@ -27,7 +27,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode label:vscode.dev is:open\n" + "value": "repo:microsoft/vscode label:vscode.dev is:open" }, { "kind": 1, @@ -37,11 +37,11 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode-remote-repositories-github $milestone is:open\n" + "value": "repo:microsoft/vscode-remote-repositories-github $milestone is:open" }, { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode-remotehub $milestone is:open\n" + "value": "repo:microsoft/vscode-remotehub $milestone is:open" } ] \ No newline at end of file From 77fe3a11220e8c5594c3398d666fced868dd6dda Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 23 Jan 2024 16:11:19 -0800 Subject: [PATCH 254/333] fix #203211 --- src/vs/platform/audioCues/browser/audioCueService.ts | 5 +++++ .../audioCues/browser/audioCueLineFeatureContribution.ts | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/audioCues/browser/audioCueService.ts b/src/vs/platform/audioCues/browser/audioCueService.ts index 62fd6b5042def..6de9aaf6c4ea6 100644 --- a/src/vs/platform/audioCues/browser/audioCueService.ts +++ b/src/vs/platform/audioCues/browser/audioCueService.ts @@ -22,6 +22,7 @@ export interface IAudioCueService { isCueEnabled(cue: AudioCue): boolean; isAlertEnabled(cue: AudioCue): boolean; onEnabledChanged(cue: AudioCue): Event; + onAlertEnabledChanged(cue: AudioCue): Event; playSound(cue: Sound, allowManyInParallel?: boolean): Promise; playAudioCueLoop(cue: AudioCue, milliseconds: number): IDisposable; @@ -235,6 +236,10 @@ export class AudioCueService extends Disposable implements IAudioCueService { public onEnabledChanged(cue: AudioCue): Event { return Event.fromObservableLight(this.isCueEnabledCache.get({ cue })); } + + public onAlertEnabledChanged(cue: AudioCue): Event { + return Event.fromObservableLight(this.isAlertEnabledCache.get({ cue })); + } } diff --git a/src/vs/workbench/contrib/audioCues/browser/audioCueLineFeatureContribution.ts b/src/vs/workbench/contrib/audioCues/browser/audioCueLineFeatureContribution.ts index 06a9a8645ed33..e7bba6b3f35b2 100644 --- a/src/vs/workbench/contrib/audioCues/browser/audioCueLineFeatureContribution.ts +++ b/src/vs/workbench/contrib/audioCues/browser/audioCueLineFeatureContribution.ts @@ -37,6 +37,11 @@ export class AudioCueLineFeatureContribution () => this.audioCueService.isCueEnabled(cue) )); + private readonly isAlertEnabledCache = new CachedFunction>((cue) => observableFromEvent( + this.audioCueService.onAlertEnabledChanged(cue), + () => this.audioCueService.isAlertEnabled(cue) + )); + constructor( @IEditorService private readonly editorService: IEditorService, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -47,7 +52,7 @@ export class AudioCueLineFeatureContribution const someAudioCueFeatureIsEnabled = derived( (reader) => /** @description someAudioCueFeatureIsEnabled */ this.features.some((feature) => - this.isEnabledCache.get(feature.audioCue).read(reader) + this.isEnabledCache.get(feature.audioCue).read(reader) || this.isAlertEnabledCache.get(feature.audioCue).read(reader) ) ); @@ -116,7 +121,7 @@ export class AudioCueLineFeatureContribution const isFeaturePresent = derivedOpts( { debugName: `isPresentInLine:${feature.audioCue.name}` }, (reader) => { - if (!this.isEnabledCache.get(feature.audioCue).read(reader)) { + if (!this.isEnabledCache.get(feature.audioCue).read(reader) && !this.isAlertEnabledCache.get(feature.audioCue).read(reader)) { return false; } const position = debouncedPosition.read(reader); From 076bd0bd1e49235e2bffe88de4c6ca4638781532 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 23 Jan 2024 16:16:39 -0800 Subject: [PATCH 255/333] compile issue --- src/vs/editor/standalone/browser/standaloneServices.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index b20bde39b150d..97841a6012baf 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -1077,6 +1077,10 @@ class StandaloneAudioService implements IAudioCueService { return Event.None; } + onAlertEnabledChanged(cue: AudioCue): Event { + return Event.None; + } + async playSound(cue: Sound, allowManyInParallel?: boolean | undefined): Promise { } playAudioCueLoop(cue: AudioCue): IDisposable { From c77b63e865bbb2e8d9bd2196dfb51cb5439d8417 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 23 Jan 2024 17:02:49 -0800 Subject: [PATCH 256/333] Accept cell chat response before running follow up (#203271) --- .../notebook/browser/view/cellParts/chat/cellChatController.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts index 58aa70314cf19..d14a84bed36b4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts @@ -379,11 +379,12 @@ export class NotebookCellChatController extends Disposable { const followups = await this._activeSession.provider.provideFollowups(this._activeSession.session, replyResponse.raw, followupCts.token); if (followups && this._widget) { const widget = this._widget; - widget.updateFollowUps(followups, followup => { + widget.updateFollowUps(followups, async followup => { if (followup.kind === 'reply') { widget.value = followup.message; this.acceptInput(); } else { + await this.acceptSession(); this._commandService.executeCommand(followup.commandId, ...(followup.args ?? [])); } }); From 9c958288987e16c4a959eb669ed8cf3142f562c0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 23 Jan 2024 17:08:58 -0800 Subject: [PATCH 257/333] Enable paste url for file uris (#203270) Fixes #203180 --- .../languageFeatures/copyFiles/pasteUrlProvider.ts | 13 +++++++++++-- .../src/languageFeatures/copyFiles/shared.ts | 6 ------ .../markdown-language-features/src/util/schemes.ts | 2 ++ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts index 86fa146f1af92..68dca194702be 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts @@ -7,7 +7,8 @@ import * as vscode from 'vscode'; import { IMdParser } from '../../markdownEngine'; import { ITextDocument } from '../../types/textDocument'; import { Mime } from '../../util/mimes'; -import { createInsertUriListEdit, externalUriSchemes } from './shared'; +import { createInsertUriListEdit } from './shared'; +import { Schemes } from '../../util/schemes'; export enum PasteUrlAsMarkdownLink { Always = 'always', @@ -170,6 +171,14 @@ async function shouldSmartPasteForSelection( return true; } + +const externalUriSchemes: ReadonlySet = new Set([ + Schemes.http, + Schemes.https, + Schemes.mailto, + Schemes.file, +]); + export function findValidUriInText(text: string): string | undefined { const trimmedUrlList = text.trim(); @@ -186,7 +195,7 @@ export function findValidUriInText(text: string): string | undefined { return; } - if (!externalUriSchemes.includes(uri.scheme.toLowerCase()) || uri.authority.length <= 1) { + if (!externalUriSchemes.has(uri.scheme.toLowerCase()) || uri.authority.length <= 1) { return; } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts index bd2bf0b805349..7881e5938a546 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts @@ -19,12 +19,6 @@ enum MediaKind { Audio, } -export const externalUriSchemes = [ - 'http', - 'https', - 'mailto', -]; - export const mediaFileExtensions = new Map([ // Images ['bmp', MediaKind.Image], diff --git a/extensions/markdown-language-features/src/util/schemes.ts b/extensions/markdown-language-features/src/util/schemes.ts index 3eae0754ad299..dbb3d14d5e2ad 100644 --- a/extensions/markdown-language-features/src/util/schemes.ts +++ b/extensions/markdown-language-features/src/util/schemes.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ export const Schemes = Object.freeze({ + http: 'http', + https: 'https', file: 'file', untitled: 'untitled', mailto: 'mailto', From 1091f68d834974d78cd4f9769034b4ae4581ff2a Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 23 Jan 2024 17:09:38 -0800 Subject: [PATCH 258/333] Fix #203214. Read code window correctly (#203267) * Fix #203214. Read code window correctly * fix diff --- .../notebook/browser/diff/notebookDiffEditor.ts | 4 ++-- .../notebook/browser/notebookEditorWidget.ts | 4 ++-- .../browser/view/renderers/backLayerWebView.ts | 14 ++++++++------ .../workbench/contrib/webview/browser/webview.ts | 2 ++ .../contrib/webview/browser/webviewElement.ts | 3 +-- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts index 5b87a0024a20a..fb2f265296908 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts @@ -484,7 +484,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD }, undefined) as BackLayerWebView; // attach the webview container to the DOM tree first this._list.rowsContainer.insertAdjacentElement('afterbegin', this._modifiedWebview.element); - this._modifiedWebview.createWebview(); + this._modifiedWebview.createWebview(DOM.getActiveWindow()); this._modifiedWebview.element.style.width = `calc(50% - 16px)`; this._modifiedWebview.element.style.left = `calc(50%)`; } @@ -501,7 +501,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD }, undefined) as BackLayerWebView; // attach the webview container to the DOM tree first this._list.rowsContainer.insertAdjacentElement('afterbegin', this._originalWebview.element); - this._originalWebview.createWebview(); + this._originalWebview.createWebview(DOM.getActiveWindow()); this._originalWebview.element.style.width = `calc(50% - 16px)`; this._originalWebview.element.style.left = `16px`; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index e70b4b62870d5..5554136a78c4c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -17,11 +17,11 @@ import 'vs/css!./media/notebookCellOutput'; import 'vs/css!./media/notebookEditorStickyScroll'; import 'vs/css!./media/notebookKernelActionViewItem'; import 'vs/css!./media/notebookOutline'; - import { PixelRatio } from 'vs/base/browser/browser'; import * as DOM from 'vs/base/browser/dom'; import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; +import { mainWindow } from 'vs/base/browser/window'; import { DeferredPromise, SequencerByKey } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Color, RGBA } from 'vs/base/common/color'; @@ -1374,7 +1374,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD throw new Error('Notebook output webview object is not created successfully.'); } - await this._webview.createWebview(); + await this._webview.createWebview(this.creationOptions.codeWindow ?? mainWindow); if (!this._webview.webview) { throw new Error('Notebook output webview element was not created successfully.'); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 6891a9ff4bf30..9488f73671173 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -5,6 +5,7 @@ import { getWindow } from 'vs/base/browser/dom'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { CodeWindow } from 'vs/base/browser/window'; import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; import { coalesce } from 'vs/base/common/arrays'; import { DeferredPromise, runWhenGlobalIdle } from 'vs/base/common/async'; @@ -512,10 +513,10 @@ export class BackLayerWebView extends Themable { return !!this.webview; } - createWebview(): Promise { + createWebview(codeWindow: CodeWindow): Promise { const baseUrl = this.asWebviewUri(this.getNotebookBaseUri(), undefined); const htmlContent = this.generateContent(baseUrl.toString()); - return this._initialize(htmlContent); + return this._initialize(htmlContent, codeWindow); } private getNotebookBaseUri() { @@ -550,12 +551,12 @@ export class BackLayerWebView extends Themable { ]; } - private _initialize(content: string): Promise { + private _initialize(content: string, codeWindow: CodeWindow): Promise { if (!getWindow(this.element).document.body.contains(this.element)) { throw new Error('Element is already detached from the DOM tree'); } - this.webview = this._createInset(this.webviewService, content); + this.webview = this._createInset(this.webviewService, content, codeWindow); this.webview.mountTo(this.element); this._register(this.webview); @@ -1122,7 +1123,7 @@ export class BackLayerWebView extends Themable { await this.openerService.open(newFileUri); } - private _createInset(webviewService: IWebviewService, content: string) { + private _createInset(webviewService: IWebviewService, content: string, codeWindow: CodeWindow) { this.localResourceRootsCache = this._getResourceRootsCache(); const webview = webviewService.createWebviewElement({ origin: BackLayerWebView.getOriginStore(this.storageService).getOrigin(this.notebookViewType, undefined), @@ -1138,7 +1139,8 @@ export class BackLayerWebView extends Themable { localResourceRoots: this.localResourceRootsCache, }, extension: undefined, - providedViewType: 'notebook.output' + providedViewType: 'notebook.output', + codeWindow: codeWindow }); webview.setHtml(content); diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 2a9158a46d1df..dfd7c6fc5b47b 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -5,6 +5,7 @@ import { Dimension } from 'vs/base/browser/dom'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { CodeWindow } from 'vs/base/browser/window'; import { equals } from 'vs/base/common/arrays'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -77,6 +78,7 @@ export interface WebviewInitInfo { readonly contentOptions: WebviewContentOptions; readonly extension: WebviewExtensionDescription | undefined; + readonly codeWindow?: CodeWindow; } export const enum WebviewContentPurpose { diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index a3f4989a1da89..0cfe554c54e8d 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -180,8 +180,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD this._element = this._createElement(initInfo.options, initInfo.contentOptions); - - const subscription = this._register(addDisposableListener(getActiveWindow(), 'message', (e: MessageEvent) => { + const subscription = this._register(addDisposableListener(initInfo.codeWindow ?? getActiveWindow(), 'message', (e: MessageEvent) => { if (!this._encodedWebviewOrigin || e?.data?.target !== this.id) { return; } From 3b8295758652e275c45238280f0a027ff1dd98b0 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:22:28 +0100 Subject: [PATCH 259/333] SCM - fix close repository/close other repositories actions (#203292) --- .../workbench/contrib/scm/browser/scmRepositoriesViewPane.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts index c7d13a3107782..20b33dd78fac4 100644 --- a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts @@ -151,10 +151,7 @@ export class SCMRepositoriesViewPane extends ViewPane { const actions = collectContextMenuActions(menu); const actionRunner = this._register(new RepositoryActionRunner(() => { - const focusedRepositories = this.list.getFocusedElements(); - const selectedRepositories = this.list.getSelectedElements(); - - return Array.from(new Set([...focusedRepositories, ...selectedRepositories])); + return this.list.getSelectedElements(); })); actionRunner.onWillRun(() => this.list.domFocus()); From b27e28197eee3a6b367d5f0f4e1990ab6fa5c1c1 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 24 Jan 2024 10:32:12 +0100 Subject: [PATCH 260/333] remove FeatureInsight tag (#203295) - remove FeatureInsight tag - report event per setting with owner and purpose information - adopt the opted in settings --- src/vs/editor/common/config/editorOptions.ts | 2 +- .../browser/workbench.contribution.ts | 1 - .../contrib/chat/common/chatService.ts | 2 + .../actions/voiceChatActions.ts | 18 +- .../browser/extensions.contribution.ts | 2 +- .../files/browser/files.contribution.ts | 3 +- .../browser/telemetry.contribution.ts | 184 +++++++++++++++++- .../electron-sandbox/desktop.contribution.ts | 7 +- .../browser/configurationService.ts | 96 +-------- 9 files changed, 198 insertions(+), 117 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 73fc32d3b34a5..5d150a58558ed 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2818,7 +2818,7 @@ class EditorStickyScroll extends BaseEditorOption(ConfigurationExtensions.Con localize('workbench.activityBar.location.top', "Show the Activity Bar on top of the Primary Side Bar."), localize('workbench.activityBar.location.hide', "Hide the Activity Bar.") ], - tags: ['FeatureInsight'] }, 'workbench.activityBar.iconClickBehavior': { 'type': 'string', diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 5b90ef33acfdf..3bc59a462ab1f 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -313,3 +313,5 @@ export interface IChatService { transferChatSession(transferredSessionData: IChatTransferredSessionData, toWorkspace: URI): void; } + +export const KEYWORD_ACTIVIATION_SETTING_ID = 'accessibility.voice.keywordActivation'; diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 1666adf085de9..aaa7195ba5a30 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -17,7 +17,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { spinningLoading } from 'vs/platform/theme/common/iconRegistry'; import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions'; import { IChatWidget, IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; -import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatService, KEYWORD_ACTIVIATION_SETTING_ID } from 'vs/workbench/contrib/chat/common/chatService'; import { CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, MENU_INLINE_CHAT_INPUT } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { CONTEXT_CHAT_REQUEST_IN_PROGRESS, CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; @@ -771,15 +771,13 @@ function supportsKeywordActivation(configurationService: IConfigurationService, return false; } - const value = configurationService.getValue(KeywordActivationContribution.SETTINGS_ID); + const value = configurationService.getValue(KEYWORD_ACTIVIATION_SETTING_ID); return typeof value === 'string' && value !== KeywordActivationContribution.SETTINGS_VALUE.OFF; } export class KeywordActivationContribution extends Disposable implements IWorkbenchContribution { - static SETTINGS_ID = 'accessibility.voice.keywordActivation'; - static SETTINGS_VALUE = { OFF: 'off', INLINE_CHAT: 'inlineChat', @@ -817,7 +815,7 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe this._register(this.speechService.onDidEndSpeechToTextSession(() => this.handleKeywordActivation())); this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(KeywordActivationContribution.SETTINGS_ID)) { + if (e.affectsConfiguration(KEYWORD_ACTIVIATION_SETTING_ID)) { this.handleKeywordActivation(); } })); @@ -836,7 +834,7 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe registry.registerConfiguration({ ...accessibilityConfigurationNodeBase, properties: { - [KeywordActivationContribution.SETTINGS_ID]: { + [KEYWORD_ACTIVIATION_SETTING_ID]: { 'type': 'string', 'enum': [ KeywordActivationContribution.SETTINGS_VALUE.OFF, @@ -854,7 +852,7 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe ], 'description': localize('voice.keywordActivation', "Controls whether the keyword phrase 'Hey Code' is recognized to start a voice chat session. Enabling this will start recording from the microphone but the audio is processed locally and never sent to a server."), 'default': 'off', - 'tags': ['accessibility', 'FeatureInsight'] + 'tags': ['accessibility'] } } }); @@ -905,7 +903,7 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe } private getKeywordCommand(): string { - const setting = this.configurationService.getValue(KeywordActivationContribution.SETTINGS_ID); + const setting = this.configurationService.getValue(KEYWORD_ACTIVIATION_SETTING_ID); switch (setting) { case KeywordActivationContribution.SETTINGS_VALUE.INLINE_CHAT: return InlineVoiceChatAction.ID; @@ -949,7 +947,7 @@ class KeywordActivationStatusEntry extends Disposable { ) { super(); - CommandsRegistry.registerCommand(KeywordActivationStatusEntry.STATUS_COMMAND, () => this.commandService.executeCommand('workbench.action.openSettings', KeywordActivationContribution.SETTINGS_ID)); + CommandsRegistry.registerCommand(KeywordActivationStatusEntry.STATUS_COMMAND, () => this.commandService.executeCommand('workbench.action.openSettings', KEYWORD_ACTIVIATION_SETTING_ID)); this.registerListeners(); this.updateStatusEntry(); @@ -959,7 +957,7 @@ class KeywordActivationStatusEntry extends Disposable { this._register(this.speechService.onDidStartKeywordRecognition(() => this.updateStatusEntry())); this._register(this.speechService.onDidEndKeywordRecognition(() => this.updateStatusEntry())); this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(KeywordActivationContribution.SETTINGS_ID)) { + if (e.affectsConfiguration(KEYWORD_ACTIVIATION_SETTING_ID)) { this.updateStatusEntry(); } })); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 31972316d6521..2254a20509238 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -146,7 +146,7 @@ Registry.as(ConfigurationExtensions.Configuration) description: localize('extensions.autoUpdate', "Controls the automatic update behavior of extensions. The updates are fetched from a Microsoft online service."), default: true, scope: ConfigurationScope.APPLICATION, - tags: ['usesOnlineServices', 'FeatureInsight'] + tags: ['usesOnlineServices'] }, 'extensions.autoCheckUpdates': { type: 'boolean', diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 0ea210b535de6..efd9fe59af700 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -251,8 +251,7 @@ configurationRegistry.registerConfiguration({ ], 'default': isWeb ? AutoSaveConfiguration.AFTER_DELAY : AutoSaveConfiguration.OFF, 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSave' }, "Controls [auto save](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) of editors that have unsaved changes.", AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE, AutoSaveConfiguration.AFTER_DELAY), - scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, - tags: ['FeatureInsight'] + scope: ConfigurationScope.LANGUAGE_OVERRIDABLE }, 'files.autoSaveDelay': { 'type': 'number', diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 5293305cc5668..d52e62ee7a7df 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -16,10 +16,11 @@ import { language } from 'vs/base/common/platform'; import { Disposable } from 'vs/base/common/lifecycle'; import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; import { TelemetryTrustedValue } from 'vs/platform/telemetry/common/telemetryUtils'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationTarget, ConfigurationTargetToString, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextFileService, ITextFileSaveEvent, ITextFileResolveEvent } from 'vs/workbench/services/textfile/common/textfiles'; import { extname, basename, isEqual, isEqualOrParent } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; +import { Event } from 'vs/base/common/event'; import { Schemas } from 'vs/base/common/network'; import { getMimeTypes } from 'vs/editor/common/services/languagesAssociations'; import { hash } from 'vs/base/common/hash'; @@ -27,6 +28,11 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b import { ViewContainerLocation } from 'vs/workbench/common/views'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { mainWindow } from 'vs/base/browser/window'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { isBoolean, isNumber, isString } from 'vs/base/common/types'; +import { LayoutSettings } from 'vs/workbench/services/layout/browser/layoutService'; +import { AutoUpdateConfigurationKey } from 'vs/workbench/contrib/extensions/common/extensions'; +import { KEYWORD_ACTIVIATION_SETTING_ID } from 'vs/workbench/contrib/chat/common/chatService'; type TelemetryData = { mimeType: TelemetryTrustedValue; @@ -58,7 +64,6 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr @IWorkbenchThemeService themeService: IWorkbenchThemeService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, - @IConfigurationService configurationService: IConfigurationService, @IPaneCompositePartService paneCompositeService: IPaneCompositePartService, @ITextFileService textFileService: ITextFileService ) { @@ -229,4 +234,177 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(TelemetryContribution, LifecyclePhase.Restored); +class ConfigurationTelemetryContribution extends Disposable implements IWorkbenchContribution { + + private readonly configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + ) { + super(); + + // Debounce the event by 1000 ms and merge all affected keys into one event + const debouncedConfigService = Event.debounce(configurationService.onDidChangeConfiguration, (last, cur) => { + const newAffectedKeys: ReadonlySet = last ? new Set([...last.affectedKeys, ...cur.affectedKeys]) : cur.affectedKeys; + return { ...cur, affectedKeys: newAffectedKeys }; + }, 1000, true); + + debouncedConfigService(event => { + if (event.source !== ConfigurationTarget.DEFAULT) { + type UpdateConfigurationClassification = { + owner: 'sandy081'; + comment: 'Event which fires when user updates settings'; + configurationSource: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'What configuration file was updated i.e user or workspace' }; + configurationKeys: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'What configuration keys were updated' }; + }; + type UpdateConfigurationEvent = { + configurationSource: string; + configurationKeys: string[]; + }; + telemetryService.publicLog2('updateConfiguration', { + configurationSource: ConfigurationTargetToString(event.source), + configurationKeys: Array.from(event.affectedKeys) + }); + } + }); + + const { user, workspace } = configurationService.keys(); + for (const setting of user) { + this.reportTelemetry(setting, ConfigurationTarget.USER_LOCAL); + } + for (const setting of workspace) { + this.reportTelemetry(setting, ConfigurationTarget.WORKSPACE); + } + } + + /** + * Report value of a setting only if it is an enum, boolean, or number or an array of those. + */ + private getValueToReport(key: string, target: ConfigurationTarget.USER_LOCAL | ConfigurationTarget.WORKSPACE): any { + const schema = this.configurationRegistry.getConfigurationProperties()[key]; + const inpsectData = this.configurationService.inspect(key); + const value = target === ConfigurationTarget.USER_LOCAL ? inpsectData.user?.value : inpsectData.workspace?.value; + if (isNumber(value) || isBoolean(value)) { + return value; + } + if (isString(value)) { + if (schema?.enum?.includes(value)) { + return value; + } + return undefined; + } + if (Array.isArray(value)) { + if (value.every(v => isNumber(v) || isBoolean(v) || (isString(v) && schema?.enum?.includes(v)))) { + return value; + } + } + return undefined; + } + + private reportTelemetry(key: string, target: ConfigurationTarget.USER_LOCAL | ConfigurationTarget.WORKSPACE): void { + type UpdatedSettingEvent = { + value: any; + source: string; + }; + const source = ConfigurationTargetToString(target); + + switch (key) { + + case LayoutSettings.ACTIVITY_BAR_LOCATION: + this.telemetryService.publicLog2('workbench.activityBar.location', { value: this.getValueToReport(key, target), source }); + return; + + case AutoUpdateConfigurationKey: + this.telemetryService.publicLog2('extensions.autoUpdate', { value: this.getValueToReport(key, target), source }); + return; + + case 'files.autoSave': + this.telemetryService.publicLog2('files.autoSave', { value: this.getValueToReport(key, target), source }); + return; + + case 'editor.stickyScroll.enabled': + this.telemetryService.publicLog2('editor.stickyScroll.enabled', { value: this.getValueToReport(key, target), source }); + return; + + case KEYWORD_ACTIVIATION_SETTING_ID: + this.telemetryService.publicLog2('accessibility.voice.keywordActivation', { value: this.getValueToReport(key, target), source }); + return; + + case 'window.zoomLevel': + this.telemetryService.publicLog2('window.zoomLevel', { value: this.getValueToReport(key, target), source }); + return; + + case 'window.zoomPerWindow': + this.telemetryService.publicLog2('window.zoomPerWindow', { value: this.getValueToReport(key, target), source }); + return; + + case 'window.titleBarStyle': + this.telemetryService.publicLog2('window.titleBarStyle', { value: this.getValueToReport(key, target), source }); + return; + + case 'window.customTitleBarVisibility': + this.telemetryService.publicLog2('window.customTitleBarVisibility', { value: this.getValueToReport(key, target), source }); + return; + + case 'window.nativeTabs': + this.telemetryService.publicLog2('window.nativeTabs', { value: this.getValueToReport(key, target), source }); + return; + } + } + +} + +const workbenchContributionRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchContributionRegistry.registerWorkbenchContribution(TelemetryContribution, LifecyclePhase.Restored); +workbenchContributionRegistry.registerWorkbenchContribution(ConfigurationTelemetryContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index ffcd7a7a0a5bb..1c38985809b75 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -195,13 +195,13 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand 'maximum': MAX_ZOOM_LEVEL, 'markdownDescription': localize({ comment: ['{0} will be a setting name rendered as a link'], key: 'zoomLevel' }, "Adjust the default zoom level for all windows. Each increment above `0` (e.g. `1`) or below (e.g. `-1`) represents zooming `20%` larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity. See {0} for configuring if the 'Zoom In' and 'Zoom Out' commands apply the zoom level to all windows or only the active window.", '`#window.zoomPerWindow#`'), ignoreSync: true, - tags: ['accessibility', 'FeatureInsight'] + tags: ['accessibility'] }, 'window.zoomPerWindow': { 'type': 'boolean', 'default': true, 'markdownDescription': localize({ comment: ['{0} will be a setting name rendered as a link'], key: 'zoomPerWindow' }, "Controls if the 'Zoom In' and 'Zoom Out' commands apply the zoom level to all windows or only the active window. See {0} for configuring a default zoom level for all windows.", '`#window.zoomLevel#`'), - tags: ['accessibility', 'FeatureInsight'] + tags: ['accessibility'] }, 'window.newWindowDimensions': { 'type': 'string', @@ -234,7 +234,6 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand 'default': isLinux ? 'native' : 'custom', 'scope': ConfigurationScope.APPLICATION, 'description': localize('titleBarStyle', "Adjust the appearance of the window title bar to be native by the OS or custom. On Linux and Windows, this setting also affects the application and context menu appearances. Changes require a full restart to apply."), - tags: ['FeatureInsight'] }, 'window.customTitleBarVisibility': { 'type': 'string', @@ -247,7 +246,6 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand 'default': isLinux ? 'never' : 'auto', 'scope': ConfigurationScope.APPLICATION, 'description': localize('window.customTitleBarVisibility', "Adjust when the custom title bar should be shown."), - tags: ['FeatureInsight'] }, 'window.dialogStyle': { 'type': 'string', @@ -262,7 +260,6 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand 'scope': ConfigurationScope.APPLICATION, 'description': localize('window.nativeTabs', "Enables macOS Sierra window tabs. Note that changes require a full restart to apply and that native tabs will disable a custom title bar style if configured."), 'included': isMacintosh, - tags: ['FeatureInsight'] }, 'window.nativeFullScreen': { 'type': 'boolean', diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index f3cc5a0eff647..c15c42669a34f 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -17,7 +17,7 @@ import { IPolicyConfiguration, NullPolicyConfiguration, PolicyConfiguration } fr import { Configuration } from 'vs/workbench/services/configuration/common/configurationModels'; import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, IWorkbenchConfigurationService, RestrictedSettings, PROFILE_SCOPES, LOCAL_MACHINE_PROFILE_SCOPES, profileSettingsSchemaId, APPLY_ALL_PROFILES_SETTING } from 'vs/workbench/services/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings, ConfigurationScope, IConfigurationPropertySchema, keyFromOverrideIdentifiers, OVERRIDE_PROPERTY_PATTERN, resourceLanguageSettingsSchemaId, configurationDefaultsSchemaId, IRegisteredConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings, machineOverridableSettings, ConfigurationScope, IConfigurationPropertySchema, keyFromOverrideIdentifiers, OVERRIDE_PROPERTY_PATTERN, resourceLanguageSettingsSchemaId, configurationDefaultsSchemaId } from 'vs/platform/configuration/common/configurationRegistry'; import { IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, getStoredWorkspaceFolder, toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ConfigurationEditing, EditableConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; @@ -37,7 +37,7 @@ import { delta, distinct, equals as arrayEquals } from 'vs/base/common/arrays'; import { IStringDictionary } from 'vs/base/common/collections'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; -import { isBoolean, isNumber, isString, isUndefined } from 'vs/base/common/types'; +import { isUndefined } from 'vs/base/common/types'; import { localize } from 'vs/nls'; import { DidChangeUserDataProfileEvent, IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; @@ -47,7 +47,6 @@ import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/envir import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; import { mainWindow } from 'vs/base/browser/window'; import { runWhenWindowIdle } from 'vs/base/browser/dom'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; function getLocalUserConfigurationScopes(userDataProfile: IUserDataProfile, hasRemote: boolean): ConfigurationScope[] | undefined { return (userDataProfile.isDefault || userDataProfile.useDefaultFlags?.settings) @@ -1325,96 +1324,6 @@ class ResetConfigurationDefaultsOverridesCache extends Disposable implements IWo } } -class ConfigurationTelemetryContribution extends Disposable implements IWorkbenchContribution { - - private readonly configurationRegistry = Registry.as(Extensions.Configuration); - - constructor( - @IConfigurationService private readonly configurationService: WorkspaceService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - ) { - super(); - - // Debounce the event by 1000 ms and merge all affected keys into one event - const debouncedConfigService = Event.debounce(configurationService.onDidChangeConfiguration, (last, cur) => { - const newAffectedKeys: ReadonlySet = last ? new Set([...last.affectedKeys, ...cur.affectedKeys]) : cur.affectedKeys; - return { ...cur, affectedKeys: newAffectedKeys }; - }, 1000, true); - - debouncedConfigService(event => { - if (event.source !== ConfigurationTarget.DEFAULT) { - type UpdateConfigurationClassification = { - owner: 'sandy081'; - comment: 'Event which fires when user updates settings'; - configurationSource: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'What configuration file was updated i.e user or workspace' }; - configurationKeys: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'What configuration keys were updated' }; - }; - type UpdateConfigurationEvent = { - configurationSource: string; - configurationKeys: string[]; - }; - telemetryService.publicLog2('updateConfiguration', { - configurationSource: ConfigurationTargetToString(event.source), - configurationKeys: Array.from(event.affectedKeys) - }); - } - }); - - type UpdatedSettingClassification = { - owner: 'sandy081'; - comment: 'Event reporting the updated setting'; - setting: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'name of the setting' }; - value: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'value of the setting' }; - source: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'source of the setting' }; - }; - type UpdatedSettingEvent = { - setting: string; - value: string; - source: string; - }; - const { user, workspace } = configurationService.keys(); - const userSource = ConfigurationTargetToString(ConfigurationTarget.USER_LOCAL); - for (const setting of user) { - const schema = this.configurationRegistry.getConfigurationProperties()[setting]; - if (schema?.tags?.includes('FeatureInsight')) { - const value = this.getValueToReport(setting, ConfigurationTarget.USER_LOCAL, schema); - this.telemetryService.publicLog2('updatedsetting', { setting, value, source: userSource }); - } - } - const worskpaceSource = ConfigurationTargetToString(ConfigurationTarget.WORKSPACE); - for (const setting of workspace) { - const schema = this.configurationRegistry.getConfigurationProperties()[setting]; - if (schema?.tags?.includes('FeatureInsight')) { - const value = this.getValueToReport(setting, ConfigurationTarget.WORKSPACE, schema); - this.telemetryService.publicLog2('updatedsetting', { setting, value, source: worskpaceSource }); - } - } - } - - /** - * Report value of a setting only if it is an enum, boolean, or number or an array of those. - */ - private getValueToReport(key: string, target: ConfigurationTarget, schema: IRegisteredConfigurationPropertySchema): any { - const configurationModel = this.configurationService.getConfigurationModel(target); - const value = configurationModel?.getValue(key); - if (isNumber(value) || isBoolean(value)) { - return value; - } - if (isString(value)) { - if (schema.enum?.includes(value)) { - return value; - } - return undefined; - } - if (Array.isArray(value)) { - if (value.every(v => isNumber(v) || isBoolean(v) || (isString(v) && schema.enum?.includes(v)))) { - return value; - } - } - return undefined; - } -} - class UpdateExperimentalSettingsDefaults extends Disposable implements IWorkbenchContribution { private readonly processedExperimentalSettings = new Set(); @@ -1457,7 +1366,6 @@ const workbenchContributionsRegistry = Registry.as(Extensions.Configuration); configurationRegistry.registerConfiguration({ From afd4f631aa6703773767567d8d291e431ae77bc4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 24 Jan 2024 10:40:58 +0100 Subject: [PATCH 261/333] inline chat fixes (#203297) * Pause state should be more relaxed and expect partial state fixes https://github.com/microsoft/vscode/issues/203173 * clear stashed session before starting a new session fixes https://github.com/microsoft/vscode-copilot/issues/3719 --- .../contrib/inlineChat/browser/inlineChatController.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index ca86cd75143b7..fd19bd595a9e0 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -247,6 +247,7 @@ export class InlineChatController implements IEditorContribution { } this._historyOffset = -1; this._historyCandidate = ''; + this._stashedSession.clear(); this._onWillStartSession.fire(); this._currentRun = this._nextState(State.CREATE_SESSION, options); await this._currentRun; @@ -815,12 +816,10 @@ export class InlineChatController implements IEditorContribution { } private async[State.PAUSE]() { - assertType(this._session); - assertType(this._strategy); this._resetWidget(); - this._strategy.pause?.(); + this._strategy?.pause?.(); this._session = undefined; } From 8458f875df6190889248c68568efe66c31a792ca Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:03:54 +0100 Subject: [PATCH 262/333] Custom Titlebar Menu Fix (#203300) custom titlebar menu fix --- .../browser/parts/titlebar/titlebarActions.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index a528f6ab90cb9..1ed7b88b35ae6 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -106,8 +106,24 @@ class ToggleCustomTitleBar extends Action2 { title: localize('toggle.customTitleBar', 'Custom Title Bar'), toggled: TitleBarVisibleContext, menu: [ - { id: MenuId.MenubarAppearanceMenu, order: 6, when: ContextKeyExpr.or(ContextKeyExpr.equals(TitleBarStyleContext.key, TitlebarStyle.NATIVE), IsMainWindowFullscreenContext), group: '2_workbench_layout' }, - ] + { + id: MenuId.MenubarAppearanceMenu, + order: 6, + when: ContextKeyExpr.or( + ContextKeyExpr.and( + ContextKeyExpr.equals(TitleBarStyleContext.key, TitlebarStyle.NATIVE), + ContextKeyExpr.and( + ContextKeyExpr.equals('config.workbench.layoutControl.enabled', false), + ContextKeyExpr.equals('config.window.commandCenter', false), + ContextKeyExpr.notEquals('config.workbench.editor.editorActionsLocation', 'titleBar'), + ContextKeyExpr.notEquals('config.workbench.activityBar.location', 'top') + )?.negate() + ), + IsMainWindowFullscreenContext + ), + group: '2_workbench_layout' + }, + ], }); } From 4a3c073d9e5214d667e873888cde5c4446e70e05 Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:12:27 +0100 Subject: [PATCH 263/333] Fix #203235 (#203302) fix #203235 --- 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 3e2fd54542128..c3ec65eaee1ee 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -52,7 +52,7 @@ const registry = Registry.as(ConfigurationExtensions.Con [LayoutSettings.EDITOR_ACTIONS_LOCATION]: { 'type': 'string', 'enum': [EditorActionsLocation.DEFAULT, EditorActionsLocation.TITLEBAR, EditorActionsLocation.HIDDEN], - 'enumDescriptions': [ + 'markdownEnumDescriptions': [ localize({ comment: ['{0} will be a setting name rendered as a link'], key: 'workbench.editor.editorActionsLocation.default' }, "Show editor actions in the window title bar when {0} is set to {1}. Otherwise, editor actions are shown in the editor tab bar.", '`#workbench.editor.showTabs#`', '`none`'), localize({ comment: ['{0} will be a setting name rendered as a link'], key: 'workbench.editor.editorActionsLocation.titleBar' }, "Show editor actions in the window title bar. If {0} is set to {1}, editor actions are hidden.", '`#window.customTitleBarVisibility#`', '`never`'), localize('workbench.editor.editorActionsLocation.hidden', "Editor actions are not shown."), From 205e4e53a1415424c6e2194017bb4395aba143d6 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 24 Jan 2024 11:19:17 +0100 Subject: [PATCH 264/333] Fixes #201713 (#201809) * Fixes #201713 --- .../heuristicSequenceOptimizations.ts | 2 +- .../node/diffing/fixtures/issue-201713/1.tst | 13 ++++++ .../node/diffing/fixtures/issue-201713/2.tst | 20 +++++++++ .../issue-201713/advanced.expected.diff.json | 44 +++++++++++++++++++ .../issue-201713/legacy.expected.diff.json | 39 ++++++++++++++++ .../advanced.expected.diff.json | 2 +- .../advanced.expected.diff.json | 2 +- 7 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 src/vs/editor/test/node/diffing/fixtures/issue-201713/1.tst create mode 100644 src/vs/editor/test/node/diffing/fixtures/issue-201713/2.tst create mode 100644 src/vs/editor/test/node/diffing/fixtures/issue-201713/advanced.expected.diff.json create mode 100644 src/vs/editor/test/node/diffing/fixtures/issue-201713/legacy.expected.diff.json diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts index 08ead77d7c6c0..08efd57813629 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts @@ -272,7 +272,7 @@ export function extendDiffsToEntireWordIfAppropriate(sequence1: LinesSliceCharSe } if (equalChars1 + equalChars2 < (w.seq1Range.length + w.seq2Range.length) * 2 / 3) { - additional.push(new SequenceDiff(w1, w2)); + additional.push(w); } lastPoint = w.getEndExclusives(); diff --git a/src/vs/editor/test/node/diffing/fixtures/issue-201713/1.tst b/src/vs/editor/test/node/diffing/fixtures/issue-201713/1.tst new file mode 100644 index 0000000000000..1287957879c22 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/issue-201713/1.tst @@ -0,0 +1,13 @@ +const deletedCodeLineBreaksComputer = !renderSideBySide ? this._editors.modified._getViewModel()?.createLineBreaksComputer() : undefined; +if (deletedCodeLineBreaksComputer) { + for (const a of alignmentsVal) { + if (a.diff) { + for (let i = a.originalRange.startLineNumber; i < a.originalRange.endLineNumberExclusive; i++) { + deletedCodeLineBreaksComputer?.addRequest(this._editors.original.getModel()!.getLineContent(i), null, null); + } + } + } +} + +const lineBreakData = deletedCodeLineBreaksComputer?.finalize() ?? []; +let lineBreakDataIdx = 0; \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/issue-201713/2.tst b/src/vs/editor/test/node/diffing/fixtures/issue-201713/2.tst new file mode 100644 index 0000000000000..3b91ab5630236 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/issue-201713/2.tst @@ -0,0 +1,20 @@ +const deletedCodeLineBreaksComputer = !renderSideBySide ? this._editors.modified._getViewModel()?.createLineBreaksComputer() : undefined; +if (deletedCodeLineBreaksComputer) { + const originalModel = this._editors.original.getModel()!; + for (const a of alignmentsVal) { + if (a.diff) { + for (let i = a.originalRange.startLineNumber; i < a.originalRange.endLineNumberExclusive; i++) { + // `i` can be out of bound when the diff has not been updated yet. + // In this case, we do an early return. + // TODO@hediet: Fix this by applying the edit directly to the diff model, so that the diff is always valid. + if (i > originalModel.getLineCount()) { + return { orig: origViewZones, mod: modViewZones }; + } + deletedCodeLineBreaksComputer?.addRequest(originalModel.getLineContent(i), null, null); + } + } + } +} + +const lineBreakData = deletedCodeLineBreaksComputer?.finalize() ?? []; +let lineBreakDataIdx = 0; \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/issue-201713/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/issue-201713/advanced.expected.diff.json new file mode 100644 index 0000000000000..a3aebbb57ed9c --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/issue-201713/advanced.expected.diff.json @@ -0,0 +1,44 @@ +{ + "original": { + "content": "const deletedCodeLineBreaksComputer = !renderSideBySide ? this._editors.modified._getViewModel()?.createLineBreaksComputer() : undefined;\nif (deletedCodeLineBreaksComputer) {\n\tfor (const a of alignmentsVal) {\n\t\tif (a.diff) {\n\t\t\tfor (let i = a.originalRange.startLineNumber; i < a.originalRange.endLineNumberExclusive; i++) {\n\t\t\t\tdeletedCodeLineBreaksComputer?.addRequest(this._editors.original.getModel()!.getLineContent(i), null, null);\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst lineBreakData = deletedCodeLineBreaksComputer?.finalize() ?? [];\nlet lineBreakDataIdx = 0;", + "fileName": "./1.tst" + }, + "modified": { + "content": "const deletedCodeLineBreaksComputer = !renderSideBySide ? this._editors.modified._getViewModel()?.createLineBreaksComputer() : undefined;\nif (deletedCodeLineBreaksComputer) {\n\tconst originalModel = this._editors.original.getModel()!;\n\tfor (const a of alignmentsVal) {\n\t\tif (a.diff) {\n\t\t\tfor (let i = a.originalRange.startLineNumber; i < a.originalRange.endLineNumberExclusive; i++) {\n\t\t\t\t// `i` can be out of bound when the diff has not been updated yet.\n\t\t\t\t// In this case, we do an early return.\n\t\t\t\t// TODO@hediet: Fix this by applying the edit directly to the diff model, so that the diff is always valid.\n\t\t\t\tif (i > originalModel.getLineCount()) {\n\t\t\t\t\treturn { orig: origViewZones, mod: modViewZones };\n\t\t\t\t}\n\t\t\t\tdeletedCodeLineBreaksComputer?.addRequest(originalModel.getLineContent(i), null, null);\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst lineBreakData = deletedCodeLineBreaksComputer?.finalize() ?? [];\nlet lineBreakDataIdx = 0;", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[3,3)", + "modifiedRange": "[3,4)", + "innerChanges": [ + { + "originalRange": "[3,1 -> 3,1]", + "modifiedRange": "[3,1 -> 4,1]" + } + ] + }, + { + "originalRange": "[6,7)", + "modifiedRange": "[7,14)", + "innerChanges": [ + { + "originalRange": "[6,1 -> 6,34]", + "modifiedRange": "[7,1 -> 13,34]" + }, + { + "originalRange": "[6,47 -> 6,61]", + "modifiedRange": "[13,47 -> 13,47]" + }, + { + "originalRange": "[6,69 -> 6,73]", + "modifiedRange": "[13,55 -> 13,55]" + }, + { + "originalRange": "[6,78 -> 6,81]", + "modifiedRange": "[13,60 -> 13,60]" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/issue-201713/legacy.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/issue-201713/legacy.expected.diff.json new file mode 100644 index 0000000000000..57d210b6c641b --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/issue-201713/legacy.expected.diff.json @@ -0,0 +1,39 @@ +{ + "original": { + "content": "const deletedCodeLineBreaksComputer = !renderSideBySide ? this._editors.modified._getViewModel()?.createLineBreaksComputer() : undefined;\nif (deletedCodeLineBreaksComputer) {\n\tfor (const a of alignmentsVal) {\n\t\tif (a.diff) {\n\t\t\tfor (let i = a.originalRange.startLineNumber; i < a.originalRange.endLineNumberExclusive; i++) {\n\t\t\t\tdeletedCodeLineBreaksComputer?.addRequest(this._editors.original.getModel()!.getLineContent(i), null, null);\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst lineBreakData = deletedCodeLineBreaksComputer?.finalize() ?? [];\nlet lineBreakDataIdx = 0;", + "fileName": "./1.tst" + }, + "modified": { + "content": "const deletedCodeLineBreaksComputer = !renderSideBySide ? this._editors.modified._getViewModel()?.createLineBreaksComputer() : undefined;\nif (deletedCodeLineBreaksComputer) {\n\tconst originalModel = this._editors.original.getModel()!;\n\tfor (const a of alignmentsVal) {\n\t\tif (a.diff) {\n\t\t\tfor (let i = a.originalRange.startLineNumber; i < a.originalRange.endLineNumberExclusive; i++) {\n\t\t\t\t// `i` can be out of bound when the diff has not been updated yet.\n\t\t\t\t// In this case, we do an early return.\n\t\t\t\t// TODO@hediet: Fix this by applying the edit directly to the diff model, so that the diff is always valid.\n\t\t\t\tif (i > originalModel.getLineCount()) {\n\t\t\t\t\treturn { orig: origViewZones, mod: modViewZones };\n\t\t\t\t}\n\t\t\t\tdeletedCodeLineBreaksComputer?.addRequest(originalModel.getLineContent(i), null, null);\n\t\t\t}\n\t\t}\n\t}\n}\n\nconst lineBreakData = deletedCodeLineBreaksComputer?.finalize() ?? [];\nlet lineBreakDataIdx = 0;", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[3,3)", + "modifiedRange": "[3,4)", + "innerChanges": null + }, + { + "originalRange": "[6,7)", + "modifiedRange": "[7,14)", + "innerChanges": [ + { + "originalRange": "[6,1 -> 6,1]", + "modifiedRange": "[7,1 -> 13,1]" + }, + { + "originalRange": "[6,47 -> 6,61]", + "modifiedRange": "[13,47 -> 13,47]" + }, + { + "originalRange": "[6,69 -> 6,73]", + "modifiedRange": "[13,55 -> 13,55]" + }, + { + "originalRange": "[6,78 -> 6,81]", + "modifiedRange": "[13,60 -> 13,60]" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/ts-confusing-2/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ts-confusing-2/advanced.expected.diff.json index e5ad584f33e3b..71a6075ada89b 100644 --- a/src/vs/editor/test/node/diffing/fixtures/ts-confusing-2/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/ts-confusing-2/advanced.expected.diff.json @@ -75,7 +75,7 @@ "modifiedRange": "[23,25)", "innerChanges": [ { - "originalRange": "[25,9 -> 26,13]", + "originalRange": "[25,9 -> 26,18]", "modifiedRange": "[23,9 -> 23,18]" }, { diff --git a/src/vs/editor/test/node/diffing/fixtures/word-shared-letters/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/word-shared-letters/advanced.expected.diff.json index 7d964a9441916..9396ddae0c8a9 100644 --- a/src/vs/editor/test/node/diffing/fixtures/word-shared-letters/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/word-shared-letters/advanced.expected.diff.json @@ -112,7 +112,7 @@ "innerChanges": [ { "originalRange": "[53,8 -> 53,17]", - "modifiedRange": "[53,8 -> 53,28]" + "modifiedRange": "[53,8 -> 53,31]" } ] } From c75ad1fd11fb0d8d24621db4408c93c240033f0b Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:41:01 +0100 Subject: [PATCH 265/333] Fix for Sticky Scroll Focus Space Key Issue (#203314) sticky scroll focus "Space" fix --- src/vs/base/browser/ui/tree/abstractTree.ts | 6 ++++-- src/vs/workbench/browser/actions/listCommands.ts | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index f6d5ac1ad81bc..788d9c23852c1 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -1767,8 +1767,10 @@ class StickyScrollFocus extends Disposable { private _domHasFocus: boolean = false; private get domHasFocus(): boolean { return this._domHasFocus; } private set domHasFocus(hasFocus: boolean) { - this._domHasFocus = hasFocus; - this._onDidChangeHasFocus.fire(hasFocus); + if (hasFocus !== this._domHasFocus) { + this._onDidChangeHasFocus.fire(hasFocus); + this._domHasFocus = hasFocus; + } } constructor( diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index e4482953dd8a2..33f21c3b41b06 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -266,7 +266,7 @@ function revealFocusedStickyScroll(tree: ObjectTree | DataTree widget.setSelection([focus])); + revealFocusedStickyScroll(widget); } }); From fa7c021a82036a3ecc3a3e6aa70c41e499b8e954 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:54:39 +0100 Subject: [PATCH 266/333] SCM - fix treeview comparison function (#203325) --- .../contrib/scm/browser/scmViewPane.ts | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 562bc92778030..336f381c964b6 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1171,28 +1171,16 @@ export class SCMTreeSorter implements ITreeSorter { return 1; } - if (isSCMViewSeparator(one)) { - if (!isSCMHistoryItemGroupTreeElement(other) && !isSCMResourceGroup(other)) { - throw new Error('Invalid comparison'); - } - - return 0; - } - if (isSCMResourceGroup(one)) { - if (!isSCMResourceGroup(other)) { - throw new Error('Invalid comparison'); - } + return isSCMResourceGroup(other) ? 0 : -1; + } - return 0; + if (isSCMViewSeparator(one)) { + return isSCMResourceGroup(other) ? 1 : -1; } if (isSCMHistoryItemGroupTreeElement(one)) { - if (!isSCMHistoryItemGroupTreeElement(other) && !isSCMResourceGroup(other) && !isSCMViewSeparator(other)) { - throw new Error('Invalid comparison'); - } - - return 0; + return isSCMHistoryItemGroupTreeElement(other) ? 0 : 1; } if (isSCMHistoryItemTreeElement(one)) { From 1f607d67c507091d70c66e9bedfb7704560f87e7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 24 Jan 2024 13:08:06 +0100 Subject: [PATCH 267/333] fix https://github.com/microsoft/vscode-copilot/issues/3652 (#203305) --- .../contrib/inlineChat/browser/inlineChatController.ts | 2 +- .../contrib/inlineChat/browser/inlineChatStrategies.ts | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index fd19bd595a9e0..bdc49d917ef2e 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -819,7 +819,7 @@ export class InlineChatController implements IEditorContribution { this._resetWidget(); - this._strategy?.pause?.(); + this._strategy?.dispose?.(); this._session = undefined; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index dab83d92c176d..4c347325e643b 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -61,7 +61,6 @@ export abstract class EditModeStrategy { readonly onDidDiscard: Event = this._onDidDiscard.event; toggleDiff?: () => any; - pause?: () => any; constructor( protected readonly _session: Session, @@ -466,10 +465,6 @@ export class LiveStrategy extends EditModeStrategy { } } - override pause = () => { - this._ctxCurrentChangeShowsDiff.reset(); - }; - async apply() { this._resetDiff(); if (this._editCount > 0) { From 7970376dda21f53bb592d6f723a589f89975991f Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 24 Jan 2024 13:08:41 +0100 Subject: [PATCH 268/333] Protect against empty readonly-message (#203328) Fixes #203122 --- src/vs/workbench/api/common/extHostFileSystem.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostFileSystem.ts b/src/vs/workbench/api/common/extHostFileSystem.ts index 8d06a8dd0250c..dc740ddf39772 100644 --- a/src/vs/workbench/api/common/extHostFileSystem.ts +++ b/src/vs/workbench/api/common/extHostFileSystem.ts @@ -166,7 +166,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { } let readOnlyMessage: IMarkdownString | undefined; - if (options.isReadonly && isMarkdownString(options.isReadonly)) { + if (options.isReadonly && isMarkdownString(options.isReadonly) && options.isReadonly.value !== '') { readOnlyMessage = { value: options.isReadonly.value, isTrusted: options.isReadonly.isTrusted, From 4bc3583c4e20ff3e24a17f387377503ff155eaea Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 24 Jan 2024 13:48:40 +0100 Subject: [PATCH 269/333] handle `rerun` message during make_request state (#203333) handle `rerun` message during make_request state, fixes https://github.com/microsoft/vscode-copilot/issues/3650 --- .../contrib/inlineChat/browser/inlineChatController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index bdc49d917ef2e..9fb003a71d60b 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -716,7 +716,7 @@ export class InlineChatController implements IEditorContribution { return State.PAUSE; } else if (message & Message.ACCEPT_SESSION) { return State.ACCEPT; - } else if (message & Message.ACCEPT_INPUT) { + } else if (message & (Message.ACCEPT_INPUT | Message.RERUN_INPUT)) { return State.MAKE_REQUEST; } else { return State.APPLY_RESPONSE; From 5375e374812f080ae23755a78f2b2dd27b61e94b Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 24 Jan 2024 13:49:08 +0100 Subject: [PATCH 270/333] Error: Cannot register two commands with the same id: workbench.action.focusCommentsPanel (#203331) Third time's the charm Fixes #202563 --- .../contrib/comments/browser/comments.contribution.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts index 6545fa6c4441a..b47cdb2b883ee 100644 --- a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts +++ b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts @@ -17,21 +17,11 @@ import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/co import { COMMENTS_VIEW_ID } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; import { CommentThreadState } from 'vs/editor/common/languages'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { CONTEXT_KEY_HAS_COMMENTS, CONTEXT_KEY_SOME_COMMENTS_EXPANDED, CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsView'; import { ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; import { Codicon } from 'vs/base/common/codicons'; -CommandsRegistry.registerCommand({ - id: 'workbench.action.focusCommentsPanel', - handler: async (accessor) => { - const viewsService = accessor.get(IViewsService); - viewsService.openView(COMMENTS_VIEW_ID, true); - } -}); - registerAction2(class Collapse extends ViewAction { constructor() { super({ From c9040d0567113043893ab3c10e75f393860fc34b Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 24 Jan 2024 13:49:21 +0100 Subject: [PATCH 271/333] Fix Drag n Drop behavior in Open Editors with multiple groups (#203330) fix #203179 --- src/vs/base/browser/ui/list/listView.ts | 11 ++++++++++- src/vs/base/browser/ui/list/listWidget.ts | 3 +-- .../files/browser/views/openEditorsView.ts | 19 +++++++++++++++---- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 4f8ab395ce644..5465520c08cbe 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -1221,7 +1221,7 @@ export class ListView implements IListView { feedback = distinct(feedback).filter(i => i >= -1 && i < this.length).sort((a, b) => a - b); feedback = feedback[0] === -1 ? [-1] : feedback; - const dragOverEffectPosition = typeof result !== 'boolean' && result.effect && result.effect.position ? result.effect.position : ListDragOverEffectPosition.Over; + let dragOverEffectPosition = typeof result !== 'boolean' && result.effect && result.effect.position ? result.effect.position : ListDragOverEffectPosition.Over; if (equalsDragFeedback(this.currentDragFeedback, feedback) && this.currentDragFeedbackPosition === dragOverEffectPosition) { return true; @@ -1244,6 +1244,15 @@ export class ListView implements IListView { throw new Error('Can\'t use multiple feedbacks with position different than \'over\''); } + // Make sure there is no flicker when moving between two items + // Always use the before feedback if possible + if (dragOverEffectPosition === ListDragOverEffectPosition.After) { + if (feedback[0] < this.length - 1) { + feedback[0] += 1; + dragOverEffectPosition = ListDragOverEffectPosition.Before; + } + } + for (const index of feedback) { const item = this.items[index]!; item.dropTarget = true; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 4d61198158cd1..1a72e4e64d0d0 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -979,14 +979,13 @@ export class DefaultStyleController implements IStyleController { if (styles.listDropBetweenBackground) { content.push(` .monaco-list${suffix} .monaco-list-rows.drop-target-before .monaco-list-row:first-child::before, - .monaco-list${suffix} .monaco-list-row.drop-target-after + .monaco-list-row::before, .monaco-list${suffix} .monaco-list-row.drop-target-before::before { content: ""; position: absolute; top: 0px; left: 0px; width: 100%; height: 1px; background-color: ${styles.listDropBetweenBackground}; }`); content.push(` .monaco-list${suffix} .monaco-list-rows.drop-target-after .monaco-list-row:last-child::after, - .monaco-list${suffix} .monaco-list-row:last-child.drop-target-after::after { + .monaco-list${suffix} .monaco-list-row.drop-target-after::after { content: ""; position: absolute; bottom: 0px; left: 0px; width: 100%; height: 1px; background-color: ${styles.listDropBetweenBackground}; }`); diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index d38538f3e9713..e4b7b0ca2c9a3 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -53,6 +53,7 @@ import { Schemas } from 'vs/base/common/network'; import { extUriIgnorePathCase } from 'vs/base/common/resources'; import { ILocalizedString } from 'vs/platform/action/common/action'; import { mainWindow } from 'vs/base/browser/window'; +import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; const $ = dom.$; @@ -727,7 +728,7 @@ class OpenEditorsDragAndDrop implements IListDragAndDrop Date: Wed, 24 Jan 2024 15:03:59 +0100 Subject: [PATCH 272/333] Clarify custom title bar style setting description (#203336) fix #203290 --- src/vs/workbench/electron-sandbox/desktop.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 1c38985809b75..f1e76e53c9933 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -245,7 +245,7 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand ], 'default': isLinux ? 'never' : 'auto', 'scope': ConfigurationScope.APPLICATION, - 'description': localize('window.customTitleBarVisibility', "Adjust when the custom title bar should be shown."), + 'markdownDescription': localize('window.customTitleBarVisibility', "Adjust when the custom title bar should be shown. The custom title bar can be hidden when in full screen mode with `windowed`. The custom title bar can only be hidden in none full screen mode with `never` when `#window.titleBarStyle#` is set to `native`."), }, 'window.dialogStyle': { 'type': 'string', From 97b87273430f80c2179d258d337539c4c1bc2937 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:09:02 +0100 Subject: [PATCH 273/333] Git - fix viewing stahes with added/deleted/renamed files (#203341) --- extensions/git/src/commands.ts | 19 +++++--- extensions/git/src/git.ts | 75 +++++++++++++++++++++++++++++--- extensions/git/src/repository.ts | 2 +- 3 files changed, 84 insertions(+), 12 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 6a15bd94455f5..acf699eeca34a 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -3708,19 +3708,26 @@ export class CommandCenter { return; } - const stashFiles = await repository.showStash(stash.index); + const stashChanges = await repository.showStash(stash.index); - if (!stashFiles || stashFiles.length === 0) { + if (!stashChanges || stashChanges.length === 0) { return; } const title = `Git Stash #${stash.index}: ${stash.description}`; const multiDiffSourceUri = toGitUri(Uri.file(repository.root), `stash@{${stash.index}}`, { scheme: 'git-stash' }); - const resources: { originalUri: Uri; modifiedUri: Uri }[] = []; - for (const file of stashFiles) { - const fileUri = Uri.file(path.join(repository.root, file)); - resources.push({ originalUri: fileUri, modifiedUri: toGitUri(fileUri, `stash@{${stash.index}}`) }); + const resources: { originalUri: Uri | undefined; modifiedUri: Uri | undefined }[] = []; + for (const change of stashChanges) { + if (change.status === Status.INDEX_ADDED) { + resources.push({ originalUri: undefined, modifiedUri: toGitUri(change.uri, `stash@{${stash.index}}`) }); + } else if (change.status === Status.DELETED) { + resources.push({ originalUri: change.uri, modifiedUri: undefined }); + } else if (change.status === Status.INDEX_RENAMED) { + resources.push({ originalUri: change.originalUri, modifiedUri: toGitUri(change.uri, `stash@{${stash.index}}`) }); + } else { + resources.push({ originalUri: change.uri, modifiedUri: toGitUri(change.uri, `stash@{${stash.index}}`) }); + } } commands.executeCommand('_workbench.openMultiDiffEditor', { multiDiffSourceUri, title, resources }); diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 2c8be161784b1..f86c5ad99f573 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -997,6 +997,70 @@ function parseGitStashes(raw: string): Stash[] { return result; } +// TODO@lszomoru - adopt in diffFiles() +function parseGitChanges(repositoryRoot: string, raw: string): Change[] { + let index = 0; + const result: Change[] = []; + const segments = raw.trim() + .split('\x00') + .filter(s => s); + + segmentsLoop: + while (index < segments.length - 1) { + const change = segments[index++]; + const resourcePath = segments[index++]; + + if (!change || !resourcePath) { + break; + } + + const originalUri = Uri.file(path.isAbsolute(resourcePath) ? resourcePath : path.join(repositoryRoot, resourcePath)); + + let uri = originalUri; + let renameUri = originalUri; + let status = Status.UNTRACKED; + + // Copy or Rename status comes with a number (ex: 'R100'). + // We don't need the number, we use only first character of the status. + switch (change[0]) { + case 'A': + status = Status.INDEX_ADDED; + break; + + case 'M': + status = Status.MODIFIED; + break; + + case 'D': + status = Status.DELETED; + break; + + // Rename contains two paths, the second one is what the file is renamed/copied to. + case 'R': { + if (index >= segments.length) { + break; + } + + const newPath = segments[index++]; + if (!newPath) { + break; + } + + status = Status.INDEX_RENAMED; + uri = renameUri = Uri.file(path.isAbsolute(newPath) ? newPath : path.join(repositoryRoot, newPath)); + break; + } + default: + // Unknown status + break segmentsLoop; + } + + result.push({ status, uri, originalUri, renameUri }); + } + + return result; +} + export interface PullOptions { unshallow?: boolean; tags?: boolean; @@ -2147,15 +2211,16 @@ export class Repository { } } - async showStash(index: number): Promise { - const args = ['stash', 'show', `stash@{${index}}`, '--name-only']; + async showStash(index: number): Promise { + const args = ['stash', 'show', `stash@{${index}}`, '--name-status', '-z']; try { const result = await this.exec(args); + if (result.exitCode) { + return []; + } - return result.stdout.trim() - .split('\n') - .filter(line => !!line); + return parseGitChanges(this.repositoryRoot, result.stdout.trim()); } catch (err) { if (/No stash found/.test(err.stderr || '')) { return undefined; diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 698ab8affd58f..70e4e98c43a55 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1995,7 +1995,7 @@ export class Repository implements Disposable { return await this.run(Operation.Stash, () => this.repository.applyStash(index)); } - async showStash(index: number): Promise { + async showStash(index: number): Promise { return await this.run(Operation.Stash, () => this.repository.showStash(index)); } From 5fea9b4f26fd9de09820a78c9da643d0ff6b8498 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 24 Jan 2024 16:54:56 +0100 Subject: [PATCH 274/333] Setting default enablement state to false for the experiment (#203324) setting default enablement state to false for the experiment --- src/vs/editor/common/config/editorOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 5d150a58558ed..720944ca3e1a9 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2810,7 +2810,7 @@ export type EditorStickyScrollOptions = Readonly { constructor() { - const defaults: EditorStickyScrollOptions = { enabled: true, maxLineCount: 5, defaultModel: 'outlineModel', scrollWithEditor: true }; + const defaults: EditorStickyScrollOptions = { enabled: false, maxLineCount: 5, defaultModel: 'outlineModel', scrollWithEditor: true }; super( EditorOption.stickyScroll, 'stickyScroll', defaults, { From cc6f22bf75abaf22cd12e817e4fd716448a8ee62 Mon Sep 17 00:00:00 2001 From: Robo Date: Thu, 25 Jan 2024 01:49:21 +0900 Subject: [PATCH 275/333] chore: update builds for electron@27.2.3 (#203346) * chore: update builds for electron@27.2.3 * chore: bump distro --- .yarnrc | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.yarnrc b/.yarnrc index 12a0b977bd9fe..c95a4be2afa76 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,5 +1,5 @@ disturl "https://electronjs.org/headers" target "27.2.3" -ms_build_id "26378995" +ms_build_id "26495564" runtime "electron" build_from_source "true" diff --git a/package.json b/package.json index e9cd097d3e846..835c8df8c7a2a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.86.0", - "distro": "2124c72ef2dcad044b30293f7548a7cc63f113fe", + "distro": "bfe80ecb51ea0f99e6c250ba625938dcc55461c1", "author": { "name": "Microsoft Corporation" }, From 0c109dbcdec16d415bebe5ef2ff7b1e8c9cf9d14 Mon Sep 17 00:00:00 2001 From: Benjamin Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 24 Jan 2024 18:15:22 +0100 Subject: [PATCH 276/333] Remove redundant canActivityBarBeHidden() method (#203351) canActivityBarBeHidden() no longer needed --- src/vs/workbench/browser/layout.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index e8322cbf07d0c..ccdbaf9d6bd95 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -702,13 +702,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } - // Activity bar cannot be hidden - // This check must be called after state is set - // because canActivityBarBeHidden calls isVisible - if (this.stateModel.getRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN) && !this.canActivityBarBeHidden()) { - this.stateModel.setRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN, false); - } - // Window border this.updateWindowsBorder(true); } @@ -1742,18 +1735,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } private setActivityBarHidden(hidden: boolean, skipLayout?: boolean): void { - if (hidden && !this.canActivityBarBeHidden()) { - return; - } this.stateModel.setRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN, hidden); // Propagate to grid this.workbenchGrid.setViewVisible(this.activityBarPartView, !hidden); } - private canActivityBarBeHidden(): boolean { - return this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.TOP; - } - private setBannerHidden(hidden: boolean): void { this.workbenchGrid.setViewVisible(this.bannerPartView, !hidden); } From b96127eea4f71b47daca57e3e4f8b9271833866b Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 24 Jan 2024 09:17:09 -0800 Subject: [PATCH 277/333] fix #203266 --- .../platform/accessibility/browser/accessibilityService.ts | 6 +++++- src/vs/platform/accessibility/common/accessibility.ts | 1 + .../accessibility/test/common/testAccessibilityService.ts | 1 + src/vs/platform/audioCues/browser/audioCueService.ts | 4 ++-- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/accessibility/browser/accessibilityService.ts b/src/vs/platform/accessibility/browser/accessibilityService.ts index 1db5b0a24e512..bd84abbc6dc9f 100644 --- a/src/vs/platform/accessibility/browser/accessibilityService.ts +++ b/src/vs/platform/accessibility/browser/accessibilityService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { addDisposableListener } from 'vs/base/browser/dom'; -import { alert } from 'vs/base/browser/ui/aria/aria'; +import { alert, status } from 'vs/base/browser/ui/aria/aria'; import { mainWindow } from 'vs/base/browser/window'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -110,4 +110,8 @@ export class AccessibilityService extends Disposable implements IAccessibilitySe alert(message: string): void { alert(message); } + + status(message: string): void { + status(message); + } } diff --git a/src/vs/platform/accessibility/common/accessibility.ts b/src/vs/platform/accessibility/common/accessibility.ts index b370b1e0267ed..d6a89b230f876 100644 --- a/src/vs/platform/accessibility/common/accessibility.ts +++ b/src/vs/platform/accessibility/common/accessibility.ts @@ -21,6 +21,7 @@ export interface IAccessibilityService { getAccessibilitySupport(): AccessibilitySupport; setAccessibilitySupport(accessibilitySupport: AccessibilitySupport): void; alert(message: string): void; + status(message: string): void; } export const enum AccessibilitySupport { diff --git a/src/vs/platform/accessibility/test/common/testAccessibilityService.ts b/src/vs/platform/accessibility/test/common/testAccessibilityService.ts index 0789812b90551..f8dbe1da8755c 100644 --- a/src/vs/platform/accessibility/test/common/testAccessibilityService.ts +++ b/src/vs/platform/accessibility/test/common/testAccessibilityService.ts @@ -19,4 +19,5 @@ export class TestAccessibilityService implements IAccessibilityService { setAccessibilitySupport(accessibilitySupport: AccessibilitySupport): void { } getAccessibilitySupport(): AccessibilitySupport { return AccessibilitySupport.Unknown; } alert(message: string): void { } + status(message: string): void { } } diff --git a/src/vs/platform/audioCues/browser/audioCueService.ts b/src/vs/platform/audioCues/browser/audioCueService.ts index 6de9aaf6c4ea6..295b3c65f4d9a 100644 --- a/src/vs/platform/audioCues/browser/audioCueService.ts +++ b/src/vs/platform/audioCues/browser/audioCueService.ts @@ -59,7 +59,7 @@ export class AudioCueService extends Disposable implements IAudioCueService { public async playAudioCue(cue: AudioCue, options: IAudioCueOptions = {}): Promise { const alertMessage = cue.alertMessage; if (this.isAlertEnabled(cue, options.userGesture) && alertMessage) { - this.accessibilityService.alert(alertMessage); + this.accessibilityService.status(alertMessage); } if (this.isCueEnabled(cue, options.userGesture)) { @@ -75,7 +75,7 @@ export class AudioCueService extends Disposable implements IAudioCueService { const cueArray = cues.map(c => 'cue' in c ? c.cue : c); const alerts = cueArray.filter(cue => this.isAlertEnabled(cue)).map(c => c.alertMessage); if (alerts.length) { - this.accessibilityService.alert(alerts.join(', ')); + this.accessibilityService.status(alerts.join(', ')); } // Some audio cues might reuse sounds. Don't play the same sound twice. From 4fb1e923f931ab52a8fb2b20a9f02a6342edf9bc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 24 Jan 2024 18:45:07 +0100 Subject: [PATCH 278/333] :lipstick: move new save-functions close to existing saveAll function (#203356) --- src/vscode-dts/vscode.d.ts | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index b35ff0b13f4d1..9393c2ff51ed9 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -13286,6 +13286,29 @@ declare module 'vscode' { */ export function findFiles(include: GlobPattern, exclude?: GlobPattern | null, maxResults?: number, token?: CancellationToken): Thenable; + /** + * Saves the editor identified by the given resource and returns the resulting resource or `undefined` + * if save was not successful or no editor with the given resource was found. + * + * **Note** that an editor with the provided resource must be opened in order to be saved. + * + * @param uri the associated uri for the opened editor to save. + * @returns A thenable that resolves when the save operation has finished. + */ + export function save(uri: Uri): Thenable; + + /** + * Saves the editor identified by the given resource to a new file name as provided by the user and + * returns the resulting resource or `undefined` if save was not successful or cancelled or no editor + * with the given resource was found. + * + * **Note** that an editor with the provided resource must be opened in order to be saved as. + * + * @param uri the associated uri for the opened editor to save as. + * @returns A thenable that resolves when the save-as operation has finished. + */ + export function saveAs(uri: Uri): Thenable; + /** * Save all dirty files. * @@ -13649,29 +13672,6 @@ declare module 'vscode' { * Event that fires when the current workspace has been trusted. */ export const onDidGrantWorkspaceTrust: Event; - - /** - * Saves the editor identified by the given resource and returns the resulting resource or `undefined` - * if save was not successful or no editor with the given resource was found. - * - * **Note** that an editor with the provided resource must be opened in order to be saved. - * - * @param uri the associated uri for the opened editor to save. - * @returns A thenable that resolves when the save operation has finished. - */ - export function save(uri: Uri): Thenable; - - /** - * Saves the editor identified by the given resource to a new file name as provided by the user and - * returns the resulting resource or `undefined` if save was not successful or cancelled or no editor - * with the given resource was found. - * - * **Note** that an editor with the provided resource must be opened in order to be saved as. - * - * @param uri the associated uri for the opened editor to save as. - * @returns A thenable that resolves when the save-as operation has finished. - */ - export function saveAs(uri: Uri): Thenable; } /** From 8e5d9da1e4a2f97bd00537f1cbf9798cf02416f1 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 24 Jan 2024 09:53:41 -0800 Subject: [PATCH 279/333] fix #203197 --- .../contrib/speech/common/speechService.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/speech/common/speechService.ts b/src/vs/workbench/contrib/speech/common/speechService.ts index 807dc5f7a952f..1e0bdc569c323 100644 --- a/src/vs/workbench/contrib/speech/common/speechService.ts +++ b/src/vs/workbench/contrib/speech/common/speechService.ts @@ -16,6 +16,7 @@ import { ILogService } from 'vs/platform/log/common/log'; export const ISpeechService = createDecorator('speechService'); export const HasSpeechProvider = new RawContextKey('hasSpeechProvider', false, { type: 'string', description: localize('hasSpeechProvider', "A speech provider is registered to the speech service.") }); +export const SpeechInProgress = new RawContextKey('speechInProgress', false, { type: 'string', description: localize('speechInProgress', "A speech session is in progress.") }); export interface ISpeechProviderMetadata { readonly extension: ExtensionIdentifier; @@ -109,12 +110,21 @@ export class SpeechService extends Disposable implements ISpeechService { private readonly providers = new Map(); private readonly hasSpeechProviderContext = HasSpeechProvider.bindTo(this.contextKeyService); + private readonly speechInProgress = SpeechInProgress.bindTo(this.contextKeyService); + + private readonly _onDidStartSpeechToTextSession = this._register(new Emitter()); + readonly onDidStartSpeechToTextSession = this._onDidStartSpeechToTextSession.event; + + private readonly _onDidEndSpeechToTextSession = this._register(new Emitter()); + readonly onDidEndSpeechToTextSession = this._onDidEndSpeechToTextSession.event; constructor( @ILogService private readonly logService: ILogService, @IContextKeyService private readonly contextKeyService: IContextKeyService ) { super(); + this._register(this.onDidStartSpeechToTextSession(() => this.speechInProgress.set(true))); + this._register(this.onDidEndSpeechToTextSession(() => this.speechInProgress.reset())); } registerSpeechProvider(identifier: string, provider: ISpeechProvider): IDisposable { @@ -137,11 +147,6 @@ export class SpeechService extends Disposable implements ISpeechService { }); } - private readonly _onDidStartSpeechToTextSession = this._register(new Emitter()); - readonly onDidStartSpeechToTextSession = this._onDidStartSpeechToTextSession.event; - - private readonly _onDidEndSpeechToTextSession = this._register(new Emitter()); - readonly onDidEndSpeechToTextSession = this._onDidEndSpeechToTextSession.event; private _activeSpeechToTextSession: ISpeechToTextSession | undefined = undefined; get hasActiveSpeechToTextSession() { return !!this._activeSpeechToTextSession; } From 905ed4ffd5f5ac218de334cfd330e567968a241e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 24 Jan 2024 19:08:22 +0100 Subject: [PATCH 280/333] fix #203131 (#203360) --- src/vs/workbench/browser/parts/paneCompositeBar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/paneCompositeBar.ts b/src/vs/workbench/browser/parts/paneCompositeBar.ts index dacce52de4840..4387df1a1380c 100644 --- a/src/vs/workbench/browser/parts/paneCompositeBar.ts +++ b/src/vs/workbench/browser/parts/paneCompositeBar.ts @@ -490,7 +490,7 @@ export class PaneCompositeBar extends Disposable { name: cachedViewContainer.name, order: cachedViewContainer.order, pinned: cachedViewContainer.pinned, - visible: cachedViewContainer.visible, + visible: cachedViewContainer.visible && !!this.getViewContainer(cachedViewContainer.id), }); } From 2d2f5bdb900688709a510d9d52e8e7bd5f4ffbaa Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 24 Jan 2024 08:33:04 -1000 Subject: [PATCH 281/333] debug: address triggered breakpoint feedback (#203363) Fixes #203154 Fixes #203155 Fixes #203156 Fixes #203158 --- .../browser/breakpointEditorContribution.ts | 2 +- .../contrib/debug/browser/breakpointWidget.ts | 17 +++++++++++++---- .../debug/browser/media/debug.contribution.css | 3 +++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 67df7ad254b29..1c36e83c55cdc 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -457,7 +457,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi )); actions.push(new Action( 'addTriggeredBreakpoint', - nls.localize('addTriggeredBreakpoint', "Add Triggered Breakpoint.."), + nls.localize('addTriggeredBreakpoint', "Add Triggered Breakpoint..."), undefined, true, () => Promise.resolve(this.showBreakpointWidget(lineNumber, column, BreakpointWidgetContext.TRIGGER_POINT)) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts index 14ea1c0917680..877c0921a4c71 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts @@ -84,6 +84,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi private inputContainer!: HTMLElement; private selectBreakpointContainer!: HTMLElement; private input!: IActiveCodeEditor; + private selectBreakpointBox!: SelectBox; private toDispose: lifecycle.IDisposable[]; private conditionInput = ''; private hitCountInput = ''; @@ -205,7 +206,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi { text: nls.localize('expression', "Expression") }, { text: nls.localize('hitCount', "Hit Count") }, { text: nls.localize('logMessage', "Log Message") }, - { text: nls.localize('triggeredBy', "Wait For Breakpoint") }, + { text: nls.localize('triggeredBy', "Wait for Breakpoint") }, ], this.context, this.contextViewService, defaultSelectBoxStyles, { ariaLabel: nls.localize('breakpointType', 'Breakpoint Type') }); this.selectContainer = $('.breakpoint-select-container'); selectBox.render(dom.append(container, this.selectContainer)); @@ -228,7 +229,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi this.updateContextInput(); // Due to an electron bug we have to do the timeout, otherwise we do not get focus - setTimeout(() => this.input.focus(), 150); + setTimeout(() => this.focusInput(), 150); } private createTriggerBreakpointInput(container: HTMLElement) { @@ -256,7 +257,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi ], select); }); - const selectBreakpointBox = new SelectBox([{ text: nls.localize('triggerByLoading', 'Loading...'), isDisabled: true }], 0, this.contextViewService, defaultSelectBoxStyles, { ariaLabel: nls.localize('selectBreakpoint', 'Select breakpoint') }); + const selectBreakpointBox = this.selectBreakpointBox = new SelectBox([{ text: nls.localize('triggerByLoading', 'Loading...'), isDisabled: true }], 0, this.contextViewService, defaultSelectBoxStyles, { ariaLabel: nls.localize('selectBreakpoint', 'Select breakpoint') }); selectBreakpointBox.onDidSelect(e => { if (e.index === 0) { this.triggeredByBreakpointInput = undefined; @@ -295,7 +296,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi this.setInputMode(); const value = this.getInputValue(this.breakpoint); this.input.getModel().setValue(value); - this.input.focus(); + this.focusInput(); } } @@ -455,6 +456,14 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi this.dispose(); } + private focusInput() { + if (this.context === Context.TRIGGER_POINT) { + this.selectBreakpointBox.focus(); + } else { + this.input.focus(); + } + } + override dispose(): void { super.dispose(); this.input.dispose(); diff --git a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css index 36390d28f6ab0..269e0c453d936 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css @@ -31,6 +31,9 @@ margin-top: -1px; } + +.codicon-debug-breakpoint-conditional.codicon-debug-stackframe-focused::after, +.codicon-debug-breakpoint-conditional.codicon-debug-stackframe::after, .codicon-debug-breakpoint.codicon-debug-stackframe-focused::after, .codicon-debug-breakpoint.codicon-debug-stackframe::after { content: '\eb8a'; From f5d895ae04187711c0df92a74cb174588b91713c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 24 Jan 2024 19:44:10 +0100 Subject: [PATCH 282/333] VS Code Speech does not warn when no chat providers are found. (fix #203241) (#203365) --- .../chat/electron-sandbox/actions/voiceChatActions.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index aaa7195ba5a30..08a689112f683 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -796,7 +796,8 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe @IInstantiationService instantiationService: IInstantiationService, @ICodeEditorService private readonly codeEditorService: ICodeEditorService, @IEditorService private readonly editorService: IEditorService, - @IHostService private readonly hostService: IHostService + @IHostService private readonly hostService: IHostService, + @IChatService private readonly chatService: IChatService ) { super(); @@ -811,6 +812,8 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe this.handleKeywordActivation(); })); + this._register(this.chatService.onDidRegisterProvider(() => this.updateConfiguration())); + this._register(this.speechService.onDidStartSpeechToTextSession(() => this.handleKeywordActivation())); this._register(this.speechService.onDidEndSpeechToTextSession(() => this.handleKeywordActivation())); @@ -826,8 +829,8 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe } private updateConfiguration(): void { - if (!this.speechService.hasSpeechProvider) { - return; // these settings require a speech provider + if (!this.speechService.hasSpeechProvider || this.chatService.getProviderInfos().length === 0) { + return; // these settings require a speech and chat provider } const registry = Registry.as(Extensions.Configuration); From 8494a40f51580026ba241617eb6847ff585b8f70 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 24 Jan 2024 17:55:50 +0100 Subject: [PATCH 283/333] Fixes #203186 --- .../multiDiffEditor/browser/multiDiffEditorInput.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts index 60b430fd74454..65f6defe7ed6f 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts @@ -21,10 +21,11 @@ import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; import { localize } from 'vs/nls'; +import { ConfirmResult } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor'; import { DEFAULT_EDITOR_ASSOCIATION, EditorInputCapabilities, EditorInputWithOptions, IEditorSerializer, IResourceMultiDiffEditorInput, ISaveOptions, IUntypedEditorInput } from 'vs/workbench/common/editor'; -import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { EditorInput, IEditorCloseHandler } from 'vs/workbench/common/editor/editorInput'; import { MultiDiffEditorIcon } from 'vs/workbench/contrib/multiDiffEditor/browser/icons.contribution'; import { ConstResolvedMultiDiffSource, IMultiDiffSourceResolverService, IResolvedMultiDiffSource, MultiDiffEditorItem } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffSourceResolverService'; import { ObservableLazyStatefulPromise } from 'vs/workbench/contrib/multiDiffEditor/browser/utils'; @@ -255,6 +256,15 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor } return undefined; } + + override readonly closeHandler: IEditorCloseHandler = { + async confirm() { + return ConfirmResult.DONT_SAVE; + }, + showConfirm() { + return false; + } + }; } function isUriDirty(textFileService: ITextFileService, uri: URI) { From c4ea2a881dea86cf90c17fa785db1d7288c52bdb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 24 Jan 2024 20:19:27 +0100 Subject: [PATCH 284/333] :lipstick: --- .../contrib/speech/common/speechService.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/speech/common/speechService.ts b/src/vs/workbench/contrib/speech/common/speechService.ts index 1e0bdc569c323..48f21fb8ac134 100644 --- a/src/vs/workbench/contrib/speech/common/speechService.ts +++ b/src/vs/workbench/contrib/speech/common/speechService.ts @@ -16,7 +16,7 @@ import { ILogService } from 'vs/platform/log/common/log'; export const ISpeechService = createDecorator('speechService'); export const HasSpeechProvider = new RawContextKey('hasSpeechProvider', false, { type: 'string', description: localize('hasSpeechProvider', "A speech provider is registered to the speech service.") }); -export const SpeechInProgress = new RawContextKey('speechInProgress', false, { type: 'string', description: localize('speechInProgress', "A speech session is in progress.") }); +export const SpeechToTextInProgress = new RawContextKey('speechToTextInProgress', false, { type: 'string', description: localize('speechToTextInProgress', "A speech-to-text session is in progress.") }); export interface ISpeechProviderMetadata { readonly extension: ExtensionIdentifier; @@ -110,21 +110,12 @@ export class SpeechService extends Disposable implements ISpeechService { private readonly providers = new Map(); private readonly hasSpeechProviderContext = HasSpeechProvider.bindTo(this.contextKeyService); - private readonly speechInProgress = SpeechInProgress.bindTo(this.contextKeyService); - - private readonly _onDidStartSpeechToTextSession = this._register(new Emitter()); - readonly onDidStartSpeechToTextSession = this._onDidStartSpeechToTextSession.event; - - private readonly _onDidEndSpeechToTextSession = this._register(new Emitter()); - readonly onDidEndSpeechToTextSession = this._onDidEndSpeechToTextSession.event; constructor( @ILogService private readonly logService: ILogService, @IContextKeyService private readonly contextKeyService: IContextKeyService ) { super(); - this._register(this.onDidStartSpeechToTextSession(() => this.speechInProgress.set(true))); - this._register(this.onDidEndSpeechToTextSession(() => this.speechInProgress.reset())); } registerSpeechProvider(identifier: string, provider: ISpeechProvider): IDisposable { @@ -147,10 +138,17 @@ export class SpeechService extends Disposable implements ISpeechService { }); } + private readonly _onDidStartSpeechToTextSession = this._register(new Emitter()); + readonly onDidStartSpeechToTextSession = this._onDidStartSpeechToTextSession.event; + + private readonly _onDidEndSpeechToTextSession = this._register(new Emitter()); + readonly onDidEndSpeechToTextSession = this._onDidEndSpeechToTextSession.event; private _activeSpeechToTextSession: ISpeechToTextSession | undefined = undefined; get hasActiveSpeechToTextSession() { return !!this._activeSpeechToTextSession; } + private readonly speechToTextInProgress = SpeechToTextInProgress.bindTo(this.contextKeyService); + createSpeechToTextSession(token: CancellationToken): ISpeechToTextSession { const provider = firstOrDefault(Array.from(this.providers.values())); if (!provider) { @@ -166,6 +164,7 @@ export class SpeechService extends Disposable implements ISpeechService { const onSessionStoppedOrCanceled = () => { if (session === this._activeSpeechToTextSession) { this._activeSpeechToTextSession = undefined; + this.speechToTextInProgress.reset(); this._onDidEndSpeechToTextSession.fire(); } @@ -181,6 +180,7 @@ export class SpeechService extends Disposable implements ISpeechService { switch (e.status) { case SpeechToTextStatus.Started: if (session === this._activeSpeechToTextSession) { + this.speechToTextInProgress.set(true); this._onDidStartSpeechToTextSession.fire(); } break; From 5b912041848a8e5a1a839910aa6a85d1d671e141 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 24 Jan 2024 21:15:51 +0100 Subject: [PATCH 285/333] SCM - do not show "View Commit" or "View All Changes" action for empty commits (#203381) --- extensions/git/package.json | 8 ++++---- src/vs/workbench/contrib/scm/browser/menus.ts | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index e909d5f503049..e9d12a97405e8 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1851,14 +1851,14 @@ "scm/incomingChanges/allChanges/context": [ { "command": "git.viewAllChanges", - "when": "scmProvider == git && config.multiDiffEditor.experimental.enabled", + "when": "scmProvider == git && scmHistoryItemFileCount != 0 && config.multiDiffEditor.experimental.enabled", "group": "inline@1" } ], "scm/incomingChanges/historyItem/context": [ { "command": "git.viewCommit", - "when": "scmProvider == git && config.multiDiffEditor.experimental.enabled", + "when": "scmProvider == git && scmHistoryItemFileCount != 0 && config.multiDiffEditor.experimental.enabled", "group": "inline@1" } ], @@ -1872,14 +1872,14 @@ "scm/outgoingChanges/allChanges/context": [ { "command": "git.viewAllChanges", - "when": "scmProvider == git && config.multiDiffEditor.experimental.enabled", + "when": "scmProvider == git && scmHistoryItemFileCount != 0 && config.multiDiffEditor.experimental.enabled", "group": "inline@1" } ], "scm/outgoingChanges/historyItem/context": [ { "command": "git.viewCommit", - "when": "scmProvider == git && config.multiDiffEditor.experimental.enabled", + "when": "scmProvider == git && scmHistoryItemFileCount != 0 && config.multiDiffEditor.experimental.enabled", "group": "inline@1" } ], diff --git a/src/vs/workbench/contrib/scm/browser/menus.ts b/src/vs/workbench/contrib/scm/browser/menus.ts index 968788eb6f53d..22997f8c9c9cf 100644 --- a/src/vs/workbench/contrib/scm/browser/menus.ts +++ b/src/vs/workbench/contrib/scm/browser/menus.ts @@ -293,7 +293,11 @@ export class SCMHistoryProviderMenus implements ISCMHistoryProviderMenus, IDispo MenuId.SCMOutgoingChangesHistoryItemContext; } - result = this.menuService.createMenu(menuId, this.contextKeyService); + const contextKeyService = this.contextKeyService.createOverlay([ + ['scmHistoryItemFileCount', historyItem.statistics?.files ?? 0], + ]); + + result = this.menuService.createMenu(menuId, contextKeyService); this.historyItemMenus.set(historyItem, result); } From 17c3d007a792b2b6e32a1f791b27744466c73afb Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:24:54 -0800 Subject: [PATCH 286/333] add check for empty diagnostic/range in code action highlighting (#203382) * add check for empty diagnostics/ranges * cleanup --- .../browser/codeActionController.ts | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts index ad34973eddf7a..b91f85e6afdeb 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts @@ -279,19 +279,21 @@ export class CodeActionController extends Disposable implements IEditorContribut return { canPreview: !!action.action.edit?.edits.length }; }, onFocus: (action: CodeActionItem | undefined) => { - // If provider contributes ranges, then highlight contributed range over diagnostic range. - if (action && action.action.ranges) { + if (action && action.action) { + const ranges = action.action.ranges; + const diagnostics = action.action.diagnostics; currentDecorations.clear(); - const decorations: IModelDeltaDecoration[] = action.action.ranges.map(range => ({ range, options: CodeActionController.DECORATION })); - currentDecorations.set(decorations); - } else if (action && action.action.diagnostics) { - currentDecorations.clear(); - const decorations: IModelDeltaDecoration[] = action.action.diagnostics.map(diagnostic => ({ range: diagnostic, options: CodeActionController.DECORATION })); - currentDecorations.set(decorations); - const diagnostic = action.action.diagnostics[0]; - if (diagnostic.startLineNumber && diagnostic.startColumn) { - const selectionText = this._editor.getModel()?.getWordAtPosition({ lineNumber: diagnostic.startLineNumber, column: diagnostic.startColumn })?.word; - aria.status(localize('editingNewSelection', "Context: {0} at line {1} and column {2}.", selectionText, diagnostic.startLineNumber, diagnostic.startColumn)); + if (ranges && ranges.length > 0) { + const decorations: IModelDeltaDecoration[] = ranges.map(range => ({ range, options: CodeActionController.DECORATION })); + currentDecorations.set(decorations); + } else if (diagnostics && diagnostics.length > 0) { + const decorations: IModelDeltaDecoration[] = diagnostics.map(diagnostic => ({ range: diagnostic, options: CodeActionController.DECORATION })); + currentDecorations.set(decorations); + const diagnostic = diagnostics[0]; + if (diagnostic.startLineNumber && diagnostic.startColumn) { + const selectionText = this._editor.getModel()?.getWordAtPosition({ lineNumber: diagnostic.startLineNumber, column: diagnostic.startColumn })?.word; + aria.status(localize('editingNewSelection', "Context: {0} at line {1} and column {2}.", selectionText, diagnostic.startLineNumber, diagnostic.startColumn)); + } } } else { currentDecorations.clear(); From 1606308e1e0d6f3d2c2ad858fcc176db97ede376 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 24 Jan 2024 12:37:13 -0800 Subject: [PATCH 287/333] Add Bhavya to endgame notebook (#203383) She's cool --- .vscode/notebooks/my-endgame.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index ddcb541f8ff04..1031aa2c29ff7 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -157,7 +157,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:karrtikr -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:karrtikr -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett -author:bhavyaus" }, { "kind": 1, From bd30771483b0d4d05a9f2184d144a1a38bbd62f4 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 24 Jan 2024 15:11:26 -0800 Subject: [PATCH 288/333] Add Justin to Endgame notebook (#203392) He is also cool. --- .vscode/notebooks/my-endgame.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index 1031aa2c29ff7..b0bd9d5b3fb83 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -157,7 +157,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:karrtikr -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett -author:bhavyaus" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:karrtikr -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett -author:bhavyaus -author:justschen" }, { "kind": 1, From dc10e26a05fe2f0329589739eae981dbbcdf6a9a Mon Sep 17 00:00:00 2001 From: Michael Rienstra Date: Wed, 24 Jan 2024 16:39:51 -0800 Subject: [PATCH 289/333] docs: document new `configuration.markdown.copyFiles.destination` options (#203391) docs: document new `configuration.markdown.copyFiles.destination` And clean-up / standardize existing docs of same --- .../package.nls.json | 2 +- .../languageFeatures/copyFiles/copyFiles.ts | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index b99593a6301fd..af77144f15c82 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -64,7 +64,7 @@ "configuration.markdown.updateLinksOnFileMove.include.property": "The glob pattern to match file paths against. Set to true to enable the pattern.", "configuration.markdown.updateLinksOnFileMove.enableForDirectories": "Enable updating links when a directory is moved or renamed in the workspace.", "configuration.markdown.occurrencesHighlight.enabled": "Enable highlighting link occurrences in the current document.", - "configuration.markdown.copyFiles.destination": "Defines where files copied created by drop or paste should be created. This is a map from globs that match on the Markdown document to destinations.\n\nThe destinations may use the following variables:\n\n- `${documentFileName}` — The full filename of the Markdown document, for example: `readme.md`.\n- `${documentBaseName}` — The basename of Markdown document, for example: `readme`.\n- `${documentExtName}` — The extension of the Markdown document, for example: `md`.\n- `${documentDirName}` — The name of the Markdown document's parent directory.\n- `${documentWorkspaceFolder}` — The workspace folder for the Markdown document, for example: `/Users/me/myProject`. This is the same as `${documentDirName}` if the file is not part of a workspace.\n- `${fileName}` — The file name of the dropped file, for example: `image.png`.", + "configuration.markdown.copyFiles.destination": "Defines where files copied created by drop or paste should be created. This is a map from globs that match on the Markdown document to destinations.\n\nThe destinations may use the following variables:\n\n- `${documentDirName}` — Absolute parent directory path of the Markdown document, e.g. `/Users/me/myProject/docs`.\n- `${documentRelativeDirName}` — Relative parent directory path of the Markdown document, e.g. `docs`. This is the same as `${documentDirName}` if the file is not part of a workspace.\n- `${documentFileName}` — The full filename of the Markdown document, e.g. `README.md`.\n- `${documentBaseName}` — The basename of the Markdown document, e.g. `README`.\n- `${documentExtName}` — The extension of the Markdown document, e.g. `md`.\n- `${documentFilePath}` — Absolute path of the Markdown document, e.g. `/Users/me/myProject/docs/README.md`.\n- `${documentRelativeFilePath}` — Relative path of the Markdown document, e.g. `docs/README.md`. This is the same as `${documentFilePath}` if the file is not part of a workspace.\n- `${documentWorkspaceFolder}` — The workspace folder for the Markdown document, e.g. `/Users/me/myProject`. This is the same as `${documentDirName}` if the file is not part of a workspace.\n- `${fileName}` — The file name of the dropped file, e.g. `image.png`.\n- `${fileExtName}` — The extension of the dropped file, e.g. `png`.", "configuration.markdown.copyFiles.overwriteBehavior": "Controls if files created by drop or paste should overwrite existing files.", "configuration.markdown.copyFiles.overwriteBehavior.nameIncrementally": "If a file with the same name already exists, append a number to the file name, for example: `image.png` becomes `image-1.png`.", "configuration.markdown.copyFiles.overwriteBehavior.overwrite": "If a file with the same name already exists, overwrite it.", diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts index 299a8347b1cf6..dd41b5fa92484 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts @@ -82,20 +82,20 @@ function resolveCopyDestinationSetting(documentUri: vscode.Uri, fileName: string const vars = new Map([ // Document - ['documentDirName', documentDirName.path], // Absolute parent directory path - ['documentRelativeDirName', workspaceFolder ? path.posix.relative(workspaceFolder.path, documentDirName.path) : documentDirName.path], // Absolute parent directory path - ['documentFileName', documentBaseName], // Full filename: file.md - ['documentBaseName', documentBaseName.slice(0, documentBaseName.length - documentExtName.length)], // Just the name: file - ['documentExtName', documentExtName.replace('.', '')], // The document ext (without dot): md - ['documentFilePath', documentUri.path], // Full document path - ['documentRelativeFilePath', workspaceFolder ? path.posix.relative(workspaceFolder.path, documentUri.path) : documentUri.path], // Full document path relative to workspace + ['documentDirName', documentDirName.path], // Absolute parent directory path of the Markdown document, e.g. `/Users/me/myProject/docs`. + ['documentRelativeDirName', workspaceFolder ? path.posix.relative(workspaceFolder.path, documentDirName.path) : documentDirName.path], // Relative parent directory path of the Markdown document, e.g. `docs`. This is the same as `${documentDirName}` if the file is not part of a workspace. + ['documentFileName', documentBaseName], // The full filename of the Markdown document, e.g. `README.md`. + ['documentBaseName', documentBaseName.slice(0, documentBaseName.length - documentExtName.length)], // The basename of the Markdown document, e.g. `README`. + ['documentExtName', documentExtName.replace('.', '')], // The extension of the Markdown document, e.g. `md`. + ['documentFilePath', documentUri.path], // Absolute path of the Markdown document, e.g. `/Users/me/myProject/docs/README.md`. + ['documentRelativeFilePath', workspaceFolder ? path.posix.relative(workspaceFolder.path, documentUri.path) : documentUri.path], // Relative path of the Markdown document, e.g. `docs/README.md`. This is the same as `${documentFilePath}` if the file is not part of a workspace. // Workspace - ['documentWorkspaceFolder', ((workspaceFolder ?? documentDirName).path)], + ['documentWorkspaceFolder', ((workspaceFolder ?? documentDirName).path)], // The workspace folder for the Markdown document, e.g. `/Users/me/myProject`. This is the same as `${documentDirName}` if the file is not part of a workspace. // File - ['fileName', fileName], // Full file name - ['fileExtName', path.extname(fileName).replace('.', '')], // File extension (without dot): png + ['fileName', fileName], // The file name of the dropped file, e.g. `image.png`. + ['fileExtName', path.extname(fileName).replace('.', '')], // The extension of the dropped file, e.g. `png`. ]); return outDest.replaceAll(/(?\\\$)|(?\w+)(?:\/(?(?:\\\/|[^\}\/])+)\/(?(?:\\\/|[^\}\/])*)\/)?\}/g, (match, _escape, name, pattern, replacement, _offset, _str, groups) => { From b02edaa75a39dfbc67fe6194065a3613f4ea69f8 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 24 Jan 2024 17:49:34 -0800 Subject: [PATCH 290/333] Add Ben to endgame notebook (#203403) He is also cool! --- .vscode/notebooks/my-endgame.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index b0bd9d5b3fb83..6adad9d8532a4 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -157,7 +157,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:karrtikr -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett -author:bhavyaus -author:justschen" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:karrtikr -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett -author:bhavyaus -author:justschen -author:benibenj" }, { "kind": 1, From 3dea5cbbcb88d7f6dad71148f3d3f98b853ab836 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 24 Jan 2024 18:54:32 -0800 Subject: [PATCH 291/333] Pick up latest markdown language service (#203405) Picking up the latest stable release. This is mainly just a tag release compared to alpha-8 --- extensions/markdown-language-features/server/package.json | 4 ++-- extensions/markdown-language-features/server/yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/markdown-language-features/server/package.json b/extensions/markdown-language-features/server/package.json index 8d641861217dd..5c389810979bd 100644 --- a/extensions/markdown-language-features/server/package.json +++ b/extensions/markdown-language-features/server/package.json @@ -1,7 +1,7 @@ { "name": "vscode-markdown-languageserver", "description": "Markdown language server", - "version": "0.4.0-alpha.8", + "version": "0.4.0", "author": "Microsoft Corporation", "license": "MIT", "engines": { @@ -18,7 +18,7 @@ "vscode-languageserver": "^8.1.0", "vscode-languageserver-textdocument": "^1.0.8", "vscode-languageserver-types": "^3.17.3", - "vscode-markdown-languageservice": "^0.4.0-alpha.8", + "vscode-markdown-languageservice": "^0.4.0", "vscode-uri": "^3.0.7" }, "devDependencies": { diff --git a/extensions/markdown-language-features/server/yarn.lock b/extensions/markdown-language-features/server/yarn.lock index fcb85f9a94b37..f3a8efb60237e 100644 --- a/extensions/markdown-language-features/server/yarn.lock +++ b/extensions/markdown-language-features/server/yarn.lock @@ -128,10 +128,10 @@ vscode-languageserver@^8.1.0: dependencies: vscode-languageserver-protocol "3.17.3" -vscode-markdown-languageservice@^0.4.0-alpha.8: - version "0.4.0-alpha.8" - resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.4.0-alpha.8.tgz#5c3aaf3b3cd3f6b309f8dfcf93cbfb1397041adc" - integrity sha512-6S6RE5s+4biWg2xk9bpwNi6GihUYQIVxdO3I+jb/XDyvfmqYVxrN86cKLF8QSbaQvX3fMuBAxBLFfX93FdJi3w== +vscode-markdown-languageservice@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.4.0.tgz#1ccca383703d38043b58e096fd3224796a2b2ab5" + integrity sha512-3C8pZlC0ofHEYmWwHgenxL6//XrpkrgyytrqNpMlft46q9uBxSUfcXtEGt7wIDNLWsvmgqPqHBwEnBFtLwrWFA== dependencies: "@vscode/l10n" "^0.0.10" node-html-parser "^6.1.5" From a87e69f04892d8757ee5086436f40970a2a2b18c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 25 Jan 2024 09:20:18 +0100 Subject: [PATCH 292/333] reset `holdMode` when losing window focus (#203368) fyi @ulugbekna, re https://github.com/microsoft/vscode/issues/203187 --- .../keybinding/browser/keybindingService.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index 1345094497c66..708268ba3c3d2 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -287,10 +287,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { // for single modifier chord keybindings (e.g. shift shift) disposables.add(dom.addDisposableListener(window, dom.EventType.KEY_UP, (e: KeyboardEvent) => { - if (this._keybindingHoldMode) { - this._keybindingHoldMode.complete(); - this._keybindingHoldMode = null; - } + this._resetKeybindingHoldMode(); this.isComposingGlobalContextKey.set(e.isComposing); const keyEvent = new StandardKeyboardEvent(e); const shouldPreventDefault = this._singleModifierDispatch(keyEvent, keyEvent.target); @@ -405,10 +402,23 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { return undefined; } this._keybindingHoldMode = new DeferredPromise(); + const focusTracker = dom.trackFocus(dom.getWindow(undefined)); + const listener = focusTracker.onDidBlur(() => this._resetKeybindingHoldMode()); + this._keybindingHoldMode.p.finally(() => { + listener.dispose(); + focusTracker.dispose(); + }); this._log(`+ Enabled hold-mode for ${commandId}.`); return this._keybindingHoldMode.p; } + private _resetKeybindingHoldMode(): void { + if (this._keybindingHoldMode) { + this._keybindingHoldMode?.complete(); + this._keybindingHoldMode = null; + } + } + public override customKeybindingsCount(): number { return this.userKeybindings.keybindings.length; } From 4da46535190c445603200ad62d10965185814bb3 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 25 Jan 2024 10:18:37 +0100 Subject: [PATCH 293/333] Use descriptive reaction hover on whole comment reaction, not just count (#203421) Fixes https://github.com/microsoft/vscode-pull-request-github/issues/978 --- src/vs/workbench/contrib/comments/browser/reactionsAction.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/comments/browser/reactionsAction.ts b/src/vs/workbench/contrib/comments/browser/reactionsAction.ts index 14c771d31345f..fc77ef38ccf81 100644 --- a/src/vs/workbench/contrib/comments/browser/reactionsAction.ts +++ b/src/vs/workbench/contrib/comments/browser/reactionsAction.ts @@ -48,7 +48,6 @@ export class ReactionActionViewItem extends ActionViewItem { const reactionIcon = dom.append(this.label, dom.$('.reaction-icon')); const uri = URI.revive(action.icon); reactionIcon.style.backgroundImage = dom.asCSSUrl(uri); - reactionIcon.title = action.label; } if (action.count) { const reactionCount = dom.append(this.label, dom.$('span.reaction-count')); From b10846f1e5064190d571bfb37596bf5bfd7fb356 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 25 Jan 2024 10:23:30 +0100 Subject: [PATCH 294/333] only convert a selection when it is a selection (#203423) re https://github.com/microsoft/vscode-copilot/issues/3758 --- src/vs/workbench/api/common/extHostInlineChat.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostInlineChat.ts b/src/vs/workbench/api/common/extHostInlineChat.ts index 72fbb41c99fe0..c6e4266df75be 100644 --- a/src/vs/workbench/api/common/extHostInlineChat.ts +++ b/src/vs/workbench/api/common/extHostInlineChat.ts @@ -84,7 +84,7 @@ export class ExtHostInteractiveEditor implements ExtHostInlineChatShape { return { initialRange: v.initialRange ? typeConvert.Range.from(v.initialRange) : undefined, - initialSelection: v.initialSelection ? typeConvert.Selection.from(v.initialSelection) : undefined, + initialSelection: extHostTypes.Selection.isSelection(v.initialSelection) ? typeConvert.Selection.from(v.initialSelection) : undefined, message: v.message, autoSend: v.autoSend, position: v.position ? typeConvert.Position.from(v.position) : undefined, From 589d37d2e8b04ad1d08913363782baae9c29668c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 25 Jan 2024 10:33:42 +0100 Subject: [PATCH 295/333] restore inline suggestion (#203418) * restore inline suggestion fixes https://github.com/microsoft/vscode/issues/175190 * fix leak in test --- .../contrib/suggest/browser/suggestInlineCompletions.ts | 9 ++++++--- .../test/browser/suggestInlineCompletions.test.ts | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/suggest/browser/suggestInlineCompletions.ts b/src/vs/editor/contrib/suggest/browser/suggestInlineCompletions.ts index 2aa208f35769d..81d6f7d4d8fb2 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestInlineCompletions.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestInlineCompletions.ts @@ -6,7 +6,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { FuzzyScore } from 'vs/base/common/filters'; import { Iterable } from 'vs/base/common/iterator'; -import { RefCountedDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, RefCountedDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -104,7 +104,7 @@ class InlineCompletionResults extends RefCountedDisposable implements InlineComp } -export class SuggestInlineCompletions implements InlineCompletionsProvider { +export class SuggestInlineCompletions extends Disposable implements InlineCompletionsProvider { private _lastResult?: InlineCompletionResults; @@ -113,7 +113,10 @@ export class SuggestInlineCompletions implements InlineCompletionsProvider { diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestInlineCompletions.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestInlineCompletions.test.ts index 2a52c3166bcf0..1aaca84d48411 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestInlineCompletions.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestInlineCompletions.test.ts @@ -76,7 +76,7 @@ suite('Suggest Inline Completions', function () { test('Aggressive inline completions when typing within line #146948', async function () { - const completions: SuggestInlineCompletions = insta.createInstance(SuggestInlineCompletions); + const completions: SuggestInlineCompletions = disposables.add(insta.createInstance(SuggestInlineCompletions)); { // (1,3), end of word -> suggestions @@ -92,7 +92,7 @@ suite('Suggest Inline Completions', function () { }); test('Snippets show in inline suggestions even though they are turned off #175190', async function () { - const completions: SuggestInlineCompletions = insta.createInstance(SuggestInlineCompletions); + const completions: SuggestInlineCompletions = disposables.add(insta.createInstance(SuggestInlineCompletions)); { // unfiltered From f055b096fdcb95df4b7f5c79a2f78a61f1a63c36 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 25 Jan 2024 11:40:46 +0100 Subject: [PATCH 296/333] show message when accept or discard is need for saving (#203427) re https://github.com/microsoft/vscode-copilot/issues/3653 --- .../contrib/inlineChat/browser/inlineChatController.ts | 4 ++++ .../contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 9fb003a71d60b..be5046ad1370a 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -974,6 +974,10 @@ export class InlineChatController implements IEditorContribution { // ---- controller API + showSaveHint(): void { + const status = localize('savehint', "Accept or discard changes to continue saving"); + this._zone.value.widget.updateStatus(status, { classes: ['warn'] }); + } setPlaceholder(text: string): void { this._forcedPlaceholder = text; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts index 133923b13fe9e..23f8f2cf917cc 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts @@ -29,6 +29,7 @@ import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/commo import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; import { Event } from 'vs/base/common/event'; +import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; interface SessionData { readonly resourceUri: URI; @@ -241,6 +242,7 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { break; } this._inlineChatSessionService.moveSession(data.session, editor); + InlineChatController.get(editor)?.showSaveHint(); this._logService.info('WAIT for session to end', editor.getId(), data.session.targetUri.toString()); await this._whenSessionsEnded(Iterable.single(data), token); } From a1168b16d9afbb7ed9a2f28acd0af6b932caef5f Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 25 Jan 2024 11:41:43 +0100 Subject: [PATCH 297/333] Readonly hover stays on top of dialog (#203424) Fixes #203176 --- src/vs/editor/contrib/message/browser/messageController.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/message/browser/messageController.ts b/src/vs/editor/contrib/message/browser/messageController.ts index 6c27301f8b34b..dd32227654ccc 100644 --- a/src/vs/editor/contrib/message/browser/messageController.ts +++ b/src/vs/editor/contrib/message/browser/messageController.ts @@ -70,7 +70,10 @@ export class MessageController implements IEditorContribution { this._messageListeners.clear(); this._message = isMarkdownString(message) ? renderMarkdown(message, { actionHandler: { - callback: (url) => openLinkFromMarkdown(this._openerService, url, isMarkdownString(message) ? message.isTrusted : undefined), + callback: (url) => { + this.closeMessage(); + openLinkFromMarkdown(this._openerService, url, isMarkdownString(message) ? message.isTrusted : undefined); + }, disposables: this._messageListeners }, }) : undefined; From 290a1153b8dbf6171c0b087c07d3c47dc2c8f1f3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 25 Jan 2024 11:52:51 +0100 Subject: [PATCH 298/333] disable frequently failing chat-suite (#203430) https://github.com/microsoft/vscode/issues/203429 --- .../vscode-api-tests/src/singlefolder-tests/chat.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/chat.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/chat.test.ts index d88eea64c9625..f0a3a304064c2 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/chat.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/chat.test.ts @@ -8,7 +8,8 @@ import 'mocha'; import { CancellationToken, chat, ChatAgentRequest, ChatVariableLevel, Disposable, interactive, InteractiveSession, ProviderResult } from 'vscode'; import { assertNoRpc, closeAllEditors, DeferredPromise, disposeAll } from '../utils'; -suite('chat', () => { +suite.skip('chat', () => { + let disposables: Disposable[] = []; setup(() => { disposables = []; From a908e9d6d5a7e3dcfae58268625c46f5326bfe92 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 25 Jan 2024 11:56:50 +0100 Subject: [PATCH 299/333] Bump distro (#203431) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 835c8df8c7a2a..7720eab38e33c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.86.0", - "distro": "bfe80ecb51ea0f99e6c250ba625938dcc55461c1", + "distro": "6c14240015f9a76b34c29b4212fb8df761106443", "author": { "name": "Microsoft Corporation" }, From b0d1f894c38a163ac23e400d41c800decede6117 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 25 Jan 2024 12:25:45 +0100 Subject: [PATCH 300/333] speech recording stops after log pause in audio (fix #203189) (#203433) --- .../browser/actions/chatExecuteActions.ts | 5 ++++ .../actions/voiceChatActions.ts | 26 +++++++++---------- .../electron-sandbox/inlineChatActions.ts | 3 ++- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts index 9798132da9813..49ed40adf226d 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts @@ -12,9 +12,14 @@ import { IChatWidget, IChatWidgetService } from 'vs/workbench/contrib/chat/brows import { CONTEXT_CHAT_INPUT_HAS_TEXT, CONTEXT_CHAT_REQUEST_IN_PROGRESS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; +export interface IVoiceChatExecuteActionContext { + readonly disableTimeout?: boolean; +} + export interface IChatExecuteActionContext { widget?: IChatWidget; inputValue?: string; + voice?: IVoiceChatExecuteActionContext; } export class SubmitAction extends Action2 { diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 08a689112f683..bbaf3fa124d2f 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -262,7 +262,7 @@ class VoiceChatSessions { @IConfigurationService private readonly configurationService: IConfigurationService ) { } - async start(controller: IVoiceChatSessionController): Promise { + async start(controller: IVoiceChatSessionController, context?: IChatExecuteActionContext): Promise { this.stop(); const sessionId = ++this.voiceChatSessionIds; @@ -304,7 +304,7 @@ class VoiceChatSessions { case SpeechToTextStatus.Recognizing: if (text) { session.controller.updateInput([inputValue, text].join(' ')); - if (voiceChatTimeout > 0) { + if (voiceChatTimeout > 0 && context?.voice?.disableTimeout !== true) { acceptTranscriptionScheduler.cancel(); } } @@ -313,7 +313,7 @@ class VoiceChatSessions { if (text) { inputValue = [inputValue, text].join(' '); session.controller.updateInput(inputValue); - if (voiceChatTimeout > 0) { + if (voiceChatTimeout > 0 && context?.voice?.disableTimeout !== true) { acceptTranscriptionScheduler.schedule(); } } @@ -408,12 +408,12 @@ export class VoiceChatInChatViewAction extends Action2 { }); } - async run(accessor: ServicesAccessor): Promise { + async run(accessor: ServicesAccessor, context?: IChatExecuteActionContext): Promise { const instantiationService = accessor.get(IInstantiationService); const controller = await VoiceChatSessionControllerFactory.create(accessor, 'view'); if (controller) { - VoiceChatSessions.getInstance(instantiationService).start(controller); + VoiceChatSessions.getInstance(instantiationService).start(controller, context); } } } @@ -435,12 +435,12 @@ export class InlineVoiceChatAction extends Action2 { }); } - async run(accessor: ServicesAccessor): Promise { + async run(accessor: ServicesAccessor, context?: IChatExecuteActionContext): Promise { const instantiationService = accessor.get(IInstantiationService); const controller = await VoiceChatSessionControllerFactory.create(accessor, 'inline'); if (controller) { - VoiceChatSessions.getInstance(instantiationService).start(controller); + VoiceChatSessions.getInstance(instantiationService).start(controller, context); } } } @@ -462,12 +462,12 @@ export class QuickVoiceChatAction extends Action2 { }); } - async run(accessor: ServicesAccessor): Promise { + async run(accessor: ServicesAccessor, context?: IChatExecuteActionContext): Promise { const instantiationService = accessor.get(IInstantiationService); const controller = await VoiceChatSessionControllerFactory.create(accessor, 'quick'); if (controller) { - VoiceChatSessions.getInstance(instantiationService).start(controller); + VoiceChatSessions.getInstance(instantiationService).start(controller, context); } } } @@ -500,11 +500,11 @@ export class StartVoiceChatAction extends Action2 { }); } - async run(accessor: ServicesAccessor, context: unknown): Promise { + async run(accessor: ServicesAccessor, context?: IChatExecuteActionContext): Promise { const instantiationService = accessor.get(IInstantiationService); const commandService = accessor.get(ICommandService); - const widget = (context as IChatExecuteActionContext)?.widget; + const widget = context?.widget; if (widget) { // if we already get a context when the action is executed // from a toolbar within the chat widget, then make sure @@ -518,10 +518,10 @@ export class StartVoiceChatAction extends Action2 { const controller = await VoiceChatSessionControllerFactory.create(accessor, 'focused'); if (controller) { - VoiceChatSessions.getInstance(instantiationService).start(controller); + VoiceChatSessions.getInstance(instantiationService).start(controller, context); } else { // fallback to Quick Voice Chat command - commandService.executeCommand(QuickVoiceChatAction.ID); + commandService.executeCommand(QuickVoiceChatAction.ID, context); } } } diff --git a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts index a6b3bd9a496a3..7af9b3971bc6f 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions.ts @@ -16,6 +16,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { StartVoiceChatAction, StopListeningAction } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; +import { IChatExecuteActionContext } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions'; import { CTX_INLINE_CHAT_HAS_PROVIDER, CTX_INLINE_CHAT_VISIBLE, InlineChatConfigKeys } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { HasSpeechProvider, ISpeechService } from 'vs/workbench/contrib/speech/common/speechService'; @@ -95,7 +96,7 @@ function holdForSpeech(accessor: ServicesAccessor, ctrl: InlineChatController | let listening = false; const handle = disposableTimeout(() => { // start VOICE input - commandService.executeCommand(StartVoiceChatAction.ID); + commandService.executeCommand(StartVoiceChatAction.ID, { voice: { disableTimeout: true } } satisfies IChatExecuteActionContext); listening = true; }, 250); From adf93c270acf019a7ab0385ff5d9a66eba7aafc3 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 25 Jan 2024 15:53:20 +0100 Subject: [PATCH 301/333] Git - view stash should use the stash's parent commit for the left hand side (#203450) --- extensions/git/src/commands.ts | 13 +++++----- extensions/git/src/git.ts | 45 ++++++++++++++++------------------ 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index acf699eeca34a..33bd2ce23939c 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -3598,8 +3598,8 @@ export class CommandCenter { return; } - await result.repository.popStash(result.stash.index); await commands.executeCommand('workbench.action.closeActiveEditor'); + await result.repository.popStash(result.stash.index); } @command('git.stashApply', { repository: true }) @@ -3633,8 +3633,8 @@ export class CommandCenter { return; } - await result.repository.applyStash(result.stash.index); await commands.executeCommand('workbench.action.closeActiveEditor'); + await result.repository.applyStash(result.stash.index); } @command('git.stashDrop', { repository: true }) @@ -3709,6 +3709,7 @@ export class CommandCenter { } const stashChanges = await repository.showStash(stash.index); + const stashParentCommit = stash.parents.length > 0 ? stash.parents[0] : `${stash.hash}^`; if (!stashChanges || stashChanges.length === 0) { return; @@ -3720,13 +3721,13 @@ export class CommandCenter { const resources: { originalUri: Uri | undefined; modifiedUri: Uri | undefined }[] = []; for (const change of stashChanges) { if (change.status === Status.INDEX_ADDED) { - resources.push({ originalUri: undefined, modifiedUri: toGitUri(change.uri, `stash@{${stash.index}}`) }); + resources.push({ originalUri: undefined, modifiedUri: toGitUri(change.uri, stash.hash) }); } else if (change.status === Status.DELETED) { - resources.push({ originalUri: change.uri, modifiedUri: undefined }); + resources.push({ originalUri: toGitUri(change.uri, stashParentCommit), modifiedUri: undefined }); } else if (change.status === Status.INDEX_RENAMED) { - resources.push({ originalUri: change.originalUri, modifiedUri: toGitUri(change.uri, `stash@{${stash.index}}`) }); + resources.push({ originalUri: toGitUri(change.originalUri, stashParentCommit), modifiedUri: toGitUri(change.uri, stash.hash) }); } else { - resources.push({ originalUri: change.uri, modifiedUri: toGitUri(change.uri, `stash@{${stash.index}}`) }); + resources.push({ originalUri: toGitUri(change.uri, stashParentCommit), modifiedUri: toGitUri(change.uri, stash.hash) }); } } diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index f86c5ad99f573..119fce2815d8a 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -35,9 +35,11 @@ export interface IFileStatus { } export interface Stash { - index: number; - description: string; - branchName?: string; + readonly hash: string; + readonly parents: string[]; + readonly index: number; + readonly description: string; + readonly branchName?: string; } interface MutableRemote extends Remote { @@ -353,6 +355,7 @@ function sanitizePath(path: string): string { } const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%ct%n%P%n%D%n%B'; +const STASH_FORMAT = '%H%n%P%n%gd%n%gs'; export interface ICloneOptions { readonly parentPath: string; @@ -965,34 +968,28 @@ export function parseLsFiles(raw: string): LsFilesElement[] { .map(([, mode, object, stage, file]) => ({ mode, object, stage, file })); } +const stashRegex = /([0-9a-f]{40})\n(.*)\nstash@{(\d+)}\n(WIP\s)*on([^:]+):(.*)(?:\x00)/gmi; + function parseGitStashes(raw: string): Stash[] { const result: Stash[] = []; - const regex = /^stash@{(\d+)}:(.+)$/; - const descriptionRegex = /(WIP\s)*on([^:]+):(.*)$/i; - - for (const stash of raw.split('\n').filter(s => !!s)) { - // Extract index and description - const match = regex.exec(stash); - if (!match) { - continue; - } - const [, index, description] = match; + let match, hash, parents, index, wip, branchName, description; - // Extract branch name from description - const descriptionMatch = descriptionRegex.exec(description); - if (!descriptionMatch) { - result.push({ index: parseInt(index), description: description.trim() }); - continue; + do { + match = stashRegex.exec(raw); + if (match === null) { + break; } - const [, wip, branchName, message] = descriptionMatch; + [, hash, parents, index, wip, branchName, description] = match; result.push({ + hash, + parents: parents.split(' '), index: parseInt(index), - description: wip ? `WIP (${message.trim()})` : message.trim(), - branchName: branchName.trim() + branchName: branchName.trim(), + description: wip ? `WIP (${description.trim()})` : description.trim() }); - } + } while (true); return result; } @@ -2212,7 +2209,7 @@ export class Repository { } async showStash(index: number): Promise { - const args = ['stash', 'show', `stash@{${index}}`, '--name-status', '-z']; + const args = ['stash', 'show', `stash@{${index}}`, '--name-status', '-z', '-u']; try { const result = await this.exec(args); @@ -2500,7 +2497,7 @@ export class Repository { } async getStashes(): Promise { - const result = await this.exec(['stash', 'list']); + const result = await this.exec(['stash', 'list', `--format=${STASH_FORMAT}`, '-z']); return parseGitStashes(result.stdout.trim()); } From 5cbd1a836b1186b96bb945e6b4240dec5787db03 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 25 Jan 2024 16:21:47 +0100 Subject: [PATCH 302/333] release inline chat session for untitled file when it becomes saved (non-dirty) (#203454) fixes https://github.com/microsoft/vscode/issues/203448 --- .../browser/inlineChatSessionServiceImpl.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts index 5e211c6ac6d1a..6784574fc21e2 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts @@ -23,6 +23,7 @@ import { ITextModel, IValidEditOperation } from 'vs/editor/common/model'; import { Schemas } from 'vs/base/common/network'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { generateUuid } from 'vs/base/common/uuid'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; type SessionData = { editor: ICodeEditor; @@ -58,6 +59,7 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, @ILogService private readonly _logService: ILogService, @IInstantiationService private readonly _instaService: IInstantiationService, + @ITextFileService private readonly _textFileService: ITextFileService, ) { } dispose() { @@ -123,6 +125,18 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { targetUri.with({ scheme: Schemas.inMemory, query: new URLSearchParams({ id, 'inline-chat-textModel0': '' }).toString() }), true )); + // untitled documents are special + if (targetUri.scheme === Schemas.untitled) { + const untitledTextModel = this._textFileService.untitled.get(targetUri); + if (untitledTextModel) { + store.add(untitledTextModel.onDidChangeDirty(() => { + if (!untitledTextModel.isDirty()) { + this.releaseSession(session); + } + })); + } + } + let wholeRange = options.wholeRange; if (!wholeRange) { wholeRange = rawSession.wholeRange ? Range.lift(rawSession.wholeRange) : editor.getSelection(); From fde80ce4fbde1c406f07025d65d4730465ca5148 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 25 Jan 2024 08:56:24 -0800 Subject: [PATCH 303/333] eng: fix missing rustup command in build (#203462) Only call rustup when explicitly installing --- build/azure-pipelines/cli/cli-compile-and-publish.yml | 2 +- build/azure-pipelines/cli/install-rust-posix.yml | 1 + build/azure-pipelines/cli/test.yml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/cli/cli-compile-and-publish.yml b/build/azure-pipelines/cli/cli-compile-and-publish.yml index 4f0623b84fcac..a9aaac7325495 100644 --- a/build/azure-pipelines/cli/cli-compile-and-publish.yml +++ b/build/azure-pipelines/cli/cli-compile-and-publish.yml @@ -29,7 +29,7 @@ steps: displayName: Set product.json path - ${{ if parameters.VSCODE_CHECK_ONLY }}: - - script: rustup component add clippy && cargo clippy --target ${{ parameters.VSCODE_CLI_TARGET }} --bin=code + - script: cargo clippy --target ${{ parameters.VSCODE_CLI_TARGET }} --bin=code displayName: Lint ${{ parameters.VSCODE_CLI_TARGET }} workingDirectory: $(Build.SourcesDirectory)/cli env: diff --git a/build/azure-pipelines/cli/install-rust-posix.yml b/build/azure-pipelines/cli/install-rust-posix.yml index 00e3ecbdc51d2..78641cfb3a85e 100644 --- a/build/azure-pipelines/cli/install-rust-posix.yml +++ b/build/azure-pipelines/cli/install-rust-posix.yml @@ -33,6 +33,7 @@ steps: set -e rustup default $RUSTUP_TOOLCHAIN rustup update $RUSTUP_TOOLCHAIN + rustup component add clippy env: RUSTUP_TOOLCHAIN: ${{ parameters.channel }} displayName: "Set Rust version" diff --git a/build/azure-pipelines/cli/test.yml b/build/azure-pipelines/cli/test.yml index 24ab9682bfece..29dcf502f6a5b 100644 --- a/build/azure-pipelines/cli/test.yml +++ b/build/azure-pipelines/cli/test.yml @@ -1,7 +1,7 @@ steps: - template: ./install-rust-posix.yml - - script: rustup component add clippy && cargo clippy -- -D warnings + - script: cargo clippy -- -D warnings workingDirectory: cli displayName: Clippy lint From 6429752b2bfb99f3a1ed6cef713ba63d6dc86698 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 25 Jan 2024 17:59:38 +0100 Subject: [PATCH 304/333] aux window - hide custom title when native title is on (#203464) --- src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts b/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts index 093881157b426..22786a7c2cb51 100644 --- a/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts +++ b/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts @@ -14,7 +14,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { hasCustomTitlebar } from 'vs/platform/window/common/window'; +import { hasNativeTitlebar } from 'vs/platform/window/common/window'; import { IEditorGroupView, IEditorPartsView } from 'vs/workbench/browser/parts/editor/editor'; import { EditorPart, IEditorPartUIState } from 'vs/workbench/browser/parts/editor/editorPart'; import { IAuxiliaryTitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart'; @@ -107,7 +107,7 @@ export class AuxiliaryEditorPart { // Titlebar let titlebarPart: IAuxiliaryTitlebarPart | undefined = undefined; let titlebarPartVisible = false; - const useCustomTitle = isNative && hasCustomTitlebar(this.configurationService); // custom title in aux windows only enabled in native + const useCustomTitle = isNative && !hasNativeTitlebar(this.configurationService); // custom title in aux windows only enabled in native if (useCustomTitle) { titlebarPart = disposables.add(this.titleService.createAuxiliaryTitlebarPart(auxiliaryWindow.container, editorPart)); titlebarPartVisible = true; From bc7fc03d6a3950a709f968bedc4dd8990062d3d6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 25 Jan 2024 18:18:20 +0100 Subject: [PATCH 305/333] multi diff - implement `revert` to enable "Do not save" (#203376) * multi diff - implement `revert` to enable "Do not save" * restore close handler * align save/revert handling for original side with diff editor --- .../browser/multiDiffEditorInput.ts | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts index 65f6defe7ed6f..a8f59a63c3650 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts @@ -9,6 +9,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { Disposable, DisposableStore, IDisposable, IReference, toDisposable } from 'vs/base/common/lifecycle'; import { parse } from 'vs/base/common/marshalling'; +import { Schemas } from 'vs/base/common/network'; import { deepClone } from 'vs/base/common/objects'; import { autorun, derived, observableFromEvent } from 'vs/base/common/observable'; import { constObservable, mapObservableArrayCached } from 'vs/base/common/observableInternal/utils'; @@ -24,7 +25,7 @@ import { localize } from 'vs/nls'; import { ConfirmResult } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor'; -import { DEFAULT_EDITOR_ASSOCIATION, EditorInputCapabilities, EditorInputWithOptions, IEditorSerializer, IResourceMultiDiffEditorInput, ISaveOptions, IUntypedEditorInput } from 'vs/workbench/common/editor'; +import { DEFAULT_EDITOR_ASSOCIATION, EditorInputCapabilities, EditorInputWithOptions, GroupIdentifier, IEditorSerializer, IResourceMultiDiffEditorInput, IRevertOptions, ISaveOptions, IUntypedEditorInput } from 'vs/workbench/common/editor'; import { EditorInput, IEditorCloseHandler } from 'vs/workbench/common/editor/editorInput'; import { MultiDiffEditorIcon } from 'vs/workbench/contrib/multiDiffEditor/browser/icons.contribution'; import { ConstResolvedMultiDiffSource, IMultiDiffSourceResolverService, IResolvedMultiDiffSource, MultiDiffEditorItem } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffSourceResolverService'; @@ -242,15 +243,27 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor override readonly onDidChangeDirty = Event.fromObservableLight(this._isDirtyObservable); override isDirty() { return this._isDirtyObservable.get(); } - override async save(group: number, options?: ISaveOptions | undefined): Promise { + override async save(group: number, options?: ISaveOptions | undefined): Promise { + await this.doSaveOrRevert('save', group, options); + return this; + } + + override revert(group: GroupIdentifier, options?: IRevertOptions): Promise { + return this.doSaveOrRevert('revert', group, options); + } + + private async doSaveOrRevert(mode: 'save', group: GroupIdentifier, options?: ISaveOptions): Promise; + private async doSaveOrRevert(mode: 'revert', group: GroupIdentifier, options?: IRevertOptions): Promise; + private async doSaveOrRevert(mode: 'save' | 'revert', group: GroupIdentifier, options?: ISaveOptions | IRevertOptions): Promise { const items = this._viewModel.currentValue?.items.get(); if (items) { await Promise.all(items.map(async item => { const model = item.diffEditorViewModel.model; + const handleOriginal = model.original.uri.scheme !== Schemas.untitled && this._textFileService.isDirty(model.original.uri); // match diff editor behaviour await Promise.all([ - this._textFileService.save(model.original.uri, options), - this._textFileService.save(model.modified.uri, options), + handleOriginal ? mode === 'save' ? this._textFileService.save(model.original.uri, options) : this._textFileService.revert(model.original.uri, options) : Promise.resolve(), + mode === 'save' ? this._textFileService.save(model.modified.uri, options) : this._textFileService.revert(model.modified.uri, options), ]); })); } @@ -258,6 +271,11 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor } override readonly closeHandler: IEditorCloseHandler = { + + // TODO@bpasero TODO@hediet this is a workaround for + // not having a better way to figure out if the + // editors this input wraps around are opened or not + async confirm() { return ConfirmResult.DONT_SAVE; }, From 4f143e0e2823f888833f03e9afcb99513989adc2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 25 Jan 2024 21:01:02 +0100 Subject: [PATCH 306/333] aux window - align `layout()` behaviour with main window (#203479) * aux window - dont forget to apply `filter` when copying attributes * actually align layouts --- src/vs/base/browser/dom.ts | 8 +++++--- .../browser/auxiliaryWindowService.ts | 14 ++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 72caa84e06993..6d69e83484708 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -2341,9 +2341,11 @@ function camelCaseToHyphenCase(str: string) { return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); } -export function copyAttributes(from: Element, to: Element): void { +export function copyAttributes(from: Element, to: Element, filter?: string[]): void { for (const { name, value } of from.attributes) { - to.setAttribute(name, value); + if (!filter || filter.includes(name)) { + to.setAttribute(name, value); + } } } @@ -2357,7 +2359,7 @@ function copyAttribute(from: Element, to: Element, name: string): void { } export function trackAttributes(from: Element, to: Element, filter?: string[]): IDisposable { - copyAttributes(from, to); + copyAttributes(from, to, filter); const disposables = new DisposableStore(); diff --git a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts index 53803bb631631..57c904416e0f9 100644 --- a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts +++ b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts @@ -97,13 +97,7 @@ export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow { e.preventDefault(); })); - this._register(addDisposableListener(this.window, EventType.RESIZE, () => { - const dimension = getClientArea(this.window.document.body, this.container); - position(this.container, 0, 0, 0, 0, 'relative'); - size(this.container, dimension.width, dimension.height); - - this._onDidLayout.fire(dimension); - })); + this._register(addDisposableListener(this.window, EventType.RESIZE, () => this.layout())); this._register(addDisposableListener(this.container, EventType.SCROLL, () => this.container.scrollTop = 0)); // Prevent container from scrolling (#55456) @@ -142,7 +136,11 @@ export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow { } layout(): void { - this._onDidLayout.fire(getClientArea(this.window.document.body, this.container)); + const dimension = getClientArea(this.window.document.body, this.container); + position(this.container, 0, 0, 0, 0, 'relative'); + size(this.container, dimension.width, dimension.height); + + this._onDidLayout.fire(dimension); } override dispose(): void { From ed6c2eca2d09587ff2470fc397ccf2414e7609d7 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 25 Jan 2024 13:05:50 -0800 Subject: [PATCH 307/333] Fix #203434. Avoid layout from sticky scroll event if the editor is set to invisible. (#203485) --- .../workbench/contrib/notebook/browser/notebookEditorWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 5554136a78c4c..576970d7a3394 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1079,7 +1079,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return; } - if (this._dimension) { + if (this._dimension && this._isVisible) { if (sizeDelta > 0) { // delta > 0 ==> sticky is growing, cell list shrinking this.layout(this._dimension); this.setScrollTop(this.scrollTop + sizeDelta); From 6e2ea0ed2756722d1e46caae50c8f3a8f5f427b3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 25 Jan 2024 21:21:45 +0000 Subject: [PATCH 308/333] Also disable `` ` `` as autoclosing pair (#203487) Fixes #192676 Not ideal but likely better than the current behavior that inserts extra backticks --- extensions/markdown-basics/language-configuration.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/extensions/markdown-basics/language-configuration.json b/extensions/markdown-basics/language-configuration.json index 4a79f1c5c07b1..f1e7859ccca57 100644 --- a/extensions/markdown-basics/language-configuration.json +++ b/extensions/markdown-basics/language-configuration.json @@ -42,10 +42,6 @@ "string" ] }, - { - "open": "`", - "close": "`" - }, ], "surroundingPairs": [ [ From 274a528e33b7afca26cde92ab97a9e47c3eb3461 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Thu, 25 Jan 2024 13:23:20 -0800 Subject: [PATCH 309/333] Change default nb sticky mode based on feedback (#203488) change default nb sticky mode based on feedback --- .../workbench/contrib/notebook/browser/notebook.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index ce28e166b21bb..e7dc3cc4f97c1 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -886,7 +886,7 @@ configurationRegistry.registerConfiguration({ nls.localize('notebook.stickyScrollMode.flat', "Nested sticky lines appear flat."), nls.localize('notebook.stickyScrollMode.indented', "Nested sticky lines appear indented."), ], - default: 'flat', + default: 'indented', tags: ['notebookLayout'] }, [NotebookSetting.consolidatedOutputButton]: { From 661db2e6d3a965db59f7af4f231279291006dd6a Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Thu, 25 Jan 2024 13:52:42 -0800 Subject: [PATCH 310/333] chore: exclude not-reproducible issues (#203482) * Exclude not-reproducible issues * Add more missing labels --- .vscode/notebooks/endgame.github-issues | 8 ++++---- .vscode/notebooks/my-endgame.github-issues | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index e93d064d8de01..5e2c7f9a815fb 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"December / January 2024\"" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\r\n\r\n$MILESTONE=milestone:\"December / January 2024\"" }, { "kind": 1, @@ -112,7 +112,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:z-author-verified -label:unreleased" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:z-author-verified -label:unreleased -label:*not-reproducible" }, { "kind": 1, @@ -122,7 +122,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:z-author-verified label:unreleased" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:z-author-verified label:unreleased -label:*not-reproducible" }, { "kind": 1, @@ -132,7 +132,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:z-author-verified" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:z-author-verified -label:*not-reproducible" }, { "kind": 1, diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index 6adad9d8532a4..4d3d34a8c9229 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"December / January 2024\"\n\n$MINE=assignee:@me" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\r\n\r\n$MILESTONE=milestone:\"December / January 2024\"\r\n\r\n$MINE=assignee:@me" }, { "kind": 1, @@ -147,7 +147,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:triage-needed -label:verification-found" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:triage-needed -label:verification-found -label:*not-reproducible" }, { "kind": 1, @@ -167,7 +167,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -author:@me sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -label:*not-reproducible" }, { "kind": 1, @@ -187,6 +187,6 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:on-release-notes\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:engineering -label:on-release-notes\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:plan-item -label:on-release-notes" + "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:on-release-notes\r\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:engineering -label:on-release-notes\r\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:plan-item -label:on-release-notes" } ] \ No newline at end of file From 7f056a705e65c9b386ecc35e24fd5ffaaa6e8fc2 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Thu, 25 Jan 2024 15:01:03 -0800 Subject: [PATCH 311/333] =?UTF-8?q?Revert=20"Change=20Welcome=20page=20con?= =?UTF-8?q?tribution=20activation=20to=20LifecyclePhase=E2=80=A6=20(#20349?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "Change Welcome page contribution activation to LifecyclePhase.Starting (#201844)" This reverts commit 0d735d01abd17a5edc7bed45fdd7f15d4902954a. --- .../browser/gettingStarted.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts index 6e052c1019c7d..3d0ae3e607db7 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts @@ -327,4 +327,4 @@ configurationRegistry.registerConfiguration({ }); Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(StartupPageContribution, LifecyclePhase.Starting); + .registerWorkbenchContribution(StartupPageContribution, LifecyclePhase.Restored); From feff7c8a7e1c82320380f42d898a1e03051fefb7 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 25 Jan 2024 16:00:27 -0800 Subject: [PATCH 312/333] debug: fix closeReadonlyTabsOnEnd not working (#203500) The input itself is not readonly, the filesystem is, so this check was always failing. The entire `debug` scheme is readonly, so we don't need to try to replace the check with anything else. This is pretty safe since this code path is only hit when the new setting is enabled. Closes #197949 --- src/vs/workbench/contrib/debug/browser/debugService.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 805156e1210fa..07116a63fa38c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -710,10 +710,7 @@ export class DebugService implements IDebugService { if (this.configurationService.getValue('debug').closeReadonlyTabsOnEnd) { const editorsToClose = this.editorService.getEditors(EditorsOrder.SEQUENTIAL).filter(({ editor }) => { - if (editor.resource?.scheme === DEBUG_SCHEME) { - return editor.isReadonly() && session.getId() === Source.getEncodedDebugData(editor.resource).sessionId; - } - return false; + return editor.resource?.scheme === DEBUG_SCHEME && session.getId() === Source.getEncodedDebugData(editor.resource).sessionId; }); this.editorService.closeEditors(editorsToClose); } From 661cf5147bb4bc288b043272e9560fd916f0aaa7 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 26 Jan 2024 03:29:04 +0100 Subject: [PATCH 313/333] increasing the version to 1.87 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7720eab38e33c..a79f01cabdaa0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-oss-dev", - "version": "1.86.0", + "version": "1.87.0", "distro": "6c14240015f9a76b34c29b4212fb8df761106443", "author": { "name": "Microsoft Corporation" From b26b05031ed6a62dfe0fe3a93c27ffd4acf9f4a4 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 26 Jan 2024 10:50:25 +0100 Subject: [PATCH 314/333] SCM - Add "Reopen Closed Repositories..." action to the "Source control Repositories" view title bar (#203512) --- extensions/git/package.json | 9 +++++++++ src/vs/platform/actions/common/actions.ts | 1 + .../contrib/scm/browser/scmRepositoriesViewPane.ts | 2 +- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 5 ++--- .../services/actions/common/menusExtensionPoint.ts | 6 ++++++ .../services/extensions/common/extensionsApiProposals.ts | 1 + .../vscode.proposed.contribSourceControlTitleMenu.d.ts | 7 +++++++ 7 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.contribSourceControlTitleMenu.d.ts diff --git a/extensions/git/package.json b/extensions/git/package.json index e9d12a97405e8..4017a8618ed6f 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -18,6 +18,7 @@ "contribSourceControlHistoryItemGroupMenu", "contribSourceControlHistoryItemMenu", "contribSourceControlInputBoxMenu", + "contribSourceControlTitleMenu", "contribViewsWelcome", "diffCommand", "editSessionIdentityProvider", @@ -94,6 +95,7 @@ { "command": "git.reopenClosedRepositories", "title": "%command.reopenClosedRepositories%", + "icon": "$(repo)", "category": "Git", "enablement": "!operationInProgress && git.closedRepositoryCount != 0" }, @@ -1448,6 +1450,13 @@ "when": "scmProvider == git" } ], + "scm/sourceControl/title": [ + { + "command": "git.reopenClosedRepositories", + "group": "navigation@1", + "when": "scmProvider == git && git.closedRepositoryCount > 0" + } + ], "scm/sourceControl": [ { "command": "git.close", diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 0e39f772a7804..ece50e4de49dd 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -120,6 +120,7 @@ export class MenuId { static readonly SCMResourceGroupContext = new MenuId('SCMResourceGroupContext'); static readonly SCMSourceControl = new MenuId('SCMSourceControl'); static readonly SCMSourceControlInline = new MenuId('SCMSourceControlInline'); + static readonly SCMSourceControlTitle = new MenuId('SCMSourceControlTitle'); static readonly SCMTitle = new MenuId('SCMTitle'); static readonly SearchContext = new MenuId('SearchContext'); static readonly SearchActionMenu = new MenuId('SearchActionContext'); diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts index 20b33dd78fac4..7ec52b6aabd39 100644 --- a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts @@ -57,7 +57,7 @@ export class SCMRepositoriesViewPane extends ViewPane { @IThemeService themeService: IThemeService, @ITelemetryService telemetryService: ITelemetryService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); + super({ ...options, titleMenuId: MenuId.SCMSourceControlTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); } protected override renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 336f381c964b6..084d36256e74f 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -11,7 +11,7 @@ import { ViewPane, IViewPaneOptions, ViewAction } from 'vs/workbench/browser/par import { append, $, Dimension, asCSSUrl, trackFocus, clearNode, prepend } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryProviderCacheEntry, SCMHistoryItemChangeTreeElement, SCMHistoryItemGroupTreeElement, SCMHistoryItemTreeElement, SCMViewSeparatorElement } from 'vs/workbench/contrib/scm/common/history'; -import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent, ISCMService, SCMInputChangeReason, VIEW_PANE_ID, ISCMActionButton, ISCMActionButtonDescriptor, ISCMRepositorySortKey, REPOSITORIES_VIEW_PANE_ID, ISCMInputValueProviderContext, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent, ISCMService, SCMInputChangeReason, VIEW_PANE_ID, ISCMActionButton, ISCMActionButtonDescriptor, ISCMRepositorySortKey, ISCMInputValueProviderContext, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm'; import { ResourceLabels, IResourceLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -1639,8 +1639,7 @@ abstract class RepositorySortAction extends ViewAction { group: '1_sort' }, { - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', REPOSITORIES_VIEW_PANE_ID), + id: MenuId.SCMSourceControlTitle, group: '1_sort', }, ] diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index ead9769777214..d8918e89e6294 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -124,6 +124,12 @@ const apiMenus: IAPIMenu[] = [ id: MenuId.SCMSourceControl, description: localize('menus.scmSourceControl', "The Source Control menu") }, + { + key: 'scm/sourceControl/title', + id: MenuId.SCMSourceControlTitle, + description: localize('menus.scmSourceControlTitle', "The Source Control title menu"), + proposed: 'contribSourceControlTitleMenu' + }, { key: 'scm/resourceState/context', id: MenuId.SCMResourceContext, diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index e80c095be8450..0a1f29e9d7a24 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -36,6 +36,7 @@ export const allApiProposals = Object.freeze({ contribSourceControlHistoryItemGroupMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemGroupMenu.d.ts', contribSourceControlHistoryItemMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemMenu.d.ts', contribSourceControlInputBoxMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlInputBoxMenu.d.ts', + contribSourceControlTitleMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlTitleMenu.d.ts', contribStatusBarItems: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribStatusBarItems.d.ts', contribViewsRemote: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts', contribViewsWelcome: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts', diff --git a/src/vscode-dts/vscode.proposed.contribSourceControlTitleMenu.d.ts b/src/vscode-dts/vscode.proposed.contribSourceControlTitleMenu.d.ts new file mode 100644 index 0000000000000..af1aa65e7fb34 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribSourceControlTitleMenu.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for the `scm/sourceControl/title`-menu contribution point +// https://github.com/microsoft/vscode/issues/203511 From 0e7e31b4be159545a8fc5407c05808567b7bfdc4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 26 Jan 2024 11:53:26 +0100 Subject: [PATCH 315/333] save participants - provide access to the `from` resource in "Save As" scenarios (#203516) --- src/vs/platform/product/common/product.ts | 2 +- .../mainThreadNotebookSaveParticipant.ts | 7 ++-- .../api/browser/mainThreadSaveParticipant.ts | 7 ++-- .../codeEditor/browser/saveParticipants.ts | 24 ++++++------- .../saveParticipants/saveParticipants.ts | 12 +++---- .../textfile/browser/textFileService.ts | 5 ++- .../textfile/common/textFileEditorModel.ts | 12 +++---- .../common/textFileEditorModelManager.ts | 5 ++- .../common/textFileSaveParticipant.ts | 5 ++- .../services/textfile/common/textfiles.ts | 27 ++++++++++++-- .../test/browser/textFileEditorModel.test.ts | 27 ++++++++++++++ .../common/fileWorkingCopyManager.ts | 6 +++- .../common/storedFileWorkingCopy.ts | 27 +++++++++++--- .../storedFileWorkingCopySaveParticipant.ts | 5 ++- .../common/workingCopyFileService.ts | 21 +++++++++-- .../browser/storedFileWorkingCopy.test.ts | 36 +++++++++++++++++-- .../test/common/workbenchTestServices.ts | 4 +-- 17 files changed, 172 insertions(+), 60 deletions(-) diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 9482a2c954e44..467e11cc56a17 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -58,7 +58,7 @@ else { // Running out of sources if (Object.keys(product).length === 0) { Object.assign(product, { - version: '1.82.0-dev', + version: '1.87.0-dev', nameShort: 'Code - OSS Dev', nameLong: 'Code - OSS Dev', applicationName: 'code-oss', diff --git a/src/vs/workbench/api/browser/mainThreadNotebookSaveParticipant.ts b/src/vs/workbench/api/browser/mainThreadNotebookSaveParticipant.ts index eeba6fa2b4ede..dd17844187da6 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookSaveParticipant.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookSaveParticipant.ts @@ -8,11 +8,10 @@ import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; import { extHostCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { SaveReason } from 'vs/workbench/common/editor'; import { ExtHostContext, ExtHostNotebookDocumentSaveParticipantShape } from '../common/extHost.protocol'; import { IDisposable } from 'vs/base/common/lifecycle'; import { raceCancellationError } from 'vs/base/common/async'; -import { IStoredFileWorkingCopySaveParticipant, IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { IStoredFileWorkingCopySaveParticipant, IStoredFileWorkingCopySaveParticipantContext, IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IStoredFileWorkingCopy, IStoredFileWorkingCopyModel } from 'vs/workbench/services/workingCopy/common/storedFileWorkingCopy'; import { NotebookFileWorkingCopyModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel'; @@ -24,7 +23,7 @@ class ExtHostNotebookDocumentSaveParticipant implements IStoredFileWorkingCopySa this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebookDocumentSaveParticipant); } - async participate(workingCopy: IStoredFileWorkingCopy, env: { reason: SaveReason }, _progress: IProgress, token: CancellationToken): Promise { + async participate(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, _progress: IProgress, token: CancellationToken): Promise { if (!workingCopy.model || !(workingCopy.model instanceof NotebookFileWorkingCopyModel)) { return undefined; @@ -38,7 +37,7 @@ class ExtHostNotebookDocumentSaveParticipant implements IStoredFileWorkingCopySa () => reject(new Error(localize('timeout.onWillSave', "Aborted onWillSaveNotebookDocument-event after 1750ms"))), 1750 ); - this._proxy.$participateInSave(workingCopy.resource, env.reason, token).then(_ => { + this._proxy.$participateInSave(workingCopy.resource, context.reason, token).then(_ => { clearTimeout(_warningTimeout); return undefined; }).then(resolve, reject); diff --git a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts index fd39c8a6fbfec..a8c110c2ed4d1 100644 --- a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts @@ -9,8 +9,7 @@ import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; import { extHostCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { ITextFileSaveParticipant, ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; -import { SaveReason } from 'vs/workbench/common/editor'; +import { ITextFileSaveParticipant, ITextFileService, ITextFileEditorModel, ITextFileSaveParticipantContext } from 'vs/workbench/services/textfile/common/textfiles'; import { ExtHostContext, ExtHostDocumentSaveParticipantShape } from '../common/extHost.protocol'; import { IDisposable } from 'vs/base/common/lifecycle'; import { raceCancellationError } from 'vs/base/common/async'; @@ -23,7 +22,7 @@ class ExtHostSaveParticipant implements ITextFileSaveParticipant { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentSaveParticipant); } - async participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }, _progress: IProgress, token: CancellationToken): Promise { + async participate(editorModel: ITextFileEditorModel, context: ITextFileSaveParticipantContext, _progress: IProgress, token: CancellationToken): Promise { if (!editorModel.textEditorModel || !shouldSynchronizeModel(editorModel.textEditorModel)) { // the model never made it to the extension @@ -37,7 +36,7 @@ class ExtHostSaveParticipant implements ITextFileSaveParticipant { () => reject(new Error(localize('timeout.onWillSave', "Aborted onWillSaveTextDocument-event after 1750ms"))), 1750 ); - this._proxy.$participateInSave(editorModel.resource, env.reason).then(values => { + this._proxy.$participateInSave(editorModel.resource, context.reason).then(values => { if (!values.every(success => success)) { return Promise.reject(new Error('listener failed')); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index 122278e217aa3..ad3243f9a55f4 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -30,7 +30,7 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as import { SaveReason } from 'vs/workbench/common/editor'; import { getModifiedRanges } from 'vs/workbench/contrib/format/browser/formatModified'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { ITextFileEditorModel, ITextFileSaveParticipant, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileEditorModel, ITextFileSaveParticipant, ITextFileSaveParticipantContext, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; export class TrimWhitespaceParticipant implements ITextFileSaveParticipant { @@ -41,13 +41,13 @@ export class TrimWhitespaceParticipant implements ITextFileSaveParticipant { // Nothing } - async participate(model: ITextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(model: ITextFileEditorModel, context: ITextFileSaveParticipantContext): Promise { if (!model.textEditorModel) { return; } if (this.configurationService.getValue('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageId(), resource: model.resource })) { - this.doTrimTrailingWhitespace(model.textEditorModel, env.reason === SaveReason.AUTO); + this.doTrimTrailingWhitespace(model.textEditorModel, context.reason === SaveReason.AUTO); } } @@ -107,7 +107,7 @@ export class FinalNewLineParticipant implements ITextFileSaveParticipant { // Nothing } - async participate(model: ITextFileEditorModel, _env: { reason: SaveReason }): Promise { + async participate(model: ITextFileEditorModel, context: ITextFileSaveParticipantContext): Promise { if (!model.textEditorModel) { return; } @@ -145,13 +145,13 @@ export class TrimFinalNewLinesParticipant implements ITextFileSaveParticipant { // Nothing } - async participate(model: ITextFileEditorModel, env: { reason: SaveReason }): Promise { + async participate(model: ITextFileEditorModel, context: ITextFileSaveParticipantContext): Promise { if (!model.textEditorModel) { return; } if (this.configurationService.getValue('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageId(), resource: model.resource })) { - this.doTrimFinalNewLines(model.textEditorModel, env.reason === SaveReason.AUTO); + this.doTrimFinalNewLines(model.textEditorModel, context.reason === SaveReason.AUTO); } } @@ -217,11 +217,11 @@ class FormatOnSaveParticipant implements ITextFileSaveParticipant { // Nothing } - async participate(model: ITextFileEditorModel, env: { reason: SaveReason }, progress: IProgress, token: CancellationToken): Promise { + async participate(model: ITextFileEditorModel, context: ITextFileSaveParticipantContext, progress: IProgress, token: CancellationToken): Promise { if (!model.textEditorModel) { return; } - if (env.reason === SaveReason.AUTO) { + if (context.reason === SaveReason.AUTO) { return undefined; } @@ -272,7 +272,7 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant { @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, ) { } - async participate(model: ITextFileEditorModel, env: { reason: SaveReason }, progress: IProgress, token: CancellationToken): Promise { + async participate(model: ITextFileEditorModel, context: ITextFileSaveParticipantContext, progress: IProgress, token: CancellationToken): Promise { if (!model.textEditorModel) { return; } @@ -286,11 +286,11 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant { return undefined; } - if (env.reason === SaveReason.AUTO) { + if (context.reason === SaveReason.AUTO) { return undefined; } - if (env.reason !== SaveReason.EXPLICIT && Array.isArray(setting)) { + if (context.reason !== SaveReason.EXPLICIT && Array.isArray(setting)) { return undefined; } @@ -326,7 +326,7 @@ class CodeActionOnSaveParticipant implements ITextFileSaveParticipant { progress.report({ message: localize('codeaction', "Quick Fixes") }); - const filteredSaveList = Array.isArray(setting) ? codeActionsOnSave : codeActionsOnSave.filter(x => setting[x.value] === 'always' || ((setting[x.value] === 'explicit' || setting[x.value] === true) && env.reason === SaveReason.EXPLICIT)); + const filteredSaveList = Array.isArray(setting) ? codeActionsOnSave : codeActionsOnSave.filter(x => setting[x.value] === 'always' || ((setting[x.value] === 'explicit' || setting[x.value] === true) && context.reason === SaveReason.EXPLICIT)); await this.applyOnSaveActions(textEditorModel, filteredSaveList, excludedActions, progress, token); } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts b/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts index 04cdc764d38be..0ffd61f705a28 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts @@ -36,7 +36,7 @@ import { NotebookFileWorkingCopyModel } from 'vs/workbench/contrib/notebook/comm import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IStoredFileWorkingCopy, IStoredFileWorkingCopyModel } from 'vs/workbench/services/workingCopy/common/storedFileWorkingCopy'; -import { IStoredFileWorkingCopySaveParticipant, IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { IStoredFileWorkingCopySaveParticipant, IStoredFileWorkingCopySaveParticipantContext, IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; class FormatOnSaveParticipant implements IStoredFileWorkingCopySaveParticipant { constructor( @@ -47,7 +47,7 @@ class FormatOnSaveParticipant implements IStoredFileWorkingCopySaveParticipant { @IConfigurationService private readonly configurationService: IConfigurationService, ) { } - async participate(workingCopy: IStoredFileWorkingCopy, context: { reason: SaveReason }, progress: IProgress, token: CancellationToken): Promise { + async participate(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress, token: CancellationToken): Promise { if (!workingCopy.model || !(workingCopy.model instanceof NotebookFileWorkingCopyModel)) { return; } @@ -108,7 +108,7 @@ class TrimWhitespaceParticipant implements IStoredFileWorkingCopySaveParticipant @IBulkEditService private readonly bulkEditService: IBulkEditService, ) { } - async participate(workingCopy: IStoredFileWorkingCopy, context: { reason: SaveReason }, progress: IProgress, _token: CancellationToken): Promise { + async participate(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress, _token: CancellationToken): Promise { if (this.configurationService.getValue('files.trimTrailingWhitespace')) { await this.doTrimTrailingWhitespace(workingCopy, context.reason === SaveReason.AUTO, progress); } @@ -175,7 +175,7 @@ class TrimFinalNewLinesParticipant implements IStoredFileWorkingCopySaveParticip @IBulkEditService private readonly bulkEditService: IBulkEditService, ) { } - async participate(workingCopy: IStoredFileWorkingCopy, context: { reason: SaveReason }, progress: IProgress, _token: CancellationToken): Promise { + async participate(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress, _token: CancellationToken): Promise { if (this.configurationService.getValue('files.trimFinalNewlines')) { await this.doTrimFinalNewLines(workingCopy, context.reason === SaveReason.AUTO, progress); } @@ -252,7 +252,7 @@ class FinalNewLineParticipant implements IStoredFileWorkingCopySaveParticipant { @IEditorService private readonly editorService: IEditorService, ) { } - async participate(workingCopy: IStoredFileWorkingCopy, context: { reason: SaveReason }, progress: IProgress, _token: CancellationToken): Promise { + async participate(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress, _token: CancellationToken): Promise { // waiting on notebook-specific override before this feature can sync with 'files.insertFinalNewline' // if (this.configurationService.getValue('files.insertFinalNewline')) { @@ -317,7 +317,7 @@ class CodeActionOnSaveParticipant implements IStoredFileWorkingCopySaveParticipa ) { } - async participate(workingCopy: IStoredFileWorkingCopy, context: { reason: SaveReason }, progress: IProgress, token: CancellationToken): Promise { + async participate(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress, token: CancellationToken): Promise { const nbDisposable = new DisposableStore(); const isTrusted = this.workspaceTrustManagementService.isWorkspaceTrusted(); if (!isTrusted) { diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index b877f973eb6b8..b388e7cc33477 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -560,7 +560,10 @@ export abstract class AbstractTextFileService extends Disposable implements ITex } // save model - return targetModel.save(options); + return targetModel.save({ + ...options, + from: source + }); } private async confirmOverwrite(resource: URI): Promise { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 32c62dc4dd6e1..4fb09ccd01506 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -8,7 +8,7 @@ import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { mark } from 'vs/base/common/performance'; import { assertIsDefined } from 'vs/base/common/types'; -import { EncodingMode, ITextFileService, TextFileEditorModelState, ITextFileEditorModel, ITextFileStreamContent, ITextFileResolveOptions, IResolvedTextFileEditorModel, ITextFileSaveOptions, TextFileResolveReason, ITextFileEditorModelSaveEvent } from 'vs/workbench/services/textfile/common/textfiles'; +import { EncodingMode, ITextFileService, TextFileEditorModelState, ITextFileEditorModel, ITextFileStreamContent, ITextFileResolveOptions, IResolvedTextFileEditorModel, TextFileResolveReason, ITextFileEditorModelSaveEvent, ITextFileSaveAsOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { IRevertOptions, SaveReason, SaveSourceRegistry } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { IWorkingCopyBackupService, IResolvedWorkingCopyBackup } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; @@ -723,7 +723,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil //#region Save - async save(options: ITextFileSaveOptions = Object.create(null)): Promise { + async save(options: ITextFileSaveAsOptions = Object.create(null)): Promise { if (!this.isResolved()) { return false; } @@ -751,7 +751,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.hasState(TextFileEditorModelState.SAVED); } - private async doSave(options: ITextFileSaveOptions): Promise { + private async doSave(options: ITextFileSaveAsOptions): Promise { if (typeof options.reason !== 'number') { options.reason = SaveReason.EXPLICIT; } @@ -852,7 +852,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (!saveCancellation.token.isCancellationRequested) { this.ignoreSaveFromSaveParticipants = true; try { - await this.textFileService.files.runSaveParticipants(this, { reason: options.reason ?? SaveReason.EXPLICIT }, saveCancellation.token); + await this.textFileService.files.runSaveParticipants(this, { reason: options.reason ?? SaveReason.EXPLICIT, savedFrom: options.from }, saveCancellation.token); } finally { this.ignoreSaveFromSaveParticipants = false; } @@ -919,7 +919,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil })(), () => saveCancellation.cancel()); } - private handleSaveSuccess(stat: IFileStatWithMetadata, versionId: number, options: ITextFileSaveOptions): void { + private handleSaveSuccess(stat: IFileStatWithMetadata, versionId: number, options: ITextFileSaveAsOptions): void { // Updated resolved stat with updated stat this.updateLastResolvedFileStat(stat); @@ -939,7 +939,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this._onDidSave.fire({ reason: options.reason, stat, source: options.source }); } - private handleSaveError(error: Error, versionId: number, options: ITextFileSaveOptions): void { + private handleSaveError(error: Error, versionId: number, options: ITextFileSaveAsOptions): void { (options.ignoreErrorHandler ? this.logService.trace : this.logService.error).apply(this.logService, [`[text file model] handleSaveError(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource.toString()]); // Return early if the save() call was made asking to diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 7328c0609368c..7aa0c00f8c1cc 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -16,10 +16,9 @@ import { IFileService, FileChangesEvent, FileOperation, FileChangeType, IFileSys import { Promises, ResourceQueue } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { TextFileSaveParticipant } from 'vs/workbench/services/textfile/common/textFileSaveParticipant'; -import { SaveReason } from 'vs/workbench/common/editor'; import { CancellationToken } from 'vs/base/common/cancellation'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IWorkingCopyFileService, WorkingCopyFileEvent } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { IStoredFileWorkingCopySaveParticipantContext, IWorkingCopyFileService, WorkingCopyFileEvent } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { ITextSnapshot } from 'vs/editor/common/model'; import { extname, joinPath } from 'vs/base/common/resources'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; @@ -536,7 +535,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return this.saveParticipants.addSaveParticipant(participant); } - runSaveParticipants(model: ITextFileEditorModel, context: { reason: SaveReason }, token: CancellationToken): Promise { + runSaveParticipants(model: ITextFileEditorModel, context: IStoredFileWorkingCopySaveParticipantContext, token: CancellationToken): Promise { return this.saveParticipants.participate(model, context, token); } diff --git a/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts index 3dc16d1a46ec1..97d74549b9ac6 100644 --- a/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts +++ b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts @@ -8,8 +8,7 @@ import { raceCancellation } from 'vs/base/common/async'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { ITextFileSaveParticipant, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; -import { SaveReason } from 'vs/workbench/common/editor'; +import { ITextFileSaveParticipant, ITextFileEditorModel, ITextFileSaveParticipantContext } from 'vs/workbench/services/textfile/common/textfiles'; import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { insert } from 'vs/base/common/arrays'; @@ -30,7 +29,7 @@ export class TextFileSaveParticipant extends Disposable { return toDisposable(() => remove()); } - participate(model: ITextFileEditorModel, context: { reason: SaveReason }, token: CancellationToken): Promise { + participate(model: ITextFileEditorModel, context: ITextFileSaveParticipantContext, token: CancellationToken): Promise { const cts = new CancellationTokenSource(token); return this.progressService.withProgress({ diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 314e30a451b88..306bca1e19c86 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -313,6 +313,22 @@ export interface ITextFileResolveEvent { readonly reason: TextFileResolveReason; } +export interface ITextFileSaveParticipantContext { + + /** + * The reason why the save was triggered. + */ + readonly reason: SaveReason; + + /** + * Only applies to when a text file was saved as, for + * example when starting with untitled and saving. This + * provides access to the initial resource the text + * file had before. + */ + readonly savedFrom?: URI; +} + export interface ITextFileSaveParticipant { /** @@ -321,7 +337,7 @@ export interface ITextFileSaveParticipant { */ participate( model: ITextFileEditorModel, - context: { reason: SaveReason }, + context: ITextFileSaveParticipantContext, progress: IProgress, token: CancellationToken ): Promise; @@ -369,7 +385,7 @@ export interface ITextFileEditorModelManager { /** * Runs the registered save participants on the provided model. */ - runSaveParticipants(model: ITextFileEditorModel, context: { reason: SaveReason }, token: CancellationToken): Promise; + runSaveParticipants(model: ITextFileEditorModel, context: ITextFileSaveParticipantContext, token: CancellationToken): Promise; /** * Waits for the model to be ready to be disposed. There may be conditions @@ -406,6 +422,11 @@ export interface ITextFileSaveOptions extends ISaveOptions { export interface ITextFileSaveAsOptions extends ITextFileSaveOptions { + /** + * Optional URI of the resource the text file is saved from if known. + */ + readonly from?: URI; + /** * Optional URI to use as suggested file path to save as. */ @@ -498,7 +519,7 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport updatePreferredEncoding(encoding: string | undefined): void; - save(options?: ITextFileSaveOptions): Promise; + save(options?: ITextFileSaveAsOptions): Promise; revert(options?: IRevertOptions): Promise; resolve(options?: ITextFileResolveOptions): Promise; diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index 8528d367687e5..4b42d9de86b17 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -19,6 +19,7 @@ import { SaveReason, SaveSourceRegistry } from 'vs/workbench/common/editor'; import { isEqual } from 'vs/base/common/resources'; import { UTF16be } from 'vs/workbench/services/textfile/common/encoding'; import { isWeb } from 'vs/base/common/platform'; +import { URI } from 'vs/base/common/uri'; suite('Files - TextFileEditorModel', () => { @@ -849,5 +850,31 @@ suite('Files - TextFileEditorModel', () => { await savePromise; } + test('Save Participant carries context', async function () { + const model: TextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined)); + + const from = URI.file('testFrom'); + let e: Error | undefined = undefined; + disposables.add(accessor.textFileService.files.addSaveParticipant({ + participate: async (wc, context) => { + try { + assert.strictEqual(context.reason, SaveReason.EXPLICIT); + assert.strictEqual(context.savedFrom?.toString(), from.toString()); + } catch (error) { + e = error; + } + } + })); + + await model.resolve(); + model.updateTextEditorModel(createTextBufferFactory('foo')); + + await model.save({ force: true, from }); + + if (e) { + throw e; + } + }); + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts b/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts index 8493b64cc30c2..829afbd8c47da 100644 --- a/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts +++ b/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts @@ -429,7 +429,11 @@ export class FileWorkingCopyManager e * Whether the stored file working copy is readonly or not. */ isReadonly(): boolean | IMarkdownString; + + /** + * Asks the stored file working copy to save. If the stored file + * working copy was dirty, it is expected to be non-dirty after + * this operation has finished. + * + * @returns `true` if the operation was successful and `false` otherwise. + */ + save(options?: IStoredFileWorkingCopySaveAsOptions): Promise; } export interface IResolvedStoredFileWorkingCopy extends IStoredFileWorkingCopy { @@ -236,6 +245,14 @@ export interface IStoredFileWorkingCopySaveOptions extends ISaveOptions { readonly ignoreErrorHandler?: boolean; } +export interface IStoredFileWorkingCopySaveAsOptions extends IStoredFileWorkingCopySaveOptions { + + /** + * Optional URI of the resource the text file is saved from if known. + */ + readonly from?: URI; +} + export interface IStoredFileWorkingCopyResolver { /** @@ -815,7 +832,7 @@ export class StoredFileWorkingCopy extend private ignoreSaveFromSaveParticipants = false; - async save(options: IStoredFileWorkingCopySaveOptions = Object.create(null)): Promise { + async save(options: IStoredFileWorkingCopySaveAsOptions = Object.create(null)): Promise { if (!this.isResolved()) { return false; } @@ -843,7 +860,7 @@ export class StoredFileWorkingCopy extend return this.hasState(StoredFileWorkingCopyState.SAVED); } - private async doSave(options: IStoredFileWorkingCopySaveOptions): Promise { + private async doSave(options: IStoredFileWorkingCopySaveAsOptions): Promise { if (typeof options.reason !== 'number') { options.reason = SaveReason.EXPLICIT; } @@ -947,7 +964,7 @@ export class StoredFileWorkingCopy extend if (!saveCancellation.token.isCancellationRequested) { this.ignoreSaveFromSaveParticipants = true; try { - await this.workingCopyFileService.runSaveParticipants(this, { reason: options.reason ?? SaveReason.EXPLICIT }, saveCancellation.token); + await this.workingCopyFileService.runSaveParticipants(this, { reason: options.reason ?? SaveReason.EXPLICIT, savedFrom: options.from }, saveCancellation.token); } finally { this.ignoreSaveFromSaveParticipants = false; } @@ -1039,7 +1056,7 @@ export class StoredFileWorkingCopy extend })(), () => saveCancellation.cancel()); } - private handleSaveSuccess(stat: IFileStatWithMetadata, versionId: number, options: IStoredFileWorkingCopySaveOptions): void { + private handleSaveSuccess(stat: IFileStatWithMetadata, versionId: number, options: IStoredFileWorkingCopySaveAsOptions): void { // Updated resolved stat with updated stat this.updateLastResolvedFileStat(stat); @@ -1059,7 +1076,7 @@ export class StoredFileWorkingCopy extend this._onDidSave.fire({ reason: options.reason, stat, source: options.source }); } - private handleSaveError(error: Error, versionId: number, options: IStoredFileWorkingCopySaveOptions): void { + private handleSaveError(error: Error, versionId: number, options: IStoredFileWorkingCopySaveAsOptions): void { (options.ignoreErrorHandler ? this.logService.trace : this.logService.error).apply(this.logService, [`[stored file working copy] handleSaveError(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource.toString(), this.typeId]); // Return early if the save() call was made asking to diff --git a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts index 86177563f33aa..2473efad9f035 100644 --- a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts +++ b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts @@ -8,10 +8,9 @@ import { raceCancellation } from 'vs/base/common/async'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { SaveReason } from 'vs/workbench/common/editor'; import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { insert } from 'vs/base/common/arrays'; -import { IStoredFileWorkingCopySaveParticipant } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { IStoredFileWorkingCopySaveParticipant, IStoredFileWorkingCopySaveParticipantContext } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IStoredFileWorkingCopy, IStoredFileWorkingCopyModel } from 'vs/workbench/services/workingCopy/common/storedFileWorkingCopy'; export class StoredFileWorkingCopySaveParticipant extends Disposable { @@ -33,7 +32,7 @@ export class StoredFileWorkingCopySaveParticipant extends Disposable { return toDisposable(() => remove()); } - participate(workingCopy: IStoredFileWorkingCopy, context: { reason: SaveReason }, token: CancellationToken): Promise { + participate(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, token: CancellationToken): Promise { const cts = new CancellationTokenSource(token); return this.progressService.withProgress({ diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts index b70d40f4676b7..1896c95759798 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts @@ -84,6 +84,21 @@ export interface IWorkingCopyFileOperationParticipant { ): Promise; } +export interface IStoredFileWorkingCopySaveParticipantContext { + /** + * The reason why the save was triggered. + */ + readonly reason: SaveReason; + + /** + * Only applies to when a text file was saved as, for + * example when starting with untitled and saving. This + * provides access to the initial resource the text + * file had before. + */ + readonly savedFrom?: URI; +} + export interface IStoredFileWorkingCopySaveParticipant { /** @@ -92,7 +107,7 @@ export interface IStoredFileWorkingCopySaveParticipant { */ participate( workingCopy: IStoredFileWorkingCopy, - context: { reason: SaveReason }, + context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress, token: CancellationToken ): Promise; @@ -191,7 +206,7 @@ export interface IWorkingCopyFileService { /** * Runs all available save participants for stored file working copies. */ - runSaveParticipants(workingCopy: IStoredFileWorkingCopy, context: { reason: SaveReason }, token: CancellationToken): Promise; + runSaveParticipants(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, token: CancellationToken): Promise; //#endregion @@ -492,7 +507,7 @@ export class WorkingCopyFileService extends Disposable implements IWorkingCopyFi return this.saveParticipants.addSaveParticipant(participant); } - runSaveParticipants(workingCopy: IStoredFileWorkingCopy, context: { reason: SaveReason }, token: CancellationToken): Promise { + runSaveParticipants(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, token: CancellationToken): Promise { return this.saveParticipants.participate(workingCopy, context, token); } diff --git a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts index ba5268aff391d..afe82979c8d1e 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts @@ -879,11 +879,12 @@ suite('StoredFileWorkingCopy', function () { }); async function testSaveFromSaveParticipant(workingCopy: StoredFileWorkingCopy, async: boolean): Promise { - + const from = URI.file('testFrom'); assert.strictEqual(accessor.workingCopyFileService.hasSaveParticipants, false); const disposable = accessor.workingCopyFileService.addSaveParticipant({ - participate: async () => { + participate: async (wc, context) => { + if (async) { await timeout(10); } @@ -894,11 +895,40 @@ suite('StoredFileWorkingCopy', function () { assert.strictEqual(accessor.workingCopyFileService.hasSaveParticipants, true); - await workingCopy.save({ force: true }); + await workingCopy.save({ force: true, from }); disposable.dispose(); } + test('Save Participant carries context', async function () { + await workingCopy.resolve(); + + const from = URI.file('testFrom'); + assert.strictEqual(accessor.workingCopyFileService.hasSaveParticipants, false); + + let e: Error | undefined = undefined; + const disposable = accessor.workingCopyFileService.addSaveParticipant({ + participate: async (wc, context) => { + try { + assert.strictEqual(context.reason, SaveReason.EXPLICIT); + assert.strictEqual(context.savedFrom?.toString(), from.toString()); + } catch (error) { + e = error; + } + } + }); + + assert.strictEqual(accessor.workingCopyFileService.hasSaveParticipants, true); + + await workingCopy.save({ force: true, from }); + + if (e) { + throw e; + } + + disposable.dispose(); + }); + test('revert', async () => { await workingCopy.resolve(); workingCopy.model?.updateContents('hello revert'); diff --git a/src/vs/workbench/test/common/workbenchTestServices.ts b/src/vs/workbench/test/common/workbenchTestServices.ts index 864c6e9ed8fb2..1a938d7dbd676 100644 --- a/src/vs/workbench/test/common/workbenchTestServices.ts +++ b/src/vs/workbench/test/common/workbenchTestServices.ts @@ -15,7 +15,7 @@ import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { InMemoryStorageService, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopy'; import { NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IWorkingCopyFileService, IWorkingCopyFileOperationParticipant, WorkingCopyFileEvent, IDeleteOperation, ICopyOperation, IMoveOperation, IFileOperationUndoRedoInfo, ICreateFileOperation, ICreateOperation, IStoredFileWorkingCopySaveParticipant } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; +import { IWorkingCopyFileService, IWorkingCopyFileOperationParticipant, WorkingCopyFileEvent, IDeleteOperation, ICopyOperation, IMoveOperation, IFileOperationUndoRedoInfo, ICreateFileOperation, ICreateOperation, IStoredFileWorkingCopySaveParticipant, IStoredFileWorkingCopySaveParticipantContext } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IBaseFileStat, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { ISaveOptions, IRevertOptions, SaveReason, GroupIdentifier } from 'vs/workbench/common/editor'; @@ -253,7 +253,7 @@ export class TestWorkingCopyFileService implements IWorkingCopyFileService { readonly hasSaveParticipants = false; addSaveParticipant(participant: IStoredFileWorkingCopySaveParticipant): IDisposable { return Disposable.None; } - async runSaveParticipants(workingCopy: IWorkingCopy, context: { reason: SaveReason }, token: CancellationToken): Promise { } + async runSaveParticipants(workingCopy: IWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, token: CancellationToken): Promise { } async delete(operations: IDeleteOperation[], token: CancellationToken, undoInfo?: IFileOperationUndoRedoInfo): Promise { } From 35d97bc7e439fce0f50f42074041ab2d8571b20a Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 26 Jan 2024 13:06:22 +0100 Subject: [PATCH 316/333] Update grammars (#203521) --- extensions/csharp/cgmanifest.json | 2 +- .../csharp/syntaxes/csharp.tmLanguage.json | 33 +- extensions/fsharp/cgmanifest.json | 2 +- .../fsharp/syntaxes/fsharp.tmLanguage.json | 7 +- extensions/go/cgmanifest.json | 4 +- extensions/go/syntaxes/go.tmLanguage.json | 405 ++++++++------ .../syntaxes/JavaScript.tmLanguage.json | 9 +- .../syntaxes/JavaScriptReact.tmLanguage.json | 9 +- extensions/julia/cgmanifest.json | 2 +- .../julia/syntaxes/julia.tmLanguage.json | 24 +- extensions/latex/cgmanifest.json | 2 +- .../latex/syntaxes/LaTeX.tmLanguage.json | 47 +- .../markdown-latex-combined.tmLanguage.json | 9 +- extensions/python/cgmanifest.json | 2 +- extensions/rust/cgmanifest.json | 2 +- extensions/rust/syntaxes/rust.tmLanguage.json | 16 +- extensions/typescript-basics/cgmanifest.json | 2 +- .../syntaxes/TypeScript.tmLanguage.json | 9 +- .../syntaxes/TypeScriptReact.tmLanguage.json | 9 +- .../vb/syntaxes/asp-vb-net.tmLanguage.json | 2 +- .../test/colorize-results/test_jl.json | 522 +++++++++++++++++- 21 files changed, 882 insertions(+), 237 deletions(-) diff --git a/extensions/csharp/cgmanifest.json b/extensions/csharp/cgmanifest.json index 0dceaebc96625..1c88bd17296ca 100644 --- a/extensions/csharp/cgmanifest.json +++ b/extensions/csharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dotnet/csharp-tmLanguage", "repositoryUrl": "https://github.com/dotnet/csharp-tmLanguage", - "commitHash": "7bf5709ac1a713e340a618d1b41f44a043e393c6" + "commitHash": "7a7482ffc72a6677a87eb1ed76005593a4f7f131" } }, "license": "MIT", diff --git a/extensions/csharp/syntaxes/csharp.tmLanguage.json b/extensions/csharp/syntaxes/csharp.tmLanguage.json index 4e6a722d41326..96dbe04473c32 100644 --- a/extensions/csharp/syntaxes/csharp.tmLanguage.json +++ b/extensions/csharp/syntaxes/csharp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/dotnet/csharp-tmLanguage/commit/7bf5709ac1a713e340a618d1b41f44a043e393c6", + "version": "https://github.com/dotnet/csharp-tmLanguage/commit/7a7482ffc72a6677a87eb1ed76005593a4f7f131", "name": "C#", "scopeName": "source.cs", "patterns": [ @@ -164,6 +164,9 @@ { "include": "#comment" }, + { + "include": "#storage-modifier" + }, { "include": "#property-declaration" }, @@ -176,6 +179,9 @@ { "include": "#method-declaration" }, + { + "include": "#operator-declaration" + }, { "include": "#attribute-section" }, @@ -1050,6 +1056,18 @@ "name": "storage.type.struct.cs", "match": "\\bstruct\\b" }, + { + "name": "keyword.other.constraint.default.cs", + "match": "\\bdefault\\b" + }, + { + "name": "keyword.other.constraint.notnull.cs", + "match": "\\bnotnull\\b" + }, + { + "name": "keyword.other.constraint.unmanaged.cs", + "match": "\\bunmanaged\\b" + }, { "match": "(new)\\s*(\\()\\s*(\\))", "captures": { @@ -2632,7 +2650,7 @@ }, "patterns": [ { - "begin": "\\G", + "begin": "(?=[^;\\)])", "end": "(?=;|\\))", "patterns": [ { @@ -2695,22 +2713,25 @@ "include": "#intrusive" }, { - "match": "(?x)\n(?:\n (\\bvar\\b)|\n (?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n )\n)\\s+\n(\\g)\\s+\n\\b(in)\\b", + "match": "(?x)\n(?:\n (?:(\\bref)\\s+)?(\\bvar\\b)| # ref local\n (?\n (?:\n (?:ref\\s+)? # ref local\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n )\n)\\s+\n(\\g)\\s+\n\\b(in)\\b", "captures": { "1": { - "name": "storage.type.var.cs" + "name": "storage.modifier.ref.cs" }, "2": { + "name": "storage.type.var.cs" + }, + "3": { "patterns": [ { "include": "#type" } ] }, - "7": { + "8": { "name": "entity.name.variable.local.cs" }, - "8": { + "9": { "name": "keyword.control.loop.in.cs" } } diff --git a/extensions/fsharp/cgmanifest.json b/extensions/fsharp/cgmanifest.json index 03dad6e72c95d..524b3fa0d46a4 100644 --- a/extensions/fsharp/cgmanifest.json +++ b/extensions/fsharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "ionide/ionide-fsgrammar", "repositoryUrl": "https://github.com/ionide/ionide-fsgrammar", - "commitHash": "472c6b2030c962217cbbb26e4ddcce1b8ffe0867" + "commitHash": "7d029a46f17637228b2ee85dd02e511c3e8039b3" } }, "license": "MIT", diff --git a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json index 20151138ccfe2..5063f1c5210c0 100644 --- a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json +++ b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/ionide/ionide-fsgrammar/commit/472c6b2030c962217cbbb26e4ddcce1b8ffe0867", + "version": "https://github.com/ionide/ionide-fsgrammar/commit/7d029a46f17637228b2ee85dd02e511c3e8039b3", "name": "fsharp", "scopeName": "source.fsharp", "patterns": [ @@ -574,6 +574,11 @@ "name": "fast-capture.comment.line.double-slash.fsharp", "match": "//" }, + { + "comments": "Capture (*) when inside of (* *) so that it doesn't prematurely end the comment block.", + "name": "fast-capture.comment.line.mul-operator.fsharp", + "match": "\\(\\*\\)" + }, { "include": "#comments" } diff --git a/extensions/go/cgmanifest.json b/extensions/go/cgmanifest.json index 90df81c75ecfd..dd832ef5145c6 100644 --- a/extensions/go/cgmanifest.json +++ b/extensions/go/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "go-syntax", "repositoryUrl": "https://github.com/worlpaker/go-syntax", - "commitHash": "4014e9376f32e5317eb0c6be286db7ffcba838f9" + "commitHash": "80a9e153c018b6c3b9c52766b39d7be9d915f68b" } }, "license": "MIT", "description": "The file syntaxes/go.tmLanguage.json is from https://github.com/worlpaker/go-syntax, which in turn was derived from https://github.com/jeff-hykin/better-go-syntax.", - "version": "0.5.1" + "version": "0.5.5" } ], "version": 1 diff --git a/extensions/go/syntaxes/go.tmLanguage.json b/extensions/go/syntaxes/go.tmLanguage.json index 85e2bbfe78e18..5917568d4ccfb 100644 --- a/extensions/go/syntaxes/go.tmLanguage.json +++ b/extensions/go/syntaxes/go.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/worlpaker/go-syntax/commit/4014e9376f32e5317eb0c6be286db7ffcba838f9", + "version": "https://github.com/worlpaker/go-syntax/commit/80a9e153c018b6c3b9c52766b39d7be9d915f68b", "name": "Go", "scopeName": "source.go", "patterns": [ @@ -61,6 +61,9 @@ { "include": "#other_struct_interface_expressions" }, + { + "include": "#type_assertion_inline" + }, { "include": "#struct_variables_types" }, @@ -91,19 +94,22 @@ "comment": "all statements related to variables", "patterns": [ { - "include": "#after_control_variables" + "include": "#var_const_assignment" + }, + { + "include": "#variable_assignment" }, { - "include": "#var_const_single_assignment" + "include": "#label_loop_variables" }, { - "include": "#var_assignment" + "include": "#slice_index_variables" }, { - "include": "#var_other_assignment" + "include": "#property_variables" }, { - "include": "#var_const_multi_assignment" + "include": "#switch_select_case_variables" }, { "include": "#other_variables" @@ -141,7 +147,7 @@ "include": "#storage_types" }, { - "include": "#raw_strings_literals" + "include": "#raw_string_literals" }, { "include": "#string_literals" @@ -182,7 +188,7 @@ "include": "#storage_types" }, { - "include": "#raw_strings_literals" + "include": "#raw_string_literals" }, { "include": "#string_literals" @@ -582,7 +588,7 @@ } ] }, - "raw_strings_literals": { + "raw_string_literals": { "comment": "Raw string literals", "begin": "`", "beginCaptures": { @@ -1272,7 +1278,7 @@ }, { "comment": "struct type declaration", - "match": "((?:(?:\\w+\\,\\s*)+)?\\w+)\\s+(?=(?:(?:\\s*(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:[\\[\\]\\*]+)?\\bstruct\\b\\s*\\{)", + "match": "((?:(?:\\b\\w+\\,\\s*)+)?\\b\\w+)\\s+(?=(?:(?:\\s*(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:[\\[\\]\\*]+)?\\bstruct\\b\\s*\\{)", "captures": { "1": { "patterns": [ @@ -1289,7 +1295,7 @@ }, { "comment": "multiple parameters one type -with multilines", - "match": "(?:(?:(?<=\\()|^\\s*)((?:(?:\\w+\\,\\s*)+)(?:/(?:/|\\*).*)?)$)", + "match": "(?:(?:(?<=\\()|^\\s*)((?:(?:\\b\\w+\\,\\s*)+)(?:/(?:/|\\*).*)?)$)", "captures": { "1": { "patterns": [ @@ -1306,7 +1312,7 @@ }, { "comment": "multiple params and types | multiple params one type | one param one type", - "match": "(?:((?:(?:\\w+\\,\\s*)+)?\\w+)(?:\\s+)((?:(?:\\s*(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:(?:(?:[\\w\\[\\]\\.\\*]+)?(?:(?:\\bfunc\\b\\((?:[^\\)]+)?\\))(?:(?:\\s*(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:\\s*))+(?:(?:(?:[\\w\\*\\.\\[\\]]+)|(?:\\((?:[^\\)]+)?\\))))?)|(?:(?:[\\[\\]\\*]+)?[\\w\\*\\.]+(?:\\[(?:[^\\]]+)\\])?(?:[\\w\\.\\*]+)?)+)))", + "match": "(?:((?:(?:\\b\\w+\\,\\s*)+)?\\b\\w+)(?:\\s+)((?:(?:\\s*(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:(?:(?:[\\w\\[\\]\\.\\*]+)?(?:(?:\\bfunc\\b\\((?:[^\\)]+)?\\))(?:(?:\\s*(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:\\s*))+(?:(?:(?:[\\w\\*\\.\\[\\]]+)|(?:\\((?:[^\\)]+)?\\))))?)|(?:(?:[\\[\\]\\*]+)?[\\w\\*\\.]+(?:\\[(?:[^\\]]+)\\])?(?:[\\w\\.\\*]+)?)+)))", "captures": { "1": { "patterns": [ @@ -1368,7 +1374,7 @@ }, { "comment": "multiple parameters one type -with multilines", - "match": "(?:(?:(?<=\\()|^\\s*)((?:(?:\\w+\\,\\s*)+)(?:/(?:/|\\*).*)?)$)", + "match": "(?:(?:(?<=\\()|^\\s*)((?:(?:\\b\\w+\\,\\s*)+)(?:/(?:/|\\*).*)?)$)", "captures": { "1": { "patterns": [ @@ -1385,7 +1391,7 @@ }, { "comment": "multiple params and types | multiple types one param", - "match": "(?:((?:(?:\\w+\\,\\s*)+)?\\w+)(?:\\s+)((?:(?:\\s*(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:(?:(?:[\\w\\[\\]\\.\\*]+)?(?:(?:\\bfunc\\b\\((?:[^\\)]+)?\\))(?:(?:\\s*(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:\\s*))+(?:(?:(?:[\\w\\*\\.]+)|(?:\\((?:[^\\)]+)?\\))))?)|(?:(?:(?:[\\w\\*\\.\\~]+)|(?:\\[(?:(?:[\\w\\.\\*]+)?(?:\\[(?:[^\\]]+)?\\])?(?:\\,\\s+)?)+\\]))(?:[\\w\\.\\*]+)?)+)))", + "match": "(?:((?:(?:\\b\\w+\\,\\s*)+)?\\b\\w+)(?:\\s+)((?:(?:\\s*(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:(?:(?:[\\w\\[\\]\\.\\*]+)?(?:(?:\\bfunc\\b\\((?:[^\\)]+)?\\))(?:(?:\\s*(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:\\s*))+(?:(?:(?:[\\w\\*\\.]+)|(?:\\((?:[^\\)]+)?\\))))?)|(?:(?:(?:[\\w\\*\\.\\~]+)|(?:\\[(?:(?:[\\w\\.\\*]+)?(?:\\[(?:[^\\]]+)?\\])?(?:\\,\\s+)?)+\\]))(?:[\\w\\.\\*]+)?)+)))", "captures": { "1": { "patterns": [ @@ -1430,7 +1436,7 @@ }, { "comment": "other types", - "match": "([\\w\\.]+)", + "match": "(?:\\b([\\w\\.]+))", "captures": { "1": { "patterns": [ @@ -1590,25 +1596,13 @@ } }, "other_struct_interface_expressions": { + "comment": "struct and interface expression in-line (before curly bracket)", "patterns": [ - { - "include": "#storage_types" - }, - { - "include": "#label_loop_variable" - }, - { - "include": "#property_variables" - }, - { - "include": "#switch_select_case_variables" - }, { "include": "#after_control_variables" }, { - "comment": "struct expression before curly bracket", - "match": "((?:(?:\\w+\\.)+)?\\w+)(\\[(?:[^\\]]+)?\\])?(?=\\{)(?|\\<\\=|\\>\\=|\\=\\=|\\!\\=|\\w(?:\\+|/|\\-|\\*|\\%)(?:\\=)?|\\|\\||\\&\\&)(?:\\s*)([[:alnum:]\\-\\_\\!\\.\\[\\]\\<\\>\\=\\*/\\+\\%\\:]+)(?:\\s*)(?=\\{))", + "match": "(?:(?<=\\brange\\b|\\bswitch\\b|\\;|\\bif\\b|\\bfor\\b|\\<|\\>|\\<\\=|\\>\\=|\\=\\=|\\!\\=|\\w(?:\\+|/|\\-|\\*|\\%)|\\w(?:\\+|/|\\-|\\*|\\%)\\=|\\|\\||\\&\\&)(?:\\s*)([[:alnum:]\\-\\_\\!\\.\\[\\]\\<\\>\\=\\*/\\+\\%\\:]+)(?:\\s*)(?=\\{))", "captures": { "1": { "patterns": [ @@ -2385,90 +2379,40 @@ } ] }, - "var_const_single_assignment": { - "comment": "var and const with single type assignment", - "match": "(?:(?<=\\bvar\\b|\\bconst\\b)(?:\\s*)([\\w\\.]+(?:(?:\\,\\s*[\\w\\.]+)+)?)(?:\\s*)(?:(?!(?:(?:[\\[\\]\\*]+)?\\bstruct\\b\\s*\\{)|(?:(?:[\\[\\]\\*]+)?\\bfunc\\b))((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:(?:(?:[\\w\\.\\*]+)?(?:\\[(?:.*)\\])?(?:[\\w\\.\\*]+)?))?\\s*(?:\\=)?))?)", - "captures": { - "1": { - "patterns": [ - { - "include": "#delimiters" - }, - { - "match": "(?:\\w+)", - "name": "variable.other.assignment.go" - } - ] - }, - "2": { - "patterns": [ - { - "include": "#type-declarations" - }, - { - "include": "#generic_types" - }, - { - "match": "(?:\\w+)", - "name": "entity.name.type.go" - } - ] - } - } - }, - "var_assignment": { - "comment": "variable assignment", - "match": "(?,\\s*\\w+(?:\\.\\w+)*)*)(?=\\s*=(?!=))", - "captures": { - "1": { - "patterns": [ - { - "match": "\\d\\w*", - "name": "invalid.illegal.identifier.go" - }, - { - "match": "\\w+(?:\\.\\w+)*", - "name": "variable.other.assignment.go", - "captures": { - "0": { - "patterns": [ - { - "include": "#delimiters" - } - ] + "var_const_assignment": { + "comment": "variable assignment with var and const keyword", + "patterns": [ + { + "comment": "var and const with single type assignment", + "match": "(?:(?<=\\bvar\\b|\\bconst\\b)(?:\\s*)([\\w\\.]+(?:(?:\\,\\s*[\\w\\.]+)+)?)(?:\\s*)(?:((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?!(?:(?:[\\[\\]\\*]+)?\\bstruct\\b\\s*\\{)|(?:(?:[\\[\\]\\*]+)?\\bfunc\\b))(?:(?:(?:[\\w\\.\\*]+)?(?:\\[(?:.*)\\])?(?:[\\w\\.\\*]+)?))?\\s*(?:\\=)?))?)", + "captures": { + "1": { + "patterns": [ + { + "include": "#delimiters" + }, + { + "match": "(?:\\w+)", + "name": "variable.other.assignment.go" } - } - }, - { - "include": "#delimiters" - } - ] - } - } - }, - "var_other_assignment": { - "comment": "variable other assignment", - "match": "\\b\\w+(?:,\\s*\\w+)*(?=\\s*:=)", - "captures": { - "0": { - "patterns": [ - { - "match": "\\d\\w*", - "name": "invalid.illegal.identifier.go" - }, - { - "match": "\\w+", - "name": "variable.other.assignment.go" + ] }, - { - "include": "#delimiters" + "2": { + "patterns": [ + { + "include": "#type-declarations" + }, + { + "include": "#generic_types" + }, + { + "match": "(?:\\w+)", + "name": "entity.name.type.go" + } + ] } - ] - } - } - }, - "var_const_multi_assignment": { - "patterns": [ + } + }, { "comment": "var and const with multi type assignment", "begin": "(?:(?<=\\bvar\\b|\\bconst\\b)(?:\\s*)(\\())", @@ -2485,7 +2429,7 @@ }, "patterns": [ { - "match": "(?:(?:^\\s+)([\\w\\.]+(?:(?:\\,\\s*[\\w\\.]+)+)?)(?:\\s*)((?:(?!(?:(?:[\\[\\]\\*]+)?\\bstruct\\b\\s*\\{)|(?:(?:[\\[\\]\\*]+)?\\bfunc\\b))((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:(?:(?:[\\w\\.\\*]+)?(?:\\[(?:.*)\\])?(?:[\\w\\.\\*]+)?))?\\s*(?:\\=)?))?))", + "match": "(?:(?:^\\s+)([\\w\\.]+(?:(?:\\,\\s*[\\w\\.]+)+)?)(?:\\s*)((?:((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?!(?:(?:[\\[\\]\\*]+)?\\bstruct\\b\\s*\\{)|(?:(?:[\\[\\]\\*]+)?\\bfunc\\b))(?:(?:(?:[\\w\\.\\*]+)?(?:\\[(?:.*)\\])?(?:[\\w\\.\\*]+)?))?\\s*(?:\\=)?))?))", "captures": { "1": { "patterns": [ @@ -2521,6 +2465,56 @@ } ] }, + "variable_assignment": { + "comment": "variable assignment", + "patterns": [ + { + "comment": "variable assignment with :=", + "match": "\\b\\w+(?:\\,\\s*\\w+)*(?=\\s*:=)", + "captures": { + "0": { + "patterns": [ + { + "include": "#delimiters" + }, + { + "match": "\\d\\w*", + "name": "invalid.illegal.identifier.go" + }, + { + "match": "\\w+", + "name": "variable.other.assignment.go" + } + ] + } + } + }, + { + "comment": "variable assignment with =", + "match": "\\b[\\w\\.\\*]+(?:\\,\\s*[\\w\\.\\*]+)*(?=\\s*=(?!=))", + "captures": { + "0": { + "patterns": [ + { + "include": "#delimiters" + }, + { + "include": "#operators" + }, + { + "match": "\\d\\w*", + "name": "invalid.illegal.identifier.go" + }, + { + "match": "\\w+", + "name": "variable.other.assignment.go" + } + ] + } + } + } + ] + }, "generic_types": { "comment": "Generic support for all types", "match": "(?:([\\w\\.\\*]+)(\\[(?:[^\\]]+)?\\]))", @@ -2568,52 +2562,103 @@ } } }, + "slice_index_variables": { + "comment": "slice index and capacity variables, to not scope them as property variables", + "match": "(?<=\\w\\[)((?:(?:\\b[\\w\\.\\*\\+/\\-\\*\\%\\<\\>\\|\\&]+\\:)|(?:\\:\\b[\\w\\.\\*\\+/\\-\\*\\%\\<\\>\\|\\&]+))(?:\\b[\\w\\.\\*\\+/\\-\\*\\%\\<\\>\\|\\&]+)?(?:\\:\\b[\\w\\.\\*\\+/\\-\\*\\%\\<\\>\\|\\&]+)?)(?=\\])", + "captures": { + "1": { + "patterns": [ + { + "include": "#type-declarations" + }, + { + "match": "\\w+", + "name": "variable.other.go" + } + ] + } + } + }, "property_variables": { - "patterns": [ - { - "comment": "Property variables in struct", - "match": "(?:((?:[\\w\\.]+)(?:\\:))(?!\\=))", - "captures": { - "1": { - "patterns": [ - { - "include": "#type-declarations" - }, - { - "match": "\\w+", - "name": "variable.other.property.go" - } - ] + "comment": "Property variables in struct | parameter field in struct initialization", + "match": "(?:(?:((?:\\b[\\w\\.]+)(?:\\:(?!\\=))))(?:(?:\\s*([\\w\\.\\*\\&\\[\\]]+)(\\.\\w+)(?![\\w\\.\\*\\&\\[\\]]*(?:\\{|\\()))((?:\\s*(?:\\<|\\>|\\<\\=|\\>\\=|\\=\\=|\\!\\=|\\|\\||\\&\\&|\\+|/|\\-|\\*|\\%|\\||\\&)\\s*(?:[\\w\\.\\*\\&\\[\\]]+)(?:\\.\\w+)(?![\\w\\.\\*\\&\\[\\]]*(?:\\{|\\()))*))?)", + "captures": { + "1": { + "patterns": [ + { + "include": "#type-declarations" + }, + { + "match": "\\w+", + "name": "variable.other.property.go" } - } + ] }, - { - "comment": "property variables as parameter field in struct initialization", - "match": "(?<=[\\w\\.]\\:)(?:\\s*)([\\w\\.\\*\\&\\[\\]]+)(\\.\\w+)(?![\\w\\.\\*\\&\\[\\]]*(?:\\{|\\())", - "captures": { - "1": { - "patterns": [ - { - "include": "$self" - } - ] + "2": { + "patterns": [ + { + "include": "#type-declarations" }, - "2": { - "patterns": [ - { - "include": "#type-declarations" + { + "match": "\\w+", + "name": "variable.other.go" + }, + { + "include": "$self" + } + ] + }, + "3": { + "patterns": [ + { + "include": "#type-declarations" + }, + { + "match": "\\w+", + "name": "variable.other.property.field.go" + }, + { + "include": "$self" + } + ] + }, + "4": { + "patterns": [ + { + "match": "([\\w\\.\\*\\&\\[\\]]+)(\\.\\w+)", + "captures": { + "1": { + "patterns": [ + { + "include": "#type-declarations" + }, + { + "match": "\\w+", + "name": "variable.other.go" + } + ] }, - { - "match": "(?:\\w+)", - "name": "variable.other.property.field.go" + "2": { + "patterns": [ + { + "include": "#type-declarations" + }, + { + "match": "\\w+", + "name": "variable.other.property.field.go" + } + ] } - ] + } + }, + { + "include": "$self" } - } + ] } - ] + } }, - "label_loop_variable": { + "label_loop_variables": { "comment": "labeled loop variable name", "match": "((?:^\\s*\\w+:\\s*$)|(?:^\\s*(?:\\bbreak\\b|\\bgoto\\b|\\bcontinue\\b)\\s+\\w+(?:\\s*/(?:/|\\*)\\s*.*)?$))", "captures": { @@ -2632,24 +2677,34 @@ }, "double_parentheses_types": { "comment": "double parentheses types", - "match": "(?:(?:(\\()([^\\)]+)(\\)))(?=\\((?:[\\w\\.\\*\\&]+)\\)))", + "match": "(?:(\\((?:[\\w\\.\\[\\]\\*\\&]+)\\))(?=\\())", "captures": { "1": { - "name": "punctuation.definition.begin.bracket.round.go" - }, - "2": { "patterns": [ { - "include": "#type-declarations" + "include": "#type-declarations-without-brackets" + }, + { + "match": "\\(", + "name": "punctuation.definition.begin.bracket.round.go" + }, + { + "match": "\\)", + "name": "punctuation.definition.end.bracket.round.go" + }, + { + "match": "\\[", + "name": "punctuation.definition.begin.bracket.square.go" + }, + { + "match": "\\]", + "name": "punctuation.definition.end.bracket.square.go" }, { "match": "\\w+", "name": "entity.name.type.go" } ] - }, - "3": { - "name": "punctuation.definition.end.bracket.round.go" } } }, diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 4fe09e087aa1a..bec7b9f5b924e 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/8c7482b94b548eab56da64dbfb30b82589b3f747", + "version": "https://github.com/microsoft/TypeScript-TmLanguage/commit/b80b7509a78e642f789c567e144ed951ab98b4e3", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -2351,12 +2351,15 @@ ] }, "import-export-assert-clause": { - "begin": "(? Date: Fri, 26 Jan 2024 13:19:43 +0100 Subject: [PATCH 317/333] fix #203523 (#203524) --- .../node/extensionManagementService.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index c7137910f446b..905dec7cd9060 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -599,7 +599,13 @@ export class ExtensionsScanner extends Disposable { metadata = { ...source?.metadata, ...metadata }; if (target) { - await this.extensionsProfileScannerService.updateMetadata([[extension, { ...target.metadata, ...metadata }]], toProfileLocation); + if (this.uriIdentityService.extUri.isEqual(target.location, extension.location)) { + await this.extensionsProfileScannerService.updateMetadata([[extension, { ...target.metadata, ...metadata }]], toProfileLocation); + } else { + const targetExtension = await this.scanLocalExtension(target.location, extension.type, toProfileLocation); + await this.extensionsProfileScannerService.removeExtensionFromProfile(targetExtension, toProfileLocation); + await this.extensionsProfileScannerService.addExtensionsToProfile([[extension, { ...target.metadata, ...metadata }]], toProfileLocation); + } } else { await this.extensionsProfileScannerService.addExtensionsToProfile([[extension, metadata]], toProfileLocation); } From fb5a1aca6de5209439268dc853e31464df69600c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 26 Jan 2024 13:59:40 +0100 Subject: [PATCH 318/333] add more leakage checks in tests (#203525) re https://github.com/microsoft/vscode/issues/200091 --- .eslintrc.json | 14 --------- .../test/browser/smartSelect.test.ts | 3 ++ .../test/browser/snippetSession.test.ts | 3 ++ .../test/browser/completionModel.test.ts | 3 ++ .../test/browser/suggestMemory.test.ts | 3 ++ .../instantiation/test/common/graph.test.ts | 4 +++ .../markers/test/common/markerService.test.ts | 31 +++++++++++++------ .../progress/test/common/progress.test.ts | 4 +++ .../test/browser/extHostApiCommands.test.ts | 11 ++++--- .../api/test/browser/extHostBulkEdits.test.ts | 3 ++ .../extHostDocumentSaveParticipant.test.ts | 8 ++++- .../test/browser/extHostTypeConverter.test.ts | 5 ++- .../test/browser/mainThreadDocuments.test.ts | 3 ++ .../snippets/test/browser/snippetFile.test.ts | 3 ++ .../test/browser/snippetsRegistry.test.ts | 3 ++ .../test/browser/snippetsRewrite.test.ts | 3 ++ 16 files changed, 74 insertions(+), 30 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index e9670fc9c83c0..4bb1fc53e36e4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -154,10 +154,6 @@ "src/vs/editor/contrib/codeAction/test/browser/codeActionKeybindingResolver.test.ts", "src/vs/editor/contrib/codeAction/test/browser/codeActionModel.test.ts", "src/vs/editor/contrib/gotoSymbol/test/browser/referencesModel.test.ts", - "src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts", - "src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts", - "src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts", - "src/vs/editor/contrib/suggest/test/browser/suggestMemory.test.ts", "src/vs/editor/test/common/services/languageService.test.ts", "src/vs/editor/test/node/classification/typescript.test.ts", "src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts", @@ -167,22 +163,15 @@ "src/vs/platform/contextkey/test/common/parser.test.ts", "src/vs/platform/contextkey/test/common/scanner.test.ts", "src/vs/platform/extensions/test/common/extensionValidator.test.ts", - "src/vs/platform/instantiation/test/common/graph.test.ts", "src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts", "src/vs/platform/keybinding/test/common/keybindingLabels.test.ts", "src/vs/platform/keybinding/test/common/keybindingResolver.test.ts", - "src/vs/platform/markers/test/common/markerService.test.ts", "src/vs/platform/opener/test/common/opener.test.ts", - "src/vs/platform/progress/test/common/progress.test.ts", "src/vs/platform/registry/test/common/platform.test.ts", "src/vs/platform/remote/test/common/remoteHosts.test.ts", "src/vs/platform/telemetry/test/browser/1dsAppender.test.ts", "src/vs/platform/workspace/test/common/workspace.test.ts", "src/vs/platform/workspaces/test/electron-main/workspaces.test.ts", - "src/vs/workbench/api/test/browser/extHostApiCommands.test.ts", - "src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts", - "src/vs/workbench/api/test/browser/extHostDocumentSaveParticipant.test.ts", - "src/vs/workbench/api/test/browser/extHostTypeConverter.test.ts", "src/vs/workbench/api/test/browser/extHostWorkspace.test.ts", "src/vs/workbench/api/test/browser/mainThreadConfiguration.test.ts", "src/vs/workbench/api/test/browser/mainThreadDocuments.test.ts", @@ -194,9 +183,6 @@ "src/vs/workbench/contrib/extensions/test/common/extensionQuery.test.ts", "src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts", "src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts", - "src/vs/workbench/contrib/snippets/test/browser/snippetFile.test.ts", - "src/vs/workbench/contrib/snippets/test/browser/snippetsRegistry.test.ts", - "src/vs/workbench/contrib/snippets/test/browser/snippetsRewrite.test.ts", "src/vs/workbench/contrib/tasks/test/common/problemMatcher.test.ts", "src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts", "src/vs/workbench/services/commands/test/common/commandService.test.ts", diff --git a/src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts index dadceb8254a5b..fb1307529f657 100644 --- a/src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts @@ -19,6 +19,7 @@ import { createModelServices } from 'vs/editor/test/common/testTextModel'; import { javascriptOnEnterRules } from 'vs/editor/test/common/modes/supports/javascriptOnEnterRules'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; import { ILanguageSelection, ILanguageService } from 'vs/editor/common/languages/language'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; class StaticLanguageSelector implements ILanguageSelection { readonly onDidChange: Event = Event.None; @@ -64,6 +65,8 @@ suite('SmartSelect', () => { disposables.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + async function assertGetRangesToPosition(text: string[], lineNumber: number, column: number, ranges: Range[], selectLeadingAndTrailingWhitespace = true): Promise { const uri = URI.file('test.js'); const model = modelService.createModel(text.join('\n'), new StaticLanguageSelector(languageId), uri); diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts index a9563c30f1339..6106184672439 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { mock } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; @@ -58,6 +59,8 @@ suite('SnippetSession', function () { editor.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('normalize whitespace', function () { function assertNormalized(position: IPosition, input: string, expected: string): void { diff --git a/src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts b/src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts index 2cc955597b45a..cb08e56fb7f99 100644 --- a/src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { EditorOptions, InternalSuggestOptions } from 'vs/editor/common/config/editorOptions'; import { IPosition } from 'vs/editor/common/core/position'; import * as languages from 'vs/editor/common/languages'; @@ -83,6 +84,8 @@ suite('CompletionModel', function () { }, WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('filtering - cached', function () { const itemsNow = model.items; diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestMemory.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestMemory.test.ts index e0c1496c0d0da..3bbc8c5883869 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestMemory.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestMemory.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IPosition } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; import { CompletionItem } from 'vs/editor/contrib/suggest/browser/suggest'; @@ -30,6 +31,8 @@ suite('SuggestMemories', function () { buffer.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('AbstractMemory, select', function () { const mem = new class extends Memory { diff --git a/src/vs/platform/instantiation/test/common/graph.test.ts b/src/vs/platform/instantiation/test/common/graph.test.ts index 29ef9de95b33c..c5abc22ccff82 100644 --- a/src/vs/platform/instantiation/test/common/graph.test.ts +++ b/src/vs/platform/instantiation/test/common/graph.test.ts @@ -3,15 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { Graph } from 'vs/platform/instantiation/common/graph'; suite('Graph', () => { + let graph: Graph; setup(() => { graph = new Graph(s => s); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('is possible to lookup nodes that don\'t exist', function () { assert.strictEqual(graph.lookup('ddd'), undefined); }); diff --git a/src/vs/platform/markers/test/common/markerService.test.ts b/src/vs/platform/markers/test/common/markerService.test.ts index 73ab130a90aa7..d8ebccf746fd2 100644 --- a/src/vs/platform/markers/test/common/markerService.test.ts +++ b/src/vs/platform/markers/test/common/markerService.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import * as markerService from 'vs/platform/markers/common/markerService'; @@ -21,9 +22,17 @@ function randomMarkerData(severity = MarkerSeverity.Error): IMarkerData { suite('Marker Service', () => { + let service: markerService.MarkerService; + + teardown(function () { + service.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + test('query', () => { - const service = new markerService.MarkerService(); + service = new markerService.MarkerService(); service.changeAll('far', [{ resource: URI.parse('file:///c/test/file.cs'), @@ -55,7 +64,7 @@ suite('Marker Service', () => { test('changeOne override', () => { - const service = new markerService.MarkerService(); + service = new markerService.MarkerService(); service.changeOne('far', URI.parse('file:///path/only.cs'), [randomMarkerData()]); assert.strictEqual(service.read().length, 1); assert.strictEqual(service.read({ owner: 'far' }).length, 1); @@ -73,7 +82,7 @@ suite('Marker Service', () => { test('changeOne/All clears', () => { - const service = new markerService.MarkerService(); + service = new markerService.MarkerService(); service.changeOne('far', URI.parse('file:///path/only.cs'), [randomMarkerData()]); service.changeOne('boo', URI.parse('file:///path/only.cs'), [randomMarkerData()]); assert.strictEqual(service.read({ owner: 'far' }).length, 1); @@ -93,7 +102,7 @@ suite('Marker Service', () => { test('changeAll sends event for cleared', () => { - const service = new markerService.MarkerService(); + service = new markerService.MarkerService(); service.changeAll('far', [{ resource: URI.parse('file:///d/path'), marker: randomMarkerData() @@ -104,17 +113,19 @@ suite('Marker Service', () => { assert.strictEqual(service.read({ owner: 'far' }).length, 2); - service.onMarkerChanged(changedResources => { + const d = service.onMarkerChanged(changedResources => { assert.strictEqual(changedResources.length, 1); changedResources.forEach(u => assert.strictEqual(u.toString(), 'file:///d/path')); assert.strictEqual(service.read({ owner: 'far' }).length, 0); }); service.changeAll('far', []); + + d.dispose(); }); test('changeAll merges', () => { - const service = new markerService.MarkerService(); + service = new markerService.MarkerService(); service.changeAll('far', [{ resource: URI.parse('file:///c/test/file.cs'), @@ -128,7 +139,7 @@ suite('Marker Service', () => { }); test('changeAll must not break integrety, issue #12635', () => { - const service = new markerService.MarkerService(); + service = new markerService.MarkerService(); service.changeAll('far', [{ resource: URI.parse('scheme:path1'), @@ -158,7 +169,7 @@ suite('Marker Service', () => { test('invalid marker data', () => { const data = randomMarkerData(); - const service = new markerService.MarkerService(); + service = new markerService.MarkerService(); data.message = undefined!; service.changeOne('far', URI.parse('some:uri/path'), [data]); @@ -174,7 +185,7 @@ suite('Marker Service', () => { }); test('MapMap#remove returns bad values, https://github.com/microsoft/vscode/issues/13548', () => { - const service = new markerService.MarkerService(); + service = new markerService.MarkerService(); service.changeOne('o', URI.parse('some:uri/1'), [randomMarkerData()]); service.changeOne('o', URI.parse('some:uri/2'), []); @@ -192,7 +203,7 @@ suite('Marker Service', () => { severity: 0 as MarkerSeverity, source: 'me' }; - const service = new markerService.MarkerService(); + service = new markerService.MarkerService(); service.changeOne('far', URI.parse('some:thing'), [data]); const marker = service.read({ resource: URI.parse('some:thing') }); diff --git a/src/vs/platform/progress/test/common/progress.test.ts b/src/vs/platform/progress/test/common/progress.test.ts index 638f7d595248b..85bce306781a1 100644 --- a/src/vs/platform/progress/test/common/progress.test.ts +++ b/src/vs/platform/progress/test/common/progress.test.ts @@ -5,9 +5,13 @@ import * as assert from 'assert'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { AsyncProgress } from 'vs/platform/progress/common/progress'; suite('Progress', () => { + + ensureNoDisposablesAreLeakedInTestSuite(); + test('multiple report calls are processed in sequence', async () => { await runWithFakedTimers({ useFakeTimers: true, maxTaskCount: 100 }, async () => { const executionOrder: string[] = []; diff --git a/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts b/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts index 3a60e4c025ba3..2e4e2560bfc1c 100644 --- a/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts +++ b/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts @@ -34,7 +34,6 @@ import { mock } from 'vs/base/test/common/mock'; import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; import { URITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; @@ -62,7 +61,7 @@ import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ensureFileSystemProviderError } from 'vs/platform/files/common/files'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; function assertRejects(fn: () => Promise, message: string = 'Expected rejection') { return fn().then(() => assert.ok(false, message), _err => assert.ok(true)); @@ -77,6 +76,7 @@ suite('ExtHostLanguageFeatureCommands', function () { const defaultSelector = { scheme: 'far' }; let model: ITextModel; + let insta: TestInstantiationService; let rpcProtocol: TestRPCProtocol; let extHost: ExtHostLanguageFeatures; let mainThread: MainThreadLanguageFeatures; @@ -153,7 +153,7 @@ suite('ExtHostLanguageFeatureCommands', function () { services.set(IOutlineModelService, new SyncDescriptor(OutlineModelService)); services.set(IConfigurationService, new TestConfigurationService()); - const insta = new InstantiationService(services); + insta = new TestInstantiationService(services); const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ @@ -197,6 +197,9 @@ suite('ExtHostLanguageFeatureCommands', function () { setUnexpectedErrorHandler(originalErrorHandler); model.dispose(); mainThread.dispose(); + + (insta.get(IOutlineModelService)).dispose(); + insta.dispose(); }); teardown(() => { @@ -204,7 +207,7 @@ suite('ExtHostLanguageFeatureCommands', function () { return rpcProtocol.sync(); }); - ensureFileSystemProviderError(); + // ensureNoDisposablesAreLeakedInTestSuite(); // --- workspace symbols diff --git a/src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts b/src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts index f1f18d641e85d..f17a6c5b0e96c 100644 --- a/src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts +++ b/src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts @@ -12,6 +12,7 @@ import { SingleProxyRPCProtocol, TestRPCProtocol } from 'vs/workbench/api/test/c import { NullLogService } from 'vs/platform/log/common/log'; import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('ExtHostBulkEdits.applyWorkspaceEdit', () => { @@ -43,6 +44,8 @@ suite('ExtHostBulkEdits.applyWorkspaceEdit', () => { bulkEdits = new ExtHostBulkEdits(rpcProtocol, documentsAndEditors); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('uses version id if document available', async () => { const edit = new extHostTypes.WorkspaceEdit(); edit.replace(resource, new extHostTypes.Range(0, 0, 0, 0), 'hello'); diff --git a/src/vs/workbench/api/test/browser/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/api/test/browser/extHostDocumentSaveParticipant.test.ts index c50aabb812f15..59c02e4d3b1d6 100644 --- a/src/vs/workbench/api/test/browser/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/api/test/browser/extHostDocumentSaveParticipant.test.ts @@ -14,8 +14,12 @@ import { SaveReason } from 'vs/workbench/common/editor'; import type * as vscode from 'vscode'; import { mock } from 'vs/base/test/common/mock'; import { NullLogService } from 'vs/platform/log/common/log'; -import { timeout } from 'vs/base/common/async'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; + +function timeout(n: number) { + return new Promise(resolve => setTimeout(resolve, n)); +} suite('ExtHostDocumentSaveParticipant', () => { @@ -39,6 +43,8 @@ suite('ExtHostDocumentSaveParticipant', () => { documents = new ExtHostDocuments(SingleProxyRPCProtocol(null), documentsAndEditors); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('no listeners, no problem', () => { const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadBulkEdits); return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => assert.ok(true)); diff --git a/src/vs/workbench/api/test/browser/extHostTypeConverter.test.ts b/src/vs/workbench/api/test/browser/extHostTypeConverter.test.ts index 07060c51e4d3b..727c4dea88099 100644 --- a/src/vs/workbench/api/test/browser/extHostTypeConverter.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTypeConverter.test.ts @@ -8,11 +8,14 @@ import * as assert from 'assert'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { MarkdownString, NotebookCellOutputItem, NotebookData, LanguageSelector, WorkspaceEdit } from 'vs/workbench/api/common/extHostTypeConverters'; import { isEmptyObject } from 'vs/base/common/types'; -import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log'; import { URI } from 'vs/base/common/uri'; import { IWorkspaceTextEditDto } from 'vs/workbench/api/common/extHost.protocol'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('ExtHostTypeConverter', function () { + + ensureNoDisposablesAreLeakedInTestSuite(); + function size(from: Record): number { let count = 0; for (const key in from) { diff --git a/src/vs/workbench/api/test/browser/mainThreadDocuments.test.ts b/src/vs/workbench/api/test/browser/mainThreadDocuments.test.ts index 69554639bd247..8331e51cc0381 100644 --- a/src/vs/workbench/api/test/browser/mainThreadDocuments.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadDocuments.test.ts @@ -8,6 +8,7 @@ import { BoundModelReferenceCollection } from 'vs/workbench/api/browser/mainThre import { timeout } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import { extUri } from 'vs/base/common/resources'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('BoundModelReferenceCollection', function () { @@ -21,6 +22,8 @@ suite('BoundModelReferenceCollection', function () { col.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('max age', async function () { let didDispose = false; diff --git a/src/vs/workbench/contrib/snippets/test/browser/snippetFile.test.ts b/src/vs/workbench/contrib/snippets/test/browser/snippetFile.test.ts index e9bb5b9853ce2..82e14ccc1a611 100644 --- a/src/vs/workbench/contrib/snippets/test/browser/snippetFile.test.ts +++ b/src/vs/workbench/contrib/snippets/test/browser/snippetFile.test.ts @@ -8,9 +8,12 @@ import { SnippetFile, Snippet, SnippetSource } from 'vs/workbench/contrib/snippe import { URI } from 'vs/base/common/uri'; import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser'; import { generateUuid } from 'vs/base/common/uuid'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Snippets', function () { + ensureNoDisposablesAreLeakedInTestSuite(); + class TestSnippetFile extends SnippetFile { constructor(filepath: URI, snippets: Snippet[]) { super(SnippetSource.Extension, filepath, undefined, undefined, undefined!, undefined!); diff --git a/src/vs/workbench/contrib/snippets/test/browser/snippetsRegistry.test.ts b/src/vs/workbench/contrib/snippets/test/browser/snippetsRegistry.test.ts index 8a9d39422e465..8c185e766a5c9 100644 --- a/src/vs/workbench/contrib/snippets/test/browser/snippetsRegistry.test.ts +++ b/src/vs/workbench/contrib/snippets/test/browser/snippetsRegistry.test.ts @@ -6,9 +6,12 @@ import * as assert from 'assert'; import { getNonWhitespacePrefix } from 'vs/workbench/contrib/snippets/browser/snippetsService'; import { Position } from 'vs/editor/common/core/position'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('getNonWhitespacePrefix', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + function assertGetNonWhitespacePrefix(line: string, column: number, expected: string): void { const model = { getLineContent: (lineNumber: number) => line diff --git a/src/vs/workbench/contrib/snippets/test/browser/snippetsRewrite.test.ts b/src/vs/workbench/contrib/snippets/test/browser/snippetsRewrite.test.ts index 54e7e2794f0da..5ac2018ef7dad 100644 --- a/src/vs/workbench/contrib/snippets/test/browser/snippetsRewrite.test.ts +++ b/src/vs/workbench/contrib/snippets/test/browser/snippetsRewrite.test.ts @@ -5,10 +5,13 @@ import * as assert from 'assert'; import { generateUuid } from 'vs/base/common/uuid'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile'; suite('SnippetRewrite', function () { + ensureNoDisposablesAreLeakedInTestSuite(); + function assertRewrite(input: string, expected: string | boolean): void { const actual = new Snippet(false, ['foo'], 'foo', 'foo', 'foo', input, 'foo', SnippetSource.User, generateUuid()); if (typeof expected === 'boolean') { From cd003c2df3a6bc75163b4ed50ade525342696475 Mon Sep 17 00:00:00 2001 From: Robo Date: Fri, 26 Jan 2024 22:24:02 +0900 Subject: [PATCH 319/333] fix: double account for DPR in mouse wheel classifier (#203513) --- .../base/browser/ui/scrollbar/scrollableElement.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 85253acf938f1..83e2d27eef7bd 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getZoomFactor } from 'vs/base/browser/browser'; +import { getZoomFactor, isChrome } from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import { createFastDomNode, FastDomNode } from 'vs/base/browser/fastDomNode'; import { IMouseEvent, IMouseWheelEvent, StandardWheelEvent } from 'vs/base/browser/mouseEvent'; @@ -87,12 +87,12 @@ export class MouseWheelClassifier { } public acceptStandardWheelEvent(e: StandardWheelEvent): void { - const targetWindow = dom.getWindow(e.browserEvent); - const osZoomFactor = targetWindow.devicePixelRatio / getZoomFactor(targetWindow); - if (platform.isWindows || platform.isLinux) { - // On Windows and Linux, the incoming delta events are multiplied with the OS zoom factor. + if (isChrome) { + const targetWindow = dom.getWindow(e.browserEvent); + const pageZoomFactor = getZoomFactor(targetWindow); + // On Chrome, the incoming delta events are multiplied with the OS zoom factor. // The OS zoom factor can be reverse engineered by using the device pixel ratio and the configured zoom factor into account. - this.accept(Date.now(), e.deltaX / osZoomFactor, e.deltaY / osZoomFactor); + this.accept(Date.now(), e.deltaX * pageZoomFactor, e.deltaY * pageZoomFactor); } else { this.accept(Date.now(), e.deltaX, e.deltaY); } From a10ecfaefdd72727ad0633b541f5eecbd6c5c71d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 26 Jan 2024 14:31:18 +0100 Subject: [PATCH 320/333] Git - fix issue related to a renamed resource (#203529) --- extensions/git/src/historyProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index 88dc4e0a668aa..82c43be52fb75 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -133,7 +133,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec historyItemChanges.push({ uri: historyItemUri, originalUri: toGitUri(change.originalUri, historyItemParentId), - modifiedUri: toGitUri(change.originalUri, historyItemId), + modifiedUri: toGitUri(change.uri, historyItemId), renameUri: change.renameUri, }); From 6eb3213066b6d07b65071859aec795200428ae70 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 26 Jan 2024 15:40:43 +0100 Subject: [PATCH 321/333] inline chat work (#203534) * debt - use hunks to render preview diff * add `InteractiveEditorRequest#previewDocument` to indicate for which document edits are previewed before being applied --- .../workbench/api/common/extHostInlineChat.ts | 1 + .../browser/inlineChatController.ts | 1 + .../browser/inlineChatSessionServiceImpl.ts | 4 +- .../browser/inlineChatStrategies.ts | 6 ++- .../inlineChat/browser/inlineChatWidget.ts | 40 +++++++-------- .../contrib/inlineChat/common/inlineChat.ts | 2 + .../test/browser/inlineChatController.test.ts | 51 ++++++++++++++++++- .../view/cellParts/chat/cellChatController.ts | 1 + .../vscode.proposed.interactive.d.ts | 1 + 9 files changed, 78 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/api/common/extHostInlineChat.ts b/src/vs/workbench/api/common/extHostInlineChat.ts index c6e4266df75be..47ec20ccea2be 100644 --- a/src/vs/workbench/api/common/extHostInlineChat.ts +++ b/src/vs/workbench/api/common/extHostInlineChat.ts @@ -151,6 +151,7 @@ export class ExtHostInteractiveEditor implements ExtHostInlineChatShape { wholeRange: typeConvert.Range.to(request.wholeRange), attempt: request.attempt, live: request.live, + previewDocument: this._documents.getDocument(URI.revive(request.previewDocument)), withIntentDetection: request.withIntentDetection, }; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index be5046ad1370a..d0ff5ed48e595 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -559,6 +559,7 @@ export class InlineChatController implements IEditorContribution { selection: this._editor.getSelection(), wholeRange: this._session.wholeRange.trackedInitialRange, live: this._session.editMode !== EditMode.Preview, // TODO@jrieken let extension know what document is used for previewing + previewDocument: this._session.textModelN.uri, withIntentDetection: options.withIntentDetection ?? true /* use intent detection by default */, }; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts index 6784574fc21e2..e711ffb0fbe65 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts @@ -110,7 +110,7 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { textModelN = store.add(this._modelService.createModel( createTextBufferFactoryFromSnapshot(textModel.createSnapshot()), { languageId: textModel.getLanguageId(), onDidChange: Event.None }, - targetUri.with({ scheme: Schemas.inMemory, query: new URLSearchParams({ id, 'inline-chat-textModelN': '' }).toString() }), true + targetUri.with({ scheme: Schemas.vscode, authority: 'inline-chat', path: '', query: new URLSearchParams({ id, 'textModelN': '' }).toString() }) )); } else { // AI edits happen in the actual model, keep a reference but make no copy @@ -122,7 +122,7 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { const textModel0 = store.add(this._modelService.createModel( createTextBufferFactoryFromSnapshot(textModel.createSnapshot()), { languageId: textModel.getLanguageId(), onDidChange: Event.None }, - targetUri.with({ scheme: Schemas.inMemory, query: new URLSearchParams({ id, 'inline-chat-textModel0': '' }).toString() }), true + targetUri.with({ scheme: Schemas.vscode, authority: 'inline-chat', path: '', query: new URLSearchParams({ id, 'textModel0': '' }).toString() }), true )); // untitled documents are special diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 4c347325e643b..fdc930567c18d 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -192,7 +192,9 @@ export class PreviewStrategy extends EditModeStrategy { } override async makeProgressiveChanges(edits: ISingleEditOperation[], obs: IEditObserver, opts: ProgressingEditsOptions): Promise { - return this._makeChanges(edits, obs, opts, undefined); + await this._makeChanges(edits, obs, opts, new Progress(() => { + this._zone.widget.showEditsPreview(this._session.hunkData, this._session.textModel0, this._session.textModelN); + })); } override async undoChanges(altVersionId: number): Promise { @@ -202,7 +204,7 @@ export class PreviewStrategy extends EditModeStrategy { override async renderChanges(response: ReplyResponse): Promise { if (response.allLocalEdits.length > 0) { - await this._zone.widget.showEditsPreview(this._session.textModel0, this._session.textModelN); + this._zone.widget.showEditsPreview(this._session.hunkData, this._session.textModel0, this._session.textModelN); } else { this._zone.widget.hideEditsPreview(); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index ae291d9d4625d..14a780fd04a53 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -41,7 +41,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; -import { ExpansionState } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { ExpansionState, HunkData } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IWorkbenchButtonBarOptions, MenuWorkbenchButtonBar } from 'vs/platform/actions/browser/buttonbar'; import { SlashCommandContentWidget } from 'vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget'; @@ -54,7 +54,6 @@ import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions import { MenuId } from 'vs/platform/actions/common/actions'; import { editorForeground, inputBackground, editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { Lazy } from 'vs/base/common/lazy'; -import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { ChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { ChatModel, ChatResponseModel } from 'vs/workbench/contrib/chat/common/chatModel'; import { ILogService } from 'vs/platform/log/common/log'; @@ -246,7 +245,6 @@ export class InlineChatWidget { @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IAccessibleViewService private readonly _accessibleViewService: IAccessibleViewService, - @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, @ILogService private readonly _logService: ILogService, @ITextModelService private readonly _textModelResolverService: ITextModelService, @IChatAgentService private readonly _chatAgentService: IChatAgentService, @@ -749,36 +747,32 @@ export class InlineChatWidget { // --- preview - async showEditsPreview(textModel0: ITextModel, textModelN: ITextModel) { + showEditsPreview(hunks: HunkData, textModel0: ITextModel, textModelN: ITextModel) { - this._elements.previewDiff.classList.remove('hidden'); - - const diff = await this._editorWorkerService.computeDiff(textModel0.uri, textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: 5000, computeMoves: false }, 'advanced'); - if (!diff || diff.changes.length === 0) { + if (hunks.size === 0) { this.hideEditsPreview(); return; } + this._elements.previewDiff.classList.remove('hidden'); + this._previewDiffEditor.value.setModel({ original: textModel0, modified: textModelN }); // joined ranges - 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); + let originalLineRange: LineRange | undefined; + let modifiedLineRange: LineRange | undefined; + for (const item of hunks.getInfo()) { + const [first0] = item.getRanges0(); + const [firstN] = item.getRangesN(); + + originalLineRange = !originalLineRange ? LineRange.fromRangeInclusive(first0) : originalLineRange.join(LineRange.fromRangeInclusive(first0)); + modifiedLineRange = !modifiedLineRange ? LineRange.fromRangeInclusive(firstN) : modifiedLineRange.join(LineRange.fromRangeInclusive(firstN)); } - // apply extra padding - const pad = 3; - const newStartLine = Math.max(1, originalLineRange.startLineNumber - pad); - modifiedLineRange = new LineRange(newStartLine, modifiedLineRange.endLineNumberExclusive); - originalLineRange = new LineRange(newStartLine, originalLineRange.endLineNumberExclusive); - - const newEndLineModified = Math.min(modifiedLineRange.endLineNumberExclusive + pad, textModelN.getLineCount()); - modifiedLineRange = new LineRange(modifiedLineRange.startLineNumber, newEndLineModified); - const newEndLineOriginal = Math.min(originalLineRange.endLineNumberExclusive + pad, textModel0.getLineCount()); - originalLineRange = new LineRange(originalLineRange.startLineNumber, newEndLineOriginal); + if (!originalLineRange || !modifiedLineRange) { + this.hideEditsPreview(); + return; + } const hiddenOriginal = invertLineRange(originalLineRange, textModel0); const hiddenModified = invertLineRange(modifiedLineRange, textModelN); diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index 1b5712c36e2cf..bf8c26d3a7d7d 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -21,6 +21,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { diffInserted, diffRemoved, editorHoverHighlight, editorWidgetBackground, editorWidgetBorder, focusBorder, inputBackground, inputPlaceholderForeground, registerColor, transparent, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { Extensions as ExtensionsMigration, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; import { IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; +import { URI } from 'vs/base/common/uri'; export interface IInlineChatSlashCommand { command: string; @@ -45,6 +46,7 @@ export interface IInlineChatRequest { attempt: number; requestId: string; live: boolean; + previewDocument: URI; withIntentDetection: boolean; } 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 8efc9619cc9d6..cb1260e7a0f51 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -36,12 +36,13 @@ import { IInlineChatSavingService } from '../../browser/inlineChatSavingService' import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { InlineChatSessionServiceImpl } from '../../browser/inlineChatSessionServiceImpl'; import { IInlineChatSessionService } from '../../browser/inlineChatSessionService'; -import { CTX_INLINE_CHAT_USER_DID_EDIT, EditMode, IInlineChatService, InlineChatConfigKeys, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { CTX_INLINE_CHAT_USER_DID_EDIT, EditMode, IInlineChatRequest, IInlineChatService, InlineChatConfigKeys, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { InlineChatServiceImpl } from 'vs/workbench/contrib/inlineChat/common/inlineChatServiceImpl'; import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { TestWorkerService } from './testWorkerService'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; +import { Schemas } from 'vs/base/common/network'; suite('InteractiveChatController', function () { class TestController extends InlineChatController { @@ -68,7 +69,7 @@ suite('InteractiveChatController', function () { setTimeout(() => { d.dispose(); - reject(`timeout, \nWANTED ${states.join('>')}, \nGOT ${actual.join('>')}`); + reject(new Error(`timeout, \nEXPECTED: ${states.join('>')}, \nACTUAL : ${actual.join('>')}`)); }, 1000); }); } @@ -105,6 +106,7 @@ suite('InteractiveChatController', function () { configurationService = new TestConfigurationService(); configurationService.setUserConfiguration('chat', { editor: { fontSize: 14, fontFamily: 'default' } }); + configurationService.setUserConfiguration('inlineChat', { mode: 'livePreview' }); configurationService.setUserConfiguration('editor', {}); const serviceCollection = new ServiceCollection( @@ -477,4 +479,49 @@ suite('InteractiveChatController', function () { }); + test('context has correct preview document', async function () { + + const requests: IInlineChatRequest[] = []; + + store.add(inlineChatService.addProvider({ + debugName: 'Unit Test', + label: 'Unit Test', + prepareInlineChatSession() { + return { + id: Math.random() + }; + }, + provideResponse(_session, request) { + requests.push(request); + return undefined; + } + })); + + async function makeRequest() { + const p = ctrl.waitFor(TestController.INIT_SEQUENCE_AUTO_SEND); + const r = ctrl.run({ message: 'Hello', autoSend: true }); + await p; + await ctrl.cancelSession(); + await r; + } + + // manual edits -> finish + ctrl = instaService.createInstance(TestController, editor); + + configurationService.setUserConfiguration('inlineChat', { mode: EditMode.Live }); + await makeRequest(); + + configurationService.setUserConfiguration('inlineChat', { mode: EditMode.LivePreview }); + await makeRequest(); + + configurationService.setUserConfiguration('inlineChat', { mode: EditMode.Preview }); + await makeRequest(); + + assert.strictEqual(requests.length, 3); + + assert.strictEqual(requests[0].previewDocument.toString(), model.uri.toString()); // live + assert.strictEqual(requests[1].previewDocument.toString(), model.uri.toString()); // live preview + assert.strictEqual(requests[2].previewDocument.scheme, Schemas.vscode); // preview + assert.strictEqual(requests[2].previewDocument.authority, 'inline-chat'); + }); }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts index d14a84bed36b4..829d4d0c4a5f8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/chat/cellChatController.ts @@ -307,6 +307,7 @@ export class NotebookCellChatController extends Disposable { selection: { selectionStartLineNumber: 1, selectionStartColumn: 1, positionLineNumber: 1, positionColumn: 1 }, wholeRange: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }, live: true, + previewDocument: editor.getModel().uri, withIntentDetection: true, // TODO: don't hard code but allow in corresponding UI to run without intent detection? }; diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index e233f52fa5c21..3b3e6e53c316b 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -33,6 +33,7 @@ declare module 'vscode' { wholeRange: Range; attempt: number; live: boolean; + previewDocument: TextDocument | undefined; withIntentDetection: boolean; } From fa558765b1cc3af3981fafc8e8d185ce3cace263 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 26 Jan 2024 16:19:08 +0100 Subject: [PATCH 322/333] Joh/spatial-pig (#203535) * more `ensureNoDisposablesAreLeakedInTestSuite` * adopt `ensureNoDisposablesAreLeakedInTestSuite` for api command tests --- .eslintrc.json | 3 - .../test/browser/referencesModel.test.ts | 3 + .../test/browser/extHostApiCommands.test.ts | 177 ++++++++++-------- .../api/test/browser/extHostWorkspace.test.ts | 3 + 4 files changed, 104 insertions(+), 82 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 4bb1fc53e36e4..5ce74c4cc1070 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -153,7 +153,6 @@ "src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts", "src/vs/editor/contrib/codeAction/test/browser/codeActionKeybindingResolver.test.ts", "src/vs/editor/contrib/codeAction/test/browser/codeActionModel.test.ts", - "src/vs/editor/contrib/gotoSymbol/test/browser/referencesModel.test.ts", "src/vs/editor/test/common/services/languageService.test.ts", "src/vs/editor/test/node/classification/typescript.test.ts", "src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts", @@ -172,9 +171,7 @@ "src/vs/platform/telemetry/test/browser/1dsAppender.test.ts", "src/vs/platform/workspace/test/common/workspace.test.ts", "src/vs/platform/workspaces/test/electron-main/workspaces.test.ts", - "src/vs/workbench/api/test/browser/extHostWorkspace.test.ts", "src/vs/workbench/api/test/browser/mainThreadConfiguration.test.ts", - "src/vs/workbench/api/test/browser/mainThreadDocuments.test.ts", "src/vs/workbench/api/test/common/extHostExtensionActivator.test.ts", "src/vs/workbench/api/test/node/extHostTunnelService.test.ts", "src/vs/workbench/contrib/bulkEdit/test/browser/bulkCellEdits.test.ts", diff --git a/src/vs/editor/contrib/gotoSymbol/test/browser/referencesModel.test.ts b/src/vs/editor/contrib/gotoSymbol/test/browser/referencesModel.test.ts index 55291bf4d1382..d1195449779ff 100644 --- a/src/vs/editor/contrib/gotoSymbol/test/browser/referencesModel.test.ts +++ b/src/vs/editor/contrib/gotoSymbol/test/browser/referencesModel.test.ts @@ -5,12 +5,15 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ReferencesModel } from 'vs/editor/contrib/gotoSymbol/browser/referencesModel'; suite('references', function () { + ensureNoDisposablesAreLeakedInTestSuite(); + test('nearestReference', () => { const model = new ReferencesModel([{ uri: URI.file('/out/obj/can'), diff --git a/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts b/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts index 2e4e2560bfc1c..25bcbd3f930dc 100644 --- a/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts +++ b/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts @@ -3,6 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/editor/contrib/codeAction/browser/codeAction'; +import 'vs/editor/contrib/codelens/browser/codelens'; +import 'vs/editor/contrib/colorPicker/browser/color'; +import 'vs/editor/contrib/format/browser/format'; +import 'vs/editor/contrib/gotoSymbol/browser/goToCommands'; +import 'vs/editor/contrib/documentSymbols/browser/documentSymbols'; +import 'vs/editor/contrib/hover/browser/getHover'; +import 'vs/editor/contrib/links/browser/getLinks'; +import 'vs/editor/contrib/parameterHints/browser/provideSignatureHelp'; +import 'vs/editor/contrib/smartSelect/browser/smartSelect'; +import 'vs/editor/contrib/suggest/browser/suggest'; +import 'vs/editor/contrib/rename/browser/rename'; +import 'vs/editor/contrib/inlayHints/browser/inlayHintsController'; + import * as assert from 'assert'; import { setUnexpectedErrorHandler, errorHandler } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; @@ -39,20 +53,6 @@ import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSyste import { URITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; import { IOutlineModelService, OutlineModelService } from 'vs/editor/contrib/documentSymbols/browser/outlineModel'; import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; - -import 'vs/editor/contrib/codeAction/browser/codeAction'; -import 'vs/editor/contrib/codelens/browser/codelens'; -import 'vs/editor/contrib/colorPicker/browser/color'; -import 'vs/editor/contrib/format/browser/format'; -import 'vs/editor/contrib/gotoSymbol/browser/goToCommands'; -import 'vs/editor/contrib/documentSymbols/browser/documentSymbols'; -import 'vs/editor/contrib/hover/browser/getHover'; -import 'vs/editor/contrib/links/browser/getLinks'; -import 'vs/editor/contrib/parameterHints/browser/provideSignatureHelp'; -import 'vs/editor/contrib/smartSelect/browser/smartSelect'; -import 'vs/editor/contrib/suggest/browser/suggest'; -import 'vs/editor/contrib/rename/browser/rename'; -import 'vs/editor/contrib/inlayHints/browser/inlayHintsController'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; import { assertType } from 'vs/base/common/types'; @@ -62,6 +62,9 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { timeout } from 'vs/base/common/async'; function assertRejects(fn: () => Promise, message: string = 'Expected rejection') { return fn().then(() => assert.ok(false, message), _err => assert.ok(true)); @@ -190,6 +193,9 @@ suite('ExtHostLanguageFeatureCommands', function () { mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, insta.createInstance(MainThreadLanguageFeatures, rpcProtocol)); + // forcefully create the outline service so that `ensureNoDisposablesAreLeakedInTestSuite` doesn't bark + insta.get(IOutlineModelService); + return rpcProtocol.sync(); }); @@ -207,10 +213,21 @@ suite('ExtHostLanguageFeatureCommands', function () { return rpcProtocol.sync(); }); - // ensureNoDisposablesAreLeakedInTestSuite(); + ensureNoDisposablesAreLeakedInTestSuite(); // --- workspace symbols + function testApiCmd(name: string, fn: () => Promise) { + test(name, async function () { + await runWithFakedTimers({}, async () => { + await fn(); + await timeout(10000); // API commands for things that allow commands dispose their result delay. This is to be nice + // because otherwise properties like command are disposed too early + }); + }); + + } + test('WorkspaceSymbols, invalid arguments', function () { const promises = [ assertRejects(() => commands.executeCommand('vscode.executeWorkspaceSymbolProvider')), @@ -767,7 +784,7 @@ suite('ExtHostLanguageFeatureCommands', function () { // --- suggest - test('triggerCharacter is null when completion provider is called programmatically #159914', async function () { + testApiCmd('triggerCharacter is null when completion provider is called programmatically #159914', async function () { let actualContext: vscode.CompletionContext | undefined; @@ -784,9 +801,11 @@ suite('ExtHostLanguageFeatureCommands', function () { assert.ok(actualContext); assert.deepStrictEqual(actualContext, { triggerKind: types.CompletionTriggerKind.Invoke, triggerCharacter: undefined }); + }); - test('Suggest, back and forth', function () { + testApiCmd('Suggest, back and forth', async function () { + disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(): any { const a = new types.CompletionItem('item1'); @@ -804,49 +823,45 @@ suite('ExtHostLanguageFeatureCommands', function () { } }, [])); - return rpcProtocol.sync().then(() => { - return commands.executeCommand('vscode.executeCompletionItemProvider', model.uri, new types.Position(0, 4)).then(list => { + await rpcProtocol.sync(); + + const list = await commands.executeCommand('vscode.executeCompletionItemProvider', model.uri, new types.Position(0, 4)); + assert.ok(list instanceof types.CompletionList); + const values = list.items; + assert.ok(Array.isArray(values)); + assert.strictEqual(values.length, 4); + const [first, second, third, fourth] = values; + assert.strictEqual(first.label, 'item1'); + assert.strictEqual(first.textEdit, undefined); // no text edit, default ranges + assert.ok(!types.Range.isRange(first.range)); + assert.strictEqual((first.documentation).value, 'hello_md_string'); + assert.strictEqual(second.label, 'item2'); + assert.strictEqual(second.textEdit!.newText, 'foo'); + assert.strictEqual(second.textEdit!.range.start.line, 0); + assert.strictEqual(second.textEdit!.range.start.character, 4); + assert.strictEqual(second.textEdit!.range.end.line, 0); + assert.strictEqual(second.textEdit!.range.end.character, 8); + assert.strictEqual(third.label, 'item3'); + assert.strictEqual(third.textEdit!.newText, 'foobar'); + assert.strictEqual(third.textEdit!.range.start.line, 0); + assert.strictEqual(third.textEdit!.range.start.character, 1); + assert.strictEqual(third.textEdit!.range.end.line, 0); + assert.strictEqual(third.textEdit!.range.end.character, 6); + assert.strictEqual(fourth.label, 'item4'); + assert.strictEqual(fourth.textEdit, undefined); + const range: any = fourth.range!; + assert.ok(types.Range.isRange(range)); + assert.strictEqual(range.start.line, 0); + assert.strictEqual(range.start.character, 1); + assert.strictEqual(range.end.line, 0); + assert.strictEqual(range.end.character, 4); + assert.ok(fourth.insertText instanceof types.SnippetString); + assert.strictEqual((fourth.insertText).value, 'foo$0bar'); - assert.ok(list instanceof types.CompletionList); - const values = list.items; - assert.ok(Array.isArray(values)); - assert.strictEqual(values.length, 4); - const [first, second, third, fourth] = values; - assert.strictEqual(first.label, 'item1'); - assert.strictEqual(first.textEdit, undefined);// no text edit, default ranges - assert.ok(!types.Range.isRange(first.range)); - assert.strictEqual((first.documentation).value, 'hello_md_string'); - - assert.strictEqual(second.label, 'item2'); - assert.strictEqual(second.textEdit!.newText, 'foo'); - assert.strictEqual(second.textEdit!.range.start.line, 0); - assert.strictEqual(second.textEdit!.range.start.character, 4); - assert.strictEqual(second.textEdit!.range.end.line, 0); - assert.strictEqual(second.textEdit!.range.end.character, 8); - - assert.strictEqual(third.label, 'item3'); - assert.strictEqual(third.textEdit!.newText, 'foobar'); - assert.strictEqual(third.textEdit!.range.start.line, 0); - assert.strictEqual(third.textEdit!.range.start.character, 1); - assert.strictEqual(third.textEdit!.range.end.line, 0); - assert.strictEqual(third.textEdit!.range.end.character, 6); - - assert.strictEqual(fourth.label, 'item4'); - assert.strictEqual(fourth.textEdit, undefined); - - const range: any = fourth.range!; - assert.ok(types.Range.isRange(range)); - assert.strictEqual(range.start.line, 0); - assert.strictEqual(range.start.character, 1); - assert.strictEqual(range.end.line, 0); - assert.strictEqual(range.end.character, 4); - assert.ok(fourth.insertText instanceof types.SnippetString); - assert.strictEqual((fourth.insertText).value, 'foo$0bar'); - }); - }); }); - test('Suggest, return CompletionList !array', function () { + testApiCmd('Suggest, return CompletionList !array', async function () { + disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(): any { const a = new types.CompletionItem('item1'); @@ -855,15 +870,16 @@ suite('ExtHostLanguageFeatureCommands', function () { } }, [])); - return rpcProtocol.sync().then(() => { - return commands.executeCommand('vscode.executeCompletionItemProvider', model.uri, new types.Position(0, 4)).then(list => { - assert.ok(list instanceof types.CompletionList); - assert.strictEqual(list.isIncomplete, true); - }); - }); + await rpcProtocol.sync(); + + const list = await commands.executeCommand('vscode.executeCompletionItemProvider', model.uri, new types.Position(0, 4)); + + assert.ok(list instanceof types.CompletionList); + assert.strictEqual(list.isIncomplete, true); }); - test('Suggest, resolve completion items', async function () { + testApiCmd('Suggest, resolve completion items', async function () { + let resolveCount = 0; @@ -896,7 +912,10 @@ suite('ExtHostLanguageFeatureCommands', function () { }); - test('"vscode.executeCompletionItemProvider" doesnot return a preselect field #53749', async function () { + testApiCmd('"vscode.executeCompletionItemProvider" doesnot return a preselect field #53749', async function () { + + + disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(): any { const a = new types.CompletionItem('item1'); @@ -928,7 +947,7 @@ suite('ExtHostLanguageFeatureCommands', function () { assert.strictEqual(d.preselect, undefined); }); - test('executeCompletionItemProvider doesn\'t capture commitCharacters #58228', async function () { + testApiCmd('executeCompletionItemProvider doesn\'t capture commitCharacters #58228', async function () { disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(): any { const a = new types.CompletionItem('item1'); @@ -955,7 +974,7 @@ suite('ExtHostLanguageFeatureCommands', function () { assert.strictEqual(b.commitCharacters, undefined); }); - test('vscode.executeCompletionItemProvider returns the wrong CompletionItemKinds in insiders #95715', async function () { + testApiCmd('vscode.executeCompletionItemProvider returns the wrong CompletionItemKinds in insiders #95715', async function () { disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(): any { return [ @@ -1013,7 +1032,7 @@ suite('ExtHostLanguageFeatureCommands', function () { // --- quickfix - test('QuickFix, back and forth', function () { + testApiCmd('QuickFix, back and forth', function () { disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, { provideCodeActions(): vscode.Command[] { return [{ command: 'testing', title: 'Title', arguments: [1, 2, true] }]; @@ -1031,7 +1050,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); }); - test('vscode.executeCodeActionProvider results seem to be missing their `command` property #45124', function () { + testApiCmd('vscode.executeCodeActionProvider results seem to be missing their `command` property #45124', function () { disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, { provideCodeActions(document, range): vscode.CodeAction[] { return [{ @@ -1060,7 +1079,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); }); - test('vscode.executeCodeActionProvider passes Range to provider although Selection is passed in #77997', function () { + testApiCmd('vscode.executeCodeActionProvider passes Range to provider although Selection is passed in #77997', function () { disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, { provideCodeActions(document, rangeOrSelection): vscode.CodeAction[] { return [{ @@ -1088,7 +1107,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); }); - test('vscode.executeCodeActionProvider results seem to be missing their `isPreferred` property #78098', function () { + testApiCmd('vscode.executeCodeActionProvider results seem to be missing their `isPreferred` property #78098', function () { disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, { provideCodeActions(document, rangeOrSelection): vscode.CodeAction[] { return [{ @@ -1115,7 +1134,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); }); - test('resolving code action', async function () { + testApiCmd('resolving code action', async function () { let didCallResolve = 0; class MyAction extends types.CodeAction { } @@ -1149,7 +1168,7 @@ suite('ExtHostLanguageFeatureCommands', function () { // --- code lens - test('CodeLens, back and forth', function () { + testApiCmd('CodeLens, back and forth', function () { const complexArg = { foo() { }, @@ -1177,7 +1196,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); }); - test('CodeLens, resolve', async function () { + testApiCmd('CodeLens, resolve', async function () { let resolveCount = 0; @@ -1211,7 +1230,7 @@ suite('ExtHostLanguageFeatureCommands', function () { assert.strictEqual(resolveCount, 0); }); - test('Links, back and forth', function () { + testApiCmd('Links, back and forth', function () { disposables.push(extHost.registerDocumentLinkProvider(nullExtensionDescription, defaultSelector, { provideDocumentLinks(): any { @@ -1233,7 +1252,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); }); - test('What\'s the condition for DocumentLink target to be undefined? #106308', async function () { + testApiCmd('What\'s the condition for DocumentLink target to be undefined? #106308', async function () { disposables.push(extHost.registerDocumentLinkProvider(nullExtensionDescription, defaultSelector, { provideDocumentLinks(): any { return [new types.DocumentLink(new types.Range(0, 0, 0, 20), undefined)]; @@ -1325,7 +1344,7 @@ suite('ExtHostLanguageFeatureCommands', function () { // --- inline hints - test('Inlay Hints, back and forth', async function () { + testApiCmd('Inlay Hints, back and forth', async function () { disposables.push(extHost.registerInlayHintsProvider(nullExtensionDescription, defaultSelector, { provideInlayHints() { return [new types.InlayHint(new types.Position(0, 1), 'Foo')]; @@ -1343,7 +1362,7 @@ suite('ExtHostLanguageFeatureCommands', function () { assert.strictEqual(first.position.character, 1); }); - test('Inline Hints, merge', async function () { + testApiCmd('Inline Hints, merge', async function () { disposables.push(extHost.registerInlayHintsProvider(nullExtensionDescription, defaultSelector, { provideInlayHints() { const part = new types.InlayHintLabelPart('Bar'); @@ -1391,7 +1410,7 @@ suite('ExtHostLanguageFeatureCommands', function () { assert.strictEqual(label.command?.title, 'part'); }); - test('Inline Hints, bad provider', async function () { + testApiCmd('Inline Hints, bad provider', async function () { disposables.push(extHost.registerInlayHintsProvider(nullExtensionDescription, defaultSelector, { provideInlayHints() { return [new types.InlayHint(new types.Position(0, 1), 'Foo')]; diff --git a/src/vs/workbench/api/test/browser/extHostWorkspace.test.ts b/src/vs/workbench/api/test/browser/extHostWorkspace.test.ts index 9d980e145028e..0dc3b5ab6ed0e 100644 --- a/src/vs/workbench/api/test/browser/extHostWorkspace.test.ts +++ b/src/vs/workbench/api/test/browser/extHostWorkspace.test.ts @@ -25,6 +25,7 @@ import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSyste import { FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { nullExtensionDescription as extensionDescriptor } from 'vs/workbench/services/extensions/common/extensions'; import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; function createExtHostWorkspace(mainContext: IMainContext, data: IWorkspaceData, logService: ILogService): ExtHostWorkspace { const result = new ExtHostWorkspace( @@ -40,6 +41,8 @@ function createExtHostWorkspace(mainContext: IMainContext, data: IWorkspaceData, suite('ExtHostWorkspace', function () { + ensureNoDisposablesAreLeakedInTestSuite(); + function assertAsRelativePath(workspace: ExtHostWorkspace, input: string, expected: string, includeWorkspace?: boolean) { const actual = workspace.getRelativePath(input, includeWorkspace); assert.strictEqual(actual, expected); From 7b873a31170e4bda2faea577066d79c638d3be79 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 08:49:07 -0700 Subject: [PATCH 323/333] Bump actions/setup-python from 4 to 5 (#200546) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/deep-classifier-runner.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee204799cf6c6..097bdcf8c2cf6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: with: node-version-file: .nvmrc - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: "2.x" diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index 5ee7d048945b6..576bfa12fc39d 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -33,7 +33,7 @@ jobs: token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} - name: Set up Python 3.7 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.7 - name: Install dependencies From 01827d8f2009f738cf1427ba3a942f42b1c2f938 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 26 Jan 2024 17:13:18 +0100 Subject: [PATCH 324/333] aux window - restore `role: application` (#203540) --- .../services/auxiliaryWindow/browser/auxiliaryWindowService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts index 57c904416e0f9..6480665c278d6 100644 --- a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts +++ b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts @@ -417,6 +417,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili // Create workbench container and apply classes const container = document.createElement('div'); + container.setAttribute('role', 'application'); auxiliaryWindow.document.body.append(container); // Track attributes From d21ba4a403b27d370a36082aeccbc5a69125a87e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 26 Jan 2024 17:46:01 +0100 Subject: [PATCH 325/333] use `savedFrom` to participate in the right resource when doing save-as or when saving an untitled file (#203543) fixes https://github.com/microsoft/vscode-copilot/issues/3690 --- .../inlineChat/browser/inlineChatSavingServiceImpl.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts index 23f8f2cf917cc..a86426304a313 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl.ts @@ -105,13 +105,13 @@ export class InlineChatSavingServiceImpl implements IInlineChatSavingService { const queue = new Queue(); const d1 = this._textFileService.files.addSaveParticipant({ - participate: (model, context, progress, token) => { - return queue.queue(() => this._participate(model.textEditorModel?.uri, context.reason, progress, token)); + participate: (model, ctx, progress, token) => { + return queue.queue(() => this._participate(ctx.savedFrom ?? model.textEditorModel?.uri, ctx.reason, progress, token)); } }); const d2 = this._workingCopyFileService.addSaveParticipant({ - participate: (workingCopy, env, progress, token) => { - return queue.queue(() => this._participate(workingCopy.resource, env.reason, progress, token)); + participate: (workingCopy, ctx, progress, token) => { + return queue.queue(() => this._participate(ctx.savedFrom ?? workingCopy.resource, ctx.reason, progress, token)); } }); this._saveParticipant.value = combinedDisposable(d1, d2, queue); From 6be4d6cc529a5d5112d02d730808e861a3c5917f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 26 Jan 2024 18:03:09 +0100 Subject: [PATCH 326/333] Don't let actual decorations grow, but still grow the "ambient" background decoration (#203546) fixes https://github.com/microsoft/vscode-copilot/issues/3713 --- .../contrib/inlineChat/browser/inlineChatStrategies.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index fdc930567c18d..ea6a747f627cc 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -18,7 +18,7 @@ import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; -import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel, IValidEditOperation, OverviewRulerLane } from 'vs/editor/common/model'; +import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel, IValidEditOperation, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel'; @@ -419,6 +419,7 @@ export class LiveStrategy extends EditModeStrategy { private readonly _decoInsertedTextRange = ModelDecorationOptions.register({ description: 'inline-chat-inserted-range-linehighlight', className: 'inline-chat-inserted-range', + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, }); private readonly _previewZone: Lazy; From 1b58747581ed45da9838b6d231c098885d25ea48 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 26 Jan 2024 11:22:01 -0800 Subject: [PATCH 327/333] simplify the workspace trust status bar entry (#203557) --- .../browser/workspace.contribution.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index dbb1822d05386..45b9ee2b5a107 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -222,7 +222,7 @@ export class WorkspaceTrustRequestHandler extends Disposable implements IWorkben */ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchContribution { - private readonly entryId = `status.workspaceTrust.${this.workspaceContextService.getWorkspace().id}`; + private readonly entryId = `status.workspaceTrust`; private readonly statusbarEntryAccessor: MutableDisposable; @@ -253,7 +253,7 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon if (this.workspaceTrustEnablementService.isWorkspaceTrustEnabled()) { this.registerListeners(); - this.createStatusbarEntry(); + this.updateStatusbarEntry(this.workspaceTrustManagementService.isWorkspaceTrusted()); // Show modal dialog if (this.hostService.hasFocus) { @@ -555,12 +555,6 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon //#region Statusbar - private createStatusbarEntry(): void { - const entry = this.getStatusbarEntry(this.workspaceTrustManagementService.isWorkspaceTrusted()); - this.statusbarEntryAccessor.value = this.statusbarService.addEntry(entry, this.entryId, StatusbarAlignment.LEFT, 0.99 * Number.MAX_VALUE /* Right of remote indicator */); - this.statusbarService.updateEntryVisibility(this.entryId, false); - } - private getStatusbarEntry(trusted: boolean): IStatusbarEntry { const text = workspaceTrustToString(trusted); @@ -625,8 +619,15 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon } private updateStatusbarEntry(trusted: boolean): void { - this.statusbarEntryAccessor.value?.update(this.getStatusbarEntry(trusted)); - this.statusbarService.updateEntryVisibility(this.entryId, !trusted); + if (trusted && this.statusbarEntryAccessor.value) { + this.statusbarEntryAccessor.clear(); + return; + } + + if (!trusted && !this.statusbarEntryAccessor.value) { + const entry = this.getStatusbarEntry(trusted); + this.statusbarEntryAccessor.value = this.statusbarService.addEntry(entry, this.entryId, StatusbarAlignment.LEFT, 0.99 * Number.MAX_VALUE /* Right of remote indicator */); + } } //#endregion From 5233f3b81e5cd43c779bde9b454c2d3954234e26 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 26 Jan 2024 11:26:08 -0800 Subject: [PATCH 328/333] debug: bump js-debug (#203558) --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index 7f445fef7a297..b17eeda961047 100644 --- a/product.json +++ b/product.json @@ -50,8 +50,8 @@ }, { "name": "ms-vscode.js-debug", - "version": "1.86.0", - "sha256": "cdd3187384c953160c3d0ad1e8ebd869cc420930a9993f977f7d739029a02a54", + "version": "1.86.1", + "sha256": "e382de75b63a57d3419bbb110e17551d40d88b4bb0a49452dab2c7278b815e72", "repo": "https://github.com/microsoft/vscode-js-debug", "metadata": { "id": "25629058-ddac-4e17-abba-74678e126c5d", From 394659848a23b02c0bfd86abeb18aa70fced5b21 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 26 Jan 2024 16:39:40 -0300 Subject: [PATCH 329/333] Fix #203556 (#203561) --- src/vs/workbench/contrib/debug/browser/debugCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 30272d26dcca1..e10b4bbd72b7b 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -224,7 +224,7 @@ async function navigateCallStack(debugService: IDebugService, down: boolean) { } if (nextVisibleFrame) { - debugService.focusStackFrame(nextVisibleFrame); + debugService.focusStackFrame(nextVisibleFrame, undefined, undefined, { preserveFocus: false }); } } } From ac56e6eb5a536b661f50dd63fad38351dcf44438 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 26 Jan 2024 21:22:16 +0100 Subject: [PATCH 330/333] debt - reduce large `workbench.statusbar.hidden` state (#203564) --- src/vs/workbench/browser/parts/statusbar/statusbarModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts b/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts index 98c2fd7a37d8d..ae4ef6a97e4fe 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts @@ -48,7 +48,7 @@ export class StatusbarViewModel extends Disposable { if (hiddenRaw) { try { const hiddenArray: string[] = JSON.parse(hiddenRaw); - this.hidden = new Set(hiddenArray); + this.hidden = new Set(hiddenArray.filter(entry => !entry.startsWith('status.workspaceTrust.'))); // TODO@bpasero remove this migration eventually } catch (error) { // ignore parsing errors } From 9237441cd23f7d92af81106a0e34578f791f1423 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Fri, 26 Jan 2024 12:37:11 -0800 Subject: [PATCH 331/333] Add terminal as a startup editor (#203567) --- .../browser/gettingStarted.contribution.ts | 3 ++- .../contrib/welcomeGettingStarted/browser/startupPage.ts | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts index 3d0ae3e607db7..a804657f8fd3d 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts @@ -305,13 +305,14 @@ configurationRegistry.registerConfiguration({ 'workbench.startupEditor': { 'scope': ConfigurationScope.RESOURCE, 'type': 'string', - 'enum': ['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench'], + 'enum': ['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench', 'terminal'], 'enumDescriptions': [ localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.none' }, "Start without an editor."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePage' }, "Open the Welcome page, with content to aid in getting started with VS Code and extensions."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.readme' }, "Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise. Note: This is only observed as a global configuration, it will be ignored if set in a workspace or folder configuration."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.newUntitledFile' }, "Open a new untitled text file (only applies when opening an empty window)."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePageInEmptyWorkbench' }, "Open the Welcome page when opening an empty workbench."), + localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.terminal' }, "Open a new terminal in the editor area."), ], 'default': 'welcomePage', 'description': localize('workbench.startupEditor', "Controls which editor is shown at startup, if none are restored from the previous session.") diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/startupPage.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/startupPage.ts index e69262830cf40..da1a4f35e0fcc 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/startupPage.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/startupPage.ts @@ -28,6 +28,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { localize } from 'vs/nls'; import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; +import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; export const restoreWalkthroughsConfigurationKey = 'workbench.welcomePage.restorableWalkthroughs'; export type RestoreWalkthroughsConfigurationValue = { folder: string; category?: string; step?: string }; @@ -126,6 +127,8 @@ export class StartupPageContribution implements IWorkbenchContribution { await this.openReadme(); } else if (startupEditorSetting.value === 'welcomePage' || startupEditorSetting.value === 'welcomePageInEmptyWorkbench') { await this.openGettingStarted(); + } else if (startupEditorSetting.value === 'terminal') { + this.commandService.executeCommand(TerminalCommandId.CreateTerminalEditor); } } } @@ -213,5 +216,6 @@ function isStartupPageEnabled(configurationService: IConfigurationService, conte return startupEditor.value === 'welcomePage' || startupEditor.value === 'readme' && (startupEditor.userValue === 'readme' || startupEditor.defaultValue === 'readme') - || (contextService.getWorkbenchState() === WorkbenchState.EMPTY && startupEditor.value === 'welcomePageInEmptyWorkbench'); + || (contextService.getWorkbenchState() === WorkbenchState.EMPTY && startupEditor.value === 'welcomePageInEmptyWorkbench') + || startupEditor.value === 'terminal'; } From c0de87c441a6505c3bd038e9e89a43ab0424bb22 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Fri, 26 Jan 2024 13:07:46 -0800 Subject: [PATCH 332/333] Complete walkthrough steps with commands after command execution (#203573) --- .../contrib/welcomeGettingStarted/browser/gettingStarted.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts index 85a3eebf4c219..8f8e501e67754 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts @@ -718,7 +718,7 @@ export class GettingStartedPage extends EditorPane { stepElement.classList.add('expanded'); stepElement.setAttribute('aria-expanded', 'true'); this.buildMediaComponent(id); - this.gettingStartedService.progressStep(id); + this.gettingStartedService.progressByEvent('stepSelected:' + id); } else { this.editorInput.selectedStep = undefined; } From fe0632cbb2d6d3517676748a9601d3cb3dc36b01 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 26 Jan 2024 23:28:45 +0100 Subject: [PATCH 333/333] Git - handle stashes that contain untracked files (#203572) --- extensions/git/src/commands.ts | 39 ++++++++++++++++++++++++-------- extensions/git/src/git.ts | 11 ++++++--- extensions/git/src/operation.ts | 15 +++++++----- extensions/git/src/repository.ts | 6 ++++- 4 files changed, 51 insertions(+), 20 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 33bd2ce23939c..5ff4fc4f4a659 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -3709,25 +3709,44 @@ export class CommandCenter { } const stashChanges = await repository.showStash(stash.index); - const stashParentCommit = stash.parents.length > 0 ? stash.parents[0] : `${stash.hash}^`; - if (!stashChanges || stashChanges.length === 0) { return; } + // A stash commit can have up to 3 parents: + // 1. The first parent is the commit that was HEAD when the stash was created. + // 2. The second parent is the commit that represents the index when the stash was created. + // 3. The third parent (when present) represents the untracked files when the stash was created. + const stashFirstParentCommit = stash.parents.length > 0 ? stash.parents[0] : `${stash.hash}^`; + const stashUntrackedFilesParentCommit = stash.parents.length === 3 ? stash.parents[2] : undefined; + const stashUntrackedFiles: string[] = []; + + if (stashUntrackedFilesParentCommit) { + const untrackedFiles = await repository.getObjectFiles(stashUntrackedFilesParentCommit); + stashUntrackedFiles.push(...untrackedFiles.map(f => path.join(repository.root, f.file))); + } + const title = `Git Stash #${stash.index}: ${stash.description}`; const multiDiffSourceUri = toGitUri(Uri.file(repository.root), `stash@{${stash.index}}`, { scheme: 'git-stash' }); const resources: { originalUri: Uri | undefined; modifiedUri: Uri | undefined }[] = []; for (const change of stashChanges) { - if (change.status === Status.INDEX_ADDED) { - resources.push({ originalUri: undefined, modifiedUri: toGitUri(change.uri, stash.hash) }); - } else if (change.status === Status.DELETED) { - resources.push({ originalUri: toGitUri(change.uri, stashParentCommit), modifiedUri: undefined }); - } else if (change.status === Status.INDEX_RENAMED) { - resources.push({ originalUri: toGitUri(change.originalUri, stashParentCommit), modifiedUri: toGitUri(change.uri, stash.hash) }); - } else { - resources.push({ originalUri: toGitUri(change.uri, stashParentCommit), modifiedUri: toGitUri(change.uri, stash.hash) }); + const isChangeUntracked = !!stashUntrackedFiles.find(f => pathEquals(f, change.uri.fsPath)); + const modifiedUriRef = !isChangeUntracked ? stash.hash : stashUntrackedFilesParentCommit ?? stash.hash; + + switch (change.status) { + case Status.INDEX_ADDED: + resources.push({ originalUri: undefined, modifiedUri: toGitUri(change.uri, modifiedUriRef) }); + break; + case Status.DELETED: + resources.push({ originalUri: toGitUri(change.uri, stashFirstParentCommit), modifiedUri: undefined }); + break; + case Status.INDEX_RENAMED: + resources.push({ originalUri: toGitUri(change.originalUri, stashFirstParentCommit), modifiedUri: toGitUri(change.uri, modifiedUriRef) }); + break; + default: + resources.push({ originalUri: toGitUri(change.uri, stashFirstParentCommit), modifiedUri: toGitUri(change.uri, modifiedUriRef) }); + break; } } diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 119fce2815d8a..785a9cd226797 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -937,7 +937,7 @@ function parseGitDiffShortStat(data: string): CommitShortStat { return { files: parseInt(files), insertions: parseInt(insertions ?? '0'), deletions: parseInt(deletions ?? '0') }; } -interface LsTreeElement { +export interface LsTreeElement { mode: string; type: string; object: string; @@ -1294,8 +1294,13 @@ export class Repository { return { mode, object, size: parseInt(size) }; } - async lstree(treeish: string, path: string): Promise { - const { stdout } = await this.exec(['ls-tree', '-l', treeish, '--', sanitizePath(path)]); + async lstree(treeish: string, path?: string): Promise { + const args = ['ls-tree', '-l', treeish]; + if (path) { + args.push('--', sanitizePath(path)); + } + + const { stdout } = await this.exec(args); return parseLsTree(stdout); } diff --git a/extensions/git/src/operation.ts b/extensions/git/src/operation.ts index 15e494f5fab65..ead345c0b0621 100644 --- a/extensions/git/src/operation.ts +++ b/extensions/git/src/operation.ts @@ -28,6 +28,7 @@ export const enum OperationKind { GetBranches = 'GetBranches', GetCommitTemplate = 'GetCommitTemplate', GetObjectDetails = 'GetObjectDetails', + GetObjectFiles = 'GetObjectFiles', GetRefs = 'GetRefs', GetRemoteRefs = 'GetRemoteRefs', HashObject = 'HashObject', @@ -65,12 +66,12 @@ export const enum OperationKind { export type Operation = AddOperation | ApplyOperation | BlameOperation | BranchOperation | CheckIgnoreOperation | CherryPickOperation | CheckoutOperation | CheckoutTrackingOperation | CleanOperation | CommitOperation | ConfigOperation | DeleteBranchOperation | DeleteRefOperation | DeleteRemoteTagOperation | DeleteTagOperation | DiffOperation | FetchOperation | FindTrackingBranchesOperation | - GetBranchOperation | GetBranchesOperation | GetCommitTemplateOperation | GetObjectDetailsOperation | GetRefsOperation | GetRemoteRefsOperation | - HashObjectOperation | IgnoreOperation | LogOperation | LogFileOperation | MergeOperation | MergeAbortOperation | MergeBaseOperation | - MoveOperation | PostCommitCommandOperation | PullOperation | PushOperation | RemoteOperation | RenameBranchOperation | RemoveOperation | - ResetOperation | RebaseOperation | RebaseAbortOperation | RebaseContinueOperation | RefreshOperation | RevertFilesOperation | RevListOperation | - RevParseOperation | SetBranchUpstreamOperation | ShowOperation | StageOperation | StatusOperation | StashOperation | SubmoduleUpdateOperation | - SyncOperation | TagOperation; + GetBranchOperation | GetBranchesOperation | GetCommitTemplateOperation | GetObjectDetailsOperation | GetObjectFilesOperation | GetRefsOperation | + GetRemoteRefsOperation | HashObjectOperation | IgnoreOperation | LogOperation | LogFileOperation | MergeOperation | MergeAbortOperation | + MergeBaseOperation | MoveOperation | PostCommitCommandOperation | PullOperation | PushOperation | RemoteOperation | RenameBranchOperation | + RemoveOperation | ResetOperation | RebaseOperation | RebaseAbortOperation | RebaseContinueOperation | RefreshOperation | RevertFilesOperation | + RevListOperation | RevParseOperation | SetBranchUpstreamOperation | ShowOperation | StageOperation | StatusOperation | StashOperation | + SubmoduleUpdateOperation | SyncOperation | TagOperation; type BaseOperation = { kind: OperationKind; blocking: boolean; readOnly: boolean; remote: boolean; retry: boolean; showProgress: boolean }; export type AddOperation = BaseOperation & { kind: OperationKind.Add }; @@ -95,6 +96,7 @@ export type GetBranchOperation = BaseOperation & { kind: OperationKind.GetBranch export type GetBranchesOperation = BaseOperation & { kind: OperationKind.GetBranches }; export type GetCommitTemplateOperation = BaseOperation & { kind: OperationKind.GetCommitTemplate }; export type GetObjectDetailsOperation = BaseOperation & { kind: OperationKind.GetObjectDetails }; +export type GetObjectFilesOperation = BaseOperation & { kind: OperationKind.GetObjectFiles }; export type GetRefsOperation = BaseOperation & { kind: OperationKind.GetRefs }; export type GetRemoteRefsOperation = BaseOperation & { kind: OperationKind.GetRemoteRefs }; export type HashObjectOperation = BaseOperation & { kind: OperationKind.HashObject }; @@ -151,6 +153,7 @@ export const Operation = { GetBranches: { kind: OperationKind.GetBranches, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as GetBranchesOperation, GetCommitTemplate: { kind: OperationKind.GetCommitTemplate, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as GetCommitTemplateOperation, GetObjectDetails: { kind: OperationKind.GetObjectDetails, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetObjectDetailsOperation, + GetObjectFiles: { kind: OperationKind.GetObjectFiles, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetObjectFilesOperation, GetRefs: { kind: OperationKind.GetRefs, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetRefsOperation, GetRemoteRefs: { kind: OperationKind.GetRemoteRefs, blocking: false, readOnly: true, remote: true, retry: false, showProgress: false } as GetRemoteRefsOperation, HashObject: { kind: OperationKind.HashObject, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as HashObjectOperation, diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 70e4e98c43a55..89bdc37f62474 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -11,7 +11,7 @@ import TelemetryReporter from '@vscode/extension-telemetry'; import { Branch, Change, ForcePushMode, GitErrorCodes, LogOptions, Ref, Remote, Status, CommitOptions, BranchQuery, FetchOptions, RefQuery, RefType } from './api/git'; import { AutoFetcher } from './autofetch'; import { debounce, memoize, throttle } from './decorators'; -import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions, PullOptions } from './git'; +import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions, PullOptions, LsTreeElement } from './git'; import { StatusBarCommands } from './statusbar'; import { toGitUri } from './uri'; import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util'; @@ -1955,6 +1955,10 @@ export class Repository implements Disposable { }); } + getObjectFiles(ref: string): Promise { + return this.run(Operation.GetObjectFiles, () => this.repository.lstree(ref)); + } + getObjectDetails(ref: string, filePath: string): Promise<{ mode: string; object: string; size: number }> { return this.run(Operation.GetObjectDetails, () => this.repository.getObjectDetails(ref, filePath)); }