import execa from 'execa' ; import { ChildProcess , spawn } from 'mz/child_process' ; import * as net from 'net' ; import * as path from 'path' ; import * as semver from 'semver' ; import * as url from 'url' ; import * as vscode from 'vscode' ; import { LanguageClient , LanguageClientOptions , RevealOutputChannelOn , StreamInfo } from 'vscode-languageclient' ; const composerJson = require ( '../composer.json' ) ; export async function activate ( context ) { const conf = vscode . workspace . getConfiguration ( 'php' ) ; const executablePath = conf . get < string > ( 'executablePath' ) || conf . get < string > ( 'validate.executablePath' ) || ( process . platform === 'win32' ? 'php.exe' : 'php' ) ; const memoryLimit = conf . get < string > ( 'memoryLimit' ) || '4095M' ; if ( memoryLimit !== '-1' && ! /^\d+[KMG]?$/ . exec ( memoryLimit ) ) { const selected = await vscode . window . showErrorMessage ( 'The memory limit you\'d provided is not numeric, nor "-1" nor valid php shorthand notation!' , 'Open settings' ) ; if ( selected === 'Open settings' ) { await vscode . commands . executeCommand ( 'workbench.action.openGlobalSettings' ) ; } return ; } let stdout ; try { stdout = await execa . stdout ( executablePath , [ '--version' ] ) ; } catch ( err ) { if ( err . code === 'ENOENT' ) { const selected = await vscode . window . showErrorMessage ( 'PHP executable not found. Install PHP 7 and add it to your PATH or set the php.executablePath setting' , 'Open settings' ) ; if ( selected === 'Open settings' ) { await vscode . commands . executeCommand ( 'workbench.action.openGlobalSettings' ) ; } } else { vscode . window . showErrorMessage ( 'Error spawning PHP: ' + err . message ) ; console . error ( err ) ; } return ; } const match = stdout . match ( /^PHP ([^\s]+)/m ) ; if ( ! match ) { vscode . window . showErrorMessage ( 'Error parsing PHP version. Please check the output of php --version' ) ; return ; } let version = match [ 1 ] . split ( '-' ) [ 0 ] ; if ( ! /^\d+.\d+.\d+$/ . test ( version ) ) { version = version . replace ( /(\d+.\d+.\d+)/ , '$1-' ) ; } if ( semver . lt ( version , composerJson . config . platform . php ) ) { vscode . window . showErrorMessage ( 'The language server needs at least PHP 7.1 installed. Version found: ' + version ) ; return ; } let client ; const serverOptions = ( ) => new Promise < ChildProcess | StreamInfo > ( ( resolve , reject ) => { const server = net . createServer ( socket => { console . log ( 'PHP process connected' ) ; socket . on ( 'end' , ( ) => { console . log ( 'PHP process disconnected' ) ; } ) ; server . close ( ) ; resolve ( { reader : socket , writer : socket } ) ; } ) ; server . listen ( 0 , '127.0.0.1' , ( ) => { const childProcess = spawn ( executablePath , [ context . asAbsolutePath ( path . join ( 'vendor' , 'felixfbecker' , 'language-server' , 'bin' , 'php-language-server.php' ) ) , '--tcp=127.0.0.1:' + server . address ( ) . port , '--memory-limit=' + memoryLimit , ] ) ; childProcess . stderr . on ( 'data' , ( chunk ) => { const str = chunk . toString ( ) ; console . log ( 'PHP Language Server:' , str ) ; client . outputChannel . appendLine ( str ) ; } ) ; childProcess . on ( 'exit' , ( code , signal ) => { client . outputChannel . appendLine ( `Language server exited ` + ( signal ? `from signal ${ signal }` : `with exit code ${ code }` ) ) ; if ( code !== 0 ) { client . outputChannel . show ( ) ; } } ) ; return childProcess ; } ) ; } ) ; const clientOptions = { documentSelector : [ { scheme : 'file' , language : 'php' } , { scheme : 'untitled' , language : 'php' } ] , revealOutputChannelOn : RevealOutputChannelOn . Never , uriConverters : { code2Protocol : uri => url . format ( url . parse ( uri . toString ( true ) ) ) , protocol2Code : str => vscode . Uri . parse ( str ) , } , synchronize : { configurationSection : 'php' , fileEvents : vscode . workspace . createFileSystemWatcher ( '**/*.php' ) , } , } ; client = new LanguageClient ( 'PHP Language Server' , serverOptions , clientOptions ) ; const disposable = client . start ( ) ; context . subscriptions . push ( disposable ) ; }