injectLabelToCodeLine()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\Markdown\Processing;
6
7
use Hyde\Facades\Config;
8
use Hyde\Markdown\Contracts\MarkdownPostProcessorContract;
9
use Hyde\Markdown\Contracts\MarkdownPreProcessorContract;
10
use Illuminate\Support\Facades\View;
11
use Illuminate\Support\HtmlString;
12
13
use function array_merge;
14
use function preg_replace;
15
use function str_contains;
16
use function str_ireplace;
17
use function str_starts_with;
18
use function strtolower;
19
use function str_replace;
20
use function explode;
21
use function implode;
22
use function sprintf;
23
use function trim;
24
25
/**
26
 * Resolves file path comments found in Markdown code blocks into a neat badge shown in the top right corner.
27
 *
28
 * @todo See about replacing this with a custom Codeblock Blade view that can be customized, even supporting click to copy buttons or arbitrary other features.
29
 */
30
class CodeblockFilepathProcessor implements MarkdownPreProcessorContract, MarkdownPostProcessorContract
31
{
32
    protected static string $torchlightKey = '<!-- Syntax highlighted by torchlight.dev -->';
33
34
    /** @var array<string> */
35
    protected static array $patterns = [
36
        '// filepath: ',
37
        '// filepath ',
38
        '/* filepath: ',
39
        '/* filepath ',
40
        '# filepath: ',
41
        '# filepath ',
42
        '<!-- filepath: ',
43
        '<!-- filepath ',
44
    ];
45
46
    /**
47
     * Extract lines matching the shortcode pattern and replace them with meta-blocks that will be processed later.
48
     */
49
    public static function preprocess(string $markdown): string
50
    {
51
        $lines = explode("\n", $markdown);
52
53
        foreach ($lines as $index => $line) {
54
            if (static::lineMatchesPattern($line)) {
55
                // Add the meta-block two lines before the pattern, placing it just above the code block.
56
                // This prevents the meta-block from interfering with other processes during compile.
57
                // We then replace these markers in the post-processor.
58
                $lines[$index - 2] .= sprintf(
59
                    "\n<!-- HYDE[Filepath]%s -->",
60
                    trim(str_ireplace(array_merge(static::$patterns, ['-->']), '', $line))
0 ignored issues
show
Bug introduced by
It seems like str_ireplace(array_merge...ray('-->')), '', $line) can also be of type array; however, parameter $string of trim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

60
                    trim(/** @scrutinizer ignore-type */ str_ireplace(array_merge(static::$patterns, ['-->']), '', $line))
Loading history...
61
                );
62
63
                // Remove the original comment lines
64
                unset($lines[$index]);
65
                // Only unset the next line if it's empty
66
                if (trim($lines[$index + 1]) === '') {
67
                    unset($lines[$index + 1]);
68
                }
69
            }
70
        }
71
72
        return implode("\n", $lines);
73
    }
74
75
    /**
76
     * Process the meta-blocks added by the preprocessor, injecting the filepath badge template into the code block.
77
     */
78
    public static function postprocess(string $html): string
79
    {
80
        $lines = explode("\n", $html);
81
        $highlightedByTorchlight = str_contains($html, static::$torchlightKey);
82
83
        /** @var int $index */
84
        foreach ($lines as $index => $line) {
85
            if (str_starts_with($line, '<!-- HYDE[Filepath]')) {
86
                $lines = static::processFilepathLine($lines, $index, $highlightedByTorchlight);
87
            }
88
        }
89
90
        return implode("\n", $lines);
91
    }
92
93
    protected static function processFilepathLine(array $lines, int $index, bool $highlightedByTorchlight): array
94
    {
95
        $path = static::trimHydeDirective($lines[$index]);
96
        $label = static::resolveTemplate($path, $highlightedByTorchlight);
97
        $codeBlockLine = $index + 1;
98
99
        unset($lines[$index]);
100
101
        $lines[$codeBlockLine] = static::injectLabel($label, $lines[$codeBlockLine], $highlightedByTorchlight);
102
103
        return $lines;
104
    }
105
106
    protected static function injectLabel(string $label, string $line, bool $highlightedByTorchlight): string
107
    {
108
        return $highlightedByTorchlight
109
            ? static::injectLabelToTorchlightCodeLine($label, $line)
110
            : static::injectLabelToCodeLine($label, $line);
111
    }
112
113
    protected static function lineMatchesPattern(string $line): bool
114
    {
115
        foreach (static::$patterns as $pattern) {
116
            if (str_starts_with(strtolower($line), (string) $pattern)) {
117
                return true;
118
            }
119
        }
120
121
        return false;
122
    }
123
124
    protected static function trimHydeDirective(string $line): string
125
    {
126
        return trim(str_replace('-->', '',
127
            str_replace('<!-- HYDE[Filepath]', '', $line)
128
        ));
129
    }
130
131
    protected static function resolveTemplate(string $path, bool $highlightedByTorchlight): string
132
    {
133
        return View::make('hyde::components.filepath-label', [
134
            'path' => Config::getBool('markdown.allow_html', false) ? new HtmlString($path) : $path,
135
            'highlightedByTorchlight' => $highlightedByTorchlight,
136
        ])->render();
137
    }
138
139
    protected static function injectLabelToTorchlightCodeLine(string $label, string $lines): string
140
    {
141
        return str_replace(
142
            static::$torchlightKey,
143
            static::$torchlightKey.$label,
144
            $lines
145
        );
146
    }
147
148
    protected static function injectLabelToCodeLine(string $label, string $lines): string
149
    {
150
        return preg_replace(
151
            '/<pre><code class="language-(.*?)">/',
152
            '<pre><code class="language-$1">'.$label,
153
            $lines
154
        );
155
    }
156
}
157