]> git.lyx.org Git - lyx.git/blob - src/frontends/qt/LaTeXHighlighter.cpp
Extended comment and indentation for source code
[lyx.git] / src / frontends / qt / LaTeXHighlighter.cpp
1 /**
2  * \file LaTeXHighlighter.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Bo Peng
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "LaTeXHighlighter.h"
14 #include "qt_helpers.h"
15
16 #include <QString>
17 #include <QTextDocument>
18
19 namespace lyx {
20 namespace frontend {
21
22
23 LaTeXHighlighter::LaTeXHighlighter(QTextDocument * parent, bool at_letter, bool keyval)
24         : QSyntaxHighlighter(parent), at_letter_(at_letter), keyval_(keyval)
25 {
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);
31         };
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,
37                                                   QPalette::Text));
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));
44 }
45
46
47 void LaTeXHighlighter::highlightBlock(QString const & text)
48 {
49 #if QT_VERSION < 0x060000
50         // keyval
51         if (keyval_) {
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);
60                         if (kvvindex > 0) {
61                                 length += exprKeyvalval.matchedLength();
62                                 setFormat(kvvindex, length, valFormat);
63                         }
64                         kvindex = exprKeyvalkey.indexIn(text, kvindex + length);
65                 }
66         }
67         // $ $
68         static const QRegExp exprMath("\\$[^\\$]*\\$");
69         int index = exprMath.indexIn(text);
70         while (index >= 0) {
71                 int length = exprMath.matchedLength();
72                 setFormat(index, length, mathFormat);
73                 index = exprMath.indexIn(text, index + length);
74         }
75         // [ ]
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\\**\\}"
85                 ")");
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\\**\\}"
95                 ")");
96         int startIndex = 0;
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);
104                 int length;
105                 if (endIndex == -1) {
106                         setCurrentBlockState(1);
107                         length = text.length() - startIndex;
108                 } else {
109                         length = endIndex - startIndex + exprEndDispMath.matchedLength();
110                 }
111                 setFormat(startIndex, length, mathFormat);
112                 startIndex = exprStartDispMath.indexIn(text, startIndex + length);
113         }
114         // \whatever
115         static const QRegExp exprKeywordAtOther("\\\\[A-Za-z]+");
116         // \wh@tever
117         static const QRegExp exprKeywordAtLetter("\\\\[A-Za-z@]+");
118         QRegExp const & exprKeyword = at_letter_ ? exprKeywordAtLetter
119                                                  : exprKeywordAtOther;
120         index = exprKeyword.indexIn(text);
121         while (index >= 0) {
122                 int length = exprKeyword.matchedLength();
123                 setFormat(index, length, keywordFormat);
124                 index = exprKeyword.indexIn(text, index + length);
125         }
126         // White space
127         QRegExp exprWhiteSpace("\\s");
128         index = exprWhiteSpace.indexIn(text);
129         while (index >= 0) {
130                 int length = exprWhiteSpace.matchedLength();
131                 setFormat(index, length, commentFormat);
132                 index = exprWhiteSpace.indexIn(text, index + length);
133         }
134         // %comment
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);
143         while (index >= 0) {
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);
149         }
150         // <LyX Warning: ...>
151         QString lyxwarn = qt_("LyX Warning: ");
152         QRegExp exprWarning("<" + lyxwarn + "[^<]*>");
153         index = exprWarning.indexIn(text);
154         while (index >= 0) {
155                 int length = exprWarning.matchedLength();
156                 setFormat(index, length, warningFormat);
157                 index = exprWarning.indexIn(text, index + length);
158         }
159 #else
160         // keyval
161         if (keyval_) {
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);
173                         if (kvvindex > 0) {
174                                 length += matchval.capturedLength(0);
175                                 setFormat(kvvindex, length, valFormat);
176                         }
177                         matchkey = exprKeyvalkey.match(text, kvindex + length);
178                         kvindex = matchkey.capturedStart(0);
179                 }
180         }
181         // $ $
182         static const QRegularExpression exprMath("\\$[^\\$]*\\$");
183         QRegularExpressionMatch match = exprMath.match(text);
184         int index = match.capturedStart(0);
185         while (index >= 0) {
186                 int length = match.capturedLength(0);
187                 setFormat(index, length, mathFormat);
188                 match = exprMath.match(text, index + length);
189                 index = match.capturedStart(0);
190         }
191         // [ ]
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\\**\\}"
201                 ")");
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\\**\\}"
211                 ")");
212         int startIndex = 0;
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);
219         }
220         while (startIndex >= 0) {
221                 match = exprEndDispMath.match(text, startIndex);
222                 int endIndex = match.capturedStart(0);
223                 int length;
224                 if (endIndex == -1) {
225                         setCurrentBlockState(1);
226                         length = text.length() - startIndex;
227                 } else {
228                         length = endIndex - startIndex + match.capturedLength(0);
229                 }
230                 setFormat(startIndex, length, mathFormat);
231                 match = exprStartDispMath.match(text, startIndex + length);
232                 startIndex = match.capturedStart(0);
233         }
234         // \whatever
235         static const QRegularExpression exprKeywordAtOther("\\\\[A-Za-z]+");
236         // \wh@tever
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);
242         while (index >= 0) {
243                 int length = match.capturedLength(0);
244                 setFormat(index, length, keywordFormat);
245                 match = exprKeyword.match(text, index + length);
246                 index = match.capturedStart(0);
247         }
248         // White space
249         QRegularExpression exprWhiteSpace("\\s");
250         match = exprWhiteSpace.match(text);
251         index = match.capturedStart(0);
252         while (index >= 0) {
253                 int length = match.capturedLength(0);
254                 setFormat(index, length, commentFormat);
255                 match = exprWhiteSpace.match(text, index + length);
256                 index = match.capturedStart(0);
257         }
258         // %comment
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);
267         while (index >= 0) {
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);
273         }
274         // <LyX Warning: ...>
275         QString lyxwarn = qt_("LyX Warning: ");
276         QRegularExpression exprWarning("<" + lyxwarn + "[^<]*>");
277         match = exprWarning.match(text);
278         index = match.capturedStart(0);
279         while (index >= 0) {
280                 int length = match.capturedLength(0);
281                 setFormat(index, length, warningFormat);
282                 match = exprWarning.match(text, index + length);
283                 index = match.capturedStart(0);
284         }
285 #endif
286 }
287
288 } // namespace frontend
289 } // namespace lyx