|
| 1 | +/*--------------------------------------------------------------------------------------------- |
| 2 | + * Copyright (c) Microsoft Corporation. All rights reserved. |
| 3 | + * Licensed under the MIT License. See License.txt in the project root for license information. |
| 4 | + *--------------------------------------------------------------------------------------------*/ |
| 5 | +import * as vscode from 'vscode'; |
| 6 | + |
| 7 | +export enum AnalysisSetting { |
| 8 | + FullSolution = 'fullSolution', |
| 9 | + OpenFiles = 'openFiles', |
| 10 | + None = 'none', |
| 11 | +} |
| 12 | + |
| 13 | +export class BuildDiagnosticsService { |
| 14 | + /** All the build results sent by the DevKit extension. */ |
| 15 | + private _allBuildDiagnostics: Array<[vscode.Uri, vscode.Diagnostic[]]> = []; |
| 16 | + |
| 17 | + /** The diagnostic results from build displayed by VS Code. When live diagnostics are available for a file, these are errors that only build knows about. |
| 18 | + * When live diagnostics aren't loaded for a file, then these are all of the diagnostics reported by the build.*/ |
| 19 | + private _diagnosticsReportedByBuild: vscode.DiagnosticCollection; |
| 20 | + |
| 21 | + constructor(buildDiagnostics: vscode.DiagnosticCollection) { |
| 22 | + this._diagnosticsReportedByBuild = buildDiagnostics; |
| 23 | + } |
| 24 | + |
| 25 | + public clearDiagnostics() { |
| 26 | + this._diagnosticsReportedByBuild.clear(); |
| 27 | + } |
| 28 | + |
| 29 | + public async setBuildDiagnostics( |
| 30 | + buildDiagnostics: Array<[vscode.Uri, vscode.Diagnostic[]]>, |
| 31 | + buildOnlyIds: string[] |
| 32 | + ) { |
| 33 | + this._allBuildDiagnostics = buildDiagnostics; |
| 34 | + const displayedBuildDiagnostics = new Array<[vscode.Uri, vscode.Diagnostic[]]>(); |
| 35 | + const allDocuments = vscode.workspace.textDocuments; |
| 36 | + |
| 37 | + this._allBuildDiagnostics.forEach((fileDiagnostics) => { |
| 38 | + const uri = fileDiagnostics[0]; |
| 39 | + const diagnosticList = fileDiagnostics[1]; |
| 40 | + |
| 41 | + // Check if the document is open |
| 42 | + const document = allDocuments.find((d) => this.compareUri(d.uri, uri)); |
| 43 | + const isDocumentOpen = document !== undefined ? !document.isClosed : false; |
| 44 | + |
| 45 | + // Show the build-only diagnostics |
| 46 | + displayedBuildDiagnostics.push([ |
| 47 | + uri, |
| 48 | + BuildDiagnosticsService.filerDiagnosticsFromBuild(diagnosticList, buildOnlyIds, isDocumentOpen), |
| 49 | + ]); |
| 50 | + }); |
| 51 | + |
| 52 | + this._diagnosticsReportedByBuild.set(displayedBuildDiagnostics); |
| 53 | + } |
| 54 | + |
| 55 | + private compareUri(a: vscode.Uri, b: vscode.Uri): boolean { |
| 56 | + return a.path.localeCompare(b.path, undefined, { sensitivity: 'accent' }) === 0; |
| 57 | + } |
| 58 | + |
| 59 | + public async _onFileOpened(document: vscode.TextDocument, buildOnlyIds: string[]) { |
| 60 | + const uri = document.uri; |
| 61 | + const currentFileBuildDiagnostics = this._allBuildDiagnostics?.find(([u]) => this.compareUri(u, uri)); |
| 62 | + |
| 63 | + // The document is now open in the editor and live diagnostics are being shown. Filter diagnostics |
| 64 | + // reported by the build to show build-only problems. |
| 65 | + if (currentFileBuildDiagnostics) { |
| 66 | + const buildDiagnostics = BuildDiagnosticsService.filerDiagnosticsFromBuild( |
| 67 | + currentFileBuildDiagnostics[1], |
| 68 | + buildOnlyIds, |
| 69 | + true |
| 70 | + ); |
| 71 | + this._diagnosticsReportedByBuild.set(uri, buildDiagnostics); |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + public static filerDiagnosticsFromBuild( |
| 76 | + diagnosticList: vscode.Diagnostic[], |
| 77 | + buildOnlyIds: string[], |
| 78 | + isDocumentOpen: boolean |
| 79 | + ): vscode.Diagnostic[] { |
| 80 | + const configuration = vscode.workspace.getConfiguration(); |
| 81 | + const analyzerDiagnosticScope = configuration.get( |
| 82 | + 'dotnet.backgroundAnalysis.analyzerDiagnosticsScope' |
| 83 | + ) as AnalysisSetting; |
| 84 | + const compilerDiagnosticScope = configuration.get( |
| 85 | + 'dotnet.backgroundAnalysis.compilerDiagnosticsScope' |
| 86 | + ) as AnalysisSetting; |
| 87 | + |
| 88 | + // If compiler and analyzer diagnostics are set to "none", show everything reported by the build |
| 89 | + if (analyzerDiagnosticScope === AnalysisSetting.None && compilerDiagnosticScope === AnalysisSetting.None) { |
| 90 | + return diagnosticList; |
| 91 | + } |
| 92 | + |
| 93 | + // Filter the diagnostics reported by the build. Some may already be shown by live diagnostics. |
| 94 | + const buildOnlyDiagnostics: vscode.Diagnostic[] = []; |
| 95 | + diagnosticList.forEach((d) => { |
| 96 | + if (d.code) { |
| 97 | + // If it is a "build-only"diagnostics (e.g. it can only be found by building) |
| 98 | + // the diagnostic will always be included |
| 99 | + if (buildOnlyIds.find((b_id) => b_id === d.code)) { |
| 100 | + buildOnlyDiagnostics.push(d); |
| 101 | + } else { |
| 102 | + const isAnalyzerDiagnostic = BuildDiagnosticsService.isAnalyzerDiagnostic(d); |
| 103 | + const isCompilerDiagnostic = BuildDiagnosticsService.isCompilerDiagnostic(d); |
| 104 | + |
| 105 | + if ( |
| 106 | + (isAnalyzerDiagnostic && analyzerDiagnosticScope === AnalysisSetting.None) || |
| 107 | + (isCompilerDiagnostic && compilerDiagnosticScope === AnalysisSetting.None) |
| 108 | + ) { |
| 109 | + // If live diagnostics are completely turned off for this type, then show the build diagnostic |
| 110 | + buildOnlyDiagnostics.push(d); |
| 111 | + } else if (isDocumentOpen) { |
| 112 | + // no-op. The document is open and live diagnostis are on. This diagnostic is already being shown. |
| 113 | + } else if ( |
| 114 | + (isAnalyzerDiagnostic && analyzerDiagnosticScope === AnalysisSetting.FullSolution) || |
| 115 | + (isCompilerDiagnostic && compilerDiagnosticScope === AnalysisSetting.FullSolution) |
| 116 | + ) { |
| 117 | + // no-op. Full solution analysis is on for this diagnostic type. All diagnostics are already being shown. |
| 118 | + } else { |
| 119 | + // The document is closed, and the analysis setting is to only show for open files. |
| 120 | + // Show the diagnostic reported by build. |
| 121 | + buildOnlyDiagnostics.push(d); |
| 122 | + } |
| 123 | + } |
| 124 | + } |
| 125 | + }); |
| 126 | + |
| 127 | + return buildOnlyDiagnostics; |
| 128 | + } |
| 129 | + |
| 130 | + private static isCompilerDiagnostic(d: vscode.Diagnostic): boolean { |
| 131 | + return d.code ? d.code.toString().startsWith('CS') : false; |
| 132 | + } |
| 133 | + |
| 134 | + private static isAnalyzerDiagnostic(d: vscode.Diagnostic): boolean { |
| 135 | + return d.code ? !d.code.toString().startsWith('CS') : false; |
| 136 | + } |
| 137 | +} |
0 commit comments