diff --git a/docs/custom.md b/docs/custom.md index da4eaf8e..1293af01 100644 --- a/docs/custom.md +++ b/docs/custom.md @@ -93,6 +93,10 @@ The `TwigCsFixer\Token\Tokenizer` transform the file into a list of tokens which The name used in the definition of a macro function. Like `foo` in `{% macro foo() %}`. +- **TwigCsFixer\Token\Token::MACRO_VAR_NAME_TYPE**: + + The name used for params of a macro function. Like `bar` in `{% macro foo(bar) %}`. + - **TwigCsFixer\Token\Token::TEST_NAME_TYPE**: The name of a test function. Like in `{% if foo is test(bar) %}`. diff --git a/src/Token/Token.php b/src/Token/Token.php index b396c6e9..15be5432 100644 --- a/src/Token/Token.php +++ b/src/Token/Token.php @@ -32,6 +32,7 @@ final class Token public const FUNCTION_NAME_TYPE = 'FUNCTION_NAME_TYPE'; public const FILTER_NAME_TYPE = 'FILTER_NAME_TYPE'; public const MACRO_NAME_TYPE = 'MACRO_NAME_TYPE'; + public const MACRO_VAR_NAME_TYPE = 'MACRO_VAR_NAME_TYPE'; public const TEST_NAME_TYPE = 'TEST_NAME_TYPE'; public const WHITESPACE_TYPE = 'WHITESPACE_TYPE'; public const TAB_TYPE = 'TAB_TYPE'; diff --git a/src/Token/Tokenizer.php b/src/Token/Tokenizer.php index 42520e6c..c9478aec 100644 --- a/src/Token/Tokenizer.php +++ b/src/Token/Tokenizer.php @@ -182,11 +182,16 @@ private function getBrackets(): array return array_filter($this->bracketsAndTernary, static fn (Token $token): bool => '?' !== $token->getValue()); } - private function isInTernary(): bool + private function lastBracketMatch(string $value): bool { $lastBracket = end($this->bracketsAndTernary); - return false !== $lastBracket && '?' === $lastBracket->getValue(); + return false !== $lastBracket && $value === $lastBracket->getValue(); + } + + private function isInTernary(): bool + { + return $this->lastBracketMatch('?'); } /** @@ -430,11 +435,8 @@ private function lexDqString(): void */ private function lexInterpolation(): void { - $bracket = end($this->bracketsAndTernary); - Assert::notFalse($bracket, 'Interpolation always start with a bracket.'); - if ( - '#{' === $bracket->getValue() + $this->lastBracketMatch('#{') && 1 === preg_match(self::REGEX_INTERPOLATION_END, $this->code, $match, 0, $this->cursor) ) { $bracket = array_pop($this->bracketsAndTernary); @@ -592,8 +594,7 @@ private function lexOperator(string $operator): void self::STATE_BLOCK !== $this->getState() || 'macro' !== $this->getStateParam('blockName') ) { - $bracket = end($this->bracketsAndTernary); - if (false !== $bracket && '(' === $bracket->getValue()) { + if ($this->lastBracketMatch('(')) { // This is a named argument separator instead $this->pushToken(Token::NAMED_ARGUMENT_SEPARATOR_TYPE, $operator); @@ -640,6 +641,12 @@ private function lexName(string $name): void && null === $lastNonEmptyToken->getRelatedToken() ) { $this->pushToken(Token::TEST_NAME_TYPE, $name, $lastNonEmptyToken); + } elseif ( + self::STATE_BLOCK === $this->getState() + && 'macro' === $this->getStateParam('blockName') + && $this->lastBracketMatch('(') + ) { + $this->pushToken(Token::MACRO_VAR_NAME_TYPE, $name); } else { $this->pushToken(Token::NAME_TYPE, $name); } @@ -699,18 +706,13 @@ private function lexPunctuation(): void $this->pushToken(Token::PUNCTUATION_TYPE, $currentCode, $bracket); } elseif (':' === $currentCode) { - if ([] === $this->bracketsAndTernary) { - throw CannotTokenizeException::unexpectedCharacter($currentCode, $this->line); - } - - $bracket = end($this->bracketsAndTernary); - if ('[' === $bracket->getValue()) { + if ($this->lastBracketMatch('[')) { // This is a slice shortcut '[0:1]' instead $this->lexOperator($currentCode); return; } - if ('(' === $bracket->getValue()) { + if ($this->lastBracketMatch('(')) { // This is a named argument separator instead $this->pushToken(Token::NAMED_ARGUMENT_SEPARATOR_TYPE, $currentCode); diff --git a/tests/Token/Tokenizer/Fixtures/invalid7.twig b/tests/Token/Tokenizer/Fixtures/invalid7.twig deleted file mode 100644 index 227a3c8f..00000000 --- a/tests/Token/Tokenizer/Fixtures/invalid7.twig +++ /dev/null @@ -1 +0,0 @@ -{{ foo : bar }} diff --git a/tests/Token/Tokenizer/Fixtures/test15.twig b/tests/Token/Tokenizer/Fixtures/test15.twig index 3a676d57..74968a40 100644 --- a/tests/Token/Tokenizer/Fixtures/test15.twig +++ b/tests/Token/Tokenizer/Fixtures/test15.twig @@ -3,4 +3,4 @@ {{ foo(foo:1) }} {{ foo({foo:1}) }} {{ (foo==1) }} -{% macro input(name='foo') %}{{ name }}{% endmacro %} +{% macro input(name='foo', opts={foo: 1}) %}{{ name }}{% endmacro %} diff --git a/tests/Token/Tokenizer/TokenizerTest.php b/tests/Token/Tokenizer/TokenizerTest.php index db59ad84..7bb2e865 100644 --- a/tests/Token/Tokenizer/TokenizerTest.php +++ b/tests/Token/Tokenizer/TokenizerTest.php @@ -753,24 +753,34 @@ public static function tokenizeDataProvider(): iterable 58 => Token::WHITESPACE_TYPE, 59 => Token::MACRO_NAME_TYPE, 60 => Token::PUNCTUATION_TYPE, - 61 => Token::NAME_TYPE, + 61 => Token::MACRO_VAR_NAME_TYPE, 62 => Token::OPERATOR_TYPE, 63 => Token::STRING_TYPE, 64 => Token::PUNCTUATION_TYPE, 65 => Token::WHITESPACE_TYPE, - 66 => Token::BLOCK_END_TYPE, - 67 => Token::VAR_START_TYPE, - 68 => Token::WHITESPACE_TYPE, + 66 => Token::MACRO_VAR_NAME_TYPE, + 67 => Token::OPERATOR_TYPE, + 68 => Token::PUNCTUATION_TYPE, 69 => Token::NAME_TYPE, - 70 => Token::WHITESPACE_TYPE, - 71 => Token::VAR_END_TYPE, - 72 => Token::BLOCK_START_TYPE, - 73 => Token::WHITESPACE_TYPE, - 74 => Token::BLOCK_NAME_TYPE, + 70 => Token::PUNCTUATION_TYPE, + 71 => Token::WHITESPACE_TYPE, + 72 => Token::NUMBER_TYPE, + 73 => Token::PUNCTUATION_TYPE, + 74 => Token::PUNCTUATION_TYPE, 75 => Token::WHITESPACE_TYPE, 76 => Token::BLOCK_END_TYPE, - 77 => Token::EOL_TYPE, - 78 => Token::EOF_TYPE, + 77 => Token::VAR_START_TYPE, + 78 => Token::WHITESPACE_TYPE, + 79 => Token::NAME_TYPE, + 80 => Token::WHITESPACE_TYPE, + 81 => Token::VAR_END_TYPE, + 82 => Token::BLOCK_START_TYPE, + 83 => Token::WHITESPACE_TYPE, + 84 => Token::BLOCK_NAME_TYPE, + 85 => Token::WHITESPACE_TYPE, + 86 => Token::BLOCK_END_TYPE, + 87 => Token::EOL_TYPE, + 88 => Token::EOF_TYPE, ], ]; @@ -829,6 +839,5 @@ public static function tokenizeInvalidDataProvider(): iterable yield [__DIR__.'/Fixtures/invalid4.twig', 'Unexpected character ")" at line 1.']; yield [__DIR__.'/Fixtures/invalid5.twig', 'Unexpected character "#" at line 1.']; yield [__DIR__.'/Fixtures/invalid6.twig', 'Unclosed comment at line 1.']; - yield [__DIR__.'/Fixtures/invalid7.twig', 'Unexpected character ":" at line 1.']; } }