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

dg / texy / 29654833201

18 Jul 2026 04:09PM UTC coverage: 93.039% (+0.04%) from 93.004%
29654833201

push

github

dg
Restored test coverage dropped during the AST migration

The migration removed five test files coupled to v3 contracts (block,
content-model, formatter-indent, html, latte) with the intent to bring
them back once the output layer settled. Restored here, ported to the
AST-era API; latte and most of content-model pass unchanged.

Expected outputs were re-derived and every difference against the v3
files reviewed case by case. The differences fall into the intentional
categories: list items merge continuation lines, whitespace-sensitive
typography follows visible length instead of protection-mark key length,
rejected tags are escaped consistently (both halves of a pair) with
typography applied to the escaped text, paragraphs of escaped markup are
<p>-wrapped, and <p> inside <form> is preserved. Inner texysource
fragments are parsed without the transform phase, so their passthrough
tags stay live - a documented wrinkle.

content-model.phpt again guards the WellFormer content model, nesting
prohibitions, transparent and unknown elements and auto-closing in the
public suite, independently of the private regression corpus.

3248 of 3491 relevant lines covered (93.04%)

0.93 hits per line

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

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

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

8
namespace Texy\Modules;
9

10
use Texy;
11
use Texy\Modifier;
12
use Texy\Nodes\ContentNode;
13
use Texy\Nodes\FigureNode;
14
use Texy\Nodes\ImageNode;
15
use Texy\Nodes\LinkNode;
16
use Texy\ParseContext;
17
use Texy\Patterns;
18
use Texy\Position;
19
use Texy\Syntax;
20
use function strlen;
21

22

23
/**
24
 * Processes images with captions.
25
 */
