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 LILYPOND_ERR_REGEX = '/\.ly:(?<line>\d+):(?<column>\d+): error: (?<message>.+)$/m';
31
32    private const BEAUTIFIED_ERR_FORMAT = "line %d - column %d:\n%s";
33    private const 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     * @param string $message
54     *
55     * @return string
56     */
57    public function beautifyMessage( $message ) {
58        if ( !preg_match_all(
59            self::LILYPOND_ERR_REGEX,
60            $message,
61            $errorMatches,
62            PREG_SET_ORDER
63        ) ) {
64            return '';
65        }
66
67        $beautifiedMessages = [];
68
69        foreach ( $errorMatches as $errorMatch ) {
70            $beautifiedMessages[] = $this->formatErrorMatchLine( $errorMatch );
71        }
72
73        return implode( self::BEAUTIFIED_ERR_SEPARATOR, $beautifiedMessages );
74    }
75
76    /**
77     * @param array $errorMatch
78     * @return string
79     */
80    private function formatErrorMatchLine( array $errorMatch ) {
81        return sprintf(
82            self::BEAUTIFIED_ERR_FORMAT,
83            intval( $errorMatch[ 'line' ] ) - $this->scoreFirstLineOffset,
84            intval( $errorMatch[ 'column' ] ),
85            $errorMatch[ 'message' ]
86        );
87    }
88}