diff --git a/README.md b/README.md index 861888e..0adf585 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,23 @@ Can also be used with the BEM Function:
``` +### Switch Case Twig Extension + +This adds the ability to do a `switch/case` function from within Twig templates. To use: + +```twig +{% switch content.field_name.0 %} + {% case "text" %} +

This appears if the field name value is set to "text"

+ {% case "image" %} +

This appears if the field name value is set to "image"

+ {% default %} +

The field text did not match any case.

+{% endswitch %} +``` + +Note that the `switch`, `endswitch`, and `case` tags are required and the `default` is optional. + ## Development --- diff --git a/emulsify_tools.services.yml b/emulsify_tools.services.yml index 238d8dc..2e162c4 100644 --- a/emulsify_tools.services.yml +++ b/emulsify_tools.services.yml @@ -16,3 +16,7 @@ services: - { name: drush.command } emulsify_tools.subtheme_generator: class: Drupal\emulsify_tools\SubThemeGenerator + emulsify_tools.twig.switch: + class: Drupal\emulsify_tools\SwitchExtension + tags: + - { name: twig.extension } diff --git a/src/SwitchExtension.php b/src/SwitchExtension.php new file mode 100644 index 0000000..3b32776 --- /dev/null +++ b/src/SwitchExtension.php @@ -0,0 +1,21 @@ +addDebugInfo($this) + ->write('switch (') + ->subcompile($this->getNode('value')) + ->raw(") {\n") + ->indent(); + + foreach ($this->getNode('cases') as $case) { + /** @var Twig\Node\Node $case */ + /* The 'body' node may have been removed by Twig if it was an empty text + * node in a sub-template, outside of any blocks. + */ + if (!$case->hasNode('body')) { + continue; + } + + foreach ($case->getNode('values') as $value) { + $compiler + ->write('case ') + ->subcompile($value) + ->raw(":\n"); + } + + $compiler + ->write("{\n") + ->indent() + ->subcompile($case->getNode('body')) + ->write("break;\n") + ->outdent() + ->write("}\n"); + } + + if ($this->hasNode('default')) { + $compiler + ->write("default:\n") + ->write("{\n") + ->indent() + ->subcompile($this->getNode('default')) + ->outdent() + ->write("}\n"); + } + + $compiler + ->outdent() + ->write("}\n"); + } + +} diff --git a/src/SwitchTokenParser.php b/src/SwitchTokenParser.php new file mode 100644 index 0000000..df6ede3 --- /dev/null +++ b/src/SwitchTokenParser.php @@ -0,0 +1,120 @@ +getLine(); + $parser = $this->parser; + $stream = $parser->getStream(); + + $nodes = [ + 'value' => $parser->getExpressionParser()->parseExpression(), + ]; + + $stream->expect(Token::BLOCK_END_TYPE); + + // Trim whitespace between the {% switch %} and first {% case %} tag. + while ($stream->getCurrent()->getType() == Token::TEXT_TYPE && trim($stream->getCurrent()->getValue()) === '') { + $stream->next(); + } + + $stream->expect(Token::BLOCK_START_TYPE); + + $expressionParser = $parser->getExpressionParser(); + $cases = []; + $end = FALSE; + + while (!$end) { + $next = $stream->next(); + + switch ($next->getValue()) { + case 'case': + $values = []; + while (TRUE) { + $values[] = $expressionParser->parsePrimaryExpression(); + // Multiple allowed values? + if ($stream->test(Token::OPERATOR_TYPE, 'or')) { + $stream->next(); + } + else { + break; + } + } + $stream->expect(Token::BLOCK_END_TYPE); + $body = $parser->subparse([$this, 'decideIfFork']); + $cases[] = new Node([ + 'values' => new Node($values), + 'body' => $body, + ]); + break; + + case 'default': + $stream->expect(Token::BLOCK_END_TYPE); + $nodes['default'] = $parser->subparse([$this, 'decideIfEnd']); + break; + + case 'endswitch': + $end = TRUE; + break; + + default: + throw new SyntaxError(sprintf('Unexpected end of template. Twig was looking for the following tags "case", "default", or "endswitch" to close the "switch" block started at line %d)', $lineno), -1); + } + } + + $nodes['cases'] = new Node($cases); + + $stream->expect(Token::BLOCK_END_TYPE); + + return new SwitchNode($nodes, [], $lineno, $this->getTag()); + } + + /** + * Decide IF Fork. + * + * @param Twig\Token $token + * The token to parse. + * + * @return bool + * Returns if one of the tokens is part of this switch statement. + */ + public function decideIfFork(Token $token): bool { + return $token->test(['case', 'default', 'endswitch']); + } + + /** + * Decides if end of switch statement. + * + * @param Twig\Token $token + * The token to parse. + * + * @return bool + * Returns if we are at the end of the switch statement. + */ + public function decideIfEnd(Token $token): bool { + return $token->test(['endswitch']); + } + +}