Change:
Does this issue occur when all extensions are disabled?: Yes VS Code Version: 1.77.2 OS Version: Windows 10 It seems that when refreshing () the in a short period, the second refresh is not actually performed. I have provided an example repo and a video demonstrating the issue. Basically we have a that shadows the active editor so whenever a user changes the active editor we update the read only document. However, if the user changes the editors quickly enough then the second change is dropped... Steps to Reproduce: the extension the command "Open Read Only Document" tabs quickly and see how it falls behind even though the content is up to date https://user- The content is actually set but it seems the second is dropped. Because if you refresh the document manually it shows the correct content (see the video above). The issue seems to happen more frequently if the content of the file(s) is large.
I found out why it happens and attempted a fix (with explanation) in
Pinging so it does not get lost

Discussion:
this._resourceContentProvider.deleteAndDispose(handle); } <del> $onVirtualDocumentChange(uri: UriComponents, value: string): void { </del> <ins> async $onVirtualDocumentChange(uri: UriComponents, value: string): Promise<void> { </ins> const model = this._modelService.getModel(URI.revive(uri)); if (!model) { return;

const myToken = new CancellationTokenSource(); this._pendingUpdate.set(model.id, myToken); <del> this._editorWorkerService.computeMoreMinimalEdits(model.uri, [{ text: value, range: model.getFullModelRange() }]).then(edits => { </del> <ins> try { const edits = await this._editorWorkerService.computeMoreMinimalEdits(model.uri, [{ text: value, range: model.getFullModelRange() }]); </ins> // remove token this._pendingUpdate.delete(model.id);

// use the evil-edit as these models show in readonly-editor only model.applyEdits(edits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text))); } <del> }).catch(onUnexpectedError); </del> <ins> } catch (error) { onUnexpectedError(error); } </ins> } }

export interface MainThreadDocumentContentProvidersShape extends IDisposable { $registerTextContentProvider(handle: number, scheme: string): void; $unregisterTextContentProvider(handle: number): void; <del> $onVirtualDocumentChange(uri: UriComponents, value: string): void; </del> <ins> $onVirtualDocumentChange(uri: UriComponents, value: string): Promise<void>; </ins> } export interface MainThreadDocumentsShape extends IDisposable {

let subscription: IDisposable | undefined; if (typeof provider.onDidChange === 'function') { <del> subscription = provider.onDidChange(uri => { </del> <ins> let lastEvent: Promise<void> | undefined; subscription = provider.onDidChange(async uri => { </ins> if (uri.scheme !== scheme) { this._logService.warn(`Provider for scheme '${scheme}' is firing event for schema '${uri.scheme}' which will be IGNORED`); return; } <del> if (this._documentsAndEditors.getDocument(uri)) { this.$provideTextDocumentContent(handle, uri).then(value => { </del> <ins> if (!this._documentsAndEditors.getDocument(uri)) { // ignore event if document isn't open return; } if (lastEvent) { await lastEvent; } const thisEvent = this.$provideTextDocumentContent(handle, uri) .then(async value => { </ins> if (!value && typeof value !== 'string') { return; }

if (!document.equalLines(lines)) { return this._proxy.$onVirtualDocumentChange(uri, value); } <ins> }) .catch(onUnexpectedError) .finally(() => { if (lastEvent === thisEvent) { lastEvent = undefined; } }); </ins> <del> }, onUnexpectedError); } </del> <ins> lastEvent = thisEvent; </ins> }); } return new Disposable(() => {

<ins> /*--------------------------------------------------------------------------------------------- *  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 { URI, UriComponents } from 'vs/base/common/uri'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { SingleProxyRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol'; import { NullLogService } from 'vs/platform/log/common/log'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { ExtHostDocumentContentProvider } from 'vs/workbench/api/common/extHostDocumentContentProviders'; import { Emitter } from 'vs/base/common/event'; import { MainThreadDocumentContentProvidersShape } from 'vs/workbench/api/common/extHost.protocol'; import { timeout } from 'vs/base/common/async'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; suite('ExtHostDocumentContentProvider', () => { ensureNoDisposablesAreLeakedInTestSuite(); const resource = URI.parse('foo:bar'); let documentContentProvider: ExtHostDocumentContentProvider; let mainThreadContentProvider: MainThreadDocumentContentProvidersShape; const changes: [uri: UriComponents, value: string][] = []; setup(() => { changes.length = 0; mainThreadContentProvider = new class implements MainThreadDocumentContentProvidersShape { $registerTextContentProvider(handle: number, scheme: string): void { } $unregisterTextContentProvider(handle: number): void { } async $onVirtualDocumentChange(uri: UriComponents, value: string): Promise<void> { await timeout(10); changes.push([uri, value]); } dispose(): void { throw new Error('Method not implemented.'); } }; const ehContext = SingleProxyRPCProtocol(mainThreadContentProvider); const documentsAndEditors = new ExtHostDocumentsAndEditors(ehContext, new NullLogService()); documentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: [{ isDirty: false, languageId: 'foo', uri: resource, versionId: 1, lines: ['foo'], EOL: 'n', }] }); documentContentProvider = new ExtHostDocumentContentProvider(ehContext, documentsAndEditors, new NullLogService()); }); test('TextDocumentContentProvider drops onDidChange events when they happen quickly #179711', async () => { await runWithFakedTimers({}, async function () { const emitter = new Emitter<URI>(); const contents = ['X', 'Y']; let counter = 0; let stack = 0; const d = documentContentProvider.registerTextDocumentContentProvider(resource.scheme, { onDidChange: emitter.event, async provideTextDocumentContent(_uri) { assert.strictEqual(stack, 0); stack++; try { await timeout(0); return contents[counter++ % contents.length]; } finally { stack--; } } }); emitter.fire(resource); emitter.fire(resource); await timeout(100); assert.strictEqual(changes.length, 2); assert.strictEqual(changes[0][1], 'X'); assert.strictEqual(changes[1][1], 'Y'); d.dispose(); }); }); }); </ins>