提交 1ab4589b 编写于 作者: J Jason Malinowski 提交者: David Barbet

Allow a IDynamicFileInfoProvider to be able to provide LSP diagnostics

ASP.NET Razor is prototyping a system where they can add files into
our workspace via IDynamicFileInfoProvider and can then query our LSP
server to get diagnostics about those files. They're normally closed
files but we want them to get analyzed always if they want to query
them via LSP, and we also don't want those diagnostics to end up in the
error list since further processing needs to be done with them with
LSP.

This implements a hook where Razor can specify that diagnostics produced
for their documents are ingored by most of the system, but can still
be queried through their special LSP server.
上级 2630251a
......@@ -272,6 +272,12 @@ public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancel
private static bool AnalysisEnabled(Document document)
{
if (document.Services.GetService<DocumentPropertiesService>()?.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();
......
......@@ -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<Dictionary<Uri, LanguageServer.Protocol.Diagnostic[]>> 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);
......
......@@ -74,7 +74,7 @@ public LiveShareLanguageServerClient(LanguageServerProtocol languageServerProtoc
public Task<Connection> 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));
}
......
......@@ -29,7 +29,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Lsp
/// </summary>
[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";
/// <summary>
/// Gets the name of the language client (displayed to the user).
/// </summary>
......
......@@ -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)
{
}
......
......@@ -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:
......
......@@ -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<DocumentPropertiesService>();
var diagnosticsLspClientName = documentPropertiesService?.DiagnosticsLspClientName;
if (diagnosticsLspClientName != null)
{
if (additionalProperties == null)
{
additionalProperties = ImmutableDictionary.Create<string, string>();
}
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(
......
......@@ -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
{
/// <summary>
......@@ -16,5 +18,11 @@ internal class DocumentPropertiesService : IDocumentService
/// but is not passed to the compiler when the containing project is built.
/// </summary>
public virtual bool DesignTimeOnly => false;
/// <summary>
/// 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.
/// </summary>
public virtual string? DiagnosticsLspClientName => null;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册