@@ -4,9 +4,11 @@ import {
44 LanguageClientOptions ,
55 StreamInfo ,
66 RevealOutputChannelOn ,
7- } from 'vscode-languageclient'
7+ } from 'vscode-languageclient/node '
88import { EvaluatableExpressionRequest } from './protocol'
99
10+ import { spawn } from 'child_process'
11+
1012import * as vscode from 'vscode'
1113import { join } from 'path'
1214import * as net from 'net'
@@ -29,7 +31,7 @@ interface PhpactorConfig {
2931 launchServerArgs : string [ ]
3032}
3133
32- export function activate ( context : vscode . ExtensionContext ) : void {
34+ export async function activate ( context : vscode . ExtensionContext ) : Promise < void > {
3335 if ( ! checkPlatform ( ) ) {
3436 return
3537 }
@@ -56,7 +58,7 @@ export function activate(context: vscode.ExtensionContext): void {
5658 if ( enable === false ) return
5759
5860 languageClient = createClient ( config )
59- languageClient . start ( )
61+ await languageClient . start ( )
6062
6163 context . subscriptions . push (
6264 vscode . languages . registerEvaluatableExpressionProvider ( 'php' , {
@@ -98,7 +100,10 @@ function checkPlatform(): boolean {
98100
99101function getServerOptions ( config : PhpactorConfig ) : ServerOptions {
100102 let serverOptions
101- if ( ! config . remote . enabled ) {
103+ if ( ! config . remote . enabled && process . platform === 'win32' ) {
104+ // PHP on windows has problems with STDIO so we need special handling
105+ serverOptions = getWindowsServerOptions ( config )
106+ } else if ( ! config . remote . enabled ) {
102107 // launch language server via stdio
103108 serverOptions = < ServerOptions > {
104109 run : {
@@ -139,6 +144,71 @@ function getServerOptions(config: PhpactorConfig): ServerOptions {
139144 return serverOptions
140145}
141146
147+ function getWindowsServerOptions ( config : PhpactorConfig ) : ServerOptions {
148+ // Find a free port, start PHPActor and connect to it
149+ const serverOptions = async ( ) => {
150+ const findPort = new Promise < number > ( resolve => {
151+ const server = net . createServer ( )
152+ server . listen ( 0 , '127.0.0.1' , ( ) => {
153+ const freePort = ( server . address ( ) ! as net . AddressInfo ) . port
154+ server . close ( )
155+ resolve ( freePort )
156+ } )
157+ } )
158+
159+ const freePort = await findPort
160+
161+ const startServer = new Promise < void > ( ( resolve , reject ) => {
162+ const childProcess = spawn (
163+ config . executablePath ,
164+ [ config . path , 'language-server' , `--address=127.0.0.1:${ freePort } ` , ...config . launchServerArgs ] ,
165+
166+ {
167+ env : {
168+ ...process . env ,
169+ XDEBUG_MODE : 'debug' ,
170+ PHPACTOR_ALLOW_XDEBUG : '1' ,
171+ } ,
172+ }
173+ )
174+
175+ childProcess . stderr . on ( 'data' , ( chunk : Buffer ) => {
176+ const str = chunk . toString ( )
177+ languageClient . outputChannel . appendLine ( str )
178+
179+ // when we get the first line, the server is running
180+ resolve ( )
181+ } )
182+ childProcess . on ( 'exit' , ( code , signal ) => {
183+ languageClient . outputChannel . appendLine (
184+ `Language server exited ` + ( signal ? `from signal ${ signal } ` : `with exit code ${ code } ` )
185+ )
186+ if ( code !== 0 ) {
187+ languageClient . outputChannel . show ( )
188+ }
189+
190+ reject ( new Error ( 'Language Server exited' ) )
191+ } )
192+ } )
193+
194+ await startServer
195+
196+ const socket = net . connect ( {
197+ host : '127.0.0.1' ,
198+ port : freePort ,
199+ } )
200+
201+ const result = < StreamInfo > {
202+ writer : socket ,
203+ reader : socket ,
204+ }
205+
206+ return result
207+ }
208+
209+ return serverOptions
210+ }
211+
142212function createClient ( config : PhpactorConfig ) : LanguageClient {
143213 const serverOptions = getServerOptions ( config )
144214
0 commit comments