Skip to content

Commit 51aa46f

Browse files
aeschlijoshspicer
andauthored
Constant flickering when typing in mode file (microsoft#253941)
Co-authored-by: Josh Spicer <[email protected]>
1 parent bcf133c commit 51aa46f

File tree

13 files changed

+76
-62
lines changed

13 files changed

+76
-62
lines changed

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,17 @@ export class PromptFileRewriter {
2727
const model = editor.getModel();
2828

2929
const parser = this._promptsService.getSyntaxParserFor(model);
30-
const { header } = await parser.start(token).settled();
31-
32-
if ((header === undefined) || token.isCancellationRequested) {
30+
await parser.start(token).settled();
31+
const { header } = parser;
32+
if (header === undefined) {
3333
return undefined;
3434
}
3535

36+
const completed = await header.settled;
37+
if (!completed || token.isCancellationRequested) {
38+
return;
39+
}
40+
3641
if (('tools' in header.metadataUtility) === false) {
3742
return undefined;
3843
}

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,14 @@ class PromptToolsCodeLensProvider extends Disposable implements CodeLensProvider
4848

4949
const parser = this.promptsService.getSyntaxParserFor(model);
5050

51-
const { header } = await parser
52-
.start(token)
53-
.settled();
51+
await parser.start(token).settled();
52+
const { header } = parser;
53+
if (!header) {
54+
return undefined;
55+
}
5456

55-
if ((header === undefined) || token.isCancellationRequested) {
57+
const completed = await header.settled;
58+
if (!completed || token.isCancellationRequested) {
5659
return undefined;
5760
}
5861

src/vs/workbench/contrib/chat/common/promptSyntax/codecs/base/baseDecoder.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,22 @@ export abstract class BaseDecoder<
6767
* Promise that resolves when the stream has ended, either by
6868
* receiving the `end` event or by a disposal, but not when
6969
* the `error` event is received alone.
70+
* The promise is true if the stream has ended, and false
71+
* if the stream has been disposed without ending.
7072
*/
71-
private settledPromise = new DeferredPromise<void>();
73+
private settledPromise = new DeferredPromise<boolean>();
7274

7375
/**
7476
* Promise that resolves when the stream has ended, either by
7577
* receiving the `end` event or by a disposal, but not when
7678
* the `error` event is received alone.
79+
* The promise is true if the stream has ended, and false
80+
* if the stream has been disposed without ending.
7781
*
7882
* @throws If the stream was not yet started to prevent this
7983
* promise to block the consumer calls indefinitely.
8084
*/
81-
public get settled(): Promise<void> {
85+
public get settled(): Promise<boolean> {
8286
// if the stream has not started yet, the promise might
8387
// block the consumer calls indefinitely if they forget
8488
// to call the `start()` method, or if the call happens
@@ -296,7 +300,7 @@ export abstract class BaseDecoder<
296300

297301
this._ended = true;
298302
this._onEnd.fire();
299-
this.settledPromise.complete();
303+
this.settledPromise.complete(this._ended);
300304
}
301305

302306
/**
@@ -347,7 +351,7 @@ export abstract class BaseDecoder<
347351
}
348352

349353
public override dispose(): void {
350-
this.settledPromise.complete();
354+
this.settledPromise.complete(this.ended);
351355

352356
this._listeners.clearAndDisposeAll();
353357
this.stream.destroy();

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ export class PromptHeaderAutocompletion extends Disposable implements Completion
7171
return undefined;
7272
}
7373

74-
await header.settled;
74+
const completed = await header.settled;
75+
if (!completed || token.isCancellationRequested) {
76+
return undefined;
77+
}
7578

7679
const fullHeaderRange = parser.header.range;
7780
const headerRange = new Range(fullHeaderRange.startLineNumber + 1, 0, fullHeaderRange.endLineNumber - 1, model.getLineMaxColumn(fullHeaderRange.endLineNumber - 1),);

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,20 @@ class PromptHeaderDiagnosticsProvider extends ProviderInstanceBase {
5353
_error: Error | undefined,
5454
token: CancellationToken,
5555
): Promise<void> {
56-
// clean up all previously added markers
57-
this.markerService.remove(MARKERS_OWNER_ID, [this.model.uri]);
5856

5957
const { header } = this.parser;
6058
if (header === undefined) {
59+
this.markerService.remove(MARKERS_OWNER_ID, [this.model.uri]);
6160
return;
6261
}
6362

6463
// header parsing process is separate from the prompt parsing one, hence
6564
// apply markers only after the header is settled and so has diagnostics
66-
await header.settled;
67-
// by the time the promise finishes, the token might have been cancelled
68-
// already due to a new 'onSettle' event, hence don't apply outdated markers
69-
if (token.isCancellationRequested) {
65+
const completed = await header.settled;
66+
if (!completed || token.isCancellationRequested) {
7067
return;
7168
}
69+
console.log(`Prompt header diagnostics for ${this.model.uri.toString()}:`);
7270

7371
const markers: IMarkerData[] = [];
7472
for (const diagnostic of header.diagnostics) {
@@ -84,6 +82,12 @@ class PromptHeaderDiagnosticsProvider extends ProviderInstanceBase {
8482

8583
}
8684

85+
if (markers.length === 0) {
86+
console.warn(`No markers for ${this.model.uri.toString()}`);
87+
this.markerService.remove(MARKERS_OWNER_ID, [this.model.uri]);
88+
return;
89+
}
90+
8791
this.markerService.changeOne(
8892
MARKERS_OWNER_ID,
8993
this.model.uri,

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ export class PromptHeaderHoverProvider extends Disposable implements HoverProvid
7070
return undefined;
7171
}
7272

73-
await header.settled;
73+
const completed = await header.settled;
74+
if (!completed || token.isCancellationRequested) {
75+
return undefined;
76+
}
7477

7578
if (header instanceof InstructionsHeader) {
7679
const descriptionRange = header.metadataUtility.description?.range;

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

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class PromptLinkProvider implements LinkProvider {
2626
public async provideLinks(
2727
model: ITextModel,
2828
token: CancellationToken,
29-
): Promise<ILinksList> {
29+
): Promise<ILinksList | undefined> {
3030
assert(
3131
!token.isCancellationRequested,
3232
new CancellationError(),
@@ -40,15 +40,11 @@ export class PromptLinkProvider implements LinkProvider {
4040

4141
// start the parser in case it was not started yet,
4242
// and wait for it to settle to a final result
43-
const { references } = await parser
44-
.start(token)
45-
.settled();
46-
47-
// validate that the cancellation was not yet requested
48-
assert(
49-
!token.isCancellationRequested,
50-
new CancellationError(),
51-
);
43+
const completed = await parser.start(token).settled();
44+
if (!completed || token.isCancellationRequested) {
45+
return undefined;
46+
}
47+
const { references } = parser;
5248

5349
// filter out references that are not valid links
5450
const links: ILink[] = references

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,11 @@ export class PromptPathAutocompletion extends Disposable implements CompletionIt
137137

138138
// start the parser in case it was not started yet,
139139
// and wait for it to settle to a final result
140-
const { references } = await parser
141-
.start(token)
142-
.settled();
143-
144-
// validate that the cancellation was not yet requested
145-
assert(
146-
!token.isCancellationRequested,
147-
new CancellationError(),
148-
);
140+
const completed = await parser.start(token).settled();
141+
if (!completed || token.isCancellationRequested) {
142+
return undefined;
143+
}
144+
const { references } = parser;
149145

150146
const fileReference = findFileReference(references, position);
151147
if (!fileReference) {

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

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ export class BasePromptParser<TContentsProvider extends IPromptContentsProvider>
191191
* block until the latest prompt contents parsing logic is settled
192192
* (e.g., for every `onContentChanged` event of the prompt source).
193193
*/
194-
public async settled(): Promise<this> {
194+
public async settled(): Promise<boolean> {
195195
assert(
196196
this.started,
197197
'Cannot wait on the parser that did not start yet.',
@@ -200,38 +200,31 @@ export class BasePromptParser<TContentsProvider extends IPromptContentsProvider>
200200
await this.firstParseResult.promise;
201201

202202
if (this.errorCondition) {
203-
return this;
203+
return false;
204204
}
205205

206206
// by the time when the `firstParseResult` promise is resolved,
207207
// this object may have been already disposed, hence noop
208208
if (this.isDisposed) {
209-
return this;
209+
return false;
210210
}
211211

212212
assertDefined(
213213
this.stream,
214214
'No stream reference found.',
215215
);
216216

217-
await this.stream.settled;
217+
const completed = await this.stream.settled;
218218

219219
// if prompt header exists, also wait for it to be settled
220220
if (this.promptHeader) {
221-
await this.promptHeader.settled;
221+
const headerCompleted = await this.promptHeader.settled;
222+
if (!headerCompleted) {
223+
return false;
224+
}
222225
}
223226

224-
return this;
225-
}
226-
227-
/**
228-
* Same as {@link settled} but also waits for all possible
229-
* nested child prompt references and their children to be settled.
230-
*/
231-
public async allSettled(): Promise<this> {
232-
await this.settled();
233-
234-
return this;
227+
return completed;
235228
}
236229

237230
constructor(

src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/headerBase.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export abstract class HeaderBase<
249249
* Promise that resolves when parsing process of
250250
* the prompt header completes.
251251
*/
252-
public get settled(): Promise<void> {
252+
public get settled(): Promise<boolean> {
253253
return this.stream.settled;
254254
}
255255

0 commit comments

Comments
 (0)