debugger: Remove PHP debug adapter (#34020)

Piotr Osiewicz created

This commit removes the PHP debug adapter in favor of a new version
(0.3.0) of PHP extension.
The name of a debug adapter has been changed from "PHP" to "Xdebug",
which makes this a breaking change in user-configured scenarios

Release Notes:

- debugger: PHP debug adapter is no longer shipped in core Zed editor;
it is now available in PHP extension (starting with version 0.3.0). The
adapter has been renamed from `PHP` to `Xdebug`, which might break your
user-defined debug scenarios.

Change summary

assets/settings/initial_debug_tasks.json          |   7 
crates/dap_adapters/src/dap_adapters.rs           |   3 
crates/dap_adapters/src/php.rs                    | 368 -----------------
crates/debugger_ui/src/tests/new_process_modal.rs |   1 
crates/task/src/vscode_debug_format.rs            |   2 
5 files changed, 1 insertion(+), 380 deletions(-)

Detailed changes

assets/settings/initial_debug_tasks.json 🔗

@@ -3,13 +3,6 @@
 // For more documentation on how to configure debug tasks,
 // see: https://zed.dev/docs/debugger
 [
-  {
-    "label": "Debug active PHP file",
-    "adapter": "PHP",
-    "program": "$ZED_FILE",
-    "request": "launch",
-    "cwd": "$ZED_WORKTREE_ROOT"
-  },
   {
     "label": "Debug active Python file",
     "adapter": "Debugpy",

crates/dap_adapters/src/dap_adapters.rs 🔗

@@ -2,7 +2,6 @@ mod codelldb;
 mod gdb;
 mod go;
 mod javascript;
-mod php;
 mod python;
 
 use std::sync::Arc;
@@ -22,7 +21,6 @@ use gdb::GdbDebugAdapter;
 use go::GoDebugAdapter;
 use gpui::{App, BorrowAppContext};
 use javascript::JsDebugAdapter;
-use php::PhpDebugAdapter;
 use python::PythonDebugAdapter;
 use serde_json::json;
 use task::{DebugScenario, ZedDebugConfig};
@@ -31,7 +29,6 @@ pub fn init(cx: &mut App) {
     cx.update_default_global(|registry: &mut DapRegistry, _cx| {
         registry.add_adapter(Arc::from(CodeLldbDebugAdapter::default()));
         registry.add_adapter(Arc::from(PythonDebugAdapter::default()));
-        registry.add_adapter(Arc::from(PhpDebugAdapter::default()));
         registry.add_adapter(Arc::from(JsDebugAdapter::default()));
         registry.add_adapter(Arc::from(GoDebugAdapter::default()));
         registry.add_adapter(Arc::from(GdbDebugAdapter));

crates/dap_adapters/src/php.rs 🔗

@@ -1,368 +0,0 @@
-use adapters::latest_github_release;
-use anyhow::Context as _;
-use anyhow::bail;
-use dap::StartDebuggingRequestArguments;
-use dap::StartDebuggingRequestArgumentsRequest;
-use dap::adapters::{DebugTaskDefinition, TcpArguments};
-use gpui::{AsyncApp, SharedString};
-use language::LanguageName;
-use std::{collections::HashMap, path::PathBuf, sync::OnceLock};
-use util::ResultExt;
-
-use crate::*;
-
-#[derive(Default)]
-pub(crate) struct PhpDebugAdapter {
-    checked: OnceLock<()>,
-}
-
-impl PhpDebugAdapter {
-    const ADAPTER_NAME: &'static str = "PHP";
-    const ADAPTER_PACKAGE_NAME: &'static str = "vscode-php-debug";
-    const ADAPTER_PATH: &'static str = "extension/out/phpDebug.js";
-
-    async fn fetch_latest_adapter_version(
-        &self,
-        delegate: &Arc<dyn DapDelegate>,
-    ) -> Result<AdapterVersion> {
-        let release = latest_github_release(
-            &format!("{}/{}", "xdebug", Self::ADAPTER_PACKAGE_NAME),
-            true,
-            false,
-            delegate.http_client(),
-        )
-        .await?;
-
-        let asset_name = format!("php-debug-{}.vsix", release.tag_name.replace("v", ""));
-
-        Ok(AdapterVersion {
-            tag_name: release.tag_name,
-            url: release
-                .assets
-                .iter()
-                .find(|asset| asset.name == asset_name)
-                .with_context(|| format!("no asset found matching {asset_name:?}"))?
-                .browser_download_url
-                .clone(),
-        })
-    }
-
-    async fn get_installed_binary(
-        &self,
-        delegate: &Arc<dyn DapDelegate>,
-        task_definition: &DebugTaskDefinition,
-        user_installed_path: Option<PathBuf>,
-        user_args: Option<Vec<String>>,
-        _: &mut AsyncApp,
-    ) -> Result<DebugAdapterBinary> {
-        let adapter_path = if let Some(user_installed_path) = user_installed_path {
-            user_installed_path
-        } else {
-            let adapter_path = paths::debug_adapters_dir().join(self.name().as_ref());
-
-            let file_name_prefix = format!("{}_", self.name());
-
-            util::fs::find_file_name_in_dir(adapter_path.as_path(), |file_name| {
-                file_name.starts_with(&file_name_prefix)
-            })
-            .await
-            .context("Couldn't find PHP dap directory")?
-        };
-
-        let tcp_connection = task_definition.tcp_connection.clone().unwrap_or_default();
-        let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
-
-        let mut configuration = task_definition.config.clone();
-        if let Some(obj) = configuration.as_object_mut() {
-            obj.entry("cwd")
-                .or_insert_with(|| delegate.worktree_root_path().to_string_lossy().into());
-        }
-
-        let arguments = if let Some(mut args) = user_args {
-            args.insert(
-                0,
-                adapter_path
-                    .join(Self::ADAPTER_PATH)
-                    .to_string_lossy()
-                    .to_string(),
-            );
-            args
-        } else {
-            vec![
-                adapter_path
-                    .join(Self::ADAPTER_PATH)
-                    .to_string_lossy()
-                    .to_string(),
-                format!("--server={}", port),
-            ]
-        };
-
-        Ok(DebugAdapterBinary {
-            command: Some(
-                delegate
-                    .node_runtime()
-                    .binary_path()
-                    .await?
-                    .to_string_lossy()
-                    .into_owned(),
-            ),
-            arguments,
-            connection: Some(TcpArguments {
-                port,
-                host,
-                timeout,
-            }),
-            cwd: Some(delegate.worktree_root_path().to_path_buf()),
-            envs: HashMap::default(),
-            request_args: StartDebuggingRequestArguments {
-                configuration,
-                request: <Self as DebugAdapter>::request_kind(self, &task_definition.config)
-                    .await?,
-            },
-        })
-    }
-}
-
-#[async_trait(?Send)]
-impl DebugAdapter for PhpDebugAdapter {
-    fn dap_schema(&self) -> serde_json::Value {
-        json!({
-            "properties": {
-                "request": {
-                    "type": "string",
-                    "enum": ["launch"],
-                    "description": "The request type for the PHP debug adapter, always \"launch\"",
-                    "default": "launch"
-                },
-                "hostname": {
-                    "type": "string",
-                    "description": "The address to bind to when listening for Xdebug (default: all IPv6 connections if available, else all IPv4 connections) or Unix Domain socket (prefix with unix://) or Windows Pipe (\\\\?\\pipe\\name) - cannot be combined with port"
-                },
-                "port": {
-                    "type": "integer",
-                    "description": "The port on which to listen for Xdebug (default: 9003). If port is set to 0 a random port is chosen by the system and a placeholder ${port} is replaced with the chosen port in env and runtimeArgs.",
-                    "default": 9003
-                },
-                "program": {
-                    "type": "string",
-                    "description": "The PHP script to debug (typically a path to a file)",
-                    "default": "${file}"
-                },
-                "cwd": {
-                    "type": "string",
-                    "description": "Working directory for the debugged program"
-                },
-                "args": {
-                    "type": "array",
-                    "items": {
-                        "type": "string"
-                    },
-                    "description": "Command line arguments to pass to the program"
-                },
-                "env": {
-                    "type": "object",
-                    "description": "Environment variables to pass to the program",
-                    "additionalProperties": {
-                        "type": "string"
-                    }
-                },
-                "stopOnEntry": {
-                    "type": "boolean",
-                    "description": "Whether to break at the beginning of the script",
-                    "default": false
-                },
-                "pathMappings": {
-                    "type": "object",
-                    "description": "A mapping of server paths to local paths.",
-                },
-                "log": {
-                    "type": "boolean",
-                    "description": "Whether to log all communication between editor and the adapter to the debug console",
-                    "default": false
-                },
-                "ignore": {
-                    "type": "array",
-                    "description": "An array of glob patterns that errors should be ignored from (for example **/vendor/**/*.php)",
-                    "items": {
-                        "type": "string"
-                    }
-                },
-                "ignoreExceptions": {
-                    "type": "array",
-                    "description": "An array of exception class names that should be ignored (for example BaseException, \\NS1\\Exception, \\*\\Exception or \\**\\Exception*)",
-                    "items": {
-                        "type": "string"
-                    }
-                },
-                "skipFiles": {
-                    "type": "array",
-                    "description": "An array of glob patterns to skip when debugging. Star patterns and negations are allowed.",
-                    "items": {
-                        "type": "string"
-                    }
-                },
-                "skipEntryPaths": {
-                    "type": "array",
-                    "description": "An array of glob patterns to immediately detach from and ignore for debugging if the entry script matches",
-                    "items": {
-                        "type": "string"
-                    }
-                },
-                "maxConnections": {
-                    "type": "integer",
-                    "description": "Accept only this number of parallel debugging sessions. Additional connections will be dropped.",
-                    "default": 1
-                },
-                "proxy": {
-                    "type": "object",
-                    "description": "DBGp Proxy settings",
-                    "properties": {
-                        "enable": {
-                            "type": "boolean",
-                            "description": "To enable proxy registration",
-                            "default": false
-                        },
-                        "host": {
-                            "type": "string",
-                            "description": "The address of the proxy. Supports host name, IP address, or Unix domain socket.",
-                            "default": "127.0.0.1"
-                        },
-                        "port": {
-                            "type": "integer",
-                            "description": "The port where the adapter will register with the proxy",
-                            "default": 9001
-                        },
-                        "key": {
-                            "type": "string",
-                            "description": "A unique key that allows the proxy to match requests to your editor",
-                            "default": "vsc"
-                        },
-                        "timeout": {
-                            "type": "integer",
-                            "description": "The number of milliseconds to wait before giving up on the connection to proxy",
-                            "default": 3000
-                        },
-                        "allowMultipleSessions": {
-                            "type": "boolean",
-                            "description": "If the proxy should forward multiple sessions/connections at the same time or not",
-                            "default": true
-                        }
-                    }
-                },
-                "xdebugSettings": {
-                    "type": "object",
-                    "description": "Allows you to override Xdebug's remote debugging settings to fine tune Xdebug to your needs",
-                    "properties": {
-                        "max_children": {
-                            "type": "integer",
-                            "description": "Max number of array or object children to initially retrieve"
-                        },
-                        "max_data": {
-                            "type": "integer",
-                            "description": "Max amount of variable data to initially retrieve"
-                        },
-                        "max_depth": {
-                            "type": "integer",
-                            "description": "Maximum depth that the debugger engine may return when sending arrays, hashes or object structures to the IDE"
-                        },
-                        "show_hidden": {
-                            "type": "integer",
-                            "description": "Whether to show detailed internal information on properties (e.g. private members of classes). Zero means hidden members are not shown.",
-                            "enum": [0, 1]
-                        },
-                        "breakpoint_include_return_value": {
-                            "type": "boolean",
-                            "description": "Determines whether to enable an additional \"return from function\" debugging step, allowing inspection of the return value when a function call returns"
-                        }
-                    }
-                },
-                "xdebugCloudToken": {
-                    "type": "string",
-                    "description": "Instead of listening locally, open a connection and register with Xdebug Cloud and accept debugging sessions on that connection"
-                },
-                "stream": {
-                    "type": "object",
-                    "description": "Allows to influence DBGp streams. Xdebug only supports stdout",
-                    "properties": {
-                        "stdout": {
-                            "type": "integer",
-                            "description": "Redirect stdout stream: 0 (disable), 1 (copy), 2 (redirect)",
-                            "enum": [0, 1, 2],
-                            "default": 0
-                        }
-                    }
-                }
-            },
-            "required": ["request", "program"]
-        })
-    }
-
-    fn name(&self) -> DebugAdapterName {
-        DebugAdapterName(Self::ADAPTER_NAME.into())
-    }
-
-    fn adapter_language_name(&self) -> Option<LanguageName> {
-        Some(SharedString::new_static("PHP").into())
-    }
-
-    async fn request_kind(
-        &self,
-        _: &serde_json::Value,
-    ) -> Result<StartDebuggingRequestArgumentsRequest> {
-        Ok(StartDebuggingRequestArgumentsRequest::Launch)
-    }
-
-    async fn config_from_zed_format(&self, zed_scenario: ZedDebugConfig) -> Result<DebugScenario> {
-        let obj = match &zed_scenario.request {
-            dap::DebugRequest::Attach(_) => {
-                bail!("Php adapter doesn't support attaching")
-            }
-            dap::DebugRequest::Launch(launch_config) => json!({
-                "program": launch_config.program,
-                "cwd": launch_config.cwd,
-                "args": launch_config.args,
-                "env": launch_config.env_json(),
-                "stopOnEntry": zed_scenario.stop_on_entry.unwrap_or_default(),
-            }),
-        };
-
-        Ok(DebugScenario {
-            adapter: zed_scenario.adapter,
-            label: zed_scenario.label,
-            build: None,
-            config: obj,
-            tcp_connection: None,
-        })
-    }
-
-    async fn get_binary(
-        &self,
-        delegate: &Arc<dyn DapDelegate>,
-        task_definition: &DebugTaskDefinition,
-        user_installed_path: Option<PathBuf>,
-        user_args: Option<Vec<String>>,
-        cx: &mut AsyncApp,
-    ) -> Result<DebugAdapterBinary> {
-        if self.checked.set(()).is_ok() {
-            delegate.output_to_console(format!("Checking latest version of {}...", self.name()));
-            if let Some(version) = self.fetch_latest_adapter_version(delegate).await.log_err() {
-                adapters::download_adapter_from_github(
-                    self.name(),
-                    version,
-                    adapters::DownloadedFileType::Vsix,
-                    delegate.as_ref(),
-                )
-                .await?;
-            }
-        }
-
-        self.get_installed_binary(
-            delegate,
-            &task_definition,
-            user_installed_path,
-            user_args,
-            cx,
-        )
-        .await
-    }
-}

crates/task/src/vscode_debug_format.rs 🔗

@@ -90,7 +90,7 @@ fn task_type_to_adapter_name(task_type: &str) -> String {
         "pwa-node" | "node" | "node-terminal" | "chrome" | "pwa-chrome" | "edge" | "pwa-edge"
         | "msedge" | "pwa-msedge" => "JavaScript",
         "go" => "Delve",
-        "php" => "PHP",
+        "php" => "Xdebug",
         "cppdbg" | "lldb" => "CodeLLDB",
         "debugpy" => "Debugpy",
         "rdbg" => "rdbg",