From 3081cdcdc9cdd76cff0ac1369ce34b6db6a0abeb Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 17 Aug 2024 12:53:01 +0200 Subject: [PATCH] Introduce NAMED_ARGUMENT_OPERATOR_TYPE --- docs/custom.md | 4 ++ src/Token/Token.php | 1 + src/Token/Tokenizer.php | 20 ++++++ .../HashQuote/HashQuoteRuleTest.fixed.twig | 2 + .../String/HashQuote/HashQuoteRuleTest.twig | 2 + .../HashQuoteRuleTest.with.fixed.twig | 2 + tests/Token/Tokenizer/Fixtures/test15.twig | 5 ++ tests/Token/Tokenizer/TokenizerTest.php | 62 +++++++++++++++++++ 8 files changed, 98 insertions(+) create mode 100644 tests/Token/Tokenizer/Fixtures/test15.twig diff --git a/docs/custom.md b/docs/custom.md index 54ed234b..8b54ff13 100644 --- a/docs/custom.md +++ b/docs/custom.md @@ -124,6 +124,10 @@ The `TwigCsFixer\Token\Tokenizer` transform the file into a list of tokens which The `#}` delimiter. +- **TwigCsFixer\Token\Token::NAMED_ARGUMENT_OPERATOR_TYPE**: + + The `=` or `:` operator used when using named argument. Like `{{ foo(bar=true, baz: false) }}`. + Then, the easiest way to write a custom rule is to implement the `TwigCsFixer\Rules\AbstractRule` class or the `TwigCsFixer\Rules\AbstractFixableRule` if the rule can be automatically fixed. diff --git a/src/Token/Token.php b/src/Token/Token.php index efb687cd..3f12ff2e 100644 --- a/src/Token/Token.php +++ b/src/Token/Token.php @@ -41,6 +41,7 @@ final class Token public const COMMENT_TAB_TYPE = 'COMMENT_TAB_TYPE'; public const COMMENT_EOL_TYPE = 'COMMENT_EOL_TYPE'; public const COMMENT_END_TYPE = 'COMMENT_END_TYPE'; + public const NAMED_ARGUMENT_OPERATOR_TYPE = 'NAMED_ARGUMENT_OPERATOR_TYPE'; public const WHITESPACE_TOKENS = [ self::WHITESPACE_TYPE => self::WHITESPACE_TYPE, diff --git a/src/Token/Tokenizer.php b/src/Token/Tokenizer.php index 69e048a3..abfe5ad2 100644 --- a/src/Token/Tokenizer.php +++ b/src/Token/Tokenizer.php @@ -579,6 +579,9 @@ private function lexArrowFunction(): void $this->pushToken(Token::ARROW_TYPE, '=>'); } + /** + * @throws CannotTokenizeException + */ private function lexOperator(string $operator): void { if ('?' === $operator) { @@ -587,6 +590,17 @@ private function lexOperator(string $operator): void } elseif (':' === $operator && $this->isInTernary()) { $bracket = array_pop($this->bracketsAndTernary); $this->pushToken(Token::OPERATOR_TYPE, $operator, $bracket); + } elseif ('=' === $operator) { + $bracket = end($this->bracketsAndTernary); + if (false !== $bracket && '(' === $bracket->getValue()) { + // This is a named argument operator instead + $this->pushToken(Token::NAMED_ARGUMENT_OPERATOR_TYPE, $operator); + + return; + } + + + $this->pushToken(Token::OPERATOR_TYPE, $operator); } else { $this->pushToken(Token::OPERATOR_TYPE, $operator); } @@ -681,6 +695,12 @@ private function lexPunctuation(): void return; } + if ('(' === $bracket->getValue()) { + // This is a named argument operator instead + $this->pushToken(Token::NAMED_ARGUMENT_OPERATOR_TYPE, $currentCode); + + return; + } $this->pushToken(Token::PUNCTUATION_TYPE, $currentCode); } else { diff --git a/tests/Rules/String/HashQuote/HashQuoteRuleTest.fixed.twig b/tests/Rules/String/HashQuote/HashQuoteRuleTest.fixed.twig index bd9ff2c8..ac6a6b43 100644 --- a/tests/Rules/String/HashQuote/HashQuoteRuleTest.fixed.twig +++ b/tests/Rules/String/HashQuote/HashQuoteRuleTest.fixed.twig @@ -15,3 +15,5 @@ {% set float = {'12.3': a} %} {% set needQuote = {'data-foo': a} %} + +{% set namedArgument = foo(bar: true, baz: false) %} diff --git a/tests/Rules/String/HashQuote/HashQuoteRuleTest.twig b/tests/Rules/String/HashQuote/HashQuoteRuleTest.twig index 3fd48af7..c363d3c7 100644 --- a/tests/Rules/String/HashQuote/HashQuoteRuleTest.twig +++ b/tests/Rules/String/HashQuote/HashQuoteRuleTest.twig @@ -15,3 +15,5 @@ {% set float = {'12.3': a} %} {% set needQuote = {'data-foo': a} %} + +{% set namedArgument = foo(bar: true, baz: false) %} diff --git a/tests/Rules/String/HashQuote/HashQuoteRuleTest.with.fixed.twig b/tests/Rules/String/HashQuote/HashQuoteRuleTest.with.fixed.twig index 09554751..8acba392 100644 --- a/tests/Rules/String/HashQuote/HashQuoteRuleTest.with.fixed.twig +++ b/tests/Rules/String/HashQuote/HashQuoteRuleTest.with.fixed.twig @@ -15,3 +15,5 @@ {% set float = {'12.3': a} %} {% set needQuote = {'data-foo': a} %} + +{% set namedArgument = foo(bar: true, baz: false) %} diff --git a/tests/Token/Tokenizer/Fixtures/test15.twig b/tests/Token/Tokenizer/Fixtures/test15.twig new file mode 100644 index 00000000..b885331f --- /dev/null +++ b/tests/Token/Tokenizer/Fixtures/test15.twig @@ -0,0 +1,5 @@ +{% set foo=1 %} +{{ foo(foo=1) }} +{{ foo(foo:1) }} +{{ foo({foo:1}) }} +{{ (foo==1) }} diff --git a/tests/Token/Tokenizer/TokenizerTest.php b/tests/Token/Tokenizer/TokenizerTest.php index d2edf51c..e14d9001 100644 --- a/tests/Token/Tokenizer/TokenizerTest.php +++ b/tests/Token/Tokenizer/TokenizerTest.php @@ -688,6 +688,68 @@ public static function tokenizeDataProvider(): iterable 36 => Token::EOF_TYPE, ], ]; + + yield [ + __DIR__.'/Fixtures/test15.twig', + [ + 0 => Token::BLOCK_START_TYPE, + 1 => Token::WHITESPACE_TYPE, + 2 => Token::BLOCK_NAME_TYPE, + 3 => Token::WHITESPACE_TYPE, + 4 => Token::NAME_TYPE, + 5 => Token::OPERATOR_TYPE, + 6 => Token::NUMBER_TYPE, + 7 => Token::WHITESPACE_TYPE, + 8 => Token::BLOCK_END_TYPE, + 9 => Token::EOL_TYPE, + 10 => Token::VAR_START_TYPE, + 11 => Token::WHITESPACE_TYPE, + 12 => Token::FUNCTION_NAME_TYPE, + 13 => Token::PUNCTUATION_TYPE, + 14 => Token::NAME_TYPE, + 15 => Token::NAMED_ARGUMENT_OPERATOR_TYPE, + 16 => Token::NUMBER_TYPE, + 17 => Token::PUNCTUATION_TYPE, + 18 => Token::WHITESPACE_TYPE, + 19 => Token::VAR_END_TYPE, + 20 => Token::EOL_TYPE, + 21 => Token::VAR_START_TYPE, + 22 => Token::WHITESPACE_TYPE, + 23 => Token::FUNCTION_NAME_TYPE, + 24 => Token::PUNCTUATION_TYPE, + 25 => Token::NAME_TYPE, + 26 => Token::NAMED_ARGUMENT_OPERATOR_TYPE, + 27 => Token::NUMBER_TYPE, + 28 => Token::PUNCTUATION_TYPE, + 29 => Token::WHITESPACE_TYPE, + 30 => Token::VAR_END_TYPE, + 31 => Token::EOL_TYPE, + 32 => Token::VAR_START_TYPE, + 33 => Token::WHITESPACE_TYPE, + 34 => Token::FUNCTION_NAME_TYPE, + 35 => Token::PUNCTUATION_TYPE, + 36 => Token::PUNCTUATION_TYPE, + 37 => Token::NAME_TYPE, + 38 => Token::PUNCTUATION_TYPE, + 39 => Token::NUMBER_TYPE, + 40 => Token::PUNCTUATION_TYPE, + 41 => Token::PUNCTUATION_TYPE, + 42 => Token::WHITESPACE_TYPE, + 43 => Token::VAR_END_TYPE, + 44 => Token::EOL_TYPE, + 45 => Token::VAR_START_TYPE, + 46 => Token::WHITESPACE_TYPE, + 47 => Token::PUNCTUATION_TYPE, + 48 => Token::NAME_TYPE, + 49 => Token::OPERATOR_TYPE, + 50 => Token::NUMBER_TYPE, + 51 => Token::PUNCTUATION_TYPE, + 52 => Token::WHITESPACE_TYPE, + 53 => Token::VAR_END_TYPE, + 54 => Token::EOL_TYPE, + 55 => Token::EOF_TYPE, + ], + ]; } /**