Migate PHP Extension to zed-extensions/php (#24583)
PHP Extension has been extracted to it's own repository available here: - https://github.com/zed-extensions/php
This commit is contained in:
parent
0af048a7cf
commit
62bb3398ed
22 changed files with 0 additions and 905 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -16823,13 +16823,6 @@ dependencies = [
|
|||
"zed_extension_api 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_php"
|
||||
version = "0.2.4"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_proto"
|
||||
version = "0.2.1"
|
||||
|
|
|
@ -171,7 +171,6 @@ members = [
|
|||
"extensions/haskell",
|
||||
"extensions/html",
|
||||
"extensions/lua",
|
||||
"extensions/php",
|
||||
"extensions/perplexity",
|
||||
"extensions/proto",
|
||||
"extensions/purescript",
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
[package]
|
||||
name = "zed_php"
|
||||
version = "0.2.4"
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
license = "Apache-2.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/php.rs"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
zed_extension_api = "0.1.0"
|
|
@ -1 +0,0 @@
|
|||
../../LICENSE-APACHE
|
|
@ -1,25 +0,0 @@
|
|||
id = "php"
|
||||
name = "PHP"
|
||||
description = "PHP support."
|
||||
version = "0.2.4"
|
||||
schema_version = 1
|
||||
authors = ["Piotr Osiewicz <piotr@zed.dev>"]
|
||||
repository = "https://github.com/zed-industries/zed"
|
||||
|
||||
[language_servers.intelephense]
|
||||
name = "Intelephense"
|
||||
language = "PHP"
|
||||
language_ids = { PHP = "php"}
|
||||
|
||||
[language_servers.phpactor]
|
||||
name = "Phpactor"
|
||||
language = "PHP"
|
||||
|
||||
[grammars.php]
|
||||
repository = "https://github.com/tree-sitter/tree-sitter-php"
|
||||
commit = "8ab93274065cbaf529ea15c24360cfa3348ec9e4"
|
||||
path = "php"
|
||||
|
||||
[grammars.phpdoc]
|
||||
repository = "https://github.com/claytonrcarter/tree-sitter-phpdoc"
|
||||
commit = "1d0e255b37477d0ca46f1c9e9268c8fa76c0b3fc"
|
|
@ -1,4 +0,0 @@
|
|||
("{" @open "}" @close)
|
||||
("(" @open ")" @close)
|
||||
("[" @open "]" @close)
|
||||
("\"" @open "\"" @close)
|
|
@ -1,18 +0,0 @@
|
|||
name = "PHP"
|
||||
grammar = "php"
|
||||
path_suffixes = ["php"]
|
||||
first_line_pattern = '^#!.*php'
|
||||
line_comments = ["// ", "# "]
|
||||
autoclose_before = ";:.,=}])>"
|
||||
brackets = [
|
||||
{ start = "{", end = "}", close = true, newline = true },
|
||||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "(", end = ")", close = true, newline = true },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
|
||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["string"] },
|
||||
]
|
||||
collapsed_placeholder = "/* ... */"
|
||||
word_characters = ["$"]
|
||||
scope_opt_in_language_servers = ["tailwindcss-language-server"]
|
||||
prettier_parser_name = "php"
|
||||
prettier_plugins = ["@prettier/plugin-php"]
|
|
@ -1,36 +0,0 @@
|
|||
(
|
||||
(comment)* @context
|
||||
.
|
||||
[
|
||||
(function_definition
|
||||
"function" @name
|
||||
name: (_) @name
|
||||
body: (_
|
||||
"{" @keep
|
||||
"}" @keep) @collapse
|
||||
)
|
||||
|
||||
(trait_declaration
|
||||
"trait" @name
|
||||
name: (_) @name)
|
||||
|
||||
(method_declaration
|
||||
"function" @name
|
||||
name: (_) @name
|
||||
body: (_
|
||||
"{" @keep
|
||||
"}" @keep) @collapse
|
||||
)
|
||||
|
||||
(interface_declaration
|
||||
"interface" @name
|
||||
name: (_) @name
|
||||
)
|
||||
|
||||
(enum_declaration
|
||||
"enum" @name
|
||||
name: (_) @name
|
||||
)
|
||||
|
||||
] @item
|
||||
)
|
|
@ -1,137 +0,0 @@
|
|||
(php_tag) @tag
|
||||
"?>" @tag
|
||||
|
||||
; Types
|
||||
|
||||
(primitive_type) @type.builtin
|
||||
(cast_type) @type.builtin
|
||||
(named_type (name) @type) @type
|
||||
(named_type (qualified_name) @type) @type
|
||||
|
||||
; Functions
|
||||
|
||||
(array_creation_expression "array" @function.builtin)
|
||||
(list_literal "list" @function.builtin)
|
||||
|
||||
(method_declaration
|
||||
name: (name) @function.method)
|
||||
|
||||
(function_call_expression
|
||||
function: [(qualified_name (name)) (name)] @function)
|
||||
|
||||
(scoped_call_expression
|
||||
name: (name) @function)
|
||||
|
||||
(member_call_expression
|
||||
name: (name) @function.method)
|
||||
|
||||
(function_definition
|
||||
name: (name) @function)
|
||||
|
||||
; Member
|
||||
|
||||
(property_element
|
||||
(variable_name) @property)
|
||||
|
||||
(member_access_expression
|
||||
name: (variable_name (name)) @property)
|
||||
(member_access_expression
|
||||
name: (name) @property)
|
||||
|
||||
; Variables
|
||||
|
||||
(relative_scope) @variable.builtin
|
||||
|
||||
((name) @constant
|
||||
(#match? @constant "^_?[A-Z][A-Z\\d_]+$"))
|
||||
((name) @constant.builtin
|
||||
(#match? @constant.builtin "^__[A-Z][A-Z\d_]+__$"))
|
||||
|
||||
((name) @constructor
|
||||
(#match? @constructor "^[A-Z]"))
|
||||
|
||||
((name) @variable.builtin
|
||||
(#eq? @variable.builtin "this"))
|
||||
|
||||
(variable_name) @variable
|
||||
|
||||
; Basic tokens
|
||||
[
|
||||
(string)
|
||||
(string_value)
|
||||
(encapsed_string)
|
||||
(heredoc)
|
||||
(heredoc_body)
|
||||
(nowdoc_body)
|
||||
] @string
|
||||
(boolean) @constant.builtin
|
||||
(null) @constant.builtin
|
||||
(integer) @number
|
||||
(float) @number
|
||||
(comment) @comment
|
||||
|
||||
"$" @operator
|
||||
|
||||
; Keywords
|
||||
|
||||
"abstract" @keyword
|
||||
"and" @keyword
|
||||
"as" @keyword
|
||||
"break" @keyword
|
||||
"callable" @keyword
|
||||
"case" @keyword
|
||||
"catch" @keyword
|
||||
"class" @keyword
|
||||
"clone" @keyword
|
||||
"const" @keyword
|
||||
"continue" @keyword
|
||||
"declare" @keyword
|
||||
"default" @keyword
|
||||
"do" @keyword
|
||||
"echo" @keyword
|
||||
"else" @keyword
|
||||
"elseif" @keyword
|
||||
"enum" @keyword
|
||||
"enddeclare" @keyword
|
||||
"endfor" @keyword
|
||||
"endforeach" @keyword
|
||||
"endif" @keyword
|
||||
"endswitch" @keyword
|
||||
"endwhile" @keyword
|
||||
"extends" @keyword
|
||||
"final" @keyword
|
||||
"readonly" @keyword
|
||||
"finally" @keyword
|
||||
"for" @keyword
|
||||
"foreach" @keyword
|
||||
"fn" @keyword
|
||||
"function" @keyword
|
||||
"global" @keyword
|
||||
"goto" @keyword
|
||||
"if" @keyword
|
||||
"implements" @keyword
|
||||
"include_once" @keyword
|
||||
"include" @keyword
|
||||
"instanceof" @keyword
|
||||
"insteadof" @keyword
|
||||
"interface" @keyword
|
||||
"match" @keyword
|
||||
"namespace" @keyword
|
||||
"new" @keyword
|
||||
"or" @keyword
|
||||
"print" @keyword
|
||||
"private" @keyword
|
||||
"protected" @keyword
|
||||
"public" @keyword
|
||||
"readonly" @keyword
|
||||
"require_once" @keyword
|
||||
"require" @keyword
|
||||
"return" @keyword
|
||||
"static" @keyword
|
||||
"switch" @keyword
|
||||
"throw" @keyword
|
||||
"trait" @keyword
|
||||
"try" @keyword
|
||||
"use" @keyword
|
||||
"while" @keyword
|
||||
"xor" @keyword
|
|
@ -1 +0,0 @@
|
|||
(_ "{" "}" @end) @indent
|
|
@ -1,11 +0,0 @@
|
|||
((text) @injection.content
|
||||
(#set! injection.language "html")
|
||||
(#set! injection.combined))
|
||||
|
||||
((comment) @injection.content
|
||||
(#match? @injection.content "^/\\*\\*[^*]")
|
||||
(#set! injection.language "phpdoc"))
|
||||
|
||||
((heredoc_body) (heredoc_end) @injection.language) @injection.content
|
||||
|
||||
((nowdoc_body) (heredoc_end) @injection.language) @injection.content
|
|
@ -1,46 +0,0 @@
|
|||
(class_declaration
|
||||
"class" @context
|
||||
name: (name) @name
|
||||
) @item
|
||||
|
||||
(function_definition
|
||||
"function" @context
|
||||
name: (_) @name
|
||||
) @item
|
||||
|
||||
(method_declaration
|
||||
"function" @context
|
||||
name: (_) @name
|
||||
) @item
|
||||
|
||||
(interface_declaration
|
||||
"interface" @context
|
||||
name: (_) @name
|
||||
) @item
|
||||
|
||||
(enum_declaration
|
||||
"enum" @context
|
||||
name: (_) @name
|
||||
) @item
|
||||
|
||||
(trait_declaration
|
||||
"trait" @context
|
||||
name: (_) @name
|
||||
) @item
|
||||
|
||||
; Add support for Pest runnable
|
||||
(function_call_expression
|
||||
function: (_) @context
|
||||
(#any-of? @context "it" "test" "describe")
|
||||
arguments: (arguments
|
||||
.
|
||||
(argument
|
||||
[
|
||||
(encapsed_string (string_value) @name)
|
||||
(string (string_value) @name)
|
||||
]
|
||||
)
|
||||
)
|
||||
) @item
|
||||
|
||||
(comment) @annotation
|
|
@ -1,105 +0,0 @@
|
|||
; Class that follow the naming convention of PHPUnit test classes
|
||||
; and that doesn't have the abstract modifier
|
||||
; and have a method that follow the naming convention of PHPUnit test methods
|
||||
; and the method is public
|
||||
(
|
||||
(class_declaration
|
||||
modifier: (_)? @_modifier
|
||||
(#not-eq? @_modifier "abstract")
|
||||
name: (_) @_name
|
||||
(#match? @_name ".*Test$")
|
||||
body: (declaration_list
|
||||
(method_declaration
|
||||
(visibility_modifier)? @_visibility
|
||||
(#eq? @_visibility "public")
|
||||
name: (_) @run
|
||||
(#match? @run "^test.*")
|
||||
)
|
||||
)
|
||||
) @_phpunit-test
|
||||
(#set! tag phpunit-test)
|
||||
)
|
||||
|
||||
; Class that follow the naming convention of PHPUnit test classes
|
||||
; and that doesn't have the abstract modifier
|
||||
; and have a method that has the @test annotation
|
||||
; and the method is public
|
||||
(
|
||||
(class_declaration
|
||||
modifier: (_)? @_modifier
|
||||
(#not-eq? @_modifier "abstract")
|
||||
name: (_) @_name
|
||||
(#match? @_name ".*Test$")
|
||||
body: (declaration_list
|
||||
((comment) @_comment
|
||||
(#match? @_comment ".*@test\\b.*")
|
||||
.
|
||||
(method_declaration
|
||||
(visibility_modifier)? @_visibility
|
||||
(#eq? @_visibility "public")
|
||||
name: (_) @run
|
||||
(#not-match? @run "^test.*")
|
||||
))
|
||||
)
|
||||
) @_phpunit-test
|
||||
(#set! tag phpunit-test)
|
||||
)
|
||||
|
||||
; Class that follow the naming convention of PHPUnit test classes
|
||||
; and that doesn't have the abstract modifier
|
||||
; and have a method that has the #[Test] attribute
|
||||
; and the method is public
|
||||
(
|
||||
(class_declaration
|
||||
modifier: (_)? @_modifier
|
||||
(#not-eq? @_modifier "abstract")
|
||||
name: (_) @_name
|
||||
(#match? @_name ".*Test$")
|
||||
body: (declaration_list
|
||||
(method_declaration
|
||||
(attribute_list
|
||||
(attribute_group
|
||||
(attribute (name) @_attribute)
|
||||
)
|
||||
)
|
||||
(#eq? @_attribute "Test")
|
||||
(visibility_modifier)? @_visibility
|
||||
(#eq? @_visibility "public")
|
||||
name: (_) @run
|
||||
(#not-match? @run "^test.*")
|
||||
)
|
||||
)
|
||||
) @_phpunit-test
|
||||
(#set! tag phpunit-test)
|
||||
)
|
||||
|
||||
; Class that follow the naming convention of PHPUnit test classes
|
||||
; and that doesn't have the abstract modifier
|
||||
(
|
||||
(class_declaration
|
||||
modifier: (_)? @_modifier
|
||||
(#not-eq? @_modifier "abstract")
|
||||
name: (_) @run
|
||||
(#match? @run ".*Test$")
|
||||
) @_phpunit-test
|
||||
(#set! tag phpunit-test)
|
||||
)
|
||||
|
||||
; Add support for Pest runnable
|
||||
; Function expression that has `it`, `test` or `describe` as the function name
|
||||
(
|
||||
(function_call_expression
|
||||
function: (_) @_name
|
||||
(#any-of? @_name "it" "test" "describe")
|
||||
arguments: (arguments
|
||||
.
|
||||
(argument
|
||||
[
|
||||
(encapsed_string (string_value) @run)
|
||||
(string (string_value) @run)
|
||||
]
|
||||
)
|
||||
)
|
||||
) @_pest-test
|
||||
(#set! tag pest-test)
|
||||
)
|
|
@ -1,40 +0,0 @@
|
|||
(namespace_definition
|
||||
name: (namespace_name) @name) @module
|
||||
|
||||
(interface_declaration
|
||||
name: (name) @name) @definition.interface
|
||||
|
||||
(trait_declaration
|
||||
name: (name) @name) @definition.interface
|
||||
|
||||
(class_declaration
|
||||
name: (name) @name) @definition.class
|
||||
|
||||
(class_interface_clause [(name) (qualified_name)] @name) @impl
|
||||
|
||||
(property_declaration
|
||||
(property_element (variable_name (name) @name))) @definition.field
|
||||
|
||||
(function_definition
|
||||
name: (name) @name) @definition.function
|
||||
|
||||
(method_declaration
|
||||
name: (name) @name) @definition.function
|
||||
|
||||
(object_creation_expression
|
||||
[
|
||||
(qualified_name (name) @name)
|
||||
(variable_name (name) @name)
|
||||
]) @reference.class
|
||||
|
||||
(function_call_expression
|
||||
function: [
|
||||
(qualified_name (name) @name)
|
||||
(variable_name (name)) @name
|
||||
]) @reference.call
|
||||
|
||||
(scoped_call_expression
|
||||
name: (name) @name) @reference.call
|
||||
|
||||
(member_call_expression
|
||||
name: (name) @name) @reference.call
|
|
@ -1,19 +0,0 @@
|
|||
[
|
||||
{
|
||||
"label": "phpunit test $ZED_SYMBOL",
|
||||
"command": "./vendor/bin/phpunit",
|
||||
"args": ["--filter $ZED_SYMBOL $ZED_FILE"],
|
||||
"tags": ["phpunit-test"]
|
||||
},
|
||||
{
|
||||
"label": "pest test $ZED_SYMBOL",
|
||||
"command": "./vendor/bin/pest",
|
||||
"args": ["--filter \"$ZED_SYMBOL\" $ZED_FILE"],
|
||||
"tags": ["pest-test"]
|
||||
},
|
||||
{
|
||||
"label": "execute selection $ZED_SELECTED_TEXT",
|
||||
"command": "php",
|
||||
"args": ["-r", "$ZED_SELECTED_TEXT"]
|
||||
}
|
||||
]
|
|
@ -1,45 +0,0 @@
|
|||
(function_definition
|
||||
body: (_
|
||||
"{"
|
||||
(_)* @function.inside
|
||||
"}" )) @function.around
|
||||
|
||||
(method_declaration
|
||||
body: (_
|
||||
"{"
|
||||
(_)* @function.inside
|
||||
"}" )) @function.around
|
||||
|
||||
(method_declaration) @function.around
|
||||
|
||||
(class_declaration
|
||||
body: (_
|
||||
"{"
|
||||
(_)* @class.inside
|
||||
"}")) @class.around
|
||||
|
||||
(interface_declaration
|
||||
body: (_
|
||||
"{"
|
||||
(_)* @class.inside
|
||||
"}")) @class.around
|
||||
|
||||
(trait_declaration
|
||||
body: (_
|
||||
"{"
|
||||
(_)* @class.inside
|
||||
"}")) @class.around
|
||||
|
||||
(enum_declaration
|
||||
body: (_
|
||||
"{"
|
||||
(_)* @class.inside
|
||||
"}")) @class.around
|
||||
|
||||
(namespace_definition
|
||||
body: (_
|
||||
"{"
|
||||
(_)* @class.inside
|
||||
"}")) @class.around
|
||||
|
||||
(comment)+ @comment.around
|
|
@ -1,9 +0,0 @@
|
|||
name = "PHPDoc"
|
||||
grammar = "phpdoc"
|
||||
autoclose_before = "]})>"
|
||||
brackets = [
|
||||
{ start = "{", end = "}", close = true, newline = false },
|
||||
{ start = "[", end = "]", close = true, newline = false },
|
||||
{ start = "(", end = ")", close = true, newline = false },
|
||||
{ start = "<", end = ">", close = true, newline = false },
|
||||
]
|
|
@ -1,13 +0,0 @@
|
|||
(tag_name) @keyword
|
||||
|
||||
(tag (variable_name) @variable)
|
||||
(variable_name "$" @operator)
|
||||
|
||||
(tag
|
||||
(tag_name) @keyword
|
||||
(#eq? @keyword "@method")
|
||||
(name) @function.method)
|
||||
|
||||
(primitive_type) @type.builtin
|
||||
(named_type (name) @type) @type
|
||||
(named_type (qualified_name) @type) @type
|
|
@ -1,5 +0,0 @@
|
|||
mod intelephense;
|
||||
mod phpactor;
|
||||
|
||||
pub use intelephense::*;
|
||||
pub use phpactor::*;
|
|
@ -1,209 +0,0 @@
|
|||
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};
|
||||
|
||||
const SERVER_PATH: &str = "node_modules/intelephense/lib/intelephense.js";
|
||||
const PACKAGE_NAME: &str = "intelephense";
|
||||
|
||||
pub struct Intelephense {
|
||||
did_find_server: bool,
|
||||
}
|
||||
|
||||
impl Intelephense {
|
||||
pub const LANGUAGE_SERVER_ID: &'static str = "intelephense";
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
did_find_server: false,
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
zed::set_language_server_installation_status(
|
||||
language_server_id,
|
||||
&zed::LanguageServerInstallationStatus::CheckingForUpdate,
|
||||
);
|
||||
let version = zed::npm_package_latest_version(PACKAGE_NAME)?;
|
||||
|
||||
if !server_exists
|
||||
|| zed::npm_package_installed_version(PACKAGE_NAME)?.as_ref() != Some(&version)
|
||||
{
|
||||
zed::set_language_server_installation_status(
|
||||
language_server_id,
|
||||
&zed::LanguageServerInstallationStatus::Downloading,
|
||||
);
|
||||
let result = zed::npm_install_package(PACKAGE_NAME, &version);
|
||||
match result {
|
||||
Ok(()) => {
|
||||
if !self.server_exists() {
|
||||
Err(format!(
|
||||
"installed package '{PACKAGE_NAME}' did not contain expected path '{SERVER_PATH}'",
|
||||
))?;
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
if !self.server_exists() {
|
||||
Err(error)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.did_find_server = true;
|
||||
Ok(SERVER_PATH.to_string())
|
||||
}
|
||||
|
||||
pub fn language_server_workspace_configuration(
|
||||
&mut self,
|
||||
worktree: &zed::Worktree,
|
||||
) -> Result<Option<serde_json::Value>> {
|
||||
let settings = LspSettings::for_worktree("intelephense", worktree)
|
||||
.ok()
|
||||
.and_then(|lsp_settings| lsp_settings.settings.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(Some(serde_json::json!({
|
||||
"intelephense": settings
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn label_for_completion(&self, completion: zed::lsp::Completion) -> Option<CodeLabel> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
use std::fs;
|
||||
|
||||
use zed_extension_api::{self as zed, LanguageServerId, Result};
|
||||
|
||||
pub struct Phpactor {
|
||||
cached_binary_path: Option<String>,
|
||||
}
|
||||
|
||||
impl Phpactor {
|
||||
pub const LANGUAGE_SERVER_ID: &'static str = "phpactor";
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
cached_binary_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn language_server_binary_path(
|
||||
&mut self,
|
||||
language_server_id: &LanguageServerId,
|
||||
worktree: &zed::Worktree,
|
||||
) -> Result<String> {
|
||||
if let Some(path) = worktree.which("phpactor") {
|
||||
return Ok(path);
|
||||
}
|
||||
|
||||
if let Some(path) = &self.cached_binary_path {
|
||||
if fs::metadata(path).map_or(false, |stat| stat.is_file()) {
|
||||
return Ok(path.clone());
|
||||
}
|
||||
}
|
||||
|
||||
zed::set_language_server_installation_status(
|
||||
language_server_id,
|
||||
&zed::LanguageServerInstallationStatus::CheckingForUpdate,
|
||||
);
|
||||
let release = zed::latest_github_release(
|
||||
"phpactor/phpactor",
|
||||
zed::GithubReleaseOptions {
|
||||
require_assets: true,
|
||||
pre_release: false,
|
||||
},
|
||||
)?;
|
||||
|
||||
let asset_name = "phpactor.phar";
|
||||
let asset = release
|
||||
.assets
|
||||
.iter()
|
||||
.find(|asset| asset.name == asset_name)
|
||||
.ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;
|
||||
|
||||
let version_dir = format!("phpactor-{}", release.version);
|
||||
fs::create_dir_all(&version_dir).map_err(|e| format!("failed to create directory: {e}"))?;
|
||||
|
||||
let binary_path = format!("{version_dir}/phpactor.phar");
|
||||
|
||||
if !fs::metadata(&binary_path).map_or(false, |stat| stat.is_file()) {
|
||||
zed::set_language_server_installation_status(
|
||||
language_server_id,
|
||||
&zed::LanguageServerInstallationStatus::Downloading,
|
||||
);
|
||||
|
||||
zed::download_file(
|
||||
&asset.download_url,
|
||||
&binary_path,
|
||||
zed::DownloadedFileType::Uncompressed,
|
||||
)
|
||||
.map_err(|e| format!("failed to download file: {e}"))?;
|
||||
|
||||
zed::make_file_executable(&binary_path)?;
|
||||
|
||||
let entries =
|
||||
fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
|
||||
for entry in entries {
|
||||
let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
|
||||
if entry.file_name().to_str() != Some(&version_dir) {
|
||||
fs::remove_dir_all(entry.path()).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.cached_binary_path = Some(binary_path.clone());
|
||||
Ok(binary_path)
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
mod language_servers;
|
||||
|
||||
use zed::CodeLabel;
|
||||
use zed_extension_api::{self as zed, serde_json, LanguageServerId, Result};
|
||||
|
||||
use crate::language_servers::{Intelephense, Phpactor};
|
||||
|
||||
struct PhpExtension {
|
||||
intelephense: Option<Intelephense>,
|
||||
phpactor: Option<Phpactor>,
|
||||
}
|
||||
|
||||
impl zed::Extension for PhpExtension {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
intelephense: None,
|
||||
phpactor: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn language_server_command(
|
||||
&mut self,
|
||||
language_server_id: &LanguageServerId,
|
||||
worktree: &zed::Worktree,
|
||||
) -> Result<zed::Command> {
|
||||
match language_server_id.as_ref() {
|
||||
Intelephense::LANGUAGE_SERVER_ID => {
|
||||
let intelephense = self.intelephense.get_or_insert_with(Intelephense::new);
|
||||
intelephense.language_server_command(language_server_id, worktree)
|
||||
}
|
||||
Phpactor::LANGUAGE_SERVER_ID => {
|
||||
let phpactor = self.phpactor.get_or_insert_with(Phpactor::new);
|
||||
|
||||
Ok(zed::Command {
|
||||
command: phpactor.language_server_binary_path(language_server_id, worktree)?,
|
||||
args: vec!["language-server".into()],
|
||||
env: Default::default(),
|
||||
})
|
||||
}
|
||||
language_server_id => Err(format!("unknown language server: {language_server_id}")),
|
||||
}
|
||||
}
|
||||
|
||||
fn language_server_workspace_configuration(
|
||||
&mut self,
|
||||
language_server_id: &LanguageServerId,
|
||||
worktree: &zed::Worktree,
|
||||
) -> Result<Option<serde_json::Value>> {
|
||||
if language_server_id.as_ref() == Intelephense::LANGUAGE_SERVER_ID {
|
||||
if let Some(intelephense) = self.intelephense.as_mut() {
|
||||
return intelephense.language_server_workspace_configuration(worktree);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn label_for_completion(
|
||||
&self,
|
||||
language_server_id: &zed::LanguageServerId,
|
||||
completion: zed::lsp::Completion,
|
||||
) -> Option<CodeLabel> {
|
||||
match language_server_id.as_ref() {
|
||||
Intelephense::LANGUAGE_SERVER_ID => {
|
||||
self.intelephense.as_ref()?.label_for_completion(completion)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zed::register_extension!(PhpExtension);
|
Loading…
Add table
Add a link
Reference in a new issue