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

dg / texy / 22262497275

21 Feb 2026 07:01PM UTC coverage: 93.057% (+0.7%) from 92.367%
22262497275

push

github

dg
added CLAUDE.md

2426 of 2607 relevant lines covered (93.06%)

0.93 hits per line

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

93.51
/src/Texy/LineParser.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;
9

10
use function array_keys, strlen, substr_replace;
11

12

13
/**
14
 * Parses inline structures (links, images, formatting, etc.).
15
 */
16
class LineParser extends Parser
17
{
18
        /** @var array<string, array{handler: \Closure(LineParser, array<string>, string): (HtmlElement|string|null), pattern: string, again: ?string}> */
19
        public array $patterns;
20
        public bool $again;
21

22

23
        public function __construct(Texy $texy, HtmlElement $element)
1✔
24
        {
25
                $this->texy = $texy;
1✔
26
                $this->element = $element;
1✔
27
                $this->patterns = $texy->getLinePatterns();
1✔
28
        }
1✔
29

30

31
        /**
32
         * Parses text and appends results to parent element.
33
         */
34
        public function parse(string $text): void
1✔
35
        {
36
                if (!$this->patterns) { // nothing to do
1✔
37
                        $this->element->insert(null, $text);
×
38
                        return;
×
39
                }
40

41
                $offset = 0;
1✔
42
                $names = array_keys($this->patterns);
1✔
43
                /** @var array<string, array<string>> $matches */
44
                $matches = $offsets = [];
1✔
45
                foreach ($names as $name) {
1✔
46
                        $offsets[$name] = -1;
1✔
47
                }
48

49
                do {
50
                        $first = $this->match($text, $offset, $names, $offsets, $matches);
1✔
51
                        if ($first === null) {
1✔
52
                                break;
1✔
53
                        }
54

55
                        $px = $this->patterns[$first];
1✔
56
                        $offset = $start = $offsets[$first];
1✔
57

58
                        $this->again = false;
1✔
59
                        $res = $px['handler']($this, $matches[$first], $first);
1✔
60

61
                        if ($res instanceof HtmlElement) {
1✔
62
                                $res = $res->toString($this->texy);
1✔
63
                        } elseif ($res === null) {
1✔
64
                                $offsets[$first] = -2;
1✔
65
                                continue;
1✔
66
                        }
67

68
                        $len = strlen($matches[$first][0]);
1✔
69
                        $text = substr_replace(
1✔
70
                                $text,
1✔
71
                                (string) $res,
1✔
72
                                $start,
1✔
73
                                $len,
1✔
74
                        );
75

76
                        $delta = strlen($res) - $len;
1✔
77
                        foreach ($names as $name) {
1✔
78
                                if ($offsets[$name] < $start + $len) {
1✔
79
                                        $offsets[$name] = -1;
1✔
80
                                } else {
81
                                        $offsets[$name] += $delta;
1✔
82
                                }
83
                        }
84

85
                        if ($this->again) {
1✔
86
                                $offsets[$first] = -2;
1✔
87
                        } else {
88
                                $offsets[$first] = -1;
1✔
89
                                $offset += strlen($res);
1✔
90
                        }
91
                } while (1);
1✔
92

93
                $this->element->insert(null, $text);
1✔
94
        }
1✔
95

96

97
        /**
98
         * @param  array<int, string>  $names
99
         * @param  array<string, int>  $offsets
100
         * @param  array<string, array<string>>  $matches
101
         */
102
        private function match(string $text, int $offset, array &$names, array &$offsets, array &$matches): ?string
1✔
103
        {
104
                $first = null;
1✔
105
                $minOffset = strlen($text);
1✔
106

107
                foreach ($names as $index => $name) {
1✔
108
                        if ($offsets[$name] < $offset) {
1✔
109
                                $delta = 0;
1✔
110
                                if ($offsets[$name] === -2) {
1✔
111
                                        do {
112
                                                $delta++;
1✔
113
                                        } while (
114
                                                isset($text[$offset + $delta])
1✔
115
                                                && $text[$offset + $delta] >= "\x80"
1✔
116
                                                && $text[$offset + $delta] < "\xC0"
1✔
117
                                        );
118
                                }
119

120
                                if ($offset + $delta > strlen($text)) {
1✔
121
                                        unset($names[$index]);
×
122
                                        continue;
×
123

124
                                } elseif ($m = Regexp::match(
1✔
125
                                        $text,
1✔
126
                                        $this->patterns[$name]['pattern'],
1✔
127
                                        Regexp::OFFSET_CAPTURE,
1✔
128
                                        $offset + $delta,
1✔
129
                                )) {
130
                                        /** @var array<int|string, array{string, int}> $m */
131
                                        if (!strlen($m[0][0])) {
1✔
132
                                                continue;
×
133
                                        }
134

135
                                        $offsets[$name] = $m[0][1];
1✔
136
                                        $matches[$name] = [];
1✔
137
                                        foreach ($m as $keyx => $value) {
1✔
138
                                                $matches[$name][$keyx] = $value[0];
1✔
139
                                        }
140
                                } else {
141
                                        // try next time?
142
                                        if (
143
                                                !$this->patterns[$name]['again']
1✔
144
                                                || !Regexp::match($text, $this->patterns[$name]['again'], 0, $offset + $delta)
1✔
145
                                        ) {
146
                                                unset($names[$index]);
1✔
147
                                        }
148

149
                                        continue;
1✔
150
                                }
151
                        }
152

153
                        if ($offsets[$name] < $minOffset) {
1✔
154
                                $minOffset = $offsets[$name];
1✔
155
                                $first = $name;
1✔
156
                        }
157
                }
158

159
                return $first;
1✔
160
        }
161
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc