mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-01 13:15:22 +01:00
Merge branch 'main' into dev/mjbvz/comprehensive-raven
This commit is contained in:
@@ -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<vscode.TerminalCompletionItem[] | vscode.TerminalCompletionList | undefined> {
|
||||
currentTerminalEnv = terminal.shellIntegration?.env?.value ?? process.env;
|
||||
if (token.isCancellationRequested) {
|
||||
|
||||
@@ -685,7 +685,7 @@ export abstract class ReferenceCollection<T> {
|
||||
|
||||
private readonly references: Map<string, { readonly object: T; counter: number }> = new Map();
|
||||
|
||||
acquire(key: string, ...args: any[]): IReference<T> {
|
||||
acquire(key: string, ...args: unknown[]): IReference<T> {
|
||||
let reference = this.references.get(key);
|
||||
|
||||
if (!reference) {
|
||||
@@ -706,7 +706,7 @@ export abstract class ReferenceCollection<T> {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
+137
-25
@@ -259,6 +259,108 @@ export interface IAuthorizationServerMetadata {
|
||||
code_challenge_methods_supported?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Request for the dynamic client registration endpoint.
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc7591#section-2
|
||||
*/
|
||||
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 +851,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 +1040,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;
|
||||
}
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -595,7 +595,7 @@ export class RenameWidget implements IRenameWidget, IContentWidget, IDisposable
|
||||
}
|
||||
|
||||
private async _updateRenameCandidates(candidates: ProviderResult<NewSymbolName[]>[], 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);
|
||||
|
||||
@@ -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<void> => {
|
||||
const run = (_accessor?: ServicesAccessor, ...args: unknown[]): Promise<void> => {
|
||||
return Promise.resolve(_descriptor.run(this, ...args));
|
||||
};
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ export function addEditorAction(descriptor: IActionDescriptor): IDisposable {
|
||||
}
|
||||
|
||||
const precondition = ContextKeyExpr.deserialize(descriptor.precondition);
|
||||
const run = (accessor: ServicesAccessor, ...args: any[]): void | Promise<void> => {
|
||||
const run = (accessor: ServicesAccessor, ...args: unknown[]): void | Promise<void> => {
|
||||
return EditorCommand.runEditorCommand(accessor, args, precondition, (accessor, editor, args) => Promise.resolve(descriptor.run(editor, ...args)));
|
||||
};
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class MonacoWebWorkerImpl<T extends object> extends EditorWorkerClient implement
|
||||
if (typeof prop !== 'string') {
|
||||
throw new Error(`Not supported`);
|
||||
}
|
||||
return (...args: any[]) => {
|
||||
return (...args: unknown[]) => {
|
||||
return proxy.$fmr(prop, args);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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<LogLevel> = new Emitter<LogLevel>().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 { }
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<string | undefined> {
|
||||
async $getTokenFromServerMetadata(id: number, authServerComponents: UriComponents, serverMetadata: IAuthorizationServerMetadata, resourceMetadata: IAuthorizationProtectedResourceMetadata | undefined, scopes: string[] | undefined, errorOnUserInteraction?: boolean): Promise<string | undefined> {
|
||||
const server = this._serverDefinitions.get(id);
|
||||
if (!server) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const authorizationServer = URI.revive(authServerComponents);
|
||||
const scopesSupported = 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);
|
||||
@@ -194,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) {
|
||||
@@ -205,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;
|
||||
}
|
||||
}
|
||||
@@ -229,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 {
|
||||
@@ -240,7 +239,7 @@ export class MainThreadMcp extends Disposable implements MainThreadMcpShape {
|
||||
do {
|
||||
session = await this._authenticationService.createSession(
|
||||
providerId,
|
||||
scopesSupported,
|
||||
resolvedScopes,
|
||||
{
|
||||
activateImmediate: true,
|
||||
account: accountToCreate,
|
||||
@@ -255,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<vscode.TerminalCompletionItem>, ...triggerCharacters: string[]): vscode.Disposable {
|
||||
registerTerminalCompletionProvider(provider: vscode.TerminalCompletionProvider<vscode.TerminalCompletionItem>, ...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');
|
||||
|
||||
@@ -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<string | undefined>;
|
||||
$getTokenFromServerMetadata(id: number, authorizationServer: UriComponents, serverMetadata: IAuthorizationServerMetadata, resourceMetadata: IAuthorizationProtectedResourceMetadata | undefined, scopes: string[] | undefined, errorOnUserInteraction?: boolean): Promise<string | undefined>;
|
||||
}
|
||||
|
||||
export interface MainThreadDataChannelsShape extends IDisposable {
|
||||
|
||||
@@ -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<void> {
|
||||
// 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<string, string>) {
|
||||
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).filter(s => s.trim().length);
|
||||
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<string, string>): Promise<CommonResponse> {
|
||||
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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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<vscode.TerminalCompletionItem>, ...triggerCharacters: string[]): vscode.Disposable;
|
||||
registerTerminalCompletionProvider(extension: IExtensionDescription, provider: vscode.TerminalCompletionProvider<vscode.TerminalCompletionItem>, ...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<TerminalCompletionItem>, ...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<TerminalCompletionItem>, ...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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ const emptyCommandService: ICommandService = {
|
||||
_serviceBrand: undefined,
|
||||
onWillExecuteCommand: () => Disposable.None,
|
||||
onDidExecuteCommand: () => Disposable.None,
|
||||
executeCommand: (commandId: string, ...args: any[]): Promise<any> => {
|
||||
executeCommand: (commandId: string, ...args: unknown[]): Promise<any> => {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
};
|
||||
@@ -27,16 +27,16 @@ const emptyCommandService: ICommandService = {
|
||||
const emptyNotificationService = new class implements INotificationService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
onDidChangeFilter: Event<void> = 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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<void> {
|
||||
override async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: unknown[]): Promise<void> {
|
||||
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<void> {
|
||||
async runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: unknown[]): Promise<void> {
|
||||
ctrl.cancelSession();
|
||||
}
|
||||
}
|
||||
@@ -438,7 +438,7 @@ export class ConfigureInlineChatAction extends AbstractInline1ChatAction {
|
||||
});
|
||||
}
|
||||
|
||||
async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: any[]): Promise<void> {
|
||||
async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: unknown[]): Promise<void> {
|
||||
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<void> {
|
||||
override async runInlineChatCommand(accessor: ServicesAccessor, _ctrl: InlineChatController2, editor: ICodeEditor, ..._args: unknown[]): Promise<void> {
|
||||
const inlineChatSessions = accessor.get(IInlineChatSessionService);
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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() {
|
||||
|
||||
@@ -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<boolean>('notebookCellChatFocused', false, localize('notebookCellChatFocused', "Whether the cell chat editor is focused"));
|
||||
export const CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST = new RawContextKey<boolean>('notebookChatHasActiveRequest', false, localize('notebookChatHasActiveRequest', "Whether the cell chat editor has an active request"));
|
||||
export const CTX_NOTEBOOK_CHAT_USER_DID_EDIT = new RawContextKey<boolean>('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<boolean>('notebookChatAgentRegistered', false, localize('notebookChatAgentRegistered', "Whether a chat agent for notebook is registered"));
|
||||
|
||||
@@ -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>(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<string, string>(1000, 0.7);
|
||||
private readonly _onDidChangePromptCache = this._register(new Emitter<{ cell: URI }>());
|
||||
readonly onDidChangePromptCache = this._onDidChangePromptCache.event;
|
||||
|
||||
private _strategy: EditStrategy | undefined;
|
||||
private _sessionCtor: CancelablePromise<void> | undefined;
|
||||
private _activeRequestCts?: CancellationTokenSource;
|
||||
private readonly _ctxHasActiveRequest: IContextKey<boolean>;
|
||||
private readonly _ctxCellWidgetFocused: IContextKey<boolean>;
|
||||
private readonly _ctxUserDidEdit: IContextKey<boolean>;
|
||||
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<ChatModel> = 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<void>(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<void>();
|
||||
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<void> {
|
||||
// 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<void> {
|
||||
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);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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';
|
||||
@@ -285,7 +284,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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
@@ -342,7 +326,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 +373,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 +408,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 +486,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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<boolean>;
|
||||
private cellLineNumbers!: IContextKey<'on' | 'off' | 'inherit'>;
|
||||
private cellResource!: IContextKey<string>;
|
||||
private cellGeneratedByChat!: IContextKey<boolean>;
|
||||
private cellHasErrorDiagnostics!: IContextKey<boolean>;
|
||||
|
||||
private markdownEditMode!: IContextKey<boolean>;
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@ export const NOTEBOOK_CELL_OUTPUT_MIMETYPE = new RawContextKey<string>('notebook
|
||||
export const NOTEBOOK_CELL_INPUT_COLLAPSED = new RawContextKey<boolean>('notebookCellInputIsCollapsed', false);
|
||||
export const NOTEBOOK_CELL_OUTPUT_COLLAPSED = new RawContextKey<boolean>('notebookCellOutputIsCollapsed', false);
|
||||
export const NOTEBOOK_CELL_RESOURCE = new RawContextKey<string>('notebookCellResource', '');
|
||||
export const NOTEBOOK_CELL_GENERATED_BY_CHAT = new RawContextKey<boolean>('notebookCellGenerateByChat', false);
|
||||
export const NOTEBOOK_CELL_HAS_ERROR_DIAGNOSTICS = new RawContextKey<boolean>('notebookCellHasErrorDiagnostics', false);
|
||||
export const NOTEBOOK_CELL_OUTPUT_MIME_TYPE_LIST_FOR_CHAT = new RawContextKey<string[]>('notebookCellOutputMimeTypeListForChat', []);
|
||||
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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, ({
|
||||
|
||||
@@ -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<T extends TerminalCompletionItem>(id: string, provider: TerminalCompletionProvider<T>, ...triggerCharacters: string[]): Disposable;
|
||||
export function registerTerminalCompletionProvider<T extends TerminalCompletionItem>(provider: TerminalCompletionProvider<T>, ...triggerCharacters: string[]): Disposable;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user