Change:
Issue Type: <bBug</bMoved off Steps to reproduce: code that generates a linting warning (in this case, should should generate an unused variable warning) the file, and then close it before the diagnostics appear Expected behavior: Diagnostics are not shown when the file is closed. Actual behavior: Diagnostics are sometimes shown when the file is closed, and will not disappear until I reload the window. Please note that I couldn't reproduce this behaviour reliably. Action gif: ! VS Code version: Code 1.46.1 (, 2020-06-17T21:17:14.222Z) OS version: Darwin x64 19.5.0 <details<summarySystem Info</summary </details<details<summaryExtensions (15)</summaryExtensionVersion ------ bracket-pair-colorizer-20.2.0 gitlens10.2.2 prettier-vscode5.1.3 vscode-pull-request-github0.17.0 vscode-docker1.3.1 python2020.6. vscode-pylance2020.6.1 remote-containers0.122.1 remote-ssh0.51.0 remote-ssh-edit0.51.0 remote-wsl0.44.4 vscode-remote-extensionpack0.20.0 azure-account0.8.11 vscode-typescript-tslint-plugin1.2.3 vscode-icons10.1.1 (3 theme extensions excluded) </details<!-- generated by issue reporter --

Discussion:
import { Delayer, setImmediate } from '../utils/async'; import { nulToken } from '../utils/cancellation'; import { Disposable } from '../utils/dispose'; <ins> import { vscodeNotebookCell } from '../utils/fileSchemes'; </ins> import * as languageModeIds from '../utils/languageIds'; import { ResourceMap } from '../utils/resourceMap'; import * as typeConverters from '../utils/typeConverters';

} } <ins> class TabResourceTracker extends Disposable { private readonly _onDidChange = this._register(new vscode.EventEmitter<{ readonly closed: Iterable<vscode.Uri>; readonly opened: Iterable<vscode.Uri>; }>()); public readonly onDidChange = this._onDidChange.event; private readonly _tabResources: ResourceMap<{ readonly tabs: Set<vscode.Tab> }>; constructor( normalizePath: (resource: vscode.Uri) => string | undefined, config: { readonly onCaseInsensitiveFileSystem: boolean; }, ) { super(); this._tabResources = new ResourceMap<{ readonly tabs: Set<vscode.Tab> }>(normalizePath, config); for (const tabGroup of vscode.window.tabGroups.all) { for (const tab of tabGroup.tabs) { this.add(tab); } } this._register(vscode.window.tabGroups.onDidChangeTabs(e => { const closed = e.closed.flatMap(tab => this.delete(tab)); const opened = e.opened.flatMap(tab => this.add(tab)); if (closed.length || opened.length) { this._onDidChange.fire({ closed, opened }); } })); } public has(resource: vscode.Uri): boolean { if (resource.scheme === vscodeNotebookCell) { const notebook = vscode.workspace.notebookDocuments.find(doc => doc.getCells().some(cell => cell.document.uri.toString() === resource.toString())); return !!notebook && this.has(notebook.uri); } const entry = this._tabResources.get(resource); return !!entry && entry.tabs.size > 0; } private add(tab: vscode.Tab): vscode.Uri[] { const addedResources: vscode.Uri[] = []; for (const uri of this.getResourcesForTab(tab)) { const entry = this._tabResources.get(uri); if (entry) { entry.tabs.add(tab); } else { this._tabResources.set(uri, { tabs: new Set([tab]) }); addedResources.push(uri); } } return addedResources; } private delete(tab: vscode.Tab): vscode.Uri[] { const closedResources: vscode.Uri[] = []; for (const uri of this.getResourcesForTab(tab)) { const entry = this._tabResources.get(uri); if (!entry) { continue; } entry.tabs.delete(tab); if (entry.tabs.size === 0) { this._tabResources.delete(uri); closedResources.push(uri); } } return closedResources; } private getResourcesForTab(tab: vscode.Tab): vscode.Uri[] { if (tab.input instanceof vscode.TabInputText) { return [tab.input.uri]; } else if (tab.input instanceof vscode.TabInputTextDiff) { return [tab.input.original, tab.input.modified]; } else if (tab.input instanceof vscode.TabInputNotebook) { return [tab.input.uri]; } else { return []; } } } </ins> export default class BufferSyncSupport extends Disposable { private readonly client: ITypeScriptServiceClient;

private listening: boolean = false; private readonly synchronizer: BufferSynchronizer; <ins> private readonly _tabResources: TabResourceTracker; </ins> constructor( client: ITypeScriptServiceClient, modeIds: readonly string[],

this.pendingDiagnostics = new PendingDiagnostics(pathNormalizer, { onCaseInsensitiveFileSystem }); this.synchronizer = new BufferSynchronizer(client, pathNormalizer, onCaseInsensitiveFileSystem); <ins> this._tabResources = this._register(new TabResourceTracker(pathNormalizer, { onCaseInsensitiveFileSystem })); this._register(this._tabResources.onDidChange(e => { if (this.client.configuration.enableProjectDiagnostics) { return; } for (const closed of e.closed) { const syncedBuffer = this.syncedBuffers.get(closed); if (syncedBuffer) { this.pendingDiagnostics.delete(closed); this.pendingGetErr?.files.delete(closed); } } for (const opened of e.opened) { const syncedBuffer = this.syncedBuffers.get(opened); if (syncedBuffer) { this.requestDiagnostic(syncedBuffer); } } })); </ins> this.updateConfiguration(); vscode.workspace.onDidChangeConfiguration(this.updateConfiguration, this, this._disposables); }

if (!syncedBuffer) { return; } this.pendingDiagnostics.delete(resource); this.pendingGetErr?.files.delete(resource); this.syncedBuffers.delete(resource);

public interruptGetErr<R>(f: () => R): R { if (!this.pendingGetErr <del> || this.client.configuration.enableProjectDiagnostics // `geterr` happens on seperate server so no need to cancel it. </del> <ins> || this.client.configuration.enableProjectDiagnostics // `geterr` happens on separate server so no need to cancel it. </ins> ) { return f(); }

this._validateTypeScript = tsConfig.get<boolean>('validate.enable', true); } <del> private shouldValidate(buffer: SyncedBuffer) { </del> <ins> private shouldValidate(buffer: SyncedBuffer): boolean { if (!this.client.configuration.enableProjectDiagnostics && !this._tabResources.has(buffer.resource)) { // Only validate resources that are showing to the user return false; } </ins> switch (buffer.kind) { case BufferKind.JavaScript: return this._validateJavaScript;