Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add blade support #9513

Merged
merged 2 commits into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions book/src/generated/lang-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
| beancount | ✓ | | | |
| bibtex | ✓ | | | `texlab` |
| bicep | ✓ | | | `bicep-langserver` |
| blade | ✓ | | | |
| blueprint | ✓ | | | `blueprint-compiler` |
| c | ✓ | ✓ | ✓ | `clangd` |
| c-sharp | ✓ | ✓ | | `OmniSharp` |
Expand Down Expand Up @@ -128,6 +129,7 @@
| pem | ✓ | | | |
| perl | ✓ | ✓ | ✓ | `perlnavigator` |
| php | ✓ | ✓ | ✓ | `intelephense` |
| php-only | ✓ | | | |
| pkl | ✓ | | ✓ | |
| po | ✓ | ✓ | | |
| pod | ✓ | | | |
Expand Down
23 changes: 23 additions & 0 deletions languages.toml
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,29 @@ indent = { tab-width = 4, unit = " " }
name = "php"
source = { git = "https://github.com/tree-sitter/tree-sitter-php", rev = "f860e598194f4a71747f91789bf536b393ad4a56" }

[[language]]
name = "php-only"
scope = "source.php-only"
lelgenio marked this conversation as resolved.
Show resolved Hide resolved
injection-regex = "php-only"
file-types = []
indent = { tab-width = 4, unit = " " }
roots = ["composer.json", "index.php"]

[[grammar]]
name = "php-only"
source = { git = "https://github.com/tree-sitter/tree-sitter-php", rev = "cf1f4a0f1c01c705c1d6cf992b104028d5df0b53", subpath = "php_only" }

[[language]]
name = "blade"
scope = "source.blade.php"
file-types = [{ glob = "*.blade.php" }, "blade"]
injection-regex = "blade"
roots = ["composer.json", "index.php"]

[[grammar]]
name = "blade"
source = { git = "https://github.com/EmranMR/tree-sitter-blade", rev = "4c66efe1e05c639c555ee70092021b8223d2f440" }

[[language]]
name = "twig"
scope = "source.twig"
Expand Down
8 changes: 8 additions & 0 deletions runtime/queries/blade/folds.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
((directive_start) @start
(directive_end) @end.after
(#set! role block))


((bracket_start) @start
(bracket_end) @end
(#set! role block))
4 changes: 4 additions & 0 deletions runtime/queries/blade/highlights.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(directive) @tag
(directive_start) @tag
(directive_end) @tag
(comment) @comment
9 changes: 9 additions & 0 deletions runtime/queries/blade/injections.scm
Copy link
Contributor

@EmranMR EmranMR Mar 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lelgenio I am afraid, without the #not-has-ancestor and #has-ancestor predicates, the injections will not work with the latest versions of the tree-sitter-blade (v0.9.0^)

Did you remove them because Helix does not support these predicates? I am fairly new to Helix myself and already hooked big time, but not sure about their tree-sitter support.

((text) @injection.content
    (#not-has-ancestor? @injection.content "envoy")
    (#set! injection.combined)
    (#set! injection.language php))

; could be bash or zsh
; or whatever tree-sitter grammar you have.
((text) @injection.content
    (#has-ancestor? @injection.content "envoy")
    (#set! injection.combined)
    (#set! injection.language bash))


((php_only) @injection.content
    (#set! injection.language php_only))
((parameter) @injection.content
    (#set! injection.language php_only))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you're right! @tasks don't work correctly. I wonder if there's an equivalent predicated to #has-ancestor in helix 🤔

image

#9513 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I just saw the comment by @the-mikedavis

#has-ancestor? / #not-has-ancestor? is a predicate that only exists in nvim as far as I can tell. For now we can remove that line and always inject php

Just to add, Nova added support for this last year with v11 as well. It is quite handy for extremely unusual template grammars! We could maybe do a "feature request" for these?

However for time being, feel free to safely comment/remove this section as it is redundant :)

((text) @injection.content
    (#has-ancestor? @injection.content "envoy")
    (#set! injection.combined)
    (#set! injection.language bash))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like #has-ancestor? is working towards recognizing arbitrary nesting tree-sitter/tree-sitter#981

It was introduced in neovim/neovim#23606 and scans from the currently captured node potentially up to the root. I don't think that's particularly expensive but I was imagining that when arbitrary nesting was supported upstream by tree-sitter itself (probably a new syntax in the queries) it would track heritage internally and eliminate the cost of scanning.

I will write up an issue for this when I get a chance but let's not block this PR on it since the design is up for debate

Copy link
Contributor

@EmranMR EmranMR Mar 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@the-mikedavis Absolutely, by all means! The bash injection and envoy is for a very specific and niche use case in blade and its existence or lack of would not do any harm :). In fact no parser out there, had ever had this feature or been able to parse shell correctly in the blade files due to their limitation/complexity. In all honesty, I was just trying to see how far I can push tree-sitter with this feature when I was writing the grammar for it haha (tree-sitter never fails to please!).
But yea, having that section removed or commented for now, before the merge, is very sensible. Just to avoid unintended behaviour, because in the injection.scm both bash and php were being injected to the same node.

And I am totally with you on that one, I wished 'tree-sitter' had some form of standardisation for the predicates...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just updated the PR to no longer include the bash injection, by the way.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
((text) @injection.content
(#set! injection.combined)
(#set! injection.language php))

((php_only) @injection.content
(#set! injection.language php-only))
((parameter) @injection.content
(#set! injection.language php-only))

123 changes: 123 additions & 0 deletions runtime/queries/php-only/highlights.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
(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) @variable.other.member)

(member_access_expression
name: (variable_name (name)) @variable.other.member)
(member_access_expression
name: (name) @variable.other.member)

; 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.boolean
(null) @constant.builtin
(integer) @constant.builtin.integer
(float) @constant.builtin.float
(comment) @comment

"$" @operator

; Keywords

"abstract" @keyword
"as" @keyword
"break" @keyword
"case" @keyword
"catch" @keyword
"class" @keyword
"const" @keyword
"continue" @keyword
"declare" @keyword
"default" @keyword
"do" @keyword
"echo" @keyword
"else" @keyword
"elseif" @keyword
"enddeclare" @keyword
"endforeach" @keyword
"endif" @keyword
"endswitch" @keyword
"endwhile" @keyword
"extends" @keyword
"final" @keyword
"finally" @keyword
"for" @keyword
"foreach" @keyword
"function" @keyword
"global" @keyword
"if" @keyword
"implements" @keyword
"include_once" @keyword
"include" @keyword
"insteadof" @keyword
"interface" @keyword
"namespace" @keyword
"new" @keyword
"private" @keyword
"protected" @keyword
"public" @keyword
"require_once" @keyword
"require" @keyword
"return" @keyword
"static" @keyword
"switch" @keyword
"throw" @keyword
"trait" @keyword
"try" @keyword
"use" @keyword
"while" @keyword
10 changes: 10 additions & 0 deletions runtime/queries/php-only/injections.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
((comment) @injection.content
(#set! injection.language "comment"))

(heredoc
(heredoc_body) @injection.content
(heredoc_end) @injection.language)

(nowdoc
(nowdoc_body) @injection.content
(heredoc_end) @injection.language)
40 changes: 40 additions & 0 deletions runtime/queries/php-only/tags.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
(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
Loading