2 * \file LaTeXHighlighter.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
13 #include "LaTeXHighlighter.h"
14 #include "qt_helpers.h"
17 #include <QTextDocument>
23 LaTeXHighlighter::LaTeXHighlighter(QTextDocument * parent, bool at_letter, bool keyval)
24 : QSyntaxHighlighter(parent), at_letter_(at_letter), keyval_(keyval)
26 auto blend = [](QColor color1, QColor color2) {
27 int r = 0.5 * (color1.red() + color2.red());
28 int g = 0.5 * (color1.green() + color2.green());
29 int b = 0.5 * (color1.blue() + color2.blue());
30 return QColor(r, g, b);
32 QPalette palette = QPalette();
33 QColor text_color = palette.color(QPalette::Active, QPalette::Text);
34 keywordFormat.setForeground(blend(Qt::blue, text_color));
35 keywordFormat.setFontWeight(QFont::Bold);
36 commentFormat.setForeground(palette.color(QPalette::Disabled,
38 mathFormat.setForeground(blend(Qt::red, text_color));
39 warningFormat.setForeground(Qt::red);
40 warningFormat.setFontWeight(QFont::Bold);
41 keyFormat.setForeground(blend(Qt::darkRed, text_color));
42 keyFormat.setFontWeight(QFont::Bold);
43 valFormat.setForeground(blend(Qt::darkGreen, text_color));
47 void LaTeXHighlighter::highlightBlock(QString const & text)
49 #if QT_VERSION < 0x060000
52 // Highlight key-val options. Used in some option widgets.
53 static const QRegExp exprKeyvalkey("[^=,]+");
54 static const QRegExp exprKeyvalval("[^,]+");
55 int kvindex = exprKeyvalkey.indexIn(text);
56 while (kvindex >= 0) {
57 int length = exprKeyvalkey.matchedLength();
58 setFormat(kvindex, length, keyFormat);
59 int kvvindex = exprKeyvalval.indexIn(text, kvindex + length);
61 length += exprKeyvalval.matchedLength();
62 setFormat(kvvindex, length, valFormat);
64 kvindex = exprKeyvalkey.indexIn(text, kvindex + length);
68 static const QRegExp exprMath("\\$[^\\$]*\\$");
69 int index = exprMath.indexIn(text);
71 int length = exprMath.matchedLength();
72 setFormat(index, length, mathFormat);
73 index = exprMath.indexIn(text, index + length);
76 static const QRegExp exprStartDispMath("(\\\\\\[|"
77 "\\\\begin\\{equation\\**\\}|"
78 "\\\\begin\\{eqnarray\\**\\}|"
79 "\\\\begin\\{align(ed|at)*\\**\\}|"
80 "\\\\begin\\{flalign\\**\\}|"
81 "\\\\begin\\{gather\\**\\}|"
82 "\\\\begin\\{multline\\**\\}|"
83 "\\\\begin\\{array\\**\\}|"
84 "\\\\begin\\{cases\\**\\}"
86 static const QRegExp exprEndDispMath("(\\\\\\]|"
87 "\\\\end\\{equation\\**\\}|"
88 "\\\\end\\{eqnarray\\**\\}|"
89 "\\\\end\\{align(ed|at)*\\**\\}|"
90 "\\\\end\\{flalign\\**\\}|"
91 "\\\\end\\{gather\\**\\}|"
92 "\\\\end\\{multline\\**\\}|"
93 "\\\\end\\{array\\**\\}|"
94 "\\\\end\\{cases\\**\\}"
97 // if previous block was in 'disp math'
98 // start search from 0 (for end disp math)
99 // otherwise, start search from 'begin disp math'
100 if (previousBlockState() != 1)
101 startIndex = exprStartDispMath.indexIn(text);
102 while (startIndex >= 0) {
103 int endIndex = exprEndDispMath.indexIn(text, startIndex);
105 if (endIndex == -1) {
106 setCurrentBlockState(1);
107 length = text.length() - startIndex;
109 length = endIndex - startIndex + exprEndDispMath.matchedLength();
111 setFormat(startIndex, length, mathFormat);
112 startIndex = exprStartDispMath.indexIn(text, startIndex + length);
115 static const QRegExp exprKeywordAtOther("\\\\[A-Za-z]+");
117 static const QRegExp exprKeywordAtLetter("\\\\[A-Za-z@]+");
118 QRegExp const & exprKeyword = at_letter_ ? exprKeywordAtLetter
119 : exprKeywordAtOther;
120 index = exprKeyword.indexIn(text);
122 int length = exprKeyword.matchedLength();
123 setFormat(index, length, keywordFormat);
124 index = exprKeyword.indexIn(text, index + length);
127 QRegExp exprWhiteSpace("\\s");
128 index = exprWhiteSpace.indexIn(text);
130 int length = exprWhiteSpace.matchedLength();
131 setFormat(index, length, commentFormat);
132 index = exprWhiteSpace.indexIn(text, index + length);
135 // Treat a line as a comment starting at a percent sign
136 // * that is the first character in a line
137 // * that is preceded by
138 // ** an even number of backslashes
139 // ** any character other than a backslash
140 QRegExp exprComment("(?:^|[^\\\\])(?:\\\\\\\\)*(%).*$");
141 exprComment.indexIn(text);
142 index = exprComment.pos(1);
144 int const length = exprComment.matchedLength()
145 - (index - exprComment.pos(0));
146 setFormat(index, length, commentFormat);
147 exprComment.indexIn(text, index + length);
148 index = exprComment.pos(1);
150 // <LyX Warning: ...>
151 QString lyxwarn = qt_("LyX Warning: ");
152 QRegExp exprWarning("<" + lyxwarn + "[^<]*>");
153 index = exprWarning.indexIn(text);
155 int length = exprWarning.matchedLength();
156 setFormat(index, length, warningFormat);
157 index = exprWarning.indexIn(text, index + length);
162 // Highlight key-val options. Used in some option widgets.
163 static const QRegularExpression exprKeyvalkey("[^=,]+");
164 static const QRegularExpression exprKeyvalval("[^,]+");
165 QRegularExpressionMatch matchkey = exprKeyvalkey.match(text);
166 int kvindex = matchkey.capturedStart(0);
167 while (kvindex >= 0) {
168 int length = matchkey.capturedLength(0);
169 setFormat(kvindex, length, keyFormat);
170 QRegularExpressionMatch matchval =
171 exprKeyvalval.match(text, kvindex + length);
172 int kvvindex = matchval.capturedStart(0);
174 length += matchval.capturedLength(0);
175 setFormat(kvvindex, length, valFormat);
177 matchkey = exprKeyvalkey.match(text, kvindex + length);
178 kvindex = matchkey.capturedStart(0);
182 static const QRegularExpression exprMath("\\$[^\\$]*\\$");
183 QRegularExpressionMatch match = exprMath.match(text);
184 int index = match.capturedStart(0);
186 int length = match.capturedLength(0);
187 setFormat(index, length, mathFormat);
188 match = exprMath.match(text, index + length);
189 index = match.capturedStart(0);
192 static const QRegularExpression exprStartDispMath("(\\\\\\[|"
193 "\\\\begin\\{equation\\**\\}|"
194 "\\\\begin\\{eqnarray\\**\\}|"
195 "\\\\begin\\{align(ed|at)*\\**\\}|"
196 "\\\\begin\\{flalign\\**\\}|"
197 "\\\\begin\\{gather\\**\\}|"
198 "\\\\begin\\{multline\\**\\}|"
199 "\\\\begin\\{array\\**\\}|"
200 "\\\\begin\\{cases\\**\\}"
202 static const QRegularExpression exprEndDispMath("(\\\\\\]|"
203 "\\\\end\\{equation\\**\\}|"
204 "\\\\end\\{eqnarray\\**\\}|"
205 "\\\\end\\{align(ed|at)*\\**\\}|"
206 "\\\\end\\{flalign\\**\\}|"
207 "\\\\end\\{gather\\**\\}|"
208 "\\\\end\\{multline\\**\\}|"
209 "\\\\end\\{array\\**\\}|"
210 "\\\\end\\{cases\\**\\}"
213 // if previous block was in 'disp math'
214 // start search from 0 (for end disp math)
215 // otherwise, start search from 'begin disp math'
216 if (previousBlockState() != 1) {
217 match = exprStartDispMath.match(text);
218 startIndex = match.capturedStart(0);
220 while (startIndex >= 0) {
221 match = exprEndDispMath.match(text, startIndex);
222 int endIndex = match.capturedStart(0);
224 if (endIndex == -1) {
225 setCurrentBlockState(1);
226 length = text.length() - startIndex;
228 length = endIndex - startIndex + match.capturedLength(0);
230 setFormat(startIndex, length, mathFormat);
231 match = exprStartDispMath.match(text, startIndex + length);
232 startIndex = match.capturedStart(0);
235 static const QRegularExpression exprKeywordAtOther("\\\\[A-Za-z]+");
237 static const QRegularExpression exprKeywordAtLetter("\\\\[A-Za-z@]+");
238 QRegularExpression const & exprKeyword = at_letter_
239 ? exprKeywordAtLetter : exprKeywordAtOther;
240 match = exprKeyword.match(text);
241 index = match.capturedStart(0);
243 int length = match.capturedLength(0);
244 setFormat(index, length, keywordFormat);
245 match = exprKeyword.match(text, index + length);
246 index = match.capturedStart(0);
249 QRegularExpression exprWhiteSpace("\\s");
250 match = exprWhiteSpace.match(text);
251 index = match.capturedStart(0);
253 int length = match.capturedLength(0);
254 setFormat(index, length, commentFormat);
255 match = exprWhiteSpace.match(text, index + length);
256 index = match.capturedStart(0);
259 // Treat a line as a comment starting at a percent sign
260 // * that is the first character in a line
261 // * that is preceded by
262 // ** an even number of backslashes
263 // ** any character other than a backslash
264 QRegularExpression exprComment("(?:^|[^\\\\])(?:\\\\\\\\)*(%).*$");
265 match = exprComment.match(text);
266 index = match.capturedStart(1);
268 int const length = match.capturedLength(0)
269 - (index - match.capturedStart(0));
270 setFormat(index, length, commentFormat);
271 match = exprComment.match(text, index + length);
272 index = match.capturedStart(1);
274 // <LyX Warning: ...>
275 QString lyxwarn = qt_("LyX Warning: ");
276 QRegularExpression exprWarning("<" + lyxwarn + "[^<]*>");
277 match = exprWarning.match(text);
278 index = match.capturedStart(0);
280 int length = match.capturedLength(0);
281 setFormat(index, length, warningFormat);
282 match = exprWarning.match(text, index + length);
283 index = match.capturedStart(0);
288 } // namespace frontend