diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index d599ab1684a..a72fa72c158 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1375,6 +1375,7 @@ declare namespace monaco.editor { startColumn: number; endLineNumber: number; endColumn: number; + modelVersionId?: number; relatedInformation?: IRelatedInformation[]; tags?: MarkerTag[]; } @@ -1394,6 +1395,7 @@ declare namespace monaco.editor { startColumn: number; endLineNumber: number; endColumn: number; + modelVersionId?: number; relatedInformation?: IRelatedInformation[]; tags?: MarkerTag[]; } diff --git a/src/vs/platform/markers/common/markers.ts b/src/vs/platform/markers/common/markers.ts index 9af13732580..8b68c4f80f3 100644 --- a/src/vs/platform/markers/common/markers.ts +++ b/src/vs/platform/markers/common/markers.ts @@ -95,6 +95,7 @@ export interface IMarkerData { startColumn: number; endLineNumber: number; endColumn: number; + modelVersionId?: number; relatedInformation?: IRelatedInformation[]; tags?: MarkerTag[]; } @@ -115,6 +116,7 @@ export interface IMarker { startColumn: number; endLineNumber: number; endColumn: number; + modelVersionId?: number; relatedInformation?: IRelatedInformation[]; tags?: MarkerTag[]; } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index de0e2f03f4e..4a534788610 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -166,7 +166,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors)); const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService)); const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.remote)); - const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol, extHostLogService, extHostFileSystemInfo)); + const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol, extHostLogService, extHostFileSystemInfo, extHostDocumentsAndEditors)); const extHostLanguages = rpcProtocol.set(ExtHostContext.ExtHostLanguages, new ExtHostLanguages(rpcProtocol, extHostDocuments, extHostCommands.converter, uriTransformer)); const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService, extHostApiDeprecation, extHostTelemetry)); const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures)); diff --git a/src/vs/workbench/api/common/extHostDiagnostics.ts b/src/vs/workbench/api/common/extHostDiagnostics.ts index 81cf1f2b756..7376effaf75 100644 --- a/src/vs/workbench/api/common/extHostDiagnostics.ts +++ b/src/vs/workbench/api/common/extHostDiagnostics.ts @@ -16,6 +16,7 @@ import { ResourceMap } from 'vs/base/common/map'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; import { IExtUri } from 'vs/base/common/resources'; +import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; export class DiagnosticCollection implements vscode.DiagnosticCollection { @@ -29,6 +30,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { private readonly _name: string, private readonly _owner: string, private readonly _maxDiagnosticsPerFile: number, + private readonly _modelVersionIdProvider: (uri: URI) => number | undefined, extUri: IExtUri, proxy: MainThreadDiagnosticsShape | undefined, onDidChangeDiagnostics: Emitter @@ -131,7 +133,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { orderLoop: for (let i = 0; i < 4; i++) { for (const diagnostic of diagnostics) { if (diagnostic.severity === order[i]) { - const len = marker.push(converter.Diagnostic.from(diagnostic)); + const len = marker.push({ ...converter.Diagnostic.from(diagnostic), modelVersionId: this._modelVersionIdProvider(uri) }); if (len === this._maxDiagnosticsPerFile) { break orderLoop; } @@ -149,7 +151,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { endColumn: marker[marker.length - 1].endColumn }); } else { - marker = diagnostics.map(diag => converter.Diagnostic.from(diag)); + marker = diagnostics.map(diag => ({ ...converter.Diagnostic.from(diag), modelVersionId: this._modelVersionIdProvider(uri) })); } } @@ -240,13 +242,14 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape { mainContext: IMainContext, @ILogService private readonly _logService: ILogService, @IExtHostFileSystemInfo private readonly _fileSystemInfoService: IExtHostFileSystemInfo, + private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors, ) { this._proxy = mainContext.getProxy(MainContext.MainThreadDiagnostics); } createDiagnosticCollection(extensionId: ExtensionIdentifier, name?: string): vscode.DiagnosticCollection { - const { _collections, _proxy, _onDidChangeDiagnostics, _logService, _fileSystemInfoService } = this; + const { _collections, _proxy, _onDidChangeDiagnostics, _logService, _fileSystemInfoService, _extHostDocumentsAndEditors } = this; const loggingProxy = new class implements MainThreadDiagnosticsShape { $changeMany(owner: string, entries: [UriComponents, IMarkerData[] | undefined][]): void { @@ -278,7 +281,11 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape { const result = new class extends DiagnosticCollection { constructor() { - super(name!, owner, ExtHostDiagnostics._maxDiagnosticsPerFile, _fileSystemInfoService.extUri, loggingProxy, _onDidChangeDiagnostics); + super( + name!, owner, ExtHostDiagnostics._maxDiagnosticsPerFile, + uri => _extHostDocumentsAndEditors.getDocument(uri)?.version, + _fileSystemInfoService.extUri, loggingProxy, _onDidChangeDiagnostics + ); _collections.set(owner, this); } override dispose() { @@ -330,7 +337,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape { if (!this._mirrorCollection) { const name = '_generated_mirror'; - const collection = new DiagnosticCollection(name, name, ExtHostDiagnostics._maxDiagnosticsPerFile, this._fileSystemInfoService.extUri, undefined, this._onDidChangeDiagnostics); + const collection = new DiagnosticCollection(name, name, ExtHostDiagnostics._maxDiagnosticsPerFile, _uri => undefined, this._fileSystemInfoService.extUri, undefined, this._onDidChangeDiagnostics); this._collections.set(name, collection); this._mirrorCollection = collection; } diff --git a/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts b/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts index a8b3cb85a52..cc708501c45 100644 --- a/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts +++ b/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts @@ -165,7 +165,7 @@ suite('ExtHostLanguageFeatureCommands', function () { rpcProtocol.set(MainContext.MainThreadCommands, insta.createInstance(MainThreadCommands, rpcProtocol)); ExtHostApiCommands.register(commands); - const diagnostics = new ExtHostDiagnostics(rpcProtocol, new NullLogService(), new class extends mock() { }); + const diagnostics = new ExtHostDiagnostics(rpcProtocol, new NullLogService(), new class extends mock() { }, extHostDocumentsAndEditors); rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics); extHost = new ExtHostLanguageFeatures(rpcProtocol, new URITransformerService(null), extHostDocuments, commands, diagnostics, new NullLogService(), NullApiDeprecationService, new class extends mock() { }); diff --git a/src/vs/workbench/api/test/browser/extHostDiagnostics.test.ts b/src/vs/workbench/api/test/browser/extHostDiagnostics.test.ts index f7d92af7f58..aaeb523fbe2 100644 --- a/src/vs/workbench/api/test/browser/extHostDiagnostics.test.ts +++ b/src/vs/workbench/api/test/browser/extHostDiagnostics.test.ts @@ -17,6 +17,7 @@ import { nullExtensionDescription } from 'vs/workbench/services/extensions/commo import { ExtUri, extUri } from 'vs/base/common/resources'; import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; suite('ExtHostDiagnostics', () => { @@ -33,9 +34,13 @@ suite('ExtHostDiagnostics', () => { override readonly extUri = extUri; }; + const versionProvider = (uri: URI): number | undefined => { + return undefined; + }; + test('disposeCheck', () => { - const collection = new DiagnosticCollection('test', 'test', 100, extUri, new DiagnosticsShape(), new Emitter()); + const collection = new DiagnosticCollection('test', 'test', 100, versionProvider, extUri, new DiagnosticsShape(), new Emitter()); collection.dispose(); collection.dispose(); // that's OK @@ -51,13 +56,13 @@ suite('ExtHostDiagnostics', () => { test('diagnostic collection, forEach, clear, has', function () { - let collection = new DiagnosticCollection('test', 'test', 100, extUri, new DiagnosticsShape(), new Emitter()); + let collection = new DiagnosticCollection('test', 'test', 100, versionProvider, extUri, new DiagnosticsShape(), new Emitter()); assert.strictEqual(collection.name, 'test'); collection.dispose(); assert.throws(() => collection.name); let c = 0; - collection = new DiagnosticCollection('test', 'test', 100, extUri, new DiagnosticsShape(), new Emitter()); + collection = new DiagnosticCollection('test', 'test', 100, versionProvider, extUri, new DiagnosticsShape(), new Emitter()); collection.forEach(() => c++); assert.strictEqual(c, 0); @@ -94,7 +99,7 @@ suite('ExtHostDiagnostics', () => { }); test('diagnostic collection, immutable read', function () { - const collection = new DiagnosticCollection('test', 'test', 100, extUri, new DiagnosticsShape(), new Emitter()); + const collection = new DiagnosticCollection('test', 'test', 100, versionProvider, extUri, new DiagnosticsShape(), new Emitter()); collection.set(URI.parse('foo:bar'), [ new Diagnostic(new Range(0, 0, 1, 1), 'message-1'), new Diagnostic(new Range(0, 0, 1, 1), 'message-2') @@ -119,7 +124,7 @@ suite('ExtHostDiagnostics', () => { test('diagnostics collection, set with dupliclated tuples', function () { - const collection = new DiagnosticCollection('test', 'test', 100, extUri, new DiagnosticsShape(), new Emitter()); + const collection = new DiagnosticCollection('test', 'test', 100, versionProvider, extUri, new DiagnosticsShape(), new Emitter()); const uri = URI.parse('sc:hightower'); collection.set([ [uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-1')]], @@ -170,7 +175,7 @@ suite('ExtHostDiagnostics', () => { test('diagnostics collection, set tuple overrides, #11547', function () { let lastEntries!: [UriComponents, IMarkerData[]][]; - const collection = new DiagnosticCollection('test', 'test', 100, extUri, new class extends DiagnosticsShape { + const collection = new DiagnosticCollection('test', 'test', 100, versionProvider, extUri, new class extends DiagnosticsShape { override $changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void { lastEntries = entries; return super.$changeMany(owner, entries); @@ -204,7 +209,7 @@ suite('ExtHostDiagnostics', () => { const emitter = new Emitter(); emitter.event(_ => eventCount += 1); - const collection = new DiagnosticCollection('test', 'test', 100, extUri, new class extends DiagnosticsShape { + const collection = new DiagnosticCollection('test', 'test', 100, versionProvider, extUri, new class extends DiagnosticsShape { override $changeMany() { changeCount += 1; } @@ -224,7 +229,7 @@ suite('ExtHostDiagnostics', () => { test('diagnostics collection, tuples and undefined (small array), #15585', function () { - const collection = new DiagnosticCollection('test', 'test', 100, extUri, new DiagnosticsShape(), new Emitter()); + const collection = new DiagnosticCollection('test', 'test', 100, versionProvider, extUri, new DiagnosticsShape(), new Emitter()); const uri = URI.parse('sc:hightower'); const uri2 = URI.parse('sc:nomad'); const diag = new Diagnostic(new Range(0, 0, 0, 1), 'ffff'); @@ -245,7 +250,7 @@ suite('ExtHostDiagnostics', () => { test('diagnostics collection, tuples and undefined (large array), #15585', function () { - const collection = new DiagnosticCollection('test', 'test', 100, extUri, new DiagnosticsShape(), new Emitter()); + const collection = new DiagnosticCollection('test', 'test', 100, versionProvider, extUri, new DiagnosticsShape(), new Emitter()); const tuples: [URI, Diagnostic[]][] = []; for (let i = 0; i < 500; i++) { @@ -269,7 +274,7 @@ suite('ExtHostDiagnostics', () => { test('diagnostic capping', function () { let lastEntries!: [UriComponents, IMarkerData[]][]; - const collection = new DiagnosticCollection('test', 'test', 250, extUri, new class extends DiagnosticsShape { + const collection = new DiagnosticCollection('test', 'test', 250, versionProvider, extUri, new class extends DiagnosticsShape { override $changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void { lastEntries = entries; return super.$changeMany(owner, entries); @@ -295,7 +300,7 @@ suite('ExtHostDiagnostics', () => { test('diagnostic eventing', async function () { const emitter = new Emitter>(); - const collection = new DiagnosticCollection('ddd', 'test', 100, extUri, new DiagnosticsShape(), emitter); + const collection = new DiagnosticCollection('ddd', 'test', 100, versionProvider, extUri, new DiagnosticsShape(), emitter); const diag1 = new Diagnostic(new Range(1, 1, 2, 3), 'diag1'); const diag2 = new Diagnostic(new Range(1, 1, 2, 3), 'diag2'); @@ -333,7 +338,7 @@ suite('ExtHostDiagnostics', () => { test('vscode.languages.onDidChangeDiagnostics Does Not Provide Document URI #49582', async function () { const emitter = new Emitter>(); - const collection = new DiagnosticCollection('ddd', 'test', 100, extUri, new DiagnosticsShape(), emitter); + const collection = new DiagnosticCollection('ddd', 'test', 100, versionProvider, extUri, new DiagnosticsShape(), emitter); const diag1 = new Diagnostic(new Range(1, 1, 2, 3), 'diag1'); @@ -356,7 +361,7 @@ suite('ExtHostDiagnostics', () => { test('diagnostics with related information', function (done) { - const collection = new DiagnosticCollection('ddd', 'test', 100, extUri, new class extends DiagnosticsShape { + const collection = new DiagnosticCollection('ddd', 'test', 100, versionProvider, extUri, new class extends DiagnosticsShape { override $changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]) { const [[, data]] = entries; @@ -400,7 +405,11 @@ suite('ExtHostDiagnostics', () => { drain() { return undefined!; } - }, new NullLogService(), fileSystemInfoService); + }, new NullLogService(), fileSystemInfoService, new class extends mock() { + override getDocument() { + return undefined; + } + }); const collection1 = diags.createDiagnosticCollection(nullExtensionDescription.identifier, 'foo'); const collection2 = diags.createDiagnosticCollection(nullExtensionDescription.identifier, 'foo'); // warns, uses a different owner @@ -415,7 +424,7 @@ suite('ExtHostDiagnostics', () => { test('Error updating diagnostics from extension #60394', function () { let callCount = 0; - const collection = new DiagnosticCollection('ddd', 'test', 100, extUri, new class extends DiagnosticsShape { + const collection = new DiagnosticCollection('ddd', 'test', 100, versionProvider, extUri, new class extends DiagnosticsShape { override $changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]) { callCount += 1; } @@ -438,6 +447,32 @@ suite('ExtHostDiagnostics', () => { assert.strictEqual(callCount, 3); // same but un-equal array }); + test('Version id is set whenever possible', function () { + + const all: [UriComponents, IMarkerData[]][] = []; + + const collection = new DiagnosticCollection('ddd', 'test', 100, uri => { + return 7; + }, extUri, new class extends DiagnosticsShape { + override $changeMany(_owner: string, entries: [UriComponents, IMarkerData[]][]) { + all.push(...entries); + } + }, new Emitter()); + + const array: Diagnostic[] = []; + const diag1 = new Diagnostic(new Range(0, 0, 1, 1), 'Foo'); + const diag2 = new Diagnostic(new Range(0, 0, 1, 1), 'Bar'); + + array.push(diag1, diag2); + + collection.set(URI.parse('test:one'), array); + collection.set(URI.parse('test:two'), [diag1]); + collection.set(URI.parse('test:three'), [diag2]); + + const allVersions = all.map(tuple => tuple[1].map(t => t.modelVersionId)).flat(); + assert.deepStrictEqual(allVersions, [7, 7, 7, 7]); + }); + test('Diagnostics created by tasks aren\'t accessible to extensions #47292', async function () { return runWithFakedTimers({}, async function () { @@ -455,7 +490,11 @@ suite('ExtHostDiagnostics', () => { drain() { return undefined!; } - }, new NullLogService(), fileSystemInfoService); + }, new NullLogService(), fileSystemInfoService, new class extends mock() { + override getDocument() { + return undefined; + } + }); // @@ -500,6 +539,10 @@ suite('ExtHostDiagnostics', () => { }, new NullLogService(), new class extends mock() { override readonly extUri = new ExtUri(uri => uri.scheme === 'insensitive'); + }, new class extends mock() { + override getDocument() { + return undefined; + } }); const col = diags.createDiagnosticCollection(nullExtensionDescription.identifier); diff --git a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts index 71a6d4d28c3..14eed94e561 100644 --- a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts @@ -119,7 +119,7 @@ suite('ExtHostLanguageFeatures', function () { rpcProtocol.set(ExtHostContext.ExtHostCommands, commands); rpcProtocol.set(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, rpcProtocol)); - const diagnostics = new ExtHostDiagnostics(rpcProtocol, new NullLogService(), new class extends mock() { }); + const diagnostics = new ExtHostDiagnostics(rpcProtocol, new NullLogService(), new class extends mock() { }, extHostDocumentsAndEditors); rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics); extHost = new ExtHostLanguageFeatures(rpcProtocol, new URITransformerService(null), extHostDocuments, commands, diagnostics, new NullLogService(), NullApiDeprecationService, new class extends mock() { });