-
-
Notifications
You must be signed in to change notification settings - Fork 156
Description
After reading #1858, I decided to see what it would take to get Phpactor running on Windows, without Docker, WSL etc. as discussed in that issue. I'm happy to report that I got it working, with a few small but really ugly hacks to Phpactor and its dependencies. I'm filing this to document the underlying issues, and ask for some suggestions as for how to resolve them properly.
This is the full list of changes I have locally. I haven't tested everything, but at the very least I get completions and hover tips when using Sublime Text on Windows 10 on the https://github.com/wikimedia/mediawiki repository (tested with PHP 8.1 and 8.3).
diff -u phpactor_orig/lib/Extension/LanguageServerCompletion/Handler/CompletionHandler.php phpactor/lib/Extension/LanguageServerCompletion/Handler/CompletionHandler.php
--- phpactor_orig/lib/Extension/LanguageServerCompletion/Handler/CompletionHandler.php 2024-02-21 03:28:24.667006800 +0100
+++ phpactor/lib/Extension/LanguageServerCompletion/Handler/CompletionHandler.php 2024-02-21 05:17:32.214568200 +0100
@@ -119,7 +119,7 @@
$isIncomplete = true;
break;
}
- yield new Delayed(0);
+ // yield new Delayed(0);
}
diff -u phpactor_orig/lib/Extension/LanguageServerIndexer/Handler/IndexerHandler.php phpactor/lib/Extension/LanguageServerIndexer/Handler/IndexerHandler.php
--- phpactor_orig/lib/Extension/LanguageServerIndexer/Handler/IndexerHandler.php 2024-02-21 03:28:24.704035500 +0100
+++ phpactor/lib/Extension/LanguageServerIndexer/Handler/IndexerHandler.php 2024-02-21 05:18:00.712987900 +0100
@@ -99,7 +99,7 @@
break;
}
- yield new Delayed(1);
+ // yield new Delayed(1);
}
$process = yield $this->watcher->watch();
@@ -162,7 +162,7 @@
$process->stop();
return;
}
- yield new Delayed(100);
+ // yield new Delayed(100);
}
});
try {
@@ -185,7 +185,7 @@
continue;
}
$this->logger->debug(sprintf('Indexed file: %s', $file->path()));
- yield new Delayed(0);
+ // yield new Delayed(0);
}
} catch (WatcherDied $watcherDied) {
$this->clientApi->window()->showMessage()->error(sprintf('File watcher died: %s', $watcherDied->getMessage()));
diff -u phpactor_orig/lib/Extension/LanguageServerReferenceFinder/Handler/ReferencesHandler.php phpactor/lib/Extension/LanguageServerReferenceFinder/Handler/ReferencesHandler.php
--- phpactor_orig/lib/Extension/LanguageServerReferenceFinder/Handler/ReferencesHandler.php 2024-02-21 03:28:24.732353300 +0100
+++ phpactor/lib/Extension/LanguageServerReferenceFinder/Handler/ReferencesHandler.php 2024-02-21 05:18:03.791091100 +0100
@@ -106,7 +106,7 @@
if ($count % 10) {
// give other co-routines a chance
- yield new Delayed(0);
+ // yield new Delayed(0);
}
}
diff -u phpactor_orig/lib/FilePathResolver/LoggingPathResolver.php phpactor/lib/FilePathResolver/LoggingPathResolver.php
--- phpactor_orig/lib/FilePathResolver/LoggingPathResolver.php 2024-02-21 03:28:24.845838500 +0100
+++ phpactor/lib/FilePathResolver/LoggingPathResolver.php 2024-02-21 05:32:06.874791500 +0100
@@ -17,6 +17,7 @@
public function resolve(string $path): string
{
$resolvedPath = $this->pathResolver->resolve($path);
+ $resolvedPath = ltrim($resolvedPath, '/\\');
$this->logger->log(
$this->level,
sprintf(
diff -u phpactor_orig/lib/Indexer/Extension/Command/IndexBuildCommand.php phpactor/lib/Indexer/Extension/Command/IndexBuildCommand.php
--- phpactor_orig/lib/Indexer/Extension/Command/IndexBuildCommand.php 2024-02-21 03:28:24.880577000 +0100
+++ phpactor/lib/Indexer/Extension/Command/IndexBuildCommand.php 2024-02-21 05:35:54.829720700 +0100
@@ -106,13 +106,6 @@
Loop::run(function () use ($output) {
$process = yield $this->watcher->watch();
- Loop::onSignal(SIGINT, function () use ($output, $process): void {
- $output->write('Shutting down watchers...');
- $process->stop();
- $output->writeln('done');
- Loop::stop();
- });
-
$output->writeln(sprintf('<info>Watching for file changes with </>%s<info>...</>', $this->watcher->describe()));
while (null !== $file = yield $process->wait()) {
diff -u phpactor_orig/lib/Indexer/Extension/Rpc/IndexHandler.php phpactor/lib/Indexer/Extension/Rpc/IndexHandler.php
--- phpactor_orig/lib/Indexer/Extension/Rpc/IndexHandler.php 2024-02-21 03:28:24.883507800 +0100
+++ phpactor/lib/Indexer/Extension/Rpc/IndexHandler.php 2024-02-21 05:18:05.593248200 +0100
@@ -49,7 +49,7 @@
assert($file instanceof ModifiedFile);
$job = $this->indexer->getJob($file->path());
$job->run();
- yield new Delayed($arguments[self::PARAM_INTERVAL]);
+ // yield new Delayed($arguments[self::PARAM_INTERVAL]);
}
});
}
diff -u phpactor_orig/vendor/amphp/process/lib/Internal/Windows/Runner.php phpactor/vendor/amphp/process/lib/Internal/Windows/Runner.php
--- phpactor_orig/vendor/amphp/process/lib/Internal/Windows/Runner.php 2022-07-07 01:50:12.000000000 +0200
+++ phpactor/vendor/amphp/process/lib/Internal/Windows/Runner.php 2024-02-21 05:39:34.813929900 +0100
@@ -82,6 +82,10 @@
throw new ProcessException("Can't execute commands that contain null bytes.");
}
+ if ($cwd !== null) {
+ $cwd = ltrim($cwd, '/\\');
+ }
+
$options['bypass_shell'] = true;
$handle = new Handle;
diff -u phpactor_orig/vendor/amphp/process/lib/Internal/Windows/SocketConnector.php phpactor/vendor/amphp/process/lib/Internal/Windows/SocketConnector.php
--- phpactor_orig/vendor/amphp/process/lib/Internal/Windows/SocketConnector.php 2022-07-07 01:50:12.000000000 +0200
+++ phpactor/vendor/amphp/process/lib/Internal/Windows/SocketConnector.php 2024-02-21 01:44:33.051906400 +0100
@@ -16,7 +16,7 @@
{
const SERVER_SOCKET_URI = 'tcp://127.0.0.1:0';
const SECURITY_TOKEN_SIZE = 16;
- const CONNECT_TIMEOUT = 1000;
+ const CONNECT_TIMEOUT = 30000;
/** @var resource */
private $server;
diff -u phpactor_orig/vendor/phpactor/language-server/lib/Core/Server/LanguageServer.php phpactor/vendor/phpactor/language-server/lib/Core/Server/LanguageServer.php
--- phpactor_orig/vendor/phpactor/language-server/lib/Core/Server/LanguageServer.php 2023-09-22 10:53:04.000000000 +0200
+++ phpactor/vendor/phpactor/language-server/lib/Core/Server/LanguageServer.php 2024-02-21 05:28:24.687710200 +0100
@@ -93,11 +93,6 @@
*/
public function run(): void
{
- Loop::onSignal(SIGINT, function (string $watcherId) {
- Loop::cancel($watcherId);
- yield $this->shutdown();
- });
-
Loop::setErrorHandler(function (Throwable $error): void {
if ($error instanceof ShutdownServer) {
Loop::stop();
diff -u phpactor_orig/vendor/symfony/filesystem/Path.php phpactor/vendor/symfony/filesystem/Path.php
--- phpactor_orig/vendor/symfony/filesystem/Path.php 2024-01-23 14:51:25.000000000 +0100
+++ phpactor/vendor/symfony/filesystem/Path.php 2024-02-21 05:40:56.691359900 +0100
@@ -771,6 +771,10 @@
*/
private static function split(string $path): array
{
+ if (str_starts_with($path, '/')) {
+ $path = ltrim($path, '/');
+ }
+
if ('' === $path) {
return ['', ''];
}There are four kinds of hacks here:
- Commented out a few instances of
yield new Delayed(…). Otherwise, the event loop would sometimes get stuck, and the Phpactor server would stop processing any requests. This occurred with both Amp\Loop\NativeDriver and with Amp\Loop\EventDriver. I can't figure out why it happens, but it's true. I'm hoping you might have some idea. (Maybe this gets magically resolved in the future by the new AMPHP version that no longer ships its own event loop, as promised at https://amphp.org/upgrade#event-loop – I didn't try to install or even find it.)
(Commenting them out presumably prevents other coroutines from running in these places, but it didn't seem to break anything for me.) - Added trimming of leading
/from file paths in several places. The paths looked like/C:, which is invalid. Some Unix path handling must be leaking somewhere, but I didn't really try to figure out where the bad data was coming from. Apart from this, Windows paths seem to be handled well. - Removed a few instances of
Loop::onSignal(SIGINT, …). Signals are not supported on Windows. This is only used to "gracefully shut down" the server, and I'm not sure if that's even necessary, but it might be possible to implement something equivalent using sapi_windows_set_ctrl_handler().
(And then you could remove the dependencies onext-pcntlandext-posixfrom composer.json – as far as I can tell, the only thing you're directly using from them is theSIGINTconstant, everything else is handled by libraries that already support Windows.) - Increased a timeout in amphp/process. I ran into issues that looked like Moderate to high concurrency load causes "Failed to launch process" errors on Windows amphp/process#21. This might be caused by my commenting-out of those
yield new Delayed(…)calls?
Lastly, I had the server crash like this a couple of times. I couldn't reproduce it when I tried, and restarting it made it go away.
Fatal error: Uncaught TypeError: stream_select(): supplied resource is not a valid stream resource in C:\dev\phpactor\vendor\amphp\amp\lib\Loop\NativeDriver.php:300
Stack trace:
#0 C:\dev\phpactor\vendor\amphp\amp\lib\Loop\NativeDriver.php(300): stream_select(Array, Array, Array, 0, 0)
#1 C:\dev\phpactor\vendor\amphp\amp\lib\Loop\NativeDriver.php(127): Amp\Loop\NativeDriver->selectStreams(Array, Array, 0)
#2 C:\dev\phpactor\vendor\amphp\amp\lib\Loop\Driver.php(138): Amp\Loop\NativeDriver->dispatch(false)
#3 C:\dev\phpactor\vendor\amphp\amp\lib\Loop\Driver.php(72): Amp\Loop\Driver->tick()
#4 C:\dev\phpactor\vendor\amphp\amp\lib\Loop.php(95): Amp\Loop\Driver->run()
#5 C:\dev\phpactor\vendor\phpactor\language-server\lib\Core\Server\LanguageServer.php(108): Amp\Loop::run(Object(Closure))
#6 C:\dev\phpactor\lib\Extension\LanguageServer\Command\StartCommand.php(50): Phpactor\LanguageServer\Core\Server\LanguageServer->run()
#7 C:\dev\phpactor\vendor\symfony\console\Command\Command.php(298): Phpactor\Extension\LanguageServer\Command\StartCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#8 C:\dev\phpactor\vendor\symfony\console\Application.php(1040): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#9 C:\dev\phpactor\vendor\symfony\console\Application.php(301): Symfony\Component\Console\Application->doRunCommand(Object(Phpactor\Extension\LanguageServer\Command\StartCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 C:\dev\phpactor\lib\Application.php(48): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#11 C:\dev\phpactor\vendor\symfony\console\Application.php(171): Phpactor\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#12 C:\dev\phpactor\bin\phpactor(46): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#13 {main}
If you have the time to offer some advice – or at least educated guesses – about what causes these problems, and outline how you'd want them to be fixed, I'd be happy to work on some proper patches, and get rid of my local hacks.
Even if you don't have the time for this, perhaps it helps someone else. :)