WSL/SLF GitLab Repository

SyntaxHighlighter.cc 7.92 KB
Newer Older
Mathias Bavay's avatar
Mathias Bavay committed
1
//SPDX-License-Identifier: GPL-3.0-or-later
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*****************************************************************************/
/*  Copyright 2020 WSL Institute for Snow and Avalanche Research  SLF-DAVOS  */
/*****************************************************************************/
/* This file is part of INIshell.
   INIshell is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   INIshell is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with INIshell.  If not, see <http://www.gnu.org/licenses/>.
*/

20
#include <src/main/SyntaxHighlighter.h>
21

22
23
24
25
#include <src/main/colors.h>
#include <src/main/constants.h>
#include <src/main/inishell.h>
#include <src/panels/Atomic.h>
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

/**
 * @class INISyntaxHighlighter
 * @brief Default constructor for a syntax highlighter used in the INI preview.
 * @param[in] textdoc Text document to handle syntax highlighting for.
 */
INISyntaxHighlighter::INISyntaxHighlighter(QTextDocument *textdoc) : QSyntaxHighlighter(textdoc)
{
	HighlightingRule rule;

	/*
	 * The SyntaxHighlighter parses a document for INI file syntax and checks the sections
	 * and keys against the curently loaded XML. Sections and keys that are not present
	 * in the XML are highlighted differently.
	 */

	/* unknown sections */
	QTextCharFormat format_section;
	format_section.setForeground(colors::getQColor("syntax_unknown_section"));
	format_section.setFontWeight(QFont::Bold);
	rule.pattern = QRegularExpression(R"(.*\)" + Cst::section_open + R"(.*\)" + Cst::section_close + R"(.*)");
	rule.format = format_section;
	rules_.append(rule);

	/* unknown keys */
	QTextCharFormat format_unknown_key;
	format_unknown_key.setForeground(colors::getQColor("syntax_unknown_key"));
	rule.pattern = QRegularExpression(R"(^\s*[\w|:|*]+(?=\s*=))"); //TODO: only :: but not :
	//TODO: collect and unify all regex handling (e. g. use the same here as in INIParser)
	rule.format = format_unknown_key;
	rules_.append(rule);

	/* INI values */
	QTextCharFormat format_value;
	format_value.setForeground(colors::getQColor("syntax_value"));
	rule.pattern = QRegularExpression(R"((?<=\=).*)");
	rule.format = format_value;
	rules_.append(rule);

	/* populate highlighter with known sections and keys */
	QTextCharFormat format_known_key;
	format_known_key.setForeground(colors::getQColor("syntax_known_key"));
	QTextCharFormat format_known_section;
	format_known_section.setForeground(colors::getQColor("syntax_known_section"));
	format_known_section.setFontWeight(QFont::Bold);

	QStringList added_sections_; //to avoid duplicated rules
	const QList<Atomic *> panel_list( getMainWindow()->findChildren<Atomic *>() );
	for (auto &panel : panel_list) {
		if (panel->property("no_ini").toBool()) //e. g. Groups / frames
			continue;
		QString section, key;
		(void) panel->getIniValue(section, key);
		key.replace("*", "\\*");
		rule.pattern = QRegularExpression("\\" + Cst::section_open + section + "\\" +
			Cst::section_close, QRegularExpression::CaseInsensitiveOption); //TODO: escape only if needed for the set char
		rule.format = format_known_section;
83
		if (added_sections_.indexOf(QRegularExpression(section, QRegularExpression::CaseInsensitiveOption)) == -1) {
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
			rules_.append(rule);
			added_sections_.append(section);
		}
		rule.pattern = QRegularExpression(R"(^\s*)" + key + R"((=|\s))", QRegularExpression::CaseInsensitiveOption);
		rule.format = format_known_key;
		rules_.append(rule);
	}
	//for dynamic sections we do not need to iterate through the GUI because we have them in a list:
	QStringList *dynamic_sections( getMainWindow()->getControlPanel()->getSectionTab()->getDynamicSectionList() );
	for (auto &dyn_sec : *dynamic_sections) { //add all dynamic sections with numeric postfixes
		rule.pattern = QRegularExpression("\\" + Cst::section_open + dyn_sec + "[0-9]+" + "\\" +
			Cst::section_close, QRegularExpression::CaseInsensitiveOption);
		rule.format = format_known_section;
		rules_.append(rule);
	}

	/* comments */
	QTextCharFormat format_block_comment;
	format_block_comment.setForeground(colors::getQColor("syntax_comment"));
	rule.pattern = QRegularExpression(R"(^\s*[#;].*)");
	rule.format = format_block_comment;
	rules_.append(rule);
	rule.pattern = QRegularExpression(R"(([#;](?!$).*))");
	rules_.append(rule);

	/* coordinates */
	QTextCharFormat format_coordinate;
	format_coordinate.setForeground(colors::getQColor("coordinate"));
	rule.pattern = QRegularExpression(R"((latlon|xy)\s*\(([-\d\.]+)(?:,)\s*([-\d\.]+)((?:,)\s*([-\d\.]+))?\))");
	rule.format = format_coordinate;
	rules_.append(rule);

	/* equals sign */
	QTextCharFormat format_equal;
	format_equal.setForeground(Qt::black);
	rule.pattern = QRegularExpression(R"(=)");
	rule.format = format_equal;
	rules_.append(rule);
}

