Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

migrate to amphp #746

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion .phan/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,14 @@
// your application should be included in this list.
'directory_list' => [
'src',
'vendor/amphp/amp/lib',
'vendor/amphp/file/lib',
'vendor/amphp/socket/src',
'vendor/composer/xdebug-handler/src',
'vendor/felixfbecker/advanced-json-rpc/lib',
'vendor/felixfbecker/language-server-protocol/src/',
'vendor/league/event/src',
'vendor/league/uri-parser/src',
'vendor/microsoft/tolerant-php-parser/src',
'vendor/netresearch/jsonmapper/src',
'vendor/phpdocumentor/reflection-common/src',
Expand All @@ -294,7 +299,6 @@
'vendor/phpunit/phpunit/src',
'vendor/psr/log/Psr',
'vendor/sabre/event/lib',
'vendor/sabre/uri/lib',
'vendor/webmozart/glob/src',
'vendor/webmozart/path-util/src',
],
Expand Down
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ language: php

php:
- '7.0'
- '7.1'
- '7.2'
- '7.3'

git:
depth: 10
Expand Down
102 changes: 44 additions & 58 deletions bin/php-language-server.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
<?php

use LanguageServer\{LanguageServer, ProtocolStreamReader, ProtocolStreamWriter, StderrLogger};
use Sabre\Event\Loop;
use Amp\ByteStream\ResourceInputStream;
use Amp\ByteStream\ResourceOutputStream;
use Amp\Loop;
use Amp\Socket\ClientSocket;
use Amp\Socket\ServerSocket;
use Composer\XdebugHandler\XdebugHandler;
use LanguageServer\{LanguageServer, ProtocolStreamReader, ProtocolStreamWriter, StderrLogger};

$options = getopt('', ['tcp::', 'tcp-server::', 'memory-limit::']);

