1
0
Fork 0
pull/6/head
Felix Becker 2016-08-22 17:32:31 +02:00
parent 89a688b778
commit 64e496fac9
47 changed files with 1106 additions and 18 deletions

View File

@ -5,13 +5,18 @@
"bin": ["bin/main.php"],
"minimum-stability": "dev",
"require": {
"php": ">=7.0",
"nikic/php-parser": "3.0.0alpha1",
"phpdocumentor/reflection-docblock": "^3.0",
"sabre/event": "^3.0"
"sabre/event": "^3.0",
"netresearch/jsonmapper": "^0.11.0"
},
"autoload": {
"psr-4": {
"LanguageServer\\": "src/"
}
},
"require-dev": {
"squizlabs/php_codesniffer": "^2.6"
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace LanguageServer\Protocol;
class ClientCapabilites
{
}

View File

@ -0,0 +1,79 @@
<?php
namespace LanguageServer\Protocol\TextDocument;
class CompletionItem
{
/**
* The label of this completion item. By default
* also the text that is inserted when selecting
* this completion.
*
* @var string
*/
public $label;
/**
* The kind of this completion item. Based of the kind
* an icon is chosen by the editor.
*
* @var number|null
*/
public $kind;
/**
* A human-readable string with additional information
* about this item, like type or symbol information.
*
* @var string|null
*/
public $detail;
/**
* A human-readable string that represents a doc-comment.
*
* @var string|null
*/
public $documentation;
/**
* A string that shoud be used when comparing this item
* with other items. When `falsy` the label is used.
*
* @var string|null
*/
public $sortText;
/**
* A string that should be used when filtering a set of
* completion items. When `falsy` the label is used.
*
* @var string|null
*/
public $filterText;
/**
* A string that should be inserted a document when selecting
* this completion. When `falsy` the label is used.
*
* @var string|null
*/
public $insertText;
/**
* An edit which is applied to a document when selecting
* this completion. When an edit is provided the value of
* insertText is ignored.
*
* @var TextEdit|null
*/
public $textEdit;
/**
* An data entry field that is preserved on a completion item between
* a completion and a completion resolve request.
*
* @var mixed
*/
public $data;
}

View File

@ -0,0 +1,27 @@
<?php
namespace LanguageServer\Protocol\TextDocument;
/**
* The kind of a completion entry.
*/
abstract class CompletionItemKind {
const TEXT = 1;
const METHOD = 2;
const _FUNCTION = 3;
const CONSTRUCTOR = 4;
const FIELD = 5;
const VARIABLE = 6;
const _CLASS = 7;
const _INTERFACE = 8;
const MODULE = 9;
const PROPERTY = 10;
const UNIT = 11;
const VALUE = 12;
const ENUM = 13;
const KEYWORD = 14;
const SNIPPET = 15;
const COLOR = 16;
const FILE = 17;
const REFERENCE = 18;
}

View File

@ -0,0 +1,25 @@
<?php
namespace LanguageServer\Protocol\TextDocument;
/**
* Represents a collection of [completion items](#CompletionItem) to be presented in
* the editor.
*/
class CompletionList
{
/**
* This list it not complete. Further typing should result in recomputing this
* list.
*
* @var bool
*/
public $isIncomplete;
/**
* The completion items.
*
* @var CompletionItem[]
*/
public $items;
}

View File

@ -0,0 +1,32 @@
<?php
namespace LanguageServer\Protocol\TextDocument;
/**
* An event describing a change to a text document. If range and rangeLength are
* omitted the new text is considered to be the full content of the document.
*/
class ContentChangeEvent
{
/**
* The range of the document that changed.
*
* @var Range|null
*/
public $range;
/**
* The length of the range that got replaced.
*
* @var int|null
*/
public $rangeLength;
/**
* The new text of the document.
*
* @var string
*/
public $text;
}

View File

@ -0,0 +1,15 @@
<?php
/**
* Enum
*/
abstract class ErrorCode
{
const PARSE_ERROR = -32700;
const INVALID_REQUEST = -32600;
const METHOD_NOT_FOUND = -32601;
const INVALID_PARAMS = -32602;
const INTERNAL_ERROR = -32603;
const SERVER_ERROR_START = -32099;
const SERVER_ERROR_END = -32000;
}

View File

@ -0,0 +1,24 @@
<?php
namespace LanguageServer\Protocol;
/**
* The file event type. Enum
*/
abstract class FileChangeType
{
/**
* The file got created.
*/
const CREATED = 1;
/**
* The file got changed.
*/
const CHANGED = 2;
/**
* The file got deleted.
*/
const DELETED = 3;
}

View File

@ -0,0 +1,23 @@
<?php
namespace LanguageServer\Protocol\TextDocument;
/**
* An event describing a file change.
*/
class FileEvent
{
/**
* The file's URI.
*
* @var string
*/
public $uri;
/**
* The change type.
*
* @var int
*/
public $type;
}

23
src/Protocol/Hover.php Normal file
View File

@ -0,0 +1,23 @@
<?php
namespace LanguageServer\Protocol;
/**
* The result of a hover request.
*/
class Hover
{
/**
* The hover's content
*
* @var string|string[]|MarkedString|MarkedString[]
*/
public $contents;
/**
* An optional range
*
* @var Range|null
*/
public $range;
}

View File

@ -0,0 +1,25 @@
<?php
namespace LanguageServer;
use LanguageServer\Protocol\ProtocolServer;
/**
* Enum
*/
abstract class ParsingMode {
const HEADERS = 1;
const BODY = 2;
}
class LanguageServer extends ProtocolServer
{
public function listen()
{
$this->on('initialize', function (InitializeRequest $req) {
$res = new InitializeResponse();
$this->sendResponse($res);
});
parent::listen();
}
}

11
src/Protocol/Message.php Normal file
View File

@ -0,0 +1,11 @@
<?php
abstract class Message
{
/**
* The version (2.0)
*
* @var string
*/
public $jsonrpc;
}

View File

@ -0,0 +1,15 @@
<?php
namespace LanguageServer\Protocol\Methods\CompletionItem\ResolveRequest;
/**
* The request is sent from the client to the server to resolve additional
* information for a given completion item.
*/
class CompletionItemResolveRequest
{
/**
* @var CompletionItem
*/
public $params;
}

View File

@ -0,0 +1,14 @@
<?php
namespace LanguageServer\Protocol\Methods\Initialize;
class InitializeError
{
/*
* Indicates whether the client should retry to send the initilize request after
* showing the message provided in the ResponseError.
*
* @var bool
*/
public $retry;
}

View File

@ -0,0 +1,28 @@
<?php
namespace LanguageServer\Protocol\Methods\Initialize;
class InitializeParams
{
/**
* The rootPath of the workspace. Is null if no folder is open.
*
* @var string
*/
public $rootPath;
/**
* The process Id of the parent process that started the server.
*
* @var number
*/
public $processId;
/**
* The capabilities provided by the client (editor)
*
* @var ClientCapabilities
*/
public $capabilities;
}

View File

@ -0,0 +1,14 @@
<?php
namespace LanguageServer\Protocol\Methods\InitializeRequest;
/*
* The initialize request is sent as the first request from the client to the server.
*/
class CompletionItemResolveRequest
{
/**
* @var InitializeParams
*/
public $params;
}

View File

@ -0,0 +1,15 @@
<?php
namespace LanguageServer\Protocol\Methods\Initialize;
use LanguageServer\Protocol\Response;
class InitializeResponse extends Response
{
/**
* The capabilities the language server provides.
*
* @var ServerCapabilities
*/
public $capabilites;
}

View File

@ -0,0 +1,12 @@
<?php
namespace LanguageServer\Protocol\Telementry;
/**
* The telemetry notification is sent from the server to the client to ask the client
* to log a telemetry event.
*/
class EventParams
{
}

View File

@ -0,0 +1,21 @@
<?php
namespace LanguageServer\Protocol\Methods\TextDocument;
use LanguageServer\Protocol\Request;
/*
* The Completion request is sent from the client to the server to compute completion
* items at a given cursor position. Completion items are presented in the
* [IntelliSense](https://code.visualstudio.com/docs/editor/editingevolved#_intellisense)
* user interface. If computing full completion items is expensive, servers can
* additionally provide a handler for the completion item resolve request. This
* request is sent when a completion item is selected in the user interface.
*/
class CompletionRequest extends Request
{
/**
* @var PositionParams
*/
public $params;
}

View File

@ -0,0 +1,17 @@
<?php
namespace LanguageServer\Protocol\TextDocument;
use LanguageServer\Protocol\Response;
/**
* Diagnostics notification are sent from the server to the client to signal results
* of validation runs.
*/
class PublishDiagnosticsParams extends Response
{
/**
* @var CompletionItem[]|CompletionList
*/
public $result;
}

View File

@ -0,0 +1,26 @@
<?php
namespace LanguageServer\Protocol\TextDocument;
/**
* The log message notification is sent from the server to the client to ask the
* client to log a particular message.
*/
class DidChangeParams
{
/**
* The document that did change. The version number points
* to the version after all provided content changes have
* been applied.
*
* @var VersionedTextDocumentIdentifier
*/
public $textDocument;
/**
* The actual content changes.
*
* @var ContentChangeEvent[]
*/
public $contentChanges;
}

View File

@ -0,0 +1,19 @@
<?php
namespace LanguageServer\Protocol\TextDocument;
use LanguageServer\Protocol\Params;
/**
* The watched files notification is sent from the client to the server when the
* client detects changes to files watched by the language client.
*/
class DidChangeWatchedFilesParams extends Params
{
/**
* The actual file events.
*
* @var FileEvent[]
*/
public $changes;
}

View File

@ -0,0 +1,21 @@
<?php
namespace LanguageServer\Protocol\TextDocument;
use LanguageServer\Protocol\Params;
/**
* The document close notification is sent from the client to the server when the
* document got closed in the client. The document's truth now exists where the
* document's uri points to (e.g. if the document's uri is a file uri the truth now
* exists on disk).
*/
class DidCloseTextDocumentParams extends Params
{
/**
* The document that was closed.
*
* @var TextDocumentIdentifier
*/
public $textDocument;
}

View File

@ -0,0 +1,19 @@
<?php
namespace LanguageServer\Protocol\TextDocument;
use LanguageServer\Protocol\Params;
/**
* The document save notification is sent from the client to the server when the
* document was saved in the client.
*/
class DidSaveTextDocumentParams extends Params
{
/**
* The document that was closed.
*
* @var TextDocumentIdentifier
*/
public $textDocument;
}

View File

@ -0,0 +1,13 @@
<?php
namespace LanguageServer\Protocol\Methods\TextDocument\HoverRequest;
use LanguageServer\Protocol\Request;
class HoverRequest extends Request
{
/**
* @var PositionParams
*/
public $params;
}

View File

@ -0,0 +1,13 @@
<?php
namespace LanguageServer\Protocol\TextDocument;
use LanguageServer\Protocol\Response;
class HoverResponse extends Response
{
/**
* @var Hover
*/
public $result;
}

View File

@ -0,0 +1,26 @@
<?php
namespace LanguageServer\Protocol\TextDocument;
use LanguageServer\Protocol\RequestParams;
/*
* A parameter literal used in requests to pass a text document and a position inside
* that document.
*/
class TextDocumentPositionParams extends RequestParams
{
/**
* The text document.
*
* @var TextDocumentIdentifier
*/
public $textDocument;
/**
* The position inside the text document.
*
* @var Position
*/
public $position;
}

View File

@ -0,0 +1,26 @@
<?php
namespace LanguageServer\Protocol\Window;
use LanguageServer\Protocol\RequestParams;
/**
* The log message notification is sent from the server to the client to ask the
* client to log a particular message.
*/
class LogMessageParams extends RequestParams
{
/**
* The message type. See {@link MessageType}
*
* @var number
*/
public $type;
/**
* The actual message
*
* @var string
*/
public $message;
}

View File

@ -0,0 +1,13 @@
<?php
namespace LanguageServer\Protocol\Window;
class ShowMessageParams
{
/**
* A short title like 'Retry', 'Open Log' etc.
*
* @var string
*/
public $title;
}

View File

@ -0,0 +1,27 @@
<?php
/**
* Enum
*/
abstract class MessageType
{
/**
* An error message.
*/
const ERROR = 1;
/**
* A warning message.
*/
const WARNING = 2;
/**
* An information message.
*/
const INFO = 3;
/**
* A log message.
*/
const LOG = 4;
}

View File

@ -0,0 +1,20 @@
<?php
namespace LanguageServer\Protocol\Window;
class ShowMessageParams
{
/**
* The message type. See {@link MessageType}.
*
* @var int
*/
public $type;
/**
* The actual message.
*
* @var string
*/
public $message;
}

View File

@ -0,0 +1,27 @@
<?php
namespace LanguageServer\Protocol\Window;
class ShowMessageParams
{
/**
* The message type. See {@link MessageType}
*
* @var int
*/
public $type;
/**
* The actual message
*
* @var string
*/
public $message;
/**
* The message action items to present.
*
* @var MessageActionItem[]|null
*/
public $actions;
}

View File

@ -0,0 +1,17 @@
<?php
namespace LanguageServer\Protocol\Methods\InitializeRequest;
use LanguageServer\Protocol\Notification;
/**
* A notification sent from the client to the server to signal the change of
* configuration settings.
*/
class DidChangeConfigurationNotification extends Notification
{
/**
* @var DidChangeConfigurationParams
*/
public $params;
}

View File

@ -0,0 +1,15 @@
<?php
namespace LanguageServer\Protocol\Workspace;
use LanguageServer\Protocol\Params;
class DidChangeConfigurationParams extends Params
{
/**
* The actual changed settings
*
* @var mixed
*/
public $settings;
}

View File

@ -0,0 +1,24 @@
<?php
namespace LanguageServer\Protocol;
/**
* A notification message. A processed notification message must not send a response
* back. They work like events.
*/
abstract class Notification extends Message
{
/**
* The method to be invoked.
*
* @var string
*/
public $method;
/**
* The notification's params.
*
* @var mixed|null
*/
public $params;
}

8
src/Protocol/Params.php Normal file
View File

@ -0,0 +1,8 @@
<?php
namespace LanguageServer\Protocol;
abstract class Params
{
}

View File

@ -1,6 +1,6 @@
<?php
namespace LanguageServer;
namespace LanguageServer\Protocol;
use Sabre\Event\Loop;
use Sabre\Event\EventEmitter;
@ -10,10 +10,14 @@ abstract class ParsingMode {
const BODY = 2;
}
class LanguageServer extends EventEmitter
class ProtocolServer extends EventEmitter
{
private $input;
private $output;
private $buffer = '';
private $parsingMode = ParsingMode::HEADERS;
private $headers = [];
private $contentLength = 0;
/**
* @param resource $input The input stream
@ -27,31 +31,27 @@ class LanguageServer extends EventEmitter
public function listen()
{
$buffer = '';
$parsingMode = ParsingMode::HEADERS;
$headers = [];
$contentLength = 0;
Loop\addReadStream($this->input, function() use ($buffer, $parsingMode, $headers, $contentLength) {
$buffer .= fgetc($this->output);
Loop\addReadStream($this->input, function() {
$this->buffer .= fgetc($this->output);
switch ($parsingMode) {
case ParsingMode::HEADERS:
if (substr($buffer, -4) === '\r\n\r\n') {
$parsingMode = ParsingMode::BODY;
$contentLength = (int)$headers['Content-Length'];
$buffer = '';
$this->parsingMode = ParsingMode::BODY;
$this->contentLength = (int)$headers['Content-Length'];
$this->buffer = '';
} else if (substr($buffer, -2) === '\r\n') {
$parts = explode(': ', $buffer);
$headers[$parts[0]] = $parts[1];
$buffer = '';
$this->buffer = '';
}
break;
case ParsingMode::BODY:
if (strlen($buffer) === $contentLength) {
$body = json_decode($buffer);
$this->emit($body->method, [$body->params]);
$parsingMode = ParsingMode::HEADERS;
$buffer = '';
$req = Request::parse($body);
$this->emit($body->method, [$req]);
$this->parsingMode = ParsingMode::HEADERS;
$this->buffer = '';
}
break;
}
@ -59,4 +59,9 @@ class LanguageServer extends EventEmitter
Loop\run();
}
public function sendResponse(Response $res)
{
fwrite($this->output, json_encode($res));
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace LanguageServer\Protocol\TextDocument;
use LanguageServer\Protocol\RequestParams;
/**
* Diagnostics notification are sent from the server to the client to signal results
* of validation runs.
*/
class PublishDiagnosticsParams extends RequestParams
{
/**
* The URI for which diagnostic information is reported.
*
* @var string
*/
public $uri;
/**
* An array of diagnostic information items.
*
* @var Diagnostic[]
*/
public $diagnostics;
}

43
src/Protocol/Request.php Normal file
View File

@ -0,0 +1,43 @@
<?php
namespace LanguageServer\Protocol;
use JsonMapper;
class Request extends Message
{
/**
* @var int|string
*/
public $id;
/**
* @var string
*/
public $method;
/**
* @var object
*/
public $params;
public static function parse(string $body)
{
$mapper = new JsonMapper();
$request->id = $decoded->id;
$request->method = $decoded->method;
$pascalCasedMethod = ucfirst($decoded->method);
$namespace = __NAMESPACE__ . '\\' . str_replace('/', '\\', $pascalCasedMethod);
$className = end(explode('\\', $request->method)) . 'Params';
$fullyQualifiedName = $namespace . $className;
$mapper->classMap['RequestParams'] = $fullyQualifiedName;
$request = $mapper->map(json_decode($body), new self());
if (class_exists($fullyQualifiedName)) {
$request->params = new $fullyQualifiedName();
}
foreach ($request->params as $key => $value) {
$request->{$key} = $value;
}
return $request;
}
}

26
src/Protocol/Response.php Normal file
View File

@ -0,0 +1,26 @@
<?php
namespace LanguageServer\Protocol;
class Response extends Message
{
/**
* @var int|string
*/
public $id;
/**
* @var string
*/
public $method;
/**
* @var object|null
*/
public $params;
/**
* @var ResponseError|null
*/
public $error;
}

View File

@ -0,0 +1,26 @@
<?php
class ResponseError
{
/**
* A number indicating the error type that occurred.
*
* @var int
*/
public $code;
/**
* A string providing a short description of the error.
*
* @var string
*/
public $message;
/**
* A Primitive or Structured value that contains additional information about the
* error. Can be omitted.
*
* @var mixed
*/
public $data;
}

View File

@ -0,0 +1,111 @@
<?php
namespace LanguageServer\Protocol;
class ServerCapabilites
{
/**
* Defines how text documents are synced.
*
* @var int|null
*/
public $textDocumentSync;
/**
* The server provides hover support.
*
* @var bool|null
*/
public $hoverProvider;
/**
* The server provides completion support.
*
* @var CompletionOptions|null
*/
public $completionProvider;
/**
* The server provides signature help support.
*
* @var SignatureHelpOptions|null
*/
public $signatureHelpProvider;
/**
* The server provides goto definition support.
*
* @var bool|null
*/
public $definitionProvider;
/**
* The server provides find references support.
*
* @var bool|null
*/
public $referencesProvider;
/**
* The server provides document highlight support.
*
* @var bool|null
*/
public $documentHighlightProvider;
/**
* The server provides document symbol support.
*
* @var bool|null
*/
public $documentSymbolProvider;
/**
* The server provides workspace symbol support.
*
* @var bool|null
*/
public $workspaceSymbolProvider;
/**
* The server provides code actions.
*
* @var bool|null
*/
public $codeActionProvider;
/**
* The server provides code lens.
*
* @var CodeLensOptions|null
*/
public $codeLensProvider;
/**
* The server provides document formatting.
*
* @var bool|null
*/
public $documentFormattingProvider;
/**
* The server provides document range formatting.
*
* @var bool|null
*/
public $documentRangeFormattingProvider;
/**
* The server provides document formatting on typing.
*
* @var DocumentOnTypeFormattingOptions|null
*/
public $documentOnTypeFormattingProvider;
/**
* The server provides rename support.
*
* @var bool|null
*/
public $renameProvidern;
}

View File

@ -0,0 +1,16 @@
<?php
namespace LanguageServer\Protocol\Methods\Initialize\ServerCapabilities;
/**
* Code Lens options.
*/
class CodeLensOptions
{
/**
* Code lens has a resolve provider as well.
*
* @var bool|null
*/
public $resolveProvider;
}

View File

@ -0,0 +1,24 @@
<?php
namespace LanguageServer\Protocol\Methods\Initialize\ServerCapabilities;
/**
* Completion options.
*/
class CompletionOptions
{
/*
* The server provides support to resolve additional information for a completion
* item.
*
* @var bool
*/
public $resolveProvider;
/**
* The characters that trigger completion automatically.
*
* @var string|null
*/
public $triggerCharacters;
}

View File

@ -0,0 +1,23 @@
<?php
namespace LanguageServer\Protocol\Methods\Initialize\ServerCapabilities;
/**
* Format document on type options
*/
class DocumentOnTypeFormattingOptions
{
/**
* A character on which formatting should be triggered, like `}`.
*
* @var string
*/
public $firstTriggerCharacter;
/**
* More trigger characters.
*
* @var string[]|null
*/
public $moreTriggerCharacter;
}

View File

@ -0,0 +1,16 @@
<?php
namespace LanguageServer\Protocol\Methods\Initialize\ServerCapabilities;
/**
* Signature help options.
*/
class SignatureHelpOptions
{
/**
* The characters that trigger signature help automatically.
*
* @var string[]|null
*/
public $triggerCharacters;
}

View File

@ -0,0 +1,25 @@
<?php
namespace LanguageServer\Protocol\Methods\Initialize\ServerCapabilities;
/**
* Defines how the host (editor) should sync document changes to the language server.
*/
abstract class TextDocumentSyncKind
{
/**
* Documents should not be synced at all.
*/
const NONE = 0;
/**
* Documents are synced by always sending the full content of the document.
*/
const FULL = 1;
/*
* Documents are synced by sending the full content on open. After that only
* incremental updates to the document are sent.
*/
const INCREMENTAL = 2;
}