Skip to content

Commit ece9f5c

Browse files
committed
update for FSA
1 parent 0de66fb commit ece9f5c

File tree

4 files changed

+287
-69
lines changed

4 files changed

+287
-69
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
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+
}

src/lsptoolshost/roslynLanguageServer.ts

Lines changed: 8 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import { registerOnAutoInsert } from './onAutoInsert';
5656
import { commonOptions, languageServerOptions, omnisharpOptions } from '../shared/options';
5757
import { NamedPipeInformation } from './roslynProtocol';
5858
import { IDisposable } from '../disposable';
59+
import { BuildDiagnosticsService } from './buildDiagnosticsService';
5960

6061
let _channel: vscode.OutputChannel;
6162
let _traceChannel: vscode.OutputChannel;
@@ -94,12 +95,7 @@ export class RoslynLanguageServer {
9495
/** The project files previously opened; we hold onto this for the same reason as _solutionFile. */
9596
private _projectFiles: vscode.Uri[] = new Array<vscode.Uri>();
9697

97-
/** 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.
98-
* When live diagnostics aren't loaded for a file, then these are all of the diagnostics reported by the build.*/
99-
private _diagnosticsReportedByBuild: vscode.DiagnosticCollection;
100-
101-
/** All the build results sent by the DevKit extension. */
102-
private _allBuildDiagnostics: Array<[vscode.Uri, vscode.Diagnostic[]]> = [];
98+
public _buildDiagnosticService: BuildDiagnosticsService;
10399

104100
constructor(
105101
private _languageClient: RoslynLanguageClient,
@@ -115,7 +111,8 @@ export class RoslynLanguageServer {
115111
this.registerExtensionsChanged();
116112
this.registerTelemetryChanged();
117113

118-
this._diagnosticsReportedByBuild = vscode.languages.createDiagnosticCollection('csharp-build');
114+
const diagnosticsReportedByBuild = vscode.languages.createDiagnosticCollection('csharp-build');
115+
this._buildDiagnosticService = new BuildDiagnosticsService(diagnosticsReportedByBuild);
119116
this.registerDocumentOpenForDiagnostics();
120117

121118
// Register Razor dynamic file info handling
@@ -708,68 +705,13 @@ export class RoslynLanguageServer {
708705
private registerDocumentOpenForDiagnostics() {
709706
// When a file is opened process any build diagnostics that may be shown
710707
this._languageClient.addDisposable(
711-
vscode.workspace.onDidOpenTextDocument(async (event) => this._onFileOpened(event))
708+
vscode.workspace.onDidOpenTextDocument(async (event) => {
709+
const buildIds = await this.getBuildOnlyDiagnosticIds(CancellationToken.None);
710+
this._buildDiagnosticService._onFileOpened(event, buildIds);
711+
})
712712
);
713713
}
714714

715-
public clearDiagnostics() {
716-
this._diagnosticsReportedByBuild.clear();
717-
}
718-
719-
public async setBuildDiagnostics(buildDiagnostics: Array<[vscode.Uri, vscode.Diagnostic[]]>) {
720-
this._allBuildDiagnostics = buildDiagnostics;
721-
const buildOnlyIds = await this.getBuildOnlyDiagnosticIds(CancellationToken.None);
722-
const displayedBuildDiagnostics = new Array<[vscode.Uri, vscode.Diagnostic[]]>();
723-
724-
this._allBuildDiagnostics.forEach((fileDiagnostics) => {
725-
const uri = fileDiagnostics[0];
726-
const diagnosticList = fileDiagnostics[1];
727-
728-
// Check if we have live diagnostics shown for this document
729-
const liveDiagnostics = vscode.languages.getDiagnostics(uri);
730-
if (liveDiagnostics && liveDiagnostics.length > 0) {
731-
// Show the build-only diagnostics
732-
displayedBuildDiagnostics.push([uri, this._getBuildOnlyDiagnostics(diagnosticList, buildOnlyIds)]);
733-
} else {
734-
// Document isn't shown in live diagnostics, so display everything reported by the build
735-
displayedBuildDiagnostics.push(fileDiagnostics);
736-
}
737-
});
738-
739-
this._diagnosticsReportedByBuild.set(displayedBuildDiagnostics);
740-
}
741-
742-
private compareUri(a: vscode.Uri, b: vscode.Uri): boolean {
743-
return a.path.localeCompare(b.path, undefined, { sensitivity: 'accent' }) === 0;
744-
}
745-
746-
private async _onFileOpened(document: vscode.TextDocument) {
747-
const uri = document.uri;
748-
const currentFileBuildDiagnostics = this._allBuildDiagnostics?.find(([u]) => this.compareUri(u, uri));
749-
750-
// The document is now open in the editor and live diagnostics are being shown. Filter diagnostics
751-
// reported by the build to show build-only problems.
752-
if (currentFileBuildDiagnostics) {
753-
const buildIds = await this.getBuildOnlyDiagnosticIds(CancellationToken.None);
754-
const buildDiagnostics = this._getBuildOnlyDiagnostics(currentFileBuildDiagnostics[1], buildIds);
755-
this._diagnosticsReportedByBuild.set(uri, buildDiagnostics);
756-
}
757-
}
758-
759-
private _getBuildOnlyDiagnostics(diagnosticList: vscode.Diagnostic[], buildOnlyIds: string[]): vscode.Diagnostic[] {
760-
const buildOnlyDiagnostics: vscode.Diagnostic[] = [];
761-
diagnosticList.forEach((d) => {
762-
if (d.code) {
763-
// Include diagnostic in the list if it is build
764-
if (buildOnlyIds.find((b_id) => b_id === d.code)) {
765-
buildOnlyDiagnostics.push(d);
766-
}
767-
}
768-
});
769-
770-
return buildOnlyDiagnostics;
771-
}
772-
773715
private registerExtensionsChanged() {
774716
// subscribe to extension change events so that we can get notified if C# Dev Kit is added/removed later.
775717
this._languageClient.addDisposable(

src/lsptoolshost/services/buildResultReporterService.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
5-
import { CancellationToken, Diagnostic, Uri } from 'vscode';
5+
import { Diagnostic, Uri } from 'vscode';
66
import { RoslynLanguageServer } from '../roslynLanguageServer';
7+
import { CancellationToken } from 'vscode-jsonrpc';
78

89
interface IBuildResultDiagnostics {
910
buildStarted(cancellationToken?: CancellationToken): Promise<void>;
@@ -18,11 +19,12 @@ export class BuildResultDiagnostics implements IBuildResultDiagnostics {
1819

1920
public async buildStarted(): Promise<void> {
2021
const langServer = await this._languageServerPromise;
21-
langServer.clearDiagnostics();
22+
langServer._buildDiagnosticService.clearDiagnostics();
2223
}
2324

2425
public async reportBuildResult(buildDiagnostics: Array<[Uri, Diagnostic[]]>): Promise<void> {
2526
const langServer = await this._languageServerPromise;
26-
langServer.setBuildDiagnostics(buildDiagnostics);
27+
const buildOnlyIds = await langServer.getBuildOnlyDiagnosticIds(CancellationToken.None);
28+
langServer._buildDiagnosticService.setBuildDiagnostics(buildDiagnostics, buildOnlyIds);
2729
}
2830
}

0 commit comments

Comments
 (0)