php: Allow using `intelephense` from PATH (#14643)

Marshall Bowers created

This PR updates the PHP extension to use `intelephense` from the PATH,
if it exists.

Tested using the following Nix shell:

```sh
NIXPKGS_ALLOW_UNFREE=1 nix-shell -p php nodePackages_latest.intelephense
```

Resolves #11994.

Release Notes:

- N/A

Change summary

extensions/php/src/language_servers/intelephense.rs | 32 ++++++++++++++
extensions/php/src/php.rs                           | 17 -------
2 files changed, 31 insertions(+), 18 deletions(-)

Detailed changes

extensions/php/src/language_servers/intelephense.rs 🔗

@@ -1,4 +1,4 @@
-use std::fs;
+use std::{env, fs};
 
 use zed_extension_api::{self as zed, LanguageServerId, Result};
 
@@ -18,11 +18,39 @@ impl Intelephense {
         }
     }
 
+    pub fn language_server_command(
+        &mut self,
+        language_server_id: &LanguageServerId,
+        worktree: &zed::Worktree,
+    ) -> Result<zed::Command> {
+        if let Some(path) = worktree.which("intelephense") {
+            return Ok(zed::Command {
+                command: path,
+                args: vec!["--stdio".to_string()],
+                env: Default::default(),
+            });
+        }
+
+        let server_path = self.server_script_path(language_server_id)?;
+        Ok(zed::Command {
+            command: zed::node_binary_path()?,
+            args: vec![
+                env::current_dir()
+                    .unwrap()
+                    .join(&server_path)
+                    .to_string_lossy()
+                    .to_string(),
+                "--stdio".to_string(),
+            ],
+            env: Default::default(),
+        })
+    }
+
     fn server_exists(&self) -> bool {
         fs::metadata(SERVER_PATH).map_or(false, |stat| stat.is_file())
     }
 
-    pub fn server_script_path(&mut self, language_server_id: &LanguageServerId) -> Result<String> {
+    fn server_script_path(&mut self, language_server_id: &LanguageServerId) -> Result<String> {
         let server_exists = self.server_exists();
         if self.did_find_server && server_exists {
             return Ok(SERVER_PATH.to_string());

extensions/php/src/php.rs 🔗

@@ -1,7 +1,5 @@
 mod language_servers;
 
-use std::env;
-
 use zed_extension_api::{self as zed, LanguageServerId, Result};
 
 use crate::language_servers::{Intelephense, Phpactor};
@@ -27,20 +25,7 @@ impl zed::Extension for PhpExtension {
         match language_server_id.as_ref() {
             Intelephense::LANGUAGE_SERVER_ID => {
                 let intelephense = self.intelephense.get_or_insert_with(|| Intelephense::new());
-
-                let server_path = intelephense.server_script_path(language_server_id)?;
-                Ok(zed::Command {
-                    command: zed::node_binary_path()?,
-                    args: vec![
-                        env::current_dir()
-                            .unwrap()
-                            .join(&server_path)
-                            .to_string_lossy()
-                            .to_string(),
-                        "--stdio".to_string(),
-                    ],
-                    env: Default::default(),
-                })
+                intelephense.language_server_command(language_server_id, worktree)
             }
             Phpactor::LANGUAGE_SERVER_ID => {
                 let phpactor = self.phpactor.get_or_insert_with(|| Phpactor::new());