From 75ff485ed963e2edd6c007f4686fe8cd7a716db7 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 6 Oct 2025 16:44:25 -0400 Subject: [PATCH 1/5] rm ID from terminal completion provider (#270094) --- .../terminal-suggest/src/terminalSuggestMain.ts | 2 +- src/vs/workbench/api/common/extHost.api.impl.ts | 4 ++-- .../api/common/extHostTerminalService.ts | 16 ++++++++-------- ...code.proposed.terminalCompletionProvider.d.ts | 3 +-- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/extensions/terminal-suggest/src/terminalSuggestMain.ts b/extensions/terminal-suggest/src/terminalSuggestMain.ts index 4f12b258652..9db6122f729 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -250,7 +250,7 @@ export async function activate(context: vscode.ExtensionContext) { const machineId = await vscode.env.machineId; const remoteAuthority = vscode.env.remoteName; - context.subscriptions.push(vscode.window.registerTerminalCompletionProvider('terminal-suggest', { + context.subscriptions.push(vscode.window.registerTerminalCompletionProvider({ async provideTerminalCompletions(terminal: vscode.Terminal, terminalContext: vscode.TerminalCompletionContext, token: vscode.CancellationToken): Promise { currentTerminalEnv = terminal.shellIntegration?.env?.value ?? process.env; if (token.isCancellationRequested) { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index cac3d4cc155..cfe5c39b099 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -877,9 +877,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerTerminalProfileProvider(id: string, provider: vscode.TerminalProfileProvider): vscode.Disposable { return extHostTerminalService.registerProfileProvider(extension, id, provider); }, - registerTerminalCompletionProvider(id: string, provider: vscode.TerminalCompletionProvider, ...triggerCharacters: string[]): vscode.Disposable { + registerTerminalCompletionProvider(provider: vscode.TerminalCompletionProvider, ...triggerCharacters: string[]): vscode.Disposable { checkProposedApiEnabled(extension, 'terminalCompletionProvider'); - return extHostTerminalService.registerTerminalCompletionProvider(extension, id, provider, ...triggerCharacters); + return extHostTerminalService.registerTerminalCompletionProvider(extension, provider, ...triggerCharacters); }, registerTerminalQuickFixProvider(id: string, provider: vscode.TerminalQuickFixProvider): vscode.Disposable { checkProposedApiEnabled(extension, 'terminalQuickFixProvider'); diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 9730650d1fa..270e3633e85 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -57,7 +57,7 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, ID getEnvironmentVariableCollection(extension: IExtensionDescription): IEnvironmentVariableCollection; getTerminalById(id: number): ExtHostTerminal | null; getTerminalIdByApiObject(apiTerminal: vscode.Terminal): number | null; - registerTerminalCompletionProvider(extension: IExtensionDescription, id: string, provider: vscode.TerminalCompletionProvider, ...triggerCharacters: string[]): vscode.Disposable; + registerTerminalCompletionProvider(extension: IExtensionDescription, provider: vscode.TerminalCompletionProvider, ...triggerCharacters: string[]): vscode.Disposable; } interface IEnvironmentVariableCollection extends vscode.EnvironmentVariableCollection { @@ -757,15 +757,15 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I }); } - public registerTerminalCompletionProvider(extension: IExtensionDescription, id: string, provider: vscode.TerminalCompletionProvider, ...triggerCharacters: string[]): vscode.Disposable { - if (this._completionProviders.has(id)) { - throw new Error(`Terminal completion provider "${id}" already registered`); + public registerTerminalCompletionProvider(extension: IExtensionDescription, provider: vscode.TerminalCompletionProvider, ...triggerCharacters: string[]): vscode.Disposable { + if (this._completionProviders.has(extension.identifier.value)) { + throw new Error(`Terminal completion provider "${extension.identifier.value}" already registered`); } - this._completionProviders.set(id, provider); - this._proxy.$registerCompletionProvider(id, extension.identifier.value, ...triggerCharacters); + this._completionProviders.set(extension.identifier.value, provider); + this._proxy.$registerCompletionProvider(extension.identifier.value, extension.identifier.value, ...triggerCharacters); return new VSCodeDisposable(() => { - this._completionProviders.delete(id); - this._proxy.$unregisterCompletionProvider(id); + this._completionProviders.delete(extension.identifier.value); + this._proxy.$unregisterCompletionProvider(extension.identifier.value); }); } diff --git a/src/vscode-dts/vscode.proposed.terminalCompletionProvider.d.ts b/src/vscode-dts/vscode.proposed.terminalCompletionProvider.d.ts index 1a0196f683c..37152b7145f 100644 --- a/src/vscode-dts/vscode.proposed.terminalCompletionProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.terminalCompletionProvider.d.ts @@ -133,7 +133,6 @@ declare module 'vscode' { export namespace window { /** * Register a completion provider for terminals. - * @param id The unique identifier of the terminal provider, used as a settings key and shown in the information hover of the suggest widget. * @param provider The completion provider. * @returns A {@link Disposable} that unregisters this provider when being disposed. * @@ -146,7 +145,7 @@ declare module 'vscode' { * } * }); */ - export function registerTerminalCompletionProvider(id: string, provider: TerminalCompletionProvider, ...triggerCharacters: string[]): Disposable; + export function registerTerminalCompletionProvider(provider: TerminalCompletionProvider, ...triggerCharacters: string[]): Disposable; } /** From 2b636917cea6bf222ba780cd18989c12c13fd5b8 Mon Sep 17 00:00:00 2001 From: Matt Bierner <12821956+mjbvz@users.noreply.github.com> Date: Mon, 6 Oct 2025 14:12:10 -0700 Subject: [PATCH 2/5] Replace more `...args: any[]` with unknown Follow up on #269213 Typing only change to make `args` type safer --- src/vs/base/common/lifecycle.ts | 4 +- .../contrib/rename/browser/renameWidget.ts | 2 +- .../browser/standaloneCodeEditor.ts | 2 +- .../standalone/browser/standaloneEditor.ts | 2 +- .../standalone/browser/standaloneWebWorker.ts | 2 +- src/vs/platform/log/common/log.ts | 82 +++++++++---------- src/vs/platform/log/common/logService.ts | 10 +-- .../test/common/telemetryLogAppender.test.ts | 10 +-- .../terminal/common/terminalLogService.ts | 10 +-- .../userDataSync/common/userDataSyncLog.ts | 10 +-- src/vs/server/node/serverServices.ts | 10 +-- src/vs/workbench/api/common/extHostOutput.ts | 10 +-- .../browser/extHostMessagerService.test.ts | 10 +-- .../chatEditing/chatEditingEditorActions.ts | 2 +- .../common/editSessionsLogService.ts | 10 +-- .../inlineChat/browser/inlineChatActions.ts | 24 +++--- .../browser/inlineChatController.ts | 4 +- .../browser/inlineChatCurrentLine.ts | 2 +- .../electron-browser/inlineChatActions.ts | 2 +- .../contrib/cellCommands/cellCommands.ts | 6 +- .../browser/controller/coreActions.ts | 2 +- .../browser/controller/executeActions.ts | 10 +-- .../browser/preferences.contribution.ts | 2 +- .../contrib/timeline/browser/timelinePane.ts | 2 +- 24 files changed, 115 insertions(+), 115 deletions(-) diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index c02a023cb00..ff7684f382b 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -685,7 +685,7 @@ export abstract class ReferenceCollection { private readonly references: Map = new Map(); - acquire(key: string, ...args: any[]): IReference { + acquire(key: string, ...args: unknown[]): IReference { let reference = this.references.get(key); if (!reference) { @@ -706,7 +706,7 @@ export abstract class ReferenceCollection { return { object, dispose }; } - protected abstract createReferencedObject(key: string, ...args: any[]): T; + protected abstract createReferencedObject(key: string, ...args: unknown[]): T; protected abstract destroyReferencedObject(key: string, object: T): void; } diff --git a/src/vs/editor/contrib/rename/browser/renameWidget.ts b/src/vs/editor/contrib/rename/browser/renameWidget.ts index c59f382e763..340d9dc2e83 100644 --- a/src/vs/editor/contrib/rename/browser/renameWidget.ts +++ b/src/vs/editor/contrib/rename/browser/renameWidget.ts @@ -595,7 +595,7 @@ export class RenameWidget implements IRenameWidget, IContentWidget, IDisposable } private async _updateRenameCandidates(candidates: ProviderResult[], currentName: string, token: CancellationToken) { - const trace = (...args: any[]) => this._trace('_updateRenameCandidates', ...args); + const trace = (...args: unknown[]) => this._trace('_updateRenameCandidates', ...args); trace('start'); const namesListResults = await raceCancellation(Promise.allSettled(candidates), token); diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index dffab646d2b..32dbaa2265c 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -335,7 +335,7 @@ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandalon ); const contextMenuGroupId = _descriptor.contextMenuGroupId || null; const contextMenuOrder = _descriptor.contextMenuOrder || 0; - const run = (_accessor?: ServicesAccessor, ...args: any[]): Promise => { + const run = (_accessor?: ServicesAccessor, ...args: unknown[]): Promise => { return Promise.resolve(_descriptor.run(this, ...args)); }; diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 6a9604e9038..6d5cbfebc1f 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -137,7 +137,7 @@ export function addEditorAction(descriptor: IActionDescriptor): IDisposable { } const precondition = ContextKeyExpr.deserialize(descriptor.precondition); - const run = (accessor: ServicesAccessor, ...args: any[]): void | Promise => { + const run = (accessor: ServicesAccessor, ...args: unknown[]): void | Promise => { return EditorCommand.runEditorCommand(accessor, args, precondition, (accessor, editor, args) => Promise.resolve(descriptor.run(editor, ...args))); }; diff --git a/src/vs/editor/standalone/browser/standaloneWebWorker.ts b/src/vs/editor/standalone/browser/standaloneWebWorker.ts index 74f1a2770b9..43f1cc93608 100644 --- a/src/vs/editor/standalone/browser/standaloneWebWorker.ts +++ b/src/vs/editor/standalone/browser/standaloneWebWorker.ts @@ -68,7 +68,7 @@ class MonacoWebWorkerImpl extends EditorWorkerClient implement if (typeof prop !== 'string') { throw new Error(`Not supported`); } - return (...args: any[]) => { + return (...args: unknown[]) => { return proxy.$fmr(prop, args); }; } diff --git a/src/vs/platform/log/common/log.ts b/src/vs/platform/log/common/log.ts index 054ebeb45b6..1124bc8f828 100644 --- a/src/vs/platform/log/common/log.ts +++ b/src/vs/platform/log/common/log.ts @@ -45,11 +45,11 @@ export interface ILogger extends IDisposable { getLevel(): LogLevel; setLevel(level: LogLevel): void; - trace(message: string, ...args: any[]): void; - debug(message: string, ...args: any[]): void; - info(message: string, ...args: any[]): void; - warn(message: string, ...args: any[]): void; - error(message: string | Error, ...args: any[]): void; + trace(message: string, ...args: unknown[]): void; + debug(message: string, ...args: unknown[]): void; + info(message: string, ...args: unknown[]): void; + warn(message: string, ...args: unknown[]): void; + error(message: string | Error, ...args: unknown[]): void; /** * An operation to flush the contents. Can be synchronous. @@ -281,11 +281,11 @@ export abstract class AbstractLogger extends Disposable implements ILogger { return this.checkLogLevel(level); } - abstract trace(message: string, ...args: any[]): void; - abstract debug(message: string, ...args: any[]): void; - abstract info(message: string, ...args: any[]): void; - abstract warn(message: string, ...args: any[]): void; - abstract error(message: string | Error, ...args: any[]): void; + abstract trace(message: string, ...args: unknown[]): void; + abstract debug(message: string, ...args: unknown[]): void; + abstract info(message: string, ...args: unknown[]): void; + abstract warn(message: string, ...args: unknown[]): void; + abstract error(message: string | Error, ...args: unknown[]): void; abstract flush(): void; } @@ -299,31 +299,31 @@ export abstract class AbstractMessageLogger extends AbstractLogger implements IL return this.logAlways || super.checkLogLevel(level); } - trace(message: string, ...args: any[]): void { + trace(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Trace)) { this.log(LogLevel.Trace, format([message, ...args], true)); } } - debug(message: string, ...args: any[]): void { + debug(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Debug)) { this.log(LogLevel.Debug, format([message, ...args])); } } - info(message: string, ...args: any[]): void { + info(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Info)) { this.log(LogLevel.Info, format([message, ...args])); } } - warn(message: string, ...args: any[]): void { + warn(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Warning)) { this.log(LogLevel.Warning, format([message, ...args])); } } - error(message: string | Error, ...args: any[]): void { + error(message: string | Error, ...args: unknown[]): void { if (this.canLog(LogLevel.Error)) { if (message instanceof Error) { const array = Array.prototype.slice.call(arguments) as any[]; @@ -351,7 +351,7 @@ export class ConsoleMainLogger extends AbstractLogger implements ILogger { this.useColors = !isWindows; } - trace(message: string, ...args: any[]): void { + trace(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Trace)) { if (this.useColors) { console.log(`\x1b[90m[main ${now()}]\x1b[0m`, message, ...args); @@ -361,7 +361,7 @@ export class ConsoleMainLogger extends AbstractLogger implements ILogger { } } - debug(message: string, ...args: any[]): void { + debug(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Debug)) { if (this.useColors) { console.log(`\x1b[90m[main ${now()}]\x1b[0m`, message, ...args); @@ -371,7 +371,7 @@ export class ConsoleMainLogger extends AbstractLogger implements ILogger { } } - info(message: string, ...args: any[]): void { + info(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Info)) { if (this.useColors) { console.log(`\x1b[90m[main ${now()}]\x1b[0m`, message, ...args); @@ -381,7 +381,7 @@ export class ConsoleMainLogger extends AbstractLogger implements ILogger { } } - warn(message: string | Error, ...args: any[]): void { + warn(message: string | Error, ...args: unknown[]): void { if (this.canLog(LogLevel.Warning)) { if (this.useColors) { console.warn(`\x1b[93m[main ${now()}]\x1b[0m`, message, ...args); @@ -391,7 +391,7 @@ export class ConsoleMainLogger extends AbstractLogger implements ILogger { } } - error(message: string, ...args: any[]): void { + error(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Error)) { if (this.useColors) { console.error(`\x1b[91m[main ${now()}]\x1b[0m`, message, ...args); @@ -414,7 +414,7 @@ export class ConsoleLogger extends AbstractLogger implements ILogger { this.setLevel(logLevel); } - trace(message: string, ...args: any[]): void { + trace(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Trace)) { if (this.useColors) { console.log('%cTRACE', 'color: #888', message, ...args); @@ -424,7 +424,7 @@ export class ConsoleLogger extends AbstractLogger implements ILogger { } } - debug(message: string, ...args: any[]): void { + debug(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Debug)) { if (this.useColors) { console.log('%cDEBUG', 'background: #eee; color: #888', message, ...args); @@ -434,7 +434,7 @@ export class ConsoleLogger extends AbstractLogger implements ILogger { } } - info(message: string, ...args: any[]): void { + info(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Info)) { if (this.useColors) { console.log('%c INFO', 'color: #33f', message, ...args); @@ -444,7 +444,7 @@ export class ConsoleLogger extends AbstractLogger implements ILogger { } } - warn(message: string | Error, ...args: any[]): void { + warn(message: string | Error, ...args: unknown[]): void { if (this.canLog(LogLevel.Warning)) { if (this.useColors) { console.warn('%c WARN', 'color: #993', message, ...args); @@ -454,7 +454,7 @@ export class ConsoleLogger extends AbstractLogger implements ILogger { } } - error(message: string, ...args: any[]): void { + error(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Error)) { if (this.useColors) { console.error('%c ERR', 'color: #f33', message, ...args); @@ -477,31 +477,31 @@ export class AdapterLogger extends AbstractLogger implements ILogger { this.setLevel(logLevel); } - trace(message: string, ...args: any[]): void { + trace(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Trace)) { this.adapter.log(LogLevel.Trace, [this.extractMessage(message), ...args]); } } - debug(message: string, ...args: any[]): void { + debug(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Debug)) { this.adapter.log(LogLevel.Debug, [this.extractMessage(message), ...args]); } } - info(message: string, ...args: any[]): void { + info(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Info)) { this.adapter.log(LogLevel.Info, [this.extractMessage(message), ...args]); } } - warn(message: string | Error, ...args: any[]): void { + warn(message: string | Error, ...args: unknown[]): void { if (this.canLog(LogLevel.Warning)) { this.adapter.log(LogLevel.Warning, [this.extractMessage(message), ...args]); } } - error(message: string | Error, ...args: any[]): void { + error(message: string | Error, ...args: unknown[]): void { if (this.canLog(LogLevel.Error)) { this.adapter.log(LogLevel.Error, [this.extractMessage(message), ...args]); } @@ -536,31 +536,31 @@ export class MultiplexLogger extends AbstractLogger implements ILogger { super.setLevel(level); } - trace(message: string, ...args: any[]): void { + trace(message: string, ...args: unknown[]): void { for (const logger of this.loggers) { logger.trace(message, ...args); } } - debug(message: string, ...args: any[]): void { + debug(message: string, ...args: unknown[]): void { for (const logger of this.loggers) { logger.debug(message, ...args); } } - info(message: string, ...args: any[]): void { + info(message: string, ...args: unknown[]): void { for (const logger of this.loggers) { logger.info(message, ...args); } } - warn(message: string, ...args: any[]): void { + warn(message: string, ...args: unknown[]): void { for (const logger of this.loggers) { logger.warn(message, ...args); } } - error(message: string | Error, ...args: any[]): void { + error(message: string | Error, ...args: unknown[]): void { for (const logger of this.loggers) { logger.error(message, ...args); } @@ -740,12 +740,12 @@ export class NullLogger implements ILogger { readonly onDidChangeLogLevel: Event = new Emitter().event; setLevel(level: LogLevel): void { } getLevel(): LogLevel { return LogLevel.Info; } - trace(message: string, ...args: any[]): void { } - debug(message: string, ...args: any[]): void { } - info(message: string, ...args: any[]): void { } - warn(message: string, ...args: any[]): void { } - error(message: string | Error, ...args: any[]): void { } - critical(message: string | Error, ...args: any[]): void { } + trace(message: string, ...args: unknown[]): void { } + debug(message: string, ...args: unknown[]): void { } + info(message: string, ...args: unknown[]): void { } + warn(message: string, ...args: unknown[]): void { } + error(message: string | Error, ...args: unknown[]): void { } + critical(message: string | Error, ...args: unknown[]): void { } dispose(): void { } flush(): void { } } diff --git a/src/vs/platform/log/common/logService.ts b/src/vs/platform/log/common/logService.ts index e76c4c30d7a..ee201810ae1 100644 --- a/src/vs/platform/log/common/logService.ts +++ b/src/vs/platform/log/common/logService.ts @@ -31,23 +31,23 @@ export class LogService extends Disposable implements ILogService { return this.logger.getLevel(); } - trace(message: string, ...args: any[]): void { + trace(message: string, ...args: unknown[]): void { this.logger.trace(message, ...args); } - debug(message: string, ...args: any[]): void { + debug(message: string, ...args: unknown[]): void { this.logger.debug(message, ...args); } - info(message: string, ...args: any[]): void { + info(message: string, ...args: unknown[]): void { this.logger.info(message, ...args); } - warn(message: string, ...args: any[]): void { + warn(message: string, ...args: unknown[]): void { this.logger.warn(message, ...args); } - error(message: string | Error, ...args: any[]): void { + error(message: string | Error, ...args: unknown[]): void { this.logger.error(message, ...args); } diff --git a/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts b/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts index 2a41688bc8d..1b4f4a1d274 100644 --- a/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts +++ b/src/vs/platform/telemetry/test/common/telemetryLogAppender.test.ts @@ -20,31 +20,31 @@ class TestTelemetryLogger extends AbstractLogger implements ILogger { this.setLevel(logLevel); } - trace(message: string, ...args: any[]): void { + trace(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Trace)) { this.logs.push(message + JSON.stringify(args)); } } - debug(message: string, ...args: any[]): void { + debug(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Debug)) { this.logs.push(message); } } - info(message: string, ...args: any[]): void { + info(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Info)) { this.logs.push(message); } } - warn(message: string | Error, ...args: any[]): void { + warn(message: string | Error, ...args: unknown[]): void { if (this.canLog(LogLevel.Warning)) { this.logs.push(message.toString()); } } - error(message: string, ...args: any[]): void { + error(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Error)) { this.logs.push(message); } diff --git a/src/vs/platform/terminal/common/terminalLogService.ts b/src/vs/platform/terminal/common/terminalLogService.ts index c536b084517..4588544e17d 100644 --- a/src/vs/platform/terminal/common/terminalLogService.ts +++ b/src/vs/platform/terminal/common/terminalLogService.ts @@ -38,11 +38,11 @@ export class TerminalLogService extends Disposable implements ITerminalLogServic setLevel(level: LogLevel): void { this._logger.setLevel(level); } flush(): void { this._logger.flush(); } - trace(message: string, ...args: any[]): void { this._logger.trace(this._formatMessage(message), args); } - debug(message: string, ...args: any[]): void { this._logger.debug(this._formatMessage(message), args); } - info(message: string, ...args: any[]): void { this._logger.info(this._formatMessage(message), args); } - warn(message: string, ...args: any[]): void { this._logger.warn(this._formatMessage(message), args); } - error(message: string | Error, ...args: any[]): void { + trace(message: string, ...args: unknown[]): void { this._logger.trace(this._formatMessage(message), args); } + debug(message: string, ...args: unknown[]): void { this._logger.debug(this._formatMessage(message), args); } + info(message: string, ...args: unknown[]): void { this._logger.info(this._formatMessage(message), args); } + warn(message: string, ...args: unknown[]): void { this._logger.warn(this._formatMessage(message), args); } + error(message: string | Error, ...args: unknown[]): void { if (message instanceof Error) { this._logger.error(this._formatMessage(''), message, args); return; diff --git a/src/vs/platform/userDataSync/common/userDataSyncLog.ts b/src/vs/platform/userDataSync/common/userDataSyncLog.ts index 464be1fa944..ff00d8624bf 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncLog.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncLog.ts @@ -22,23 +22,23 @@ export class UserDataSyncLogService extends AbstractLogger implements IUserDataS this.logger = this._register(loggerService.createLogger(joinPath(environmentService.logsHome, `${USER_DATA_SYNC_LOG_ID}.log`), { id: USER_DATA_SYNC_LOG_ID, name: localize('userDataSyncLog', "Settings Sync") })); } - trace(message: string, ...args: any[]): void { + trace(message: string, ...args: unknown[]): void { this.logger.trace(message, ...args); } - debug(message: string, ...args: any[]): void { + debug(message: string, ...args: unknown[]): void { this.logger.debug(message, ...args); } - info(message: string, ...args: any[]): void { + info(message: string, ...args: unknown[]): void { this.logger.info(message, ...args); } - warn(message: string, ...args: any[]): void { + warn(message: string, ...args: unknown[]): void { this.logger.warn(message, ...args); } - error(message: string | Error, ...args: any[]): void { + error(message: string | Error, ...args: unknown[]): void { this.logger.error(message, ...args); } diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index 1e6758f1c88..48aed965931 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -304,7 +304,7 @@ class ServerLogger extends AbstractLogger { this.useColors = Boolean(process.stdout.isTTY); } - trace(message: string, ...args: any[]): void { + trace(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Trace)) { if (this.useColors) { console.log(`\x1b[90m[${now()}]\x1b[0m`, message, ...args); @@ -314,7 +314,7 @@ class ServerLogger extends AbstractLogger { } } - debug(message: string, ...args: any[]): void { + debug(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Debug)) { if (this.useColors) { console.log(`\x1b[90m[${now()}]\x1b[0m`, message, ...args); @@ -324,7 +324,7 @@ class ServerLogger extends AbstractLogger { } } - info(message: string, ...args: any[]): void { + info(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Info)) { if (this.useColors) { console.log(`\x1b[90m[${now()}]\x1b[0m`, message, ...args); @@ -334,7 +334,7 @@ class ServerLogger extends AbstractLogger { } } - warn(message: string | Error, ...args: any[]): void { + warn(message: string | Error, ...args: unknown[]): void { if (this.canLog(LogLevel.Warning)) { if (this.useColors) { console.warn(`\x1b[93m[${now()}]\x1b[0m`, message, ...args); @@ -344,7 +344,7 @@ class ServerLogger extends AbstractLogger { } } - error(message: string, ...args: any[]): void { + error(message: string, ...args: unknown[]): void { if (this.canLog(LogLevel.Error)) { if (this.useColors) { console.error(`\x1b[91m[${now()}]\x1b[0m`, message, ...args); diff --git a/src/vs/workbench/api/common/extHostOutput.ts b/src/vs/workbench/api/common/extHostOutput.ts index 6b8ba4c1595..1d37a34b7ba 100644 --- a/src/vs/workbench/api/common/extHostOutput.ts +++ b/src/vs/workbench/api/common/extHostOutput.ts @@ -257,23 +257,23 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { ...this.createExtHostOutputChannel(name, channelPromise, channelDisposables), get logLevel() { return logLevel; }, onDidChangeLogLevel: onDidChangeLogLevel.event, - trace(value: string, ...args: any[]): void { + trace(value: string, ...args: unknown[]): void { validate(); channelPromise.then(channel => channel.trace(value, ...args)); }, - debug(value: string, ...args: any[]): void { + debug(value: string, ...args: unknown[]): void { validate(); channelPromise.then(channel => channel.debug(value, ...args)); }, - info(value: string, ...args: any[]): void { + info(value: string, ...args: unknown[]): void { validate(); channelPromise.then(channel => channel.info(value, ...args)); }, - warn(value: string, ...args: any[]): void { + warn(value: string, ...args: unknown[]): void { validate(); channelPromise.then(channel => channel.warn(value, ...args)); }, - error(value: Error | string, ...args: any[]): void { + error(value: Error | string, ...args: unknown[]): void { validate(); channelPromise.then(channel => channel.error(value, ...args)); } diff --git a/src/vs/workbench/api/test/browser/extHostMessagerService.test.ts b/src/vs/workbench/api/test/browser/extHostMessagerService.test.ts index bedd41eb4db..23f3ef10a48 100644 --- a/src/vs/workbench/api/test/browser/extHostMessagerService.test.ts +++ b/src/vs/workbench/api/test/browser/extHostMessagerService.test.ts @@ -19,7 +19,7 @@ const emptyCommandService: ICommandService = { _serviceBrand: undefined, onWillExecuteCommand: () => Disposable.None, onDidExecuteCommand: () => Disposable.None, - executeCommand: (commandId: string, ...args: any[]): Promise => { + executeCommand: (commandId: string, ...args: unknown[]): Promise => { return Promise.resolve(undefined); } }; @@ -27,16 +27,16 @@ const emptyCommandService: ICommandService = { const emptyNotificationService = new class implements INotificationService { declare readonly _serviceBrand: undefined; onDidChangeFilter: Event = Event.None; - notify(...args: any[]): never { + notify(...args: unknown[]): never { throw new Error('not implemented'); } - info(...args: any[]): never { + info(...args: unknown[]): never { throw new Error('not implemented'); } - warn(...args: any[]): never { + warn(...args: unknown[]): never { throw new Error('not implemented'); } - error(...args: any[]): never { + error(...args: unknown[]): never { throw new Error('not implemented'); } prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts index 7ad127ad5ea..32f636aebf8 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts @@ -338,7 +338,7 @@ export class ReviewChangesAction extends ChatEditingEditorAction { }); } - override runChatEditingCommand(_accessor: ServicesAccessor, _session: IChatEditingSession, entry: IModifiedFileEntry, _integration: IModifiedFileEntryEditorIntegration, ..._args: any[]): void { + override runChatEditingCommand(_accessor: ServicesAccessor, _session: IChatEditingSession, entry: IModifiedFileEntry, _integration: IModifiedFileEntryEditorIntegration, ..._args: unknown[]): void { entry.enableReviewModeUntilSettled(); } } diff --git a/src/vs/workbench/contrib/editSessions/common/editSessionsLogService.ts b/src/vs/workbench/contrib/editSessions/common/editSessionsLogService.ts index 08ff7d57671..4be8234c928 100644 --- a/src/vs/workbench/contrib/editSessions/common/editSessionsLogService.ts +++ b/src/vs/workbench/contrib/editSessions/common/editSessionsLogService.ts @@ -23,23 +23,23 @@ export class EditSessionsLogService extends AbstractLogger implements IEditSessi this.logger = this._register(loggerService.createLogger(joinPath(environmentService.logsHome, `${editSessionsLogId}.log`), { id: editSessionsLogId, name: localize('cloudChangesLog', "Cloud Changes"), group: windowLogGroup })); } - trace(message: string, ...args: any[]): void { + trace(message: string, ...args: unknown[]): void { this.logger.trace(message, ...args); } - debug(message: string, ...args: any[]): void { + debug(message: string, ...args: unknown[]): void { this.logger.debug(message, ...args); } - info(message: string, ...args: any[]): void { + info(message: string, ...args: unknown[]): void { this.logger.info(message, ...args); } - warn(message: string, ...args: any[]): void { + warn(message: string, ...args: unknown[]): void { this.logger.warn(message, ...args); } - error(message: string | Error, ...args: any[]): void { + error(message: string | Error, ...args: unknown[]): void { this.logger.error(message, ...args); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 1fb92a65714..4fd0a8b1a74 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -105,7 +105,7 @@ export class StartSessionAction extends Action2 { }); } - private _runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) { + private _runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ..._args: unknown[]) { const ctrl = InlineChatController.get(editor); if (!ctrl) { @@ -146,7 +146,7 @@ export class FocusInlineChat extends EditorAction2 { }); } - override runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) { + override runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, ..._args: unknown[]) { InlineChatController.get(editor)?.focus(); } } @@ -167,7 +167,7 @@ export class UnstashSessionAction extends EditorAction2 { }); } - override async runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) { + override async runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, ..._args: unknown[]) { const ctrl = InlineChatController1.get(editor); if (ctrl) { const session = ctrl.unstashLastSession(); @@ -208,7 +208,7 @@ export abstract class AbstractInline1ChatAction extends EditorAction2 { }); } - override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) { + override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ..._args: unknown[]) { const editorService = accessor.get(IEditorService); const logService = accessor.get(ILogService); @@ -260,7 +260,7 @@ export class ArrowOutUpAction extends AbstractInline1ChatAction { }); } - runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: any[]): void { + runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: unknown[]): void { ctrl.arrowOut(true); } } @@ -278,7 +278,7 @@ export class ArrowOutDownAction extends AbstractInline1ChatAction { }); } - runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: any[]): void { + runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: unknown[]): void { ctrl.arrowOut(false); } } @@ -371,7 +371,7 @@ export class RerunAction extends AbstractInline1ChatAction { }); } - override async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: any[]): Promise { + override async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: unknown[]): Promise { const chatService = accessor.get(IChatService); const chatWidgetService = accessor.get(IChatWidgetService); const model = ctrl.chatWidget.viewModel?.model; @@ -417,7 +417,7 @@ export class CloseAction extends AbstractInline1ChatAction { }); } - async runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: any[]): Promise { + async runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: unknown[]): Promise { ctrl.cancelSession(); } } @@ -438,7 +438,7 @@ export class ConfigureInlineChatAction extends AbstractInline1ChatAction { }); } - async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: any[]): Promise { + async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: unknown[]): Promise { accessor.get(IPreferencesService).openSettings({ query: 'inlineChat' }); } } @@ -512,7 +512,7 @@ export class ViewInChatAction extends AbstractInline1ChatAction { } }); } - override runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: any[]) { + override runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: unknown[]) { return ctrl.viewInChat(); } } @@ -577,7 +577,7 @@ abstract class AbstractInline2ChatAction extends EditorAction2 { }); } - override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) { + override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ..._args: unknown[]) { const editorService = accessor.get(IEditorService); const logService = accessor.get(ILogService); @@ -642,7 +642,7 @@ class KeepOrUndoSessionAction extends AbstractInline2ChatAction { }); } - override async runInlineChatCommand(accessor: ServicesAccessor, _ctrl: InlineChatController2, editor: ICodeEditor, ..._args: any[]): Promise { + override async runInlineChatCommand(accessor: ServicesAccessor, _ctrl: InlineChatController2, editor: ICodeEditor, ..._args: unknown[]): Promise { const inlineChatSessions = accessor.get(IInlineChatSessionService); if (!editor.hasModel()) { return; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index dab7d18a8d1..eb42a9bde57 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -315,7 +315,7 @@ export class InlineChatController1 implements IEditorContribution { this._log('DISPOSED controller'); } - private _log(message: string | Error, ...more: any[]): void { + private _log(message: string | Error, ...more: unknown[]): void { if (message instanceof Error) { this._logService.error(message, ...more); } else { @@ -715,7 +715,7 @@ export class InlineChatController1 implements IEditorContribution { } if (e.kind === 'move') { assertType(this._session); - const log: typeof this._log = (msg: string, ...args: any[]) => this._log('state=_showRequest) moving inline chat', msg, ...args); + const log: typeof this._log = (msg: string, ...args: unknown[]) => this._log('state=_showRequest) moving inline chat', msg, ...args); log('move was requested', e.target, e.range); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts index dc0266c2baf..d416e8b6ef7 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts @@ -123,7 +123,7 @@ export class ShowInlineChatHintAction extends EditorAction2 { }); } - override async runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, ...args: [uri: URI, position: IPosition, ...rest: any[]]) { + override async runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, ...args: [uri: URI, position: IPosition, ...rest: unknown[]]) { if (!editor.hasModel()) { return; } diff --git a/src/vs/workbench/contrib/inlineChat/electron-browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/electron-browser/inlineChatActions.ts index 1f0e9638235..914993f57ae 100644 --- a/src/vs/workbench/contrib/inlineChat/electron-browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/electron-browser/inlineChatActions.ts @@ -38,7 +38,7 @@ export class HoldToSpeak extends EditorAction2 { }); } - override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) { + override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ..._args: unknown[]) { const ctrl = InlineChatController.get(editor); if (ctrl) { holdForSpeech(accessor, ctrl, this); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands.ts index 2abc28493e9..239be611260 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands.ts @@ -369,7 +369,7 @@ registerAction2(class CollapseCellInputAction extends NotebookMultiCellAction { }); } - override parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined { + override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined { return parseMultiCellExecutionArgs(accessor, ...args); } @@ -395,7 +395,7 @@ registerAction2(class ExpandCellInputAction extends NotebookMultiCellAction { }); } - override parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined { + override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined { return parseMultiCellExecutionArgs(accessor, ...args); } @@ -465,7 +465,7 @@ registerAction2(class extends NotebookMultiCellAction { }); } - override parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined { + override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined { return parseMultiCellExecutionArgs(accessor, ...args); } diff --git a/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts index 84b255c0183..399b121e1e0 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts @@ -207,7 +207,7 @@ export abstract class NotebookMultiCellAction extends Action2 { super(desc); } - parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined { + parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined { return undefined; } diff --git a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts index f52fbf3c8d4..639109ff03b 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts @@ -285,7 +285,7 @@ registerAction2(class ExecuteCell extends NotebookMultiCellAction { }); } - override parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined { + override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined { return parseMultiCellExecutionArgs(accessor, ...args); } @@ -342,7 +342,7 @@ registerAction2(class ExecuteAboveCells extends NotebookMultiCellAction { }); } - override parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined { + override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined { return parseMultiCellExecutionArgs(accessor, ...args); } @@ -389,7 +389,7 @@ registerAction2(class ExecuteCellAndBelow extends NotebookMultiCellAction { }); } - override parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined { + override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined { return parseMultiCellExecutionArgs(accessor, ...args); } @@ -424,7 +424,7 @@ registerAction2(class ExecuteCellFocusContainer extends NotebookMultiCellAction }); } - override parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined { + override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined { return parseMultiCellExecutionArgs(accessor, ...args); } @@ -502,7 +502,7 @@ registerAction2(class CancelExecuteCell extends NotebookMultiCellAction { }); } - override parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined { + override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined { return parseMultiCellExecutionArgs(accessor, ...args); } diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 2b32a1538da..ccf0c627bc1 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -1264,7 +1264,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon for (const folder of this.workspaceContextService.getWorkspace().folders) { const commandId = `_workbench.openFolderSettings.${folder.uri.toString()}`; if (!CommandsRegistry.getCommand(commandId)) { - CommandsRegistry.registerCommand(commandId, (accessor: ServicesAccessor, ...args: any[]) => { + CommandsRegistry.registerCommand(commandId, (accessor: ServicesAccessor, ...args: unknown[]) => { const groupId = getEditorGroupFromArguments(accessor, args)?.id; if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.FOLDER) { return this.preferencesService.openWorkspaceSettings({ jsonEditor: false, groupId }); diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 4fe094b5ae3..6591a385f8f 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -1296,7 +1296,7 @@ class TimelinePaneCommands extends Disposable { })); this._register(CommandsRegistry.registerCommand('timeline.toggleFollowActiveEditor', - (accessor: ServicesAccessor, ...args: any[]) => pane.followActiveEditor = !pane.followActiveEditor + (accessor: ServicesAccessor, ...args: unknown[]) => pane.followActiveEditor = !pane.followActiveEditor )); this._register(MenuRegistry.appendMenuItem(MenuId.TimelineTitle, ({ From 2414493f6e5decdda58eb8378bf3dc9a9ce16a37 Mon Sep 17 00:00:00 2001 From: Aaron Munger <2019016+amunger@users.noreply.github.com> Date: Mon, 6 Oct 2025 15:13:09 -0700 Subject: [PATCH 3/5] cleanup: remove notebook chat controller (#270047) * first pass, one bug fix * remove unused code --- .../browser/contrib/navigation/arrow.ts | 5 +- .../controller/chat/cellChatActions.ts | 460 +-------- .../controller/chat/notebookChatContext.ts | 12 - .../controller/chat/notebookChatController.ts | 949 ------------------ .../browser/controller/executeActions.ts | 16 - .../browser/controller/insertCellActions.ts | 3 +- .../browser/view/cellParts/cellContextKeys.ts | 27 +- .../notebook/common/notebookContextKeys.ts | 1 - 8 files changed, 9 insertions(+), 1464 deletions(-) delete mode 100644 src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts 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 9a94c4a3f2b..e07c0d65973 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 '../../../../../../platform/instantiation/commo import { KeybindingWeight } from '../../../../../../platform/keybinding/common/keybindingsRegistry.js'; import { Registry } from '../../../../../../platform/registry/common/platform.js'; import { InlineChatController } from '../../../../inlineChat/browser/inlineChatController.js'; -import { CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION } from '../../controller/chat/notebookChatContext.js'; import { INotebookActionContext, INotebookCellActionContext, NotebookAction, NotebookCellAction, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, findTargetCellEditor } from '../../controller/coreActions.js'; import { CellEditState } from '../../notebookBrowser.js'; import { CellKind, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, NOTEBOOK_EDITOR_CURSOR_LINE_BOUNDARY } from '../../../common/notebookCommon.js'; @@ -237,7 +236,7 @@ registerAction2(class extends NotebookAction { weight: KeybindingWeight.WorkbenchContrib }, { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey), CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.isEqualTo('')), + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), mac: { primary: KeyMod.CtrlCmd | KeyCode.UpArrow }, weight: KeybindingWeight.WorkbenchContrib } @@ -269,7 +268,7 @@ registerAction2(class extends NotebookAction { weight: KeybindingWeight.WorkbenchContrib }, { - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey), CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.isEqualTo('')), + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow }, weight: KeybindingWeight.WorkbenchContrib } 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 7c52036cb9a..55310ea8efb 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts @@ -5,9 +5,7 @@ import { Codicon } from '../../../../../../base/common/codicons.js'; import { KeyChord, KeyCode, KeyMod } from '../../../../../../base/common/keyCodes.js'; -import { EditorContextKeys } from '../../../../../../editor/common/editorContextKeys.js'; import { localize, localize2 } from '../../../../../../nls.js'; -import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from '../../../../../../platform/accessibility/common/accessibility.js'; import { MenuId, MenuRegistry, registerAction2 } from '../../../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../../../platform/commands/common/commands.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; @@ -15,14 +13,12 @@ import { ContextKeyExpr } from '../../../../../../platform/contextkey/common/con import { InputFocusedContextKey } from '../../../../../../platform/contextkey/common/contextkeys.js'; import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation.js'; import { KeybindingWeight } from '../../../../../../platform/keybinding/common/keybindingsRegistry.js'; -import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, CTX_INLINE_CHAT_VISIBLE, InlineChatResponseType, MENU_INLINE_CHAT_WIDGET_STATUS } from '../../../../inlineChat/common/inlineChat.js'; -import { CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST, CTX_NOTEBOOK_CHAT_HAS_AGENT, CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION, CTX_NOTEBOOK_CHAT_USER_DID_EDIT, MENU_CELL_CHAT_INPUT, MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_STATUS } from './notebookChatContext.js'; -import { NotebookChatController } from './notebookChatController.js'; -import { CELL_TITLE_CELL_GROUP_ID, INotebookActionContext, INotebookCellActionContext, NotebookAction, NotebookCellAction, getContextFromActiveEditor, getEditorFromArgsOrActivePane } from '../coreActions.js'; +import { CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, CTX_INLINE_CHAT_VISIBLE, InlineChatResponseType, MENU_INLINE_CHAT_WIDGET_STATUS } from '../../../../inlineChat/common/inlineChat.js'; +import { CTX_NOTEBOOK_CHAT_HAS_AGENT } from './notebookChatContext.js'; +import { INotebookActionContext, NotebookAction, getContextFromActiveEditor, getEditorFromArgsOrActivePane } from '../coreActions.js'; import { insertNewCell } from '../insertCellActions.js'; -import { CellEditState } from '../../notebookBrowser.js'; -import { CellKind, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, NotebookSetting } from '../../../common/notebookCommon.js'; -import { IS_COMPOSITE_NOTEBOOK, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_GENERATED_BY_CHAT, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED } from '../../../common/notebookContextKeys.js'; +import { CellKind, NotebookSetting } from '../../../common/notebookCommon.js'; +import { NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED } from '../../../common/notebookContextKeys.js'; import { Iterable } from '../../../../../../base/common/iterator.js'; import { ICodeEditor } from '../../../../../../editor/browser/editorBrowser.js'; import { IEditorService } from '../../../../../services/editor/common/editorService.js'; @@ -30,283 +26,6 @@ import { ChatContextKeys } from '../../../../chat/common/chatContextKeys.js'; import { InlineChatController } from '../../../../inlineChat/browser/inlineChatController.js'; import { EditorAction2 } from '../../../../../../editor/browser/editorExtensions.js'; -registerAction2(class extends NotebookAction { - constructor() { - super( - { - id: 'notebook.cell.chat.accept', - title: localize2('notebook.cell.chat.accept', "Make Request"), - icon: Codicon.send, - keybinding: { - when: ContextKeyExpr.and(CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_INLINE_CHAT_FOCUSED, NOTEBOOK_CELL_EDITOR_FOCUSED.negate()), - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyCode.Enter - }, - menu: { - id: MENU_CELL_CHAT_INPUT, - group: 'navigation', - order: 1, - when: CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST.negate() - }, - f1: false - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext) { - NotebookChatController.get(context.notebookEditor)?.acceptInput(); - } -}); - -registerAction2(class extends NotebookCellAction { - constructor() { - super( - { - id: 'notebook.cell.chat.arrowOutUp', - title: localize('arrowUp', 'Cursor Up'), - keybinding: { - when: ContextKeyExpr.and( - CTX_NOTEBOOK_CELL_CHAT_FOCUSED, - CTX_INLINE_CHAT_FOCUSED, - CTX_INLINE_CHAT_INNER_CURSOR_FIRST, - NOTEBOOK_CELL_EDITOR_FOCUSED.negate(), - CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate() - ), - weight: KeybindingWeight.EditorCore + 7, - primary: KeyMod.CtrlCmd | KeyCode.UpArrow - }, - f1: false - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { - const editor = context.notebookEditor; - const activeCell = context.cell; - - const idx = editor.getCellIndex(activeCell); - if (typeof idx !== 'number') { - return; - } - - if (idx < 1 || editor.getLength() === 0) { - // we don't do loop - return; - } - - const newCell = editor.cellAt(idx - 1); - const newFocusMode = newCell.cellKind === CellKind.Markup && newCell.getEditState() === CellEditState.Preview ? 'container' : 'editor'; - const focusEditorLine = newCell.textBuffer.getLineCount(); - await editor.focusNotebookCell(newCell, newFocusMode, { focusEditorLine: focusEditorLine }); - } -}); - -registerAction2(class extends NotebookAction { - constructor() { - super( - { - id: 'notebook.cell.chat.arrowOutDown', - title: localize('arrowDown', 'Cursor Down'), - keybinding: { - when: ContextKeyExpr.and( - CTX_NOTEBOOK_CELL_CHAT_FOCUSED, - CTX_INLINE_CHAT_FOCUSED, - CTX_INLINE_CHAT_INNER_CURSOR_LAST, - NOTEBOOK_CELL_EDITOR_FOCUSED.negate(), - CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate() - ), - weight: KeybindingWeight.EditorCore + 7, - primary: KeyMod.CtrlCmd | KeyCode.DownArrow - }, - f1: false - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext) { - await NotebookChatController.get(context.notebookEditor)?.focusNext(); - } -}); - -registerAction2(class extends NotebookCellAction { - constructor() { - super( - { - id: 'notebook.cell.focusChatWidget', - title: localize('focusChatWidget', 'Focus Chat Widget'), - keybinding: { - when: ContextKeyExpr.and( - NOTEBOOK_EDITOR_FOCUSED, - CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate(), - ContextKeyExpr.and( - ContextKeyExpr.has(InputFocusedContextKey), - EditorContextKeys.editorTextFocus, - NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('bottom'), - NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none'), - ), - EditorContextKeys.isEmbeddedDiffEditor.negate() - ), - weight: KeybindingWeight.EditorCore + 7, - primary: KeyMod.CtrlCmd | KeyCode.UpArrow - }, - f1: false - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { - const index = context.notebookEditor.getCellIndex(context.cell); - await NotebookChatController.get(context.notebookEditor)?.focusNearestWidget(index, 'above'); - } -}); - -registerAction2(class extends NotebookCellAction { - constructor() { - super( - { - id: 'notebook.cell.focusNextChatWidget', - title: localize('focusNextChatWidget', 'Focus Next Cell Chat Widget'), - keybinding: { - when: ContextKeyExpr.and( - CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate(), - ContextKeyExpr.and( - ContextKeyExpr.has(InputFocusedContextKey), - EditorContextKeys.editorTextFocus, - NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('top'), - NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none'), - ), - EditorContextKeys.isEmbeddedDiffEditor.negate() - ), - weight: KeybindingWeight.EditorCore + 7, - primary: KeyMod.CtrlCmd | KeyCode.DownArrow - }, - f1: false, - precondition: ContextKeyExpr.or( - ContextKeyExpr.and(IS_COMPOSITE_NOTEBOOK.negate(), NOTEBOOK_CELL_EDITOR_FOCUSED), - ContextKeyExpr.and(IS_COMPOSITE_NOTEBOOK, NOTEBOOK_CELL_EDITOR_FOCUSED.negate()), - ) - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { - const index = context.notebookEditor.getCellIndex(context.cell); - await NotebookChatController.get(context.notebookEditor)?.focusNearestWidget(index, 'below'); - } -}); - -registerAction2(class extends NotebookAction { - constructor() { - super( - { - id: 'notebook.cell.chat.stop', - title: localize2('notebook.cell.chat.stop', "Stop Request"), - icon: Codicon.debugStop, - menu: { - id: MENU_CELL_CHAT_INPUT, - group: 'navigation', - order: 1, - when: CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST - }, - f1: false - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext) { - NotebookChatController.get(context.notebookEditor)?.cancelCurrentRequest(false); - } -}); - -registerAction2(class extends NotebookAction { - constructor() { - super( - { - id: 'notebook.cell.chat.close', - title: localize2('notebook.cell.chat.close', "Close Chat"), - icon: Codicon.close, - menu: { - id: MENU_CELL_CHAT_WIDGET, - group: 'navigation', - order: 2 - }, - f1: false - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext) { - NotebookChatController.get(context.notebookEditor)?.dismiss(false); - } -}); - -registerAction2(class extends NotebookAction { - constructor() { - super( - { - id: 'notebook.cell.chat.acceptChanges', - title: localize2('apply1', "Accept Changes"), - shortTitle: localize('apply2', 'Accept'), - icon: Codicon.check, - tooltip: localize('apply3', 'Accept Changes'), - keybinding: [ - { - when: ContextKeyExpr.and(CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_INLINE_CHAT_FOCUSED, NOTEBOOK_CELL_EDITOR_FOCUSED.negate()), - weight: KeybindingWeight.EditorContrib + 10, - primary: KeyMod.CtrlCmd | KeyCode.Enter, - }, - { - when: ContextKeyExpr.and(CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_INLINE_CHAT_FOCUSED, CTX_NOTEBOOK_CHAT_USER_DID_EDIT, NOTEBOOK_CELL_EDITOR_FOCUSED.negate()), - weight: KeybindingWeight.EditorCore + 10, - primary: KeyCode.Escape - }, - { - when: ContextKeyExpr.and( - NOTEBOOK_EDITOR_FOCUSED, - ContextKeyExpr.not(InputFocusedContextKey), - NOTEBOOK_CELL_EDITOR_FOCUSED.negate(), - CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.isEqualTo('below') - ), - primary: KeyMod.CtrlCmd | KeyCode.Enter, - weight: KeybindingWeight.WorkbenchContrib - } - ], - menu: [ - { - id: MENU_CELL_CHAT_WIDGET_STATUS, - group: '0_main', - order: 0, - when: CTX_INLINE_CHAT_RESPONSE_TYPE.notEqualsTo(InlineChatResponseType.Messages), - } - ], - f1: false - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext) { - NotebookChatController.get(context.notebookEditor)?.acceptSession(); - } -}); - -registerAction2(class extends NotebookAction { - constructor() { - super( - { - id: 'notebook.cell.chat.discard', - title: localize('discard', 'Discard'), - icon: Codicon.discard, - keybinding: { - when: ContextKeyExpr.and(CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_INLINE_CHAT_FOCUSED, CTX_NOTEBOOK_CHAT_USER_DID_EDIT.negate(), NOTEBOOK_CELL_EDITOR_FOCUSED.negate()), - weight: KeybindingWeight.EditorContrib, - primary: KeyCode.Escape - }, - menu: { - id: MENU_CELL_CHAT_WIDGET_STATUS, - group: '0_main', - order: 1 - }, - f1: false - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext) { - NotebookChatController.get(context.notebookEditor)?.discard(); - } -}); - interface IInsertCellWithChatArgs extends INotebookActionContext { input?: string; autoSend?: boolean; @@ -501,175 +220,6 @@ MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, { ) }); -registerAction2(class extends NotebookAction { - constructor() { - super({ - id: 'notebook.cell.chat.focus', - title: localize('focusNotebookChat', 'Focus Chat'), - keybinding: [ - { - when: ContextKeyExpr.and( - NOTEBOOK_EDITOR_FOCUSED, - ContextKeyExpr.not(InputFocusedContextKey), - CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.isEqualTo('above') - ), - primary: KeyMod.CtrlCmd | KeyCode.DownArrow, - weight: KeybindingWeight.WorkbenchContrib - }, - { - when: ContextKeyExpr.and( - NOTEBOOK_EDITOR_FOCUSED, - ContextKeyExpr.not(InputFocusedContextKey), - CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.isEqualTo('below') - ), - primary: KeyMod.CtrlCmd | KeyCode.UpArrow, - weight: KeybindingWeight.WorkbenchContrib - } - ], - f1: false - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { - NotebookChatController.get(context.notebookEditor)?.focus(); - } -}); - -registerAction2(class extends NotebookAction { - constructor() { - super({ - id: 'notebook.cell.chat.focusNextCell', - title: localize('focusNextCell', 'Focus Next Cell'), - keybinding: [ - { - when: ContextKeyExpr.and( - CTX_NOTEBOOK_CELL_CHAT_FOCUSED, - CTX_INLINE_CHAT_FOCUSED, - ), - primary: KeyMod.CtrlCmd | KeyCode.DownArrow, - weight: KeybindingWeight.WorkbenchContrib - } - ], - f1: false - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { - NotebookChatController.get(context.notebookEditor)?.focusNext(); - } -}); - -registerAction2(class extends NotebookAction { - constructor() { - super({ - id: 'notebook.cell.chat.focusPreviousCell', - title: localize('focusPreviousCell', 'Focus Previous Cell'), - keybinding: [ - { - when: ContextKeyExpr.and( - CTX_NOTEBOOK_CELL_CHAT_FOCUSED, - CTX_INLINE_CHAT_FOCUSED, - ), - primary: KeyMod.CtrlCmd | KeyCode.UpArrow, - weight: KeybindingWeight.WorkbenchContrib - } - ], - f1: false - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { - 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, - }, - f1: false - }); - } - - 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 - }, - f1: false - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext) { - NotebookChatController.get(context.notebookEditor)?.populateHistory(false); - } -}); - -registerAction2(class extends NotebookCellAction { - constructor() { - super( - { - id: 'notebook.cell.chat.restore', - title: localize2('notebookActions.restoreCellprompt', "Generate"), - icon: Codicon.sparkle, - menu: { - id: MenuId.NotebookCellTitle, - group: CELL_TITLE_CELL_GROUP_ID, - order: 0, - when: ContextKeyExpr.and( - NOTEBOOK_EDITOR_EDITABLE.isEqualTo(true), - CTX_NOTEBOOK_CHAT_HAS_AGENT, - NOTEBOOK_CELL_GENERATED_BY_CHAT, - ContextKeyExpr.equals(`config.${NotebookSetting.cellChat}`, true) - ) - }, - f1: false - }); - } - - async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) { - const cell = context.cell; - - if (!cell) { - return; - } - - const notebookEditor = context.notebookEditor; - const controller = NotebookChatController.get(notebookEditor); - - if (!controller) { - return; - } - - const prompt = controller.getPromptFromCache(cell); - - if (prompt) { - controller.restore(cell, prompt); - } - } -}); - - export class AcceptChangesAndRun extends EditorAction2 { constructor() { diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext.ts index c09bdb176b5..66c60504d98 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext.ts @@ -4,18 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from '../../../../../../nls.js'; -import { MenuId } from '../../../../../../platform/actions/common/actions.js'; import { RawContextKey } from '../../../../../../platform/contextkey/common/contextkey.js'; -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")); -export const CTX_NOTEBOOK_CHAT_USER_DID_EDIT = new RawContextKey('notebookChatUserDidEdit', false, localize('notebookChatUserDidEdit', "Whether the user did changes ontop of the notebook cell chat")); -export const CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION = new RawContextKey<'above' | 'below' | ''>('notebookChatOuterFocusPosition', '', localize('notebookChatOuterFocusPosition', "Whether the focus of the notebook editor is above or below the cell chat")); - -export const MENU_CELL_CHAT_INPUT = MenuId.for('cellChatInput'); -export const MENU_CELL_CHAT_WIDGET = MenuId.for('cellChatWidget'); -export const MENU_CELL_CHAT_WIDGET_STATUS = MenuId.for('cellChatWidget.status'); -export const MENU_CELL_CHAT_WIDGET_FEEDBACK = MenuId.for('cellChatWidget.feedback'); -export const MENU_CELL_CHAT_WIDGET_TOOLBAR = MenuId.for('cellChatWidget.toolbar'); - export const CTX_NOTEBOOK_CHAT_HAS_AGENT = new RawContextKey('notebookChatAgentRegistered', false, localize('notebookChatAgentRegistered', "Whether a chat agent for notebook is registered")); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts deleted file mode 100644 index 205d040c15b..00000000000 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts +++ /dev/null @@ -1,949 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Dimension, IFocusTracker, WindowIntervalTimer, getWindow, scheduleAtNextAnimationFrame, trackFocus } from '../../../../../../base/browser/dom.js'; -import { CancelablePromise, DeferredPromise, Queue, createCancelablePromise, disposableTimeout } from '../../../../../../base/common/async.js'; -import { CancellationToken, CancellationTokenSource } from '../../../../../../base/common/cancellation.js'; -import { Emitter } from '../../../../../../base/common/event.js'; -import { Disposable, DisposableStore, MutableDisposable, toDisposable } from '../../../../../../base/common/lifecycle.js'; -import { LRUCache } from '../../../../../../base/common/map.js'; -import { Schemas } from '../../../../../../base/common/network.js'; -import { MovingAverage } from '../../../../../../base/common/numbers.js'; -import { isEqual } from '../../../../../../base/common/resources.js'; -import { StopWatch } from '../../../../../../base/common/stopwatch.js'; -import { assertType } from '../../../../../../base/common/types.js'; -import { URI } from '../../../../../../base/common/uri.js'; -import { IActiveCodeEditor } from '../../../../../../editor/browser/editorBrowser.js'; -import { CodeEditorWidget } from '../../../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; -import { ISingleEditOperation } from '../../../../../../editor/common/core/editOperation.js'; -import { Position } from '../../../../../../editor/common/core/position.js'; -import { Selection } from '../../../../../../editor/common/core/selection.js'; -import { TextEdit } from '../../../../../../editor/common/languages.js'; -import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; -import { ICursorStateComputer, ITextModel } from '../../../../../../editor/common/model.js'; -import { IEditorWorkerService } from '../../../../../../editor/common/services/editorWorker.js'; -import { IModelService } from '../../../../../../editor/common/services/model.js'; -import { localize } from '../../../../../../nls.js'; -import { IContextKey, IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; -import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; -import { IStorageService, StorageScope, StorageTarget } from '../../../../../../platform/storage/common/storage.js'; -import { ChatModel, IChatModel } from '../../../../chat/common/chatModel.js'; -import { IChatService } from '../../../../chat/common/chatService.js'; -import { countWords } from '../../../../chat/common/chatWordCounter.js'; -import { ChatAgentLocation } from '../../../../chat/common/constants.js'; -import { ProgressingEditsOptions } from '../../../../inlineChat/browser/inlineChatStrategies.js'; -import { InlineChatWidget } from '../../../../inlineChat/browser/inlineChatWidget.js'; -import { asProgressiveEdit, performAsyncTextEdit } from '../../../../inlineChat/browser/utils.js'; -import { CellKind } from '../../../common/notebookCommon.js'; -import { INotebookExecutionStateService, NotebookExecutionType } from '../../../common/notebookExecutionStateService.js'; -import { ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebookViewZone } from '../../notebookBrowser.js'; -import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; -import { insertCell, runDeleteAction } from '../cellOperations.js'; -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_WIDGET_STATUS } from './notebookChatContext.js'; - -class NotebookChatWidget extends Disposable implements INotebookViewZone { - set afterModelPosition(afterModelPosition: number) { - this.notebookViewZone.afterModelPosition = afterModelPosition; - } - - get afterModelPosition(): number { - return this.notebookViewZone.afterModelPosition; - } - - set heightInPx(heightInPx: number) { - this.notebookViewZone.heightInPx = heightInPx; - } - - get heightInPx(): number { - return this.notebookViewZone.heightInPx; - } - - private _editingCell: ICellViewModel | null = null; - - get editingCell() { - return this._editingCell; - } - - constructor( - private readonly _notebookEditor: INotebookEditor, - readonly id: string, - readonly notebookViewZone: INotebookViewZone, - readonly domNode: HTMLElement, - readonly widgetContainer: HTMLElement, - readonly inlineChatWidget: InlineChatWidget, - readonly parentEditor: CodeEditorWidget, - private readonly _languageService: ILanguageService, - ) { - super(); - - const updateHeight = () => { - if (this.heightInPx === inlineChatWidget.contentHeight) { - return; - } - - this.heightInPx = inlineChatWidget.contentHeight; - this._notebookEditor.changeViewZones(accessor => { - accessor.layoutZone(id); - }); - this._layoutWidget(inlineChatWidget, widgetContainer); - }; - - this._register(inlineChatWidget.onDidChangeHeight(() => { - updateHeight(); - })); - - this._register(inlineChatWidget.chatWidget.onDidChangeHeight(() => { - updateHeight(); - })); - - this.heightInPx = inlineChatWidget.contentHeight; - this._layoutWidget(inlineChatWidget, widgetContainer); - } - - layout() { - this._layoutWidget(this.inlineChatWidget, this.widgetContainer); - } - - restoreEditingCell(initEditingCell: ICellViewModel) { - this._editingCell = initEditingCell; - - const decorationIds = this._notebookEditor.deltaCellDecorations([], [{ - handle: this._editingCell.handle, - options: { className: 'nb-chatGenerationHighlight', outputClassName: 'nb-chatGenerationHighlight' } - }]); - - this._register(toDisposable(() => { - this._notebookEditor.deltaCellDecorations(decorationIds, []); - })); - } - - 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: ICellViewModel; editor: IActiveCodeEditor } | undefined> { - if (this._editingCell) { - const codeEditor = this._notebookEditor.codeEditors.find(ce => ce[0] === this._editingCell)?.[1]; - if (codeEditor?.hasModel()) { - return { - cell: this._editingCell, - editor: codeEditor - }; - } else { - return undefined; - } - } - - if (!this._notebookEditor.hasModel()) { - 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.revealFirstLineIfOutsideViewport(this._editingCell); - - // update decoration - const decorationIds = this._notebookEditor.deltaCellDecorations([], [{ - handle: this._editingCell.handle, - options: { className: 'nb-chatGenerationHighlight', outputClassName: 'nb-chatGenerationHighlight' } - }]); - - this._register(toDisposable(() => { - this._notebookEditor.deltaCellDecorations(decorationIds, []); - })); - - if (widgetHasFocus) { - this.focus(); - } - - const codeEditor = this._notebookEditor.codeEditors.find(ce => ce[0] === this._editingCell)?.[1]; - if (codeEditor?.hasModel()) { - return { - cell: this._editingCell, - editor: codeEditor - }; - } - - return undefined; - } - - async discardChange() { - if (this._notebookEditor.hasModel() && this._editingCell) { - // remove the cell from the notebook - runDeleteAction(this._notebookEditor, this._editingCell); - } - } - - private _layoutWidget(inlineChatWidget: InlineChatWidget, widgetContainer: HTMLElement) { - const layoutConfiguration = this._notebookEditor.notebookOptions.getLayoutConfiguration(); - const rightMargin = layoutConfiguration.cellRightMargin; - const leftMargin = this._notebookEditor.notebookOptions.getCellEditorContainerLeftMargin(); - const maxWidth = 640; - const width = Math.min(maxWidth, this._notebookEditor.getLayoutInfo().width - leftMargin - rightMargin); - - inlineChatWidget.layout(new Dimension(width, this.heightInPx)); - inlineChatWidget.domNode.style.width = `${width}px`; - widgetContainer.style.left = `${leftMargin}px`; - } - - override dispose() { - this._notebookEditor.changeViewZones(accessor => { - accessor.removeZone(this.id); - }); - this.domNode.remove(); - super.dispose(); - } -} - -export interface INotebookCellTextModelLike { uri: URI; viewType: string } -class NotebookCellTextModelLikeId { - static str(k: INotebookCellTextModelLike): string { - return `${k.viewType}/${k.uri.toString()}`; - } - static obj(s: string): INotebookCellTextModelLike { - const idx = s.indexOf('/'); - return { - viewType: s.substring(0, idx), - uri: URI.parse(s.substring(idx + 1)) - }; - } -} - -export class NotebookChatController extends Disposable implements INotebookEditorContribution { - static id: string = 'workbench.notebook.chatController'; - static counter: number = 0; - - 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 _promptCache = new LRUCache(1000, 0.7); - private readonly _onDidChangePromptCache = this._register(new Emitter<{ cell: URI }>()); - readonly onDidChangePromptCache = this._onDidChangePromptCache.event; - - private _strategy: EditStrategy | undefined; - private _sessionCtor: CancelablePromise | undefined; - private _activeRequestCts?: CancellationTokenSource; - private readonly _ctxHasActiveRequest: IContextKey; - private readonly _ctxCellWidgetFocused: IContextKey; - private readonly _ctxUserDidEdit: IContextKey; - private readonly _ctxOuterFocusPosition: IContextKey<'above' | 'below' | ''>; - private readonly _userEditingDisposables = this._register(new DisposableStore()); - private readonly _widgetDisposableStore = this._register(new DisposableStore()); - private _focusTracker: IFocusTracker | undefined; - private _widget: NotebookChatWidget | undefined; - - private readonly _model: MutableDisposable = this._register(new MutableDisposable()); - constructor( - private readonly _notebookEditor: INotebookEditor, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, - @IModelService private readonly _modelService: IModelService, - @ILanguageService private readonly _languageService: ILanguageService, - @INotebookExecutionStateService private _executionStateService: INotebookExecutionStateService, - @IStorageService private readonly _storageService: IStorageService, - @IChatService private readonly _chatService: IChatService - ) { - super(); - this._ctxHasActiveRequest = CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST.bindTo(this._contextKeyService); - this._ctxCellWidgetFocused = CTX_NOTEBOOK_CELL_CHAT_FOCUSED.bindTo(this._contextKeyService); - this._ctxUserDidEdit = CTX_NOTEBOOK_CHAT_USER_DID_EDIT.bindTo(this._contextKeyService); - 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() { - this._register(this._notebookEditor.onDidChangeFocus(() => { - if (!this._widget) { - this._ctxOuterFocusPosition.set(''); - return; - } - - const widgetIndex = this._widget.afterModelPosition; - const focus = this._notebookEditor.getFocus().start; - - if (focus + 1 === widgetIndex) { - this._ctxOuterFocusPosition.set('above'); - } else if (focus === widgetIndex) { - this._ctxOuterFocusPosition.set('below'); - } else { - this._ctxOuterFocusPosition.set(''); - } - })); - } - - run(index: number, input: string | undefined, autoSend: boolean | undefined): void { - if (this._widget) { - if (this._widget.afterModelPosition !== index) { - const window = getWindow(this._widget.domNode); - this._disposeWidget(); - - scheduleAtNextAnimationFrame(window, () => { - this._createWidget(index, input, autoSend, undefined); - }); - } - - return; - } - - this._createWidget(index, input, autoSend, undefined); - // TODO: reveal widget to the center if it's out of the viewport - } - - restore(editingCell: ICellViewModel, input: string) { - if (!this._notebookEditor.hasModel()) { - return; - } - - const index = this._notebookEditor.textModel.cells.indexOf(editingCell.model); - - if (index < 0) { - return; - } - - if (this._widget) { - if (this._widget.afterModelPosition !== index) { - this._disposeWidget(); - const window = getWindow(this._widget.domNode); - - scheduleAtNextAnimationFrame(window, () => { - this._createWidget(index, input, false, editingCell); - }); - } - - return; - } - - this._createWidget(index, input, false, editingCell); - } - - private _disposeWidget() { - this._widget?.dispose(); - this._widget = undefined; - this._widgetDisposableStore.clear(); - - this._historyOffset = -1; - this._historyCandidate = ''; - } - - - private _createWidget(index: number, input: string | undefined, autoSend: boolean | undefined, initEditingCell: ICellViewModel | undefined) { - if (!this._notebookEditor.hasModel()) { - return; - } - - // Clear the widget if it's already there - this._widgetDisposableStore.clear(); - - const viewZoneContainer = document.createElement('div'); - viewZoneContainer.classList.add('monaco-editor'); - const widgetContainer = document.createElement('div'); - widgetContainer.style.position = 'absolute'; - viewZoneContainer.appendChild(widgetContainer); - - this._focusTracker = this._widgetDisposableStore.add(trackFocus(viewZoneContainer)); - this._widgetDisposableStore.add(this._focusTracker.onDidFocus(() => { - this._updateNotebookEditorFocusNSelections(); - })); - - const fakeParentEditorElement = document.createElement('div'); - - const fakeParentEditor = this._widgetDisposableStore.add(this._instantiationService.createInstance( - CodeEditorWidget, - fakeParentEditorElement, - { - }, - { isSimpleWidget: true } - )); - - const inputBoxFragment = `notebook-chat-input-${NotebookChatController.counter++}`; - const notebookUri = this._notebookEditor.textModel.uri; - const inputUri = notebookUri.with({ scheme: Schemas.untitled, fragment: inputBoxFragment }); - const result: ITextModel = this._modelService.createModel('', null, inputUri, false); - fakeParentEditor.setModel(result); - - const inlineChatWidget = this._widgetDisposableStore.add(this._instantiationService.createInstance( - InlineChatWidget, - { - location: ChatAgentLocation.Notebook, - resolveData: () => { - const sessionInputUri = this.getSessionInputUri(); - if (!sessionInputUri) { - return undefined; - } - return { - type: ChatAgentLocation.Notebook, - sessionInputUri - }; - } - }, - { - statusMenuId: MENU_CELL_CHAT_WIDGET_STATUS, - chatWidgetViewOptions: { - rendererOptions: { - renderTextEditsAsSummary: (uri) => { - return isEqual(uri, this._widget?.parentEditor.getModel()?.uri) - || isEqual(uri, this._notebookEditor.textModel?.uri); - } - }, - menus: { - telemetrySource: 'notebook-generate-cell' - } - } - } - )); - inlineChatWidget.placeholder = localize('default.placeholder', "Ask or edit in context"); - inlineChatWidget.updateInfo(localize('welcome.1', "AI-generated code may be incorrect")); - widgetContainer.appendChild(inlineChatWidget.domNode); - - - this._notebookEditor.changeViewZones(accessor => { - const notebookViewZone = { - afterModelPosition: index, - heightInPx: 80, - domNode: viewZoneContainer - }; - - const id = accessor.addZone(notebookViewZone); - this._scrollWidgetIntoView(index); - - this._widget = new NotebookChatWidget( - this._notebookEditor, - id, - notebookViewZone, - viewZoneContainer, - widgetContainer, - inlineChatWidget, - fakeParentEditor, - this._languageService - ); - - if (initEditingCell) { - this._widget.restoreEditingCell(initEditingCell); - this._updateUserEditingState(); - } - - this._ctxCellWidgetFocused.set(true); - - disposableTimeout(() => { - this._focusWidget(); - }, 0, this._store); - - this._sessionCtor = createCancelablePromise(async token => { - await this._startSession(token); - assertType(this._model.value); - const model = this._model.value; - this._widget?.inlineChatWidget.setChatModel(model); - - if (fakeParentEditor.hasModel()) { - - if (this._widget) { - this._focusWidget(); - } - - if (this._widget && input) { - this._widget.inlineChatWidget.value = input; - - if (autoSend) { - this.acceptInput(); - } - } - } - }); - }); - } - - private async _startSession(token: CancellationToken) { - if (!this._model.value) { - this._model.value = this._chatService.startSession(ChatAgentLocation.EditorInline, token); - - if (!this._model.value) { - throw new Error('Failed to start chat session'); - } - } - - this._strategy = new EditStrategy(); - } - - private _scrollWidgetIntoView(index: number) { - if (index === 0 || this._notebookEditor.getLength() === 0) { - // the cell is at the beginning of the notebook - this._notebookEditor.revealOffsetInCenterIfOutsideViewport(0); - } else { - // the cell is at the end of the notebook - const previousCell = this._notebookEditor.cellAt(Math.min(index - 1, this._notebookEditor.getLength() - 1)); - if (previousCell) { - const cellTop = this._notebookEditor.getAbsoluteTopOfElement(previousCell); - const cellHeight = this._notebookEditor.getHeightOfElement(previousCell); - - this._notebookEditor.revealOffsetInCenterIfOutsideViewport(cellTop + cellHeight + 48 /** center of the dialog */); - } - } - } - - private _focusWidget() { - if (!this._widget) { - return; - } - - this._updateNotebookEditorFocusNSelections(); - this._widget.focus(); - } - - private _updateNotebookEditorFocusNSelections() { - if (!this._widget) { - return; - } - - this._widget.updateNotebookEditorFocusNSelections(); - } - - hasSession(chatModel: IChatModel) { - return this._model.value === chatModel; - } - - getSessionInputUri() { - return this._widget?.parentEditor.getModel()?.uri; - } - - async acceptInput() { - assertType(this._widget); - await this._sessionCtor; - assertType(this._model.value); - assertType(this._strategy); - - const lastInput = this._widget.inlineChatWidget.value; - this._historyUpdate(lastInput); - - const editor = this._widget.parentEditor; - const textModel = editor.getModel(); - - if (!editor.hasModel() || !textModel) { - return; - } - - if (this._widget.editingCell && this._widget.editingCell.textBuffer.getLength() > 0) { - // it already contains some text, clear it - const ref = await this._widget.editingCell.resolveTextModel(); - ref.setValue(''); - } - - const editingCellIndex = this._widget.editingCell ? this._notebookEditor.getCellIndex(this._widget.editingCell) : undefined; - if (editingCellIndex !== undefined) { - this._notebookEditor.setSelections([{ - start: editingCellIndex, - end: editingCellIndex + 1 - }]); - } else { - // Update selection to the widget index - this._notebookEditor.setSelections([{ - start: this._widget.afterModelPosition, - end: this._widget.afterModelPosition - }]); - } - - this._ctxHasActiveRequest.set(true); - - this._activeRequestCts?.cancel(); - this._activeRequestCts = new CancellationTokenSource(); - - const store = new DisposableStore(); - - try { - this._ctxHasActiveRequest.set(true); - - const progressiveEditsQueue = new Queue(); - const progressiveEditsClock = StopWatch.create(); - const progressiveEditsAvgDuration = new MovingAverage(); - const progressiveEditsCts = new CancellationTokenSource(this._activeRequestCts.token); - - const responsePromise = new DeferredPromise(); - const response = await this._widget.inlineChatWidget.chatWidget.acceptInput(); - if (response) { - let lastLength = 0; - - store.add(response.onDidChange(e => { - if (response.isCanceled) { - progressiveEditsCts.cancel(); - responsePromise.complete(); - return; - } - - if (response.isComplete) { - responsePromise.complete(); - return; - } - - const edits = response.response.value.map(part => { - if (part.kind === 'textEditGroup' - // && isEqual(part.uri, this._session?.textModelN.uri) - ) { - return part.edits; - } else { - return []; - } - }).flat(); - - const newEdits = edits.slice(lastLength); - // console.log('NEW edits', newEdits, edits); - if (newEdits.length === 0) { - return; // NO change - } - lastLength = edits.length; - progressiveEditsAvgDuration.update(progressiveEditsClock.elapsed()); - progressiveEditsClock.reset(); - - progressiveEditsQueue.queue(async () => { - for (const edits of newEdits) { - await this._makeChanges(edits, { - duration: progressiveEditsAvgDuration.value, - token: progressiveEditsCts.token - }); - } - }); - })); - } - - await responsePromise.p; - await progressiveEditsQueue.whenIdle(); - - this._userEditingDisposables.clear(); - // monitor user edits - const editingCell = this._widget.getEditingCell(); - if (editingCell) { - this._userEditingDisposables.add(editingCell.model.onDidChangeContent(() => this._updateUserEditingState())); - this._userEditingDisposables.add(editingCell.model.onDidChangeLanguage(() => this._updateUserEditingState())); - this._userEditingDisposables.add(editingCell.model.onDidChangeMetadata(() => this._updateUserEditingState())); - this._userEditingDisposables.add(editingCell.model.onDidChangeInternalMetadata(() => this._updateUserEditingState())); - this._userEditingDisposables.add(editingCell.model.onDidChangeOutputs(() => this._updateUserEditingState())); - this._userEditingDisposables.add(this._executionStateService.onDidChangeExecution(e => { - if (e.type === NotebookExecutionType.cell && e.affectsCell(editingCell.uri)) { - this._updateUserEditingState(); - } - })); - } - } catch (e) { - } finally { - store.dispose(); - - this._ctxHasActiveRequest.set(false); - this._widget.inlineChatWidget.updateInfo(''); - this._widget.inlineChatWidget.updateToolbar(true); - } - } - - private async _makeChanges(edits: TextEdit[], opts: ProgressingEditsOptions | undefined) { - assertType(this._strategy); - assertType(this._widget); - - const editingCell = await this._widget.getOrCreateEditingCell(); - - if (!editingCell) { - return; - } - - const editor = editingCell.editor; - - const moreMinimalEdits = await this._editorWorkerService.computeMoreMinimalEdits(editor.getModel().uri, edits); - // this._log('edits from PROVIDER and after making them MORE MINIMAL', this._activeSession.provider.debugName, edits, moreMinimalEdits); - - if (moreMinimalEdits?.length === 0) { - // nothing left to do - return; - } - - const actualEdits = !opts && moreMinimalEdits ? moreMinimalEdits : edits; - const editOperations = actualEdits.map(TextEdit.asEditOperation); - - try { - if (opts) { - await this._strategy.makeProgressiveChanges(editor, editOperations, opts); - } else { - await this._strategy.makeChanges(editor, editOperations); - } - } finally { - } - } - - private _updateUserEditingState() { - this._ctxUserDidEdit.set(true); - } - - async acceptSession() { - assertType(this._model); - assertType(this._strategy); - - const editor = this._widget?.parentEditor; - if (!editor?.hasModel()) { - return; - } - - const editingCell = this._widget?.getEditingCell(); - - if (editingCell && this._notebookEditor.hasModel()) { - const cellId = NotebookCellTextModelLikeId.str({ uri: editingCell.uri, viewType: this._notebookEditor.textModel.viewType }); - if (this._widget?.inlineChatWidget.value) { - this._promptCache.set(cellId, this._widget.inlineChatWidget.value); - } - this._onDidChangePromptCache.fire({ cell: editingCell.uri }); - } - - try { - this._model.clear(); - } catch (_err) { } - - this.dismiss(false); - } - - async focusAbove() { - if (!this._widget) { - return; - } - - const index = this._widget.afterModelPosition; - const prev = index - 1; - if (prev < 0) { - return; - } - - const cell = this._notebookEditor.cellAt(prev); - if (!cell) { - return; - } - - await this._notebookEditor.focusNotebookCell(cell, 'editor'); - } - - async focusNext() { - if (!this._widget) { - return; - } - - const index = this._widget.afterModelPosition; - const cell = this._notebookEditor.cellAt(index); - if (!cell) { - return; - } - - await this._notebookEditor.focusNotebookCell(cell, 'editor'); - } - - hasFocus() { - return this._widget?.hasFocus() ?? false; - } - - focus() { - this._focusWidget(); - } - - focusNearestWidget(index: number, direction: 'above' | 'below') { - switch (direction) { - case 'above': - if (this._widget?.afterModelPosition === index) { - this._focusWidget(); - } - break; - case 'below': - if (this._widget?.afterModelPosition === index + 1) { - this._focusWidget(); - } - break; - default: - break; - } - } - - 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) { - this._activeRequestCts?.cancel(); - } - - getEditingCell() { - return this._widget?.getEditingCell(); - } - - discard() { - this._activeRequestCts?.cancel(); - this._widget?.discardChange(); - this.dismiss(true); - } - - dismiss(discard: boolean) { - const widget = this._widget; - const widgetIndex = widget?.afterModelPosition; - const currentFocus = this._notebookEditor.getFocus(); - const isWidgetFocused = currentFocus.start === widgetIndex && currentFocus.end === widgetIndex; - - if (widget && isWidgetFocused) { - // change focus only when the widget is focused - const editingCell = widget.getEditingCell(); - const shouldFocusEditingCell = editingCell && !discard; - const shouldFocusTopCell = widgetIndex === 0 && this._notebookEditor.getLength() > 0; - const shouldFocusAboveCell = widgetIndex !== 0 && this._notebookEditor.cellAt(widgetIndex - 1); - - if (shouldFocusEditingCell) { - this._notebookEditor.focusNotebookCell(editingCell, 'container'); - } else if (shouldFocusTopCell) { - this._notebookEditor.focusNotebookCell(this._notebookEditor.cellAt(0)!, 'container'); - } else if (shouldFocusAboveCell) { - this._notebookEditor.focusNotebookCell(this._notebookEditor.cellAt(widgetIndex - 1)!, 'container'); - } - } - - this._ctxCellWidgetFocused.set(false); - this._ctxUserDidEdit.set(false); - this._sessionCtor?.cancel(); - this._sessionCtor = undefined; - this._model.clear(); - this._widget?.dispose(); - this._widget = undefined; - this._widgetDisposableStore.clear(); - } - - // check if a cell is generated by prompt by checking prompt cache - isCellGeneratedByChat(cell: ICellViewModel) { - if (!this._notebookEditor.hasModel()) { - // no model attached yet - return false; - } - - const cellId = NotebookCellTextModelLikeId.str({ uri: cell.uri, viewType: this._notebookEditor.textModel.viewType }); - return this._promptCache.has(cellId); - } - - // get prompt from cache - getPromptFromCache(cell: ICellViewModel) { - if (!this._notebookEditor.hasModel()) { - // no model attached yet - return undefined; - } - - const cellId = NotebookCellTextModelLikeId.str({ uri: cell.uri, viewType: this._notebookEditor.textModel.viewType }); - return this._promptCache.get(cellId); - } - public override dispose(): void { - this.dismiss(false); - super.dispose(); - } -} - -export class EditStrategy { - private _editCount: number = 0; - - constructor() { - } - - async makeProgressiveChanges(editor: IActiveCodeEditor, edits: ISingleEditOperation[], opts: ProgressingEditsOptions): Promise { - // push undo stop before first edit - if (++this._editCount === 1) { - editor.pushUndoStop(); - } - - 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(editor.getModel(), asProgressiveEdit(new WindowIntervalTimer(), edit, speed, opts.token)); - } - } - - async makeChanges(editor: IActiveCodeEditor, edits: ISingleEditOperation[]): 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; - // this._inlineDiffDecorations.collectEditOperation(edit); - } - return last && [Selection.fromPositions(last)]; - }; - - // push undo stop before first edit - if (++this._editCount === 1) { - editor.pushUndoStop(); - } - editor.executeEdits('inline-chat-live', edits, cursorStateComputerAndInlineDiffCollection); - } -} - - -registerNotebookContribution(NotebookChatController.id, NotebookChatController); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts index 639109ff03b..852f234ca14 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts @@ -18,7 +18,6 @@ import { ServicesAccessor } from '../../../../../platform/instantiation/common/i import { IDebugService } from '../../../debug/common/debug.js'; import { CTX_INLINE_CHAT_FOCUSED } from '../../../inlineChat/common/inlineChat.js'; import { insertCell } from './cellOperations.js'; -import { NotebookChatController } from './chat/notebookChatController.js'; import { CELL_TITLE_CELL_GROUP_ID, CellToolbarOrder, INotebookActionContext, INotebookCellActionContext, INotebookCellToolbarActionContext, INotebookCommandContext, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, NotebookAction, NotebookCellAction, NotebookMultiCellAction, cellExecutionArgs, getContextFromActiveEditor, getContextFromUri, parseMultiCellExecutionArgs } from './coreActions.js'; import { CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, IActiveNotebookEditor, ICellViewModel, IFocusNotebookCellOptions, ScrollToRevealBehavior } from '../notebookBrowser.js'; import * as icons from '../notebookIcons.js'; @@ -297,21 +296,6 @@ 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, editorService); } }); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts index b7bdf36f347..7f798a85f97 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts @@ -17,7 +17,6 @@ import { INotebookActionContext, NotebookAction } from './coreActions.js'; import { NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_EDITOR_EDITABLE } from '../../common/notebookContextKeys.js'; import { CellViewModel } from '../viewModel/notebookViewModelImpl.js'; import { CellKind, NotebookSetting } from '../../common/notebookCommon.js'; -import { CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION } from './chat/notebookChatContext.js'; import { INotebookKernelHistoryService } from '../../common/notebookKernelService.js'; const INSERT_CODE_CELL_ABOVE_COMMAND_ID = 'notebook.cell.insertCodeCellAbove'; @@ -114,7 +113,7 @@ registerAction2(class InsertCodeCellBelowAction extends InsertCellCommand { title: localize('notebookActions.insertCodeCellBelow', "Insert Code Cell Below"), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.Enter, - when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, InputFocusedContext.toNegated(), CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.isEqualTo('')), + when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, InputFocusedContext.toNegated()), weight: KeybindingWeight.WorkbenchContrib }, menu: { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts index 1ea7c34b6bc..351d117ed2f 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts @@ -7,14 +7,13 @@ import { Disposable, DisposableStore } from '../../../../../../base/common/lifec import { autorun } from '../../../../../../base/common/observable.js'; import { IContextKey, IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; -import { NotebookChatController } from '../../controller/chat/notebookChatController.js'; import { CellEditState, CellFocusMode, ICellViewModel, INotebookEditorDelegate } from '../../notebookBrowser.js'; import { CellViewModelStateChangeEvent } from '../../notebookViewEvents.js'; import { CellContentPart } from '../cellPart.js'; import { CodeCellViewModel } from '../../viewModel/codeCellViewModel.js'; import { MarkupCellViewModel } from '../../viewModel/markupCellViewModel.js'; import { NotebookCellExecutionState } from '../../../common/notebookCommon.js'; -import { NotebookCellExecutionStateContext, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LINE_NUMBERS, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_RESOURCE, NOTEBOOK_CELL_TYPE, NOTEBOOK_CELL_GENERATED_BY_CHAT, NOTEBOOK_CELL_HAS_ERROR_DIAGNOSTICS } from '../../../common/notebookContextKeys.js'; +import { NotebookCellExecutionStateContext, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LINE_NUMBERS, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_RESOURCE, NOTEBOOK_CELL_TYPE, NOTEBOOK_CELL_HAS_ERROR_DIAGNOSTICS } from '../../../common/notebookContextKeys.js'; import { INotebookExecutionStateService, NotebookExecutionType } from '../../../common/notebookExecutionStateService.js'; export class CellContextKeyPart extends CellContentPart { @@ -47,7 +46,6 @@ export class CellContextKeyManager extends Disposable { private cellOutputCollapsed!: IContextKey; private cellLineNumbers!: IContextKey<'on' | 'off' | 'inherit'>; private cellResource!: IContextKey; - private cellGeneratedByChat!: IContextKey; private cellHasErrorDiagnostics!: IContextKey; private markdownEditMode!: IContextKey; @@ -74,7 +72,6 @@ export class CellContextKeyManager extends Disposable { this.cellContentCollapsed = NOTEBOOK_CELL_INPUT_COLLAPSED.bindTo(this._contextKeyService); this.cellOutputCollapsed = NOTEBOOK_CELL_OUTPUT_COLLAPSED.bindTo(this._contextKeyService); this.cellLineNumbers = NOTEBOOK_CELL_LINE_NUMBERS.bindTo(this._contextKeyService); - this.cellGeneratedByChat = NOTEBOOK_CELL_GENERATED_BY_CHAT.bindTo(this._contextKeyService); this.cellResource = NOTEBOOK_CELL_RESOURCE.bindTo(this._contextKeyService); this.cellHasErrorDiagnostics = NOTEBOOK_CELL_HAS_ERROR_DIAGNOSTICS.bindTo(this._contextKeyService); @@ -121,21 +118,10 @@ export class CellContextKeyManager extends Disposable { this.updateForEditState(); this.updateForCollapseState(); this.updateForOutputs(); - this.updateForChat(); this.cellLineNumbers.set(this.element!.lineNumbers); this.cellResource.set(this.element!.uri.toString()); }); - - const chatController = NotebookChatController.get(this.notebookEditor); - - if (chatController) { - this.elementDisposables.add(chatController.onDidChangePromptCache(e => { - if (e.cell.toString() === this.element!.uri.toString()) { - this.updateForChat(); - } - })); - } } private onDidChangeState(e: CellViewModelStateChangeEvent) { @@ -236,15 +222,4 @@ export class CellContextKeyManager extends Disposable { this.cellHasOutputs.set(false); } } - - private updateForChat() { - const chatController = NotebookChatController.get(this.notebookEditor); - - if (!chatController || !this.element) { - this.cellGeneratedByChat.set(false); - return; - } - - this.cellGeneratedByChat.set(chatController.isCellGeneratedByChat(this.element)); - } } diff --git a/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts b/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts index 57cb304453e..f4eb63f37dc 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts @@ -55,7 +55,6 @@ export const NOTEBOOK_CELL_OUTPUT_MIMETYPE = new RawContextKey('notebook export const NOTEBOOK_CELL_INPUT_COLLAPSED = new RawContextKey('notebookCellInputIsCollapsed', false); export const NOTEBOOK_CELL_OUTPUT_COLLAPSED = new RawContextKey('notebookCellOutputIsCollapsed', false); export const NOTEBOOK_CELL_RESOURCE = new RawContextKey('notebookCellResource', ''); -export const NOTEBOOK_CELL_GENERATED_BY_CHAT = new RawContextKey('notebookCellGenerateByChat', false); export const NOTEBOOK_CELL_HAS_ERROR_DIAGNOSTICS = new RawContextKey('notebookCellHasErrorDiagnostics', false); export const NOTEBOOK_CELL_OUTPUT_MIME_TYPE_LIST_FOR_CHAT = new RawContextKey('notebookCellOutputMimeTypeListForChat', []); From d9136e807b85c23a009c0937de5bb9b2019fa9fe Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt <2644648+TylerLeonhardt@users.noreply.github.com> Date: Mon, 6 Oct 2025 15:19:59 -0700 Subject: [PATCH 4/5] Enable WWW-Authenticate Step-up (#269884) * Enable WWW-Authenticate Step-up Fixes https://github.com/microsoft/vscode/issues/269883 * Add scopes back in --- src/vs/base/common/oauth.ts | 161 +++++++++++++++--- src/vs/base/test/common/oauth.test.ts | 12 ++ src/vs/workbench/api/browser/mainThreadMcp.ts | 5 +- .../workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostMcp.ts | 71 +++++--- 5 files changed, 203 insertions(+), 48 deletions(-) diff --git a/src/vs/base/common/oauth.ts b/src/vs/base/common/oauth.ts index 4b332685a8c..db7d985de4c 100644 --- a/src/vs/base/common/oauth.ts +++ b/src/vs/base/common/oauth.ts @@ -259,6 +259,107 @@ export interface IAuthorizationServerMetadata { code_challenge_methods_supported?: string[]; } +/** + * Request for the dynamic client registration endpoint as per RFC 7591. + */ +export interface IAuthorizationDynamicClientRegistrationRequest { + /** + * OPTIONAL. Array of redirection URI strings for use in redirect-based flows + * such as the authorization code and implicit flows. + */ + redirect_uris?: string[]; + + /** + * OPTIONAL. String indicator of the requested authentication method for the token endpoint. + * Values: "none", "client_secret_post", "client_secret_basic". + * Default is "client_secret_basic". + */ + token_endpoint_auth_method?: string; + + /** + * OPTIONAL. Array of OAuth 2.0 grant type strings that the client can use at the token endpoint. + * Default is ["authorization_code"]. + */ + grant_types?: string[]; + + /** + * OPTIONAL. Array of the OAuth 2.0 response type strings that the client can use at the authorization endpoint. + * Default is ["code"]. + */ + response_types?: string[]; + + /** + * OPTIONAL. Human-readable string name of the client to be presented to the end-user during authorization. + */ + client_name?: string; + + /** + * OPTIONAL. URL string of a web page providing information about the client. + */ + client_uri?: string; + + /** + * OPTIONAL. URL string that references a logo for the client. + */ + logo_uri?: string; + + /** + * OPTIONAL. String containing a space-separated list of scope values that the client can use when requesting access tokens. + */ + scope?: string; + + /** + * OPTIONAL. Array of strings representing ways to contact people responsible for this client, typically email addresses. + */ + contacts?: string[]; + + /** + * OPTIONAL. URL string that points to a human-readable terms of service document for the client. + */ + tos_uri?: string; + + /** + * OPTIONAL. URL string that points to a human-readable privacy policy document. + */ + policy_uri?: string; + + /** + * OPTIONAL. URL string referencing the client's JSON Web Key (JWK) Set document. + */ + jwks_uri?: string; + + /** + * OPTIONAL. Client's JSON Web Key Set document value. + */ + jwks?: object; + + /** + * OPTIONAL. A unique identifier string assigned by the client developer or software publisher. + */ + software_id?: string; + + /** + * OPTIONAL. A version identifier string for the client software. + */ + software_version?: string; + + /** + * OPTIONAL. A software statement containing client metadata values about the client software as claims. + */ + software_statement?: string; + + /** + * OPTIONAL. Application type. Usually "native" for OAuth clients. + * https://openid.net/specs/openid-connect-registration-1_0.html + */ + application_type?: 'native' | 'web' | string; + + /** + * OPTIONAL. Additional metadata fields as defined by extensions. + */ + [key: string]: unknown; +} + /** * Response from the dynamic client registration endpoint. */ @@ -749,33 +850,35 @@ export async function fetchDynamicRegistration(serverMetadata: IAuthorizationSer if (!serverMetadata.registration_endpoint) { throw new Error('Server does not support dynamic registration'); } + + const requestBody: IAuthorizationDynamicClientRegistrationRequest = { + client_name: clientName, + client_uri: 'https://code.visualstudio.com', + grant_types: serverMetadata.grant_types_supported + ? serverMetadata.grant_types_supported.filter(gt => grantTypesSupported.includes(gt)) + : grantTypesSupported, + response_types: ['code'], + redirect_uris: [ + 'https://insiders.vscode.dev/redirect', + 'https://vscode.dev/redirect', + 'http://127.0.0.1/', + // Added these for any server that might do + // only exact match on the redirect URI even + // though the spec says it should not care + // about the port. + `http://127.0.0.1:${DEFAULT_AUTH_FLOW_PORT}/` + ], + scope: scopes?.join(AUTH_SCOPE_SEPARATOR), + token_endpoint_auth_method: 'none', + application_type: 'native' + }; + const response = await fetch(serverMetadata.registration_endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - client_name: clientName, - client_uri: 'https://code.visualstudio.com', - grant_types: serverMetadata.grant_types_supported - ? serverMetadata.grant_types_supported.filter(gt => grantTypesSupported.includes(gt)) - : grantTypesSupported, - response_types: ['code'], - redirect_uris: [ - 'https://insiders.vscode.dev/redirect', - 'https://vscode.dev/redirect', - 'http://127.0.0.1/', - // Added these for any server that might do - // only exact match on the redirect URI even - // though the spec says it should not care - // about the port. - `http://127.0.0.1:${DEFAULT_AUTH_FLOW_PORT}/` - ], - scope: scopes?.join(AUTH_SCOPE_SEPARATOR), - token_endpoint_auth_method: 'none', - // https://openid.net/specs/openid-connect-registration-1_0.html - application_type: 'native' - }) + body: JSON.stringify(requestBody) }); if (!response.ok) { @@ -936,17 +1039,25 @@ export function getClaimsFromJWT(token: string): IAuthorizationJWTClaims { * Checks if two scope lists are equivalent, regardless of order. * This is useful for comparing OAuth scopes where the order should not matter. * - * @param scopes1 First list of scopes to compare - * @param scopes2 Second list of scopes to compare + * @param scopes1 First list of scopes to compare (can be undefined) + * @param scopes2 Second list of scopes to compare (can be undefined) * @returns true if the scope lists contain the same scopes (order-independent), false otherwise * * @example * ```typescript * scopesMatch(['read', 'write'], ['write', 'read']) // Returns: true * scopesMatch(['read'], ['write']) // Returns: false + * scopesMatch(undefined, undefined) // Returns: true + * scopesMatch(['read'], undefined) // Returns: false * ``` */ -export function scopesMatch(scopes1: readonly string[], scopes2: readonly string[]): boolean { +export function scopesMatch(scopes1: readonly string[] | undefined, scopes2: readonly string[] | undefined): boolean { + if (scopes1 === scopes2) { + return true; + } + if (!scopes1 || !scopes2) { + return false; + } if (scopes1.length !== scopes2.length) { return false; } diff --git a/src/vs/base/test/common/oauth.test.ts b/src/vs/base/test/common/oauth.test.ts index acdcc429a06..d846ac2d929 100644 --- a/src/vs/base/test/common/oauth.test.ts +++ b/src/vs/base/test/common/oauth.test.ts @@ -310,6 +310,18 @@ suite('OAuth', () => { const scopes2 = ['scope2', 'scope1', 'scope1']; assert.strictEqual(scopesMatch(scopes1, scopes2), true); }); + + test('scopesMatch should handle undefined values', () => { + assert.strictEqual(scopesMatch(undefined, undefined), true); + assert.strictEqual(scopesMatch(['read'], undefined), false); + assert.strictEqual(scopesMatch(undefined, ['write']), false); + }); + + test('scopesMatch should handle mixed undefined and empty arrays', () => { + assert.strictEqual(scopesMatch([], undefined), false); + assert.strictEqual(scopesMatch(undefined, []), false); + assert.strictEqual(scopesMatch([], []), true); + }); }); suite('Utility Functions', () => { diff --git a/src/vs/workbench/api/browser/mainThreadMcp.ts b/src/vs/workbench/api/browser/mainThreadMcp.ts index 2dcf4444bb0..3f1cc964e13 100644 --- a/src/vs/workbench/api/browser/mainThreadMcp.ts +++ b/src/vs/workbench/api/browser/mainThreadMcp.ts @@ -178,14 +178,13 @@ export class MainThreadMcp extends Disposable implements MainThreadMcpShape { this._servers.get(id)?.pushMessage(message); } - async $getTokenFromServerMetadata(id: number, authServerComponents: UriComponents, serverMetadata: IAuthorizationServerMetadata, resourceMetadata: IAuthorizationProtectedResourceMetadata | undefined, errorOnUserInteraction?: boolean): Promise { + async $getTokenFromServerMetadata(id: number, authServerComponents: UriComponents, serverMetadata: IAuthorizationServerMetadata, resourceMetadata: IAuthorizationProtectedResourceMetadata | undefined, scopes: string[] | undefined, errorOnUserInteraction?: boolean): Promise { const server = this._serverDefinitions.get(id); if (!server) { return undefined; } - const authorizationServer = URI.revive(authServerComponents); - const scopesSupported = resourceMetadata?.scopes_supported || serverMetadata.scopes_supported || []; + const scopesSupported = scopes ?? resourceMetadata?.scopes_supported ?? serverMetadata.scopes_supported ?? []; let providerId = await this._authenticationService.getOrActivateProviderIdForServer(authorizationServer); if (!providerId) { const provider = await this._authenticationService.createDynamicAuthenticationProvider(authorizationServer, serverMetadata, resourceMetadata); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 4821f351962..f7ca67e2a19 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -3041,7 +3041,7 @@ export interface MainThreadMcpShape { $onDidReceiveMessage(id: number, message: string): void; $upsertMcpCollection(collection: McpCollectionDefinition.FromExtHost, servers: McpServerDefinition.Serialized[]): void; $deleteMcpCollection(collectionId: string): void; - $getTokenFromServerMetadata(id: number, authorizationServer: UriComponents, serverMetadata: IAuthorizationServerMetadata, resourceMetadata: IAuthorizationProtectedResourceMetadata | undefined, errorOnUserInteraction?: boolean): Promise; + $getTokenFromServerMetadata(id: number, authorizationServer: UriComponents, serverMetadata: IAuthorizationServerMetadata, resourceMetadata: IAuthorizationProtectedResourceMetadata | undefined, scopes: string[] | undefined, errorOnUserInteraction?: boolean): Promise; } export interface MainThreadDataChannelsShape extends IDisposable { diff --git a/src/vs/workbench/api/common/extHostMcp.ts b/src/vs/workbench/api/common/extHostMcp.ts index 0c6baa4ede8..851551174d7 100644 --- a/src/vs/workbench/api/common/extHostMcp.ts +++ b/src/vs/workbench/api/common/extHostMcp.ts @@ -8,7 +8,7 @@ import { DeferredPromise, raceCancellationError, Sequencer, timeout } from '../. import { CancellationToken, CancellationTokenSource } from '../../../base/common/cancellation.js'; import { CancellationError } from '../../../base/common/errors.js'; import { Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from '../../../base/common/lifecycle.js'; -import { AUTH_SERVER_METADATA_DISCOVERY_PATH, fetchResourceMetadata, getDefaultMetadataForUrl, IAuthorizationProtectedResourceMetadata, IAuthorizationServerMetadata, isAuthorizationServerMetadata, OPENID_CONNECT_DISCOVERY_PATH, parseWWWAuthenticateHeader } from '../../../base/common/oauth.js'; +import { AUTH_SCOPE_SEPARATOR, AUTH_SERVER_METADATA_DISCOVERY_PATH, fetchResourceMetadata, getDefaultMetadataForUrl, IAuthorizationProtectedResourceMetadata, IAuthorizationServerMetadata, isAuthorizationServerMetadata, OPENID_CONNECT_DISCOVERY_PATH, parseWWWAuthenticateHeader, scopesMatch } from '../../../base/common/oauth.js'; import { SSEParser } from '../../../base/common/sseParser.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; import { ConfigurationTarget } from '../../../platform/configuration/common/configuration.js'; @@ -215,6 +215,7 @@ export class McpHTTPHandle extends Disposable { authorizationServer: URI; serverMetadata: IAuthorizationServerMetadata; resourceMetadata?: IAuthorizationProtectedResourceMetadata; + scopes?: string[]; }; constructor( @@ -337,22 +338,11 @@ export class McpHTTPHandle extends Disposable { private async _populateAuthMetadata(mcpUrl: string, originalResponse: CommonResponse): Promise { // If there is a resource_metadata challenge, use that to get the oauth server. This is done in 2 steps. // First, extract the resource_metada challenge from the WWW-Authenticate header (if available) - let resourceMetadataChallenge: string | undefined; - if (originalResponse.headers.has('WWW-Authenticate')) { - const authHeader = originalResponse.headers.get('WWW-Authenticate')!; - const challenges = parseWWWAuthenticateHeader(authHeader); - for (const challenge of challenges) { - if (challenge.scheme === 'Bearer' && challenge.params['resource_metadata']) { - this._log(LogLevel.Debug, `Found resource_metadata challenge in WWW-Authenticate header: ${challenge.params['resource_metadata']}`); - resourceMetadataChallenge = challenge.params['resource_metadata']; - break; - } - } - } + const { resourceMetadataChallenge, scopesChallenge: scopesChallengeFromHeader } = this._parseWWWAuthenticateHeader(originalResponse); // Second, fetch the resource metadata either from the challenge URL or from well-known URIs let serverMetadataUrl: string | undefined; - let scopesSupported: string[] | undefined; let resource: IAuthorizationProtectedResourceMetadata | undefined; + let scopesChallenge = scopesChallengeFromHeader; try { const resourceMetadata = await fetchResourceMetadata(mcpUrl, resourceMetadataChallenge, { sameOriginHeaders: { @@ -365,7 +355,7 @@ export class McpHTTPHandle extends Disposable { // Consider using one that has an auth provider first, over the dynamic flow serverMetadataUrl = resourceMetadata.authorization_servers?.[0]; this._log(LogLevel.Debug, `Using auth server metadata url: ${serverMetadataUrl}`); - scopesSupported = resourceMetadata.scopes_supported; + scopesChallenge ??= resourceMetadata.scopes_supported; resource = resourceMetadata; } catch (e) { this._log(LogLevel.Debug, `Could not fetch resource metadata: ${String(e)}`); @@ -389,7 +379,8 @@ export class McpHTTPHandle extends Disposable { this._authMetadata = { authorizationServer: URI.parse(serverMetadataUrl), serverMetadata: serverMetadataResponse, - resourceMetadata: resource + resourceMetadata: resource, + scopes: scopesChallenge }; return; } catch (e) { @@ -398,11 +389,11 @@ export class McpHTTPHandle extends Disposable { // If there's no well-known server metadata, then use the default values based off of the url. const defaultMetadata = getDefaultMetadataForUrl(new URL(baseUrl)); - defaultMetadata.scopes_supported = scopesSupported ?? defaultMetadata.scopes_supported ?? []; this._authMetadata = { authorizationServer: URI.parse(baseUrl), serverMetadata: defaultMetadata, - resourceMetadata: resource + resourceMetadata: resource, + scopes: scopesChallenge }; this._log(LogLevel.Info, 'Using default auth metadata'); } @@ -663,7 +654,7 @@ export class McpHTTPHandle extends Disposable { private async _addAuthHeader(headers: Record) { if (this._authMetadata) { try { - const token = await this._proxy.$getTokenFromServerMetadata(this._id, this._authMetadata.authorizationServer, this._authMetadata.serverMetadata, this._authMetadata.resourceMetadata, this._errorOnUserInteraction); + const token = await this._proxy.$getTokenFromServerMetadata(this._id, this._authMetadata.authorizationServer, this._authMetadata.serverMetadata, this._authMetadata.resourceMetadata, this._authMetadata.scopes, this._errorOnUserInteraction); if (token) { headers['Authorization'] = `Bearer ${token}`; } @@ -684,6 +675,34 @@ export class McpHTTPHandle extends Disposable { } } + private _parseWWWAuthenticateHeader(response: CommonResponse): { resourceMetadataChallenge: string | undefined; scopesChallenge: string[] | undefined } { + let resourceMetadataChallenge: string | undefined; + let scopesChallenge: string[] | undefined; + if (response.headers.has('WWW-Authenticate')) { + const authHeader = response.headers.get('WWW-Authenticate')!; + const challenges = parseWWWAuthenticateHeader(authHeader); + for (const challenge of challenges) { + if (challenge.scheme === 'Bearer') { + if (!resourceMetadataChallenge && challenge.params['resource_metadata']) { + resourceMetadataChallenge = challenge.params['resource_metadata']; + this._log(LogLevel.Debug, `Found resource_metadata challenge in WWW-Authenticate header: ${resourceMetadataChallenge}`); + } + if (!scopesChallenge && challenge.params['scope']) { + const scopes = challenge.params['scope'].split(AUTH_SCOPE_SEPARATOR); + if (scopes.length) { + this._log(LogLevel.Debug, `Found scope challenge in WWW-Authenticate header: ${challenge.params['scope']}`); + scopesChallenge = scopes; + } + } + if (resourceMetadataChallenge && scopesChallenge) { + break; + } + } + } + } + return { resourceMetadataChallenge, scopesChallenge }; + } + private async _getErrText(res: CommonResponse) { try { return await res.text(); @@ -696,6 +715,7 @@ export class McpHTTPHandle extends Disposable { * Helper method to perform fetch with 401 authentication retry logic. * If the initial request returns 401 and we don't have auth metadata, * it will populate the auth metadata and retry once. + * If we already have auth metadata, check if the scopes changed and update them. */ private async _fetchWithAuthRetry(mcpUrl: string, init: MinimalRequestInit, headers: Record): Promise { const doFetch = () => this._fetch(mcpUrl, init); @@ -710,6 +730,19 @@ export class McpHTTPHandle extends Disposable { init.headers = headers; res = await doFetch(); } + } else { + // We have auth metadata, but got a 401. Check if the scopes changed. + const { scopesChallenge } = this._parseWWWAuthenticateHeader(res); + if (!scopesMatch(scopesChallenge, this._authMetadata.scopes)) { + this._log(LogLevel.Debug, `Scopes changed from ${JSON.stringify(this._authMetadata.scopes)} to ${JSON.stringify(scopesChallenge)}, updating and retrying`); + this._authMetadata.scopes = scopesChallenge; + await this._addAuthHeader(headers); + if (headers['Authorization']) { + // Update the headers in the init object + init.headers = headers; + res = await doFetch(); + } + } } } return res; From 223e3f2f2a64f19fdc45886218a836b039b396c7 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt <2644648+TylerLeonhardt@users.noreply.github.com> Date: Mon, 6 Oct 2025 15:44:12 -0700 Subject: [PATCH 5/5] PR Feedback :lipstick: (#270106) * PR Feedback :lipstick: From https://github.com/microsoft/vscode/pull/269884 * Update src/vs/base/common/oauth.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/vs/workbench/api/common/extHostMcp.ts --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/vs/base/common/oauth.ts | 3 ++- src/vs/workbench/api/browser/mainThreadMcp.ts | 14 +++++++------- src/vs/workbench/api/common/extHostMcp.ts | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/vs/base/common/oauth.ts b/src/vs/base/common/oauth.ts index db7d985de4c..3fd447667cc 100644 --- a/src/vs/base/common/oauth.ts +++ b/src/vs/base/common/oauth.ts @@ -260,7 +260,8 @@ export interface IAuthorizationServerMetadata { } /** - * Request for the dynamic client registration endpoint as per RFC 7591. + * Request for the dynamic client registration endpoint. + * @see https://datatracker.ietf.org/doc/html/rfc7591#section-2 */ export interface IAuthorizationDynamicClientRegistrationRequest { /** diff --git a/src/vs/workbench/api/browser/mainThreadMcp.ts b/src/vs/workbench/api/browser/mainThreadMcp.ts index 3f1cc964e13..dda2b38aac5 100644 --- a/src/vs/workbench/api/browser/mainThreadMcp.ts +++ b/src/vs/workbench/api/browser/mainThreadMcp.ts @@ -184,7 +184,7 @@ export class MainThreadMcp extends Disposable implements MainThreadMcpShape { return undefined; } const authorizationServer = URI.revive(authServerComponents); - const scopesSupported = scopes ?? resourceMetadata?.scopes_supported ?? serverMetadata.scopes_supported ?? []; + const resolvedScopes = scopes ?? resourceMetadata?.scopes_supported ?? serverMetadata.scopes_supported ?? []; let providerId = await this._authenticationService.getOrActivateProviderIdForServer(authorizationServer); if (!providerId) { const provider = await this._authenticationService.createDynamicAuthenticationProvider(authorizationServer, serverMetadata, resourceMetadata); @@ -193,7 +193,7 @@ export class MainThreadMcp extends Disposable implements MainThreadMcpShape { } providerId = provider.id; } - const sessions = await this._authenticationService.getSessions(providerId, scopesSupported, { authorizationServer: authorizationServer }, true); + const sessions = await this._authenticationService.getSessions(providerId, resolvedScopes, { authorizationServer: authorizationServer }, true); const accountNamePreference = this.authenticationMcpServersService.getAccountPreference(server.id, providerId); let matchingAccountPreferenceSession: AuthenticationSession | undefined; if (accountNamePreference) { @@ -204,12 +204,12 @@ export class MainThreadMcp extends Disposable implements MainThreadMcpShape { if (sessions.length) { // If we have an existing session preference, use that. If not, we'll return any valid session at the end of this function. if (matchingAccountPreferenceSession && this.authenticationMCPServerAccessService.isAccessAllowed(providerId, matchingAccountPreferenceSession.account.label, server.id)) { - this.authenticationMCPServerUsageService.addAccountUsage(providerId, matchingAccountPreferenceSession.account.label, scopesSupported, server.id, server.label); + this.authenticationMCPServerUsageService.addAccountUsage(providerId, matchingAccountPreferenceSession.account.label, resolvedScopes, server.id, server.label); return matchingAccountPreferenceSession.accessToken; } // If we only have one account for a single auth provider, lets just check if it's allowed and return it if it is. if (!provider.supportsMultipleAccounts && this.authenticationMCPServerAccessService.isAccessAllowed(providerId, sessions[0].account.label, server.id)) { - this.authenticationMCPServerUsageService.addAccountUsage(providerId, sessions[0].account.label, scopesSupported, server.id, server.label); + this.authenticationMCPServerUsageService.addAccountUsage(providerId, sessions[0].account.label, resolvedScopes, server.id, server.label); return sessions[0].accessToken; } } @@ -228,7 +228,7 @@ export class MainThreadMcp extends Disposable implements MainThreadMcpShape { throw new UserInteractionRequiredError('authentication'); } session = provider.supportsMultipleAccounts - ? await this.authenticationMcpServersService.selectSession(providerId, server.id, server.label, scopesSupported, sessions) + ? await this.authenticationMcpServersService.selectSession(providerId, server.id, server.label, resolvedScopes, sessions) : sessions[0]; } else { @@ -239,7 +239,7 @@ export class MainThreadMcp extends Disposable implements MainThreadMcpShape { do { session = await this._authenticationService.createSession( providerId, - scopesSupported, + resolvedScopes, { activateImmediate: true, account: accountToCreate, @@ -254,7 +254,7 @@ export class MainThreadMcp extends Disposable implements MainThreadMcpShape { this.authenticationMCPServerAccessService.updateAllowedMcpServers(providerId, session.account.label, [{ id: server.id, name: server.label, allowed: true }]); this.authenticationMcpServersService.updateAccountPreference(server.id, providerId, session.account); - this.authenticationMCPServerUsageService.addAccountUsage(providerId, session.account.label, scopesSupported, server.id, server.label); + this.authenticationMCPServerUsageService.addAccountUsage(providerId, session.account.label, resolvedScopes, server.id, server.label); return session.accessToken; } diff --git a/src/vs/workbench/api/common/extHostMcp.ts b/src/vs/workbench/api/common/extHostMcp.ts index 851551174d7..457a8dea83f 100644 --- a/src/vs/workbench/api/common/extHostMcp.ts +++ b/src/vs/workbench/api/common/extHostMcp.ts @@ -688,7 +688,7 @@ export class McpHTTPHandle extends Disposable { this._log(LogLevel.Debug, `Found resource_metadata challenge in WWW-Authenticate header: ${resourceMetadataChallenge}`); } if (!scopesChallenge && challenge.params['scope']) { - const scopes = challenge.params['scope'].split(AUTH_SCOPE_SEPARATOR); + const scopes = challenge.params['scope'].split(AUTH_SCOPE_SEPARATOR).filter(s => s.trim().length); if (scopes.length) { this._log(LogLevel.Debug, `Found scope challenge in WWW-Authenticate header: ${challenge.params['scope']}`); scopesChallenge = scopes;