Merge branch 'master' into completion
commit
471d4703c5
|
@ -0,0 +1,9 @@
|
||||||
|
.DS_Store
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
.git/
|
||||||
|
tests/
|
||||||
|
fixtures/
|
||||||
|
coverage/
|
||||||
|
coverage.xml
|
||||||
|
images/
|
|
@ -3,6 +3,9 @@ language: php
|
||||||
php:
|
php:
|
||||||
- '7.0'
|
- '7.0'
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- vendor
|
- vendor
|
||||||
|
@ -16,3 +19,9 @@ script:
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
- |
|
||||||
|
if [[ $TRAVIS_TAG == v* ]]; then
|
||||||
|
docker build -t felixfbecker/php-language-server:${TRAVIS_TAG:1} .
|
||||||
|
docker login -e="$DOCKER_EMAIL" -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
|
||||||
|
docker push felixfbecker/php-language-server:${TRAVIS_TAG:1}
|
||||||
|
fi
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
|
||||||
|
# Running this container will start a language server that listens for TCP connections on port 2088
|
||||||
|
# Every connection will be run in a forked child process
|
||||||
|
|
||||||
|
# Please note that before building the image, you have to install dependencies with `composer install`
|
||||||
|
|
||||||
|
FROM php:7-cli
|
||||||
|
MAINTAINER Felix Becker <felix.b@outlook.com>
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
# Needed for CodeSniffer
|
||||||
|
&& apt-get install -y libxml2 libxml2-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN docker-php-ext-configure pcntl --enable-pcntl
|
||||||
|
RUN docker-php-ext-install pcntl
|
||||||
|
COPY ./php.ini /usr/local/etc/php/conf.d/
|
||||||
|
|
||||||
|
COPY ./ /srv/phpls
|
||||||
|
|
||||||
|
WORKDIR /srv/phpls
|
||||||
|
|
||||||
|
EXPOSE 2088
|
||||||
|
|
||||||
|
CMD ["--tcp-server=0:2088"]
|
||||||
|
|
||||||
|
ENTRYPOINT ["php", "bin/php-language-server.php"]
|
10
README.md
10
README.md
|
@ -148,6 +148,16 @@ Example:
|
||||||
|
|
||||||
php bin/php-language-server.php --tcp=127.0.0.1:12345
|
php bin/php-language-server.php --tcp=127.0.0.1:12345
|
||||||
|
|
||||||
|
#### `--tcp-server=host:port` (optional)
|
||||||
|
Causes the server to use a tcp connection for communicating with the language client instead of using STDIN/STDOUT.
|
||||||
|
The server will listen on the given address for a connection.
|
||||||
|
If PCNTL is available, will fork a child process for every connection.
|
||||||
|
If not, will only accept one connection and the connection cannot be reestablished once closed, spawn a new process instead.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
php bin/php-language-server.php --tcp-server=127.0.0.1:12345
|
||||||
|
|
||||||
#### `--memory-limit=integer` (optional)
|
#### `--memory-limit=integer` (optional)
|
||||||
Sets memory limit for language server.
|
Sets memory limit for language server.
|
||||||
Equivalent to [memory-limit](http://php.net/manual/en/ini.core.php#ini.memory-limit) php.ini directive.
|
Equivalent to [memory-limit](http://php.net/manual/en/ini.core.php#ini.memory-limit) php.ini directive.
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use LanguageServer\{LanguageServer, ProtocolStreamReader, ProtocolStreamWriter};
|
use LanguageServer\{LanguageServer, ProtocolStreamReader, ProtocolStreamWriter};
|
||||||
use Sabre\Event\Loop;
|
use Sabre\Event\Loop;
|
||||||
|
|
||||||
$options = getopt('', ['tcp::', 'memory-limit::']);
|
$options = getopt('', ['tcp::', 'tcp-server::', 'memory-limit::']);
|
||||||
|
|
||||||
ini_set('memory_limit', $options['memory-limit'] ?? -1);
|
ini_set('memory_limit', $options['memory-limit'] ?? -1);
|
||||||
|
|
||||||
|
@ -31,21 +31,67 @@ set_exception_handler(function (\Throwable $e) {
|
||||||
@cli_set_process_title('PHP Language Server');
|
@cli_set_process_title('PHP Language Server');
|
||||||
|
|
||||||
if (!empty($options['tcp'])) {
|
if (!empty($options['tcp'])) {
|
||||||
|
// Connect to a TCP server
|
||||||
$address = $options['tcp'];
|
$address = $options['tcp'];
|
||||||
$socket = stream_socket_client('tcp://' . $address, $errno, $errstr);
|
$socket = stream_socket_client('tcp://' . $address, $errno, $errstr);
|
||||||
if ($socket === false) {
|
if ($socket === false) {
|
||||||
fwrite(STDERR, "Could not connect to language client. Error $errno\n");
|
fwrite(STDERR, "Could not connect to language client. Error $errno\n$errstr");
|
||||||
fwrite(STDERR, "$errstr\n");
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
$inputStream = $outputStream = $socket;
|
stream_set_blocking($socket, false);
|
||||||
|
$ls = new LanguageServer(
|
||||||
|
new ProtocolStreamReader($socket),
|
||||||
|
new ProtocolStreamWriter($socket)
|
||||||
|
);
|
||||||
|
Loop\run();
|
||||||
|
} 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) {
|
||||||
|
fwrite(STDERR, "Could not listen on $address. Error $errno\n$errstr");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
fwrite(STDOUT, "Server listening on $address\n");
|
||||||
|
if (!extension_loaded('pcntl')) {
|
||||||
|
fwrite(STDERR, "PCNTL is not available. Only a single connection will be accepted\n");
|
||||||
|
}
|
||||||
|
while ($socket = stream_socket_accept($tcpServer, -1)) {
|
||||||
|
fwrite(STDOUT, "Connection accepted\n");
|
||||||
|
stream_set_blocking($socket, false);
|
||||||
|
if (extension_loaded('pcntl')) {
|
||||||
|
// 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) {
|
||||||
|
fwrite(STDERR, "Could not fork\n");
|
||||||
|
exit(1);
|
||||||
|
} else if ($pid === 0) {
|
||||||
|
// Child process
|
||||||
|
$ls = new LanguageServer(
|
||||||
|
new ProtocolStreamReader($socket),
|
||||||
|
new ProtocolStreamWriter($socket)
|
||||||
|
);
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$inputStream = STDIN;
|
// Use STDIO
|
||||||
$outputStream = STDOUT;
|
stream_set_blocking(STDIN, false);
|
||||||
|
$ls = new LanguageServer(
|
||||||
|
new ProtocolStreamReader(STDIN),
|
||||||
|
new ProtocolStreamWriter(STDOUT)
|
||||||
|
);
|
||||||
|
Loop\run();
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_set_blocking($inputStream, false);
|
|
||||||
|
|
||||||
$server = new LanguageServer(new ProtocolStreamReader($inputStream), new ProtocolStreamWriter($outputStream));
|
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
# php.ini for Docker
|
||||||
|
|
||||||
|
error_reporting = E_ALL
|
||||||
|
display_errors = stderr
|
|
@ -108,12 +108,12 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
|
||||||
/**
|
/**
|
||||||
* The initialize request is sent as the first request from the client to the server.
|
* The initialize request is sent as the first request from the client to the server.
|
||||||
*
|
*
|
||||||
* @param int $processId The process Id of the parent process that started the server.
|
|
||||||
* @param ClientCapabilities $capabilities The capabilities provided by the client (editor)
|
* @param ClientCapabilities $capabilities The capabilities provided by the client (editor)
|
||||||
* @param string|null $rootPath The rootPath of the workspace. Is null if no folder is open.
|
* @param string|null $rootPath The rootPath of the workspace. Is null if no folder is open.
|
||||||
|
* @param int|null $processId The process Id of the parent process that started the server. Is null if the process has not been started by another process. If the parent process is not alive then the server should exit (see exit notification) its process.
|
||||||
* @return InitializeResult
|
* @return InitializeResult
|
||||||
*/
|
*/
|
||||||
public function initialize(int $processId, ClientCapabilities $capabilities, string $rootPath = null): InitializeResult
|
public function initialize(ClientCapabilities $capabilities, string $rootPath = null, int $processId = null): InitializeResult
|
||||||
{
|
{
|
||||||
$this->rootPath = $rootPath;
|
$this->rootPath = $rootPath;
|
||||||
$this->clientCapabilities = $capabilities;
|
$this->clientCapabilities = $capabilities;
|
||||||
|
|
|
@ -344,7 +344,7 @@ class PhpDocument
|
||||||
*/
|
*/
|
||||||
public function getDefinitions()
|
public function getDefinitions()
|
||||||
{
|
{
|
||||||
return $this->definitions;
|
return $this->definitions ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -71,7 +71,7 @@ class LanguageServerTest extends TestCase
|
||||||
});
|
});
|
||||||
$server = new LanguageServer($input, $output);
|
$server = new LanguageServer($input, $output);
|
||||||
$capabilities = new ClientCapabilities;
|
$capabilities = new ClientCapabilities;
|
||||||
$server->initialize(getmypid(), $capabilities, realpath(__DIR__ . '/../fixtures'));
|
$server->initialize($capabilities, realpath(__DIR__ . '/../fixtures'), getmypid());
|
||||||
$promise->wait();
|
$promise->wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ class LanguageServerTest extends TestCase
|
||||||
$capabilities = new ClientCapabilities;
|
$capabilities = new ClientCapabilities;
|
||||||
$capabilities->xfilesProvider = true;
|
$capabilities->xfilesProvider = true;
|
||||||
$capabilities->xcontentProvider = true;
|
$capabilities->xcontentProvider = true;
|
||||||
$server->initialize(getmypid(), $capabilities, $rootPath);
|
$server->initialize($capabilities, $rootPath, getmypid());
|
||||||
$promise->wait();
|
$promise->wait();
|
||||||
$this->assertTrue($filesCalled);
|
$this->assertTrue($filesCalled);
|
||||||
$this->assertTrue($contentCalled);
|
$this->assertTrue($contentCalled);
|
||||||
|
|
Loading…
Reference in New Issue