File: SystemTextJsonLanguageServer.cs
Web Access
Project: src\src\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework\Microsoft.CommonLanguageServerProtocol.Framework.Package.csproj (Microsoft.CommonLanguageServerProtocol.Framework.Package)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
// This is consumed as 'generated' code in a source package and therefore requires an explicit nullable enable
#nullable enable
 
using System;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using StreamJsonRpc;
 
namespace Microsoft.CommonLanguageServerProtocol.Framework;
 
internal abstract class SystemTextJsonLanguageServer<TRequestContext>(
    JsonRpc jsonRpc, JsonSerializerOptions options, ILspLogger logger, AbstractTypeRefResolver? typeRefResolver = null)
    : AbstractLanguageServer<TRequestContext>(jsonRpc, logger, typeRefResolver)
{
    /// <summary>
    /// JsonSerializer options used by streamjsonrpc (and for serializing / deserializing the requests to streamjsonrpc).
    /// These options are specifically from the <see cref="StreamJsonRpc.SystemTextJsonFormatter"/> that added the exotic type converters.
    /// </summary>
    private readonly JsonSerializerOptions _jsonSerializerOptions = options;
 
    public override TRequest DeserializeRequest<TRequest>(object? serializedRequest, RequestHandlerMetadata metadata)
    {
        if (serializedRequest is null)
        {
            if (metadata.RequestTypeRef is not null)
            {
                throw new InvalidOperationException($"Handler {metadata.HandlerDescription} requires request parameters but received none");
            }
            else
            {
                // We checked that TRequest is typeof(NoValue).
                return (TRequest)(object)NoValue.Instance;
            }
        }
 
        if (metadata.RequestTypeRef is null)
        {
            throw new InvalidOperationException($"Handler {metadata.HandlerDescription} does not accept parameters, but received some.");
        }
 
        var request = (JsonElement)serializedRequest;
 
        return JsonSerializer.Deserialize<TRequest>(request, _jsonSerializerOptions)
            ?? throw new InvalidOperationException($"Unable to deserialize {request} into {typeof(TRequest)} for {metadata.HandlerDescription}");
    }
 
    protected override DelegatingEntryPoint CreateDelegatingEntryPoint(string method)
    {
        return new SystemTextJsonDelegatingEntryPoint(method, this);
    }
 
    private sealed class SystemTextJsonDelegatingEntryPoint(
        string method,
        SystemTextJsonLanguageServer<TRequestContext> target) : DelegatingEntryPoint(method)
    {
        private static readonly MethodInfo s_parameterlessEntryPoint = typeof(SystemTextJsonDelegatingEntryPoint).GetMethod(nameof(SystemTextJsonDelegatingEntryPoint.ExecuteRequest0Async), BindingFlags.NonPublic | BindingFlags.Instance)!;
        private static readonly MethodInfo s_entryPoint = typeof(SystemTextJsonDelegatingEntryPoint).GetMethod(nameof(SystemTextJsonDelegatingEntryPoint.ExecuteRequestAsync), BindingFlags.NonPublic | BindingFlags.Instance)!;
 
        public override MethodInfo GetEntryPoint(bool hasParameter)
        {
            return hasParameter ? s_entryPoint : s_parameterlessEntryPoint;
        }
 
        /// <summary>
        /// StreamJsonRpc entry point for handlers with no parameters.
        /// Unlike Newtonsoft, we have to differentiate instead of using default parameters.
        /// </summary>
        private Task<JsonElement?> ExecuteRequest0Async(CancellationToken cancellationToken = default)
        {
            return ExecuteRequestAsync(null, cancellationToken);
        }
 
        /// <summary>
        /// StreamJsonRpc entry point for handlers with parameters (and any response) type.
        /// </summary>
        private async Task<JsonElement?> ExecuteRequestAsync(JsonElement? request, CancellationToken cancellationToken = default)
        {
            var queue = target.GetRequestExecutionQueue();
            var lspServices = target.GetLspServices();
 
            var result = await InvokeAsync(queue, request, lspServices, cancellationToken).ConfigureAwait(false);
            if (result is null)
            {
                return null;
            }
 
            var serializedResult = JsonSerializer.SerializeToElement(result, target._jsonSerializerOptions);
            return serializedResult;
        }
    }
}