Getting Started

Below is an example which will run a language server which will respond to any request with a response “Hello world!”:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env php
<?php

use Amp\Success;
use Phpactor\LanguageServer\Core\Middleware\RequestHandler;
use Phpactor\LanguageServer\Core\Rpc\Message;
use Phpactor\LanguageServer\Core\Rpc\RequestMessage;
use Phpactor\LanguageServer\Core\Rpc\ResponseMessage;
use Phpactor\LanguageServer\Middleware\ClosureMiddleware;
use Phpactor\LanguageServer\Core\Dispatcher\Dispatcher\MiddlewareDispatcher;
use Phpactor\LanguageServerProtocol\InitializeParams;
use Phpactor\LanguageServer\Core\Server\Transmitter\MessageTransmitter;
use Phpactor\LanguageServer\Core\Dispatcher\Factory\ClosureDispatcherFactory;
use Phpactor\LanguageServer\LanguageServerBuilder;

require __DIR__ . '/../../vendor/autoload.php';

$builder = LanguageServerBuilder::create(new ClosureDispatcherFactory(
    function (MessageTransmitter $transmitter, InitializeParams $params) {
        return new MiddlewareDispatcher(
            new ClosureMiddleware(function (Message $message, RequestHandler $handler) {
                if (!$message instanceof RequestMessage) {
                    return $handler->handle($message);
                }

                return new Success(new ResponseMessage($message->id, 'Hello World!'));
            })
        );
    }
));

$builder
    ->build()
    ->run();
  • LanguageServerBuilder abstracts the creation of streams and builds the Language Server. It accepts an instance of DispatcherFactory - ClosureDispatcherFactory is a DispatcherFactory. This class has the responsibility initializing the session. It is invoked when the Language Server client sends initialize method, providing its capabilities.
  • MessageTransmitter is how your session can communicate with the client - you wouldn’t normally use this directly, but more on this later. The InitializeParams is a class containing the initialization information from the client, including the ClientCapabilities.
  • MiddlewareDispatcher Is a Dispatcher which uses the Middleware concept - this is the pipeline for incoming requests. Requests go in, and ResponseMessage classes come out (or null if no response is necessary).
  • ClosureMiddleware is a Middleware which allows you to specific a \Closure instead of implementing a new class (which is what you’d normally do). The Message is the incoming message (Request, Notification or Response) from the client, the RequestHandler is used to delegate to the next Middleware.
  • We return a ResponseMessage wrapped in a Promise. We only return a Response for Request messages, and the Response must reference the request’s ID.
  • The Success class is a Promise which resolves immediately. Returning a Promise allows us to run non-blocking co-routines.
  • Then finally build and run the server. It will listen on STDIO by default.

If you run this example, you should be able to connect to the language server and it should respond (incorrectly) to all requests with “Hello World!”.

Let’s try it out.

$ echo '{"id":1,"method":"foobar","params":[]}' | ./bin/proxy request | php example/server/minimal.php

The proxy binary file is used only for this demonstration, it adds the necessary formatting to the message before passing it to our new language server (running on STDIO by default).

It should show something like:

At this point you could connect an IDE to your new Language Server, but it wouldn’t do very much.

In the next chapter we’ll try and introduce some more concepts and add some language server functionality.