26
final class FigureModule extends Texy\Module
27
{
28
        /** @deprecated legacy property mapping */
29
        private const LegacyProperties = [
30
                'tagName' => 'figureTagName',
31
                'class' => 'figureClass',
32
                'leftClass' => 'figureLeftClass',
33
                'rightClass' => 'figureRightClass',
34
        ];
35

36

37
        public function __construct(
1✔
38
                private Texy\Texy $texy,
39
        ) {
40
        }
1✔
41

42

43
        public function beforeParse(string &$text): void
1✔
44
        {
45
                $this->texy->registerBlockPattern(
1✔
46
                        $this->parse(...),
1✔
47
                        '~^
48
                                (?>
49
                                        \[\*\ *+                      # opening bracket with asterisk
50
                                        ([^\n]{1,1000})               # URLs (1)
51
                                        ' . Patterns::MODIFIER . '?   # modifier (2)
1✔
52
                                        \ *+
53
                                        ( \* | (?<! < ) > | < )       # alignment (3)
54
                                ]
55
                                )
56
                                (?:
57
                                        :(' . Patterns::LINK_URL . ' | : ) # link or colon (4)
1✔
58
                                )??
59
                                (?:
60
                                        \ ++ \*\*\* \ ++              # separator
61
                                        (.{0,2000})                   # caption (5)
62
                                )?
63
                                ' . Patterns::MODIFIER_H . '?     # modifier (6)
1✔
64
                        $~mU',
65
                        Syntax::Figure,
1✔
66
                );
67
        }
1✔
68

69

70
        /**
71
         * Parses [*image*]:link *** caption .(title)[class]{style}>.
72
         * @param  array<?string>  $matches
73
         * @param  array<?int>  $offsets
74
         */
75
        public function parse(ParseContext $context, array $matches, array $offsets): ?FigureNode
1✔
76
        {
77
                /** @var array{string, string, ?string, string, ?string, ?string, ?string} $matches */
78
                [, $mURLs, $mImgMod, $mAlign, $mLink, $mContent, $mMod] = $matches;
1✔
79

80
                $texy = $this->texy;
1✔
81

82
                // Parse image content
83
                $parsed = $texy->imageModule->parseImageContent($mURLs);
1✔
84
                $modifier = Modifier::parse($mImgMod . $mAlign);
1✔
85

86
                if ($parsed['url'] === null) {
1✔
87
                        return null;
×
88
                }
89

90
                // Create ImageNode; its source span is "[* ... <alignment>]"
91
                $imageEnd = $offsets[3] + strlen($mAlign) + 1; // 1 = "]"
1✔
92
                $imageNode = new ImageNode(
1✔
93
                        $parsed['url'],
1✔
94
                        $parsed['width'],
1✔
95
                        $parsed['height'],
1✔
96
                        $modifier,
97
                        new Position($offsets[0], $imageEnd - $offsets[0]),
1✔
98
                );
99

100
                // If figure has link, wrap image in LinkNode
101
                $image = $imageNode;
1✔
102
                if ($mLink) {
1✔
103
                        if ($mLink === ':') {
1✔
104
                                // Link to image itself - use imageModule.root
105
                                $linkUrl = $parsed['linkedUrl'] ?? $parsed['url'];
1✔
106
                                $isImageLink = true;
1✔
107
                        } else {
108
                                // Direct URL or reference like [ref] or [*img*]
109
                                $linkUrl = $mLink;
1✔
110
                                // Check if it's an image reference [*...*] → use imageModule.root
111
                                $len = strlen($mLink);
1✔
112
                                $isImageLink = $len > 4 && $mLink[0] === '[' && $mLink[1] === '*'
1✔
113
                                        && $mLink[$len - 1] === ']' && $mLink[$len - 2] === '*';
1✔
114
                        }
115

116
                        $image = new LinkNode(
1✔
117
                                url: $linkUrl,
1✔
118
                                content: new ContentNode([$imageNode]),
1✔
119
                                position: new Position($offsets[0], $offsets[4] + strlen($mLink) - $offsets[0]),
1✔
120
                                isImageLink: $isImageLink,
121
                        );
122
                }
123

124
                // Parse caption as inline content
125
                $caption = null;
1✔
126
                $mContent = trim($mContent ?? '');
1✔
127
                if ($mContent !== '') {
1✔
128
                        $captionOffset = $offsets[5] ?? $offsets[0];
1✔
129
                        $caption = $context->parseInline($mContent, $captionOffset);
1✔
130
                }
131

132
                return new FigureNode(
1✔
133
                        $image,
1✔
134
                        $caption,
135
                        Modifier::parse($mMod),
1✔
136
                        new Position($offsets[0], strlen($matches[0])),
1✔
137
                );
138
        }
139

140

141
        /**
142
         * @deprecated use $texy->htmlGenerator->figureTagName etc. instead
143
         */
144
        public function __get(string $name): mixed
145
        {
146
                if (isset(self::LegacyProperties[$name])) {
×
147
                        $prop = self::LegacyProperties[$name];
×
148
                        trigger_error("Property \$texy->figureModule->$name is deprecated, use \$texy->htmlGenerator->$prop instead.", E_USER_DEPRECATED);
×
149
                        return $this->texy->htmlGenerator->$prop;
×
150
                }
151
                throw new \LogicException("Cannot read an undeclared property \$texy->figureModule->$name.");
×
152
        }
153

154

155
        /**
156
         * @deprecated use $texy->htmlGenerator->figureTagName etc. instead
157
         */
158
        public function __set(string $name, mixed $value): void
159
        {
160
                if (isset(self::LegacyProperties[$name])) {
×
161
                        $prop = self::LegacyProperties[$name];
×
162
                        trigger_error("Property \$texy->figureModule->$name is deprecated, use \$texy->htmlGenerator->$prop instead.", E_USER_DEPRECATED);
×
163
                        $this->texy->htmlGenerator->$prop = $value;
×
164
                        return;
×
165
                }
166
                throw new \LogicException("Cannot write to an undeclared property \$texy->figureModule->$name.");
×
167
        }
168
}
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