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
4 changes: 4 additions & 0 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>275e691f7e575f208290d1cbb8cb450f4a3a85d6</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Json" Version="5.0.0-alpha.1.20079.1">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>275e691f7e575f208290d1cbb8cb450f4a3a85d6</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.HashCodeCombiner.Sources" Version="5.0.0-alpha.1.20079.1">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>275e691f7e575f208290d1cbb8cb450f4a3a85d6</Sha>
Expand Down
1 change: 1 addition & 0 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>5.0.0-alpha.1.20079.1</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>5.0.0-alpha.1.20079.1</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>5.0.0-alpha.1.20079.1</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>5.0.0-alpha.1.20079.1</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsDependencyModelPackageVersion>5.0.0-alpha.1.20078.2</MicrosoftExtensionsDependencyModelPackageVersion>
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>5.0.0-alpha.1.20079.1</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>5.0.0-alpha.1.20079.1</MicrosoftExtensionsLoggingPackageVersion>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;

namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class DefaultRazorConfigurationService : RazorConfigurationService
{
private readonly ILanguageServer _server;
private readonly ILogger _logger;

public DefaultRazorConfigurationService(ILanguageServer languageServer, ILoggerFactory loggerFactory)
{
if (languageServer is null)
{
throw new ArgumentNullException(nameof(languageServer));
}

if (loggerFactory is null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}

_server = languageServer;
_logger = loggerFactory.CreateLogger<DefaultRazorConfigurationService>();
}

public async override Task<RazorLSPOptions> GetLatestOptionsAsync()
{
try
{
var request = new ConfigurationParams()
{
Items = new[]
{
new ConfigurationItem()
{
Section = "razor"
},
}
};

var result = await _server.Client.SendRequest<ConfigurationParams, object[]>("workspace/configuration", request);
if (result == null || result.Length < 1 || result[0] == null)
{
_logger.LogWarning("Client failed to provide the expected configuration.");
return null;
}

var jsonString = result[0].ToString();
var builder = new ConfigurationBuilder();
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
builder.AddJsonStream(stream);
var config = builder.Build();

var instance = BuildOptions(config);
return instance;
}
catch (Exception ex)
{
_logger.LogWarning($"Failed to sync client configuration on the server: {ex}");
return null;
}
}

private RazorLSPOptions BuildOptions(IConfiguration config)
{
var instance = RazorLSPOptions.Default;

Enum.TryParse(config["trace"], out Trace trace);

var enableFormatting = instance.EnableFormatting;
if (bool.TryParse(config["format:enable"], out var parsedEnableFormatting))
{
enableFormatting = parsedEnableFormatting;
}

return new RazorLSPOptions(trace, enableFormatting);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
<PackageReference Include="OmniSharp.Extensions.LanguageServer" Version="$(OmniSharpExtensionsLanguageServerPackageVersion)" />
</ItemGroup>

Expand Down
25 changes: 18 additions & 7 deletions src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.VisualStudio.Editor.Razor;
using OmniSharp.Extensions.JsonRpc.Serialization.Converters;
using OmniSharp.Extensions.LanguageServer.Protocol.Serialization;
Expand All @@ -34,7 +35,7 @@ public static void Main(string[] args)

public static async Task MainAsync(string[] args)
{
var logLevel = LogLevel.Information;
var trace = Trace.Messages;
for (var i = 0; i < args.Length; i++)
{
if (args[i].IndexOf("debug", StringComparison.OrdinalIgnoreCase) >= 0)
Expand All @@ -48,13 +49,13 @@ public static async Task MainAsync(string[] args)
continue;
}

if (args[i] == "--logLevel" && i + 1 < args.Length)
if (args[i] == "--trace" && i + 1 < args.Length)
{
var logLevelString = args[++i];
if (!Enum.TryParse(logLevelString, out logLevel))
var traceArg = args[++i];
if (!Enum.TryParse(traceArg, out trace))
{
logLevel = LogLevel.Information;
Console.WriteLine($"Invalid log level '{logLevelString}'. Defaulting to {logLevel.ToString()}.");
trace = Trace.Messages;
Console.WriteLine($"Invalid Razor trace '{traceArg}'. Defaulting to {trace.ToString()}.");
}
}
}
Expand All @@ -69,7 +70,7 @@ public static async Task MainAsync(string[] args)
.WithOutput(Console.OpenStandardOutput())
.ConfigureLogging(builder => builder
.AddLanguageServer()
.SetMinimumLevel(logLevel))
.SetMinimumLevel(RazorLSPOptions.GetLogLevelForTrace(trace)))
.OnInitialized(async (languageServer, request, response) =>
{
var fileChangeDetectorManager = languageServer.Services.GetRequiredService<RazorFileChangeDetectorManager>();
Expand All @@ -79,6 +80,7 @@ public static async Task MainAsync(string[] args)
.WithHandler<RazorCompletionEndpoint>()
.WithHandler<RazorHoverEndpoint>()
.WithHandler<RazorLanguageEndpoint>()
.WithHandler<RazorConfigurationEndpoint>()
.WithServices(services =>
{
services.AddSingleton<RemoteTextLoaderFactory, DefaultRemoteTextLoaderFactory>();
Expand All @@ -89,6 +91,11 @@ public static async Task MainAsync(string[] args)
services.AddSingleton<ProjectSnapshotChangeTrigger, BackgroundDocumentGenerator>();
services.AddSingleton<RazorFileChangeDetectorManager>();

// Options
services.AddSingleton<RazorConfigurationService, DefaultRazorConfigurationService>();
services.AddSingleton<RazorLSPOptionsMonitor>();
services.AddSingleton<IOptionsMonitor<RazorLSPOptions>, RazorLSPOptionsMonitor>();

// File change listeners
services.AddSingleton<IProjectConfigurationFileChangeListener, ProjectConfigurationStateSynchronizer>();
services.AddSingleton<IProjectFileChangeListener, ProjectFileSynchronizer>();
Expand Down Expand Up @@ -140,6 +147,10 @@ public static async Task MainAsync(string[] args)
// Workaround for https://github.com/OmniSharp/csharp-language-server-protocol/issues/106
var languageServer = (OmniSharp.Extensions.LanguageServer.Server.LanguageServer)server;

// Initialize our options for the first time.
var optionsMonitor = languageServer.Services.GetRequiredService<RazorLSPOptionsMonitor>();
await optionsMonitor.UpdateAsync();

try
{
var factory = new LoggerFactory();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;

namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class RazorConfigurationEndpoint : IDidChangeConfigurationHandler
{
private readonly RazorLSPOptionsMonitor _optionsMonitor;
private readonly ILogger _logger;
private DidChangeConfigurationCapability _capability;

public RazorConfigurationEndpoint(RazorLSPOptionsMonitor optionsMonitor, ILoggerFactory loggerFactory)
{
if (optionsMonitor is null)
{
throw new ArgumentNullException(nameof(optionsMonitor));
}

if (loggerFactory is null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}

_optionsMonitor = optionsMonitor;
_logger = loggerFactory.CreateLogger<RazorConfigurationEndpoint>();
}

public object GetRegistrationOptions()
{
return new TextDocumentRegistrationOptions()
{
DocumentSelector = RazorDefaults.Selector
};
}

public async Task<Unit> Handle(DidChangeConfigurationParams request, CancellationToken cancellationToken)
{
_logger.LogTrace("Settings changed. Updating the server.");

await _optionsMonitor.UpdateAsync();

return new Unit();
}

public void SetCapability(DidChangeConfigurationCapability capability)
{
_capability = capability;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Threading.Tasks;

namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal abstract class RazorConfigurationService
{
public abstract Task<RazorLSPOptions> GetLatestOptionsAsync();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Logging;

namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class RazorLSPOptions : IEquatable<RazorLSPOptions>
{
public RazorLSPOptions(Trace trace, bool enableFormatting)
{
Trace = trace;
EnableFormatting = enableFormatting;
}

public static RazorLSPOptions Default => new RazorLSPOptions(trace: default, enableFormatting: true);

public Trace Trace { get; }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm should this class be immutable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be but we will no longer be able to do things like

services.Configure<RazorLSPOptions>(opt => opt.EnableFormatting = false);

Copy link
Contributor Author

@ajaybhargavb ajaybhargavb Jan 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, our IOptionsMonitor implementation doesn't care about IConfigureOptions, so it doesn't matter. I'll make it immutable for now.

public LogLevel MinLogLevel => GetLogLevelForTrace(Trace);

public bool EnableFormatting { get; }

public static LogLevel GetLogLevelForTrace(Trace trace)
{
return trace switch

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pranav points!!!!!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a juicy syntax.

{
Trace.Off => LogLevel.None,
Trace.Messages => LogLevel.Information,
Trace.Verbose => LogLevel.Trace,
_ => LogLevel.None,
};
}

public bool Equals([AllowNull] RazorLSPOptions other)
{
return
other != null &&
Trace == other.Trace &&
EnableFormatting == other.EnableFormatting;
}

public override bool Equals(object obj)
{
return Equals(obj as RazorLSPOptions);
}

public override int GetHashCode()
{
var hash = new HashCodeCombiner();
hash.Add(Trace);
hash.Add(EnableFormatting);
return hash;
}
}
}
Loading