Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support getting various document ranges from InteractiveSessionProviders #193656

Merged
merged 8 commits into from
Sep 23, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,12 @@ suite('mapped edits provider', () => {
const r1 = vscode.chat.registerMappedEditsProvider(tsDocFilter, {
provideMappedEdits: (_doc: vscode.TextDocument, codeBlocks: string[], context: vscode.MappedEditsContext, _token: vscode.CancellationToken) => {

assert(context.selections.length === 1);
assert(context.related.length === 1);
assert('uri' in context.related[0] && 'range' in context.related[0]);
assert((context as any).selections.length === 1); // context.selections is for backward compat
assert(context.documents.length === 1);

const edit = new vscode.WorkspaceEdit();
const text = codeBlocks.join('\n//----\n');
edit.replace(uri, context.selections[0], text);
edit.replace(uri, context.documents[0][0].ranges[0], text);
return edit;
}
});
Expand All @@ -37,12 +36,16 @@ suite('mapped edits provider', () => {
`function foo() {\n\treturn 1;\n}`,
],
{
selections: [new vscode.Selection(0, 0, 1, 0)],
related: [
{
uri,
range: new vscode.Range(new vscode.Position(0, 0), new vscode.Position(1, 0))
}
documents: [
[
{
uri,
version: 1,
ranges: [
new vscode.Range(new vscode.Position(0, 0), new vscode.Position(1, 0))
]
}
]
]
}
);
Expand All @@ -60,13 +63,9 @@ suite('mapped edits provider', () => {
const r1 = vscode.chat.registerMappedEditsProvider(tsDocFilter, {
provideMappedEdits: (_doc: vscode.TextDocument, codeBlocks: string[], context: vscode.MappedEditsContext, _token: vscode.CancellationToken) => {

assert(context.selections.length === 1);
assert(context.related.length === 1);
assert('uri' in context.related[0] && 'range' in context.related[0]);

const edit = new vscode.WorkspaceEdit();
const text = codeBlocks.join('\n//----\n');
edit.replace(uri, context.selections[0], text);
edit.replace(uri, context.documents[0][0].ranges[0], text);
return edit;
}
});
Expand All @@ -80,12 +79,16 @@ suite('mapped edits provider', () => {
`function foo() {\n\treturn 1;\n}`,
],
{
selections: [new vscode.Selection(0, 0, 1, 0)],
related: [
{
uri,
range: new vscode.Range(new vscode.Position(0, 0), new vscode.Position(1, 0))
}
documents: [
[
{
uri,
version: 1,
ranges: [
new vscode.Range(new vscode.Position(0, 0), new vscode.Position(1, 0))
]
}
]
]
}
);
Expand Down
11 changes: 6 additions & 5 deletions src/vs/editor/common/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { EditOperation, ISingleEditOperation } from 'vs/editor/common/core/editOperation';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { Selection } from 'vs/editor/common/core/selection';
import { LanguageId } from 'vs/editor/common/encodedTokenAttributes';
import * as model from 'vs/editor/common/model';
import { TokenizationRegistry as TokenizationRegistryImpl } from 'vs/editor/common/tokenizationRegistry';
Expand Down Expand Up @@ -2043,14 +2043,15 @@ export interface DocumentOnDropEditProvider {
provideDocumentOnDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): ProviderResult<DocumentOnDropEdit>;
}

export interface RelatedContextItem {
export interface DocumentContextItem {
readonly uri: URI;
readonly range: IRange;
readonly version: number;
readonly ranges: IRange[];
}

export interface MappedEditsContext {
selections: ISelection[];
related: RelatedContextItem[];
/** The outer array is sorted by priority - from highest to lowest. The inner arrays contain elements of the same priority. */
documents: DocumentContextItem[][];
}

export interface MappedEditsProvider {
Expand Down
9 changes: 5 additions & 4 deletions src/vs/monaco.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7713,14 +7713,15 @@ declare namespace monaco.languages {
provideDocumentRangeSemanticTokens(model: editor.ITextModel, range: Range, token: CancellationToken): ProviderResult<SemanticTokens>;
}

export interface RelatedContextItem {
export interface DocumentContextItem {
readonly uri: Uri;
readonly range: IRange;
readonly version: number;
readonly ranges: IRange[];
}

export interface MappedEditsContext {
selections: ISelection[];
related: RelatedContextItem[];
/** The outer array is sorted by priority - from highest to lowest. The inner arrays contain elements of the same priority. */
documents: DocumentContextItem[][];
}

export interface MappedEditsProvider {
Expand Down
12 changes: 12 additions & 0 deletions src/vs/workbench/api/browser/mainThreadChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,18 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape {
return;
}

if ('documents' in progress) {
const usedContext = {
documents: progress.documents.map(({ uri, version, ranges }) => ({
uri: URI.revive(uri),
version,
ranges,
})),
};
this._activeRequestProgressCallbacks.get(id)?.(usedContext); // FIXME@ulugbekna: is this a correct thing to do?
return;
}

this._activeRequestProgressCallbacks.get(id)?.(progress);
}

Expand Down
21 changes: 16 additions & 5 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,14 +365,14 @@ export interface IShareableItemDto {
selection?: IRange;
}

export interface IRelatedContextItemDto {
export interface IDocumentContextItemDto {
readonly uri: UriComponents;
readonly range: IRange;
readonly version: number;
readonly ranges: IRange[];
}

export interface IMappedEditsContextDto {
selections: ISelection[];
related: IRelatedContextItemDto[];
documents: IDocumentContextItemDto[][];
}

export interface ISignatureHelpProviderMetadataDto {
Expand Down Expand Up @@ -1223,7 +1223,18 @@ export interface IChatResponseProgressFileTreeData {
children?: IChatResponseProgressFileTreeData[];
}

export type IChatResponseProgressDto = { content: string | IMarkdownString } | { requestId: string } | { placeholder: string } | { treeData: IChatResponseProgressFileTreeData };
export type IDocumentContextDto = {
uri: UriComponents;
version: number;
ranges: IRange[];
};

export type IChatResponseProgressDto =
| { content: string | IMarkdownString }
| { requestId: string }
| { placeholder: string }
| { treeData: IChatResponseProgressFileTreeData }
| { documents: IDocumentContextDto[] };

export interface MainThreadChatShape extends IDisposable {
$registerChatProvider(handle: number, id: string): Promise<void>;
Expand Down
8 changes: 8 additions & 0 deletions src/vs/workbench/api/common/extHostChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,14 @@ export class ExtHostChat implements ExtHostChatShape {
this._proxy.$acceptResponseProgress(handle, sessionId, {
content: typeof progress.content === 'string' ? progress.content : typeConvert.MarkdownString.from(progress.content)
});
} else if ('documents' in progress) {
this._proxy.$acceptResponseProgress(handle, sessionId, {
documents: progress.documents.map(d => ({
uri: d.uri,
version: d.version,
ranges: d.ranges.map(r => typeConvert.Range.from(r))
}))
});
} else {
this._proxy.$acceptResponseProgress(handle, sessionId, progress);
}
Expand Down
8 changes: 4 additions & 4 deletions src/vs/workbench/api/common/extHostCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {

const internalArgs = apiCommand.args.map((arg, i) => {
if (!arg.validate(apiArgs[i])) {
throw new Error(`Invalid argument '${arg.name}' when running '${apiCommand.id}', received: ${apiArgs[i]}`);
throw new Error(`Invalid argument '${arg.name}' when running '${apiCommand.id}', received: ${typeof apiArgs[i] === 'object' ? JSON.stringify(apiArgs[i], null, '\t') : apiArgs[i]} `);
}
return arg.convert(apiArgs[i]);
});
Expand Down Expand Up @@ -238,7 +238,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
try {
validateConstraint(args[i], description.args[i].constraint);
} catch (err) {
throw new Error(`Running the contributed command: '${id}' failed. Illegal argument '${description.args[i].name}' - ${description.args[i].description}`);
throw new Error(`Running the contributed command: '${id}' failed.Illegal argument '${description.args[i].name}' - ${description.args[i].description} `);
}
}
}
Expand Down Expand Up @@ -342,7 +342,7 @@ export const IExtHostCommands = createDecorator<IExtHostCommands>('IExtHostComma

export class CommandsConverter implements extHostTypeConverter.Command.ICommandsConverter {

readonly delegatingCommandId: string = `__vsc${Date.now().toString(36)}`;
readonly delegatingCommandId: string = `__vsc${Date.now().toString(36)} `;
private readonly _cache = new Map<string, vscode.Command>();
private _cachIdPool = 0;

Expand Down Expand Up @@ -387,7 +387,7 @@ export class CommandsConverter implements extHostTypeConverter.Command.ICommands
// we have a contributed command with arguments. that
// means we don't want to send the arguments around

const id = `${command.command}/${++this._cachIdPool}`;
const id = `${command.command} /${++this._cachIdPool}`;
this._cache.set(id, command);
disposables.add(toDisposable(() => {
this._cache.delete(id);
Expand Down
14 changes: 12 additions & 2 deletions src/vs/workbench/api/common/extHostLanguageFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1830,9 +1830,19 @@ class MappedEditsAdapter {
const uri = URI.revive(resource);
const doc = this._documents.getDocument(uri);

const usedContext = context.documents.map((docSubArray) =>
docSubArray.map((r) => {
return {
uri: URI.revive(r.uri),
version: r.version,
ranges: r.ranges.map((range) => typeConvert.Range.to(range)),
};
})
);

const ctx = {
selections: context.selections.map(s => typeConvert.Selection.to(s)),
related: context.related.map(r => ({ uri: URI.revive(r.uri), range: typeConvert.Range.to(r.range) })),
documents: usedContext,
selections: usedContext[0]?.[0]?.ranges ?? [] // @ulugbekna: this is a hack for backward compatibility
};

const mappedEdits = await this._provider.provideMappedEdits(doc, codeBlocks, ctx, token);
Expand Down
34 changes: 21 additions & 13 deletions src/vs/workbench/api/common/extHostTypeConverters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1567,23 +1567,31 @@ export namespace LanguageSelector {
export namespace MappedEditsContext {

export function is(v: unknown): v is vscode.MappedEditsContext {
return (!!v &&
typeof v === 'object' &&
'selections' in v &&
Array.isArray(v.selections) &&
v.selections.every(s => s instanceof types.Selection) &&
'related' in v &&
Array.isArray(v.related) &&
v.related.every(e => e && typeof e === 'object' && URI.isUri(e.uri) && e.range instanceof types.Range));
return (
!!v && typeof v === 'object' &&
'documents' in v &&
Array.isArray(v.documents) &&
v.documents.every(subArr =>
Array.isArray(subArr) &&
subArr.every(docRef =>
docRef && typeof docRef === 'object' &&
'uri' in docRef && URI.isUri(docRef.uri) &&
'version' in docRef && typeof docRef.version === 'number' &&
'ranges' in docRef && Array.isArray(docRef.ranges) && docRef.ranges.every((r: unknown) => r instanceof types.Range)
)
)
);
}

export function from(extContext: vscode.MappedEditsContext): languages.MappedEditsContext {
return {
selections: extContext.selections.map(s => Selection.from(s)),
related: extContext.related.map(r => ({
uri: URI.from(r.uri),
range: Range.from(r.range)
}))
documents: extContext.documents.map((subArray) =>
subArray.map((r) => ({
uri: URI.from(r.uri),
version: r.version,
ranges: r.ranges.map((r) => Range.from(r)),
}))
),
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { Range } from 'vs/editor/common/core/range';
import { RelatedContextItem, WorkspaceEdit } from 'vs/editor/common/languages';
import { DocumentContextItem, WorkspaceEdit } from 'vs/editor/common/languages';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { ITextModel } from 'vs/editor/common/model';
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
Expand Down Expand Up @@ -260,17 +260,37 @@ export function registerChatCodeBlockActions() {
if (mappedEditsProviders.length > 0) {
const mostRelevantProvider = mappedEditsProviders[0];

const selections = codeEditor.getSelections() ?? [];
const mappedEditsContext = {
selections,
related: [] as RelatedContextItem[], // TODO@ulugbekna: we do have not yet decided what to populate this with
};
// 0th sub-array - editor selections array if there are any selections
// 1st sub-array - array with documents used to get the chat reply
const docRefs: DocumentContextItem[][] = [];

if (codeEditor.hasModel()) {
const model = codeEditor.getModel();
const currentDocUri = model.uri;
const currentDocVersion = model.getVersionId();
const selections = codeEditor.getSelections();
if (selections.length > 0) {
docRefs.push([
{
uri: currentDocUri,
version: currentDocVersion,
ranges: selections,
}
]);
}
}

const usedDocuments = chatCodeBlockActionContext.element.response.usedContext?.documents;
if (usedDocuments) {
docRefs.push(usedDocuments);
}

const cancellationTokenSource = new CancellationTokenSource();

workspaceEdit = await mostRelevantProvider.provideMappedEdits(
activeModel,
[chatCodeBlockActionContext.code],
mappedEditsContext,
{ documents: docRefs },
cancellationTokenSource.token);
}

Expand Down
Loading
Loading