Expand Down Expand Up @@ -42,69 +46,51 @@
if (!empty($options['tcp'])) {
// Connect to a TCP server
$address = $options['tcp'];
$socket = stream_socket_client('tcp://' . $address, $errno, $errstr);
if ($socket === false) {
$logger->critical("Could not connect to language client. Error $errno\n$errstr");
exit(1);
}
stream_set_blocking($socket, false);
$ls = new LanguageServer(
new ProtocolStreamReader($socket),
new ProtocolStreamWriter($socket)
);
Loop\run();
$server = function () use ($logger, $address) {
/** @var ClientSocket $socket */
$socket = yield Amp\Socket\connect('tcp://' . $address);
$ls = new LanguageServer(
new ProtocolStreamReader($socket),
new ProtocolStreamWriter($socket)
);
yield $ls->getshutdownDeferred();
};
} else if (!empty($options['tcp-server'])) {
// Run a TCP Server
$address = $options['tcp-server'];
$tcpServer = stream_socket_server('tcp://' . $address, $errno, $errstr);
if ($tcpServer === false) {
$logger->critical("Could not listen on $address. Error $errno\n$errstr");
exit(1);
}
$logger->debug("Server listening on $address");
$pcntlAvailable = extension_loaded('pcntl');
if (!$pcntlAvailable) {
$logger->notice('PCNTL is not available. Only a single connection will be accepted');
}
while ($socket = stream_socket_accept($tcpServer, -1)) {
$logger->debug('Connection accepted');
stream_set_blocking($socket, false);
if ($pcntlAvailable) {
// If PCNTL is available, fork a child process for the connection
// An exit notification will only terminate the child process
$pid = pcntl_fork();
if ($pid === -1) {
$logger->critical('Could not fork');
exit(1);
} else if ($pid === 0) {
// Child process
$reader = new ProtocolStreamReader($socket);
$writer = new ProtocolStreamWriter($socket);
$reader->on('close', function () use ($logger) {
$logger->debug('Connection closed');
});
$ls = new LanguageServer($reader, $writer);
Loop\run();
// Just for safety
exit(0);
}
} else {
// If PCNTL is not available, we only accept one connection.
// An exit notification will terminate the server
$ls = new LanguageServer(
new ProtocolStreamReader($socket),
new ProtocolStreamWriter($socket)
);
Loop\run();
$server = function () use ($logger, $address) {

$server = Amp\Socket\listen('tcp://' . $address);

$logger->debug("Server listening on $address");

while ($socket = yield $server->accept()) {
/** @var ServerSocket $socket */
list($ip, $port) = \explode(':', $socket->getRemoteAddress());

$logger->debug("Accepted connection from {$ip}:{$port}." . PHP_EOL);

Loop::run(function () use ($socket) {
$ls = new LanguageServer(
new ProtocolStreamReader($socket),
new ProtocolStreamWriter($socket)
);
yield $ls->getshutdownDeferred();
});
}
}
};
} else {
// Use STDIO
$logger->debug('Listening on STDIN');
stream_set_blocking(STDIN, false);
$inputStream = new ResourceInputStream(STDIN);
$outputStream = new ResourceOutputStream(STDOUT);
$ls = new LanguageServer(
new ProtocolStreamReader(STDIN),
new ProtocolStreamWriter(STDOUT)
new ProtocolStreamReader($inputStream),
new ProtocolStreamWriter($outputStream)
);
Loop\run();
$server = function () use ($ls) {
yield $ls->getshutdownDeferred();
};
}

Loop::run($server);
9 changes: 7 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,21 @@
],
"require": {
"php": "^7.0",
"amphp/byte-stream": "^1.5",
"amphp/cache": "^1.2",
"amphp/file": "^0.3.5",
"amphp/socket": "^0.10.11",
"composer/xdebug-handler": "^1.0",
"felixfbecker/advanced-json-rpc": "^3.0.0",
"felixfbecker/language-server-protocol": "^1.0.1",
"jetbrains/phpstorm-stubs": "dev-master",
"league/event": "^2.2",
"league/uri-parser": "^1.4",
"microsoft/tolerant-php-parser": "0.0.*",
"netresearch/jsonmapper": "^1.0",
"php-ds/php-ds": "^1.2",
"phpdocumentor/reflection-docblock": "^4.0.0",
"psr/log": "^1.0",
"sabre/event": "^5.0",
"sabre/uri": "^2.0",
"webmozart/glob": "^4.1",
"webmozart/path-util": "^2.3"
},
Expand Down
14 changes: 6 additions & 8 deletions src/Cache/Cache.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);

namespace LanguageServer\Cache;

use Sabre\Event\Promise;

/**
* A key/value store for caching purposes
*/
Expand All @@ -14,16 +12,16 @@ interface Cache
* Gets a value from the cache
*
* @param string $key
* @return Promise <mixed>
* @return \Generator <mixed>
*/
public function get(string $key): Promise;
public function get(string $key): \Generator;

/**
* Sets a value in the cache
*
* @param string $key
* @param mixed $value
* @return Promise
* @param mixed $value
* @return \Generator
*/
public function set(string $key, $value): Promise;
public function set(string $key, $value): \Generator;
}
18 changes: 8 additions & 10 deletions src/Cache/ClientCache.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);

namespace LanguageServer\Cache;

Expand Down Expand Up @@ -30,24 +30,22 @@ public function __construct(LanguageClient $client)
* @param string $key
* @return Promise <mixed>
*/
public function get(string $key): Promise
public function get(string $key): \Generator
{
return $this->client->xcache->get($key)->then('unserialize')->otherwise(function () {
// Ignore
});
$cached = yield from $this->client->xcache->get($key);
$obj = unserialize($cached);
return $obj;
}

/**
* Sets a value in the cache
*
* @param string $key
* @param mixed $value
* @param mixed $value
* @return Promise
*/
public function set(string $key, $value): Promise
public function set(string $key, $value): \Generator
{
return $this->client->xcache->set($key, serialize($value))->otherwise(function () {
// Ignore
});
return yield from $this->client->xcache->set($key, serialize($value));
}
}
38 changes: 17 additions & 21 deletions src/Cache/FileSystemCache.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);

namespace LanguageServer\Cache;

use Sabre\Event\Promise;

