mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 13:46:13 +00:00
Merge branch 'main' into npm-extension-bun-package-manager
This commit is contained in:
commit
4fd54f93f7
|
@ -5,7 +5,7 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import 'mocha';
|
||||
import { CancellationToken, chat, ChatAgentRequest, ChatVariableLevel, CompletionItemKind, Disposable, interactive, InteractiveProgress, InteractiveRequest, InteractiveResponseForProgress, InteractiveSession, InteractiveSessionState, Progress, ProviderResult } from 'vscode';
|
||||
import { CancellationToken, chat, ChatAgentRequest, ChatVariableLevel, Disposable, interactive, InteractiveSession, ProviderResult } from 'vscode';
|
||||
import { assertNoRpc, closeAllEditors, DeferredPromise, disposeAll } from '../utils';
|
||||
|
||||
suite('chat', () => {
|
||||
|
@ -22,24 +22,12 @@ suite('chat', () => {
|
|||
|
||||
function getDeferredForRequest(): DeferredPromise<ChatAgentRequest> {
|
||||
disposables.push(interactive.registerInteractiveSessionProvider('provider', {
|
||||
prepareSession: (_initialState: InteractiveSessionState | undefined, _token: CancellationToken): ProviderResult<InteractiveSession> => {
|
||||
prepareSession: (_token: CancellationToken): ProviderResult<InteractiveSession> => {
|
||||
return {
|
||||
requester: { name: 'test' },
|
||||
responder: { name: 'test' },
|
||||
};
|
||||
},
|
||||
|
||||
provideResponseWithProgress: (_request: InteractiveRequest, _progress: Progress<InteractiveProgress>, _token: CancellationToken): ProviderResult<InteractiveResponseForProgress> => {
|
||||
return null;
|
||||
},
|
||||
|
||||
provideSlashCommands: (_session, _token) => {
|
||||
return [{ command: 'hello', title: 'Hello', kind: CompletionItemKind.Text }];
|
||||
},
|
||||
|
||||
removeRequest: (_session: InteractiveSession, _requestId: string): void => {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
}));
|
||||
|
||||
const deferred = new DeferredPromise<ChatAgentRequest>();
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import 'mocha';
|
||||
import { CancellationToken, CompletionItemKind, Disposable, interactive, InteractiveProgress, InteractiveRequest, InteractiveResponseForProgress, InteractiveSession, InteractiveSessionState, Progress, ProviderResult } from 'vscode';
|
||||
import { assertNoRpc, closeAllEditors, DeferredPromise, disposeAll } from '../utils';
|
||||
|
||||
suite('InteractiveSessionProvider', () => {
|
||||
let disposables: Disposable[] = [];
|
||||
setup(async () => {
|
||||
disposables = [];
|
||||
});
|
||||
|
||||
teardown(async function () {
|
||||
assertNoRpc();
|
||||
await closeAllEditors();
|
||||
disposeAll(disposables);
|
||||
});
|
||||
|
||||
function getDeferredForRequest(): DeferredPromise<InteractiveRequest> {
|
||||
const deferred = new DeferredPromise<InteractiveRequest>();
|
||||
disposables.push(interactive.registerInteractiveSessionProvider('provider', {
|
||||
prepareSession: (_initialState: InteractiveSessionState | undefined, _token: CancellationToken): ProviderResult<InteractiveSession> => {
|
||||
return {
|
||||
requester: { name: 'test' },
|
||||
responder: { name: 'test' },
|
||||
};
|
||||
},
|
||||
|
||||
provideResponseWithProgress: (request: InteractiveRequest, _progress: Progress<InteractiveProgress>, _token: CancellationToken): ProviderResult<InteractiveResponseForProgress> => {
|
||||
deferred.complete(request);
|
||||
return null;
|
||||
},
|
||||
|
||||
provideSlashCommands: (_session, _token) => {
|
||||
return [{ command: 'hello', title: 'Hello', kind: CompletionItemKind.Text }];
|
||||
},
|
||||
|
||||
removeRequest: (_session: InteractiveSession, _requestId: string): void => {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
}));
|
||||
return deferred;
|
||||
}
|
||||
|
||||
test('plain text query', async () => {
|
||||
const deferred = getDeferredForRequest();
|
||||
interactive.sendInteractiveRequestToProvider('provider', { message: 'hello' });
|
||||
const lastResult = await deferred.p;
|
||||
assert.strictEqual(lastResult.message, 'hello');
|
||||
});
|
||||
|
||||
test('slash command', async () => {
|
||||
const deferred = getDeferredForRequest();
|
||||
interactive.sendInteractiveRequestToProvider('provider', { message: '/hello' });
|
||||
const lastResult = await deferred.p;
|
||||
assert.strictEqual(lastResult.message, '/hello');
|
||||
});
|
||||
});
|
|
@ -133,9 +133,9 @@ export interface IAddStandardDisposableListenerSignature {
|
|||
(node: HTMLElement, type: 'pointerup', handler: (event: PointerEvent) => void, useCapture?: boolean): IDisposable;
|
||||
(node: HTMLElement, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable;
|
||||
}
|
||||
function _wrapAsStandardMouseEvent(handler: (e: IMouseEvent) => void): (e: MouseEvent) => void {
|
||||
function _wrapAsStandardMouseEvent(targetWindow: Window, handler: (e: IMouseEvent) => void): (e: MouseEvent) => void {
|
||||
return function (e: MouseEvent) {
|
||||
return handler(new StandardMouseEvent(e));
|
||||
return handler(new StandardMouseEvent(targetWindow, e));
|
||||
};
|
||||
}
|
||||
function _wrapAsStandardKeyboardEvent(handler: (e: IKeyboardEvent) => void): (e: KeyboardEvent) => void {
|
||||
|
@ -147,7 +147,7 @@ export const addStandardDisposableListener: IAddStandardDisposableListenerSignat
|
|||
let wrapHandler = handler;
|
||||
|
||||
if (type === 'click' || type === 'mousedown') {
|
||||
wrapHandler = _wrapAsStandardMouseEvent(handler);
|
||||
wrapHandler = _wrapAsStandardMouseEvent(getWindow(node), handler);
|
||||
} else if (type === 'keydown' || type === 'keypress' || type === 'keyup') {
|
||||
wrapHandler = _wrapAsStandardKeyboardEvent(handler);
|
||||
}
|
||||
|
@ -156,13 +156,13 @@ export const addStandardDisposableListener: IAddStandardDisposableListenerSignat
|
|||
};
|
||||
|
||||
export const addStandardDisposableGenericMouseDownListener = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable {
|
||||
const wrapHandler = _wrapAsStandardMouseEvent(handler);
|
||||
const wrapHandler = _wrapAsStandardMouseEvent(getWindow(node), handler);
|
||||
|
||||
return addDisposableGenericMouseDownListener(node, wrapHandler, useCapture);
|
||||
};
|
||||
|
||||
export const addStandardDisposableGenericMouseUpListener = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable {
|
||||
const wrapHandler = _wrapAsStandardMouseEvent(handler);
|
||||
const wrapHandler = _wrapAsStandardMouseEvent(getWindow(node), handler);
|
||||
|
||||
return addDisposableGenericMouseUpListener(node, wrapHandler, useCapture);
|
||||
};
|
||||
|
|
|
@ -195,7 +195,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
|
|||
const onClick = options.actionHandler.disposables.add(new DomEmitter(element, 'click'));
|
||||
const onAuxClick = options.actionHandler.disposables.add(new DomEmitter(element, 'auxclick'));
|
||||
options.actionHandler.disposables.add(Event.any(onClick.event, onAuxClick.event)(e => {
|
||||
const mouseEvent = new StandardMouseEvent(e);
|
||||
const mouseEvent = new StandardMouseEvent(DOM.getWindow(element), e);
|
||||
if (!mouseEvent.leftButton && !mouseEvent.middleButton) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { IframeUtils } from 'vs/base/browser/iframe';
|
||||
import { $window } from 'vs/base/browser/window';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
|
||||
export interface IMouseEvent {
|
||||
|
@ -46,7 +45,7 @@ export class StandardMouseEvent implements IMouseEvent {
|
|||
public readonly metaKey: boolean;
|
||||
public readonly timestamp: number;
|
||||
|
||||
constructor(e: MouseEvent) {
|
||||
constructor(targetWindow: Window, e: MouseEvent) {
|
||||
this.timestamp = Date.now();
|
||||
this.browserEvent = e;
|
||||
this.leftButton = e.button === 0;
|
||||
|
@ -75,7 +74,7 @@ export class StandardMouseEvent implements IMouseEvent {
|
|||
}
|
||||
|
||||
// Find the position of the iframe this code is executing in relative to the iframe where the event was captured.
|
||||
const iframeOffsets = IframeUtils.getPositionOfChildWindowRelativeToAncestorWindow($window, e.view);
|
||||
const iframeOffsets = IframeUtils.getPositionOfChildWindowRelativeToAncestorWindow(targetWindow, e.view);
|
||||
this.posx -= iframeOffsets.left;
|
||||
this.posy -= iframeOffsets.top;
|
||||
}
|
||||
|
@ -93,8 +92,8 @@ export class DragMouseEvent extends StandardMouseEvent {
|
|||
|
||||
public readonly dataTransfer: DataTransfer;
|
||||
|
||||
constructor(e: MouseEvent) {
|
||||
super(e);
|
||||
constructor(targetWindow: Window, e: MouseEvent) {
|
||||
super(targetWindow, e);
|
||||
this.dataTransfer = (<any>e).dataTransfer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1390,7 +1390,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
|
|||
|
||||
const fromMouse = Event.chain(this.view.onContextMenu, $ =>
|
||||
$.filter(_ => !didJustPressContextMenuKey)
|
||||
.map(({ element, index, browserEvent }) => ({ element, index, anchor: new StandardMouseEvent(browserEvent), browserEvent }))
|
||||
.map(({ element, index, browserEvent }) => ({ element, index, anchor: new StandardMouseEvent(getWindow(this.view.domNode), browserEvent), browserEvent }))
|
||||
);
|
||||
|
||||
return Event.any<IListContextMenuEvent<T>>(fromKeyDown, fromKeyUp, fromMouse);
|
||||
|
|
|
@ -478,7 +478,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
|
|||
// => to get the Copy and Paste context menu actions working on Firefox,
|
||||
// there should be no timeout here
|
||||
if (isFirefox) {
|
||||
const mouseEvent = new StandardMouseEvent(e);
|
||||
const mouseEvent = new StandardMouseEvent(getWindow(this.element), e);
|
||||
|
||||
// Allowing right click to trigger the event causes the issue described below,
|
||||
// but since the solution below does not work in FF, we must disable right click
|
||||
|
|
|
@ -258,7 +258,7 @@ export class MenuBar extends Disposable {
|
|||
|
||||
this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => {
|
||||
// Ignore non-left-click
|
||||
const mouseEvent = new StandardMouseEvent(e);
|
||||
const mouseEvent = new StandardMouseEvent(DOM.getWindow(buttonElement), e);
|
||||
if (!mouseEvent.leftButton) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
|
@ -367,7 +367,7 @@ export class MenuBar extends Disposable {
|
|||
|
||||
this._register(DOM.addDisposableListener(buttonElement, DOM.EventType.MOUSE_DOWN, (e) => {
|
||||
// Ignore non-left-click
|
||||
const mouseEvent = new StandardMouseEvent(e);
|
||||
const mouseEvent = new StandardMouseEvent(DOM.getWindow(buttonElement), e);
|
||||
if (!mouseEvent.leftButton) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
|
|
|
@ -12,19 +12,19 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
|||
export abstract class Widget extends Disposable {
|
||||
|
||||
protected onclick(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void {
|
||||
this._register(dom.addDisposableListener(domNode, dom.EventType.CLICK, (e: MouseEvent) => listener(new StandardMouseEvent(e))));
|
||||
this._register(dom.addDisposableListener(domNode, dom.EventType.CLICK, (e: MouseEvent) => listener(new StandardMouseEvent(dom.getWindow(domNode), e))));
|
||||
}
|
||||
|
||||
protected onmousedown(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void {
|
||||
this._register(dom.addDisposableListener(domNode, dom.EventType.MOUSE_DOWN, (e: MouseEvent) => listener(new StandardMouseEvent(e))));
|
||||
this._register(dom.addDisposableListener(domNode, dom.EventType.MOUSE_DOWN, (e: MouseEvent) => listener(new StandardMouseEvent(dom.getWindow(domNode), e))));
|
||||
}
|
||||
|
||||
protected onmouseover(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void {
|
||||
this._register(dom.addDisposableListener(domNode, dom.EventType.MOUSE_OVER, (e: MouseEvent) => listener(new StandardMouseEvent(e))));
|
||||
this._register(dom.addDisposableListener(domNode, dom.EventType.MOUSE_OVER, (e: MouseEvent) => listener(new StandardMouseEvent(dom.getWindow(domNode), e))));
|
||||
}
|
||||
|
||||
protected onmouseleave(domNode: HTMLElement, listener: (e: IMouseEvent) => void): void {
|
||||
this._register(dom.addDisposableListener(domNode, dom.EventType.MOUSE_LEAVE, (e: MouseEvent) => listener(new StandardMouseEvent(e))));
|
||||
this._register(dom.addDisposableListener(domNode, dom.EventType.MOUSE_LEAVE, (e: MouseEvent) => listener(new StandardMouseEvent(dom.getWindow(domNode), e))));
|
||||
}
|
||||
|
||||
protected onkeydown(domNode: HTMLElement, listener: (e: IKeyboardEvent) => void): void {
|
||||
|
|
|
@ -781,6 +781,10 @@ export class DisposableMap<K, V extends IDisposable = IDisposable> implements ID
|
|||
return this._store.keys();
|
||||
}
|
||||
|
||||
values(): IterableIterator<V> {
|
||||
return this._store.values();
|
||||
}
|
||||
|
||||
[Symbol.iterator](): IterableIterator<[K, V]> {
|
||||
return this._store[Symbol.iterator]();
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ export class EditorMouseEvent extends StandardMouseEvent {
|
|||
public readonly relativePos: CoordinatesRelativeToEditor;
|
||||
|
||||
constructor(e: MouseEvent, isFromPointerCapture: boolean, editorViewDomNode: HTMLElement) {
|
||||
super(e);
|
||||
super(dom.getWindow(editorViewDomNode), e);
|
||||
this.isFromPointerCapture = isFromPointerCapture;
|
||||
this.pos = new PageCoordinates(this.posx, this.posy);
|
||||
this.editorPos = createEditorPagePosition(editorViewDomNode);
|
||||
|
|
|
@ -98,7 +98,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib
|
|||
}
|
||||
}));
|
||||
this._register(dom.addDisposableListener(stickyScrollDomNode, dom.EventType.CONTEXT_MENU, async (event: MouseEvent) => {
|
||||
this._onContextMenu(event);
|
||||
this._onContextMenu(dom.getWindow(stickyScrollDomNode), event);
|
||||
}));
|
||||
this._stickyScrollFocusedContextKey = EditorContextKeys.stickyScrollFocused.bindTo(this._contextKeyService);
|
||||
this._stickyScrollVisibleContextKey = EditorContextKeys.stickyScrollVisible.bindTo(this._contextKeyService);
|
||||
|
@ -378,8 +378,8 @@ export class StickyScrollController extends Disposable implements IEditorContrib
|
|||
}));
|
||||
}
|
||||
|
||||
private _onContextMenu(e: MouseEvent) {
|
||||
const event = new StandardMouseEvent(e);
|
||||
private _onContextMenu(targetWindow: Window, e: MouseEvent) {
|
||||
const event = new StandardMouseEvent(targetWindow, e);
|
||||
|
||||
this._contextMenuService.showContextMenu({
|
||||
menuId: MenuId.StickyScrollContext,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { addDisposableListener } from 'vs/base/browser/dom';
|
||||
import { addDisposableListener, getWindow } from 'vs/base/browser/dom';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IToolBarOptions, ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
|
||||
import { IAction, Separator, SubmenuAction, toAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions';
|
||||
|
@ -184,7 +184,7 @@ export class WorkbenchToolBar extends ToolBar {
|
|||
// add context menu for toggle actions
|
||||
if (toggleActions.length > 0) {
|
||||
this._sessionDisposables.add(addDisposableListener(this.getElement(), 'contextmenu', e => {
|
||||
const event = new StandardMouseEvent(e);
|
||||
const event = new StandardMouseEvent(getWindow(this.getElement()), e);
|
||||
|
||||
const action = this.getItemAction(event.target);
|
||||
if (!(action)) {
|
||||
|
|
|
@ -95,14 +95,14 @@ export class ContextMenuHandler {
|
|||
|
||||
menu.onDidCancel(() => this.contextViewService.hideContextView(true), null, menuDisposables);
|
||||
menu.onDidBlur(() => this.contextViewService.hideContextView(true), null, menuDisposables);
|
||||
const window = getWindow(container);
|
||||
menuDisposables.add(addDisposableListener(window, EventType.BLUR, () => this.contextViewService.hideContextView(true)));
|
||||
menuDisposables.add(addDisposableListener(window, EventType.MOUSE_DOWN, (e: MouseEvent) => {
|
||||
const targetWindow = getWindow(container);
|
||||
menuDisposables.add(addDisposableListener(targetWindow, EventType.BLUR, () => this.contextViewService.hideContextView(true)));
|
||||
menuDisposables.add(addDisposableListener(targetWindow, EventType.MOUSE_DOWN, (e: MouseEvent) => {
|
||||
if (e.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
const event = new StandardMouseEvent(e);
|
||||
const event = new StandardMouseEvent(targetWindow, e);
|
||||
let element: HTMLElement | null = event.target;
|
||||
|
||||
// Don't do anything as we are likely creating a context menu
|
||||
|
|
|
@ -36,15 +36,11 @@ export class QuickInputBox extends Disposable {
|
|||
}
|
||||
|
||||
onKeyDown = (handler: (event: StandardKeyboardEvent) => void): IDisposable => {
|
||||
return dom.addDisposableListener(this.findInput.inputBox.inputElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
handler(new StandardKeyboardEvent(e));
|
||||
});
|
||||
return dom.addStandardDisposableListener(this.findInput.inputBox.inputElement, dom.EventType.KEY_DOWN, handler);
|
||||
};
|
||||
|
||||
onMouseDown = (handler: (event: StandardMouseEvent) => void): IDisposable => {
|
||||
return dom.addDisposableListener(this.findInput.inputBox.inputElement, dom.EventType.MOUSE_DOWN, (e: MouseEvent) => {
|
||||
handler(new StandardMouseEvent(e));
|
||||
});
|
||||
return dom.addStandardDisposableListener(this.findInput.inputBox.inputElement, dom.EventType.MOUSE_DOWN, handler);
|
||||
};
|
||||
|
||||
onDidChange = (handler: (event: string) => void): IDisposable => {
|
||||
|
|
|
@ -20,7 +20,6 @@ import { StatusBarItemsExtensionPoint } from 'vs/workbench/api/browser/statusBar
|
|||
import './mainThreadLocalization';
|
||||
import './mainThreadBulkEdits';
|
||||
import './mainThreadChatProvider';
|
||||
import './mainThreadChatAgents';
|
||||
import './mainThreadChatAgents2';
|
||||
import './mainThreadChatVariables';
|
||||
import './mainThreadCodeInsets';
|
||||
|
|
|
@ -3,31 +3,23 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DeferredPromise } from 'vs/base/common/async';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { Disposable, DisposableMap } from 'vs/base/common/lifecycle';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ExtHostChatShape, ExtHostContext, IChatRequestDto, IChatResponseProgressDto, ILocationDto, MainContext, MainThreadChatShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostChatShape, ExtHostContext, MainContext, MainThreadChatShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService';
|
||||
import { isCompleteInteractiveProgressTreeData } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { IChat, IChatDynamicRequest, IChatProgress, IChatResponse, IChatResponseProgressFileTreeData, IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatDynamicRequest, IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadChat)
|
||||
export class MainThreadChat extends Disposable implements MainThreadChatShape {
|
||||
|
||||
private readonly _providerRegistrations = this._register(new DisposableMap<number>());
|
||||
private readonly _activeRequestProgressCallbacks = new Map<string, (progress: IChatProgress) => (DeferredPromise<string | IMarkdownString> | void)>();
|
||||
private readonly _stateEmitters = new Map<number, Emitter<any>>();
|
||||
|
||||
private readonly _proxy: ExtHostChatShape;
|
||||
|
||||
private _responsePartHandlePool = 0;
|
||||
private readonly _activeResponsePartPromises = new Map<string, DeferredPromise<string | IMarkdownString | { treeData: IChatResponseProgressFileTreeData }>>();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IChatService private readonly _chatService: IChatService,
|
||||
|
@ -64,8 +56,8 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape {
|
|||
const unreg = this._chatService.registerProvider({
|
||||
id,
|
||||
displayName: registration.label,
|
||||
prepareSession: async (initialState, token) => {
|
||||
const session = await this._proxy.$prepareChat(handle, initialState, token);
|
||||
prepareSession: async (token) => {
|
||||
const session = await this._proxy.$prepareChat(handle, token);
|
||||
if (!session) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -75,14 +67,13 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape {
|
|||
|
||||
const emitter = new Emitter<any>();
|
||||
this._stateEmitters.set(session.id, emitter);
|
||||
return <IChat>{
|
||||
return {
|
||||
id: session.id,
|
||||
requesterUsername: session.requesterUsername,
|
||||
requesterAvatarIconUri: URI.revive(session.requesterAvatarIconUri),
|
||||
responderUsername: session.responderUsername,
|
||||
responderAvatarIconUri,
|
||||
inputPlaceholder: session.inputPlaceholder,
|
||||
onDidChangeState: emitter.event,
|
||||
dispose: () => {
|
||||
emitter.dispose();
|
||||
this._stateEmitters.delete(session.id);
|
||||
|
@ -90,87 +81,17 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape {
|
|||
}
|
||||
};
|
||||
},
|
||||
provideReply: async (request, progress, token) => {
|
||||
const id = `${handle}_${request.session.id}`;
|
||||
this._activeRequestProgressCallbacks.set(id, progress);
|
||||
try {
|
||||
const requestDto: IChatRequestDto = {
|
||||
message: request.message,
|
||||
variables: request.variables
|
||||
};
|
||||
const dto = await this._proxy.$provideReply(handle, request.session.id, requestDto, token);
|
||||
return <IChatResponse>{
|
||||
session: request.session,
|
||||
...dto
|
||||
};
|
||||
} finally {
|
||||
this._activeRequestProgressCallbacks.delete(id);
|
||||
}
|
||||
},
|
||||
provideWelcomeMessage: (token) => {
|
||||
return this._proxy.$provideWelcomeMessage(handle, token);
|
||||
},
|
||||
provideSampleQuestions: (token) => {
|
||||
return this._proxy.$provideSampleQuestions(handle, token);
|
||||
},
|
||||
provideSlashCommands: (session, token) => {
|
||||
return this._proxy.$provideSlashCommands(handle, session.id, token);
|
||||
},
|
||||
provideFollowups: (session, token) => {
|
||||
return this._proxy.$provideFollowups(handle, session.id, token);
|
||||
},
|
||||
removeRequest: (session, requestId) => {
|
||||
return this._proxy.$removeRequest(handle, session.id, requestId);
|
||||
}
|
||||
});
|
||||
|
||||
this._providerRegistrations.set(handle, unreg);
|
||||
}
|
||||
|
||||
async $acceptResponseProgress(handle: number, sessionId: number, progress: IChatResponseProgressDto, responsePartHandle?: number): Promise<number | void> {
|
||||
const id = `${handle}_${sessionId}`;
|
||||
|
||||
if ('placeholder' in progress) {
|
||||
const responsePartId = `${id}_${++this._responsePartHandlePool}`;
|
||||
const deferredContentPromise = new DeferredPromise<string | IMarkdownString | { treeData: IChatResponseProgressFileTreeData }>();
|
||||
this._activeResponsePartPromises.set(responsePartId, deferredContentPromise);
|
||||
this._activeRequestProgressCallbacks.get(id)?.({ ...progress, resolvedContent: deferredContentPromise.p });
|
||||
return this._responsePartHandlePool;
|
||||
} else if (responsePartHandle) {
|
||||
// Complete an existing deferred promise with resolved content
|
||||
const responsePartId = `${id}_${responsePartHandle}`;
|
||||
const deferredContentPromise = this._activeResponsePartPromises.get(responsePartId);
|
||||
if (deferredContentPromise && isCompleteInteractiveProgressTreeData(progress)) {
|
||||
const withRevivedUris = revive<{ treeData: IChatResponseProgressFileTreeData }>(progress);
|
||||
deferredContentPromise.complete(withRevivedUris);
|
||||
this._activeResponsePartPromises.delete(responsePartId);
|
||||
} else if (deferredContentPromise && 'content' in progress) {
|
||||
deferredContentPromise.complete(progress.content);
|
||||
this._activeResponsePartPromises.delete(responsePartId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// No need to support standalone tree data that's not attached to a placeholder in API
|
||||
if (isCompleteInteractiveProgressTreeData(progress)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TS won't let us change the type of `progress`
|
||||
let revivedProgress: IChatProgress;
|
||||
if ('documents' in progress) {
|
||||
revivedProgress = { documents: revive(progress.documents) };
|
||||
} else if ('reference' in progress) {
|
||||
revivedProgress = revive<{ reference: UriComponents | ILocationDto }>(progress);
|
||||
} else if ('inlineReference' in progress) {
|
||||
revivedProgress = revive<{ inlineReference: UriComponents | ILocationDto; name?: string }>(progress);
|
||||
} else {
|
||||
revivedProgress = progress;
|
||||
}
|
||||
|
||||
this._activeRequestProgressCallbacks.get(id)?.(revivedProgress);
|
||||
}
|
||||
|
||||
async $acceptChatState(sessionId: number, state: any): Promise<void> {
|
||||
this._stateEmitters.get(sessionId)?.fire(state);
|
||||
}
|
||||
|
|
|
@ -1,75 +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 { DisposableMap } from 'vs/base/common/lifecycle';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { IProgress } from 'vs/platform/progress/common/progress';
|
||||
import { ExtHostChatAgentsShape, ExtHostContext, MainContext, MainThreadChatAgentsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IChatAgentCommand, IChatAgentMetadata, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers';
|
||||
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadChatAgents)
|
||||
export class MainThreadChatAgents implements MainThreadChatAgentsShape {
|
||||
|
||||
private readonly _agents = new DisposableMap<number>;
|
||||
private readonly _pendingProgress = new Map<number, IProgress<IChatProgress>>();
|
||||
private readonly _proxy: ExtHostChatAgentsShape;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IChatAgentService private readonly _chatAgentService: IChatAgentService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostChatAgents);
|
||||
}
|
||||
|
||||
$unregisterAgent(handle: number): void {
|
||||
this._agents.deleteAndDispose(handle);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._agents.clearAndDisposeAll();
|
||||
}
|
||||
|
||||
$registerAgent(handle: number, name: string, metadata: IChatAgentMetadata & { subCommands: IChatAgentCommand[] }): void {
|
||||
const d = this._chatAgentService.registerAgent({
|
||||
id: name,
|
||||
metadata: revive(metadata),
|
||||
invoke: async (request, progress, history, token) => {
|
||||
const requestId = Math.random();
|
||||
this._pendingProgress.set(requestId, { report: progress });
|
||||
try {
|
||||
const message = request.command ? `/${request.command} ${request.message}` : request.message;
|
||||
const result = await this._proxy.$invokeAgent(handle, requestId, message, { history }, token);
|
||||
return {
|
||||
followUp: result?.followUp ?? [],
|
||||
};
|
||||
} finally {
|
||||
this._pendingProgress.delete(requestId);
|
||||
}
|
||||
},
|
||||
async provideSlashCommands() {
|
||||
return metadata.subCommands;
|
||||
},
|
||||
});
|
||||
this._agents.set(handle, d);
|
||||
}
|
||||
|
||||
async $handleProgressChunk(requestId: number, chunk: IChatSlashFragment): Promise<void> {
|
||||
// An extra step because TS really struggles with type inference in the Revived generic parameter?
|
||||
const revived = revive<IChatSlashFragment>(chunk);
|
||||
if (typeof revived.content === 'string') {
|
||||
this._pendingProgress.get(requestId)?.report({ content: revived.content });
|
||||
} else {
|
||||
this._pendingProgress.get(requestId)?.report(revived.content);
|
||||
}
|
||||
}
|
||||
|
||||
$unregisterCommand(handle: number): void {
|
||||
this._agents.deleteAndDispose(handle);
|
||||
}
|
||||
}
|
|
@ -75,7 +75,7 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA
|
|||
}
|
||||
},
|
||||
provideFollowups: async (sessionId, token): Promise<IChatFollowup[]> => {
|
||||
if (!this._agents.get(handle)?.hasSlashCommands) {
|
||||
if (!this._agents.get(handle)?.hasFollowups) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
|
@ -6,110 +6,109 @@
|
|||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
|
||||
import { OverviewRulerLane } from 'vs/editor/common/model';
|
||||
import * as languageConfiguration from 'vs/editor/common/languages/languageConfiguration';
|
||||
import { score } from 'vs/editor/common/languageSelector';
|
||||
import * as languageConfiguration from 'vs/editor/common/languages/languageConfiguration';
|
||||
import { OverviewRulerLane } from 'vs/editor/common/model';
|
||||
import { ExtensionIdentifierSet, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import * as files from 'vs/platform/files/common/files';
|
||||
import { ExtHostContext, MainContext, CandidatePortSource, ExtHostLogLevelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { UIKind } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService, ILoggerService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { matchesScheme } from 'vs/platform/opener/common/opener';
|
||||
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { TelemetryTrustedValue } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { EditSessionIdentityMatch } from 'vs/platform/workspace/common/editSessions';
|
||||
import { CandidatePortSource, ExtHostContext, ExtHostLogLevelServiceShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostRelatedInformation } from 'vs/workbench/api/common/extHostAiRelatedInformation';
|
||||
import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication';
|
||||
import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits';
|
||||
import { ExtHostChat } from 'vs/workbench/api/common/extHostChat';
|
||||
import { ExtHostChatAgents2 } from 'vs/workbench/api/common/extHostChatAgents2';
|
||||
import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider';
|
||||
import { ExtHostChatVariables } from 'vs/workbench/api/common/extHostChatVariables';
|
||||
import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard';
|
||||
import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets';
|
||||
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { createExtHostComments } from 'vs/workbench/api/common/extHostComments';
|
||||
import { ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEditors';
|
||||
import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService';
|
||||
import { IExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations';
|
||||
import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics';
|
||||
import { ExtHostDialogs } from 'vs/workbench/api/common/extHostDialogs';
|
||||
import { ExtHostDocumentContentProvider } from 'vs/workbench/api/common/extHostDocumentContentProviders';
|
||||
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/common/extHostDocumentSaveParticipant';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
||||
import { ExtHostAiEmbeddingVector } from 'vs/workbench/api/common/extHostEmbeddingVector';
|
||||
import { Extension, IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { ExtHostFileSystem } from 'vs/workbench/api/common/extHostFileSystem';
|
||||
import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
|
||||
import { ExtHostFileSystemEventService, FileSystemWatcherCreateOptions } from 'vs/workbench/api/common/extHostFileSystemEventService';
|
||||
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { ExtHostInteractiveEditor } from 'vs/workbench/api/common/extHostInlineChat';
|
||||
import { ExtHostInteractive } from 'vs/workbench/api/common/extHostInteractive';
|
||||
import { ExtHostIssueReporter } from 'vs/workbench/api/common/extHostIssueReporter';
|
||||
import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService';
|
||||
import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures';
|
||||
import { ExtHostLanguages } from 'vs/workbench/api/common/extHostLanguages';
|
||||
import { IExtHostLocalizationService } from 'vs/workbench/api/common/extHostLocalizationService';
|
||||
import { IExtHostManagedSockets } from 'vs/workbench/api/common/extHostManagedSockets';
|
||||
import { ExtHostMessageService } from 'vs/workbench/api/common/extHostMessageService';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { ExtHostNotebookDocumentSaveParticipant } from 'vs/workbench/api/common/extHostNotebookDocumentSaveParticipant';
|
||||
import { ExtHostNotebookDocuments } from 'vs/workbench/api/common/extHostNotebookDocuments';
|
||||
import { ExtHostNotebookEditors } from 'vs/workbench/api/common/extHostNotebookEditors';
|
||||
import { ExtHostNotebookKernels } from 'vs/workbench/api/common/extHostNotebookKernels';
|
||||
import { ExtHostNotebookRenderers } from 'vs/workbench/api/common/extHostNotebookRenderers';
|
||||
import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput';
|
||||
import { ExtHostProfileContentHandlers } from 'vs/workbench/api/common/extHostProfileContentHandler';
|
||||
import { ExtHostProgress } from 'vs/workbench/api/common/extHostProgress';
|
||||
import { ExtHostQuickDiff } from 'vs/workbench/api/common/extHostQuickDiff';
|
||||
import { createExtHostQuickOpen } from 'vs/workbench/api/common/extHostQuickOpen';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { ExtHostSCM } from 'vs/workbench/api/common/extHostSCM';
|
||||
import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch';
|
||||
import { IExtHostSecretState } from 'vs/workbench/api/common/extHostSecretState';
|
||||
import { ExtHostShare } from 'vs/workbench/api/common/extHostShare';
|
||||
import { ExtHostSpeech } from 'vs/workbench/api/common/extHostSpeech';
|
||||
import { ExtHostStatusBar } from 'vs/workbench/api/common/extHostStatusBar';
|
||||
import { IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import { IExtHostTask } from 'vs/workbench/api/common/extHostTask';
|
||||
import { ExtHostTelemetryLogger, IExtHostTelemetry, isNewAppInstall } from 'vs/workbench/api/common/extHostTelemetry';
|
||||
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting';
|
||||
import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors';
|
||||
import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming';
|
||||
import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline';
|
||||
import { ExtHostTreeViews } from 'vs/workbench/api/common/extHostTreeViews';
|
||||
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
|
||||
import { TelemetryTrustedValue } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { ExtHostUriOpeners } from 'vs/workbench/api/common/extHostUriOpener';
|
||||
import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService';
|
||||
import { ExtHostUrls } from 'vs/workbench/api/common/extHostUrls';
|
||||
import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels';
|
||||
import { ExtHostWebviewViews } from 'vs/workbench/api/common/extHostWebviewView';
|
||||
import { IExtHostWindow } from 'vs/workbench/api/common/extHostWindow';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import type * as vscode from 'vscode';
|
||||
import { ExtensionIdentifierSet, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets';
|
||||
import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService';
|
||||
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtHostDecorations } from 'vs/workbench/api/common/extHostDecorations';
|
||||
import { IExtHostTask } from 'vs/workbench/api/common/extHostTask';
|
||||
import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService';
|
||||
import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch';
|
||||
import { ILoggerService, ILogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming';
|
||||
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication';
|
||||
import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
|
||||
import { ExtHostWebviewViews } from 'vs/workbench/api/common/extHostWebviewView';
|
||||
import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEditors';
|
||||
import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels';
|
||||
import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits';
|
||||
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
|
||||
import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting';
|
||||
import { ExtHostUriOpeners } from 'vs/workbench/api/common/extHostUriOpener';
|
||||
import { IExtHostSecretState } from 'vs/workbench/api/common/extHostSecretState';
|
||||
import { IExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
||||
import { ExtHostTelemetryLogger, IExtHostTelemetry, isNewAppInstall } from 'vs/workbench/api/common/extHostTelemetry';
|
||||
import { ExtHostNotebookKernels } from 'vs/workbench/api/common/extHostNotebookKernels';
|
||||
import { TextSearchCompleteMessageType } from 'vs/workbench/services/search/common/searchExtTypes';
|
||||
import { ExtHostNotebookRenderers } from 'vs/workbench/api/common/extHostNotebookRenderers';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { matchesScheme } from 'vs/platform/opener/common/opener';
|
||||
import { ExtHostNotebookEditors } from 'vs/workbench/api/common/extHostNotebookEditors';
|
||||
import { ExtHostNotebookDocuments } from 'vs/workbench/api/common/extHostNotebookDocuments';
|
||||
import { ExtHostInteractive } from 'vs/workbench/api/common/extHostInteractive';
|
||||
import { combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IExtHostLocalizationService } from 'vs/workbench/api/common/extHostLocalizationService';
|
||||
import { EditSessionIdentityMatch } from 'vs/platform/workspace/common/editSessions';
|
||||
import { ExtHostProfileContentHandlers } from 'vs/workbench/api/common/extHostProfileContentHandler';
|
||||
import { ExtHostQuickDiff } from 'vs/workbench/api/common/extHostQuickDiff';
|
||||
import { ExtHostChat } from 'vs/workbench/api/common/extHostChat';
|
||||
import { ExtHostInteractiveEditor } from 'vs/workbench/api/common/extHostInlineChat';
|
||||
import { ExtHostNotebookDocumentSaveParticipant } from 'vs/workbench/api/common/extHostNotebookDocumentSaveParticipant';
|
||||
import { ExtHostIssueReporter } from 'vs/workbench/api/common/extHostIssueReporter';
|
||||
import { IExtHostManagedSockets } from 'vs/workbench/api/common/extHostManagedSockets';
|
||||
import { ExtHostShare } from 'vs/workbench/api/common/extHostShare';
|
||||
import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider';
|
||||
import { ExtHostSpeech } from 'vs/workbench/api/common/extHostSpeech';
|
||||
import { ExtHostChatVariables } from 'vs/workbench/api/common/extHostChatVariables';
|
||||
import { ExtHostRelatedInformation } from 'vs/workbench/api/common/extHostAiRelatedInformation';
|
||||
import { ExtHostAiEmbeddingVector } from 'vs/workbench/api/common/extHostEmbeddingVector';
|
||||
import { ExtHostChatAgents } from 'vs/workbench/api/common/extHostChatAgents';
|
||||
import { ExtHostChatAgents2 } from 'vs/workbench/api/common/extHostChatAgents2';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import { UIKind } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
|
||||
import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import { TextSearchCompleteMessageType } from 'vs/workbench/services/search/common/searchExtTypes';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
export interface IExtensionRegistries {
|
||||
mine: ExtensionDescriptionRegistry;
|
||||
|
@ -210,10 +209,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands, extHostLogService));
|
||||
const extHostInteractiveEditor = rpcProtocol.set(ExtHostContext.ExtHostInlineChat, new ExtHostInteractiveEditor(rpcProtocol, extHostCommands, extHostDocuments, extHostLogService));
|
||||
const extHostChatProvider = rpcProtocol.set(ExtHostContext.ExtHostChatProvider, new ExtHostChatProvider(rpcProtocol, extHostLogService));
|
||||
const extHostChatAgents = rpcProtocol.set(ExtHostContext.ExtHostChatAgents, new ExtHostChatAgents(rpcProtocol, extHostChatProvider, extHostLogService));
|
||||
const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostChatProvider, extHostLogService));
|
||||
const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol));
|
||||
const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol, extHostLogService));
|
||||
const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol));
|
||||
const extHostAiRelatedInformation = rpcProtocol.set(ExtHostContext.ExtHostAiRelatedInformation, new ExtHostRelatedInformation(rpcProtocol));
|
||||
const extHostAiEmbeddingVector = rpcProtocol.set(ExtHostContext.ExtHostAiEmbeddingVector, new ExtHostAiEmbeddingVector(rpcProtocol));
|
||||
const extHostIssueReporter = rpcProtocol.set(ExtHostContext.ExtHostIssueReporter, new ExtHostIssueReporter(rpcProtocol));
|
||||
|
@ -1382,10 +1380,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
checkProposedApiEnabled(extension, 'chatAgents2');
|
||||
return extHostChatAgents2.createChatAgent(extension, name, handler);
|
||||
},
|
||||
registerAgent(name: string, agent: vscode.ChatAgent, metadata: vscode.ChatAgentMetadata) {
|
||||
checkProposedApiEnabled(extension, 'chatAgents');
|
||||
return extHostChatAgents.registerAgent(extension.identifier, name, agent, metadata);
|
||||
}
|
||||
};
|
||||
|
||||
// namespace: speech
|
||||
|
|
|
@ -52,8 +52,7 @@ import { IRevealOptions, ITreeItem, IViewBadge } from 'vs/workbench/common/views
|
|||
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { IChatAgentCommand, IChatAgentMetadata, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } from 'vs/workbench/contrib/chat/common/chatProvider';
|
||||
import { IChatAgentDetection, IChatDynamicRequest, IChatFollowup, IChatReplyFollowup, IChatResponseErrorDetails, IChatUserActionEvent, ISlashCommand, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { IChatAgentDetection, IChatDynamicRequest, IChatFollowup, IChatReplyFollowup, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatRequestVariableValue, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
import { DebugConfigurationProviderTriggerKind, IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatMessageResponse, IInlineChatProgressItem, IInlineChatRequest, IInlineChatSession, InlineChatResponseFeedbackKind } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
|
@ -63,6 +62,7 @@ import { ICellExecutionComplete, ICellExecutionStateUpdate } from 'vs/workbench/
|
|||
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
import { InputValidationType } from 'vs/workbench/contrib/scm/common/scm';
|
||||
import { IWorkspaceSymbol, NotebookPriorityInfo } from 'vs/workbench/contrib/search/common/search';
|
||||
import { IRawClosedNotebookFileMatch } from 'vs/workbench/contrib/search/common/searchNotebookHelpers';
|
||||
import { ISpeechProviderMetadata, ISpeechToTextEvent } from 'vs/workbench/contrib/speech/common/speechService';
|
||||
import { CoverageDetails, ExtensionRunTestsRequest, ICallProfileRunHandler, IFileCoverage, ISerializedTestResults, IStartControllerTests, ITestItem, ITestMessage, ITestRunProfile, ITestRunTask, ResolvedTestRunRequest, TestResultState, TestsDiffOp } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
|
@ -80,7 +80,6 @@ import { CandidatePort } from 'vs/workbench/services/remote/common/tunnelModel';
|
|||
import { ITextQueryBuilderOptions } from 'vs/workbench/services/search/common/queryBuilder';
|
||||
import * as search from 'vs/workbench/services/search/common/search';
|
||||
import { ISaveProfileResult } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
|
||||
import { IRawClosedNotebookFileMatch } from 'vs/workbench/contrib/search/common/searchNotebookHelpers';
|
||||
|
||||
export interface IWorkspaceData extends IStaticWorkspaceData {
|
||||
folders: { uri: UriComponents; name: string; index: number }[];
|
||||
|
@ -1166,12 +1165,6 @@ export interface ExtHostChatProviderShape {
|
|||
$handleResponseFragment(requestId: number, chunk: IChatResponseFragment): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadChatAgentsShape extends IDisposable {
|
||||
$registerAgent(handle: number, name: string, metadata: IChatAgentMetadata & { subCommands: IChatAgentCommand[] }): void;
|
||||
$unregisterAgent(handle: number): void;
|
||||
$handleProgressChunk(requestId: number, chunk: IChatSlashFragment): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IExtensionChatAgentMetadata extends Dto<IChatAgentMetadata> {
|
||||
hasSlashCommands?: boolean;
|
||||
hasFollowup?: boolean;
|
||||
|
@ -1184,10 +1177,6 @@ export interface MainThreadChatAgentsShape2 extends IDisposable {
|
|||
$handleProgressChunk(requestId: string, chunk: IChatResponseProgressDto, responsePartHandle?: number): Promise<number | void>;
|
||||
}
|
||||
|
||||
export interface ExtHostChatAgentsShape {
|
||||
$invokeAgent(handle: number, requestId: number, prompt: string, context: { history: IChatMessage[] }, token: CancellationToken): Promise<any>;
|
||||
}
|
||||
|
||||
export interface ExtHostChatAgentsShape2 {
|
||||
$invokeAgent(handle: number, sessionId: string, requestId: string, request: IChatAgentRequest, context: { history: IChatMessage[] }, token: CancellationToken): Promise<IChatAgentResult | undefined>;
|
||||
$provideSlashCommands(handle: number, token: CancellationToken): Promise<IChatAgentCommand[]>;
|
||||
|
@ -1263,7 +1252,6 @@ export type IDocumentContextDto = {
|
|||
|
||||
export type IChatResponseProgressDto =
|
||||
| { content: string | IMarkdownString }
|
||||
| { requestId: string }
|
||||
| { placeholder: string }
|
||||
| { treeData: IChatResponseProgressFileTreeData }
|
||||
| { documents: IDocumentContextDto[] }
|
||||
|
@ -1276,18 +1264,13 @@ export interface MainThreadChatShape extends IDisposable {
|
|||
$acceptChatState(sessionId: number, state: any): Promise<void>;
|
||||
$sendRequestToProvider(providerId: string, message: IChatDynamicRequest): void;
|
||||
$unregisterChatProvider(handle: number): Promise<void>;
|
||||
$acceptResponseProgress(handle: number, sessionId: number, progress: IChatResponseProgressDto, responsePartHandle?: number): Promise<number | void>;
|
||||
$transferChatSession(sessionId: number, toWorkspace: UriComponents): void;
|
||||
}
|
||||
|
||||
export interface ExtHostChatShape {
|
||||
$prepareChat(handle: number, initialState: any, token: CancellationToken): Promise<IChatDto | undefined>;
|
||||
$prepareChat(handle: number, token: CancellationToken): Promise<IChatDto | undefined>;
|
||||
$provideWelcomeMessage(handle: number, token: CancellationToken): Promise<(string | IMarkdownString | IChatReplyFollowup[])[] | undefined>;
|
||||
$provideSampleQuestions(handle: number, token: CancellationToken): Promise<IChatReplyFollowup[] | undefined>;
|
||||
$provideFollowups(handle: number, sessionId: number, token: CancellationToken): Promise<IChatFollowup[] | undefined>;
|
||||
$provideReply(handle: number, sessionId: number, request: IChatRequestDto, token: CancellationToken): Promise<IChatResponseDto | undefined>;
|
||||
$removeRequest(handle: number, sessionId: number, requestId: string): void;
|
||||
$provideSlashCommands(handle: number, sessionId: number, token: CancellationToken): Promise<ISlashCommand[] | undefined>;
|
||||
$releaseSession(sessionId: number): void;
|
||||
$onDidPerformUserAction(event: IChatUserActionEvent): Promise<void>;
|
||||
}
|
||||
|
@ -2721,7 +2704,6 @@ export const MainContext = {
|
|||
MainThreadAuthentication: createProxyIdentifier<MainThreadAuthenticationShape>('MainThreadAuthentication'),
|
||||
MainThreadBulkEdits: createProxyIdentifier<MainThreadBulkEditsShape>('MainThreadBulkEdits'),
|
||||
MainThreadChatProvider: createProxyIdentifier<MainThreadChatProviderShape>('MainThreadChatProvider'),
|
||||
MainThreadChatAgents: createProxyIdentifier<MainThreadChatAgentsShape>('MainThreadChatAgents'),
|
||||
MainThreadChatAgents2: createProxyIdentifier<MainThreadChatAgentsShape2>('MainThreadChatAgents2'),
|
||||
MainThreadChatVariables: createProxyIdentifier<MainThreadChatVariablesShape>('MainThreadChatVariables'),
|
||||
MainThreadClipboard: createProxyIdentifier<MainThreadClipboardShape>('MainThreadClipboard'),
|
||||
|
@ -2844,7 +2826,6 @@ export const ExtHostContext = {
|
|||
ExtHostInteractive: createProxyIdentifier<ExtHostInteractiveShape>('ExtHostInteractive'),
|
||||
ExtHostInlineChat: createProxyIdentifier<ExtHostInlineChatShape>('ExtHostInlineChatShape'),
|
||||
ExtHostChat: createProxyIdentifier<ExtHostChatShape>('ExtHostChat'),
|
||||
ExtHostChatAgents: createProxyIdentifier<ExtHostChatAgentsShape>('ExtHostChatAgents'),
|
||||
ExtHostChatAgents2: createProxyIdentifier<ExtHostChatAgentsShape2>('ExtHostChatAgents'),
|
||||
ExtHostChatVariables: createProxyIdentifier<ExtHostChatVariablesShape>('ExtHostChatVariables'),
|
||||
ExtHostChatProvider: createProxyIdentifier<ExtHostChatProviderShape>('ExtHostChatProvider'),
|
||||
|
|
|
@ -3,19 +3,15 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { raceCancellation } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IRelaxedExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ExtHostChatShape, IChatRequestDto, IChatResponseDto, IChatDto, IMainContext, MainContext, MainThreadChatShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostChatShape, IChatDto, IMainContext, MainContext, MainThreadChatShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { IChatFollowup, IChatReplyFollowup, IChatUserActionEvent, ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatReplyFollowup, IChatUserActionEvent } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
class ChatProviderWrapper<T> {
|
||||
|
@ -36,7 +32,6 @@ export class ExtHostChat implements ExtHostChatShape {
|
|||
private readonly _chatProvider = new Map<number, ChatProviderWrapper<vscode.InteractiveSessionProvider>>();
|
||||
|
||||
private readonly _chatSessions = new Map<number, vscode.InteractiveSession>();
|
||||
// private readonly _providerResponsesByRequestId = new Map<number, { response: vscode.ProviderResult<vscode.InteractiveResponse | vscode.InteractiveResponseForProgress>; sessionId: number }>();
|
||||
|
||||
private readonly _onDidPerformUserAction = new Emitter<vscode.InteractiveSessionUserActionEvent>();
|
||||
public readonly onDidPerformUserAction = this._onDidPerformUserAction.event;
|
||||
|
@ -45,7 +40,6 @@ export class ExtHostChat implements ExtHostChatShape {
|
|||
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
private readonly logService: ILogService
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadChat);
|
||||
}
|
||||
|
@ -75,13 +69,13 @@ export class ExtHostChat implements ExtHostChatShape {
|
|||
this._proxy.$sendRequestToProvider(providerId, message);
|
||||
}
|
||||
|
||||
async $prepareChat(handle: number, initialState: any, token: CancellationToken): Promise<IChatDto | undefined> {
|
||||
async $prepareChat(handle: number, token: CancellationToken): Promise<IChatDto | undefined> {
|
||||
const entry = this._chatProvider.get(handle);
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const session = await entry.provider.prepareSession(initialState, token);
|
||||
const session = await entry.provider.prepareSession(token);
|
||||
if (!session) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -124,25 +118,6 @@ export class ExtHostChat implements ExtHostChatShape {
|
|||
});
|
||||
}
|
||||
|
||||
async $provideFollowups(handle: number, sessionId: number, token: CancellationToken): Promise<IChatFollowup[] | undefined> {
|
||||
const entry = this._chatProvider.get(handle);
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const realSession = this._chatSessions.get(sessionId);
|
||||
if (!realSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entry.provider.provideFollowups) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const rawFollowups = await entry.provider.provideFollowups(realSession, token);
|
||||
return rawFollowups?.map(f => typeConvert.ChatFollowup.from(f));
|
||||
}
|
||||
|
||||
async $provideSampleQuestions(handle: number, token: CancellationToken): Promise<IChatReplyFollowup[] | undefined> {
|
||||
const entry = this._chatProvider.get(handle);
|
||||
if (!entry) {
|
||||
|
@ -161,121 +136,6 @@ export class ExtHostChat implements ExtHostChatShape {
|
|||
return rawFollowups?.map(f => typeConvert.ChatReplyFollowup.from(f));
|
||||
}
|
||||
|
||||
$removeRequest(handle: number, sessionId: number, requestId: string): void {
|
||||
const entry = this._chatProvider.get(handle);
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const realSession = this._chatSessions.get(sessionId);
|
||||
if (!realSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entry.provider.removeRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
entry.provider.removeRequest(realSession, requestId);
|
||||
}
|
||||
|
||||
async $provideReply(handle: number, sessionId: number, request: IChatRequestDto, token: CancellationToken): Promise<IChatResponseDto | undefined> {
|
||||
const entry = this._chatProvider.get(handle);
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const realSession = this._chatSessions.get(sessionId);
|
||||
if (!realSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
const requestObj: vscode.InteractiveRequest = {
|
||||
session: realSession,
|
||||
message: request.message,
|
||||
variables: {}
|
||||
};
|
||||
|
||||
if (request.variables) {
|
||||
for (const key of Object.keys(request.variables)) {
|
||||
requestObj.variables[key] = request.variables[key].map(typeConvert.ChatVariable.to);
|
||||
}
|
||||
}
|
||||
|
||||
const stopWatch = StopWatch.create(false);
|
||||
let firstProgress: number | undefined;
|
||||
const progressObj: vscode.Progress<vscode.InteractiveProgress> = {
|
||||
report: (progress: vscode.InteractiveProgress) => {
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof firstProgress === 'undefined') {
|
||||
firstProgress = stopWatch.elapsed();
|
||||
}
|
||||
|
||||
const convertedProgress = typeConvert.ChatResponseProgress.from(entry.extension, progress);
|
||||
if ('placeholder' in progress && 'resolvedContent' in progress) {
|
||||
const resolvedContent = Promise.all([this._proxy.$acceptResponseProgress(handle, sessionId, convertedProgress), progress.resolvedContent]);
|
||||
raceCancellation(resolvedContent, token).then((res) => {
|
||||
if (!res) {
|
||||
return; /* Cancelled */
|
||||
}
|
||||
const [progressHandle, progressContent] = res;
|
||||
this._proxy.$acceptResponseProgress(handle, sessionId, progressContent, progressHandle ?? undefined);
|
||||
});
|
||||
} else {
|
||||
this._proxy.$acceptResponseProgress(handle, sessionId, convertedProgress);
|
||||
}
|
||||
}
|
||||
};
|
||||
let result: vscode.InteractiveResponseForProgress | undefined | null;
|
||||
try {
|
||||
result = await entry.provider.provideResponseWithProgress(requestObj, progressObj, token);
|
||||
if (!result) {
|
||||
result = { errorDetails: { message: localize('emptyResponse', "Provider returned null response") } };
|
||||
}
|
||||
} catch (err) {
|
||||
result = { errorDetails: { message: localize('errorResponse', "Error from provider: {0}", err.message), responseIsIncomplete: true } };
|
||||
this.logService.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
// Check that the session has not been released since the request started
|
||||
if (realSession.saveState && this._chatSessions.has(sessionId)) {
|
||||
const newState = realSession.saveState();
|
||||
this._proxy.$acceptChatState(sessionId, newState);
|
||||
}
|
||||
} catch (err) {
|
||||
this.logService.warn(err);
|
||||
}
|
||||
|
||||
const timings = { firstProgress: firstProgress ?? 0, totalElapsed: stopWatch.elapsed() };
|
||||
return { errorDetails: result.errorDetails, timings };
|
||||
}
|
||||
|
||||
async $provideSlashCommands(handle: number, sessionId: number, token: CancellationToken): Promise<ISlashCommand[] | undefined> {
|
||||
const entry = this._chatProvider.get(handle);
|
||||
if (!entry) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const realSession = this._chatSessions.get(sessionId);
|
||||
if (!realSession) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!entry.provider.provideSlashCommands) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const slashCommands = await entry.provider.provideSlashCommands(realSession, token);
|
||||
return slashCommands?.map(c => (<ISlashCommand>{
|
||||
...c,
|
||||
kind: typeConvert.CompletionItemKind.from(c.kind)
|
||||
}));
|
||||
}
|
||||
|
||||
$releaseSession(sessionId: number) {
|
||||
this._chatSessions.delete(sessionId);
|
||||
}
|
||||
|
|
|
@ -1,91 +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 { DeferredPromise, raceCancellation } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Progress } from 'vs/platform/progress/common/progress';
|
||||
import { ExtHostChatAgentsShape, IMainContext, MainContext, MainThreadChatAgentsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider';
|
||||
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { ChatMessageRole } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
export class ExtHostChatAgents implements ExtHostChatAgentsShape {
|
||||
|
||||
private static _idPool = 0;
|
||||
|
||||
private readonly _agents = new Map<number, { extension: ExtensionIdentifier; agent: vscode.ChatAgent }>();
|
||||
private readonly _proxy: MainThreadChatAgentsShape;
|
||||
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
private readonly _extHostChatProvider: ExtHostChatProvider,
|
||||
private readonly _logService: ILogService,
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadChatAgents);
|
||||
}
|
||||
|
||||
registerAgent(extension: ExtensionIdentifier, name: string, agent: vscode.ChatAgent, metadata: vscode.ChatAgentMetadata): IDisposable {
|
||||
const handle = ExtHostChatAgents._idPool++;
|
||||
this._agents.set(handle, { extension, agent });
|
||||
this._proxy.$registerAgent(handle, name, metadata);
|
||||
|
||||
return toDisposable(() => {
|
||||
this._proxy.$unregisterAgent(handle);
|
||||
this._agents.delete(handle);
|
||||
});
|
||||
}
|
||||
|
||||
async $invokeAgent(handle: number, requestId: number, prompt: string, context: { history: IChatMessage[] }, token: CancellationToken): Promise<any> {
|
||||
const data = this._agents.get(handle);
|
||||
if (!data) {
|
||||
this._logService.warn(`[CHAT](${handle}) CANNOT invoke agent because the agent is not registered`);
|
||||
return;
|
||||
}
|
||||
|
||||
let done = false;
|
||||
function throwIfDone() {
|
||||
if (done) {
|
||||
throw new Error('Only valid while executing the command');
|
||||
}
|
||||
}
|
||||
|
||||
const commandExecution = new DeferredPromise<void>();
|
||||
token.onCancellationRequested(() => commandExecution.complete());
|
||||
setTimeout(() => commandExecution.complete(), 10 * 1000);
|
||||
this._extHostChatProvider.allowListExtensionWhile(data.extension, commandExecution.p);
|
||||
|
||||
const task = data.agent(
|
||||
{ role: ChatMessageRole.User, content: prompt },
|
||||
{ history: context.history.map(typeConvert.ChatMessage.to) },
|
||||
new Progress<vscode.ChatAgentResponse>(p => {
|
||||
throwIfDone();
|
||||
this._proxy.$handleProgressChunk(requestId, { content: isInteractiveProgressFileTree(p.message) ? p.message : p.message.value });
|
||||
}),
|
||||
token
|
||||
);
|
||||
|
||||
try {
|
||||
return await raceCancellation(Promise.resolve(task).then((v) => {
|
||||
if (v && 'followUp' in v) {
|
||||
const convertedFollowup = v?.followUp?.map(f => typeConvert.ChatFollowup.from(f));
|
||||
return { followUp: convertedFollowup };
|
||||
}
|
||||
return undefined;
|
||||
}), token);
|
||||
} finally {
|
||||
done = true;
|
||||
commandExecution.complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isInteractiveProgressFileTree(thing: unknown): thing is vscode.InteractiveProgressFileTree {
|
||||
return !!thing && typeof thing === 'object' && 'treeData' in thing;
|
||||
}
|
|
@ -217,6 +217,9 @@ class ExtHostChatAgent {
|
|||
private _fullName: string | undefined;
|
||||
private _iconPath: vscode.Uri | { light: vscode.Uri; dark: vscode.Uri } | vscode.ThemeIcon | undefined;
|
||||
private _isDefault: boolean | undefined;
|
||||
private _helpTextPrefix: string | vscode.MarkdownString | undefined;
|
||||
private _helpTextPostfix: string | vscode.MarkdownString | undefined;
|
||||
private _sampleRequest?: string;
|
||||
private _isSecondary: boolean | undefined;
|
||||
private _onDidReceiveFeedback = new Emitter<vscode.ChatAgentResult2Feedback>();
|
||||
private _onDidPerformAction = new Emitter<vscode.ChatAgentUserActionEvent>();
|
||||
|
@ -259,7 +262,14 @@ class ExtHostChatAgent {
|
|||
return [];
|
||||
}
|
||||
this._lastSlashCommands = result;
|
||||
return result.map(c => ({ name: c.name, description: c.description, followupPlaceholder: c.followupPlaceholder, shouldRepopulate: c.shouldRepopulate }));
|
||||
return result
|
||||
.map(c => ({
|
||||
name: c.name,
|
||||
description: c.description,
|
||||
followupPlaceholder: c.followupPlaceholder,
|
||||
shouldRepopulate: c.shouldRepopulate,
|
||||
sampleRequest: c.sampleRequest
|
||||
}));
|
||||
}
|
||||
|
||||
async provideFollowups(result: vscode.ChatAgentResult2, token: CancellationToken): Promise<IChatFollowup[]> {
|
||||
|
@ -300,6 +310,9 @@ class ExtHostChatAgent {
|
|||
hasFollowup: this._followupProvider !== undefined,
|
||||
isDefault: this._isDefault,
|
||||
isSecondary: this._isSecondary,
|
||||
helpTextPrefix: (!this._helpTextPrefix || typeof this._helpTextPrefix === 'string') ? this._helpTextPrefix : typeConvert.MarkdownString.from(this._helpTextPrefix),
|
||||
helpTextPostfix: (!this._helpTextPostfix || typeof this._helpTextPostfix === 'string') ? this._helpTextPostfix : typeConvert.MarkdownString.from(this._helpTextPostfix),
|
||||
sampleRequest: this._sampleRequest,
|
||||
});
|
||||
updateScheduled = false;
|
||||
});
|
||||
|
@ -354,6 +367,32 @@ class ExtHostChatAgent {
|
|||
that._isDefault = v;
|
||||
updateMetadataSoon();
|
||||
},
|
||||
get helpTextPrefix() {
|
||||
checkProposedApiEnabled(that.extension, 'defaultChatAgent');
|
||||
return that._helpTextPrefix;
|
||||
},
|
||||
set helpTextPrefix(v) {
|
||||
checkProposedApiEnabled(that.extension, 'defaultChatAgent');
|
||||
if (!that._isDefault) {
|
||||
throw new Error('helpTextPrefix is only available on the default chat agent');
|
||||
}
|
||||
|
||||
that._helpTextPrefix = v;
|
||||
updateMetadataSoon();
|
||||
},
|
||||
get helpTextPostfix() {
|
||||
checkProposedApiEnabled(that.extension, 'defaultChatAgent');
|
||||
return that._helpTextPostfix;
|
||||
},
|
||||
set helpTextPostfix(v) {
|
||||
checkProposedApiEnabled(that.extension, 'defaultChatAgent');
|
||||
if (!that._isDefault) {
|
||||
throw new Error('helpTextPostfix is only available on the default chat agent');
|
||||
}
|
||||
|
||||
that._helpTextPostfix = v;
|
||||
updateMetadataSoon();
|
||||
},
|
||||
get isSecondary() {
|
||||
checkProposedApiEnabled(that.extension, 'defaultChatAgent');
|
||||
return that._isSecondary;
|
||||
|
@ -363,6 +402,13 @@ class ExtHostChatAgent {
|
|||
that._isSecondary = v;
|
||||
updateMetadataSoon();
|
||||
},
|
||||
get sampleRequest() {
|
||||
return that._sampleRequest;
|
||||
},
|
||||
set sampleRequest(v) {
|
||||
that._sampleRequest = v;
|
||||
updateMetadataSoon();
|
||||
},
|
||||
get onDidReceiveFeedback() {
|
||||
return that._onDidReceiveFeedback.event;
|
||||
},
|
||||
|
|
|
@ -2157,20 +2157,10 @@ export namespace DataTransfer {
|
|||
}
|
||||
|
||||
export namespace ChatReplyFollowup {
|
||||
export function to(followup: IChatReplyFollowup): vscode.InteractiveSessionReplyFollowup {
|
||||
return {
|
||||
message: followup.message,
|
||||
metadata: followup.metadata,
|
||||
title: followup.title,
|
||||
tooltip: followup.tooltip,
|
||||
};
|
||||
}
|
||||
|
||||
export function from(followup: vscode.InteractiveSessionReplyFollowup): IChatReplyFollowup {
|
||||
return {
|
||||
kind: 'reply',
|
||||
message: followup.message,
|
||||
metadata: followup.metadata,
|
||||
title: followup.title,
|
||||
tooltip: followup.tooltip,
|
||||
};
|
||||
|
@ -2178,7 +2168,7 @@ export namespace ChatReplyFollowup {
|
|||
}
|
||||
|
||||
export namespace ChatFollowup {
|
||||
export function from(followup: string | vscode.InteractiveSessionFollowup): IChatFollowup {
|
||||
export function from(followup: string | vscode.ChatAgentFollowup): IChatFollowup {
|
||||
if (typeof followup === 'string') {
|
||||
return <IChatReplyFollowup>{ title: followup, message: followup, kind: 'reply' };
|
||||
} else if ('commandId' in followup) {
|
||||
|
@ -2303,11 +2293,9 @@ export namespace InteractiveEditorResponseFeedbackKind {
|
|||
}
|
||||
|
||||
export namespace ChatResponseProgress {
|
||||
export function from(extension: IExtensionDescription, progress: vscode.InteractiveProgress | vscode.ChatAgentExtendedProgress): extHostProtocol.IChatResponseProgressDto {
|
||||
export function from(extension: IExtensionDescription, progress: vscode.ChatAgentExtendedProgress): extHostProtocol.IChatResponseProgressDto {
|
||||
if ('placeholder' in progress && 'resolvedContent' in progress) {
|
||||
return { placeholder: progress.placeholder };
|
||||
} else if ('responseId' in progress) {
|
||||
return { requestId: progress.responseId };
|
||||
} else if ('markdownContent' in progress) {
|
||||
checkProposedApiEnabled(extension, 'chatAgents2Additions');
|
||||
return { content: MarkdownString.from(progress.markdownContent) };
|
||||
|
|
|
@ -8,7 +8,7 @@ import { localize } from 'vs/nls';
|
|||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { EventHelper, addDisposableListener, getActiveDocument } from 'vs/base/browser/dom';
|
||||
import { EventHelper, addDisposableListener, getActiveDocument, getWindow } from 'vs/base/browser/dom';
|
||||
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
|
@ -78,12 +78,12 @@ export class TextInputActionsProvider extends Disposable implements IWorkbenchCo
|
|||
|
||||
// Context menu support in input/textarea
|
||||
this._register(Event.runAndSubscribe(this.layoutService.onDidAddContainer, container => {
|
||||
const listener = addDisposableListener(container, 'contextmenu', e => this.onContextMenu(e));
|
||||
const listener = addDisposableListener(container, 'contextmenu', e => this.onContextMenu(getWindow(container), e));
|
||||
this._register(Event.filter(this.layoutService.onDidRemoveContainer, removed => removed === container, this._store)(() => listener.dispose()));
|
||||
}, this.layoutService.container));
|
||||
}
|
||||
|
||||
private onContextMenu(e: MouseEvent): void {
|
||||
private onContextMenu(targetWindow: Window, e: MouseEvent): void {
|
||||
if (e.defaultPrevented) {
|
||||
return; // make sure to not show these actions by accident if component indicated to prevent
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ export class TextInputActionsProvider extends Disposable implements IWorkbenchCo
|
|||
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
const event = new StandardMouseEvent(e);
|
||||
const event = new StandardMouseEvent(targetWindow, e);
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => event,
|
||||
|
|
|
@ -9,7 +9,7 @@ import { IActivity } from 'vs/workbench/services/activity/common/activity';
|
|||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ActionBar, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { CompositeActionViewItem, CompositeOverflowActivityAction, CompositeOverflowActivityActionViewItem, CompositeBarAction, ICompositeBar, ICompositeBarColors, IActivityHoverOptions } from 'vs/workbench/browser/parts/compositeBarActions';
|
||||
import { Dimension, $, addDisposableListener, EventType, EventHelper, isAncestor } from 'vs/base/browser/dom';
|
||||
import { Dimension, $, addDisposableListener, EventType, EventHelper, isAncestor, getWindow } from 'vs/base/browser/dom';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
|
@ -227,9 +227,9 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
|||
}));
|
||||
|
||||
// Contextmenu for composites
|
||||
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, e => this.showContextMenu(e)));
|
||||
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, e => this.showContextMenu(getWindow(parent), e)));
|
||||
this._register(Gesture.addTarget(parent));
|
||||
this._register(addDisposableListener(parent, TouchEventType.Contextmenu, e => this.showContextMenu(e)));
|
||||
this._register(addDisposableListener(parent, TouchEventType.Contextmenu, e => this.showContextMenu(getWindow(parent), e)));
|
||||
|
||||
// Register a drop target on the whole bar to prevent forbidden feedback
|
||||
let insertDropBefore: Before2D | undefined = undefined;
|
||||
|
@ -620,10 +620,10 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
|||
return this.model.visibleItems.filter(c => overflowingIds.includes(c.id)).map(item => { return { id: item.id, name: this.getAction(item.id)?.label || item.name }; });
|
||||
}
|
||||
|
||||
private showContextMenu(e: MouseEvent | GestureEvent): void {
|
||||
private showContextMenu(targetWindow: Window, e: MouseEvent | GestureEvent): void {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
const event = new StandardMouseEvent(e);
|
||||
const event = new StandardMouseEvent(targetWindow, e);
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => event,
|
||||
getActions: () => this.getContextMenuActions(e)
|
||||
|
|
|
@ -11,7 +11,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
|||
import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput';
|
||||
import { Emitter, Relay } from 'vs/base/common/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, isAncestor, IDomNodePagePosition, isMouseEvent, isActiveElement, focusWindow } from 'vs/base/browser/dom';
|
||||
import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, isAncestor, IDomNodePagePosition, isMouseEvent, isActiveElement, focusWindow, getWindow } from 'vs/base/browser/dom';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
|
||||
|
@ -405,7 +405,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
// Find target anchor
|
||||
let anchor: HTMLElement | StandardMouseEvent = this.element;
|
||||
if (e) {
|
||||
anchor = new StandardMouseEvent(e);
|
||||
anchor = new StandardMouseEvent(getWindow(this.element), e);
|
||||
}
|
||||
|
||||
// Show it
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import 'vs/css!./media/editortabscontrol';
|
||||
import { localize } from 'vs/nls';
|
||||
import { applyDragImage, DataTransfers } from 'vs/base/browser/dnd';
|
||||
import { Dimension, isMouseEvent } from 'vs/base/browser/dom';
|
||||
import { Dimension, getWindow, isMouseEvent } from 'vs/base/browser/dom';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { ActionsOrientation, IActionViewItem, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IAction, ActionRunner } from 'vs/base/common/actions';
|
||||
|
@ -381,7 +381,7 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC
|
|||
// Find target anchor
|
||||
let anchor: HTMLElement | StandardMouseEvent = node;
|
||||
if (isMouseEvent(e)) {
|
||||
anchor = new StandardMouseEvent(e);
|
||||
anchor = new StandardMouseEvent(getWindow(node), e);
|
||||
}
|
||||
|
||||
// Show it
|
||||
|
|
|
@ -457,7 +457,7 @@ export class MultiEditorTabsControl extends EditorTabsControl {
|
|||
// Find target anchor
|
||||
let anchor: HTMLElement | StandardMouseEvent = tabsContainer;
|
||||
if (isMouseEvent(e)) {
|
||||
anchor = new StandardMouseEvent(e);
|
||||
anchor = new StandardMouseEvent(getWindow(tabsContainer), e);
|
||||
}
|
||||
|
||||
// Show it
|
||||
|
|
|
@ -227,7 +227,7 @@ abstract class AbstractGlobalActivityActionViewItem extends CompoisteBarActionVi
|
|||
const disposables = new DisposableStore();
|
||||
const actions = await this.resolveContextMenuActions(disposables);
|
||||
|
||||
const event = new StandardMouseEvent(e);
|
||||
const event = new StandardMouseEvent(getWindow(this.container), e);
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => event,
|
||||
|
|
|
@ -15,7 +15,7 @@ import { IView } from 'vs/base/browser/ui/grid/grid';
|
|||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { CompositePart, ICompositeTitleLabel } from 'vs/workbench/browser/parts/compositePart';
|
||||
import { IPaneCompositeBarOptions, PaneCompositeBar } from 'vs/workbench/browser/parts/paneCompositeBar';
|
||||
import { Dimension, EventHelper, trackFocus, $, addDisposableListener, EventType, prepend } from 'vs/base/browser/dom';
|
||||
import { Dimension, EventHelper, trackFocus, $, addDisposableListener, EventType, prepend, getWindow } from 'vs/base/browser/dom';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
@ -291,11 +291,11 @@ export abstract class AbstractPaneCompositePart extends CompositePart<PaneCompos
|
|||
const titleArea = super.createTitleArea(parent);
|
||||
|
||||
this._register(addDisposableListener(titleArea, EventType.CONTEXT_MENU, e => {
|
||||
this.onTitleAreaContextMenu(new StandardMouseEvent(e));
|
||||
this.onTitleAreaContextMenu(new StandardMouseEvent(getWindow(titleArea), e));
|
||||
}));
|
||||
this._register(Gesture.addTarget(titleArea));
|
||||
this._register(addDisposableListener(titleArea, GestureEventType.Contextmenu, e => {
|
||||
this.onTitleAreaContextMenu(new StandardMouseEvent(e));
|
||||
this.onTitleAreaContextMenu(new StandardMouseEvent(getWindow(titleArea), e));
|
||||
}));
|
||||
|
||||
const globalTitleActionsContainer = titleArea.appendChild($('.global-actions'));
|
||||
|
|
|
@ -16,7 +16,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
|||
import { STATUS_BAR_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_ITEM_HOVER_BACKGROUND, STATUS_BAR_BORDER, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_ITEM_COMPACT_HOVER_BACKGROUND, STATUS_BAR_ITEM_FOCUS_BORDER, STATUS_BAR_FOCUS_BORDER } from 'vs/workbench/common/theme';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { EventHelper, createStyleSheet, addDisposableListener, EventType, clearNode } from 'vs/base/browser/dom';
|
||||
import { EventHelper, createStyleSheet, addDisposableListener, EventType, clearNode, getWindow } from 'vs/base/browser/dom';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
@ -451,7 +451,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
|||
private showContextMenu(e: MouseEvent | GestureEvent): void {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
const event = new StandardMouseEvent(e);
|
||||
const event = new StandardMouseEvent(getWindow(this.element), e);
|
||||
|
||||
let actions: IAction[] | undefined = undefined;
|
||||
this.contextMenuService.showContextMenu({
|
||||
|
|
|
@ -19,7 +19,7 @@ import { ThemeIcon } from 'vs/base/common/themables';
|
|||
import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER, WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { isMacintosh, isWindows, isLinux, isWeb, isNative, platformLocale } from 'vs/base/common/platform';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { EventType, EventHelper, Dimension, append, $, addDisposableListener, prepend, reset } from 'vs/base/browser/dom';
|
||||
import { EventType, EventHelper, Dimension, append, $, addDisposableListener, prepend, reset, getWindow } from 'vs/base/browser/dom';
|
||||
import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
|
@ -533,7 +533,7 @@ export class TitlebarPart extends Part implements ITitleService {
|
|||
|
||||
protected onContextMenu(e: MouseEvent, menuId: MenuId): void {
|
||||
// Find target anchor
|
||||
const event = new StandardMouseEvent(e);
|
||||
const event = new StandardMouseEvent(getWindow(this.rootContainer), e);
|
||||
|
||||
// Show it
|
||||
this.contextMenuService.showContextMenu({
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { addDisposableListener, Dimension, DragAndDropObserver, EventType, focusWindow, isAncestor } from 'vs/base/browser/dom';
|
||||
import { addDisposableListener, Dimension, DragAndDropObserver, EventType, focusWindow, getWindow, isAncestor } from 'vs/base/browser/dom';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { EventType as TouchEventType, Gesture } from 'vs/base/browser/touch';
|
||||
import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
|
@ -409,9 +409,9 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
|||
this._register(this.paneview.onDidDrop(({ from, to }) => this.movePane(from as ViewPane, to as ViewPane)));
|
||||
this._register(this.paneview.onDidScroll(_ => this.onDidScrollPane()));
|
||||
this._register(this.paneview.onDidSashReset((index) => this.onDidSashReset(index)));
|
||||
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
|
||||
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(getWindow(parent), e))));
|
||||
this._register(Gesture.addTarget(parent));
|
||||
this._register(addDisposableListener(parent, TouchEventType.Contextmenu, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
|
||||
this._register(addDisposableListener(parent, TouchEventType.Contextmenu, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(getWindow(parent), e))));
|
||||
|
||||
this._menuActions = this._register(this.instantiationService.createInstance(ViewContainerMenuActions, this.paneview.element, this.viewContainer));
|
||||
this._register(this._menuActions.onDidChange(() => this.updateTitleArea()));
|
||||
|
@ -788,7 +788,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
|||
const contextMenuDisposable = addDisposableListener(pane.draggableElement, 'contextmenu', e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.onContextMenu(new StandardMouseEvent(e), pane);
|
||||
this.onContextMenu(new StandardMouseEvent(getWindow(pane.draggableElement), e), pane);
|
||||
});
|
||||
|
||||
const collapseDisposable = Event.latch(Event.map(pane.onDidChange, () => !pane.isExpanded()))(collapsed => {
|
||||
|
|
|
@ -28,7 +28,7 @@ import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatAct
|
|||
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { ICodeBlockActionContext } from 'vs/workbench/contrib/chat/browser/codeBlockPart';
|
||||
import { CONTEXT_IN_CHAT_SESSION, CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { IChatCopyAction, IChatService, IDocumentContext, InteractiveSessionCopyKind } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatService, IDocumentContext, InteractiveSessionCopyKind } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatResponseViewModel, isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { CTX_INLINE_CHAT_VISIBLE } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { insertCell } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations';
|
||||
|
@ -111,9 +111,8 @@ export function registerChatCodeBlockActions() {
|
|||
agentId: context.element.agent?.id,
|
||||
sessionId: context.element.sessionId,
|
||||
requestId: context.element.requestId,
|
||||
action: <IChatCopyAction>{
|
||||
action: {
|
||||
kind: 'copy',
|
||||
responseId: context.element.providerResponseId,
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
copyType: InteractiveSessionCopyKind.Toolbar,
|
||||
copiedCharacters: context.code.length,
|
||||
|
@ -149,24 +148,21 @@ export function registerChatCodeBlockActions() {
|
|||
const totalCharacters = editorModel.getValueLength();
|
||||
|
||||
// Report copy to extensions
|
||||
if (context.element.providerResponseId) {
|
||||
const chatService = accessor.get(IChatService);
|
||||
chatService.notifyUserAction({
|
||||
providerId: context.element.providerId,
|
||||
agentId: context.element.agent?.id,
|
||||
sessionId: context.element.sessionId,
|
||||
requestId: context.element.requestId,
|
||||
action: {
|
||||
kind: 'copy',
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
responseId: context.element.providerResponseId,
|
||||
copyType: InteractiveSessionCopyKind.Action,
|
||||
copiedText,
|
||||
copiedCharacters: copiedText.length,
|
||||
totalCharacters,
|
||||
}
|
||||
});
|
||||
}
|
||||
const chatService = accessor.get(IChatService);
|
||||
chatService.notifyUserAction({
|
||||
providerId: context.element.providerId,
|
||||
agentId: context.element.agent?.id,
|
||||
sessionId: context.element.sessionId,
|
||||
requestId: context.element.requestId,
|
||||
action: {
|
||||
kind: 'copy',
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
copyType: InteractiveSessionCopyKind.Action,
|
||||
copiedText,
|
||||
copiedCharacters: copiedText.length,
|
||||
totalCharacters,
|
||||
}
|
||||
});
|
||||
|
||||
// Copy full cell if no selection, otherwise fall back on normal editor implementation
|
||||
if (noSelection) {
|
||||
|
@ -337,7 +333,6 @@ export function registerChatCodeBlockActions() {
|
|||
requestId: context.element.requestId,
|
||||
action: {
|
||||
kind: 'insert',
|
||||
responseId: context.element.providerResponseId!,
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
totalCharacters: context.code.length,
|
||||
}
|
||||
|
@ -386,7 +381,6 @@ export function registerChatCodeBlockActions() {
|
|||
requestId: context.element.requestId,
|
||||
action: {
|
||||
kind: 'insert',
|
||||
responseId: context.element.providerResponseId!,
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
totalCharacters: context.code.length,
|
||||
newFile: true
|
||||
|
@ -482,7 +476,6 @@ export function registerChatCodeBlockActions() {
|
|||
requestId: context.element.requestId,
|
||||
action: {
|
||||
kind: 'runInTerminal',
|
||||
responseId: context.element.providerResponseId!,
|
||||
codeBlockIndex: context.codeBlockIndex,
|
||||
languageId: context.languageId,
|
||||
}
|
||||
|
|
|
@ -8,19 +8,15 @@ import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
|||
import { localize } from 'vs/nls';
|
||||
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions';
|
||||
import { IChatWidget } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { IChatWidget, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { CONTEXT_CHAT_INPUT_HAS_TEXT, CONTEXT_CHAT_REQUEST_IN_PROGRESS } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
|
||||
export interface IChatExecuteActionContext {
|
||||
widget: IChatWidget;
|
||||
widget?: IChatWidget;
|
||||
inputValue?: string;
|
||||
}
|
||||
|
||||
export function isExecuteActionContext(thing: unknown): thing is IChatExecuteActionContext {
|
||||
return typeof thing === 'object' && thing !== null && 'widget' in thing;
|
||||
}
|
||||
|
||||
export class SubmitAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.chat.submit';
|
||||
|
||||
|
@ -44,12 +40,11 @@ export class SubmitAction extends Action2 {
|
|||
}
|
||||
|
||||
run(accessor: ServicesAccessor, ...args: any[]) {
|
||||
const context = args[0];
|
||||
if (!isExecuteActionContext(context)) {
|
||||
return;
|
||||
}
|
||||
const context: IChatExecuteActionContext = args[0];
|
||||
|
||||
context.widget.acceptInput(context.inputValue);
|
||||
const widgetService = accessor.get(IChatWidgetService);
|
||||
const widget = context.widget ?? widgetService.lastFocusedWidget;
|
||||
widget?.acceptInput(context.inputValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,8 +71,8 @@ export function registerChatExecuteActions() {
|
|||
}
|
||||
|
||||
run(accessor: ServicesAccessor, ...args: any[]) {
|
||||
const context = args[0];
|
||||
if (!isExecuteActionContext(context)) {
|
||||
const context: IChatExecuteActionContext = args[0];
|
||||
if (!context.widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,6 @@ export function registerChatTitleActions() {
|
|||
action: {
|
||||
kind: 'vote',
|
||||
direction: InteractiveSessionVoteDirection.Up,
|
||||
responseId: item.providerResponseId!,
|
||||
}
|
||||
});
|
||||
item.setVote(InteractiveSessionVoteDirection.Up);
|
||||
|
@ -103,7 +102,6 @@ export function registerChatTitleActions() {
|
|||
action: {
|
||||
kind: 'vote',
|
||||
direction: InteractiveSessionVoteDirection.Down,
|
||||
responseId: item.providerResponseId!,
|
||||
}
|
||||
});
|
||||
item.setVote(InteractiveSessionVoteDirection.Down);
|
||||
|
@ -222,12 +220,12 @@ export function registerChatTitleActions() {
|
|||
item = widget?.getFocus();
|
||||
}
|
||||
|
||||
const providerRequestId = isRequestVM(item) ? item.providerRequestId :
|
||||
isResponseVM(item) ? item.providerResponseId : undefined;
|
||||
const requestId = isRequestVM(item) ? item.id :
|
||||
isResponseVM(item) ? item.requestId : undefined;
|
||||
|
||||
if (providerRequestId) {
|
||||
if (requestId) {
|
||||
const chatService = accessor.get(IChatService);
|
||||
chatService.removeRequest(item.sessionId, providerRequestId);
|
||||
chatService.removeRequest(item.sessionId, requestId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -18,7 +18,7 @@ import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/ed
|
|||
import { registerChatActions } from 'vs/workbench/contrib/chat/browser/actions/chatActions';
|
||||
import { registerChatCodeBlockActions } from 'vs/workbench/contrib/chat/browser/actions/chatCodeblockActions';
|
||||
import { registerChatCopyActions } from 'vs/workbench/contrib/chat/browser/actions/chatCopyActions';
|
||||
import { registerChatExecuteActions } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions';
|
||||
import { IChatExecuteActionContext, SubmitAction, registerChatExecuteActions } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions';
|
||||
import { registerQuickChatActions } from 'vs/workbench/contrib/chat/browser/actions/chatQuickInputActions';
|
||||
import { registerChatTitleActions } from 'vs/workbench/contrib/chat/browser/actions/chatTitleActions';
|
||||
import { registerChatExportActions } from 'vs/workbench/contrib/chat/browser/actions/chatImportExport';
|
||||
|
@ -45,7 +45,7 @@ import { ChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat
|
|||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
|
||||
import { ChatWelcomeMessageModel } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { ChatProviderService, IChatProviderService } from 'vs/workbench/contrib/chat/common/chatProvider';
|
||||
import { ChatSlashCommandService, IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { alertFocusChange } from 'vs/workbench/contrib/accessibility/browser/accessibilityContributions';
|
||||
|
@ -56,6 +56,8 @@ import { registerChatFileTreeActions } from 'vs/workbench/contrib/chat/browser/a
|
|||
import { QuickChatService } from 'vs/workbench/contrib/chat/browser/chatQuick';
|
||||
import { ChatAgentService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatVariablesService } from 'vs/workbench/contrib/chat/browser/chatVariables';
|
||||
import { chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
// Register configuration
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
|
@ -221,16 +223,52 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable {
|
|||
constructor(
|
||||
@IChatSlashCommandService slashCommandService: IChatSlashCommandService,
|
||||
@ICommandService commandService: ICommandService,
|
||||
@IChatAgentService chatAgentService: IChatAgentService,
|
||||
) {
|
||||
super();
|
||||
this._store.add(slashCommandService.registerSlashCommand({
|
||||
command: 'clear',
|
||||
detail: nls.localize('clear', "Clear the session"),
|
||||
sortText: 'z_clear',
|
||||
sortText: 'z2_clear',
|
||||
executeImmediately: true
|
||||
}, async () => {
|
||||
commandService.executeCommand(ACTION_ID_CLEAR_CHAT);
|
||||
}));
|
||||
this._store.add(slashCommandService.registerSlashCommand({
|
||||
command: 'help',
|
||||
detail: '',
|
||||
sortText: 'z1_help',
|
||||
executeImmediately: true
|
||||
}, async (prompt, progress) => {
|
||||
const defaultAgent = chatAgentService.getDefaultAgent();
|
||||
const agents = chatAgentService.getAgents();
|
||||
if (defaultAgent?.metadata.helpTextPrefix) {
|
||||
progress.report({ content: defaultAgent.metadata.helpTextPrefix });
|
||||
progress.report({ content: '\n\n' });
|
||||
}
|
||||
|
||||
const agentText = (await Promise.all(agents
|
||||
.filter(a => a.id !== defaultAgent?.id)
|
||||
.map(async a => {
|
||||
const agentWithLeader = `${chatAgentLeader}${a.id}`;
|
||||
const actionArg: IChatExecuteActionContext = { inputValue: `${agentWithLeader} ${a.metadata.sampleRequest}` };
|
||||
const urlSafeArg = encodeURIComponent(JSON.stringify(actionArg));
|
||||
const agentLine = `* [\`${agentWithLeader}\`](command:${SubmitAction.ID}?${urlSafeArg}) - ${a.metadata.description}`;
|
||||
const commands = await a.provideSlashCommands(CancellationToken.None);
|
||||
const commandText = commands.map(c => {
|
||||
const actionArg: IChatExecuteActionContext = { inputValue: `${agentWithLeader} ${chatSubcommandLeader}${c.name} ${c.sampleRequest ?? ''}` };
|
||||
const urlSafeArg = encodeURIComponent(JSON.stringify(actionArg));
|
||||
return `\t* [\`${chatSubcommandLeader}${c.name}\`](command:${SubmitAction.ID}?${urlSafeArg}) - ${c.description}`;
|
||||
}).join('\n');
|
||||
|
||||
return agentLine + '\n' + commandText;
|
||||
}))).join('\n');
|
||||
progress.report({ content: new MarkdownString(agentText, { isTrusted: { enabledCommands: [SubmitAction.ID] } }) });
|
||||
if (defaultAgent?.metadata.helpTextPostfix) {
|
||||
progress.report({ content: '\n\n' });
|
||||
progress.report({ content: defaultAgent.metadata.helpTextPostfix });
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatRequestViewModel, IChatResponseViewModel, IChatViewModel, IChatWelcomeMessageViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IChatWidgetContrib } from 'vs/workbench/contrib/chat/browser/chatWidget';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { IChatRequestViewModel, IChatResponseViewModel, IChatViewModel, IChatWelcomeMessageViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
|
||||
export const IChatWidgetService = createDecorator<IChatWidgetService>('chatWidgetService');
|
||||
export const IQuickChatService = createDecorator<IQuickChatService>('quickChatService');
|
||||
|
@ -121,7 +120,6 @@ export interface IChatWidget {
|
|||
focusLastMessage(): void;
|
||||
focusInput(): void;
|
||||
hasInputFocus(): boolean;
|
||||
getSlashCommands(): Promise<ISlashCommand[] | undefined>;
|
||||
getCodeBlockInfoForEditor(uri: URI): IChatCodeBlockInfo | undefined;
|
||||
getCodeBlockInfosForResponse(response: IChatResponseViewModel): IChatCodeBlockInfo[];
|
||||
getFileTreeInfosForResponse(response: IChatResponseViewModel): IChatFileTreeInfo[];
|
||||
|
|
|
@ -54,10 +54,10 @@ import { convertParsedRequestToMarkdown, reduceInlineContentReferences, walkTree
|
|||
import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions';
|
||||
import { CodeBlockPart, ICodeBlockData, ICodeBlockPart } from 'vs/workbench/contrib/chat/browser/codeBlockPart';
|
||||
import { IChatAgentMetadata } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_HAS_PROVIDER_ID, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { IPlaceholderMarkdownString } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { IChatContentReference, IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, ISlashCommand, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatContentReference, IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatResponseMarkdownRenderData, IChatResponseRenderData, IChatResponseViewModel, IChatWelcomeMessageViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { IWordCountResult, getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter';
|
||||
import { createFileIconThemableTreeContainerScope } from 'vs/workbench/contrib/files/browser/views/explorerView';
|
||||
|
@ -89,7 +89,6 @@ const forceVerboseLayoutTracing = false;
|
|||
|
||||
export interface IChatRendererDelegate {
|
||||
getListLength(): number;
|
||||
getSlashCommands(): ISlashCommand[];
|
||||
}
|
||||
|
||||
export interface IChatListItemRendererOptions {
|
||||
|
@ -264,7 +263,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
|
||||
CONTEXT_RESPONSE.bindTo(templateData.contextKeyService).set(isResponseVM(element));
|
||||
CONTEXT_REQUEST.bindTo(templateData.contextKeyService).set(isRequestVM(element));
|
||||
CONTEXT_RESPONSE_HAS_PROVIDER_ID.bindTo(templateData.contextKeyService).set(isResponseVM(element) && !!element.providerResponseId);
|
||||
if (isResponseVM(element)) {
|
||||
CONTEXT_RESPONSE_VOTE.bindTo(templateData.contextKeyService).set(element.vote === InteractiveSessionVoteDirection.Up ? 'up' : element.vote === InteractiveSessionVoteDirection.Down ? 'down' : '');
|
||||
} else {
|
||||
|
@ -465,7 +463,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
private renderWelcomeMessage(element: IChatWelcomeMessageViewModel, templateData: IChatListItemTemplate, height?: number) {
|
||||
dom.clearNode(templateData.value);
|
||||
dom.clearNode(templateData.referencesListContainer);
|
||||
const slashCommands = this.delegate.getSlashCommands();
|
||||
|
||||
for (const item of element.content) {
|
||||
if (Array.isArray(item)) {
|
||||
|
@ -477,11 +474,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
templateData.contextKeyService));
|
||||
} else {
|
||||
const result = this.renderMarkdown(item as IMarkdownString, element, templateData);
|
||||
for (const codeElement of result.element.querySelectorAll('code')) {
|
||||
if (codeElement.textContent && slashCommands.find(command => codeElement.textContent === `/${command.command}`)) {
|
||||
codeElement.classList.add('interactive-slash-command');
|
||||
}
|
||||
}
|
||||
templateData.value.appendChild(result.element);
|
||||
templateData.elementDisposables.add(result);
|
||||
}
|
||||
|
@ -779,11 +771,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
const disposables = new DisposableStore();
|
||||
let codeBlockIndex = 0;
|
||||
|
||||
// TODO if the slash commands stay completely dynamic, this isn't quite right
|
||||
const slashCommands = this.delegate.getSlashCommands();
|
||||
const usedSlashCommand = slashCommands.find(s => markdown.value.startsWith(`/${s.command} `));
|
||||
const toRender = usedSlashCommand ? markdown.value.slice(usedSlashCommand.command.length + 2) : markdown.value;
|
||||
markdown = new MarkdownString(toRender, {
|
||||
markdown = new MarkdownString(markdown.value, {
|
||||
isTrusted: {
|
||||
// Disable all other config options except isTrusted
|
||||
enabledCommands: typeof markdown.isTrusted === 'object' ? markdown.isTrusted?.enabledCommands : [] ?? []
|
||||
|
@ -831,15 +819,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
|||
|
||||
walkTreeAndAnnotateReferenceLinks(result.element);
|
||||
|
||||
if (usedSlashCommand) {
|
||||
const slashCommandElement = $('span.interactive-slash-command', { title: usedSlashCommand.detail }, `/${usedSlashCommand.command} `);
|
||||
if (result.element.firstChild?.nodeName.toLowerCase() === 'p') {
|
||||
result.element.firstChild.insertBefore(slashCommandElement, result.element.firstChild.firstChild);
|
||||
} else {
|
||||
result.element.insertBefore($('p', undefined, slashCommandElement), result.element.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
orderedDisposablesList.reverse().forEach(d => disposables.add(d));
|
||||
return {
|
||||
element: result.element,
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
import * as dom from 'vs/base/browser/dom';
|
||||
import { ITreeContextMenuEvent, ITreeElement } from 'vs/base/browser/ui/tree/tree';
|
||||
import { disposableTimeout } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore, IDisposable, MutableDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
@ -23,7 +22,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
|
|||
import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IViewsService } from 'vs/workbench/common/views';
|
||||
import { ChatTreeItem, IChatWidgetViewOptions, IChatAccessibilityService, IChatCodeBlockInfo, IChatFileTreeInfo, IChatWidget, IChatWidgetService, IChatWidgetViewContext } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { ChatTreeItem, IChatAccessibilityService, IChatCodeBlockInfo, IChatFileTreeInfo, IChatWidget, IChatWidgetService, IChatWidgetViewContext, IChatWidgetViewOptions } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart';
|
||||
import { ChatAccessibilityProvider, ChatListDelegate, ChatListItemRenderer, IChatListItemRendererOptions, IChatRendererDelegate } from 'vs/workbench/contrib/chat/browser/chatListRenderer';
|
||||
import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions';
|
||||
|
@ -31,7 +30,7 @@ import { ChatViewPane } from 'vs/workbench/contrib/chat/browser/chatViewPane';
|
|||
import { CONTEXT_CHAT_REQUEST_IN_PROGRESS, CONTEXT_IN_CHAT_SESSION } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService';
|
||||
import { ChatModelInitState, IChatModel } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { IChatReplyFollowup, IChatService, ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatReplyFollowup, IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { ChatViewModel, IChatResponseViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
|
||||
const $ = dom.$;
|
||||
|
@ -109,15 +108,6 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
this.viewModelDisposables.add(viewModel);
|
||||
}
|
||||
|
||||
this.slashCommandsPromise = undefined;
|
||||
this.lastSlashCommands = undefined;
|
||||
|
||||
this.getSlashCommands().then(() => {
|
||||
if (!this._isDisposed) {
|
||||
this.onDidChangeItems();
|
||||
}
|
||||
});
|
||||
|
||||
this._onDidChangeViewModel.fire();
|
||||
}
|
||||
|
||||
|
@ -125,9 +115,6 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
return this._viewModel;
|
||||
}
|
||||
|
||||
private lastSlashCommands: ISlashCommand[] | undefined;
|
||||
private slashCommandsPromise: Promise<ISlashCommand[] | undefined> | undefined;
|
||||
|
||||
constructor(
|
||||
readonly viewContext: IChatWidgetViewContext,
|
||||
private readonly viewOptions: IChatWidgetViewOptions,
|
||||
|
@ -164,12 +151,6 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
return this.inputPart.inputUri;
|
||||
}
|
||||
|
||||
private _isDisposed: boolean = false;
|
||||
public override dispose(): void {
|
||||
this._isDisposed = true;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
render(parent: HTMLElement): void {
|
||||
const viewId = 'viewId' in this.viewContext ? this.viewContext.viewId : undefined;
|
||||
this.editorOptions = this._register(this.instantiationService.createInstance(ChatEditorOptions, viewId, this.styles.listForeground, this.styles.inputEditorBackground, this.styles.resultEditorBackground));
|
||||
|
@ -260,7 +241,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
// TODO? We can give the welcome message a proper VM or get rid of the rest of the VMs
|
||||
((isWelcomeVM(element) && this.viewModel) ? `_${ChatModelInitState[this.viewModel.initState]}` : '') +
|
||||
// Ensure re-rendering an element once slash commands are loaded, so the colorization can be applied.
|
||||
`${(isRequestVM(element) || isWelcomeVM(element)) && !!this.lastSlashCommands ? '_scLoaded' : ''}` +
|
||||
`${(isRequestVM(element) || isWelcomeVM(element)) /* && !!this.lastSlashCommands ? '_scLoaded' : '' */}` +
|
||||
// If a response is in the process of progressive rendering, we need to ensure that it will
|
||||
// be re-rendered so progressive rendering is restarted, even if the model wasn't updated.
|
||||
`${isResponseVM(element) && element.renderData ? `_${this.visibleChangeCount}` : ''}` +
|
||||
|
@ -309,27 +290,11 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
}
|
||||
}
|
||||
|
||||
async getSlashCommands(): Promise<ISlashCommand[] | undefined> {
|
||||
if (!this.viewModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.slashCommandsPromise) {
|
||||
this.slashCommandsPromise = this.chatService.getSlashCommands(this.viewModel.sessionId, CancellationToken.None).then(commands => {
|
||||
this.lastSlashCommands = commands ?? [];
|
||||
return this.lastSlashCommands;
|
||||
});
|
||||
}
|
||||
|
||||
return this.slashCommandsPromise;
|
||||
}
|
||||
|
||||
private createList(listContainer: HTMLElement, options: IChatListItemRendererOptions): void {
|
||||
const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.contextKeyService]));
|
||||
const delegate = scopedInstantiationService.createInstance(ChatListDelegate);
|
||||
const rendererDelegate: IChatRendererDelegate = {
|
||||
getListLength: () => this.tree.getNode(null).visibleChildrenCount,
|
||||
getSlashCommands: () => this.lastSlashCommands ?? [],
|
||||
};
|
||||
this.renderer = this._register(scopedInstantiationService.createInstance(
|
||||
ChatListItemRenderer,
|
||||
|
@ -462,7 +427,6 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
this.container.setAttribute('data-session-id', model.sessionId);
|
||||
this.viewModel = this.instantiationService.createInstance(ChatViewModel, model);
|
||||
this.viewModelDisposables.add(this.viewModel.onDidChange(e => {
|
||||
this.slashCommandsPromise = undefined;
|
||||
this.requestInProgress.set(this.viewModel!.requestInProgress);
|
||||
this.onDidChangeItems();
|
||||
if (e?.kind === 'addRequest') {
|
||||
|
@ -536,8 +500,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
'query' in opts ? opts.query :
|
||||
`${opts.prefix} ${editorValue}`;
|
||||
const isUserQuery = !opts || 'query' in opts;
|
||||
const usedSlashCommand = this.lookupSlashCommand(input);
|
||||
const result = await this.chatService.sendRequest(this.viewModel.sessionId, input, usedSlashCommand);
|
||||
const result = await this.chatService.sendRequest(this.viewModel.sessionId, input);
|
||||
|
||||
if (result) {
|
||||
this.inputPart.acceptInput(isUserQuery ? input : undefined);
|
||||
|
@ -552,10 +515,6 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
|||
}
|
||||
}
|
||||
|
||||
private lookupSlashCommand(input: string): ISlashCommand | undefined {
|
||||
return this.lastSlashCommands?.find(sc => input.startsWith(`/${sc.command}`));
|
||||
}
|
||||
|
||||
getCodeBlockInfosForResponse(response: IChatResponseViewModel): IChatCodeBlockInfo[] {
|
||||
return this.renderer.getCodeBlockInfosForResponse(response);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import { chatSlashCommandBackground, chatSlashCommandForeground } from 'vs/workb
|
|||
import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, ChatRequestTextPart, ChatRequestVariablePart, chatAgentLeader, chatSubcommandLeader, chatVariableLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser';
|
||||
import { IChatService, ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
import { isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
|
@ -49,7 +50,7 @@ class InputEditorDecorations extends Disposable {
|
|||
|
||||
public readonly id = 'inputEditorDecorations';
|
||||
|
||||
private readonly previouslyUsedSlashCommands = new Set<string>();
|
||||
private readonly previouslyUsedAgents = new Set<string>();
|
||||
|
||||
private readonly viewModelDisposables = this._register(new MutableDisposable());
|
||||
|
||||
|
@ -71,16 +72,12 @@ class InputEditorDecorations extends Disposable {
|
|||
this._register(this.widget.inputEditor.onDidChangeModelContent(() => this.updateInputEditorDecorations()));
|
||||
this._register(this.widget.onDidChangeViewModel(() => {
|
||||
this.registerViewModelListeners();
|
||||
this.previouslyUsedSlashCommands.clear();
|
||||
this.previouslyUsedAgents.clear();
|
||||
this.updateInputEditorDecorations();
|
||||
}));
|
||||
this._register(this.chatService.onDidSubmitSlashCommand((e) => {
|
||||
this._register(this.chatService.onDidSubmitAgent((e) => {
|
||||
if (e.sessionId === this.widget.viewModel?.sessionId) {
|
||||
if ('agent' in e) {
|
||||
this.previouslyUsedSlashCommands.add(agentAndCommandToKey(e.agent.id, e.slashCommand.name));
|
||||
} else {
|
||||
this.previouslyUsedSlashCommands.add(e.slashCommand);
|
||||
}
|
||||
this.previouslyUsedAgents.add(agentAndCommandToKey(e.agent.id, e.slashCommand.name));
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -187,7 +184,7 @@ class InputEditorDecorations extends Disposable {
|
|||
const onlyAgentCommandAndWhitespace = agentPart && agentSubcommandPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestAgentPart || p instanceof ChatRequestAgentSubcommandPart);
|
||||
if (onlyAgentCommandAndWhitespace) {
|
||||
// Agent reference and subcommand with no other text - show the placeholder
|
||||
const isFollowupSlashCommand = this.previouslyUsedSlashCommands.has(agentAndCommandToKey(agentPart.agent.id, agentSubcommandPart.command.name));
|
||||
const isFollowupSlashCommand = this.previouslyUsedAgents.has(agentAndCommandToKey(agentPart.agent.id, agentSubcommandPart.command.name));
|
||||
const shouldRenderFollowupPlaceholder = isFollowupSlashCommand && agentSubcommandPart.command.followupPlaceholder;
|
||||
if (agentSubcommandPart?.command.description) {
|
||||
placeholderDecoration = [{
|
||||
|
@ -210,7 +207,7 @@ class InputEditorDecorations extends Disposable {
|
|||
const onlySlashCommandAndWhitespace = slashCommandPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestSlashCommandPart);
|
||||
if (onlySlashCommandAndWhitespace) {
|
||||
// Command reference with no other text - show the placeholder
|
||||
const isFollowupSlashCommand = this.previouslyUsedSlashCommands.has(slashCommandPart.slashCommand.command);
|
||||
const isFollowupSlashCommand = this.previouslyUsedAgents.has(slashCommandPart.slashCommand.command);
|
||||
const shouldRenderFollowupPlaceholder = isFollowupSlashCommand && slashCommandPart.slashCommand.followupPlaceholder;
|
||||
if (shouldRenderFollowupPlaceholder || slashCommandPart.slashCommand.detail) {
|
||||
placeholderDecoration = [{
|
||||
|
@ -264,15 +261,12 @@ class InputEditorSlashCommandMode extends Disposable {
|
|||
@IChatService private readonly chatService: IChatService
|
||||
) {
|
||||
super();
|
||||
this._register(this.chatService.onDidSubmitSlashCommand(e => {
|
||||
this._register(this.chatService.onDidSubmitAgent(e => {
|
||||
if (this.widget.viewModel?.sessionId !== e.sessionId) {
|
||||
return;
|
||||
}
|
||||
if ('agent' in e) {
|
||||
this.repopulateAgentCommand(e.agent, e.slashCommand);
|
||||
} else {
|
||||
this.repopulateSlashCommand(e.slashCommand);
|
||||
}
|
||||
|
||||
this.repopulateAgentCommand(e.agent, e.slashCommand);
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -283,20 +277,6 @@ class InputEditorSlashCommandMode extends Disposable {
|
|||
this.widget.inputEditor.setPosition({ lineNumber: 1, column: value.length + 1 });
|
||||
}
|
||||
}
|
||||
|
||||
private async repopulateSlashCommand(slashCommand: string) {
|
||||
const slashCommands = await this.widget.getSlashCommands();
|
||||
|
||||
if (this.widget.inputEditor.getValue().trim().length !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (slashCommands?.find(c => c.command === slashCommand)?.shouldRepopulate) {
|
||||
const value = `/${slashCommand} `;
|
||||
this.widget.inputEditor.setValue(value);
|
||||
this.widget.inputEditor.setPosition({ lineNumber: 1, column: value.length + 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChatWidget.CONTRIBS.push(InputEditorDecorations, InputEditorSlashCommandMode);
|
||||
|
@ -306,11 +286,12 @@ class SlashCommandCompletions extends Disposable {
|
|||
@ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService,
|
||||
@IChatWidgetService private readonly chatWidgetService: IChatWidgetService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IChatSlashCommandService private readonly chatSlashCommandService: IChatSlashCommandService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(this.languageFeaturesService.completionProvider.register({ scheme: ChatInputPart.INPUT_SCHEME, hasAccessToAllModels: true }, {
|
||||
_debugDisplayName: 'chatSlashCommand',
|
||||
_debugDisplayName: 'globalSlashCommands',
|
||||
triggerCharacters: ['/'],
|
||||
provideCompletionItems: async (model: ITextModel, _position: Position, _context: CompletionContext, _token: CancellationToken) => {
|
||||
const widget = this.chatWidgetService.getWidgetByInputUri(model.uri);
|
||||
|
@ -329,7 +310,7 @@ class SlashCommandCompletions extends Disposable {
|
|||
return;
|
||||
}
|
||||
|
||||
const slashCommands = await widget.getSlashCommands();
|
||||
const slashCommands = this.chatSlashCommandService.getCommands();
|
||||
if (!slashCommands) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -390,10 +390,6 @@
|
|||
color: var(--vscode-notificationsInfoIcon-foreground) !important; /* Have to override default styles which apply to all lists */
|
||||
}
|
||||
|
||||
.interactive-item-container .value .interactive-slash-command {
|
||||
color: var(--vscode-textLink-foreground);
|
||||
}
|
||||
|
||||
.interactive-session .interactive-input-part {
|
||||
padding: 12px 0px;
|
||||
display: flex;
|
||||
|
|
|
@ -5,13 +5,14 @@
|
|||
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider';
|
||||
import { IChatFollowup, IChatProgress, IChatResponseErrorDetails, IChatResponseProgressFileTreeData } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatFollowup, IChatProgress, IChatResponseErrorDetails } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
|
||||
//#region agent service, commands etc
|
||||
|
@ -27,40 +28,44 @@ export interface IChatAgent extends IChatAgentData {
|
|||
provideSlashCommands(token: CancellationToken): Promise<IChatAgentCommand[]>;
|
||||
}
|
||||
|
||||
export interface IChatAgentFragment {
|
||||
content: string | { treeData: IChatResponseProgressFileTreeData };
|
||||
}
|
||||
|
||||
export interface IChatAgentCommand {
|
||||
name: string;
|
||||
description: string;
|
||||
|
||||
/**
|
||||
* Whether the command should execute as soon
|
||||
* as it is entered. Defaults to `false`.
|
||||
*/
|
||||
executeImmediately?: boolean;
|
||||
|
||||
/**
|
||||
* Whether executing the command puts the
|
||||
* chat into a persistent mode, where the
|
||||
* slash command is prepended to the chat input.
|
||||
*/
|
||||
shouldRepopulate?: boolean;
|
||||
|
||||
/**
|
||||
* Placeholder text to render in the chat input
|
||||
* when the slash command has been repopulated.
|
||||
* Has no effect if `shouldRepopulate` is `false`.
|
||||
*/
|
||||
followupPlaceholder?: string;
|
||||
|
||||
sampleRequest?: string;
|
||||
}
|
||||
|
||||
export interface IChatAgentMetadata {
|
||||
description?: string;
|
||||
isDefault?: boolean; // The agent invoked when no agent is specified
|
||||
helpTextPrefix?: string | IMarkdownString;
|
||||
helpTextPostfix?: string | IMarkdownString;
|
||||
isSecondary?: boolean; // Invoked by ctrl/cmd+enter
|
||||
fullName?: string;
|
||||
icon?: URI;
|
||||
iconDark?: URI;
|
||||
themeIcon?: ThemeIcon;
|
||||
sampleRequest?: string;
|
||||
}
|
||||
|
||||
export interface IChatAgentRequest {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
import { localize } from 'vs/nls';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
export const CONTEXT_RESPONSE_HAS_PROVIDER_ID = new RawContextKey<boolean>('chatSessionResponseHasProviderId', false, { type: 'boolean', description: localize('interactiveSessionResponseHasProviderId', "True when the provider has assigned an id to this response.") });
|
||||
export const CONTEXT_RESPONSE_VOTE = new RawContextKey<string>('chatSessionResponseVote', '', { type: 'string', description: localize('interactiveSessionResponseVote', "When the response has been voted up, is set to 'up'. When voted down, is set to 'down'. Otherwise an empty string.") });
|
||||
export const CONTEXT_RESPONSE_FILTERED = new RawContextKey<boolean>('chatSessionResponseFiltered', false, { type: 'boolean', description: localize('chatResponseFiltered', "True when the chat response was filtered out by the server.") });
|
||||
export const CONTEXT_CHAT_REQUEST_IN_PROGRESS = new RawContextKey<boolean>('chatSessionRequestInProgress', false, { type: 'boolean', description: localize('interactiveSessionRequestInProgress', "True when the current request is still in progress.") });
|
||||
|
|
|
@ -20,7 +20,6 @@ import { IChat, IChatContentInlineReference, IChatContentReference, IChatFollowu
|
|||
|
||||
export interface IChatRequestModel {
|
||||
readonly id: string;
|
||||
readonly providerRequestId: string | undefined;
|
||||
readonly username: string;
|
||||
readonly avatarIconUri?: URI;
|
||||
readonly session: IChatModel;
|
||||
|
@ -53,7 +52,6 @@ export interface IChatResponseModel {
|
|||
readonly onDidChange: Event<void>;
|
||||
readonly id: string;
|
||||
readonly providerId: string;
|
||||
readonly providerResponseId: string | undefined;
|
||||
readonly requestId: string;
|
||||
readonly username: string;
|
||||
readonly avatarIconUri?: URI;
|
||||
|
@ -79,10 +77,6 @@ export class ChatRequestModel implements IChatRequestModel {
|
|||
return this._id;
|
||||
}
|
||||
|
||||
public get providerRequestId(): string | undefined {
|
||||
return this._providerRequestId;
|
||||
}
|
||||
|
||||
public get username(): string {
|
||||
return this.session.requesterUsername;
|
||||
}
|
||||
|
@ -93,14 +87,9 @@ export class ChatRequestModel implements IChatRequestModel {
|
|||
|
||||
constructor(
|
||||
public readonly session: ChatModel,
|
||||
public readonly message: IParsedChatRequest,
|
||||
private _providerRequestId?: string) {
|
||||
public readonly message: IParsedChatRequest) {
|
||||
this._id = 'request_' + ChatRequestModel.nextId++;
|
||||
}
|
||||
|
||||
setProviderRequestId(providerRequestId: string) {
|
||||
this._providerRequestId = providerRequestId;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IPlaceholderMarkdownString extends IMarkdownString {
|
||||
|
@ -169,7 +158,11 @@ export class Response implements IResponse {
|
|||
} else if (lastResponsePart) {
|
||||
// Combine this part with the last, non-resolving string part
|
||||
if (isMarkdownString(responsePart)) {
|
||||
this._responseParts[responsePartLength] = { string: new MarkdownString(lastResponsePart.string.value + responsePart.value, responsePart) };
|
||||
// Merge all enabled commands
|
||||
const lastPartEnabledCommands = typeof lastResponsePart.string.isTrusted === 'object' ? lastResponsePart.string.isTrusted.enabledCommands : [];
|
||||
const thisPartEnabledCommands = typeof responsePart.isTrusted === 'object' ? responsePart.isTrusted.enabledCommands : [];
|
||||
const enabledCommands = [...lastPartEnabledCommands, ...thisPartEnabledCommands];
|
||||
this._responseParts[responsePartLength] = { string: new MarkdownString(lastResponsePart.string.value + responsePart.value, { isTrusted: { enabledCommands } }) };
|
||||
} else {
|
||||
this._responseParts[responsePartLength] = { string: new MarkdownString(lastResponsePart.string.value + responsePart, lastResponsePart.string) };
|
||||
}
|
||||
|
@ -252,10 +245,6 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
|
|||
return this._id;
|
||||
}
|
||||
|
||||
public get providerResponseId(): string | undefined {
|
||||
return this._providerResponseId;
|
||||
}
|
||||
|
||||
public get isComplete(): boolean {
|
||||
return this._isComplete;
|
||||
}
|
||||
|
@ -313,7 +302,6 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
|
|||
private _isComplete: boolean = false,
|
||||
private _isCanceled = false,
|
||||
private _vote?: InteractiveSessionVoteDirection,
|
||||
private _providerResponseId?: string,
|
||||
private _errorDetails?: IChatResponseErrorDetails,
|
||||
followups?: ReadonlyArray<IChatFollowup>
|
||||
) {
|
||||
|
@ -335,10 +323,6 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel
|
|||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
setProviderResponseId(providerResponseId: string) {
|
||||
this._providerResponseId = providerResponseId;
|
||||
}
|
||||
|
||||
setErrorDetails(errorDetails?: IChatResponseErrorDetails): void {
|
||||
this._errorDetails = errorDetails;
|
||||
this._onDidChange.fire();
|
||||
|
@ -388,7 +372,6 @@ export interface ISerializableChatsData {
|
|||
export type ISerializableChatAgentData = UriDto<IChatAgentData>;
|
||||
|
||||
export interface ISerializableChatRequestData {
|
||||
providerRequestId: string | undefined;
|
||||
message: string | IParsedChatRequest;
|
||||
response: ReadonlyArray<IMarkdownString | IChatResponseProgressFileTreeData | IChatContentInlineReference> | undefined;
|
||||
agent?: ISerializableChatAgentData;
|
||||
|
@ -410,7 +393,6 @@ export interface IExportableChatData {
|
|||
responderUsername: string;
|
||||
requesterAvatarIconUri: UriComponents | undefined;
|
||||
responderAvatarIconUri: UriComponents | undefined;
|
||||
providerState: any;
|
||||
}
|
||||
|
||||
export interface ISerializableChatData extends IExportableChatData {
|
||||
|
@ -486,11 +468,6 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
return this._welcomeMessage;
|
||||
}
|
||||
|
||||
private _providerState: any;
|
||||
get providerState(): any {
|
||||
return this._providerState;
|
||||
}
|
||||
|
||||
// TODO to be clear, this is not the same as the id from the session object, which belongs to the provider.
|
||||
// It's easier to be able to identify this model before its async initialization is complete
|
||||
private _sessionId: string;
|
||||
|
@ -556,7 +533,6 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
this._isImported = (!!initialData && !isSerializableSessionData(initialData)) || (initialData?.isImported ?? false);
|
||||
this._sessionId = (isSerializableSessionData(initialData) && initialData.sessionId) || generateUuid();
|
||||
this._requests = initialData ? this._deserialize(initialData) : [];
|
||||
this._providerState = initialData ? initialData.providerState : undefined;
|
||||
this._creationDate = (isSerializableSessionData(initialData) && initialData.creationDate) || Date.now();
|
||||
|
||||
this._initialRequesterAvatarIconUri = initialData?.requesterAvatarIconUri && URI.revive(initialData.requesterAvatarIconUri);
|
||||
|
@ -581,11 +557,11 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
typeof raw.message === 'string'
|
||||
? this.getParsedRequestFromString(raw.message)
|
||||
: reviveParsedChatRequest(raw.message);
|
||||
const request = new ChatRequestModel(this, parsedRequest, raw.providerRequestId);
|
||||
const request = new ChatRequestModel(this, parsedRequest);
|
||||
if (raw.response || raw.responseErrorDetails) {
|
||||
const agent = (raw.agent && 'metadata' in raw.agent) ? // Check for the new format, ignore entries in the old format
|
||||
revive<ISerializableChatAgentData>(raw.agent) : undefined;
|
||||
request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, request.id, true, raw.isCanceled, raw.vote, raw.providerRequestId, raw.responseErrorDetails, raw.followups);
|
||||
request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, request.id, true, raw.isCanceled, raw.vote, raw.responseErrorDetails, raw.followups);
|
||||
if (raw.usedContext) { // @ulugbekna: if this's a new vscode sessions, doc versions are incorrect anyway?
|
||||
request.response.updateContent(raw.usedContext);
|
||||
}
|
||||
|
@ -638,13 +614,6 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
}
|
||||
|
||||
this._isInitializedDeferred.complete();
|
||||
|
||||
if (session.onDidChangeState) {
|
||||
this._register(session.onDidChangeState(state => {
|
||||
this._providerState = state;
|
||||
this.logService.trace('ChatModel#acceptNewSessionState');
|
||||
}));
|
||||
}
|
||||
this._onDidChange.fire({ kind: 'initialize' });
|
||||
}
|
||||
|
||||
|
@ -703,21 +672,15 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
if (agent) {
|
||||
request.response.setAgent(agent, progress.command);
|
||||
}
|
||||
} else {
|
||||
request.setProviderRequestId(progress.requestId);
|
||||
request.response.setProviderResponseId(progress.requestId);
|
||||
}
|
||||
}
|
||||
|
||||
removeRequest(requestId: string): void {
|
||||
const index = this._requests.findIndex(request => request.providerRequestId === requestId);
|
||||
removeRequest(id: string): void {
|
||||
const index = this._requests.findIndex(request => request.id === id);
|
||||
const request = this._requests[index];
|
||||
if (!request.providerRequestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (index !== -1) {
|
||||
this._onDidChange.fire({ kind: 'removeRequest', requestId: request.providerRequestId, responseId: request.response?.providerResponseId });
|
||||
this._onDidChange.fire({ kind: 'removeRequest', requestId: request.id, responseId: request.response?.id });
|
||||
this._requests.splice(index, 1);
|
||||
request.response?.dispose();
|
||||
}
|
||||
|
@ -778,21 +741,19 @@ export class ChatModel extends Disposable implements IChatModel {
|
|||
}),
|
||||
requests: this._requests.map((r): ISerializableChatRequestData => {
|
||||
return {
|
||||
providerRequestId: r.providerRequestId,
|
||||
message: r.message,
|
||||
response: r.response ? r.response.response.value : undefined,
|
||||
responseErrorDetails: r.response?.errorDetails,
|
||||
followups: r.response?.followups,
|
||||
isCanceled: r.response?.isCanceled,
|
||||
vote: r.response?.vote,
|
||||
agent: r.response?.agent,
|
||||
agent: r.response?.agent ? { id: r.response.agent.id, metadata: r.response.agent.metadata } : undefined, // May actually be the full IChatAgent instance, just take the data props
|
||||
slashCommand: r.response?.slashCommand,
|
||||
usedContext: r.response?.response.usedContext,
|
||||
contentReferences: r.response?.response.contentReferences
|
||||
};
|
||||
}),
|
||||
providerId: this.providerId,
|
||||
providerState: this._providerState
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { IPosition, Position } from 'vs/editor/common/core/position';
|
|||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestDynamicReferencePart, ChatRequestSlashCommandPart, ChatRequestTextPart, ChatRequestVariablePart, IParsedChatRequest, IParsedChatRequestPart, chatAgentLeader, chatSubcommandLeader, chatVariableLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { IChatVariablesService, IDynamicReference } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
|
||||
const agentReg = /^@([\w_\-]+)(?=(\s|$|\b))/i; // An @-agent
|
||||
|
@ -21,7 +21,7 @@ export class ChatRequestParser {
|
|||
constructor(
|
||||
@IChatAgentService private readonly agentService: IChatAgentService,
|
||||
@IChatVariablesService private readonly variableService: IChatVariablesService,
|
||||
@IChatService private readonly chatService: IChatService,
|
||||
@IChatSlashCommandService private readonly slashCommandService: IChatSlashCommandService
|
||||
) { }
|
||||
|
||||
async parseChatRequest(sessionId: string, message: string): Promise<IParsedChatRequest> {
|
||||
|
@ -174,7 +174,7 @@ export class ChatRequestParser {
|
|||
return new ChatRequestAgentSubcommandPart(slashRange, slashEditorRange, subCommand);
|
||||
}
|
||||
} else {
|
||||
const slashCommands = await this.chatService.getSlashCommands(sessionId, CancellationToken.None);
|
||||
const slashCommands = this.slashCommandService.getCommands();
|
||||
const slashCommand = slashCommands.find(c => c.command === command);
|
||||
if (slashCommand) {
|
||||
// Valid standalone slash command
|
||||
|
|
|
@ -23,7 +23,6 @@ export interface IChat {
|
|||
responderUsername: string;
|
||||
responderAvatarIconUri?: URI;
|
||||
inputPlaceholder?: string;
|
||||
onDidChangeState?: Event<any>;
|
||||
dispose?(): void;
|
||||
}
|
||||
|
||||
|
@ -100,7 +99,6 @@ export interface IChatAgentDetection {
|
|||
|
||||
export type IChatProgress =
|
||||
| { content: string | IMarkdownString }
|
||||
| { requestId: string }
|
||||
| { treeData: IChatResponseProgressFileTreeData }
|
||||
| { placeholder: string; resolvedContent: Promise<string | IMarkdownString | { treeData: IChatResponseProgressFileTreeData }> }
|
||||
| IUsedContext
|
||||
|
@ -108,18 +106,13 @@ export type IChatProgress =
|
|||
| IChatContentInlineReference
|
||||
| IChatAgentDetection;
|
||||
|
||||
export interface IPersistedChatState { }
|
||||
export interface IChatProvider {
|
||||
readonly id: string;
|
||||
readonly displayName: string;
|
||||
readonly iconUrl?: string;
|
||||
prepareSession(initialState: IPersistedChatState | undefined, token: CancellationToken): ProviderResult<IChat | undefined>;
|
||||
prepareSession(token: CancellationToken): ProviderResult<IChat | undefined>;
|
||||
provideWelcomeMessage?(token: CancellationToken): ProviderResult<(string | IMarkdownString | IChatReplyFollowup[])[] | undefined>;
|
||||
provideSampleQuestions?(token: CancellationToken): ProviderResult<IChatReplyFollowup[] | undefined>;
|
||||
provideFollowups?(session: IChat, token: CancellationToken): ProviderResult<IChatFollowup[] | undefined>;
|
||||
provideReply(request: IChatRequest, progress: (progress: IChatProgress) => void, token: CancellationToken): ProviderResult<IChatResponse>;
|
||||
provideSlashCommands?(session: IChat, token: CancellationToken): ProviderResult<ISlashCommand[]>;
|
||||
removeRequest?(session: IChat, requestId: string): void;
|
||||
}
|
||||
|
||||
export interface ISlashCommand {
|
||||
|
@ -156,7 +149,6 @@ export interface IChatReplyFollowup {
|
|||
message: string;
|
||||
title?: string;
|
||||
tooltip?: string;
|
||||
metadata?: any;
|
||||
}
|
||||
|
||||
export interface IChatResponseCommandFollowup {
|
||||
|
@ -177,7 +169,6 @@ export enum InteractiveSessionVoteDirection {
|
|||
|
||||
export interface IChatVoteAction {
|
||||
kind: 'vote';
|
||||
responseId: string;
|
||||
direction: InteractiveSessionVoteDirection;
|
||||
}
|
||||
|
||||
|
@ -189,7 +180,6 @@ export enum InteractiveSessionCopyKind {
|
|||
|
||||
export interface IChatCopyAction {
|
||||
kind: 'copy';
|
||||
responseId: string;
|
||||
codeBlockIndex: number;
|
||||
copyType: InteractiveSessionCopyKind;
|
||||
copiedCharacters: number;
|
||||
|
@ -199,7 +189,6 @@ export interface IChatCopyAction {
|
|||
|
||||
export interface IChatInsertAction {
|
||||
kind: 'insert';
|
||||
responseId: string;
|
||||
codeBlockIndex: number;
|
||||
totalCharacters: number;
|
||||
newFile?: boolean;
|
||||
|
@ -207,7 +196,6 @@ export interface IChatInsertAction {
|
|||
|
||||
export interface IChatTerminalAction {
|
||||
kind: 'runInTerminal';
|
||||
responseId: string;
|
||||
codeBlockIndex: number;
|
||||
languageId?: string;
|
||||
}
|
||||
|
@ -271,7 +259,7 @@ export interface IChatService {
|
|||
_serviceBrand: undefined;
|
||||
transferredSessionData: IChatTransferredSessionData | undefined;
|
||||
|
||||
onDidSubmitSlashCommand: Event<{ slashCommand: string; sessionId: string } | { agent: IChatAgentData; slashCommand: IChatAgentCommand; sessionId: string }>;
|
||||
onDidSubmitAgent: Event<{ agent: IChatAgentData; slashCommand: IChatAgentCommand; sessionId: string }>;
|
||||
onDidRegisterProvider: Event<{ providerId: string }>;
|
||||
registerProvider(provider: IChatProvider): IDisposable;
|
||||
hasSessions(providerId: string): boolean;
|
||||
|
@ -285,10 +273,9 @@ export interface IChatService {
|
|||
/**
|
||||
* Returns whether the request was accepted.
|
||||
*/
|
||||
sendRequest(sessionId: string, message: string, usedSlashCommand?: ISlashCommand): Promise<{ responseCompletePromise: Promise<void> } | undefined>;
|
||||
sendRequest(sessionId: string, message: string): Promise<{ responseCompletePromise: Promise<void> } | undefined>;
|
||||
removeRequest(sessionid: string, requestId: string): Promise<void>;
|
||||
cancelCurrentRequestForSession(sessionId: string): void;
|
||||
getSlashCommands(sessionId: string, token: CancellationToken): Promise<ISlashCommand[]>;
|
||||
clearSession(sessionId: string): void;
|
||||
addCompleteRequest(sessionId: string, message: IParsedChatRequest | string, response: IChatCompleteResponse): void;
|
||||
sendRequestToProvider(sessionId: string, message: IChatDynamicRequest): void;
|
||||
|
|
|
@ -8,7 +8,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
|||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { MarkdownString, isMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableMap, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
|
@ -27,8 +27,8 @@ import { ChatModel, ChatModelInitState, ChatRequestModel, ChatWelcomeMessageMode
|
|||
import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { ChatMessageRole, IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider';
|
||||
import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser';
|
||||
import { IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatProvider, IChatProviderInfo, IChatRequest, IChatResponse, IChatService, IChatTransferredSessionData, IChatUserActionEvent, ISlashCommand, InteractiveSessionCopyKind, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatSlashCommandService, IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatProvider, IChatProviderInfo, IChatResponse, IChatService, IChatTransferredSessionData, IChatUserActionEvent, InteractiveSessionCopyKind, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
||||
|
@ -134,7 +134,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
|
||||
private readonly _providers = new Map<string, IChatProvider>();
|
||||
|
||||
private readonly _sessionModels = new Map<string, ChatModel>();
|
||||
private readonly _sessionModels = this._register(new DisposableMap<string, ChatModel>());
|
||||
private readonly _pendingRequests = new Map<string, CancelablePromise<void>>();
|
||||
private readonly _persistedSessions: ISerializableChatsData;
|
||||
private readonly _hasProvider: IContextKey<boolean>;
|
||||
|
@ -147,8 +147,8 @@ export class ChatService extends Disposable implements IChatService {
|
|||
private readonly _onDidPerformUserAction = this._register(new Emitter<IChatUserActionEvent>());
|
||||
public readonly onDidPerformUserAction: Event<IChatUserActionEvent> = this._onDidPerformUserAction.event;
|
||||
|
||||
private readonly _onDidSubmitSlashCommand = this._register(new Emitter<{ slashCommand: string; sessionId: string } | { agent: IChatAgentData; slashCommand: IChatAgentCommand; sessionId: string }>());
|
||||
public readonly onDidSubmitSlashCommand = this._onDidSubmitSlashCommand.event;
|
||||
private readonly _onDidSubmitAgent = this._register(new Emitter<{ agent: IChatAgentData; slashCommand: IChatAgentCommand; sessionId: string }>());
|
||||
public readonly onDidSubmitAgent = this._onDidSubmitAgent.event;
|
||||
|
||||
private readonly _onDidDisposeSession = this._register(new Emitter<{ sessionId: string; providerId: string; reason: 'initializationFailed' | 'cleared' }>());
|
||||
public readonly onDidDisposeSession = this._onDidDisposeSession.event;
|
||||
|
@ -365,7 +365,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
|
||||
let session: IChat | undefined;
|
||||
try {
|
||||
session = await provider.prepareSession(model.providerState, token) ?? undefined;
|
||||
session = await provider.prepareSession(token) ?? undefined;
|
||||
} catch (err) {
|
||||
this.trace('initializeSession', `Provider initializeSession threw: ${err}`);
|
||||
}
|
||||
|
@ -387,8 +387,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
} catch (err) {
|
||||
this.trace('startSession', `initializeSession failed: ${err}`);
|
||||
model.setInitializationError(err);
|
||||
model.dispose();
|
||||
this._sessionModels.delete(model.sessionId);
|
||||
this._sessionModels.deleteAndDispose(model.sessionId);
|
||||
this._onDidDisposeSession.fire({ sessionId: model.sessionId, providerId: model.providerId, reason: 'initializationFailed' });
|
||||
}
|
||||
}
|
||||
|
@ -424,7 +423,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
return this._startSession(data.providerId, data, CancellationToken.None);
|
||||
}
|
||||
|
||||
async sendRequest(sessionId: string, request: string, usedSlashCommand?: ISlashCommand): Promise<{ responseCompletePromise: Promise<void> } | undefined> {
|
||||
async sendRequest(sessionId: string, request: string): Promise<{ responseCompletePromise: Promise<void> } | undefined> {
|
||||
this.trace('sendRequest', `sessionId: ${sessionId}, message: ${request.substring(0, 20)}${request.length > 20 ? '[...]' : ''}}`);
|
||||
if (!request.trim()) {
|
||||
this.trace('sendRequest', 'Rejected empty message');
|
||||
|
@ -448,10 +447,10 @@ export class ChatService extends Disposable implements IChatService {
|
|||
}
|
||||
|
||||
// This method is only returning whether the request was accepted - don't block on the actual request
|
||||
return { responseCompletePromise: this._sendRequestAsync(model, sessionId, provider, request, usedSlashCommand) };
|
||||
return { responseCompletePromise: this._sendRequestAsync(model, sessionId, provider, request) };
|
||||
}
|
||||
|
||||
private async _sendRequestAsync(model: ChatModel, sessionId: string, provider: IChatProvider, message: string, usedSlashCommand?: ISlashCommand): Promise<void> {
|
||||
private async _sendRequestAsync(model: ChatModel, sessionId: string, provider: IChatProvider, message: string): Promise<void> {
|
||||
const parsedRequest = await this.instantiationService.createInstance(ChatRequestParser).parseChatRequest(sessionId, message);
|
||||
|
||||
let request: ChatRequestModel;
|
||||
|
@ -486,7 +485,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
} else if ('agentName' in progress) {
|
||||
this.trace('sendRequest', `Provider returned an agent detection for session ${model.sessionId}:\n ${JSON.stringify(progress, null, '\t')}`);
|
||||
} else {
|
||||
this.trace('sendRequest', `Provider returned id for session ${model.sessionId}, ${progress.requestId}`);
|
||||
this.trace('sendRequest', `Provider returned unknown progress for session ${model.sessionId}:\n ${JSON.stringify(progress, null, '\t')}`);
|
||||
}
|
||||
|
||||
model.acceptResponseProgress(request, progress);
|
||||
|
@ -503,7 +502,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
result: 'cancelled',
|
||||
requestType,
|
||||
agent: agentPart?.agent.id ?? '',
|
||||
slashCommand: agentSlashCommandPart ? agentSlashCommandPart.command.name : usedSlashCommand?.command,
|
||||
slashCommand: agentSlashCommandPart ? agentSlashCommandPart.command.name : commandPart?.slashCommand.command,
|
||||
chatSessionId: model.sessionId
|
||||
});
|
||||
|
||||
|
@ -511,10 +510,8 @@ export class ChatService extends Disposable implements IChatService {
|
|||
});
|
||||
|
||||
try {
|
||||
if (usedSlashCommand?.command) {
|
||||
this._onDidSubmitSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId });
|
||||
} else if (agentPart && agentSlashCommandPart?.command) {
|
||||
this._onDidSubmitSlashCommand.fire({ agent: agentPart.agent, slashCommand: agentSlashCommandPart.command, sessionId: model.sessionId });
|
||||
if (agentPart && agentSlashCommandPart?.command) {
|
||||
this._onDidSubmitAgent.fire({ agent: agentPart.agent, slashCommand: agentSlashCommandPart.command, sessionId: model.sessionId });
|
||||
}
|
||||
|
||||
let rawResponse: IChatResponse | null | undefined;
|
||||
|
@ -567,28 +564,14 @@ export class ChatService extends Disposable implements IChatService {
|
|||
history.push({ role: ChatMessageRole.User, content: request.message.text });
|
||||
history.push({ role: ChatMessageRole.Assistant, content: request.response.response.asString() });
|
||||
}
|
||||
const commandResult = await this.chatSlashCommandService.executeCommand(commandPart.slashCommand.command, message.substring(commandPart.slashCommand.command.length + 1).trimStart(), new Progress<IChatSlashFragment>(p => {
|
||||
const { content } = p;
|
||||
const data = isCompleteInteractiveProgressTreeData(content) ? content : { content };
|
||||
progressCallback(data);
|
||||
const commandResult = await this.chatSlashCommandService.executeCommand(commandPart.slashCommand.command, message.substring(commandPart.slashCommand.command.length + 1).trimStart(), new Progress<IChatProgress>(p => {
|
||||
progressCallback(p);
|
||||
}), history, token);
|
||||
agentOrCommandFollowups = Promise.resolve(commandResult?.followUp);
|
||||
rawResponse = { session: model.session! };
|
||||
|
||||
} else {
|
||||
request = model.addRequest(parsedRequest);
|
||||
const requestProps: IChatRequest = {
|
||||
session: model.session!,
|
||||
message,
|
||||
variables: {}
|
||||
};
|
||||
|
||||
if ('parts' in parsedRequest) {
|
||||
const varResult = await this.chatVariablesService.resolveVariables(parsedRequest, model, token);
|
||||
requestProps.variables = varResult.variables;
|
||||
requestProps.message = varResult.prompt;
|
||||
}
|
||||
rawResponse = await provider.provideReply(requestProps, progressCallback, token);
|
||||
throw new Error(`Can't handle request`);
|
||||
}
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
|
@ -610,7 +593,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
result,
|
||||
requestType,
|
||||
agent: agentPart?.agent.id ?? '',
|
||||
slashCommand: agentSlashCommandPart ? agentSlashCommandPart.command.name : usedSlashCommand?.command,
|
||||
slashCommand: agentSlashCommandPart ? agentSlashCommandPart.command.name : commandPart?.slashCommand.command,
|
||||
chatSessionId: model.sessionId
|
||||
});
|
||||
model.setResponse(request, rawResponse);
|
||||
|
@ -622,11 +605,6 @@ export class ChatService extends Disposable implements IChatService {
|
|||
model.setFollowups(request, followups);
|
||||
model.completeResponse(request);
|
||||
});
|
||||
} else if (provider.provideFollowups) {
|
||||
Promise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(providerFollowups => {
|
||||
model.setFollowups(request, providerFollowups ?? undefined);
|
||||
model.completeResponse(request);
|
||||
});
|
||||
} else {
|
||||
model.completeResponse(request);
|
||||
}
|
||||
|
@ -655,43 +633,6 @@ export class ChatService extends Disposable implements IChatService {
|
|||
}
|
||||
|
||||
model.removeRequest(requestId);
|
||||
provider.removeRequest?.(model.session!, requestId);
|
||||
}
|
||||
|
||||
async getSlashCommands(sessionId: string, token: CancellationToken): Promise<ISlashCommand[]> {
|
||||
const model = this._sessionModels.get(sessionId);
|
||||
if (!model) {
|
||||
throw new Error(`Unknown session: ${sessionId}`);
|
||||
}
|
||||
|
||||
await model.waitForInitialization();
|
||||
const provider = this._providers.get(model.providerId);
|
||||
if (!provider) {
|
||||
throw new Error(`Unknown provider: ${model.providerId}`);
|
||||
}
|
||||
|
||||
const serviceResults = this.chatSlashCommandService.getCommands().map(data => {
|
||||
return <ISlashCommand>{
|
||||
command: data.command,
|
||||
detail: data.detail,
|
||||
sortText: data.sortText,
|
||||
executeImmediately: data.executeImmediately
|
||||
};
|
||||
});
|
||||
|
||||
const mainProviderRequest = provider.provideSlashCommands?.(model.session!, token);
|
||||
|
||||
try {
|
||||
const providerResults = await mainProviderRequest;
|
||||
if (providerResults) {
|
||||
return providerResults.concat(serviceResults);
|
||||
}
|
||||
return serviceResults;
|
||||
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
return serviceResults;
|
||||
}
|
||||
}
|
||||
|
||||
async sendRequestToProvider(sessionId: string, message: IChatDynamicRequest): Promise<{ responseCompletePromise: Promise<void> } | undefined> {
|
||||
|
@ -748,8 +689,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
|
||||
this._persistedSessions[sessionId] = model.toJSON();
|
||||
|
||||
model.dispose();
|
||||
this._sessionModels.delete(sessionId);
|
||||
this._sessionModels.deleteAndDispose(sessionId);
|
||||
this._pendingRequests.get(sessionId)?.cancel();
|
||||
this._onDidDisposeSession.fire({ sessionId, providerId: model.providerId, reason: 'cleared' });
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IProgress } from 'vs/platform/progress/common/progress';
|
||||
import { IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider';
|
||||
import { IChatFollowup, IChatResponseProgressFileTreeData } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatFollowup, IChatProgress, IChatResponseProgressFileTreeData } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
||||
//#region slash service, commands etc
|
||||
|
@ -29,21 +29,18 @@ export interface IChatSlashData {
|
|||
export interface IChatSlashFragment {
|
||||
content: string | { treeData: IChatResponseProgressFileTreeData };
|
||||
}
|
||||
|
||||
export type IChatSlashCallback = { (prompt: string, progress: IProgress<IChatSlashFragment>, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> };
|
||||
export type IChatSlashCallback = { (prompt: string, progress: IProgress<IChatProgress>, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> };
|
||||
|
||||
export const IChatSlashCommandService = createDecorator<IChatSlashCommandService>('chatSlashCommandService');
|
||||
|
||||
/**
|
||||
* This currently only exists to drive /clear. Delete this when the agent service can handle that scenario
|
||||
* This currently only exists to drive /clear and /help
|
||||
*/
|
||||
export interface IChatSlashCommandService {
|
||||
_serviceBrand: undefined;
|
||||
readonly onDidChangeCommands: Event<void>;
|
||||
registerSlashData(data: IChatSlashData): IDisposable;
|
||||
registerSlashCallback(id: string, command: IChatSlashCallback): IDisposable;
|
||||
registerSlashCommand(data: IChatSlashData, command: IChatSlashCallback): IDisposable;
|
||||
executeCommand(id: string, prompt: string, progress: IProgress<IChatSlashFragment>, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void>;
|
||||
executeCommand(id: string, prompt: string, progress: IProgress<IChatProgress>, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void>;
|
||||
getCommands(): Array<IChatSlashData>;
|
||||
hasCommand(id: string): boolean;
|
||||
}
|
||||
|
@ -68,11 +65,12 @@ export class ChatSlashCommandService extends Disposable implements IChatSlashCom
|
|||
this._commands.clear();
|
||||
}
|
||||
|
||||
registerSlashData(data: IChatSlashData): IDisposable {
|
||||
registerSlashCommand(data: IChatSlashData, command: IChatSlashCallback): IDisposable {
|
||||
if (this._commands.has(data.command)) {
|
||||
throw new Error(`Already registered a command with id ${data.command}}`);
|
||||
}
|
||||
this._commands.set(data.command, { data });
|
||||
|
||||
this._commands.set(data.command, { data, command });
|
||||
this._onDidChangeCommands.fire();
|
||||
|
||||
return toDisposable(() => {
|
||||
|
@ -82,22 +80,6 @@ export class ChatSlashCommandService extends Disposable implements IChatSlashCom
|
|||
});
|
||||
}
|
||||
|
||||
registerSlashCallback(id: string, command: IChatSlashCallback): IDisposable {
|
||||
const data = this._commands.get(id);
|
||||
if (!data) {
|
||||
throw new Error(`No command with id ${id} registered`);
|
||||
}
|
||||
data.command = command;
|
||||
return toDisposable(() => data.command = undefined);
|
||||
}
|
||||
|
||||
registerSlashCommand(data: IChatSlashData, command: IChatSlashCallback): IDisposable {
|
||||
return combinedDisposable(
|
||||
this.registerSlashData(data),
|
||||
this.registerSlashCallback(data.command, command)
|
||||
);
|
||||
}
|
||||
|
||||
getCommands(): Array<IChatSlashData> {
|
||||
return Array.from(this._commands.values(), v => v.data);
|
||||
}
|
||||
|
@ -106,7 +88,7 @@ export class ChatSlashCommandService extends Disposable implements IChatSlashCom
|
|||
return this._commands.has(id);
|
||||
}
|
||||
|
||||
async executeCommand(id: string, prompt: string, progress: IProgress<IChatSlashFragment>, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> {
|
||||
async executeCommand(id: string, prompt: string, progress: IProgress<IChatProgress>, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> {
|
||||
const data = this._commands.get(id);
|
||||
if (!data) {
|
||||
throw new Error('No command with id ${id} NOT registered');
|
||||
|
|
|
@ -55,7 +55,6 @@ export interface IChatViewModel {
|
|||
|
||||
export interface IChatRequestViewModel {
|
||||
readonly id: string;
|
||||
readonly providerRequestId: string | undefined;
|
||||
readonly sessionId: string;
|
||||
/** This ID updates every time the underlying data changes */
|
||||
readonly dataId: string;
|
||||
|
@ -88,7 +87,6 @@ export interface IChatResponseViewModel {
|
|||
/** This ID updates every time the underlying data changes */
|
||||
readonly dataId: string;
|
||||
readonly providerId: string;
|
||||
readonly providerResponseId: string | undefined;
|
||||
/** The ID of the associated IChatRequestViewModel */
|
||||
readonly requestId: string;
|
||||
readonly username: string;
|
||||
|
@ -173,12 +171,12 @@ export class ChatViewModel extends Disposable implements IChatViewModel {
|
|||
} else if (e.kind === 'addResponse') {
|
||||
this.onAddResponse(e.response);
|
||||
} else if (e.kind === 'removeRequest') {
|
||||
const requestIdx = this._items.findIndex(item => isRequestVM(item) && item.providerRequestId === e.requestId);
|
||||
const requestIdx = this._items.findIndex(item => isRequestVM(item) && item.id === e.requestId);
|
||||
if (requestIdx >= 0) {
|
||||
this._items.splice(requestIdx, 1);
|
||||
}
|
||||
|
||||
const responseIdx = e.responseId && this._items.findIndex(item => isResponseVM(item) && item.providerResponseId === e.responseId);
|
||||
const responseIdx = e.responseId && this._items.findIndex(item => isResponseVM(item) && item.id === e.responseId);
|
||||
if (typeof responseIdx === 'number' && responseIdx >= 0) {
|
||||
const items = this._items.splice(responseIdx, 1);
|
||||
const item = items[0];
|
||||
|
@ -218,10 +216,6 @@ export class ChatRequestViewModel implements IChatRequestViewModel {
|
|||
return this._model.id;
|
||||
}
|
||||
|
||||
get providerRequestId() {
|
||||
return this._model.providerRequestId;
|
||||
}
|
||||
|
||||
get dataId() {
|
||||
return this.id + `_${ChatModelInitState[this._model.session.initState]}`;
|
||||
}
|
||||
|
@ -269,10 +263,6 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi
|
|||
return this._model.providerId;
|
||||
}
|
||||
|
||||
get providerResponseId() {
|
||||
return this._model.providerResponseId;
|
||||
}
|
||||
|
||||
get sessionId() {
|
||||
return this._model.session.sessionId;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import { IViewsService } from 'vs/workbench/common/views';
|
|||
import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { isExecuteActionContext } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions';
|
||||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { HasSpeechProvider, ISpeechService, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
@ -41,6 +40,7 @@ import { contrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegis
|
|||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { isNumber } from 'vs/base/common/types';
|
||||
import { AccessibilityVoiceSettingId, SpeechTimeoutDefault } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
|
||||
import { IChatExecuteActionContext } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions';
|
||||
|
||||
const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey<boolean>('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when getting ready for receiving voice input from the microphone for voice chat.") });
|
||||
const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey<boolean>('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when voice recording from microphone is in progress for voice chat.") });
|
||||
|
@ -486,7 +486,8 @@ export class StartVoiceChatAction extends Action2 {
|
|||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const commandService = accessor.get(ICommandService);
|
||||
|
||||
if (isExecuteActionContext(context)) {
|
||||
const widget = (context as IChatExecuteActionContext)?.widget;
|
||||
if (widget) {
|
||||
// if we already get a context when the action is executed
|
||||
// from a toolbar within the chat widget, then make sure
|
||||
// to move focus into the input field so that the controller
|
||||
|
@ -494,7 +495,7 @@ export class StartVoiceChatAction extends Action2 {
|
|||
// TODO@bpasero this will actually not work if the button
|
||||
// is clicked from the inline editor while focus is in a
|
||||
// chat input field in a view or picker
|
||||
context.widget.focusInput();
|
||||
widget.focusInput();
|
||||
}
|
||||
|
||||
const controller = await VoiceChatSessionControllerFactory.create(accessor, 'focused');
|
||||
|
|
|
@ -6,32 +6,53 @@
|
|||
welcomeMessage: undefined,
|
||||
requests: [
|
||||
{
|
||||
providerRequestId: undefined,
|
||||
message: {
|
||||
text: "test request",
|
||||
text: "@ChatProviderWithUsedContext test request",
|
||||
parts: [
|
||||
{
|
||||
range: {
|
||||
start: 0,
|
||||
endExclusive: 12
|
||||
endExclusive: 28
|
||||
},
|
||||
editorRange: {
|
||||
startLineNumber: 1,
|
||||
startColumn: 1,
|
||||
endLineNumber: 1,
|
||||
endColumn: 13
|
||||
endColumn: 29
|
||||
},
|
||||
text: "test request",
|
||||
agent: {
|
||||
id: "ChatProviderWithUsedContext",
|
||||
metadata: { },
|
||||
provideSlashCommands: [Function provideSlashCommands],
|
||||
invoke: [Function invoke]
|
||||
},
|
||||
kind: "agent"
|
||||
},
|
||||
{
|
||||
range: {
|
||||
start: 28,
|
||||
endExclusive: 41
|
||||
},
|
||||
editorRange: {
|
||||
startLineNumber: 1,
|
||||
startColumn: 29,
|
||||
endLineNumber: 1,
|
||||
endColumn: 42
|
||||
},
|
||||
text: " test request",
|
||||
kind: "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
response: [ ],
|
||||
responseErrorDetails: undefined,
|
||||
followups: undefined,
|
||||
followups: [ ],
|
||||
isCanceled: false,
|
||||
vote: undefined,
|
||||
agent: undefined,
|
||||
agent: {
|
||||
id: "ChatProviderWithUsedContext",
|
||||
metadata: { }
|
||||
},
|
||||
slashCommand: undefined,
|
||||
usedContext: { documents: [
|
||||
{
|
||||
|
@ -58,6 +79,5 @@
|
|||
contentReferences: [ ]
|
||||
}
|
||||
],
|
||||
providerId: "ChatProviderWithUsedContext",
|
||||
providerState: undefined
|
||||
providerId: "testProvider"
|
||||
}
|
|
@ -5,6 +5,5 @@
|
|||
responderAvatarIconUri: undefined,
|
||||
welcomeMessage: undefined,
|
||||
requests: [ ],
|
||||
providerId: "ChatProviderWithUsedContext",
|
||||
providerState: undefined
|
||||
providerId: "testProvider"
|
||||
}
|
|
@ -6,32 +6,53 @@
|
|||
welcomeMessage: undefined,
|
||||
requests: [
|
||||
{
|
||||
providerRequestId: undefined,
|
||||
message: {
|
||||
parts: [
|
||||
{
|
||||
range: {
|
||||
start: 0,
|
||||
endExclusive: 12
|
||||
endExclusive: 28
|
||||
},
|
||||
editorRange: {
|
||||
startLineNumber: 1,
|
||||
startColumn: 1,
|
||||
endLineNumber: 1,
|
||||
endColumn: 13
|
||||
endColumn: 29
|
||||
},
|
||||
text: "test request",
|
||||
agent: {
|
||||
id: "ChatProviderWithUsedContext",
|
||||
metadata: { },
|
||||
provideSlashCommands: [Function provideSlashCommands],
|
||||
invoke: [Function invoke]
|
||||
},
|
||||
kind: "agent"
|
||||
},
|
||||
{
|
||||
range: {
|
||||
start: 28,
|
||||
endExclusive: 41
|
||||
},
|
||||
editorRange: {
|
||||
startLineNumber: 1,
|
||||
startColumn: 29,
|
||||
endLineNumber: 1,
|
||||
endColumn: 42
|
||||
},
|
||||
text: " test request",
|
||||
kind: "text"
|
||||
}
|
||||
],
|
||||
text: "test request"
|
||||
text: "@ChatProviderWithUsedContext test request"
|
||||
},
|
||||
response: [ ],
|
||||
responseErrorDetails: undefined,
|
||||
followups: undefined,
|
||||
followups: [ ],
|
||||
isCanceled: false,
|
||||
vote: undefined,
|
||||
agent: undefined,
|
||||
agent: {
|
||||
id: "ChatProviderWithUsedContext",
|
||||
metadata: { }
|
||||
},
|
||||
slashCommand: undefined,
|
||||
usedContext: { documents: [
|
||||
{
|
||||
|
@ -58,6 +79,5 @@
|
|||
contentReferences: [ ]
|
||||
}
|
||||
],
|
||||
providerId: "ChatProviderWithUsedContext",
|
||||
providerState: undefined
|
||||
providerId: "testProvider"
|
||||
}
|
|
@ -6,11 +6,14 @@
|
|||
import * as assert from 'assert';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { OffsetRange } from 'vs/editor/common/core/offsetRange';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ChatAgentService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatModel } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { ChatRequestTextPart } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
|
||||
|
@ -98,4 +101,18 @@ suite('ChatModel', () => {
|
|||
|
||||
assert.throws(() => model.initialize({} as any, undefined));
|
||||
});
|
||||
|
||||
test('removeRequest', async () => {
|
||||
const model = testDisposables.add(instantiationService.createInstance(ChatModel, 'provider', undefined));
|
||||
|
||||
model.startInitialize();
|
||||
model.initialize({} as any, undefined);
|
||||
const text = 'hello';
|
||||
model.addRequest({ text, parts: [new ChatRequestTextPart(new OffsetRange(0, text.length), new Range(1, text.length, 1, text.length), text)] });
|
||||
const requests = model.getRequests();
|
||||
assert.strictEqual(requests.length, 1);
|
||||
|
||||
model.removeRequest(requests[0].id);
|
||||
assert.strictEqual(model.getRequests().length, 0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ import { ILogService, NullLogService } from 'vs/platform/log/common/log';
|
|||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ChatAgentService, IChatAgent, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser';
|
||||
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
|
@ -48,9 +48,9 @@ suite('ChatRequestParser', () => {
|
|||
});
|
||||
|
||||
test('slash command', async () => {
|
||||
const chatService = mockObject<IChatService>()({});
|
||||
chatService.getSlashCommands.returns(Promise.resolve([{ command: 'fix' }]));
|
||||
instantiationService.stub(IChatService, chatService as any);
|
||||
const slashCommandService = mockObject<IChatSlashCommandService>()({});
|
||||
slashCommandService.getCommands.returns([{ command: 'fix' }]);
|
||||
instantiationService.stub(IChatSlashCommandService, slashCommandService as any);
|
||||
|
||||
parser = instantiationService.createInstance(ChatRequestParser);
|
||||
const text = '/fix this';
|
||||
|
@ -59,9 +59,9 @@ suite('ChatRequestParser', () => {
|
|||
});
|
||||
|
||||
test('invalid slash command', async () => {
|
||||
const chatService = mockObject<IChatService>()({});
|
||||
chatService.getSlashCommands.returns(Promise.resolve([{ command: 'fix' }]));
|
||||
instantiationService.stub(IChatService, chatService as any);
|
||||
const slashCommandService = mockObject<IChatSlashCommandService>()({});
|
||||
slashCommandService.getCommands.returns([{ command: 'fix' }]);
|
||||
instantiationService.stub(IChatSlashCommandService, slashCommandService as any);
|
||||
|
||||
parser = instantiationService.createInstance(ChatRequestParser);
|
||||
const text = '/explain this';
|
||||
|
@ -70,9 +70,9 @@ suite('ChatRequestParser', () => {
|
|||
});
|
||||
|
||||
test('multiple slash commands', async () => {
|
||||
const chatService = mockObject<IChatService>()({});
|
||||
chatService.getSlashCommands.returns(Promise.resolve([{ command: 'fix' }]));
|
||||
instantiationService.stub(IChatService, chatService as any);
|
||||
const slashCommandService = mockObject<IChatSlashCommandService>()({});
|
||||
slashCommandService.getCommands.returns([{ command: 'fix' }]);
|
||||
instantiationService.stub(IChatSlashCommandService, slashCommandService as any);
|
||||
|
||||
parser = instantiationService.createInstance(ChatRequestParser);
|
||||
const text = '/fix /fix';
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { assertSnapshot } from 'vs/base/test/common/snapshot';
|
||||
|
@ -22,10 +21,10 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
|||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IViewsService } from 'vs/workbench/common/views';
|
||||
import { ChatAgentService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { ChatAgentService, IChatAgent, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService';
|
||||
import { ISerializableChatData } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { IChat, IChatProgress, IChatProvider, IChatRequest, IChatResponse, IPersistedChatState, ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChat, IChatProgress, IChatProvider, IChatRequest } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { ChatService } from 'vs/workbench/contrib/chat/common/chatServiceImpl';
|
||||
import { ChatSlashCommandService, IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands';
|
||||
import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
|
@ -36,29 +35,18 @@ import { TestContextService, TestExtensionService, TestStorageService } from 'vs
|
|||
class SimpleTestProvider extends Disposable implements IChatProvider {
|
||||
private static sessionId = 0;
|
||||
|
||||
lastInitialState = undefined;
|
||||
|
||||
readonly displayName = 'Test';
|
||||
|
||||
private _onDidChangeState = this._register(new Emitter());
|
||||
|
||||
constructor(readonly id: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
prepareSession(initialState: any) {
|
||||
this.lastInitialState = initialState;
|
||||
return Promise.resolve(<IChat>{
|
||||
async prepareSession(): Promise<IChat> {
|
||||
return {
|
||||
id: SimpleTestProvider.sessionId++,
|
||||
username: 'test',
|
||||
responderUsername: 'test',
|
||||
requesterUsername: 'test',
|
||||
onDidChangeState: this._onDidChangeState.event
|
||||
});
|
||||
}
|
||||
|
||||
changeState(state: any) {
|
||||
this._onDidChangeState.fire(state);
|
||||
};
|
||||
}
|
||||
|
||||
async provideReply(request: IChatRequest, progress: (progress: IChatProgress) => void): Promise<{ session: IChat; followups: never[] }> {
|
||||
|
@ -66,10 +54,14 @@ class SimpleTestProvider extends Disposable implements IChatProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/** Chat provider for testing that returns used context */
|
||||
class ChatProviderWithUsedContext extends SimpleTestProvider implements IChatProvider {
|
||||
override provideReply(request: IChatRequest, progress: (progress: IChatProgress) => void): Promise<{ session: IChat; followups: never[] }> {
|
||||
|
||||
const chatAgentWithUsedContextId = 'ChatProviderWithUsedContext';
|
||||
const chatAgentWithUsedContext: IChatAgent = {
|
||||
id: chatAgentWithUsedContextId,
|
||||
metadata: {},
|
||||
async provideSlashCommands(token) {
|
||||
return [];
|
||||
},
|
||||
async invoke(request, progress, history, token) {
|
||||
progress({
|
||||
documents: [
|
||||
{
|
||||
|
@ -82,9 +74,9 @@ class ChatProviderWithUsedContext extends SimpleTestProvider implements IChatPro
|
|||
]
|
||||
});
|
||||
|
||||
return super.provideReply(request, progress);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
},
|
||||
};
|
||||
|
||||
suite('Chat', () => {
|
||||
const testDisposables = ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
@ -92,6 +84,8 @@ suite('Chat', () => {
|
|||
let storageService: IStorageService;
|
||||
let instantiationService: TestInstantiationService;
|
||||
|
||||
let chatAgentService: IChatAgentService;
|
||||
|
||||
setup(async () => {
|
||||
instantiationService = testDisposables.add(new TestInstantiationService(new ServiceCollection(
|
||||
[IChatVariablesService, new MockChatVariablesService()],
|
||||
|
@ -105,7 +99,18 @@ suite('Chat', () => {
|
|||
instantiationService.stub(IChatContributionService, new TestExtensionService());
|
||||
instantiationService.stub(IWorkspaceContextService, new TestContextService());
|
||||
instantiationService.stub(IChatSlashCommandService, testDisposables.add(instantiationService.createInstance(ChatSlashCommandService)));
|
||||
instantiationService.stub(IChatAgentService, testDisposables.add(instantiationService.createInstance(ChatAgentService)));
|
||||
|
||||
chatAgentService = testDisposables.add(instantiationService.createInstance(ChatAgentService));
|
||||
instantiationService.stub(IChatAgentService, chatAgentService);
|
||||
|
||||
const agent = {
|
||||
id: 'testAgent',
|
||||
metadata: { isDefault: true },
|
||||
async invoke(request, progress, history, token) {
|
||||
return {};
|
||||
},
|
||||
} as IChatAgent;
|
||||
testDisposables.add(chatAgentService.registerAgent(agent));
|
||||
});
|
||||
|
||||
test('retrieveSession', async () => {
|
||||
|
@ -123,12 +128,7 @@ suite('Chat', () => {
|
|||
await session2.waitForInitialization();
|
||||
session2!.addRequest({ parts: [], text: 'request 2' });
|
||||
|
||||
assert.strictEqual(provider1.lastInitialState, undefined);
|
||||
assert.strictEqual(provider2.lastInitialState, undefined);
|
||||
provider1.changeState({ state: 'provider1_state' });
|
||||
provider2.changeState({ state: 'provider2_state' });
|
||||
storageService.flush();
|
||||
|
||||
const testService2 = testDisposables.add(instantiationService.createInstance(ChatService));
|
||||
testDisposables.add(testService2.registerProvider(provider1));
|
||||
testDisposables.add(testService2.registerProvider(provider2));
|
||||
|
@ -136,8 +136,8 @@ suite('Chat', () => {
|
|||
await retrieved1!.waitForInitialization();
|
||||
const retrieved2 = testDisposables.add(testService2.getOrRestoreSession(session2.sessionId)!);
|
||||
await retrieved2!.waitForInitialization();
|
||||
assert.deepStrictEqual(provider1.lastInitialState, { state: 'provider1_state' });
|
||||
assert.deepStrictEqual(provider2.lastInitialState, { state: 'provider2_state' });
|
||||
assert.deepStrictEqual(retrieved1.getRequests()[0]?.message.text, 'request 1');
|
||||
assert.deepStrictEqual(retrieved2.getRequests()[0]?.message.text, 'request 2');
|
||||
});
|
||||
|
||||
test('Handles failed session startup', async () => {
|
||||
|
@ -172,10 +172,7 @@ suite('Chat', () => {
|
|||
testDisposables.add(testService.registerProvider({
|
||||
id,
|
||||
displayName: 'Test',
|
||||
prepareSession: function (initialState: IPersistedChatState | undefined, token: CancellationToken): ProviderResult<IChat | undefined> {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
provideReply: function (request: IChatRequest, progress: (progress: IChatProgress) => void, token: CancellationToken): ProviderResult<IChatResponse> {
|
||||
prepareSession: function (token: CancellationToken): ProviderResult<IChat | undefined> {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
}));
|
||||
|
@ -184,45 +181,13 @@ suite('Chat', () => {
|
|||
testDisposables.add(testService.registerProvider({
|
||||
id,
|
||||
displayName: 'Test',
|
||||
prepareSession: function (initialState: IPersistedChatState | undefined, token: CancellationToken): ProviderResult<IChat | undefined> {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
provideReply: function (request: IChatRequest, progress: (progress: IChatProgress) => void, token: CancellationToken): ProviderResult<IChatResponse> {
|
||||
prepareSession: function (token: CancellationToken): ProviderResult<IChat | undefined> {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
}));
|
||||
}, 'Expected to throw for dupe provider');
|
||||
});
|
||||
|
||||
test('getSlashCommands', async () => {
|
||||
const testService = testDisposables.add(instantiationService.createInstance(ChatService));
|
||||
const provider = testDisposables.add(new class extends SimpleTestProvider {
|
||||
constructor() {
|
||||
super('testProvider');
|
||||
}
|
||||
|
||||
provideSlashCommands(): ProviderResult<ISlashCommand[]> {
|
||||
return [
|
||||
{
|
||||
command: 'command',
|
||||
detail: 'detail',
|
||||
sortText: 'sortText',
|
||||
}
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
testDisposables.add(testService.registerProvider(provider));
|
||||
|
||||
const model = testDisposables.add(testService.startSession('testProvider', CancellationToken.None));
|
||||
const commands = await testService.getSlashCommands(model.sessionId, CancellationToken.None);
|
||||
|
||||
assert.strictEqual(commands?.length, 1);
|
||||
assert.strictEqual(commands?.[0].command, 'command');
|
||||
assert.strictEqual(commands?.[0].detail, 'detail');
|
||||
assert.strictEqual(commands?.[0].sortText, 'sortText');
|
||||
});
|
||||
|
||||
test('sendRequestToProvider', async () => {
|
||||
const testService = testDisposables.add(instantiationService.createInstance(ChatService));
|
||||
testDisposables.add(testService.registerProvider(testDisposables.add(new SimpleTestProvider('testProvider'))));
|
||||
|
@ -249,18 +214,16 @@ suite('Chat', () => {
|
|||
});
|
||||
|
||||
test('can serialize', async () => {
|
||||
|
||||
testDisposables.add(chatAgentService.registerAgent(chatAgentWithUsedContext));
|
||||
const testService = testDisposables.add(instantiationService.createInstance(ChatService));
|
||||
const providerId = 'ChatProviderWithUsedContext';
|
||||
testDisposables.add(testService.registerProvider(testDisposables.add(new SimpleTestProvider('testProvider'))));
|
||||
|
||||
testDisposables.add(testService.registerProvider(testDisposables.add(new ChatProviderWithUsedContext(providerId))));
|
||||
|
||||
const model = testDisposables.add(testService.startSession(providerId, CancellationToken.None));
|
||||
const model = testDisposables.add(testService.startSession('testProvider', CancellationToken.None));
|
||||
assert.strictEqual(model.getRequests().length, 0);
|
||||
|
||||
await assertSnapshot(model.toExport());
|
||||
|
||||
const response = await testService.sendRequest(model.sessionId, 'test request');
|
||||
const response = await testService.sendRequest(model.sessionId, `@${chatAgentWithUsedContextId} test request`);
|
||||
assert(response);
|
||||
|
||||
await response.responseCompletePromise;
|
||||
|
@ -271,21 +234,18 @@ suite('Chat', () => {
|
|||
});
|
||||
|
||||
test('can deserialize', async () => {
|
||||
|
||||
let serializedChatData: ISerializableChatData;
|
||||
|
||||
const providerId = 'ChatProviderWithUsedContext';
|
||||
testDisposables.add(chatAgentService.registerAgent(chatAgentWithUsedContext));
|
||||
|
||||
// create the first service, send request, get response, and serialize the state
|
||||
{ // serapate block to not leak variables in outer scope
|
||||
const testService = testDisposables.add(instantiationService.createInstance(ChatService));
|
||||
testDisposables.add(testService.registerProvider(testDisposables.add(new SimpleTestProvider('testProvider'))));
|
||||
|
||||
testDisposables.add(testService.registerProvider(testDisposables.add(new ChatProviderWithUsedContext(providerId))));
|
||||
|
||||
const chatModel1 = testDisposables.add(testService.startSession(providerId, CancellationToken.None));
|
||||
const chatModel1 = testDisposables.add(testService.startSession('testProvider', CancellationToken.None));
|
||||
assert.strictEqual(chatModel1.getRequests().length, 0);
|
||||
|
||||
const response = await testService.sendRequest(chatModel1.sessionId, 'test request');
|
||||
const response = await testService.sendRequest(chatModel1.sessionId, `@${chatAgentWithUsedContextId} test request`);
|
||||
assert(response);
|
||||
|
||||
await response.responseCompletePromise;
|
||||
|
@ -296,17 +256,11 @@ suite('Chat', () => {
|
|||
// try deserializing the state into a new service
|
||||
|
||||
const testService2 = testDisposables.add(instantiationService.createInstance(ChatService));
|
||||
|
||||
testDisposables.add(testService2.registerProvider(testDisposables.add(new ChatProviderWithUsedContext(providerId))));
|
||||
testDisposables.add(testService2.registerProvider(testDisposables.add(new SimpleTestProvider('testProvider'))));
|
||||
|
||||
const chatModel2 = testService2.loadSessionFromContent(serializedChatData);
|
||||
assert(chatModel2);
|
||||
|
||||
// should `loadSessionFromContent` return `ChatModel` that's disposable instead of `IChatModel`?
|
||||
testDisposables.add({
|
||||
dispose: () => testService2.clearSession(serializedChatData.sessionId)
|
||||
});
|
||||
|
||||
await assertSnapshot(chatModel2.toExport());
|
||||
});
|
||||
});
|
||||
|
|
|
@ -729,7 +729,7 @@ export class CommentNode<T extends IRange | ICellRange> extends Disposable {
|
|||
|
||||
|
||||
private onContextMenu(e: MouseEvent) {
|
||||
const event = new StandardMouseEvent(e);
|
||||
const event = new StandardMouseEvent(dom.getWindow(this._domNode), e);
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => event,
|
||||
|
|
|
@ -117,7 +117,7 @@ export class CommentThreadHeader<T = IRange> extends Disposable {
|
|||
if (!actions.length) {
|
||||
return;
|
||||
}
|
||||
const event = new StandardMouseEvent(e);
|
||||
const event = new StandardMouseEvent(dom.getWindow(this._headElement), e);
|
||||
this._contextMenuService.showContextMenu({
|
||||
getAnchor: () => event,
|
||||
getActions: () => actions,
|
||||
|
|
|
@ -726,7 +726,7 @@ class InlineBreakpointWidget implements IContentWidget, IDisposable {
|
|||
}
|
||||
}));
|
||||
this.toDispose.push(dom.addDisposableListener(this.domNode, dom.EventType.CONTEXT_MENU, e => {
|
||||
const event = new StandardMouseEvent(e);
|
||||
const event = new StandardMouseEvent(dom.getWindow(this.domNode), e);
|
||||
const actions = this.getContextMenuActions();
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => event,
|
||||
|
|
|
@ -151,7 +151,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution {
|
|||
this._register(dom.addDisposableListener(mainWindow, dom.EventType.RESIZE, () => this.setCoordinates()));
|
||||
|
||||
this._register(dom.addDisposableGenericMouseUpListener(this.dragArea, (event: MouseEvent) => {
|
||||
const mouseClickEvent = new StandardMouseEvent(event);
|
||||
const mouseClickEvent = new StandardMouseEvent(dom.getWindow(this.dragArea), event);
|
||||
if (mouseClickEvent.detail === 2) {
|
||||
// double click on debug bar centers it again #8250
|
||||
const widgetWidth = this.$el.clientWidth;
|
||||
|
@ -164,7 +164,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution {
|
|||
this.dragArea.classList.add('dragged');
|
||||
|
||||
const mouseMoveListener = dom.addDisposableGenericMouseMoveListener(mainWindow, (e: MouseEvent) => {
|
||||
const mouseMoveEvent = new StandardMouseEvent(e);
|
||||
const mouseMoveEvent = new StandardMouseEvent(mainWindow, e);
|
||||
// Prevent default to stop editor selecting text #8524
|
||||
mouseMoveEvent.preventDefault();
|
||||
// Reduce x by width of drag handle to reduce jarring #16604
|
||||
|
|
|
@ -73,7 +73,7 @@ export class ExtensionsGridView extends Disposable {
|
|||
e.preventDefault();
|
||||
};
|
||||
|
||||
this.disposableStore.add(dom.addDisposableListener(template.name, dom.EventType.CLICK, (e: MouseEvent) => handleEvent(new StandardMouseEvent(e))));
|
||||
this.disposableStore.add(dom.addDisposableListener(template.name, dom.EventType.CLICK, (e: MouseEvent) => handleEvent(new StandardMouseEvent(dom.getWindow(template.name), e))));
|
||||
this.disposableStore.add(dom.addDisposableListener(template.name, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => handleEvent(new StandardKeyboardEvent(e))));
|
||||
this.disposableStore.add(dom.addDisposableListener(extensionContainer, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => handleEvent(new StandardKeyboardEvent(e))));
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
|||
import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget';
|
||||
import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_VISIBLE, MENU_INLINE_CHAT_WIDGET, MENU_INLINE_CHAT_WIDGET_STATUS, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, CTX_INLINE_CHAT_MESSAGE_CROP_STATE, IInlineChatSlashCommand, MENU_INLINE_CHAT_WIDGET_FEEDBACK, ACTION_REGENERATE_RESPONSE, ACTION_VIEW_IN_CHAT, MENU_INLINE_CHAT_WIDGET_TOGGLE, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_INNER_CURSOR_START, CTX_INLINE_CHAT_INNER_CURSOR_END, CTX_INLINE_CHAT_RESPONSE_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
|
||||
import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
|
||||
import { EventType, Dimension, addDisposableListener, getActiveElement, getTotalHeight, getTotalWidth, h, reset } from 'vs/base/browser/dom';
|
||||
import { EventType, Dimension, addDisposableListener, getActiveElement, getTotalHeight, getTotalWidth, h, reset, getWindow } from 'vs/base/browser/dom';
|
||||
import { Emitter, Event, MicrotaskEmitter } from 'vs/base/common/event';
|
||||
import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration';
|
||||
import { ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
|
@ -403,7 +403,7 @@ export class InlineChatWidget {
|
|||
}
|
||||
|
||||
private _onContextMenu(e: MouseEvent) {
|
||||
const event = new StandardMouseEvent(e);
|
||||
const event = new StandardMouseEvent(getWindow(this._elements.root), e);
|
||||
this._contextMenuService.showContextMenu({
|
||||
menuId: MENU_INLINE_CHAT_WIDGET_TOGGLE,
|
||||
getAnchor: () => event,
|
||||
|
|
|
@ -135,7 +135,7 @@ export class NotebookStickyScroll extends Disposable {
|
|||
}
|
||||
|
||||
private onContextMenu(e: MouseEvent) {
|
||||
const event = new StandardMouseEvent(e);
|
||||
const event = new StandardMouseEvent(DOM.getWindow(this.domNode), e);
|
||||
this._contextMenuService.showContextMenu({
|
||||
menuId: MenuId.NotebookStickyScrollContext,
|
||||
getAnchor: () => event,
|
||||
|
|
|
@ -48,8 +48,8 @@ export class TerminalContextActionRunner extends ActionRunner {
|
|||
}
|
||||
}
|
||||
|
||||
export function openContextMenu(event: MouseEvent, contextInstances: SingleOrMany<ITerminalInstance> | undefined, menu: IMenu, contextMenuService: IContextMenuService, extraActions?: IAction[]): void {
|
||||
const standardEvent = new StandardMouseEvent(event);
|
||||
export function openContextMenu(targetWindow: Window, event: MouseEvent, contextInstances: SingleOrMany<ITerminalInstance> | undefined, menu: IMenu, contextMenuService: IContextMenuService, extraActions?: IAction[]): void {
|
||||
const standardEvent = new StandardMouseEvent(targetWindow, event);
|
||||
|
||||
const actions: IAction[] = [];
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ export class TerminalEditor extends EditorPane {
|
|||
|
||||
// copyPaste: Shift+right click should open context menu
|
||||
if (rightClickBehavior === 'copyPaste' && event.shiftKey) {
|
||||
openContextMenu(event, this._editorInput?.terminalInstance, this._instanceMenu, this._contextMenuService);
|
||||
openContextMenu(dom.getWindow(this._editorInstanceElement), event, this._editorInput?.terminalInstance, this._instanceMenu, this._contextMenuService);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -180,7 +180,7 @@ export class TerminalEditor extends EditorPane {
|
|||
else
|
||||
if (!this._cancelContextMenu && rightClickBehavior !== 'copyPaste' && rightClickBehavior !== 'paste') {
|
||||
if (!this._cancelContextMenu) {
|
||||
openContextMenu(event, this._editorInput?.terminalInstance, this._instanceMenu, this._contextMenuService);
|
||||
openContextMenu(dom.getWindow(this._editorInstanceElement), event, this._editorInput?.terminalInstance, this._instanceMenu, this._contextMenuService);
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
|
|
@ -353,7 +353,7 @@ export class TerminalTabbedView extends Disposable {
|
|||
else if (rightClickBehavior === 'copyPaste' || rightClickBehavior === 'paste') {
|
||||
// copyPaste: Shift+right click should open context menu
|
||||
if (rightClickBehavior === 'copyPaste' && event.shiftKey) {
|
||||
openContextMenu(event, terminal, this._instanceMenu, this._contextMenuService);
|
||||
openContextMenu(dom.getWindow(terminalContainer), event, terminal, this._instanceMenu, this._contextMenuService);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -387,7 +387,7 @@ export class TerminalTabbedView extends Disposable {
|
|||
}
|
||||
terminalContainer.focus();
|
||||
if (!this._cancelContextMenu) {
|
||||
openContextMenu(event, this._terminalGroupService.activeInstance!, this._instanceMenu, this._contextMenuService);
|
||||
openContextMenu(dom.getWindow(terminalContainer), event, this._terminalGroupService.activeInstance!, this._instanceMenu, this._contextMenuService);
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
@ -412,7 +412,7 @@ export class TerminalTabbedView extends Disposable {
|
|||
selectedInstances.unshift(focusedInstance);
|
||||
}
|
||||
|
||||
openContextMenu(event, selectedInstances, emptyList ? this._tabsListEmptyMenu : this._tabsListMenu, this._contextMenuService, emptyList ? this._getTabActions() : undefined);
|
||||
openContextMenu(dom.getWindow(this._tabContainer), event, selectedInstances, emptyList ? this._tabsListEmptyMenu : this._tabsListMenu, this._contextMenuService, emptyList ? this._getTabActions() : undefined);
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
|
|
@ -66,8 +66,9 @@ export class TerminalFindWidget extends SimpleFindWidget {
|
|||
event.stopPropagation();
|
||||
}));
|
||||
}
|
||||
this._register(dom.addDisposableListener(this.getFindInputDomNode(), 'contextmenu', (event) => {
|
||||
openContextMenu(event, _clipboardService, _contextMenuService);
|
||||
const findInputDomNode = this.getFindInputDomNode();
|
||||
this._register(dom.addDisposableListener(findInputDomNode, 'contextmenu', (event) => {
|
||||
openContextMenu(dom.getWindow(findInputDomNode), event, _clipboardService, _contextMenuService);
|
||||
event.stopPropagation();
|
||||
}));
|
||||
this._register(this._themeService.onDidColorThemeChange(() => {
|
||||
|
|
|
@ -11,8 +11,8 @@ import { localize } from 'vs/nls';
|
|||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
|
||||
export function openContextMenu(event: MouseEvent, clipboardService: IClipboardService, contextMenuService: IContextMenuService): void {
|
||||
const standardEvent = new StandardMouseEvent(event);
|
||||
export function openContextMenu(targetWindow: Window, event: MouseEvent, clipboardService: IClipboardService, contextMenuService: IContextMenuService): void {
|
||||
const standardEvent = new StandardMouseEvent(targetWindow, event);
|
||||
|
||||
// Actions from workbench/browser/actions/textInputActions
|
||||
const actions: IAction[] = [];
|
||||
|
|
|
@ -11,7 +11,6 @@ export const allApiProposals = Object.freeze({
|
|||
authSession: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authSession.d.ts',
|
||||
canonicalUriProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.canonicalUriProvider.d.ts',
|
||||
chat: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chat.d.ts',
|
||||
chatAgents: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatAgents.d.ts',
|
||||
chatAgents2: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatAgents2.d.ts',
|
||||
chatAgents2Additions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts',
|
||||
chatProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatProvider.d.ts',
|
||||
|
|
39
src/vscode-dts/vscode.proposed.chatAgents.d.ts
vendored
39
src/vscode-dts/vscode.proposed.chatAgents.d.ts
vendored
|
@ -1,39 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'vscode' {
|
||||
|
||||
export interface ChatAgentContext {
|
||||
history: ChatMessage[];
|
||||
}
|
||||
|
||||
export interface ChatAgentResponse {
|
||||
message: MarkdownString | InteractiveProgressFileTree;
|
||||
}
|
||||
|
||||
export interface ChatAgentResult {
|
||||
followUp?: InteractiveSessionFollowup[];
|
||||
}
|
||||
|
||||
export interface ChatAgentCommand {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface ChatAgentMetadata {
|
||||
description: string;
|
||||
fullName?: string;
|
||||
icon?: Uri;
|
||||
subCommands: ChatAgentCommand[];
|
||||
}
|
||||
|
||||
export interface ChatAgent {
|
||||
(prompt: ChatMessage, context: ChatAgentContext, progress: Progress<ChatAgentResponse>, token: CancellationToken): Thenable<ChatAgentResult | void>;
|
||||
}
|
||||
|
||||
export namespace chat {
|
||||
export function registerAgent(id: string, agent: ChatAgent, metadata: ChatAgentMetadata): Disposable;
|
||||
}
|
||||
}
|
10
src/vscode-dts/vscode.proposed.chatAgents2.d.ts
vendored
10
src/vscode-dts/vscode.proposed.chatAgents2.d.ts
vendored
|
@ -91,6 +91,11 @@ declare module 'vscode' {
|
|||
*/
|
||||
readonly description: string;
|
||||
|
||||
/**
|
||||
* When the user clicks this slash command in `/help`, this text will be submitted to this slash command
|
||||
*/
|
||||
readonly sampleRequest?: string;
|
||||
|
||||
/**
|
||||
* Whether executing the command puts the
|
||||
* chat into a persistent mode, where the
|
||||
|
@ -204,6 +209,11 @@ declare module 'vscode' {
|
|||
*/
|
||||
followupProvider?: FollowupProvider;
|
||||
|
||||
/**
|
||||
* When the user clicks this agent in `/help`, this text will be submitted to this slash command
|
||||
*/
|
||||
sampleRequest?: string;
|
||||
|
||||
/**
|
||||
* An event that fires whenever feedback for a result is received, e.g. when a user up- or down-votes
|
||||
* a result.
|
||||
|
|
|
@ -16,5 +16,15 @@ declare module 'vscode' {
|
|||
* TODO@API name
|
||||
*/
|
||||
isSecondary?: boolean;
|
||||
|
||||
/**
|
||||
* A string that will be added before the listing of chat agents in `/help`.
|
||||
*/
|
||||
helpTextPrefix?: string | MarkdownString;
|
||||
|
||||
/**
|
||||
* A string that will be appended after the listing of chat agents in `/help`.
|
||||
*/
|
||||
helpTextPostfix?: string | MarkdownString;
|
||||
}
|
||||
}
|
||||
|
|
106
src/vscode-dts/vscode.proposed.interactive.d.ts
vendored
106
src/vscode-dts/vscode.proposed.interactive.d.ts
vendored
|
@ -85,9 +85,6 @@ declare module 'vscode' {
|
|||
handleInteractiveEditorResponseFeedback?(session: S, response: R, kind: InteractiveEditorResponseFeedbackKind): void;
|
||||
}
|
||||
|
||||
|
||||
export interface InteractiveSessionState { }
|
||||
|
||||
export interface InteractiveSessionParticipantInformation {
|
||||
name: string;
|
||||
|
||||
|
@ -101,81 +98,8 @@ declare module 'vscode' {
|
|||
requester: InteractiveSessionParticipantInformation;
|
||||
responder: InteractiveSessionParticipantInformation;
|
||||
inputPlaceholder?: string;
|
||||
|
||||
saveState?(): InteractiveSessionState;
|
||||
}
|
||||
|
||||
export interface InteractiveSessionRequestArgs {
|
||||
command: string;
|
||||
args: any;
|
||||
}
|
||||
|
||||
export interface InteractiveRequest {
|
||||
session: InteractiveSession;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface InteractiveResponseErrorDetails {
|
||||
message: string;
|
||||
responseIsIncomplete?: boolean;
|
||||
responseIsFiltered?: boolean;
|
||||
}
|
||||
|
||||
export interface InteractiveResponseForProgress {
|
||||
errorDetails?: InteractiveResponseErrorDetails;
|
||||
}
|
||||
|
||||
export interface InteractiveContentReference {
|
||||
reference: Uri | Location;
|
||||
}
|
||||
|
||||
export interface InteractiveInlineContentReference {
|
||||
inlineReference: Uri | Location;
|
||||
title?: string; // eg symbol name
|
||||
}
|
||||
|
||||
export interface InteractiveProgressContent {
|
||||
content: string | MarkdownString;
|
||||
}
|
||||
|
||||
export interface InteractiveProgressId {
|
||||
responseId: string;
|
||||
}
|
||||
|
||||
export interface InteractiveProgressTask {
|
||||
placeholder: string;
|
||||
resolvedContent: Thenable<InteractiveProgressContent | InteractiveProgressFileTree>;
|
||||
}
|
||||
|
||||
export interface FileTreeData {
|
||||
label: string;
|
||||
uri: Uri;
|
||||
children?: FileTreeData[];
|
||||
}
|
||||
|
||||
export interface InteractiveProgressFileTree {
|
||||
treeData: FileTreeData;
|
||||
}
|
||||
|
||||
export interface DocumentContext {
|
||||
uri: Uri;
|
||||
version: number;
|
||||
ranges: Range[];
|
||||
}
|
||||
|
||||
export interface InteractiveProgressUsedContext {
|
||||
documents: DocumentContext[];
|
||||
}
|
||||
|
||||
export type InteractiveProgress =
|
||||
| InteractiveProgressContent
|
||||
| InteractiveProgressId
|
||||
| InteractiveProgressTask
|
||||
| InteractiveProgressFileTree
|
||||
| InteractiveProgressUsedContext
|
||||
| InteractiveContentReference
|
||||
| InteractiveInlineContentReference;
|
||||
|
||||
export interface InteractiveResponseCommand {
|
||||
commandId: string;
|
||||
args?: any[];
|
||||
|
@ -183,40 +107,18 @@ declare module 'vscode' {
|
|||
when?: string;
|
||||
}
|
||||
|
||||
export interface InteractiveSessionSlashCommand {
|
||||
command: string;
|
||||
kind: CompletionItemKind;
|
||||
detail?: string;
|
||||
shouldRepopulate?: boolean;
|
||||
followupPlaceholder?: string;
|
||||
executeImmediately?: boolean;
|
||||
yieldTo?: ReadonlyArray<{ readonly command: string }>;
|
||||
}
|
||||
|
||||
export interface InteractiveSessionReplyFollowup {
|
||||
message: string;
|
||||
tooltip?: string;
|
||||
title?: string;
|
||||
|
||||
// Extensions can put any serializable data here, such as an ID/version
|
||||
metadata?: any;
|
||||
}
|
||||
|
||||
export type InteractiveSessionFollowup = InteractiveSessionReplyFollowup | InteractiveResponseCommand;
|
||||
|
||||
export type InteractiveWelcomeMessageContent = string | MarkdownString | InteractiveSessionReplyFollowup[];
|
||||
|
||||
export interface InteractiveSessionProvider<S extends InteractiveSession = InteractiveSession> {
|
||||
provideWelcomeMessage?(token: CancellationToken): ProviderResult<InteractiveWelcomeMessageContent[]>;
|
||||
provideSampleQuestions?(token: CancellationToken): ProviderResult<InteractiveSessionReplyFollowup[]>;
|
||||
provideFollowups?(session: S, token: CancellationToken): ProviderResult<(string | InteractiveSessionFollowup)[]>;
|
||||
provideSlashCommands?(session: S, token: CancellationToken): ProviderResult<InteractiveSessionSlashCommand[]>;
|
||||
|
||||
prepareSession(initialState: InteractiveSessionState | undefined, token: CancellationToken): ProviderResult<S>;
|
||||
provideResponseWithProgress(request: InteractiveRequest, progress: Progress<InteractiveProgress>, token: CancellationToken): ProviderResult<InteractiveResponseForProgress>;
|
||||
|
||||
// eslint-disable-next-line local/vscode-dts-provider-naming
|
||||
removeRequest(session: S, requestId: string): void;
|
||||
prepareSession(token: CancellationToken): ProviderResult<S>;
|
||||
}
|
||||
|
||||
export interface InteractiveSessionDynamicRequest {
|
||||
|
@ -224,12 +126,6 @@ declare module 'vscode' {
|
|||
* The message that will be displayed in the UI
|
||||
*/
|
||||
message: string;
|
||||
|
||||
/**
|
||||
* Any extra metadata/context that will go to the provider.
|
||||
* NOTE not actually used yet.
|
||||
*/
|
||||
metadata?: any;
|
||||
}
|
||||
|
||||
export namespace interactive {
|
||||
|
|
|
@ -13,8 +13,6 @@ declare module 'vscode' {
|
|||
export interface InteractiveSessionVoteAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'vote';
|
||||
// sessionId: string;
|
||||
responseId: string;
|
||||
direction: InteractiveSessionVoteDirection;
|
||||
}
|
||||
|
||||
|
@ -27,8 +25,6 @@ declare module 'vscode' {
|
|||
export interface InteractiveSessionCopyAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'copy';
|
||||
// sessionId: string;
|
||||
responseId: string;
|
||||
codeBlockIndex: number;
|
||||
copyType: InteractiveSessionCopyKind;
|
||||
copiedCharacters: number;
|
||||
|
@ -39,8 +35,6 @@ declare module 'vscode' {
|
|||
export interface InteractiveSessionInsertAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'insert';
|
||||
// sessionId: string;
|
||||
responseId: string;
|
||||
codeBlockIndex: number;
|
||||
totalCharacters: number;
|
||||
newFile?: boolean;
|
||||
|
@ -49,8 +43,6 @@ declare module 'vscode' {
|
|||
export interface InteractiveSessionTerminalAction {
|
||||
// eslint-disable-next-line local/vscode-dts-string-type-literals
|
||||
kind: 'runInTerminal';
|
||||
// sessionId: string;
|
||||
responseId: string;
|
||||
codeBlockIndex: number;
|
||||
languageId?: string;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue