File: NewtonsoftLanguageServer.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.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using StreamJsonRpc;
 
namespace Microsoft.CommonLanguageServerProtocol.Framework;
 
/// <summary>
/// Basic implementation of <see cref="AbstractLanguageServer{TRequestContext}"/> using Newtonsoft for serialization.
/// </summary>
internal abstract class NewtonsoftLanguageServer<TRequestContext>(
    JsonRpc jsonRpc, JsonSerializer jsonSerializer, ILspLogger logger, AbstractTypeRefResolver? typeRefResolver = null)
    : AbstractLanguageServer<TRequestContext>(jsonRpc, logger, typeRefResolver)
{
    private readonly JsonSerializer _jsonSerializer = jsonSerializer;
 
    public override TRequest DeserializeRequest<TRequest>(object? serializedRequest, RequestHandlerMetadata metadata)
    {
        var requestTypeRef = metadata.RequestTypeRef;
 
        if (serializedRequest is null)
        {
            if (requestTypeRef is not null)
            {
                throw new InvalidOperationException($"Handler {metadata.HandlerDescription} requires request parameters but received none");
            }
 
            // We checked that TRequest is typeof(NoValue).
            return (TRequest)(object)NoValue.Instance;
        }
 
        // request is not null
        if (requestTypeRef is null)
        {
            throw new InvalidOperationException($"Handler {metadata.HandlerDescription} does not accept parameters, but received some.");
        }
 
        var request = (JToken)serializedRequest;
 
        return request.ToObject<TRequest>(_jsonSerializer)
            ?? throw new InvalidOperationException($"Unable to deserialize {request} into {requestTypeRef} for {metadata.HandlerDescription}");
    }
 
    protected override DelegatingEntryPoint CreateDelegatingEntryPoint(string method)
    {
        return new NewtonsoftDelegatingEntryPoint(method, this);
    }
 
    private class NewtonsoftDelegatingEntryPoint(
        string method,
        NewtonsoftLanguageServer<TRequestContext> target) : DelegatingEntryPoint(method)
    {
        private static readonly MethodInfo s_entryPoint = typeof(NewtonsoftDelegatingEntryPoint).GetMethod(nameof(NewtonsoftDelegatingEntryPoint.ExecuteRequestAsync), BindingFlags.NonPublic | BindingFlags.Instance)!;
 
        public override MethodInfo GetEntryPoint(bool hasParameter)
        {
            return s_entryPoint;
        }
 
        /// <summary>
        /// StreamJsonRpc entry point for all handler methods.
        /// The optional parameters allow StreamJsonRpc to call into the same method for any kind of request / notification (with any number of params or response).
        /// </summary>
        private async Task<JToken?> ExecuteRequestAsync(JToken? request = null, 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;
            }
 
            return JToken.FromObject(result, target._jsonSerializer);
        }
    }
}