/**
* Caches content on the file system
*/
Expand All @@ -30,38 +28,36 @@ public function __construct()
* Gets a value from the cache
*
* @param string $key
* @return Promise <mixed>
* @return \Generator <mixed>
*/
public function get(string $key): Promise
public function get(string $key): \Generator
{
try {
$file = $this->cacheDir . urlencode($key);
if (!file_exists($file)) {
return Promise\resolve(null);
}
return Promise\resolve(unserialize(file_get_contents($file)));
$content = yield \Amp\File\get($file);
return unserialize($content);
} catch (\Exception $e) {
return Promise\resolve(null);
return null;
}
}

/**
* Sets a value in the cache
*
* @param string $key
* @param mixed $value
* @return Promise
* @param mixed $value
* @return \Generator
*/
public function set(string $key, $value): Promise
public function set(string $key, $value): \Generator
{
try {
$file = $this->cacheDir . urlencode($key);
if (!file_exists($this->cacheDir)) {
mkdir($this->cacheDir);
}
file_put_contents($file, serialize($value));
} finally {
return Promise\resolve(null);
$file = $this->cacheDir . urlencode($key);
$dir = dirname($file);
if (yield \Amp\File\isfile($dir)) {
yield \Amp\File\unlink($dir);
}
if (!yield \Amp\File\exists($dir)) {
yield \Amp\File\mkdir($dir, 0777, true);
}
yield \Amp\File\put($file, serialize($value));
}
}
15 changes: 7 additions & 8 deletions src/Client/TextDocument.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);

namespace LanguageServer\Client;

Expand Down Expand Up @@ -36,9 +36,9 @@ public function __construct(ClientHandler $handler, JsonMapper $mapper)
* @param Diagnostic[] $diagnostics
* @return Promise <void>
*/
public function publishDiagnostics(string $uri, array $diagnostics): Promise
public function publishDiagnostics(string $uri, array $diagnostics): \Generator
{
return $this->handler->notify('textDocument/publishDiagnostics', [
yield from $this->handler->notify('textDocument/publishDiagnostics', [
'uri' => $uri,
'diagnostics' => $diagnostics
]);
Expand All @@ -51,13 +51,12 @@ public function publishDiagnostics(string $uri, array $diagnostics): Promise
* @param TextDocumentIdentifier $textDocument The document to get the content for
* @return Promise <TextDocumentItem> The document's current content
*/
public function xcontent(TextDocumentIdentifier $textDocument): Promise
public function xcontent(TextDocumentIdentifier $textDocument): \Generator
{
return $this->handler->request(
$result = yield from $this->handler->request(
'textDocument/xcontent',
['textDocument' => $textDocument]
)->then(function ($result) {
return $this->mapper->map($result, new TextDocumentItem);
});
);
return $this->mapper->map($result, new TextDocumentItem);
}
}
4 changes: 2 additions & 2 deletions src/Client/Window.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ public function showMessage(int $type, string $message): Promise
* @param string $message
* @return Promise <void>
*/
public function logMessage(int $type, string $message): Promise
public function logMessage(int $type, string $message): \Generator
{
return $this->handler->notify('window/logMessage', ['type' => $type, 'message' => $message]);
yield from $this->handler->notify('window/logMessage', ['type' => $type, 'message' => $message]);
}
}
11 changes: 5 additions & 6 deletions src/Client/Workspace.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);

namespace LanguageServer\Client;

Expand Down Expand Up @@ -35,13 +35,12 @@ public function __construct(ClientHandler $handler, JsonMapper $mapper)
* @param string $base The base directory (defaults to the workspace)
* @return Promise <TextDocumentIdentifier[]> Array of documents
*/
public function xfiles(string $base = null): Promise
public function xfiles(string $base = null): \Generator
{
return $this->handler->request(
$textDocuments = yield from $this->handler->request(
'workspace/xfiles',
['base' => $base]
)->then(function (array $textDocuments) {
return $this->mapper->mapArray($textDocuments, [], TextDocumentIdentifier::class);
});
);
return $this->mapper->mapArray($textDocuments, [], TextDocumentIdentifier::class);
}
}