Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
using Microsoft.AspNetCore.Razor;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features;

namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;

#pragma warning disable RS0030 // Do not use banned APIs
[Shared]
[CohostEndpoint(Methods.TextDocumentColorPresentationName)]
[ExportCohostStatelessLspService(typeof(CohostColorPresentationEndpoint))]
[ExportRazorStatelessLspService(typeof(CohostColorPresentationEndpoint))]
[method: ImportingConstructor]
#pragma warning restore RS0030 // Do not use banned APIs
internal sealed class CohostColorPresentationEndpoint(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
using Microsoft.AspNetCore.Razor;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features;

namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;

#pragma warning disable RS0030 // Do not use banned APIs
[Shared]
[CohostEndpoint(Methods.TextDocumentDocumentColorName)]
[Export(typeof(IDynamicRegistrationProvider))]
[ExportCohostStatelessLspService(typeof(CohostDocumentColorEndpoint))]
[ExportRazorStatelessLspService(typeof(CohostDocumentColorEndpoint))]
[method: ImportingConstructor]
#pragma warning restore RS0030 // Do not use banned APIs
internal sealed class CohostDocumentColorEndpoint(
Expand All @@ -30,16 +31,11 @@ internal sealed class CohostDocumentColorEndpoint(

public ImmutableArray<Registration> GetRegistrations(VSInternalClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext)
{
if (clientCapabilities.SupportsVisualStudioExtensions)
return [new Registration
{
return [new Registration
{
Method = Methods.TextDocumentDocumentColorName,
RegisterOptions = new DocumentColorRegistrationOptions()
}];
}

return [];
Method = Methods.TextDocumentDocumentColorName,
RegisterOptions = new DocumentColorRegistrationOptions()
}];
}

protected override RazorTextDocumentIdentifier? GetRazorTextDocumentIdentifier(DocumentColorParams request)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor;
using Microsoft.CodeAnalysis;

namespace Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
Expand Down Expand Up @@ -33,7 +34,7 @@ private void Start(TextDocument document, Func<TextDocument, CancellationToken,
_cts.Token.Register(Dispose);
_ = syncFunction.Invoke(document, _cts.Token).ContinueWith((t, state) =>
{
var tcs = (TaskCompletionSource<bool>)state;
var tcs = (TaskCompletionSource<bool>)state.AssumeNotNull();
if (t.IsCanceled)
{
tcs.SetResult(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,16 @@
<Compile Include="$(MSBuildThisFileDirectory)CohostEndpointAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CohostDocSyncEndpointRegistration.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CohostStartupService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DocumentColor\CohostColorPresentationEndpoint.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DocumentColor\CohostDocumentColorEndpoint.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DocumentSymbol\CohostDocumentSymbolEndpoint.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HtmlDocumentServices\HtmlDocumentSynchronizer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HtmlDocumentServices\HtmlDocumentSynchronizer.RazorDocumentVersion.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HtmlDocumentServices\HtmlDocumentSynchronizer.SynchronizationRequest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HtmlDocumentServices\IHtmlDocumentPublisher.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HtmlDocumentServices\IHtmlDocumentSynchronizer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HtmlDocumentServices\HtmlRequestInvokerExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)HtmlDocumentServices\IHtmlRequestInvoker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)InlayHints\CohostInlayHintEndpoint.cs" />
<Compile Include="$(MSBuildThisFileDirectory)InlayHints\CohostInlayHintResolveEndpoint.cs" />
<Compile Include="$(MSBuildThisFileDirectory)IRazorCohostStartupService.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System.ComponentModel.Composition;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.LanguageServer;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.VisualStudio.Razor.LanguageClient.Cohost;

namespace Microsoft.VisualStudioCode.RazorExtension.Services;

[Export(typeof(IHtmlDocumentPublisher))]
[method: ImportingConstructor]
internal sealed class HtmlDocumentPublisher(
RazorClientServerManagerProvider razorClientServerManagerProvider) : IHtmlDocumentPublisher
{
private readonly RazorClientServerManagerProvider _razorClientServerManagerProvider = razorClientServerManagerProvider;

public async Task PublishAsync(TextDocument document, string htmlText, CancellationToken cancellationToken)
{
var request = new HtmlUpdateParameters(new TextDocumentIdentifier { Uri = document.CreateUri() }, htmlText);

var clientConnection = _razorClientServerManagerProvider.ClientLanguageServerManager.AssumeNotNull();
await clientConnection.SendRequestAsync("razor/updateHtml", request, cancellationToken).ConfigureAwait(false);
}

private record HtmlUpdateParameters(
[property: JsonPropertyName("textDocument")]
TextDocumentIdentifier TextDocument,
[property: JsonPropertyName("text")]
string Text);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.ComponentModel.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.LanguageServer;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.VisualStudio.Razor.LanguageClient.Cohost;

namespace Microsoft.VisualStudioCode.RazorExtension.Services;

[Export(typeof(IHtmlRequestInvoker))]
[method: ImportingConstructor]
internal sealed class HtmlRequestInvoker(
RazorClientServerManagerProvider razorClientServerManagerProvider,
IHtmlDocumentSynchronizer htmlDocumentSynchronizer,
ILoggerFactory loggerFactory) : IHtmlRequestInvoker
{
private readonly RazorClientServerManagerProvider _razorClientServerManagerProvider = razorClientServerManagerProvider;
private readonly IHtmlDocumentSynchronizer _htmlDocumentSynchronizer = htmlDocumentSynchronizer;
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<HtmlRequestInvoker>();

public async Task<TResponse?> MakeHtmlLspRequestAsync<TRequest, TResponse>(TextDocument razorDocument, string method, TRequest request, TimeSpan threshold, Guid correlationId, CancellationToken cancellationToken) where TRequest : notnull
{
var syncResult = await _htmlDocumentSynchronizer.TrySynchronizeAsync(razorDocument, cancellationToken).ConfigureAwait(false);
if (!syncResult)
{
return default;
}

_logger.LogDebug($"Making Html request for {method} on {razorDocument.FilePath}");

var clientConnection = _razorClientServerManagerProvider.ClientLanguageServerManager.AssumeNotNull();
return await clientConnection.SendRequestAsync<TRequest, TResponse>(method, request, cancellationToken).ConfigureAwait(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.Composition;
Expand All @@ -33,6 +32,15 @@ public sealed partial class TestComposition
.AddAssemblies(Assembly.LoadFrom("Microsoft.CodeAnalysis.LanguageServer.Protocol.dll"))
.AddParts(typeof(RazorTestWorkspaceRegistrationService));

public static readonly TestComposition RoslynFeatures = Empty
.AddAssemblies(MefHostServices.DefaultAssemblies)
.AddAssemblies(Assembly.LoadFrom("Microsoft.CodeAnalysis.dll"))
.AddAssemblies(Assembly.LoadFrom("Microsoft.CodeAnalysis.CSharp.Features.dll"))
.AddAssemblies(Assembly.LoadFrom("Microsoft.CodeAnalysis.Features.dll"))
.AddAssemblies(Assembly.LoadFrom("Microsoft.CodeAnalysis.ExternalAccess.Razor.Features.dll"))
.AddAssemblies(Assembly.LoadFrom("Microsoft.CodeAnalysis.LanguageServer.Protocol.dll"))
.AddParts(typeof(RazorTestWorkspaceRegistrationService));

#if NETFRAMEWORK
public static readonly TestComposition Editor = Empty
.AddAssemblies(Assembly.LoadFrom("Microsoft.VisualStudio.Text.Implementation.dll"))
Expand Down Expand Up @@ -223,21 +231,16 @@ public TestComposition WithExcludedPartTypes(ImmutableHashSet<Type> excludedPart
/// Use for VS MEF composition troubleshooting.
/// </summary>
/// <returns>All composition error messages.</returns>
internal string GetCompositionErrorLog()
public IEnumerable<string> GetCompositionErrors()
{
var configuration = CompositionConfiguration.Create(GetCatalog());

using var _ = StringBuilderPool.GetPooledObject(out var sb);

foreach (var errorGroup in configuration.CompositionErrors)
{
foreach (var error in errorGroup)
{
sb.Append(error.Message);
sb.AppendLine();
yield return error.Message;
}
}

return sb.ToString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System.Linq;
using Microsoft.AspNetCore.Razor.Test.Common;
using Microsoft.AspNetCore.Razor.Test.Common.Mef;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.VisualStudioCode.RazorExtension.Test;

public class MEFCompositionTest(ITestOutputHelper testOutputHelper) : ToolingTestBase(testOutputHelper)
{
[Fact]
public void Composes()
{
var testComposition = TestComposition.RoslynFeatures
.AddAssemblies(typeof(RazorVSCodeEndpoint).Assembly);

var errors = testComposition.GetCompositionErrors().Order().ToArray();

// There are known failures that are satisfied by Microsoft.CodeAnalysis.LanguageServer, which we don't reference
Assert.Collection(errors,
e => AssertEx.Equal("""
Microsoft.CodeAnalysis.ExternalAccess.Pythia.PythiaSignatureHelpProvider.ctor(implementation): expected exactly 1 export matching constraints:
Contract name: Microsoft.CodeAnalysis.ExternalAccess.Pythia.Api.IPythiaSignatureHelpProviderImplementation
TypeIdentityName: Microsoft.CodeAnalysis.ExternalAccess.Pythia.Api.IPythiaSignatureHelpProviderImplementation
but found 0.
""", e),
e => AssertEx.Equal("""
Microsoft.VisualStudioCode.RazorExtension.Services.WorkspaceService.ctor(loggerFactory): expected exactly 1 export matching constraints:
Contract name: Microsoft.Extensions.Logging.ILoggerFactory
TypeIdentityName: Microsoft.Extensions.Logging.ILoggerFactory
but found 0.
""", e));
;
}
}