diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 553df3f8f53..80f4c3bfe32 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -2526,7 +2526,7 @@ dependencies = [ [[package]] name = "tunnels" version = "0.1.0" -source = "git+https://github.com/microsoft/dev-tunnels?rev=4de1ff7979b5758c69218a3f45f6d9784b165072#4de1ff7979b5758c69218a3f45f6d9784b165072" +source = "git+https://github.com/microsoft/dev-tunnels?rev=8cae9b2a24c65c6c1958f5a0e77d72b23b5c6c30#8cae9b2a24c65c6c1958f5a0e77d72b23b5c6c30" dependencies = [ "async-trait", "chrono", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index f51f31e9fb5..db058cd9f7c 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -34,7 +34,7 @@ serde_bytes = "0.11.9" chrono = { version = "0.4.26", features = ["serde", "std", "clock"], default-features = false } gethostname = "0.4.3" libc = "0.2.144" -tunnels = { git = "https://github.com/microsoft/dev-tunnels", rev = "4de1ff7979b5758c69218a3f45f6d9784b165072", default-features = false, features = ["connections"] } +tunnels = { git = "https://github.com/microsoft/dev-tunnels", rev = "8cae9b2a24c65c6c1958f5a0e77d72b23b5c6c30", default-features = false, features = ["connections"] } keyring = { version = "2.0.3", default-features = false, features = ["linux-secret-service-rt-tokio-crypto-openssl"] } dialoguer = "0.10.4" hyper = { version = "0.14.26", features = ["server", "http1", "runtime"] } diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 6c9b1f20294..3d73bfb7656 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -304,11 +304,11 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid const session = await this.tokenToSession(token, scopes); this.afterSessionLoad(session); - if (sessions.some(s => s.id !== session.id)) { + if (sessions.some(s => s.account.id !== session.account.id)) { const otherAccountsIndexes = new Array(); const otherAccountsLabels = new Set(); for (let i = 0; i < sessions.length; i++) { - if (sessions[i].id !== session.id) { + if (sessions[i].account.id !== session.account.id) { otherAccountsIndexes.push(i); otherAccountsLabels.add(sessions[i].account.label); } diff --git a/extensions/yaml/package.json b/extensions/yaml/package.json index 96e02e37da6..5223f71c52b 100644 --- a/extensions/yaml/package.json +++ b/extensions/yaml/package.json @@ -37,10 +37,10 @@ "yaml" ], "extensions": [ + ".yaml", ".yml", ".eyaml", ".eyml", - ".yaml", ".cff", ".yaml-tmlanguage", ".yaml-tmpreferences", diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index 2bfd12c91cb..a3902157e53 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -21,7 +21,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { isIOS } from 'vs/base/common/platform'; +import { isIOS, isMacintosh } from 'vs/base/common/platform'; import Severity from 'vs/base/common/severity'; import { ThemeIcon } from 'vs/base/common/themables'; import 'vs/css!./media/quickInput'; @@ -826,20 +826,25 @@ export class QuickPick extends QuickInput implements I this.ui.inputBox.onDidChange(value => { this.doSetValue(value, true /* skip update since this originates from the UI */); })); + // Keybindings for the input box or list if there is no input box this.visibleDisposables.add((this._hideInput ? this.ui.list : this.ui.inputBox).onKeyDown((event: KeyboardEvent | StandardKeyboardEvent) => { switch (event.keyCode) { case KeyCode.DownArrow: - this.ui.list.focus(QuickInputListFocus.Next); + if (isMacintosh ? event.metaKey : event.ctrlKey) { + this.ui.list.focus(QuickInputListFocus.NextSeparator); + } else { + this.ui.list.focus(QuickInputListFocus.Next); + } if (this.canSelectMany) { this.ui.list.domFocus(); } dom.EventHelper.stop(event, true); break; case KeyCode.UpArrow: - if (this.ui.list.getFocusedElements().length) { - this.ui.list.focus(QuickInputListFocus.Previous); + if (isMacintosh ? event.metaKey : event.ctrlKey) { + this.ui.list.focus(QuickInputListFocus.PreviousSeparator); } else { - this.ui.list.focus(QuickInputListFocus.Last); + this.ui.list.focus(QuickInputListFocus.Previous); } if (this.canSelectMany) { this.ui.list.domFocus(); diff --git a/src/vs/platform/quickinput/browser/quickInputController.ts b/src/vs/platform/quickinput/browser/quickInputController.ts index 10529b5b903..67afe5550b3 100644 --- a/src/vs/platform/quickinput/browser/quickInputController.ts +++ b/src/vs/platform/quickinput/browser/quickInputController.ts @@ -219,6 +219,7 @@ export class QuickInputController extends Disposable { inputBox.setFocus(); })); // TODO: Turn into commands instead of handling KEY_DOWN + // Keybindings for the quickinput widget as a whole this._register(dom.addStandardDisposableListener(container, dom.EventType.KEY_DOWN, (event) => { if (dom.isAncestor(event.target, widget)) { return; // Ignore event if target is inside widget to allow the widget to handle the event. diff --git a/src/vs/platform/quickinput/browser/quickInputList.ts b/src/vs/platform/quickinput/browser/quickInputList.ts index 98993dd9070..f82c848d50e 100644 --- a/src/vs/platform/quickinput/browser/quickInputList.ts +++ b/src/vs/platform/quickinput/browser/quickInputList.ts @@ -430,7 +430,9 @@ export enum QuickInputListFocus { Next, Previous, NextPage, - PreviousPage + PreviousPage, + NextSeparator, + PreviousSeparator } export class QuickInputList { @@ -499,6 +501,7 @@ export class QuickInputList { } as IListOptions); this.list.getHTMLElement().id = id; this.disposables.push(this.list); + // Keybindings for the list itself this.disposables.push(this.list.onKeyDown(e => { const event = new StandardKeyboardEvent(e); switch (event.keyCode) { @@ -510,6 +513,7 @@ export class QuickInputList { this.list.setFocus(range(this.list.length)); } break; + // When we hit the top of the list, we fire the onLeave event. case KeyCode.UpArrow: { const focus1 = this.list.getFocus(); if (focus1.length === 1 && focus1[0] === 0) { @@ -517,6 +521,7 @@ export class QuickInputList { } break; } + // When we hit the bottom of the list, we fire the onLeave event. case KeyCode.DownArrow: { const focus2 = this.list.getFocus(); if (focus2.length === 1 && focus2[0] === this.list.length - 1) { @@ -810,33 +815,67 @@ export class QuickInputList { this.list.scrollTop = this.list.scrollHeight; this.list.focusLast(undefined, (e) => !!e.item); break; - case QuickInputListFocus.Next: { + case QuickInputListFocus.Next: this.list.focusNext(undefined, true, undefined, (e) => !!e.item); - const index = this.list.getFocus()[0]; - if (index !== 0 && !this.elements[index - 1].item && this.list.firstVisibleIndex > index - 1) { - this.list.reveal(index - 1); - } break; - } - case QuickInputListFocus.Previous: { + case QuickInputListFocus.Previous: this.list.focusPrevious(undefined, true, undefined, (e) => !!e.item); - const index = this.list.getFocus()[0]; - if (index !== 0 && !this.elements[index - 1].item && this.list.firstVisibleIndex > index - 1) { - this.list.reveal(index - 1); - } break; - } case QuickInputListFocus.NextPage: this.list.focusNextPage(undefined, (e) => !!e.item); break; case QuickInputListFocus.PreviousPage: this.list.focusPreviousPage(undefined, (e) => !!e.item); break; + case QuickInputListFocus.NextSeparator: { + let foundSeparatorAsItem = false; + this.list.focusNext(undefined, true, undefined, (e) => { + if (foundSeparatorAsItem) { + // This should be the index right after the separator so it + // is the item we want to focus. + return true; + } + if (e.separator) { + if (e.item) { + return true; + } else { + foundSeparatorAsItem = true; + } + } + return false; + }); + break; + } + case QuickInputListFocus.PreviousSeparator: { + let foundSeparatorAsItem = false; + this.list.focusPrevious(undefined, true, undefined, (e) => { + if (foundSeparatorAsItem) { + // This should be the index right before the separator so it + // is the item we want to focus. + return true; + } + if (e.separator) { + if (e.item) { + // This would be an inline-separator so we should + // focus this item. + return true; + } else { + foundSeparatorAsItem = true; + } + } + return false; + }); + break; + } } const focused = this.list.getFocus()[0]; if (typeof focused === 'number') { - this.list.reveal(focused); + if (focused !== 0 && !this.elements[focused - 1].item && this.list.firstVisibleIndex > focused - 1) { + this.list.reveal(focused - 1); + } else { + this.list.reveal(focused); + } } } diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 1e0dfb3581f..211eefedc36 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -569,7 +569,7 @@ export interface IShellLaunchConfig { * until `Terminal.show` is called. The typical usage for this is when you need to run * something that may need interactivity but only want to tell the user about it when * interaction is needed. Note that the terminals will still be exposed to all extensions - * as normal and they will remain hidden when the workspace is reloaded. + * as normal. The hidden terminals will not be restored when the workspace is next opened. */ hideFromUser?: boolean; diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 94641115abb..e5dfc1a814e 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -18,6 +18,7 @@ import { convertToVSCPaths, convertToDAPaths, isSessionAttach } from 'vs/workben import { ErrorNoTelemetry } from 'vs/base/common/errors'; import { IDebugVisualizerService } from 'vs/workbench/contrib/debug/common/debugVisualizers'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { Event } from 'vs/base/common/event'; @extHostNamedCustomer(MainContext.MainThreadDebugService) export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterFactory { @@ -88,28 +89,28 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb this._debugAdapterDescriptorFactories = new Map(); this._extHostKnownSessions = new Set(); - this._toDispose.add(this.debugService.getViewModel().onDidFocusThread(({ thread, explicit, session }) => { - if (session) { - const dto: IThreadFocusDto = { + const viewModel = this.debugService.getViewModel(); + this._toDispose.add(Event.any(viewModel.onDidFocusStackFrame, viewModel.onDidFocusThread)(() => { + const stackFrame = viewModel.focusedStackFrame; + const thread = viewModel.focusedThread; + if (stackFrame) { + this._proxy.$acceptStackFrameFocus({ + kind: 'stackFrame', + threadId: stackFrame.thread.threadId, + frameId: stackFrame.frameId, + sessionId: stackFrame.thread.session.getId(), + } satisfies IStackFrameFocusDto); + } else if (thread) { + this._proxy.$acceptStackFrameFocus({ kind: 'thread', - threadId: thread?.threadId, - sessionId: session.getId(), - }; - this._proxy.$acceptStackFrameFocus(dto); + threadId: thread.threadId, + sessionId: thread.session.getId(), + } satisfies IThreadFocusDto); + } else { + this._proxy.$acceptStackFrameFocus(undefined); } })); - this._toDispose.add(this.debugService.getViewModel().onDidFocusStackFrame(({ stackFrame, explicit, session }) => { - if (session) { - const dto: IStackFrameFocusDto = { - kind: 'stackFrame', - threadId: stackFrame?.thread.threadId, - frameId: stackFrame?.frameId, - sessionId: session.getId(), - }; - this._proxy.$acceptStackFrameFocus(dto); - } - })); this.sendBreakpointsAndListen(); } diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index 678825ac6a5..c7d0f3a6611 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -7,7 +7,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { combinedDisposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @@ -151,6 +151,16 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape this._proxy.$cellExecutionChanged(e.notebook, e.cellHandle, e.changed?.state); } })); + + this._disposables.add(this._notebookKernelService.onDidChangeSelectedNotebooks(e => { + for (const [handle, [kernel,]] of this._kernels) { + if (e.oldKernel === kernel.id) { + this._proxy.$acceptNotebookAssociation(handle, e.notebook, false); + } else if (e.newKernel === kernel.id) { + this._proxy.$acceptNotebookAssociation(handle, e.notebook, true); + } + } + })); } dispose(): void { @@ -262,16 +272,8 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape } }(data, this._languageService); - const listener = this._notebookKernelService.onDidChangeSelectedNotebooks(e => { - if (e.oldKernel === kernel.id) { - this._proxy.$acceptNotebookAssociation(handle, e.notebook, false); - } else if (e.newKernel === kernel.id) { - this._proxy.$acceptNotebookAssociation(handle, e.notebook, true); - } - }); - const registration = this._notebookKernelService.registerKernel(kernel); - this._kernels.set(handle, [kernel, combinedDisposable(listener, registration)]); + this._kernels.set(handle, [kernel, registration]); } $updateKernel(handle: number, data: Partial): void { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index aac2670692d..62a2dd3ffc7 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1241,8 +1241,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get breakpoints() { return extHostDebugService.breakpoints; }, - get stackFrameFocus() { - return extHostDebugService.stackFrameFocus; + get activeStackItem() { + if (!isProposedApiEnabled(extension, 'debugFocus')) { + return undefined; + } + return extHostDebugService.activeStackItem; }, registerDebugVisualizationProvider(id, provider) { checkProposedApiEnabled(extension, 'debugVisualization'); @@ -1267,9 +1270,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I onDidChangeBreakpoints(listener, thisArgs?, disposables?) { return _asExtensionEvent(extHostDebugService.onDidChangeBreakpoints)(listener, thisArgs, disposables); }, - onDidChangeStackFrameFocus(listener, thisArg?, disposables?) { + onDidChangeActiveStackItem(listener, thisArg?, disposables?) { checkProposedApiEnabled(extension, 'debugFocus'); - return _asExtensionEvent(extHostDebugService.onDidChangeStackFrameFocus)(listener, thisArg, disposables); + return _asExtensionEvent(extHostDebugService.onDidChangeActiveStackItem)(listener, thisArg, disposables); }, registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider, triggerKind?: vscode.DebugConfigurationProviderTriggerKind) { return extHostDebugService.registerDebugConfigurationProvider(debugType, provider, triggerKind || DebugConfigurationProviderTriggerKind.Initial); @@ -1673,8 +1676,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I InteractiveSessionVoteDirection: extHostTypes.InteractiveSessionVoteDirection, ChatCopyKind: extHostTypes.ChatCopyKind, InteractiveEditorResponseFeedbackKind: extHostTypes.InteractiveEditorResponseFeedbackKind, - StackFrameFocus: extHostTypes.StackFrameFocus, - ThreadFocus: extHostTypes.ThreadFocus, + StackFrame: extHostTypes.StackFrame, + Thread: extHostTypes.Thread, RelatedInformationType: extHostTypes.RelatedInformationType, SpeechToTextStatus: extHostTypes.SpeechToTextStatus, PartialAcceptTriggerKind: extHostTypes.PartialAcceptTriggerKind, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index fb881d05c4f..e002c997721 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -2342,14 +2342,14 @@ export type IDebugSessionDto = IDebugSessionFullDto | DebugSessionUUID; export interface IThreadFocusDto { kind: 'thread'; sessionId: string; - threadId: number | undefined; + threadId: number; } export interface IStackFrameFocusDto { kind: 'stackFrame'; sessionId: string; - threadId: number | undefined; - frameId: number | undefined; + threadId: number; + frameId: number; } diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 38d5f2a3205..e38d3ee11b1 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -15,7 +15,7 @@ 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, ThemeIcon } from 'vs/workbench/api/common/extHostTypes'; +import { Breakpoint, DataBreakpoint, DebugAdapterExecutable, DebugAdapterInlineImplementation, DebugAdapterNamedPipeServer, DebugAdapterServer, DebugConsoleMode, Disposable, FunctionBreakpoint, Location, Position, setBreakpointId, SourceBreakpoint, Thread, StackFrame, 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 { MainThreadDebugVisualization, IAdapterDescriptor, IConfig, IDebugAdapter, IDebugAdapterExecutable, IDebugAdapterNamedPipeServer, IDebugAdapterServer, IDebugVisualization, IDebugVisualizationContext, IDebuggerContribution, DebugVisualizationType, IDebugVisualizationTreeItem } from 'vs/workbench/contrib/debug/common/debug'; @@ -44,8 +44,8 @@ export interface IExtHostDebugService extends ExtHostDebugServiceShape { onDidReceiveDebugSessionCustomEvent: Event; onDidChangeBreakpoints: Event; breakpoints: vscode.Breakpoint[]; - onDidChangeStackFrameFocus: Event; - stackFrameFocus: vscode.ThreadFocus | vscode.StackFrameFocus | undefined; + onDidChangeActiveStackItem: Event; + activeStackItem: vscode.Thread | vscode.StackFrame | undefined; addBreakpoints(breakpoints0: readonly vscode.Breakpoint[]): Promise; removeBreakpoints(breakpoints0: readonly vscode.Breakpoint[]): Promise; @@ -97,8 +97,8 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E private readonly _onDidChangeBreakpoints: Emitter; - private _stackFrameFocus: vscode.ThreadFocus | vscode.StackFrameFocus | undefined; - private readonly _onDidChangeStackFrameFocus: Emitter; + private _activeStackItem: vscode.Thread | vscode.StackFrame | undefined; + private readonly _onDidChangeActiveStackItem: Emitter; private _debugAdapters: Map; private _debugAdaptersTrackers: Map; @@ -144,7 +144,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E this._onDidChangeBreakpoints = new Emitter(); - this._onDidChangeStackFrameFocus = new Emitter(); + this._onDidChangeActiveStackItem = new Emitter(); this._activeDebugConsole = new ExtHostDebugConsole(this._debugServiceProxy); @@ -278,12 +278,12 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E // extension debug API - get stackFrameFocus(): vscode.ThreadFocus | vscode.StackFrameFocus | undefined { - return this._stackFrameFocus; + get activeStackItem(): vscode.Thread | vscode.StackFrame | undefined { + return this._activeStackItem; } - get onDidChangeStackFrameFocus(): Event { - return this._onDidChangeStackFrameFocus.event; + get onDidChangeActiveStackItem(): Event { + return this._onDidChangeActiveStackItem.event; } get onDidChangeBreakpoints(): Event { @@ -768,21 +768,19 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E this.fireBreakpointChanges(a, r, c); } - public async $acceptStackFrameFocus(focusDto: IThreadFocusDto | IStackFrameFocusDto): Promise { - let focus: ThreadFocus | StackFrameFocus; - const session = focusDto.sessionId ? await this.getSession(focusDto.sessionId) : undefined; - if (!session) { - throw new Error('no DebugSession found for debug focus context'); + public async $acceptStackFrameFocus(focusDto: IThreadFocusDto | IStackFrameFocusDto | undefined): Promise { + let focus: vscode.Thread | vscode.StackFrame | undefined; + if (focusDto) { + const session = await this.getSession(focusDto.sessionId); + if (focusDto.kind === 'thread') { + focus = new Thread(session.api, focusDto.threadId); + } else { + focus = new StackFrame(session.api, focusDto.threadId, focusDto.frameId); + } } - if (focusDto.kind === 'thread') { - focus = new ThreadFocus(session.api, focusDto.threadId); - } else { - focus = new StackFrameFocus(session.api, focusDto.threadId, focusDto.frameId); - } - - this._stackFrameFocus = focus; - this._onDidChangeStackFrameFocus.fire(this._stackFrameFocus); + this._activeStackItem = focus; + this._onDidChangeActiveStackItem.fire(this._activeStackItem); } public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined, token: CancellationToken): Promise { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 09cce078494..82ae6b2d1fc 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3035,23 +3035,20 @@ export class DebugAdapterInlineImplementation implements vscode.DebugAdapterInli } -@es5ClassCompat -export class StackFrameFocus { +export class StackFrame implements vscode.StackFrame { constructor( public readonly session: vscode.DebugSession, - readonly threadId?: number, - readonly frameId?: number) { } + readonly threadId: number, + readonly frameId: number) { } } -@es5ClassCompat -export class ThreadFocus { +export class Thread implements vscode.Thread { constructor( public readonly session: vscode.DebugSession, - readonly threadId?: number) { } + readonly threadId: number) { } } - @es5ClassCompat export class EvaluatableExpression implements vscode.EvaluatableExpression { readonly range: vscode.Range; diff --git a/src/vs/workbench/browser/part.ts b/src/vs/workbench/browser/part.ts index 48278fe16b9..d1bb516e825 100644 --- a/src/vs/workbench/browser/part.ts +++ b/src/vs/workbench/browser/part.ts @@ -193,10 +193,7 @@ export abstract class MultiWindowParts extends Compo registerPart(part: T): IDisposable { this._parts.add(part); - return this._register(toDisposable(() => { - this.unregisterPart(part); - part = undefined!; // helps to avoid a memory leak with closures where part is captured - })); + return toDisposable(() => this.unregisterPart(part)); } protected unregisterPart(part: T): void { diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index acf60d208b5..dc58c035fed 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -328,7 +328,7 @@ class EditorStatus extends Disposable { private readonly metadataElement = this._register(new MutableDisposable()); private readonly currentMarkerStatus = this._register(this.instantiationService.createInstance(ShowCurrentMarkerInStatusbarContribution)); - private readonly tabFocusMode = this.instantiationService.createInstance(TabFocusMode); + private readonly tabFocusMode = this._register(this.instantiationService.createInstance(TabFocusMode)); private readonly state = new State(); private toRender: StateChange | undefined = undefined; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index 6f205b9d81f..84507d095dc 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -354,7 +354,7 @@ type NotificationActionMetricsClassification = { id: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The identifier of the action that was run from a notification.' }; actionLabel: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The label of the action that was run from a notification.' }; source: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The source of the notification where an action was run.' }; - silent: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the notification where an action was run is silent or not.' }; + silent: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Whether the notification where an action was run is silent or not.' }; owner: 'bpasero'; comment: 'Tracks when actions are fired from notifcations and how they were fired.'; }; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsTelemetry.ts b/src/vs/workbench/browser/parts/notifications/notificationsTelemetry.ts index 97d1d6a18c8..1a149ef7173 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsTelemetry.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsTelemetry.ts @@ -17,7 +17,7 @@ export interface NotificationMetrics { export type NotificationMetricsClassification = { id: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The identifier of the source of the notification.' }; - silent: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the notification is silent or not.' }; + silent: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Whether the notification is silent or not.' }; source?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The source of the notification.' }; owner: 'bpasero'; comment: 'Helps us gain insights to what notifications are being shown, how many, and if they are silent or not.'; diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index a3d09742994..1bfc3526c2e 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -189,7 +189,7 @@ export abstract class MenubarControl extends Disposable { this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e))); // Listen to update service - this.updateService.onStateChange(() => this.onUpdateStateChange()); + this._register(this.updateService.onStateChange(() => this.onUpdateStateChange())); // Listen for changes in recently opened menu this._register(this.workspacesService.onDidChangeRecentlyOpened(() => { this.onDidChangeRecentlyOpened(); })); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts index faa741438ed..945637c5dfb 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts @@ -15,6 +15,7 @@ import { AccessibilityStatus } from 'vs/workbench/contrib/accessibility/browser/ import { EditorAccessibilityHelpContribution } from 'vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp'; import { SaveAccessibilitySignalContribution } from 'vs/workbench/contrib/accessibility/browser/saveAccessibilitySignal'; import { CommentsAccessibilityHelpContribution } from 'vs/workbench/contrib/comments/browser/commentsAccessibility'; +import { DiffEditorActiveAnnouncementContribution } from 'vs/workbench/contrib/accessibility/browser/openDiffEditorAnnouncement'; registerAccessibilityConfiguration(); registerSingleton(IAccessibleViewService, AccessibleViewService, InstantiationType.Delayed); @@ -30,4 +31,5 @@ workbenchRegistry.registerWorkbenchContribution(InlineCompletionsAccessibleViewC registerWorkbenchContribution2(AccessibilityStatus.ID, AccessibilityStatus, WorkbenchPhase.BlockRestore); registerWorkbenchContribution2(SaveAccessibilitySignalContribution.ID, SaveAccessibilitySignalContribution, WorkbenchPhase.AfterRestored); +registerWorkbenchContribution2(DiffEditorActiveAnnouncementContribution.ID, DiffEditorActiveAnnouncementContribution, WorkbenchPhase.AfterRestored); registerWorkbenchContribution2(DynamicSpeechAccessibilityConfiguration.ID, DynamicSpeechAccessibilityConfiguration, WorkbenchPhase.AfterRestored); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index e3b3bb1ef7a..5151b2869d9 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -53,7 +53,8 @@ export const enum AccessibilityVerbositySettingId { Hover = 'accessibility.verbosity.hover', Notification = 'accessibility.verbosity.notification', EmptyEditorHint = 'accessibility.verbosity.emptyEditorHint', - Comments = 'accessibility.verbosity.comments' + Comments = 'accessibility.verbosity.comments', + DiffEditorActive = 'accessibility.verbosity.diffEditorActive' } export const enum AccessibleViewProviderId { @@ -170,6 +171,10 @@ const configuration: IConfigurationNode = { description: localize('verbosity.comments', 'Provide information about actions that can be taken in the comment widget or in a file which contains comments.'), ...baseVerbosityProperty }, + [AccessibilityVerbositySettingId.DiffEditorActive]: { + description: localize('verbosity.diffEditorActive', 'Indicate when a diff editor becomes the active editor.'), + ...baseVerbosityProperty + }, [AccessibilityAlertSettingId.Save]: { 'markdownDescription': localize('announcement.save', "Indicates when a file is saved. Also see {0}.", '`#audioCues.save#`'), 'enum': ['userGesture', 'always', 'never'], diff --git a/src/vs/workbench/contrib/accessibility/browser/openDiffEditorAnnouncement.ts b/src/vs/workbench/contrib/accessibility/browser/openDiffEditorAnnouncement.ts new file mode 100644 index 00000000000..ed3d0f75cb0 --- /dev/null +++ b/src/vs/workbench/contrib/accessibility/browser/openDiffEditorAnnouncement.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { isDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { localize } from 'vs/nls'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Event } from 'vs/base/common/event'; +import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; + +export class DiffEditorActiveAnnouncementContribution extends Disposable implements IWorkbenchContribution { + + static readonly ID = 'workbench.contrib.diffEditorActiveAnnouncement'; + + private _onDidActiveEditorChangeListener?: IDisposable; + + constructor( + @IEditorService private readonly _editorService: IEditorService, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, + @IConfigurationService private readonly _configurationService: IConfigurationService + ) { + super(); + this._register(Event.runAndSubscribe(_accessibilityService.onDidChangeScreenReaderOptimized, () => this._updateListener())); + this._register(_configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(AccessibilityVerbositySettingId.DiffEditorActive)) { + this._updateListener(); + } + })); + } + + private _updateListener(): void { + const announcementEnabled = this._configurationService.getValue(AccessibilityVerbositySettingId.DiffEditorActive); + const screenReaderOptimized = this._accessibilityService.isScreenReaderOptimized(); + + if (!announcementEnabled || !screenReaderOptimized) { + this._onDidActiveEditorChangeListener?.dispose(); + this._onDidActiveEditorChangeListener = undefined; + return; + } + + if (this._onDidActiveEditorChangeListener) { + return; + } + + this._onDidActiveEditorChangeListener = this._register(this._editorService.onDidActiveEditorChange(() => { + if (isDiffEditor(this._editorService.activeTextEditorControl)) { + this._accessibilityService.alert(localize('openDiffEditorAnnouncement', "Diff editor")); + } + })); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts b/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts index 9d0df786e2a..b4f52ae0635 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts @@ -88,7 +88,7 @@ const chatParticipantExtensionPoint = extensionsRegistry.ExtensionsRegistry.regi additionalProperties: false, type: 'object', defaultSnippets: [{ body: { name: '', description: '' } }], - required: ['name', 'description'], + required: ['name'], properties: { name: { description: localize('chatCommand', "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. The name should be unique among the commands provided by this participant."), diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 0bc244b4cb4..473f1286eef 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -344,7 +344,7 @@ class AgentCompletions extends Disposable { return null; } - const agents = this.chatAgentService.getRegisteredAgents() + const agents = this.chatAgentService.getAgents() .filter(a => !a.isDefault); return { suggestions: agents.map((c, i) => { @@ -427,7 +427,7 @@ class AgentCompletions extends Disposable { return null; } - const agents = this.chatAgentService.getRegisteredAgents(); + const agents = this.chatAgentService.getAgents(); const justAgents: CompletionItem[] = agents .filter(a => !a.isDefault) .map(agent => { @@ -452,7 +452,7 @@ class AgentCompletions extends Disposable { filterText: `${chatSubcommandLeader}${agent.id}${c.name}`, commitCharacters: [' '], insertText: `${agentLabel} ${withSlash} `, - detail: `(${agentLabel}) ${c.description}`, + detail: `(${agentLabel}) ${c.description ?? ''}`, range: new Range(1, 1, 1, 1), kind: CompletionItemKind.Text, // The icons are disabled here anyway sortText: `${chatSubcommandLeader}${agent.id}${c.name}`, diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index 4d179487783..46bcf3f7b47 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isNonEmptyArray } from 'vs/base/common/arrays'; +import { isNonEmptyArray, distinct } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; @@ -150,9 +150,10 @@ export interface IChatAgentService { registerDynamicAgent(data: IChatAgentData, agentImpl: IChatAgentImplementation): IDisposable; invokeAgent(id: string, request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise; getFollowups(id: string, request: IChatAgentRequest, result: IChatAgentResult, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise; + getAgents(): IChatAgentData[]; getRegisteredAgents(): Array; getActivatedAgents(): Array; - getRegisteredAgent(id: string): IChatAgentData | undefined; + getAgent(id: string): IChatAgentData | undefined; getDefaultAgent(): IChatAgent | undefined; getSecondaryAgent(): IChatAgentData | undefined; updateAgent(id: string, updateMetadata: IChatAgentMetadata): void; @@ -187,7 +188,7 @@ export class ChatAgentService extends Disposable implements IChatAgentService { throw new Error(`Already registered an agent with id ${name}`); } - const data = this.getRegisteredAgent(name); + const data = this.getAgent(name); if (!data) { throw new Error(`Unknown agent: ${name}`); } @@ -249,14 +250,28 @@ export class ChatAgentService extends Disposable implements IChatAgentService { } satisfies IChatAgentData)); } + /** + * Returns all agent datas that exist- static registered and dynamic ones. + */ + getAgents(): IChatAgentData[] { + const registeredAgents = this.getRegisteredAgents(); + const dynamicAgents = Array.from(this._agents.values()).map(a => a.data); + const all = [ + ...registeredAgents, + ...dynamicAgents + ]; + + return distinct(all, a => a.id); + } + getActivatedAgents(): IChatAgent[] { return Array.from(this._agents.values()) .filter(a => !!a.impl) .map(a => new MergedChatAgent(a.data, a.impl!)); } - getRegisteredAgent(id: string): IChatAgentData | undefined { - return this.getRegisteredAgents().find(a => a.id === id); + getAgent(id: string): IChatAgentData | undefined { + return this.getAgents().find(a => a.id === id); } async invokeAgent(id: string, request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise { diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index dd882365a67..85e54c5d1c7 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -693,7 +693,7 @@ export class ChatModel extends Disposable implements IChatModel { } else if (progress.kind === 'usedContext' || progress.kind === 'reference') { request.response.applyReference(progress); } else if (progress.kind === 'agentDetection') { - const agent = this.chatAgentService.getRegisteredAgent(progress.agentName); + const agent = this.chatAgentService.getAgent(progress.agentName); if (agent) { request.response.setAgent(agent, progress.command); } diff --git a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts index ca2057b6342..52c683f60a4 100644 --- a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts +++ b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts @@ -99,7 +99,7 @@ export class ChatRequestParser { const varRange = new OffsetRange(offset, offset + full.length); const varEditorRange = new Range(position.lineNumber, position.column, position.lineNumber, position.column + full.length); - const agent = this.agentService.getRegisteredAgent(name); + const agent = this.agentService.getAgent(name); if (!agent) { return; } diff --git a/src/vs/workbench/contrib/chat/test/common/chatRequestParser.test.ts b/src/vs/workbench/contrib/chat/test/common/chatRequestParser.test.ts index e82c0f84940..b1d4231aeb2 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatRequestParser.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatRequestParser.test.ts @@ -117,7 +117,7 @@ suite('ChatRequestParser', () => { test('agent with subcommand after text', async () => { const agentsService = mockObject()({}); - agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); + agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); instantiationService.stub(IChatAgentService, agentsService as any); parser = instantiationService.createInstance(ChatRequestParser); @@ -127,7 +127,7 @@ suite('ChatRequestParser', () => { test('agents, subCommand', async () => { const agentsService = mockObject()({}); - agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); + agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); instantiationService.stub(IChatAgentService, agentsService as any); parser = instantiationService.createInstance(ChatRequestParser); @@ -137,7 +137,7 @@ suite('ChatRequestParser', () => { test('agent with question mark', async () => { const agentsService = mockObject()({}); - agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); + agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); instantiationService.stub(IChatAgentService, agentsService as any); parser = instantiationService.createInstance(ChatRequestParser); @@ -147,7 +147,7 @@ suite('ChatRequestParser', () => { test('agent and subcommand with leading whitespace', async () => { const agentsService = mockObject()({}); - agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); + agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); instantiationService.stub(IChatAgentService, agentsService as any); parser = instantiationService.createInstance(ChatRequestParser); @@ -157,7 +157,7 @@ suite('ChatRequestParser', () => { test('agent and subcommand after newline', async () => { const agentsService = mockObject()({}); - agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); + agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); instantiationService.stub(IChatAgentService, agentsService as any); parser = instantiationService.createInstance(ChatRequestParser); @@ -167,7 +167,7 @@ suite('ChatRequestParser', () => { test('agent not first', async () => { const agentsService = mockObject()({}); - agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); + agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); instantiationService.stub(IChatAgentService, agentsService as any); parser = instantiationService.createInstance(ChatRequestParser); @@ -177,7 +177,7 @@ suite('ChatRequestParser', () => { test('agents and variables and multiline', async () => { const agentsService = mockObject()({}); - agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); + agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); instantiationService.stub(IChatAgentService, agentsService as any); varService.hasVariable.returns(true); @@ -189,7 +189,7 @@ suite('ChatRequestParser', () => { test('agents and variables and multiline, part2', async () => { const agentsService = mockObject()({}); - agentsService.getRegisteredAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); + agentsService.getAgent.returns(getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])); instantiationService.stub(IChatAgentService, agentsService as any); varService.hasVariable.returns(true); diff --git a/src/vs/workbench/contrib/chat/test/common/voiceChat.test.ts b/src/vs/workbench/contrib/chat/test/common/voiceChat.test.ts index 4ef86ff838d..4f2e01e418b 100644 --- a/src/vs/workbench/contrib/chat/test/common/voiceChat.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/voiceChat.test.ts @@ -53,7 +53,8 @@ suite('VoiceChat', () => { getFollowups(id: string, request: IChatAgentRequest, result: IChatAgentResult, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise { throw new Error(); } getRegisteredAgents(): Array { return agents; } getActivatedAgents(): IChatAgent[] { return agents; } - getRegisteredAgent(id: string): IChatAgent | undefined { throw new Error(); } + getAgents(): IChatAgent[] { return agents; } + getAgent(id: string): IChatAgent | undefined { throw new Error(); } getDefaultAgent(): IChatAgent | undefined { throw new Error(); } getSecondaryAgent(): IChatAgent | undefined { throw new Error(); } updateAgent(id: string, updateMetadata: IChatAgentMetadata): void { throw new Error(); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts index 6aeddc30e35..b47c9bc5642 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts @@ -110,11 +110,14 @@ function createScreenReaderHelp(): IDisposable { switchSides = localize('switchSidesNoKb', "Run the command Diff Editor: Switch Side, which is currently not triggerable via keybinding, to toggle between the original and modified editors."); } + const diffEditorActiveAnnouncement = localize('msg5', "The setting, accessibility.verbosity.diffEditorActive, controls if a diff editor announcement is made when it becomes the active editor."); + const keys = ['accessibility.signals.diffLineDeleted', 'accessibility.signals.diffLineInserted', 'accessibility.signals.diffLineModified']; const content = [ localize('msg1', "You are in a diff editor."), localize('msg2', "View the next ({0}) or previous ({1}) diff in diff review mode, which is optimized for screen readers.", next, previous), switchSides, + diffEditorActiveAnnouncement, localize('msg4', "To control which accessibility signals should be played, the following settings can be configured: {0}.", keys.join(', ')), ]; const commentCommandInfo = getCommentCommandInfo(keybindingService, contextKeyService, codeEditor); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts index faca219b69c..329ff39bf74 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts @@ -573,3 +573,43 @@ registerAction2(class extends NotebookAction { NotebookChatController.get(context.notebookEditor)?.focusAbove(); } }); + +registerAction2(class extends NotebookAction { + constructor() { + super( + { + id: 'notebook.cell.chat.previousFromHistory', + title: localize2('notebook.cell.chat.previousFromHistory', "Previous From History"), + precondition: ContextKeyExpr.and(CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_INLINE_CHAT_FOCUSED), + keybinding: { + when: ContextKeyExpr.and(CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_INLINE_CHAT_FOCUSED), + weight: KeybindingWeight.EditorCore + 10, + primary: KeyCode.UpArrow, + } + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext) { + NotebookChatController.get(context.notebookEditor)?.populateHistory(true); + } +}); + +registerAction2(class extends NotebookAction { + constructor() { + super( + { + id: 'notebook.cell.chat.nextFromHistory', + title: localize2('notebook.cell.chat.nextFromHistory', "Next From History"), + precondition: ContextKeyExpr.and(CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_INLINE_CHAT_FOCUSED), + keybinding: { + when: ContextKeyExpr.and(CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_INLINE_CHAT_FOCUSED), + weight: KeybindingWeight.EditorCore + 10, + primary: KeyCode.DownArrow + } + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext) { + NotebookChatController.get(context.notebookEditor)?.populateHistory(false); + } +}); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts index 66e1969d4f3..626f496569f 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts @@ -29,6 +29,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { AsyncProgress } from 'vs/platform/progress/common/progress'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { SaveReason } from 'vs/workbench/common/editor'; import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; import { IInlineChatSavingService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSavingService'; @@ -40,7 +41,7 @@ import { asProgressiveEdit, performAsyncTextEdit } from 'vs/workbench/contrib/in import { CTX_INLINE_CHAT_LAST_RESPONSE_TYPE, EditMode, IInlineChatProgressItem, IInlineChatRequest, InlineChatResponseFeedbackKind, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { insertCell, runDeleteAction } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations'; import { CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST, CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION, CTX_NOTEBOOK_CHAT_USER_DID_EDIT, MENU_CELL_CHAT_INPUT, MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_FEEDBACK, MENU_CELL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext'; -import { INotebookEditor, INotebookEditorContribution, INotebookViewZone, ScrollToRevealBehavior } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookEditor, INotebookEditorContribution, INotebookViewZone } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -96,21 +97,35 @@ class NotebookChatWidget extends Disposable implements INotebookViewZone { this._layoutWidget(inlineChatWidget, widgetContainer); } + hasFocus() { + return this.inlineChatWidget.hasFocus(); + } + focus() { + this.updateNotebookEditorFocusNSelections(); this.inlineChatWidget.focus(); } + updateNotebookEditorFocusNSelections() { + this._notebookEditor.focusContainer(true); + this._notebookEditor.setFocus({ start: this.afterModelPosition, end: this.afterModelPosition }); + this._notebookEditor.setSelections([{ + start: this.afterModelPosition, + end: this.afterModelPosition + }]); + } + getEditingCell() { return this._editingCell; } async getOrCreateEditingCell(): Promise<{ cell: CellViewModel; editor: IActiveCodeEditor } | undefined> { if (this._editingCell) { - await this._notebookEditor.focusNotebookCell(this._editingCell, 'editor'); - if (this._notebookEditor.activeCodeEditor?.hasModel()) { + const codeEditor = this._notebookEditor.codeEditors.find(ce => ce[0] === this._editingCell)?.[1]; + if (codeEditor?.hasModel()) { return { cell: this._editingCell, - editor: this._notebookEditor.activeCodeEditor + editor: codeEditor }; } else { return undefined; @@ -121,17 +136,25 @@ class NotebookChatWidget extends Disposable implements INotebookViewZone { return undefined; } + const widgetHasFocus = this.inlineChatWidget.hasFocus(); + this._editingCell = insertCell(this._languageService, this._notebookEditor, this.afterModelPosition, CellKind.Code, 'above'); if (!this._editingCell) { return undefined; } - await this._notebookEditor.focusNotebookCell(this._editingCell, 'editor', { revealBehavior: ScrollToRevealBehavior.firstLine }); - if (this._notebookEditor.activeCodeEditor?.hasModel()) { + await this._notebookEditor.revealFirstLineIfOutsideViewport(this._editingCell); + + if (widgetHasFocus) { + this.focus(); + } + + const codeEditor = this._notebookEditor.codeEditors.find(ce => ce[0] === this._editingCell)?.[1]; + if (codeEditor?.hasModel()) { return { cell: this._editingCell, - editor: this._notebookEditor.activeCodeEditor + editor: codeEditor }; } @@ -173,6 +196,15 @@ export class NotebookChatController extends Disposable implements INotebookEdito public static get(editor: INotebookEditor): NotebookChatController | null { return editor.getContribution(NotebookChatController.id); } + + // History + private static _storageKey = 'inline-chat-history'; + private static _promptHistory: string[] = []; + private _historyOffset: number = -1; + private _historyCandidate: string = ''; + private _historyUpdate: (prompt: string) => void; + + private _strategy: EditStrategy | undefined; private _sessionCtor: CancelablePromise | undefined; private _activeSession?: Session; @@ -198,6 +230,7 @@ export class NotebookChatController extends Disposable implements INotebookEdito @IModelService private readonly _modelService: IModelService, @ILanguageService private readonly _languageService: ILanguageService, @INotebookExecutionStateService private _executionStateService: INotebookExecutionStateService, + @IStorageService private readonly _storageService: IStorageService, ) { super(); @@ -208,6 +241,18 @@ export class NotebookChatController extends Disposable implements INotebookEdito this._ctxOuterFocusPosition = CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.bindTo(this._contextKeyService); this._registerFocusTracker(); + + NotebookChatController._promptHistory = JSON.parse(this._storageService.get(NotebookChatController._storageKey, StorageScope.PROFILE, '[]')); + this._historyUpdate = (prompt: string) => { + const idx = NotebookChatController._promptHistory.indexOf(prompt); + if (idx >= 0) { + NotebookChatController._promptHistory.splice(idx, 1); + } + NotebookChatController._promptHistory.unshift(prompt); + this._historyOffset = -1; + this._historyCandidate = ''; + this._storageService.store(NotebookChatController._storageKey, JSON.stringify(NotebookChatController._promptHistory), StorageScope.PROFILE, StorageTarget.USER); + }; } private _registerFocusTracker() { @@ -241,6 +286,9 @@ export class NotebookChatController extends Disposable implements INotebookEdito this._widget = undefined; this._widgetDisposableStore.clear(); + this._historyOffset = -1; + this._historyCandidate = ''; + scheduleAtNextAnimationFrame(window, () => { this._createWidget(index, input, autoSend); }); @@ -390,12 +438,7 @@ export class NotebookChatController extends Disposable implements INotebookEdito return; } - this._notebookEditor.focusContainer(true); - this._notebookEditor.setFocus({ start: this._widget.afterModelPosition, end: this._widget.afterModelPosition }); - this._notebookEditor.setSelections([{ - start: this._widget.afterModelPosition, - end: this._widget.afterModelPosition - }]); + this._widget.updateNotebookEditorFocusNSelections(); } async acceptInput() { @@ -408,6 +451,9 @@ export class NotebookChatController extends Disposable implements INotebookEdito assertType(this._activeSession.lastInput); const value = this._activeSession.lastInput.value; + + this._historyUpdate(value); + const editor = this._widget.parentEditor; const model = editor.getModel(); @@ -739,6 +785,10 @@ export class NotebookChatController extends Disposable implements INotebookEdito await this._notebookEditor.focusNotebookCell(cell, 'editor'); } + hasFocus() { + return this._widget?.hasFocus() ?? false; + } + focus() { this._focusWidget(); } @@ -760,6 +810,39 @@ export class NotebookChatController extends Disposable implements INotebookEdito } } + populateHistory(up: boolean) { + if (!this._widget) { + return; + } + + const len = NotebookChatController._promptHistory.length; + if (len === 0) { + return; + } + + if (this._historyOffset === -1) { + // remember the current value + this._historyCandidate = this._widget.inlineChatWidget.value; + } + + const newIdx = this._historyOffset + (up ? 1 : -1); + if (newIdx >= len) { + // reached the end + return; + } + + let entry: string; + if (newIdx < 0) { + entry = this._historyCandidate; + this._historyOffset = -1; + } else { + entry = NotebookChatController._promptHistory[newIdx]; + this._historyOffset = newIdx; + } + + this._widget.inlineChatWidget.value = entry; + this._widget.inlineChatWidget.selectAll(); + } async cancelCurrentRequest(discard: boolean) { if (discard) { @@ -769,6 +852,10 @@ export class NotebookChatController extends Disposable implements INotebookEdito this._activeRequestCts?.cancel(); } + getEditingCell() { + return this._widget?.getEditingCell(); + } + discard() { this._strategy?.cancel(); this._activeRequestCts?.cancel(); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts index 2ca62a92f6f..ce41420deba 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts @@ -20,6 +20,8 @@ import { IDebugService } from 'vs/workbench/contrib/debug/common/debug'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; import { CTX_INLINE_CHAT_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { insertCell } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations'; +import { CTX_NOTEBOOK_CELL_CHAT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext'; +import { NotebookChatController } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController'; import { CELL_TITLE_CELL_GROUP_ID, CellToolbarOrder, INotebookActionContext, INotebookCellActionContext, INotebookCellToolbarActionContext, INotebookCommandContext, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, NotebookAction, NotebookCellAction, NotebookMultiCellAction, cellExecutionArgs, executeNotebookCondition, getContextFromActiveEditor, getContextFromUri, parseMultiCellExecutionArgs } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, IFocusNotebookCellOptions, ScrollToRevealBehavior } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; @@ -198,7 +200,10 @@ registerAction2(class ExecuteCell extends NotebookMultiCellAction { precondition: executeThisCellCondition, title: localize('notebookActions.execute', "Execute Cell"), keybinding: { - when: NOTEBOOK_CELL_LIST_FOCUSED, + when: ContextKeyExpr.or( + NOTEBOOK_CELL_LIST_FOCUSED, + ContextKeyExpr.and(CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_INLINE_CHAT_FOCUSED) + ), primary: KeyMod.WinCtrl | KeyCode.Enter, win: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter @@ -229,6 +234,21 @@ registerAction2(class ExecuteCell extends NotebookMultiCellAction { await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true }); } + const chatController = NotebookChatController.get(context.notebookEditor); + const editingCell = chatController?.getEditingCell(); + if (chatController?.hasFocus() && editingCell) { + const group = editorGroupsService.activeGroup; + + if (group) { + if (group.activeEditor) { + group.pinEditor(group.activeEditor); + } + } + + await context.notebookEditor.executeNotebookCells([editingCell]); + return; + } + await runCell(editorGroupsService, context); } }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 4f6205e00cc..8c8d3a7f64b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -629,6 +629,11 @@ export interface INotebookEditor { */ revealInCenterIfOutsideViewport(cell: ICellViewModel): Promise; + /** + * Reveal the first line of the cell into the view if the cell is outside of the viewport. + */ + revealFirstLineIfOutsideViewport(cell: ICellViewModel): Promise; + /** * Reveal a line in notebook cell into viewport with minimal scrolling. */ diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index cdffe911b5d..8ce50f30af0 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2122,8 +2122,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD await this._list.revealCell(cell, CellRevealType.CenterIfOutsideViewport); } - revealFirstLineIfOutsideViewport(cell: ICellViewModel) { - this._list.revealCell(cell, CellRevealType.FirstLineIfOutsideViewport); + async revealFirstLineIfOutsideViewport(cell: ICellViewModel) { + await this._list.revealCell(cell, CellRevealType.FirstLineIfOutsideViewport); } async revealLineInViewAsync(cell: ICellViewModel, line: number): Promise { @@ -2446,7 +2446,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._cursorNavMode.set(true); await this.revealInView(cell); } else if (options?.revealBehavior === ScrollToRevealBehavior.firstLine) { - this.revealFirstLineIfOutsideViewport(cell); + await this.revealFirstLineIfOutsideViewport(cell); } else if (options?.revealBehavior === ScrollToRevealBehavior.fullCell) { await this.revealInView(cell); } else { diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index bd47eb879e9..377857dd4ce 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -925,8 +925,12 @@ export class NotebookCellList extends WorkbenchList implements ID break; } - // wait for the editor to be created only if the cell is in editing mode (meaning it has an editor and will focus the editor) - if (cell.getEditState() === CellEditState.Editing && !cell.editorAttached) { + if (( + // wait for the editor to be created if the cell is in editing mode + cell.getEditState() === CellEditState.Editing + // wait for the editor to be created if we are revealing the first line of the cell + || revealType === CellRevealType.FirstLineIfOutsideViewport + ) && !cell.editorAttached) { return getEditorAttachedPromise(cell); } diff --git a/src/vs/workbench/contrib/speech/browser/speechService.ts b/src/vs/workbench/contrib/speech/browser/speechService.ts index fb511647f02..36186b1458e 100644 --- a/src/vs/workbench/contrib/speech/browser/speechService.ts +++ b/src/vs/workbench/contrib/speech/browser/speechService.ts @@ -99,19 +99,19 @@ export class SpeechService extends Disposable implements ISpeechService { type SpeechToTextSessionClassification = { owner: 'bpasero'; comment: 'An event that fires when a speech to text session is created'; - context: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Context of the session.' }; - duration: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Duration of the session.' }; - recognized: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'If speech was recognized.' }; + context: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Context of the session.' }; + sessionDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Duration of the session.' }; + sessionRecognized: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'If speech was recognized.' }; }; type SpeechToTextSessionEvent = { context: string; - duration: number; - recognized: boolean; + sessionDuration: number; + sessionRecognized: boolean; }; this.telemetryService.publicLog2('speechToTextSession', { context, - duration: Date.now() - sessionStart, - recognized: sessionRecognized + sessionDuration: Date.now() - sessionStart, + sessionRecognized }); } @@ -204,13 +204,13 @@ export class SpeechService extends Disposable implements ISpeechService { type KeywordRecognitionClassification = { owner: 'bpasero'; comment: 'An event that fires when a speech keyword detection is started'; - recognized: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'If the keyword was recognized.' }; + keywordRecognized: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'If the keyword was recognized.' }; }; type KeywordRecognitionEvent = { - recognized: boolean; + keywordRecognized: boolean; }; this.telemetryService.publicLog2('keywordRecognition', { - recognized: status === KeywordRecognitionStatus.Recognized + keywordRecognized: status === KeywordRecognitionStatus.Recognized }); return status; diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh index cc2cb83e0d2..d54b124e69a 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh @@ -32,12 +32,6 @@ if [[ "$VSCODE_INJECTION" == "1" ]]; then fi fi -# Shell integration was disabled by the shell, exit without warning assuming either the shell has -# explicitly disabled shell integration as it's incompatible or it implements the protocol. -if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then - builtin return -fi - # Apply EnvironmentVariableCollections if needed if [ -n "${VSCODE_ENV_REPLACE:-}" ]; then IFS=':' read -rA ADDR <<< "$VSCODE_ENV_REPLACE" @@ -64,6 +58,12 @@ if [ -n "${VSCODE_ENV_APPEND:-}" ]; then unset VSCODE_ENV_APPEND fi +# Shell integration was disabled by the shell, exit without warning assuming either the shell has +# explicitly disabled shell integration as it's incompatible or it implements the protocol. +if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then + builtin return +fi + # The property (P) and command (E) codes embed values which require escaping. # Backslashes are doubled. Non-alphanumeric characters are converted to escaped hex. __vsc_escape_value() { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index 34dca8699de..2d4db51fc24 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -99,9 +99,9 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._responseSupportsIssueReportingContextKey = TerminalChatContextKeys.responseSupportsIssueReporting.bindTo(this._contextKeyService); this._sessionResponseVoteContextKey = TerminalChatContextKeys.sessionResponseVote.bindTo(this._contextKeyService); - if (!this._chatAgentService.getRegisteredAgent(this._terminalAgentId)) { + if (!this._chatAgentService.getAgent(this._terminalAgentId)) { this._register(this._chatAgentService.onDidChangeAgents(() => { - if (this._chatAgentService.getRegisteredAgent(this._terminalAgentId)) { + if (this._chatAgentService.getAgent(this._terminalAgentId)) { this._terminalAgentRegisteredContextKey.set(true); } })); diff --git a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts index 6864585e014..21d43f66568 100644 --- a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts +++ b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts @@ -229,7 +229,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili type AuxiliaryWindowClassification = { owner: 'bpasero'; comment: 'An event that fires when an auxiliary window is opened'; - bounds: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Has window bounds provided.' }; + bounds: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Has window bounds provided.' }; }; type AuxiliaryWindowOpenEvent = { bounds: boolean; diff --git a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts index 9e0eab4f88f..17c6ac2cb10 100644 --- a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts @@ -375,9 +375,13 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten const result = new Map(); const extensionInfos: IExtensionInfo[] = []; await Promise.all(extensionGalleryResources.map(async extensionGalleryResource => { - const webExtension = await this.toWebExtensionFromExtensionGalleryResource(extensionGalleryResource); - result.set(webExtension.identifier.id.toLowerCase(), webExtension); - extensionInfos.push({ id: webExtension.identifier.id, version: webExtension.version }); + try { + const webExtension = await this.toWebExtensionFromExtensionGalleryResource(extensionGalleryResource); + result.set(webExtension.identifier.id.toLowerCase(), webExtension); + extensionInfos.push({ id: webExtension.identifier.id, version: webExtension.version }); + } catch (error) { + this.logService.info(`Ignoring additional builtin extension from gallery resource ${extensionGalleryResource.toString()} because there is an error while converting it into web extension`, getErrorMessage(error)); + } })); const galleryExtensions = await this.galleryService.getExtensions(extensionInfos, CancellationToken.None); for (const galleryExtension of galleryExtensions) { diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 614dcc68cf1..d70cd4885ae 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -11741,7 +11741,7 @@ declare module 'vscode' { * until `Terminal.show` is called. The typical usage for this is when you need to run * something that may need interactivity but only want to tell the user about it when * interaction is needed. Note that the terminals will still be exposed to all extensions - * as normal and they will remain hidden when the workspace is reloaded. + * as normal. The hidden terminals will not be restored when the workspace is next opened. */ hideFromUser?: boolean; diff --git a/src/vscode-dts/vscode.proposed.debugFocus.d.ts b/src/vscode-dts/vscode.proposed.debugFocus.d.ts index 73abb814bbe..92c9ad01f2b 100644 --- a/src/vscode-dts/vscode.proposed.debugFocus.d.ts +++ b/src/vscode-dts/vscode.proposed.debugFocus.d.ts @@ -7,17 +7,13 @@ declare module 'vscode' { // See https://github.com/microsoft/vscode/issues/63943 - export class ThreadFocus { + export class Thread { /** * Create a ThreadFocus * @param session * @param threadId - * @param frameId */ - constructor( - session: DebugSession, - threadId?: number); - + constructor(session: DebugSession, threadId: number); /** * Debug session for thread. @@ -25,23 +21,19 @@ declare module 'vscode' { readonly session: DebugSession; /** - * Id of the associated thread (DAP id). May be undefined if thread has become unselected. + * ID of the associated thread in the debug protocol. */ - readonly threadId: number | undefined; + readonly threadId: number; } - export class StackFrameFocus { + export class StackFrame { /** * Create a StackFrameFocus * @param session * @param threadId * @param frameId */ - constructor( - session: DebugSession, - threadId?: number, - frameId?: number); - + constructor(session: DebugSession, threadId?: number, frameId?: number); /** * Debug session for thread. @@ -49,26 +41,26 @@ declare module 'vscode' { readonly session: DebugSession; /** - * Id of the associated thread (DAP id). May be undefined if a frame is unselected. + * Id of the associated thread in the debug protocol. */ - readonly threadId: number | undefined; + readonly threadId: number; /** - * Id of the stack frame (DAP id). May be undefined if a frame is unselected. + * Id of the stack frame in the debug protocol. */ - readonly frameId: number | undefined; + readonly frameId: number; } export namespace debug { /** - * The currently focused thread or stack frame id, or `undefined` if this has not been set. (e.g. not in debug mode). + * The currently focused thread or stack frame, or `undefined` if no + * thread or stack is focused. */ - export let stackFrameFocus: ThreadFocus | StackFrameFocus | undefined; + export const activeStackItem: Thread | StackFrame | undefined; /** - * An {@link Event} which fires when the {@link debug.stackFrameFocus} changes. Provides a sessionId. threadId is not undefined - * when a thread of frame has gained focus. frameId is defined when a stackFrame has gained focus. + * An event which fires when the {@link debug.activeStackItem} has changed. */ - export const onDidChangeStackFrameFocus: Event; + export const onDidChangeActiveStackItem: Event; } }