Skip to content

Commit 086501e

Browse files
authored
move prompt file parser usages to IPromptsService (microsoft#251298)
1 parent 0ab638e commit 086501e

File tree

13 files changed

+95
-188
lines changed

13 files changed

+95
-188
lines changed

src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { IRange } from '../../../../editor/common/core/range.js';
1010
import { Disposable } from '../../../../base/common/lifecycle.js';
1111
import { IChatRequestFileEntry, IChatRequestVariableEntry, isPromptFileVariableEntry, toPromptFileVariableEntry } from '../common/chatVariableEntries.js';
1212
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
13-
import { ChatPromptAttachmentsCollection } from './chatAttachmentModel/chatPromptAttachmentsCollection.js';
13+
import { ChatPromptAttachmentsCollection } from './chatAttachmentModel/chatPromptAttachments.js';
1414
import { IFileService } from '../../../../platform/files/common/files.js';
1515
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
1616
import { ISharedWebContentExtractorService } from '../../../../platform/webContentExtractor/common/webContentExtractor.js';

src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentModel.ts

Lines changed: 0 additions & 117 deletions
This file was deleted.
Lines changed: 35 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import { CancelablePromise, createCancelablePromise } from '../../../../../base/common/async.js';
67
import { Emitter } from '../../../../../base/common/event.js';
78
import { Disposable } from '../../../../../base/common/lifecycle.js';
89
import { ResourceMap } from '../../../../../base/common/map.js';
10+
import { URI } from '../../../../../base/common/uri.js';
911
import { ILanguageService } from '../../../../../editor/common/languages/language.js';
1012
import { IModelService } from '../../../../../editor/common/services/model.js';
11-
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
1213
import { IChatRequestVariableEntry, IPromptFileVariableEntry, toPromptFileVariableEntry } from '../../common/chatVariableEntries.js';
13-
14-
import { ChatPromptAttachmentModel } from './chatPromptAttachmentModel.js';
14+
import { IPromptParserResult, IPromptsService } from '../../common/promptSyntax/service/promptsService.js';
1515

1616
/**
1717
* Model for a collection of prompt instruction attachments.
18-
* See {@linkcode ChatPromptAttachmentModel} for individual attachment.
18+
* Starts
1919
*/
2020
export class ChatPromptAttachmentsCollection extends Disposable {
2121
/**
@@ -24,29 +24,38 @@ export class ChatPromptAttachmentsCollection extends Disposable {
2424
* See {@linkcode onUpdate}.
2525
*/
2626
protected _onUpdate = this._register(new Emitter<IPromptFileVariableEntry>());
27+
2728
/**
2829
* Subscribe to the `onUpdate` event.
2930
*/
3031
public onUpdate = this._onUpdate.event;
3132

32-
3333
/**
3434
* List of all prompt instruction attachments.
3535
*/
36-
private _attachments: ResourceMap<ChatPromptAttachmentModel> = new ResourceMap<ChatPromptAttachmentModel>();
36+
private _attachments = new ResourceMap<CancelablePromise<IPromptParserResult>>();
37+
38+
39+
constructor(
40+
@ILanguageService private readonly languageService: ILanguageService,
41+
@IModelService private readonly modelService: IModelService,
42+
@IPromptsService private readonly promptsService: IPromptsService,
43+
) {
44+
super();
45+
}
3746

3847
/**
3948
* Check if any of the attachments is a prompt file.
4049
*/
4150
public hasPromptFiles(promptFileLanguageId: string): boolean {
42-
const hasLanguage = ({ uri }: ChatPromptAttachmentModel) => {
51+
const hasLanguage = (uri: URI) => {
4352
const model = this.modelService.getModel(uri);
4453
const languageId = model ? model.getLanguageId() : this.languageService.guessLanguageIdByFilepathOrFirstLine(uri);
4554
return languageId === promptFileLanguageId;
4655
};
4756

48-
for (const child of this._attachments.values()) {
49-
if (hasLanguage(child)) {
57+
for (const uri of this._attachments.keys()) {
58+
if (hasLanguage(uri)) {
5059
return true;
5160
}
5261
}
@@ -58,51 +67,25 @@ export class ChatPromptAttachmentsCollection extends Disposable {
5867
* nested child references of each attachment explicitly attached by user.
5968
*/
6069
public async getAttachments(): Promise<readonly IChatRequestVariableEntry[]> {
61-
await this.allSettled();
62-
6370
const result = [];
6471
const attachments = [...this._attachments.values()];
6572

66-
for (const attachment of attachments) {
67-
const { reference } = attachment;
73+
for (const parseResultPromise of attachments) {
74+
const parseResult = await parseResultPromise;
6875

6976
// the usual URIs list of prompt instructions is `bottom-up`, therefore
7077
// we do the same here - first add all child references of the model
71-
result.push(
72-
...reference.allValidReferences.map((link) => {
73-
return toPromptFileVariableEntry(link.uri, false);
74-
}),
75-
);
78+
for (const uri of parseResult.allValidReferences) {
79+
result.push(toPromptFileVariableEntry(uri, false));
80+
}
7681

7782
// then add the root reference of the model itself
78-
result.push(toPromptFileVariableEntry(reference.uri, true));
83+
result.push(toPromptFileVariableEntry(parseResult.uri, true));
7984
}
8085

8186
return result;
8287
}
8388

84-
/**
85-
* Promise that resolves when parsing of all attached prompt instruction
86-
* files completes, including parsing of all its possible child references.
87-
*/
88-
async allSettled(): Promise<void> {
89-
const attachments = [...this._attachments.values()];
90-
91-
await Promise.allSettled(
92-
attachments.map((attachment) => {
93-
return attachment.allSettled;
94-
}),
95-
);
96-
}
97-
98-
constructor(
99-
@IInstantiationService private readonly instantiationService: IInstantiationService,
100-
@ILanguageService private readonly languageService: ILanguageService,
101-
@IModelService private readonly modelService: IModelService,
102-
) {
103-
super();
104-
}
105-
10689
/**
10790
* Add prompt instruction attachment instances
10891
*/
@@ -115,8 +98,15 @@ export class ChatPromptAttachmentsCollection extends Disposable {
11598
continue;
11699
}
117100

118-
const instruction = this.instantiationService.createInstance(ChatPromptAttachmentModel, uri, () => this._onUpdate.fire(entry));
119-
this._attachments.set(uri, instruction);
101+
const parseResult = createCancelablePromise(token => this.promptsService.parse(uri, token));
102+
parseResult.then(() => {
103+
this._onUpdate.fire(entry);
104+
}).catch((error) => {
105+
// if parsing fails, we still create an attachment model
106+
// to allow the user to see the error and fix it
107+
console.error(`Failed to parse prompt file ${uri.toString()}:`, error);
108+
});
109+
this._attachments.set(uri, parseResult);
120110
}
121111
}
122112

@@ -129,7 +119,7 @@ export class ChatPromptAttachmentsCollection extends Disposable {
129119
const attachment = this._attachments.get(uri);
130120
if (attachment) {
131121
this._attachments.delete(uri);
132-
attachment.dispose();
122+
attachment.cancel();
133123
}
134124

135125
return this;
@@ -140,7 +130,7 @@ export class ChatPromptAttachmentsCollection extends Disposable {
140130
*/
141131
public clear(): this {
142132
for (const attachment of this._attachments.values()) {
143-
attachment.dispose();
133+
attachment.cancel();
144134
}
145135
this._attachments.clear();
146136

src/vs/workbench/contrib/chat/browser/promptSyntax/promptToolsCodeLensProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class PromptToolsCodeLensProvider extends Disposable implements CodeLensProvider
4949
const parser = this.promptsService.getSyntaxParserFor(model);
5050

5151
const { header } = await parser
52-
.start()
52+
.start(token)
5353
.settled();
5454

5555
if ((header === undefined) || token.isCancellationRequested) {

src/vs/workbench/contrib/chat/common/chatModes.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import { CancellationToken } from '../../../../base/common/cancellation.js';
67
import { Emitter, Event } from '../../../../base/common/event.js';
78
import { Disposable } from '../../../../base/common/lifecycle.js';
89
import { localize } from '../../../../nls.js';
@@ -49,7 +50,7 @@ export class ChatModeService extends Disposable implements IChatModeService {
4950

5051
private async refreshCustomPromptModes(fireChangeEvent?: boolean): Promise<void> {
5152
try {
52-
const modes = await this.promptsService.getCustomChatModes();
53+
const modes = await this.promptsService.getCustomChatModes(CancellationToken.None);
5354
this.latestCustomPromptModes = modes.map(customMode => new CustomChatMode(customMode));
5455
this.hasCustomModes.set(modes.length > 0);
5556
if (fireChangeEvent) {

src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/promptContentsProviderBase.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,14 @@ export abstract class PromptContentsProviderBase<
179179
/**
180180
* Start producing the prompt contents data.
181181
*/
182-
public start(): this {
182+
public start(token?: CancellationToken): this {
183183
assert(
184184
!this.isDisposed,
185185
'Cannot start contents provider that was already disposed.',
186186
);
187187

188188
// `'full'` means "everything has changed"
189-
this.onContentsChanged('full');
189+
this.onContentsChanged('full', token);
190190

191191
// subscribe to the change event emitted by a child class
192192
this._register(this.onChangeEmitter.event(this.onContentsChanged, this));

src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ResolveError } from '../../promptFileReferenceErrors.js';
99
import { IDisposable } from '../../../../../../base/common/lifecycle.js';
1010
import { VSBufferReadableStream } from '../../../../../../base/common/buffer.js';
1111
import { PromptsType } from '../promptTypes.js';
12+
import { CancellationToken } from '../../../../../../base/common/cancellation.js';
1213

1314
/**
1415
* Interface for a prompt contents provider. Prompt contents providers are
@@ -56,7 +57,7 @@ export interface IPromptContentsProvider extends IDisposable {
5657
/**
5758
* Start the contents provider to produce the underlying contents.
5859
*/
59-
start(): this;
60+
start(token?: CancellationToken): this;
6061

6162
/**
6263
* Create a new instance of prompt contents provider.

src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptLinkProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class PromptLinkProvider implements LinkProvider {
4242
// start the parser in case it was not started yet,
4343
// and wait for it to settle to a final result
4444
const { references } = await parser
45-
.start()
45+
.start(token)
4646
.settled();
4747

4848
// validate that the cancellation was not yet requested

src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptPathAutocompletion.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export class PromptPathAutocompletion extends Disposable implements CompletionIt
138138
// start the parser in case it was not started yet,
139139
// and wait for it to settle to a final result
140140
const { references } = await parser
141-
.start()
141+
.start(token)
142142
.settled();
143143

144144
// validate that the cancellation was not yet requested

src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { isPromptOrInstructionsFile } from '../config/promptFileLocations.js';
3939
import { FrontMatterHeader } from '../codecs/base/markdownExtensionsCodec/tokens/frontMatterHeader.js';
4040
import { OpenFailed, NotPromptFile, RecursiveReference, FolderReference, ResolveError } from '../../promptFileReferenceErrors.js';
4141
import { type IPromptContentsProviderOptions, DEFAULT_OPTIONS as CONTENTS_PROVIDER_DEFAULT_OPTIONS } from '../contentProviders/promptContentsProviderBase.js';
42+
import { CancellationToken } from '../../../../../../base/common/cancellation.js';
4243

4344
/**
4445
* Options of the {@link BasePromptParser} class.
@@ -511,7 +512,7 @@ export class BasePromptParser<TContentsProvider extends IPromptContentsProvider>
511512
/**
512513
* Start the prompt parser.
513514
*/
514-
public start(): this {
515+
public start(token?: CancellationToken): this {
515516
// if already started, nothing to do
516517
if (this.started) {
517518
return this;
@@ -525,7 +526,7 @@ export class BasePromptParser<TContentsProvider extends IPromptContentsProvider>
525526
return this;
526527
}
527528

528-
this.promptContentsProvider.start();
529+
this.promptContentsProvider.start(token);
529530
return this;
530531
}
531532

0 commit comments

Comments
 (0)