Skip to content

Commit 5f14f1b

Browse files
authored
Request semantic tokens again if a change event comes in during a request (microsoft#164396)
Fixes microsoft#161573: Request semantic tokens again if a change event comes in during a request
1 parent 8fe6f3e commit 5f14f1b

File tree

2 files changed

+48
-4
lines changed

2 files changed

+48
-4
lines changed

src/vs/editor/common/services/modelService.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,7 @@ export class ModelSemanticColoring extends Disposable {
770770
private _currentDocumentResponse: SemanticTokensResponse | null;
771771
private _currentDocumentRequestCancellationTokenSource: CancellationTokenSource | null;
772772
private _documentProvidersChangeListeners: IDisposable[];
773+
private _providersChangedDuringRequest: boolean;
773774

774775
constructor(
775776
model: ITextModel,
@@ -789,6 +790,7 @@ export class ModelSemanticColoring extends Disposable {
789790
this._currentDocumentResponse = null;
790791
this._currentDocumentRequestCancellationTokenSource = null;
791792
this._documentProvidersChangeListeners = [];
793+
this._providersChangedDuringRequest = false;
792794

793795
this._register(this._model.onDidChangeContent(() => {
794796
if (!this._fetchDocumentSemanticTokens.isScheduled()) {
@@ -814,7 +816,14 @@ export class ModelSemanticColoring extends Disposable {
814816
this._documentProvidersChangeListeners = [];
815817
for (const provider of this._provider.all(model)) {
816818
if (typeof provider.onDidChange === 'function') {
817-
this._documentProvidersChangeListeners.push(provider.onDidChange(() => this._fetchDocumentSemanticTokens.schedule(0)));
819+
this._documentProvidersChangeListeners.push(provider.onDidChange(() => {
820+
if (this._currentDocumentRequestCancellationTokenSource) {
821+
// there is already a request running,
822+
this._providersChangedDuringRequest = true;
823+
return;
824+
}
825+
this._fetchDocumentSemanticTokens.schedule(0);
826+
}));
818827
}
819828
}
820829
};
@@ -868,6 +877,7 @@ export class ModelSemanticColoring extends Disposable {
868877
const lastResultId = this._currentDocumentResponse ? this._currentDocumentResponse.resultId || null : null;
869878
const request = getDocumentSemanticTokens(this._provider, this._model, lastProvider, lastResultId, cancellationTokenSource.token);
870879
this._currentDocumentRequestCancellationTokenSource = cancellationTokenSource;
880+
this._providersChangedDuringRequest = false;
871881

872882
const pendingChanges: IModelContentChangedEvent[] = [];
873883
const contentChangeListener = this._model.onDidChangeContent((e) => {
@@ -898,7 +908,7 @@ export class ModelSemanticColoring extends Disposable {
898908
this._currentDocumentRequestCancellationTokenSource = null;
899909
contentChangeListener.dispose();
900910

901-
if (pendingChanges.length > 0) {
911+
if (pendingChanges.length > 0 || this._providersChangedDuringRequest) {
902912
// More changes occurred while the request was running
903913
if (!this._fetchDocumentSemanticTokens.isScheduled()) {
904914
this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model));
@@ -918,7 +928,7 @@ export class ModelSemanticColoring extends Disposable {
918928
private _setDocumentSemanticTokens(provider: DocumentSemanticTokensProvider | null, tokens: SemanticTokens | SemanticTokensEdits | null, styling: SemanticTokensProviderStyling | null, pendingChanges: IModelContentChangedEvent[]): void {
919929
const currentResponse = this._currentDocumentResponse;
920930
const rescheduleIfNeeded = () => {
921-
if (pendingChanges.length > 0 && !this._fetchDocumentSemanticTokens.isScheduled()) {
931+
if ((pendingChanges.length > 0 || this._providersChangedDuringRequest) && !this._fetchDocumentSemanticTokens.isScheduled()) {
922932
this._fetchDocumentSemanticTokens.schedule(this._debounceInformation.get(this._model));
923933
}
924934
};

src/vs/editor/test/common/services/modelService.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import * as assert from 'assert';
77
import { CharCode } from 'vs/base/common/charCode';
8-
import { Event } from 'vs/base/common/event';
8+
import { Emitter, Event } from 'vs/base/common/event';
99
import * as platform from 'vs/base/common/platform';
1010
import { URI } from 'vs/base/common/uri';
1111
import { EditOperation } from 'vs/editor/common/core/editOperation';
@@ -543,6 +543,40 @@ suite('ModelSemanticColoring', () => {
543543
});
544544
});
545545

546+
test('issue #161573: onDidChangeSemanticTokens doesn\'t consistently trigger provideDocumentSemanticTokens', async () => {
547+
await runWithFakedTimers({}, async () => {
548+
549+
disposables.add(languageService.registerLanguage({ id: 'testMode' }));
550+
551+
const emitter = new Emitter<void>();
552+
let requestCount = 0;
553+
disposables.add(languageFeaturesService.documentSemanticTokensProvider.register('testMode', new class implements DocumentSemanticTokensProvider {
554+
onDidChange = emitter.event;
555+
getLegend(): SemanticTokensLegend {
556+
return { tokenTypes: ['class'], tokenModifiers: [] };
557+
}
558+
async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
559+
requestCount++;
560+
if (requestCount === 1) {
561+
await timeout(1000);
562+
// send a change event
563+
emitter.fire();
564+
await timeout(1000);
565+
return null;
566+
}
567+
return null;
568+
}
569+
releaseDocumentSemanticTokens(resultId: string | undefined): void {
570+
}
571+
}));
572+
573+
disposables.add(modelService.createModel('', languageService.createById('testMode')));
574+
575+
await timeout(5000);
576+
assert.deepStrictEqual(requestCount, 2);
577+
});
578+
});
579+
546580
test('DocumentSemanticTokens should be pick the token provider with actual items', async () => {
547581
await runWithFakedTimers({}, async () => {
548582

0 commit comments

Comments
 (0)