3

I have been trying for a few hours to beautify the output that twig generates, such a way that will be properly indented with tabs. For example, I would like the following output:

<html>
<head>
</head>
<body>
</body>
</html>

To become something like: For example, I would like the following output:

<html>
  <head>
  </head>
  <body>
  </body>
</html>

I would like for this "autoindentation" to happen for all tags, because it makes the markup clean and easier to debug. So I found this question: https://stackoverflow.com/questions/11878728/rendering-clean-tidy-html-with-twig-templates And since I have been trying to make drupal load that twig indent extension and that twig lexer extension.

My code so far:

root.info.yml

name: Root
type: module
description: ‘Provides basic cosmetic functionality that should be in core.’
core: 8.x
version: 0.0.1

root.services.yml

services:
  # Override the default Twig Environment so we can add our custom Twig Lexer.
  twig:
    class: Drupal\root\TwigEnvironment
    arguments: ['@app.root', '@cache.default', '%twig_extension_hash%', '@state', '@twig.loader', '%twig.config%']
    tags:
      - { name: service_collector, tag: 'twig.extension', call: addExtension }
  root.twig.Indent:
    class: Drupal\root\Indent
    tags:
      - {name: twig.extension}
  root.twig.Lexer:
    class: Drupal\root\Lexer
    tags:
      - {name: twig.lexer}

and the Indent.php and Lexer.php as provided in that answer. The problem is that the lexer wold not load because the tag twig.lexer does not exist.

So I tried to add the following code:

namespace Drupal\root;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Template\TwigEnvironment as CoreTwigEnvironment;
use Drupal\root\Indent;

/**
 * Twig environment that uses temporary file storage.
 */
class TwigEnvironment extends CoreTwigEnvironment {

  public function __construct($root, CacheBackendInterface $cache, $twig_extension_hash, StateInterface $state, \Twig_LoaderInterface $loader = NULL, $options = []) {
    parent::__construct($root, $cache, $twig_extension_hash, $state, $loader, $options);
    // Set our own lexer to the Twig environment.
    $this->setLexer(new Lexer($this));
  }

}

and drupal does not let me replace the Twig Environment and comes with the following complaint:

<em class="placeholder">LogicException</em>: Unable to register extension &quot;drupal_core&quot; as extensions have already been initialized. in <em class="placeholder">Twig_Environment-&gt;addExtension()</em> (line <em class="placeholder">941</em> of <em class="placeholder">/var/www/konquer.technology/vendor/twig/twig/lib/Twig/Environment.php</em>). <pre class="backtrace">call_user_func_array(Array, Array) (Line: 322)

Is there a way to do this? I can't find any documentation on this topic. Or maybe another way to achieve my goal of indented twig output

  • Could you rephrase your question title to something more specific? Maybe: Unable to register Twig extension. Does that apply? And can you maybe make it an actual question? – leymannx Aug 16 '18 at 16:53
  • You don't need to override core twig to load a custom extension. There's lots of documentation on how to do it but this blog might help. tothenew.com/blog/overview-of-twig-extentions-in-drupal-8 – Alfred Armstrong Aug 20 '18 at 14:39
  • @AlfredArmstrong I need to load a twig lexer not an extension – dasj19 Aug 21 '18 at 15:29
  • In that case I imagine you could create a custom service that depends on core twig and inject the lexer when your service is instantiated. To ensure it is instantiated you probably need to have an event subscriber to invoke it ahead of any rendering. Sorry for the vagueness! – Alfred Armstrong Aug 21 '18 at 16:43
0

You're using an undefined twig.lexer tag there.

It looks as though you want that tag to work as a service_collector like twig.extension, but calling setLexer instead of addExtension.

For that, you probably need to add a line to your twig service definition, below the other tag:

- { name: service_collector, tag: 'twig.lexer', call: setLexer }

(Note: I don't know if this is actually good architecture. The service_collector tag is normally used when there can be multiple extensions, and multiple lexers would just replace each other. But it should work.)

| improve this answer | |

Not the answer you're looking for? Browse other questions tagged or ask your own question.