Example #1
0
        public async Task Server_HandleEmptyNotification_Success()
        {
            var testCompletion = new TaskCompletionSource <object>();

            LspConnection clientConnection = await CreateClientConnection();

            LspConnection serverConnection = await CreateServerConnection();

            var clientDispatcher = new LspDispatcher();

            clientDispatcher.HandleEmptyNotification("test", () =>
            {
                Log.LogInformation("Got notification.");

                testCompletion.SetResult(null);
            });
            clientConnection.Connect(clientDispatcher);

            serverConnection.Connect(new LspDispatcher());
            serverConnection.SendEmptyNotification("test");

            await testCompletion.Task;

            serverConnection.Disconnect(flushOutgoing: true);
            clientConnection.Disconnect();

            await Task.WhenAll(clientConnection.HasHasDisconnected, serverConnection.HasHasDisconnected);
        }
        public async Task Server_HandleRequest_Success()
        {
            LspConnection clientConnection = await CreateClientConnection();

            LspConnection serverConnection = await CreateServerConnection();

            var clientDispatcher = new LspDispatcher();

            clientDispatcher.HandleRequest <TestRequest, TestResponse>("test", (request, cancellationToken) =>
            {
                Log.LogInformation("Got request: {@Request}", request);

                return(Task.FromResult(new TestResponse
                {
                    Value = request.Value.ToString()
                }));
            });
            clientConnection.Connect(clientDispatcher);

            serverConnection.Connect(new LspDispatcher());
            TestResponse response = await serverConnection.SendRequest <TestResponse>("test", new TestRequest
            {
                Value = 1234
            });

            Assert.Equal("1234", response.Value);

            Log.LogInformation("Got response: {@Response}", response);

            serverConnection.Disconnect(flushOutgoing: true);
            clientConnection.Disconnect();

            await Task.WhenAll(clientConnection.HasHasDisconnected, serverConnection.HasHasDisconnected);
        }
        public async Task Client_HandleCommandRequest_Success()
        {
            LspConnection clientConnection = await CreateClientConnection();

            LspConnection serverConnection = await CreateServerConnection();

            var serverDispatcher = new LspDispatcher();

            serverDispatcher.HandleRequest <TestRequest>("test", (request, cancellationToken) =>
            {
                Log.LogInformation("Got request: {@Request}", request);

                Assert.Equal(1234, request.Value);

                return(Task.CompletedTask);
            });
            serverConnection.Connect(serverDispatcher);

            clientConnection.Connect(new LspDispatcher());
            await clientConnection.SendRequest("test", new TestRequest
            {
                Value = 1234
            });

            clientConnection.Disconnect(flushOutgoing: true);
            serverConnection.Disconnect();

            await Task.WhenAll(clientConnection.HasHasDisconnected, serverConnection.HasHasDisconnected);
        }
Example #4
0
        /// <summary>
        ///     Close the connection.
        /// </summary>
        /// <param name="flushOutgoing">
        ///     If <c>true</c>, stop receiving and block until all outgoing messages have been sent.
        /// </param>
        public void Disconnect(bool flushOutgoing = false)
        {
            if (flushOutgoing)
            {
                // Stop receiving.
                _incoming.CompleteAdding();

                // Wait for the outgoing message queue to drain.
                int      remainingMessageCount = 0;
                DateTime then = DateTime.Now;
                while (DateTime.Now - then < FlushTimeout)
                {
                    remainingMessageCount = _outgoing.Count;
                    if (remainingMessageCount == 0)
                    {
                        break;
                    }

                    Thread.Sleep(
                        TimeSpan.FromMilliseconds(200)
                        );
                }

                if (remainingMessageCount > 0)
                {
                    Log.LogWarning("Failed to flush outgoing messages ({RemainingMessageCount} messages remaining).", _outgoing.Count);
                }
            }

            // Cancel all outstanding requests.
            // This should not be necessary because request cancellation tokens should be linked to _cancellationSource, but better to be sure we won't leave a caller hanging.
            foreach (TaskCompletionSource <ServerMessage> responseCompletion in _responseCompletions.Values)
            {
                responseCompletion.TrySetException(
                    new OperationCanceledException("The request was canceled because the underlying connection was closed.")
                    );
            }

            try
            {
                _cancellationSource?.Cancel();
            }
            catch (AggregateException e) when(e.InnerException is ObjectDisposedException)
            {
                // Swallow object disposed exception
            }
            _sendLoop     = null;
            _receiveLoop  = null;
            _dispatchLoop = null;
            _dispatcher   = null;
        }
Example #5
0
        /// <summary>
        ///     Register a message handler.
        /// </summary>
        /// <param name="handler">
        ///     The message handler.
        /// </param>
        /// <returns>
        ///     An <see cref="IDisposable"/> representing the registration.
        /// </returns>
        public IDisposable RegisterHandler(IHandler handler)
        {
            if (handler == null)
            {
                throw new ArgumentNullException(nameof(handler));
            }

            LspDispatcher dispatcher = _dispatcher;

            if (dispatcher == null)
            {
                throw new InvalidOperationException("The connection has not been opened.");
            }

            return(dispatcher.RegisterHandler(handler));
        }
Example #6
0
        /// <summary>
        ///     Create a new <see cref="LanguageClient"/>.
        /// </summary>
        /// <param name="loggerFactory">
        ///     The logger to use.
        /// </param>
        LanguageClient(ILoggerFactory loggerFactory)
        {
            if (loggerFactory == null)
            {
                throw new ArgumentNullException(nameof(loggerFactory));
            }

            LoggerFactory = loggerFactory;
            Log           = LoggerFactory.CreateLogger <LanguageClient>();
            Workspace     = new WorkspaceClient(this);
            Window        = new WindowClient(this);
            TextDocument  = new TextDocumentClient(this);

            _dispatcher = new LspDispatcher(_serializer);
            _dispatcher.RegisterHandler(_dynamicRegistrationHandler);
        }
        /// <summary>
        ///     Open the connection.
        /// </summary>
        /// <param name="dispatcher">
        ///     The <see cref="LspDispatcher"/> used to dispatch messages to handlers.
        /// </param>
        public void Connect(LspDispatcher dispatcher)
        {
            if (dispatcher == null)
            {
                throw new ArgumentNullException(nameof(dispatcher));
            }

            if (IsOpen)
            {
                throw new InvalidOperationException("Connection is already open.");
            }

            _cancellationSource = new CancellationTokenSource();
            _cancellation       = _cancellationSource.Token;

            _dispatcher   = dispatcher;
            _sendLoop     = SendLoop();
            _receiveLoop  = ReceiveLoop();
            _dispatchLoop = DispatchLoop();

            _hasDisconnectedTask = Task.WhenAll(_sendLoop, _receiveLoop, _dispatchLoop);
        }