/**
 * @brief Send a chunk of text through the syntax highlighter.
 * @param[in] text The text to syntax-highlight.
 */
void INISyntaxHighlighter::highlightBlock(const QString &text)
{
	for (const HighlightingRule &rule : qAsConst(rules_)) {
		QRegularExpressionMatchIterator mit( rule.pattern.globalMatch(text) );
		while (mit.hasNext()) { //run trough regex matches and set the stored formats
Mathias Bavay's avatar
Mathias Bavay committed
133
134
			QRegularExpressionMatch match( mit.next() );
			setFormat(static_cast<int>(match.capturedStart()), static_cast<int>(match.capturedLength()), rule.format);
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
		}
	}
}

/**
 * @class XMLSyntaxHighlighter
 * @brief Default constructor for a syntax highlighter used in Copytexts.
 * @param[in] textdoc Text document to handle syntax highlighting for.
 */
XMLSyntaxHighlighter::XMLSyntaxHighlighter(QTextDocument *textdoc) : QSyntaxHighlighter(textdoc)
{
	HighlightingRule rule;

	/* XML syntax highlighting */
	QTextCharFormat format_element; //XML elements
	format_element.setForeground(colors::getQColor("syntax_xml_element"));
	format_element.setFontWeight(QFont::Bold);
	rule.pattern = QRegularExpression("<[?\\s]*[/]?[\\s]*([^\\n][^>]*)(?=[\\s/>])");
	rule.format = format_element;
	rules_.append(rule);

	QTextCharFormat format_attribute; //XML attributes
	format_attribute.setForeground(colors::getQColor("syntax_xml_attribute"));
	format_attribute.setFontWeight(QFont::Bold);
	format_attribute.setFontItalic(true);
	rule.pattern = QRegularExpression("\\w+(?=\\=)");
	rule.format = format_attribute;
	rules_.append(rule);

	QTextCharFormat format_value; //XML values
	format_value.setForeground(colors::getQColor("syntax_xml_value"));
	rule.pattern = QRegularExpression("\"[^\\n\"]+\"(?=[?\\s/>])");
	rule.format = format_value;
	rules_.append(rule);

	QTextCharFormat format_comment; //XML comments
	format_comment.setForeground(colors::getQColor("syntax_xml_comment"));
	rule.pattern = QRegularExpression("<!--[^\\n]*-->");
	rule.format = format_comment;
	rules_.append(rule);

	QTextCharFormat format_keyword; //XML keywords
	format_keyword.setForeground(colors::getQColor("syntax_xml_keyword"));
	format_keyword.setFontWeight(QFont::Bold);
	rule.format = format_keyword;
	QStringList keyword_patterns;
	keyword_patterns << "<\\?" << "/>" << ">" << "<" << "</" << "\\?>";
	for (auto &pat : keyword_patterns) {
		rule.pattern = QRegularExpression( pat );
		rules_.append(rule);
	}
}

/**
 * @brief Send a chunk of text through the XML syntax highlighter.
 * @param[in] text The text to syntax-highlight.
 */
void XMLSyntaxHighlighter::highlightBlock(const QString &text)
{
	for (const HighlightingRule &rule : qAsConst(rules_)) {
		QRegularExpressionMatchIterator mit( rule.pattern.globalMatch(text) );
		while (mit.hasNext()) { //run trough regex matches and set the stored formats
Mathias Bavay's avatar
Mathias Bavay committed
197
198
			QRegularExpressionMatch match( mit.next() );
			setFormat(static_cast<int>(match.capturedStart()), static_cast<int>(match.capturedLength()), rule.format);
199
200
201
		}
	}
}