diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1334874..9a6ba3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,11 +3,11 @@ name: build/test on: pull_request: branches: - - "**" + - '**' push: branches: - - "master" - + - 'master' + jobs: test: runs-on: ${{ matrix.os }} @@ -28,3 +28,13 @@ jobs: - run: npm install - run: ./node_modules/.bin/tree-sitter generate - run: ./node_modules/.bin/tree-sitter test + + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 18 + - run: npm install + - run: npm run format-check diff --git a/README.md b/README.md index 7cfa151..79c3205 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -tree-sitter-phpdoc -================== +# tree-sitter-phpdoc PHPDoc grammar for [tree-sitter][]. @@ -13,8 +12,8 @@ PHPDoc grammar for [tree-sitter][]. `npm test` -If this test script doesn't work for you, you can just run -`./node_modules/.bin/tree-sitter generate` and +If this test script doesn't work for you, you can just run +`./node_modules/.bin/tree-sitter generate` and `./node_modules/.bin/tree-sitter test` manually. #### Thanks diff --git a/grammar.js b/grammar.js index 45b30f6..e18624e 100644 --- a/grammar.js +++ b/grammar.js @@ -10,21 +10,28 @@ const PHP = require('tree-sitter-php/php/grammar').grammar; module.exports = grammar({ name: 'phpdoc', - extras: $ => [ - token(choice( - // Skip over stars at the beginnings of lines - seq(/\r?\n/, /[ \t]*/, repeat(seq('*', /[ \t]*/))), - /\s/ - )) + extras: ($) => [ + token( + choice( + // Skip over stars at the beginnings of lines + seq(/\r?\n/, /[ \t]*/, repeat(seq('*', /[ \t]*/))), + /\s/, + ), + ), ], - conflicts: $ => [ + conflicts: ($) => [ [$.primitive_type, $.static], [$.namespace_name], [$.namespace_name_as_prefix], [$.named_type, $.generic_type], [$._type_argument_list], - [$._type, $.union_type, $.intersection_type, $.disjunctive_normal_form_type], + [ + $._type, + $.union_type, + $.intersection_type, + $.disjunctive_normal_form_type, + ], [$.union_type, $.disjunctive_normal_form_type], ], @@ -49,7 +56,7 @@ module.exports = grammar({ // $._text_after_type, // seq($.variable, $text) // ) - externals: $ => [ + externals: ($) => [ $.text, // Must not start with $variable @@ -65,149 +72,110 @@ module.exports = grammar({ $._text_not_version, ], - word: $ => $.name, + word: ($) => $.name, rules: { - document: $ => seq( - $._begin, - optional($.description), - repeat($.tag), - $._end, - ), + document: ($) => + seq($._begin, optional($.description), repeat($.tag), $._end), - _begin: $ => token(seq('/**', repeat('*'))), + _begin: ($) => token(seq('/**', repeat('*'))), - description: $ => repeat1( - choice( - $.text, - $.inline_tag - ) - ), + description: ($) => repeat1(choice($.text, $.inline_tag)), - _description_after_type: $ => alias( - repeat1( - choice( - alias($._text_after_type, $.text), - $.inline_tag - ) + _description_after_type: ($) => + alias( + repeat1(choice(alias($._text_after_type, $.text), $.inline_tag)), + $.description, ), - $.description - ), - _description_not_version: $ => alias( - repeat1( - choice( - alias($._text_not_version, $.text), - $.inline_tag - ) + _description_not_version: ($) => + alias( + repeat1(choice(alias($._text_not_version, $.text), $.inline_tag)), + $.description, ), - $.description - ), - _description_in_inline_tag: $ => alias( - repeat1( - alias($._text_in_inline_tag, $.text), - ), - $.description - ), + _description_in_inline_tag: ($) => + alias(repeat1(alias($._text_in_inline_tag, $.text)), $.description), - _description_in_inline_tag_with_nesting: $ => alias( - repeat1( - choice( - alias($._text_in_inline_tag, $.text), - $.inline_tag - ) + _description_in_inline_tag_with_nesting: ($) => + alias( + repeat1(choice(alias($._text_in_inline_tag, $.text), $.inline_tag)), + $.description, ), - $.description - ), - tag: $ => choice( - - $._tag_without_description, - $._tag_with_optional_description, - $._tag_with_required_description, - - $._author_tag, - $._deprecated_tag, - $._global_tag, - $._link_tag, - $._method_tag, - $._param_tag, - $._property_tag, - $._return_tag, - $._see_tag, - $._since_tag, - $._throws_tag, - $._var_tag, - $._version_tag, - - $._phpunit_tag, - - // TODO eliminate this - $._tag_with_incomplete_implementation, - - $._generic_template_tag, - $._generic_implements_tag, - $._generic_extends_tag, - $._generic_use_tag, - - $._psalm_tag, - $._mixin_tag, - ), - - inline_tag: $ => seq( - '{', + tag: ($) => choice( - alias('@inheritdoc', $.tag_name), - alias('@inheritDoc', $.tag_name), - $._inline_internal_tag, - $._inline_link_tag, - $._inline_see_tag, + $._tag_without_description, + $._tag_with_optional_description, + $._tag_with_required_description, + + $._author_tag, + $._deprecated_tag, + $._global_tag, + $._link_tag, + $._method_tag, + $._param_tag, + $._property_tag, + $._return_tag, + $._see_tag, + $._since_tag, + $._throws_tag, + $._var_tag, + $._version_tag, + + $._phpunit_tag, + + // TODO eliminate this + $._tag_with_incomplete_implementation, + + $._generic_template_tag, + $._generic_implements_tag, + $._generic_extends_tag, + $._generic_use_tag, + + $._psalm_tag, + $._mixin_tag, + ), + + inline_tag: ($) => + seq( + '{', + choice( + alias('@inheritdoc', $.tag_name), + alias('@inheritDoc', $.tag_name), + $._inline_internal_tag, + $._inline_link_tag, + $._inline_see_tag, + ), + '}', ), - '}' - ), // @api // @filesource // @inheritdoc // @inheritDoc - _tag_without_description: $ => alias( - choice( - '@api', - '@filesource', - '@inheritdoc', - '@inheritDoc', + _tag_without_description: ($) => + alias( + choice('@api', '@filesource', '@inheritdoc', '@inheritDoc'), + $.tag_name, ), - $.tag_name - ), // @ignore [] // @internal [] - _tag_with_optional_description: $ => seq( - alias( - choice( - '@ignore', - '@internal' - ), - $.tag_name + _tag_with_optional_description: ($) => + seq( + alias(choice('@ignore', '@internal'), $.tag_name), + optional($.description), ), - optional($.description) - ), // @category [description] // @copyright [description] // @todo [description] - _tag_with_required_description: $ => seq( - alias( - choice( - '@category', - '@copyright', - '@todo', - ), - $.tag_name + _tag_with_required_description: ($) => + seq( + alias(choice('@category', '@copyright', '@todo'), $.tag_name), + $.description, ), - $.description - ), // TODO complete implementation for these tags // https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/subpackage.html @@ -225,96 +193,97 @@ module.exports = grammar({ // @source [ [] ] [] // https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/uses.html // @uses [FQSEN] [] - _tag_with_incomplete_implementation: $ => seq( - alias( - choice( - '@example', - '@license', - '@package', - '@source', - '@subpackage', - '@uses' + _tag_with_incomplete_implementation: ($) => + seq( + alias( + choice( + '@example', + '@license', + '@package', + '@source', + '@subpackage', + '@uses', + ), + $.tag_name, ), - $.tag_name + optional($.description), ), - optional($.description) - ), // @author [name] [] // specs require email address to be wrapped in angle brackets - _author_tag: $ => seq( - alias('@author', $.tag_name), - $.author_name, - optional(seq('<', $.email_address, '>')) - ), + _author_tag: ($) => + seq( + alias('@author', $.tag_name), + $.author_name, + optional(seq('<', $.email_address, '>')), + ), // @global [Type] [name] (name w/o $) // @global [Type] [description] - _global_tag: $ => seq( - alias('@global', $.tag_name), - $._type, - $.variable_name - ), + _global_tag: ($) => + seq(alias('@global', $.tag_name), $._type, $.variable_name), // @internal [description] - _inline_internal_tag: $ => seq( - alias('@internal', $.tag_name), - optional($._description_in_inline_tag_with_nesting) - ), + _inline_internal_tag: ($) => + seq( + alias('@internal', $.tag_name), + optional($._description_in_inline_tag_with_nesting), + ), // @link [URI] [] - _link_tag: $ => seq( - alias('@link', $.tag_name), - $.uri, - optional($.description) - ), - _inline_link_tag: $ => seq( - alias('@link', $.tag_name), - $.uri, - optional($._description_in_inline_tag) - ), + _link_tag: ($) => + seq(alias('@link', $.tag_name), $.uri, optional($.description)), + _inline_link_tag: ($) => + seq( + alias('@link', $.tag_name), + $.uri, + optional($._description_in_inline_tag), + ), // https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/method.html#method // @method [[static] return type] [name]([[type] [parameter]<, ...>]) [] - _method_tag: $ => seq( - alias('@method', $.tag_name), - // `[static]` is interpreted as optional despite not being in [<...>] - optional($.static), - optional($._type), - $.name, - $.parameters, - optional($.description), - ), + _method_tag: ($) => + seq( + alias('@method', $.tag_name), + // `[static]` is interpreted as optional despite not being in [<...>] + optional($.static), + optional($._type), + $.name, + $.parameters, + optional($.description), + ), // https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/param.html // @param [] [name] [] - _param_tag: $ => seq( - alias('@param', $.tag_name), - optional($._type), - $.variable_name, - optional($.description), - ), + _param_tag: ($) => + seq( + alias('@param', $.tag_name), + optional($._type), + $.variable_name, + optional($.description), + ), // https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/property.html // @property[<-read|-write>] [Type] [name] [] - _property_tag: $ => seq( - alias(choice( - '@property', - '@property-read', - '@property-write', - ), $.tag_name), - $._type, - $.variable_name, - optional($.description), - ), + _property_tag: ($) => + seq( + alias( + choice('@property', '@property-read', '@property-write'), + $.tag_name, + ), + $._type, + $.variable_name, + optional($.description), + ), // https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/return.html // @return [Type] [] - _return_tag: $ => seq( - alias('@return', $.tag_name), - $._type, - optional($._description_after_type), - ), + _return_tag: ($) => + seq( + alias('@return', $.tag_name), + $._type, + optional($._description_after_type), + ), // https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/see.html // @see [URI | FQSEN] [] @@ -326,391 +295,361 @@ module.exports = grammar({ // @see number_of() // @see MyClass::$items // @see MyClass::setItems() - _see_tag: $ => seq( - alias('@see', $.tag_name), - choice( - $.uri, - $.fqsen + _see_tag: ($) => + seq( + alias('@see', $.tag_name), + choice($.uri, $.fqsen), + optional($.description), ), - optional($.description) - ), - _inline_see_tag: $ => seq( - alias('@see', $.tag_name), - choice( - $.uri, - $.fqsen + _inline_see_tag: ($) => + seq( + alias('@see', $.tag_name), + choice($.uri, $.fqsen), + optional($._description_in_inline_tag), ), - optional($._description_in_inline_tag) - ), // @throws [Type] [] - _throws_tag: $ => seq( - alias('@throws', $.tag_name), - $._type, - optional($._description_after_type), - ), + _throws_tag: ($) => + seq( + alias('@throws', $.tag_name), + $._type, + optional($._description_after_type), + ), // https://docs.phpdoc.org/3.0/guide/references/phpdoc/tags/var.html // @var ["Type"] [element_name] [] - _var_tag: $ => seq( - alias('@var', $.tag_name), - choice( - // @var int|string[]|array description - seq($._type, $._description_after_type), - // @var int|string[]|array - seq($._type, $.variable_name), - // @var int|string[]|array $foo description - seq($._type, $.variable_name, $.description), - ) - ), + _var_tag: ($) => + seq( + alias('@var', $.tag_name), + choice( + // @var int|string[]|array description + seq($._type, $._description_after_type), + // @var int|string[]|array + seq($._type, $.variable_name), + // @var int|string[]|array $foo description + seq($._type, $.variable_name, $.description), + ), + ), // @deprecated [] [] - _deprecated_tag: $ => seq( - alias('@deprecated', $.tag_name), - optional( + _deprecated_tag: ($) => + seq( + alias('@deprecated', $.tag_name), + optional( + choice( + $.version, + $._description_not_version, + seq($.version, $.description), + ), + ), + ), + + // @since [] [] + _since_tag: ($) => + seq( + alias('@since', $.tag_name), choice( $.version, $._description_not_version, - seq($.version, $.description) - ) - ) - ), - - // @since [] [] - _since_tag: $ => seq( - alias('@since', $.tag_name), - choice( - $.version, - $._description_not_version, - seq($.version, $.description) - ) - ), + seq($.version, $.description), + ), + ), // @version [] [] - _version_tag: $ => seq( - alias('@version', $.tag_name), - choice( - $.version, - $._description_not_version, - seq($.version, $.description) - ) - ), + _version_tag: ($) => + seq( + alias('@version', $.tag_name), + choice( + $.version, + $._description_not_version, + seq($.version, $.description), + ), + ), // @template [Type] // @psalm-template [Type] // @phpstan-template [Type] - _generic_template_tag: $ => seq( - alias(choice( - '@template', - '@psalm-template', - '@phpstan-template' - ), $.tag_name), - choice( - $._type, - seq($._type, 'of', $._type), - ) - ), + _generic_template_tag: ($) => + seq( + alias( + choice('@template', '@psalm-template', '@phpstan-template'), + $.tag_name, + ), + choice($._type, seq($._type, 'of', $._type)), + ), // @implements [Type] // @template-implements [Type] - _generic_implements_tag: $ => seq( - alias(choice( - '@implements', - '@template-implements' - ), $.tag_name), - $._type, - ), + _generic_implements_tag: ($) => + seq( + alias(choice('@implements', '@template-implements'), $.tag_name), + $._type, + ), // @extends [Type] // @template-extends [Type] - _generic_extends_tag: $ => seq( - alias(choice( - '@extends', - '@template-extends' - ), $.tag_name), - $._type, - ), + _generic_extends_tag: ($) => + seq(alias(choice('@extends', '@template-extends'), $.tag_name), $._type), // @use [Type] // @template-use [Type] - _generic_use_tag: $ => seq( - alias(choice( - '@use', - '@template-use' - ), $.tag_name), - $._type, - ), + _generic_use_tag: ($) => + seq(alias(choice('@use', '@template-use'), $.tag_name), $._type), // partial support for phpunit tags // TODO id the "core" tags and flesh out their support (ie some tags take // no text, some take types, etc) // curl https://phpunit.readthedocs.io/en/9.5/annotations.html | grep '@' | sed 's/@/@/g' | sed -E 's/^.+@/@/' | sed -E 's/<.+$//' | sort | uniq - _phpunit_tag: $ => seq( - alias( - choice( - '@after', - '@afterClass', - '@annotation', - // '@author', // already part of phpdoc - '@backupGlobals', - '@backupStaticAttributes', - '@before', - '@beforeClass', - '@codeCoverageIgnore', - '@codeCoverageIgnore*', - '@codeCoverageIgnoreEnd', - '@codeCoverageIgnoreStart', - '@covers', - '@coversDefaultClass', - '@coversDefaultClass to shorten annotations', - '@coversNothing', - '@dataProvider', - '@depends', - '@depends annotation to express dependencies', - '@doesNotPerformAssertions', - '@group', - '@large', - '@medium', - '@preserveGlobalState', - '@requires', - '@requires usages', - '@runInSeparateProcess', - '@runTestsInSeparateProcesses', - '@small', - '@test', - '@testWith', - '@testdox', - '@ticket', - // '@uses', // already part of phpdoc + _phpunit_tag: ($) => + seq( + alias( + choice( + '@after', + '@afterClass', + '@annotation', + // '@author', // already part of phpdoc + '@backupGlobals', + '@backupStaticAttributes', + '@before', + '@beforeClass', + '@codeCoverageIgnore', + '@codeCoverageIgnore*', + '@codeCoverageIgnoreEnd', + '@codeCoverageIgnoreStart', + '@covers', + '@coversDefaultClass', + '@coversDefaultClass to shorten annotations', + '@coversNothing', + '@dataProvider', + '@depends', + '@depends annotation to express dependencies', + '@doesNotPerformAssertions', + '@group', + '@large', + '@medium', + '@preserveGlobalState', + '@requires', + '@requires usages', + '@runInSeparateProcess', + '@runTestsInSeparateProcesses', + '@small', + '@test', + '@testWith', + '@testdox', + '@ticket', + // '@uses', // already part of phpdoc + ), + $.tag_name, ), - $.tag_name + optional($.description), ), - optional($.description), - ), - _psalm_tag: $ => choice( - alias( - choice( - '@psalm-consistent-constructor', - '@psalm-consistent-templates', - '@psalm-var', - '@psalm-param', - '@psalm-return', - '@psalm-property', - '@psalm-property-read', - '@psalm-property-write', - '@psalm-method', - '@psalm-ignore-var', - '@psalm-if-this-is', - '@psalm-this-out', - '@psalm-ignore-nullable-return', - '@psalm-ignore-falsable-return', - '@psalm-seal-properties', - '@psalm-readonly', - '@readonly', - '@psalm-mutation-free', - '@psalm-external-mutation-free', - '@psalm-immutable', - '@psalm-pure', - '@psalm-allow-private-mutation', - '@psalm-readonly-allow-private-mutation', - '@psalm-trace', - '@no-named-arguments' + _psalm_tag: ($) => + choice( + alias( + choice( + '@psalm-consistent-constructor', + '@psalm-consistent-templates', + '@psalm-var', + '@psalm-param', + '@psalm-return', + '@psalm-property', + '@psalm-property-read', + '@psalm-property-write', + '@psalm-method', + '@psalm-ignore-var', + '@psalm-if-this-is', + '@psalm-this-out', + '@psalm-ignore-nullable-return', + '@psalm-ignore-falsable-return', + '@psalm-seal-properties', + '@psalm-readonly', + '@readonly', + '@psalm-mutation-free', + '@psalm-external-mutation-free', + '@psalm-immutable', + '@psalm-pure', + '@psalm-allow-private-mutation', + '@psalm-readonly-allow-private-mutation', + '@psalm-trace', + '@no-named-arguments', + ), + $.tag_name, ), - $.tag_name - ), - // @psalm-trace [name] - seq( - alias(choice( - '@psalm-trace' - ), $.tag_name), - $.variable_name - ), - seq( - alias(choice( - '@param-out', - '@psalm-param-out' - ), $.tag_name), - $._type, - $.variable_name - ), - // @psalm-asset[<-if-true|-if-false] [Type] [name|expression] - seq( - alias(choice( - '@psalm-assert', - '@psalm-assert-if-true', - '@psalm-assert-if-false', - ), $.tag_name), - $._type, - choice( + // @psalm-trace [name] + seq(alias(choice('@psalm-trace'), $.tag_name), $.variable_name), + seq( + alias(choice('@param-out', '@psalm-param-out'), $.tag_name), + $._type, $.variable_name, - // TODO allow PHP expression - ) - ), - //TODO implement @psalm-taint-* see https://psalm.dev/docs/security_analysis/annotations/ - seq( - alias('@psalm-import-type', $.tag_name), - $.named_type, - 'from', - $.named_type, - optional(PHP.rules.namespace_aliasing_clause) - ), - // @psalm-suppress [Type] - // @psalm-internal [Type] - // @psalm-require-[extends|implements] [Type] - seq( - alias(choice( - '@psalm-suppress', - '@psalm-internal', - '@psalm-require-extends', - '@psalm-require-implements' - ), $.tag_name), - $.named_type + ), + // @psalm-asset[<-if-true|-if-false] [Type] [name|expression] + seq( + alias( + choice( + '@psalm-assert', + '@psalm-assert-if-true', + '@psalm-assert-if-false', + ), + $.tag_name, + ), + $._type, + choice( + $.variable_name, + // TODO allow PHP expression + ), + ), + //TODO implement @psalm-taint-* see https://psalm.dev/docs/security_analysis/annotations/ + seq( + alias('@psalm-import-type', $.tag_name), + $.named_type, + 'from', + $.named_type, + optional(PHP.rules.namespace_aliasing_clause), + ), + // @psalm-suppress [Type] + // @psalm-internal [Type] + // @psalm-require-[extends|implements] [Type] + seq( + alias( + choice( + '@psalm-suppress', + '@psalm-internal', + '@psalm-require-extends', + '@psalm-require-implements', + ), + $.tag_name, + ), + $.named_type, + ), ), - ), // https://phpstan.org/writing-php-code/phpdocs-basics#mixins // https://psalm.dev/docs/annotating_code/supported_annotations/#mixins // @mixin [Type] - _mixin_tag: $ => seq( - alias('@mixin', $.tag_name), - $._type, - ), + _mixin_tag: ($) => seq(alias('@mixin', $.tag_name), $._type), // PHP.rules._type creates an alias for $.type_list - _type: $ => PHP.rules._type, + _type: ($) => PHP.rules._type, // union_type uses _types, so we override it to be "regular" types (which // aren't grouped under a parent node) or array types (which are) - _types: $ => choice( - $._regular_types, - alias($._psalm_scalar_type, $.primitive_type), - alias($._phpdoc_array_types, $.array_type), - alias($._psalm_generic_array_types, $.array_type), - alias($._psalm_list_array_types, $.array_type) - ), - _regular_types: $ => PHP.rules._types, - _phpdoc_array_types: $ => seq( - $._regular_types, - repeat1("[]") - ), - _psalm_generic_array_types: $ => seq( - field('array', $._regular_types), - "<", - field('key', $._regular_types), - ",", - field('value', $._regular_types), - ">" - ), - _psalm_list_array_types: $ => seq( - field('array', choice(alias("list", $.primitive_type), $._regular_types)), - "<", - field('value', $._types), - ">" - ), - - _psalm_scalar_type: $ => choice( + _types: ($) => + choice( + $._regular_types, + alias($._psalm_scalar_type, $.primitive_type), + alias($._phpdoc_array_types, $.array_type), + alias($._psalm_generic_array_types, $.array_type), + alias($._psalm_list_array_types, $.array_type), + ), + _regular_types: ($) => PHP.rules._types, + _phpdoc_array_types: ($) => seq($._regular_types, repeat1('[]')), + _psalm_generic_array_types: ($) => seq( - 'class-string', - optional($._type_argument_named_type) - ), - 'interface-string', - 'positive-int', - 'trait-string', - 'enum-string', - 'callable-string', - 'numeric-string', - 'literal-string', - 'lowercase-string', - 'non-empty-string', - 'non-empty-lowercase-string' - ), - - generic_type: $ => seq(PHP.rules.named_type, $._type_argument_list), + field('array', $._regular_types), + '<', + field('key', $._regular_types), + ',', + field('value', $._regular_types), + '>', + ), + _psalm_list_array_types: ($) => + seq( + field( + 'array', + choice(alias('list', $.primitive_type), $._regular_types), + ), + '<', + field('value', $._types), + '>', + ), - _type_argument_list: $ => seq( - '<', + _psalm_scalar_type: ($) => choice( - repeat(','), - sep(',', $._type), + seq('class-string', optional($._type_argument_named_type)), + 'interface-string', + 'positive-int', + 'trait-string', + 'enum-string', + 'callable-string', + 'numeric-string', + 'literal-string', + 'lowercase-string', + 'non-empty-string', + 'non-empty-lowercase-string', ), - '>' - ), - _type_argument_named_type: $ => seq( - '<', - $.named_type, - '>' - ), + generic_type: ($) => seq(PHP.rules.named_type, $._type_argument_list), - name: $ => PHP.rules.name, - named_type: $ => choice(PHP.rules.named_type, $.generic_type), - namespace_name: $ => PHP.rules.namespace_name, - namespace_name_as_prefix: $ => PHP.rules.namespace_name_as_prefix, - optional_type: $ => PHP.rules.optional_type, - primitive_type: $ => PHP.rules.primitive_type, - qualified_name: $ => PHP.rules.qualified_name, - union_type: $ => PHP.rules.union_type, - intersection_type: $ => PHP.rules.intersection_type, - disjunctive_normal_form_type: $ => PHP.rules.disjunctive_normal_form_type, - variable_name: $ => PHP.rules.variable_name, + _type_argument_list: ($) => + seq('<', choice(repeat(','), sep(',', $._type)), '>'), + + _type_argument_named_type: ($) => seq('<', $.named_type, '>'), + + name: ($) => PHP.rules.name, + named_type: ($) => choice(PHP.rules.named_type, $.generic_type), + namespace_name: ($) => PHP.rules.namespace_name, + namespace_name_as_prefix: ($) => PHP.rules.namespace_name_as_prefix, + optional_type: ($) => PHP.rules.optional_type, + primitive_type: ($) => PHP.rules.primitive_type, + qualified_name: ($) => PHP.rules.qualified_name, + union_type: ($) => PHP.rules.union_type, + intersection_type: ($) => PHP.rules.intersection_type, + disjunctive_normal_form_type: ($) => PHP.rules.disjunctive_normal_form_type, + variable_name: ($) => PHP.rules.variable_name, // Match as many words as possible, where a word is just a sequence of // non-whitespace and non-< characters, separated by a space. (The non-< // requirement makes sure this regex doesn't consume the field.) - author_name: $ => /\S+( [^\s<]+)*/, + author_name: ($) => /\S+( [^\s<]+)*/, // Simplisitic regex to match anything@anything.anything, where the last // anything also doesn't include a closing angle bracket - email_address: $ => /\S+@\S+\.[^\s>]+/, - - version: $ => choice( - // phpDoc does only recommend semantic versioning but it's not mandatory. - // So we gracefully accept every word (i.e. no space included) that - // starts with a number + dot as version. - /\d+\.[^\s]+/, - // Version vectors: - // $Id$ - // name-of-vcs: $Id$ - $._version_vector, - seq(/[a-zA-Z-_]+: */, $._version_vector), - // Used by PEAR - '@package_version@' - ), - _version_vector: $ => /\$[a-zA-Z_][a-zA-Z0-9-_]*\$/, + email_address: ($) => /\S+@\S+\.[^\s>]+/, - uri: $ => /https?:\/\/[^\s}]+/, + version: ($) => + choice( + // phpDoc does only recommend semantic versioning but it's not mandatory. + // So we gracefully accept every word (i.e. no space included) that + // starts with a number + dot as version. + /\d+\.[^\s]+/, + // Version vectors: + // $Id$ + // name-of-vcs: $Id$ + $._version_vector, + seq(/[a-zA-Z-_]+: */, $._version_vector), + // Used by PEAR + '@package_version@', + ), + _version_vector: ($) => /\$[a-zA-Z_][a-zA-Z0-9-_]*\$/, - fqsen: $ => choice( - $.named_type, - $.variable_name, - seq($.name, '()'), - seq($.named_type, '::', $.name), - seq($.named_type, '::', $.name, '()'), - seq($.named_type, '::', $.variable_name) - ), + uri: ($) => /https?:\/\/[^\s}]+/, - parameters: $ => seq( - '(', - sep($.parameter, ','), - ')', - ), + fqsen: ($) => + choice( + $.named_type, + $.variable_name, + seq($.name, '()'), + seq($.named_type, '::', $.name), + seq($.named_type, '::', $.name, '()'), + seq($.named_type, '::', $.variable_name), + ), - parameter: $ => seq( - optional($._type), - $.variable_name, - optional(seq( - '=', - $.default_value, - )), - ), + parameters: ($) => seq('(', sep($.parameter, ','), ')'), + + parameter: ($) => + seq( + optional($._type), + $.variable_name, + optional(seq('=', $.default_value)), + ), - default_value: $ => /[^, ][^,)]*/, + default_value: ($) => /[^, ][^,)]*/, - static: $ => 'static', + static: ($) => 'static', - _end: $ => '*/', + _end: ($) => '*/', }, -}) +}); function sep1(rule, sep) { return seq(rule, repeat(seq(sep, rule))); diff --git a/package-lock.json b/package-lock.json index 7ba8aa2..5e5908c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ }, "devDependencies": { "prebuildify": "^6.0.0", + "prettier": "^3.3.3", "tree-sitter-cli": "^0.22.6", "tree-sitter-php": "^0.22.2" }, @@ -239,6 +240,21 @@ "prebuildify": "bin.js" } }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", diff --git a/package.json b/package.json index 7f4f9ef..84df6ac 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "generate": "./node_modules/.bin/tree-sitter generate", "test": "if [ $(stat -f %m ./grammar.js) -gt $(stat -f %m ./src/grammar.json) -o $(stat -f %m ./src/scanner.c) -gt $(stat -f %m ./src/parser.c) ]; then echo Regenerating...; ./node_modules/.bin/tree-sitter generate; fi && ./node_modules/.bin/tree-sitter test", "install": "node-gyp-build", - "prebuildify": "prebuildify --napi --strip" + "prebuildify": "prebuildify --napi --strip", + "format": "./node_modules/.bin/prettier --write grammar.js .github/workflows/ci.yml", + "format-check": "npm run format -- --check" }, "repository": { "type": "git", @@ -36,9 +38,10 @@ }, "homepage": "https://github.com/claytonrcarter/tree-sitter-phpdoc#readme", "devDependencies": { + "prebuildify": "^6.0.0", + "prettier": "^3.3.3", "tree-sitter-cli": "^0.22.6", - "tree-sitter-php": "^0.22.2", - "prebuildify": "^6.0.0" + "tree-sitter-php": "^0.22.2" }, "dependencies": { "node-addon-api": "^7.1.0",