diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index a9b2502387d900489768e8f6a95cf5450218345d..4480ccd51413741caa8597dae0ed1623a24da625 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -272,6 +272,12 @@ public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancel private static bool AnalysisEnabled(Document document) { + if (document.Services.GetService()?.DiagnosticsLspClientName != null) + { + // This is a generated Razor document, and they want diagnostics, so let's report it + return true; + } + // change it to check active file (or visible files), not open files if active file tracking is enabled. // otherwise, use open file. return document.IsOpen() && document.SupportsDiagnostics(); diff --git a/src/VisualStudio/Core/Def/Implementation/LanguageClient/InProcLanguageServer.cs b/src/VisualStudio/Core/Def/Implementation/LanguageClient/InProcLanguageServer.cs index 42865230347e65324839dd7f324c9a962d50ebdd..3ab723d38d1e074b7fe24f5aaaa8c12ba4530590 100644 --- a/src/VisualStudio/Core/Def/Implementation/LanguageClient/InProcLanguageServer.cs +++ b/src/VisualStudio/Core/Def/Implementation/LanguageClient/InProcLanguageServer.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -32,6 +33,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService internal class InProcLanguageServer { private readonly IDiagnosticService _diagnosticService; + private readonly string? _clientName; private readonly JsonRpc _jsonRpc; private readonly LanguageServerProtocol _protocol; private readonly Workspace _workspace; @@ -39,7 +41,7 @@ internal class InProcLanguageServer private VSClientCapabilities? _clientCapabilities; public InProcLanguageServer(Stream inputStream, Stream outputStream, LanguageServerProtocol protocol, - Workspace workspace, IDiagnosticService diagnosticService) + Workspace workspace, IDiagnosticService diagnosticService, string? clientName) { _protocol = protocol; _workspace = workspace; @@ -48,6 +50,7 @@ internal class InProcLanguageServer _jsonRpc.StartListening(); _diagnosticService = diagnosticService; + _clientName = clientName; _diagnosticService.DiagnosticsUpdated += DiagnosticService_DiagnosticsUpdated; } @@ -237,7 +240,9 @@ protected virtual async Task PublishDiagnosticsAsync(Document document) private async Task> GetDiagnosticsAsync(Document document, CancellationToken cancellationToken) { - var diagnostics = _diagnosticService.GetDiagnostics(document.Project.Solution.Workspace, document.Project.Id, document.Id, null, false, cancellationToken); + var diagnostics = _diagnosticService.GetDiagnostics(document.Project.Solution.Workspace, document.Project.Id, document.Id, null, false, cancellationToken) + .Where(IncludeDiagnostic); + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); // Razor documents can import other razor documents @@ -274,6 +279,18 @@ static LanguageServer.Protocol.Diagnostic ConvertToLspDiagnostic(DiagnosticData } } + private bool IncludeDiagnostic(DiagnosticData diagnostic) + { + if (!diagnostic.Properties.TryGetValue(nameof(DocumentPropertiesService.DiagnosticsLspClientName), out var diagnosticClientName)) + { + // This diagnostic is not restricted to a specific LSP client, so just pass it through + return true; + } + + // We only include this diagnostic if it directly matches our name. + return diagnosticClientName == _clientName; + } + private static LanguageServer.Protocol.Range? GetDiagnosticRange(DiagnosticDataLocation? diagnosticDataLocation, SourceText text) { (var startLine, var endLine) = DiagnosticData.GetLinePositions(diagnosticDataLocation, text, useMapped: true); diff --git a/src/VisualStudio/Core/Def/Implementation/LanguageClient/LiveShareLanguageServerClient.cs b/src/VisualStudio/Core/Def/Implementation/LanguageClient/LiveShareLanguageServerClient.cs index afae480a095d732f0a565b194372cdfec54d87c8..41cf11571358ba1e158eb13a7cfbc357dcb9ccea 100644 --- a/src/VisualStudio/Core/Def/Implementation/LanguageClient/LiveShareLanguageServerClient.cs +++ b/src/VisualStudio/Core/Def/Implementation/LanguageClient/LiveShareLanguageServerClient.cs @@ -74,7 +74,7 @@ public LiveShareLanguageServerClient(LanguageServerProtocol languageServerProtoc public Task ActivateAsync(CancellationToken token) { var (clientStream, serverStream) = FullDuplexStream.CreatePair(); - _ = new InProcLanguageServer(serverStream, serverStream, _languageServerProtocol, _workspace, _diagnosticService); + _ = new InProcLanguageServer(serverStream, serverStream, _languageServerProtocol, _workspace, _diagnosticService, clientName: null); return Task.FromResult(new Connection(clientStream, clientStream)); } diff --git a/src/VisualStudio/Core/Def/Implementation/LanguageClient/RazorLanguageClient.cs b/src/VisualStudio/Core/Def/Implementation/LanguageClient/RazorLanguageClient.cs index 0cf10a3119dff545bb7955d44d8b4297bcf87c50..1e757114568a7072fb8b141ac93803572ec4f414 100644 --- a/src/VisualStudio/Core/Def/Implementation/LanguageClient/RazorLanguageClient.cs +++ b/src/VisualStudio/Core/Def/Implementation/LanguageClient/RazorLanguageClient.cs @@ -29,7 +29,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Lsp /// [ContentType(ContentTypeNames.CSharpLspContentTypeName)] [ContentType(ContentTypeNames.VBLspContentTypeName)] - [ClientName("RazorCSharp")] + [ClientName(ClientName)] [Export(typeof(ILanguageClient))] class RazorLanguageClient : ILanguageClient { @@ -37,6 +37,8 @@ class RazorLanguageClient : ILanguageClient private readonly LanguageServerProtocol _languageServerProtocol; private readonly Workspace _workspace; + public const string ClientName = "RazorCSharp"; + /// /// Gets the name of the language client (displayed to the user). /// diff --git a/src/VisualStudio/Core/Def/Implementation/LanguageClient/RazorLanguageServer.cs b/src/VisualStudio/Core/Def/Implementation/LanguageClient/RazorLanguageServer.cs index e74b601ea8a33d4efced551fc8c431acc8871caa..0f7115b9ab71eec0a192cf73f1ba15dc19b69ae6 100644 --- a/src/VisualStudio/Core/Def/Implementation/LanguageClient/RazorLanguageServer.cs +++ b/src/VisualStudio/Core/Def/Implementation/LanguageClient/RazorLanguageServer.cs @@ -4,11 +4,7 @@ #nullable enable -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageServer; @@ -19,7 +15,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Lsp class RazorLanguageServer : InProcLanguageServer { public RazorLanguageServer(Stream inputStream, Stream outputStream, LanguageServerProtocol protocol, Workspace workspace, IDiagnosticService diagnosticService) - : base(inputStream, outputStream, protocol, workspace, diagnosticService) + : base(inputStream, outputStream, protocol, workspace, diagnosticService, clientName: RazorLanguageClient.ClientName) { } diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs index 7556505fafc04585dd4f050ccda92fd84cb53bcb..ab99c17065e3726cddba564a9e7d904225ee6cf4 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Common; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Imaging.Interop; @@ -188,6 +189,12 @@ private static bool ShouldInclude(DiagnosticData diagnostic) return false; } + // If this diagnostic is for LSP only, then we won't show it here + if (diagnostic.Properties.ContainsKey(nameof(DocumentPropertiesService.DiagnosticsLspClientName))) + { + return false; + } + switch (diagnostic.Severity) { case DiagnosticSeverity.Info: diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs index bcfb871f8b7fc609185ef13f286fc4fc8e638f54..7e0389449160d8537f6402b6c8846cfc88865355 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs @@ -13,6 +13,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -348,13 +349,28 @@ public static DiagnosticData Create(Diagnostic diagnostic, Document document) .WhereNotNull() .ToReadOnlyCollection(); + var additionalProperties = GetAdditionalProperties(document, diagnostic); + + var documentPropertiesService = document.Services.GetService(); + var diagnosticsLspClientName = documentPropertiesService?.DiagnosticsLspClientName; + + if (diagnosticsLspClientName != null) + { + if (additionalProperties == null) + { + additionalProperties = ImmutableDictionary.Create(); + } + + additionalProperties = additionalProperties.Add(nameof(documentPropertiesService.DiagnosticsLspClientName), diagnosticsLspClientName); + } + return Create(diagnostic, project.Id, project.Language, project.Solution.Options, location, additionalLocations, - GetAdditionalProperties(document, diagnostic)); + additionalProperties); } private static DiagnosticData Create( diff --git a/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/DocumentPropertiesService.cs b/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/DocumentPropertiesService.cs index dae3f1d6756be8f40fffb99caf4449729729c8df..2bfefb2f75521ff0c1bdab8802fe353477a97d5f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/DocumentPropertiesService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/DocumentPropertiesService.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.CodeAnalysis.Host { /// @@ -16,5 +18,11 @@ internal class DocumentPropertiesService : IDocumentService /// but is not passed to the compiler when the containing project is built. /// public virtual bool DesignTimeOnly => false; + + /// + /// The LSP client name that should get the diagnostics produced by this document; any other source + /// will not show these diagnostics. If null, the diagnostics do not have this special handling. + /// + public virtual string? DiagnosticsLspClientName => null; } }