• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dg / texy / 39601969498

05 Oct 2026 06:36AM UTC coverage: 95.456% (-0.9%) from 96.335%
39601969498

push

github

dg
examples: Figure as Definition List lives in one directory

The demo sat in "Figure as Definition List" while its sample and image
were in "figure as definition list", and the readme linked the latter;
on a case-sensitive filesystem the demo did not find its sample.

3508 of 3675 relevant lines covered (95.46%)

0.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.67
/src/Texy/Modules/DirectiveModule.php
1
<?php declare(strict_types=1);
2

3
/**
4
 * This file is part of the Texy! (https://texy.deegee.dev)
5
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6
 */
7

8
namespace Texy\Modules;
9

10
use Texy;
11
use Texy\Nodes\DirectiveNode;
12
use Texy\Parsing\ParseContext;
13
use Texy\{Range, Syntax};
14
use function strlen;
15

16

17
/**
18
 * Processes {{macro}} script commands.
19
 */
20
final class DirectiveModule extends Texy\Module
21
{
22
        /** @var array<string, \Closure(?string, list<string>, DirectiveNode): ?Texy\Node> */
23
        private array $handlers = [];
24

25
        /** @var ?\Closure(?string, list<string>, DirectiveNode): ?Texy\Node */
26
        private ?\Closure $fallback = null;
27

28

29
        public function __construct(
1 ✔
30
                private Texy\Texy $texy,
31
        ) {
32
                $texy->addPass($this->processDirectives(...));
1 ✔
33
        }
1 ✔
34

35

36
        /** @param  callable(?string, list<string>, DirectiveNode): ?Texy\Node  $handler */
37
        public function addDirective(string $name, callable $handler): void
38
        {
39
                $this->handlers[Texy\Helpers::toLower($name)] = $handler(...);
1 ✔
40
        }
1 ✔
41

42

43
        /**
44
         * Registers a handler for directives that have no named handler.
45
         *
46
         * Without it an unhandled directive stays in the document and renders as the author
47
         * wrote it, and the consumer has no way of learning about it. The handler receives
48
         * the same arguments as a named one, so the name is read from the node.
49
         * @param  ?callable(?string, list<string>, DirectiveNode): ?Texy\Node  $handler
50
         */
51
        public function setFallback(?callable $handler): void
52
        {
53
                $this->fallback = $handler === null ? null : $handler(...);
1 ✔
54
        }
1 ✔
55

56

57
        /**
58
         * Consumes {{texy: ...}} into DocumentNode::$meta and hands the rest to registered
59
         * handlers. Runs as the first pass, so a handler may still change configuration the
60
         * later ones depend on. A handled directive is removed unless the handler returns
61
         * a replacement.
62
         */
63
        public function processDirectives(Texy\Nodes\DocumentNode $doc): void
64
        {
65
                (new Texy\NodeTraverser)->traverse($doc, function (Texy\Node $node) use ($doc): Texy\Node|int|null {
1 ✔
66
                        if (!$node instanceof DirectiveNode) {
1 ✔
67
                                return null;
1 ✔
68
                        }
69

70
                        $parsed = $node->parseContent();
1 ✔
71
                        if ($parsed['name'] === 'texy' && $parsed['args']) {
1 ✔
72
                                foreach ($parsed['args'] as $arg) {
1 ✔
73
                                        $doc->meta[$arg] = true;
1 ✔
74
                                }
75
                                return Texy\NodeTraverser::RemoveNode;
1 ✔
76
                        }
77

78
                        $handler = $this->handlers[Texy\Helpers::toLower($parsed['name'])] ?? $this->fallback;
1 ✔
79
                        return $handler === null
1 ✔
80
                                ? null
1 ✔
81
                                : $handler($parsed['value'], $parsed['args'], $node) ?? Texy\NodeTraverser::RemoveNode;
1 ✔
82
                });
1 ✔
83
        }
1 ✔
84

85

86
        public function beforeParse(): void
87
        {
88
                $this->texy->addLineSyntax(
1 ✔
89
                        Syntax::Directive,
1 ✔
90
                        '~
1 ✔
91
                                \{\{
92
                                ((?:
93
                                        [^}]++ |   # content not containing }
94
                                        }          # or single }
95
                                )+)
96
                                }}
97
                        ~Ux',
98
                        $this->parse(...),
1 ✔
99
                );
100
        }
1 ✔
101

102

103
        /**
104
         * Parses {{macro}}.
105
         * @param  array{string, string}  $matches
106
         * @param  array{int, int}  $offsets
107
         */
108
        public function parse(ParseContext $context, array $matches, array $offsets): ?DirectiveNode
109
        {
110
                return trim($matches[1]) === ''
1 ✔
111
                        ? null
×
112
                        : new DirectiveNode($matches[1], new Range($offsets[0], strlen($matches[0])));
1 ✔
113
        }
114
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc