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",
|
"zed_extension_api 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zed_php"
|
|
||||||
version = "0.2.4"
|
|
||||||
dependencies = [
|
|
||||||
"zed_extension_api 0.1.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_proto"
|
name = "zed_proto"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
|
|
@ -171,7 +171,6 @@ members = [
|
||||||
"extensions/haskell",
|
"extensions/haskell",
|
||||||
"extensions/html",
|
"extensions/html",
|
||||||
"extensions/lua",
|
"extensions/lua",
|
||||||
"extensions/php",
|
|
||||||
"extensions/perplexity",
|
"extensions/perplexity",
|
||||||
"extensions/proto",
|
"extensions/proto",
|
||||||
"extensions/purescript",
|
"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