diff --git a/.gitignore b/.gitignore index 4fa1889..70fdc89 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ out/ node_modules/ vendor/ composer.lock +typings/ diff --git a/package.json b/package.json index e4e8719..76b249c 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "vscode": "^0.11.17" }, "dependencies": { + "semver": "^5.3.0", "vscode-languageclient": "^2.4.2-next.13" } } diff --git a/src/extension.ts b/src/extension.ts index 4ae49b3..1e095b6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,42 +1,66 @@ 'use strict'; import * as path from 'path'; -import { spawn } from 'child_process'; -import { ExtensionContext } from 'vscode'; -import { LanguageClient, LanguageClientOptions } from 'vscode-languageclient'; +import { spawn, execFile, ChildProcess } from 'child_process'; +import * as vscode from 'vscode'; +import { LanguageClient, LanguageClientOptions, StreamInfo } from 'vscode-languageclient'; +import * as semver from 'semver'; -export function activate(context: ExtensionContext) { +export function activate(context: vscode.ExtensionContext) { - // The server is implemented in PHP - const serverPath = context.asAbsolutePath(path.join('vendor', 'felixfbecker', 'language-server', 'bin', 'php-language-server.php')); + // Check if PHP is available and version is ^7.0.0 + execFile('php', ['--version'], (err: NodeJS.ErrnoException, stdout: Buffer, stderr: Buffer) => { - const serverOptions = () => { - const childProcess = spawn('php', [serverPath]); - childProcess.stderr.on('data', (chunk: Buffer) => { - console.error(chunk + ''); - }); - childProcess.stdout.on('data', (chunk: Buffer) => { - console.log(chunk + ''); - }); - return Promise.resolve(childProcess); - }; + if (err) { + if (err.code === 'ENOENT') { + vscode.window.showErrorMessage('PHP executable not found. You need PHP 7 installed and in your PATH'); + } else { + vscode.window.showErrorMessage('Error spawning PHP: ' + err.message); + console.error(err); + } + return; + } - // Options to control the language client - let clientOptions: LanguageClientOptions = { - // Register the server for php documents - documentSelector: ['php'] - // synchronize: { - // // Synchronize the setting section 'php' to the server - // configurationSection: 'php', - // // Notify the server about file changes to composer.json files contain in the workspace - // fileEvents: workspace.createFileSystemWatcher('**/composer.json') - // } - }; + let version = stdout.toString().match(/^PHP ([^\s]+)/)[1]; + // Convert PHP prerelease format like 7.0.0rc1 to 7.0.0-rc1 + if (!/^\d+.\d+.\d+$/.test(version)) { + version = version.replace(/(\d+.\d+.\d+)/, '$1-'); + } + if (!semver.satisfies(version, '^7.0.0')) { + vscode.window.showErrorMessage('The language server needs at least PHP 7 installed and in your PATH. Version found: ' + version); + return; + } - // Create the language client and start the client. - const disposable = new LanguageClient('PHP Language Client', serverOptions, clientOptions).start(); + const serverOptions = (): Promise => { + // The server is implemented in PHP + const serverPath = context.asAbsolutePath(path.join('vendor', 'felixfbecker', 'language-server', 'bin', 'php-language-server.php')); + const childProcess = spawn('php', [serverPath]); + childProcess.stderr.on('data', (chunk: Buffer) => { + console.error(chunk + ''); + }); + childProcess.stdout.on('data', (chunk: Buffer) => { + console.log(chunk + ''); + }); + return Promise.resolve(childProcess); + }; - // Push the disposable to the context's subscriptions so that the - // client can be deactivated on extension deactivation - context.subscriptions.push(disposable); + // Options to control the language client + let clientOptions: LanguageClientOptions = { + // Register the server for php documents + documentSelector: ['php'] + // synchronize: { + // // Synchronize the setting section 'php' to the server + // configurationSection: 'php', + // // Notify the server about file changes to composer.json files contain in the workspace + // fileEvents: workspace.createFileSystemWatcher('**/composer.json') + // } + }; + + // Create the language client and start the client. + const disposable = new LanguageClient('PHP Language Client', serverOptions, clientOptions).start(); + + // Push the disposable to the context's subscriptions so that the + // client can be deactivated on extension deactivation + context.subscriptions.push(disposable); + }); } diff --git a/tsconfig.json b/tsconfig.json index 7076a94..bf51f69 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "moduleResolution": "node", "outDir": "out", "noLib": true, + "noImplicitAny": true, "sourceMap": true, "rootDir": "src" }, diff --git a/typings.json b/typings.json new file mode 100644 index 0000000..ae4f099 --- /dev/null +++ b/typings.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "semver": "registry:npm/semver#5.0.0+20160723033700" + } +}