diff --git a/src/vs/workbench/api/browser/mainThreadDiagnostics.ts b/src/vs/workbench/api/browser/mainThreadDiagnostics.ts index 94fafb8b6b1..8a7cff3c95e 100644 --- a/src/vs/workbench/api/browser/mainThreadDiagnostics.ts +++ b/src/vs/workbench/api/browser/mainThreadDiagnostics.ts @@ -37,12 +37,14 @@ export class MainThreadDiagnostics implements MainThreadDiagnosticsShape { private _forwardMarkers(resources: readonly URI[]): void { const data: [UriComponents, IMarkerData[]][] = []; for (const resource of resources) { - data.push([ - resource, - this._markerService.read({ resource }).filter(marker => !this._activeOwners.has(marker.owner)) - ]); + const markerData = this._markerService.read({ resource }).filter(marker => !this._activeOwners.has(marker.owner)); + if (markerData.length > 0) { + data.push([resource, markerData]); + } + } + if (data.length > 0) { + this._proxy.$acceptMarkersChange(data); } - this._proxy.$acceptMarkersChange(data); } $changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void { diff --git a/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts b/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts index 5a16d7d6b92..7b3c74f0457 100644 --- a/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts +++ b/src/vs/workbench/test/browser/api/mainThreadDiagnostics.test.ts @@ -6,11 +6,14 @@ import * as assert from 'assert'; import { MarkerService } from 'vs/platform/markers/common/markerService'; import { MainThreadDiagnostics } from 'vs/workbench/api/browser/mainThreadDiagnostics'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { mock } from 'vs/workbench/test/common/workbenchTestServices'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions'; +import { IMarkerData } from 'vs/platform/markers/common/markers'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { timeout } from 'vs/base/common/async'; suite('MainThreadDiagnostics', function () { @@ -57,4 +60,54 @@ suite('MainThreadDiagnostics', function () { diag.dispose(); assert.strictEqual(markerService.read().length, 0); }); + + test('OnDidChangeDiagnostics triggers twice on same diagnostics #136434', function () { + + return runWithFakedTimers({}, async () => { + + const changedData: [UriComponents, IMarkerData[]][][] = []; + + let diag = new MainThreadDiagnostics( + new class implements IExtHostContext { + remoteAuthority = ''; + extensionHostKind = ExtensionHostKind.LocalProcess; + assertRegistered() { } + set(v: any): any { return null; } + getProxy(): any { + return { + $acceptMarkersChange(data: [UriComponents, IMarkerData[]][]) { + changedData.push(data); + } + }; + } + drain(): any { return null; } + }, + markerService, + new class extends mock() { + override asCanonicalUri(uri: URI) { return uri; } + } + ); + + const markerDataStub = { + code: '666', + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: 1, + severity: 1, + source: 'me' + }; + const target = URI.file('a'); + diag.$changeMany('foo', [[target, [{ ...markerDataStub, message: 'same_owner' }]]]); + markerService.changeOne('bar', target, [{ ...markerDataStub, message: 'forgein_owner' }]); + + // added one marker via the API and one via the ext host. the latter must not + // trigger an event to the extension host + + await timeout(0); + assert.strictEqual(markerService.read().length, 2); + assert.strictEqual(changedData.length, 1); + assert.strictEqual(changedData[0][0][1][0].message, 'forgein_owner'); + }); + }); });