Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
LilypondErrorMessageBeautifier
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
3 / 3
5
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 beautifyMessage
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
 formatErrorMatchLine
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/*
3  Score, a MediaWiki extension for rendering musical scores with LilyPond.
4  Copyright © 2011 Alexander Klauer
5
6  This program is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15
16  You should have received a copy of the GNU General Public License
17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19*/
20
21namespace MediaWiki\Extension\Score;
22
23/**
24 * Convert the std error output of lilypond into a concise html form.
25 */
26class LilypondErrorMessageBeautifier {
27    /**
28     * Lilyponds error reporting line regex, only the line with the error line, column and message.
29     */
30    private const string LILYPOND_ERR_REGEX = '/\.ly:(?<line>\d+):(?<column>\d+): error: (?<message>.+)$/m';
31
32    private const string BEAUTIFIED_ERR_FORMAT = "line %d - column %d:\n%s";
33    private const string BEAUTIFIED_ERR_SEPARATOR = "\n--------\n";
34
35    /**
36     * @param int $scoreFirstLineOffset
37     *   The line number where user's score input is inserted, within the final
38     *   lilypond file that is passed to lilypond executable.
39     *
40     *   The first line is assumed to start at the first column (no column offset).
41     */
42    public function __construct(
43        private readonly int $scoreFirstLineOffset = 0,
44    ) {
45    }
46
47    /**
48     * Beautifies lilypond executable error messages by:
49     * - adjusting line numbers fit the user's input
50     * - stripping out all echoed erroneous code
51     * - stripping out unnecessary keywords
52     */
53    public function beautifyMessage( string $message ): string {
54        if ( !preg_match_all(
55            self::LILYPOND_ERR_REGEX,
56            $message,
57            $errorMatches,
58            PREG_SET_ORDER
59        ) ) {
60            return '';
61        }
62
63        $beautifiedMessages = [];
64
65        foreach ( $errorMatches as $errorMatch ) {
66            $beautifiedMessages[] = $this->formatErrorMatchLine( $errorMatch );
67        }
68
69        return implode( self::BEAUTIFIED_ERR_SEPARATOR, $beautifiedMessages );
70    }
71
72    private function formatErrorMatchLine( array $errorMatch ): string {
73        return sprintf(
74            self::BEAUTIFIED_ERR_FORMAT,
75            intval( $errorMatch[ 'line' ] ) - $this->scoreFirstLineOffset,
76            intval( $errorMatch[ 'column' ] ),
77            $errorMatch[ 'message' ]
78        );
79    }
80}