| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  | 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, StreamInfo } from 'vscode-languageclient' | 
					
						
							| 
									
										
										
										
											2016-09-09 18:11:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  | export async function activate(context: vscode.ExtensionContext): Promise<void> { | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |     const conf = vscode.workspace.getConfiguration('php') | 
					
						
							|  |  |  |     const executablePath = | 
					
						
							|  |  |  |         conf.get<string>('executablePath') || | 
					
						
							| 
									
										
										
										
											2018-11-11 04:16:58 +00:00
										 |  |  |         conf.get<string>('validate.executablePath') || | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |         (process.platform === 'win32' ? 'php.exe' : 'php') | 
					
						
							| 
									
										
										
										
											2016-09-09 18:11:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |     const memoryLimit = conf.get<string>('memoryLimit') || '4095M' | 
					
						
							| 
									
										
										
										
											2017-04-10 20:36:10 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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' | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2017-04-10 20:36:10 +00:00
										 |  |  |         if (selected === 'Open settings') { | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |             await vscode.commands.executeCommand('workbench.action.openGlobalSettings') | 
					
						
							| 
									
										
										
										
											2017-04-10 20:36:10 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2017-04-10 20:36:10 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |     // Check path (if PHP is available and version is ^7.0.0)
 | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |     let stdout: string | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |     try { | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |         stdout = await execa.stdout(executablePath, ['--version']) | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |     } 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' | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |             ) | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |             if (selected === 'Open settings') { | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |                 await vscode.commands.executeCommand('workbench.action.openGlobalSettings') | 
					
						
							| 
									
										
										
										
											2016-09-09 18:11:24 +00:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |             vscode.window.showErrorMessage('Error spawning PHP: ' + err.message) | 
					
						
							|  |  |  |             console.error(err) | 
					
						
							| 
									
										
										
										
											2016-09-09 18:11:24 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-09-09 18:11:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |     // Parse version and discard OS info like 7.0.8--0ubuntu0.16.04.2
 | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |     const match = stdout.match(/^PHP ([^\s]+)/m) | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |     if (!match) { | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |         vscode.window.showErrorMessage('Error parsing PHP version. Please check the output of php --version') | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |     let version = match[1].split('-')[0] | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |     // Convert PHP prerelease format like 7.0.0rc1 to 7.0.0-rc1
 | 
					
						
							|  |  |  |     if (!/^\d+.\d+.\d+$/.test(version)) { | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |         version = version.replace(/(\d+.\d+.\d+)/, '$1-') | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |     if (semver.lt(version, '7.0.0')) { | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |         vscode.window.showErrorMessage('The language server needs at least PHP 7 installed. Version found: ' + version) | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-09-09 18:11:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |     let client: LanguageClient | 
					
						
							| 
									
										
										
										
											2018-11-11 04:16:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |     const serverOptions = () => | 
					
						
							|  |  |  |         new Promise<ChildProcess | StreamInfo>((resolve, reject) => { | 
					
						
							|  |  |  |             // Use a TCP socket because of problems with blocking STDIO
 | 
					
						
							|  |  |  |             const server = net.createServer(socket => { | 
					
						
							|  |  |  |                 // 'connection' listener
 | 
					
						
							|  |  |  |                 console.log('PHP process connected') | 
					
						
							|  |  |  |                 socket.on('end', () => { | 
					
						
							|  |  |  |                     console.log('PHP process disconnected') | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |                 server.close() | 
					
						
							|  |  |  |                 resolve({ reader: socket, writer: socket }) | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |             // Listen on random port
 | 
					
						
							|  |  |  |             server.listen(0, '127.0.0.1', () => { | 
					
						
							|  |  |  |                 // The server is implemented in PHP
 | 
					
						
							|  |  |  |                 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: Buffer) => { | 
					
						
							|  |  |  |                     const str = chunk.toString() | 
					
						
							|  |  |  |                     console.log('PHP Language Server:', str) | 
					
						
							|  |  |  |                     client.outputChannel.appendLine(str) | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |                 // childProcess.stdout.on('data', (chunk: Buffer) => {
 | 
					
						
							|  |  |  |                 //     console.log('PHP Language Server:', chunk + '');
 | 
					
						
							|  |  |  |                 // });
 | 
					
						
							|  |  |  |                 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 | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2016-09-09 18:11:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |     // Options to control the language client
 | 
					
						
							|  |  |  |     const clientOptions: LanguageClientOptions = { | 
					
						
							|  |  |  |         // Register the server for php documents
 | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |         documentSelector: [{ scheme: 'file', language: 'php' }, { scheme: 'untitled', language: 'php' }], | 
					
						
							| 
									
										
										
										
											2016-10-21 16:40:06 +00:00
										 |  |  |         uriConverters: { | 
					
						
							|  |  |  |             // VS Code by default %-encodes even the colon after the drive letter
 | 
					
						
							|  |  |  |             // NodeJS handles it much better
 | 
					
						
							|  |  |  |             code2Protocol: uri => url.format(url.parse(uri.toString(true))), | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |             protocol2Code: str => vscode.Uri.parse(str), | 
					
						
							| 
									
										
										
										
											2016-10-21 16:40:06 +00:00
										 |  |  |         }, | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |         synchronize: { | 
					
						
							|  |  |  |             // Synchronize the setting section 'php' to the server
 | 
					
						
							| 
									
										
										
										
											2017-04-10 12:09:47 +00:00
										 |  |  |             configurationSection: 'php', | 
					
						
							|  |  |  |             // Notify the server about changes to PHP files in the workspace
 | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |             fileEvents: vscode.workspace.createFileSystemWatcher('**/*.php'), | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-09-09 18:11:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |     // Create the language client and start the client.
 | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |     client = new LanguageClient('PHP Language Server', serverOptions, clientOptions) | 
					
						
							|  |  |  |     const disposable = client.start() | 
					
						
							| 
									
										
										
										
											2016-09-09 18:11:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-12 08:26:08 +00:00
										 |  |  |     // Push the disposable to the context's subscriptions so that the
 | 
					
						
							|  |  |  |     // client can be deactivated on extension deactivation
 | 
					
						
							| 
									
										
										
										
											2018-11-11 17:32:17 +00:00
										 |  |  |     context.subscriptions.push(disposable) | 
					
						
							| 
									
										
										
										
											2016-08-25 15:55:00 +00:00
										 |  |  | } |