diff --git a/extensions/php/src/language_servers/intelephense.rs b/extensions/php/src/language_servers/intelephense.rs index 7bd66b24ab..23f47ac5c0 100644 --- a/extensions/php/src/language_servers/intelephense.rs +++ b/extensions/php/src/language_servers/intelephense.rs @@ -1,5 +1,6 @@ use std::{env, fs}; +use zed::{CodeLabel, CodeLabelSpan}; use zed_extension_api::settings::LspSettings; use zed_extension_api::{self as zed, serde_json, LanguageServerId, Result}; @@ -104,4 +105,105 @@ impl Intelephense { "intelephense": settings }))) } + + pub fn label_for_completion(&self, completion: zed::lsp::Completion) -> Option { + let label = &completion.label; + + match completion.kind? { + zed::lsp::CompletionKind::Method => { + // __construct method doesn't have a detail + if let Some(ref detail) = completion.detail { + if detail.is_empty() { + return Some(CodeLabel { + spans: vec![ + CodeLabelSpan::literal(label, Some("function.method".to_string())), + CodeLabelSpan::literal("()", None), + ], + filter_range: (0..label.len()).into(), + code: completion.label, + }); + } + } + + let mut parts = completion.detail.as_ref()?.split(":"); + // E.g., `foo(string $var)` + let name_and_params = parts.next()?; + let return_type = parts.next()?.trim(); + + let (_, params) = name_and_params.split_once("(")?; + let params = params.trim_end_matches(")"); + + Some(CodeLabel { + spans: vec![ + CodeLabelSpan::literal(label, Some("function.method".to_string())), + CodeLabelSpan::literal("(", None), + CodeLabelSpan::literal(params, Some("comment".to_string())), + CodeLabelSpan::literal("): ", None), + CodeLabelSpan::literal(return_type, Some("type".to_string())), + ], + filter_range: (0..label.len()).into(), + code: completion.label, + }) + } + zed::lsp::CompletionKind::Constant | zed::lsp::CompletionKind::EnumMember => { + if let Some(ref detail) = completion.detail { + if !detail.is_empty() { + return Some(CodeLabel { + spans: vec![ + CodeLabelSpan::literal(label, Some("constant".to_string())), + CodeLabelSpan::literal(" ", None), + CodeLabelSpan::literal(detail, Some("comment".to_string())), + ], + filter_range: (0..label.len()).into(), + code: completion.label, + }); + } + } + + Some(CodeLabel { + spans: vec![CodeLabelSpan::literal(label, Some("constant".to_string()))], + filter_range: (0..label.len()).into(), + code: completion.label, + }) + } + zed::lsp::CompletionKind::Property => { + let return_type = completion.detail?; + Some(CodeLabel { + spans: vec![ + CodeLabelSpan::literal(label, Some("attribute".to_string())), + CodeLabelSpan::literal(": ", None), + CodeLabelSpan::literal(return_type, Some("type".to_string())), + ], + filter_range: (0..label.len()).into(), + code: completion.label, + }) + } + zed::lsp::CompletionKind::Variable => { + // See https://www.php.net/manual/en/reserved.variables.php + const SYSTEM_VAR_NAMES: &[&str] = + &["argc", "argv", "php_errormsg", "http_response_header"]; + + let var_name = completion.label.trim_start_matches("$"); + let is_uppercase = var_name + .chars() + .filter(|c| c.is_alphabetic()) + .all(|c| c.is_uppercase()); + let is_system_constant = var_name.starts_with("_"); + let is_reserved = SYSTEM_VAR_NAMES.contains(&var_name); + + let highlight = if is_uppercase || is_system_constant || is_reserved { + Some("comment".to_string()) + } else { + None + }; + + Some(CodeLabel { + spans: vec![CodeLabelSpan::literal(label, highlight)], + filter_range: (0..label.len()).into(), + code: completion.label, + }) + } + _ => None, + } + } } diff --git a/extensions/php/src/php.rs b/extensions/php/src/php.rs index 7157bef074..53b4c29951 100644 --- a/extensions/php/src/php.rs +++ b/extensions/php/src/php.rs @@ -1,5 +1,6 @@ mod language_servers; +use zed::CodeLabel; use zed_extension_api::{self as zed, serde_json, LanguageServerId, Result}; use crate::language_servers::{Intelephense, Phpactor}; @@ -53,6 +54,19 @@ impl zed::Extension for PhpExtension { Ok(None) } + + fn label_for_completion( + &self, + language_server_id: &zed::LanguageServerId, + completion: zed::lsp::Completion, + ) -> Option { + match language_server_id.as_ref() { + Intelephense::LANGUAGE_SERVER_ID => { + self.intelephense.as_ref()?.label_for_completion(completion) + } + _ => None, + } + } } zed::register_extension!(PhpExtension);