1 /*
2 	Copyright (C) 2008, 2009 Andres Cabrera
3 	mantaraya36@gmail.com
4 
5 	This file is part of CsoundQt.
6 
7 	CsoundQt is free software; you can redistribute it
8 	and/or modify it under the terms of the GNU Lesser General Public
9 	License as published by the Free Software Foundation; either
10 	version 2.1 of the License, or (at your option) any later version.
11 
12 	CsoundQt is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU Lesser General Public License for more details.
16 
17 	You should have received a copy of the GNU Lesser General Public
18 	License along with Csound; if not, write to the Free Software
19 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 	02111-1307 USA
21 */
22 
23 #include "highlighter.h"
24 
25 #include <QDebug>
26 
TextBlockData()27 TextBlockData::TextBlockData()
28 {
29 	// Nothing to do
30 }
31 
parentheses()32 QVector<ParenthesisInfo *> TextBlockData::parentheses()
33 {
34 	return m_parentheses;
35 }
36 
37 
insert(ParenthesisInfo * info)38 void TextBlockData::insert(ParenthesisInfo *info)
39 {
40 	int i = 0;
41 	while (i < m_parentheses.size() &&
42 		info->position > m_parentheses.at(i)->position)
43 		++i;
44 
45 	m_parentheses.insert(i, info);
46 }
47 
saturateColor(QColor color,int percent)48 QColor saturateColor(QColor color, int percent) {
49     int sat = color.saturation();
50     sat = sat * percent / 100;
51     if(sat > 255)
52         sat = 255;
53     return QColor::fromHsl(color.hue(), sat, color.lightness());
54 }
55 
setTheme(const QString & theme)56 void Highlighter::setTheme(const QString &theme) {
57     if(theme == "none") {
58         defaultFormat.setForeground(QColor("black"));
59         defaultFormat.setBackground(QColor(250, 250, 250));
60         /*
61         csdtagFormat = defaultFormat;
62         instFormat = defaultFormat;
63         keywordFormat = defaultFormat;
64         headerFormat = defaultFormat;
65         opcodeFormat = defaultFormat;
66         singleLineCommentFormat  = defaultFormat;
67         */
68 
69 
70     }
71     else if(theme == "classic") {
72         defaultFormat.setForeground(QColor("black"));
73         defaultFormat.setBackground(QColor(250, 250, 250));
74 
75         csdtagFormat.setForeground(QColor("brown"));
76         csdtagFormat.setFontWeight(QFont::Bold);
77 
78         instFormat.setForeground(QColor("purple"));
79         instFormat.setFontWeight(QFont::Bold);
80 
81         keywordFormat.setForeground(QColor("#E65100"));
82         keywordFormat.setFontWeight(QFont::Bold);
83 
84         headerFormat.setForeground(QColor("brown"));
85         headerFormat.setFontWeight(QFont::Bold);
86 
87         opcodeFormat.setForeground(QColor("blue"));
88         opcodeFormat.setFontWeight(QFont::Bold);
89         deprecatedFormat = opcodeFormat;
90 
91         singleLineCommentFormat.setForeground(QColor("green"));
92         singleLineCommentFormat.setFontItalic(true);
93 
94         macroDefineFormat.setForeground(QColor("green"));
95         macroDefineFormat.setFontWeight(QFont::Bold);
96 
97         pfieldFormat.setFontWeight(QFont::Bold);
98 
99         irateFormat.setForeground(QColor("darkCyan"));
100         krateFormat.setForeground(QColor("darkCyan"));
101 
102         arateFormat.setForeground(QColor("darkCyan"));
103         arateFormat.setFontWeight(QFont::Bold);
104 
105         stringVarFormat.setForeground(QColor(Qt::darkYellow));
106         stringVarFormat.setFontWeight(QFont::Bold);
107 
108         fsigFormat.setForeground(QColor(Qt::gray));
109         fsigFormat.setFontWeight(QFont::Bold);
110 
111         labelFormat.setForeground(QColor(205,92,92));
112         labelFormat.setFontWeight(QFont::Bold);
113 
114         nameFormat.setForeground(QColor("#880E4F"));
115         nameFormat.setFontWeight(QFont::Bold);
116         // nameFormat.setFontUnderline(true);
117 
118         quotationFormat.setForeground(Qt::red);
119         multiLineCommentFormat.setForeground(QColor("green"));
120 
121         ioFormat = opcodeFormat;
122         udoFormat = opcodeFormat;
123         operatorFormat = defaultFormat;
124         importantCommentFormat = singleLineCommentFormat;
125     }
126     else if(theme == "light") {
127         defaultFormat.setForeground(QColor("#030303"));
128         defaultFormat.setBackground(QColor("#F9F9F9"));
129 
130         // csdtagFormat.setFontWeight(QFont::Bold);
131 
132         instFormat.setForeground(QColor("#82387B"));
133         instFormat.setFontWeight(QFont::Bold);
134 
135         keywordFormat.setForeground(QColor("#D64100"));
136         keywordFormat.setFontWeight(QFont::Bold);
137 
138         // headerFormat.setForeground(QColor("#C62828"));
139         // headerFormat.setFontWeight(QFont::Bold);
140         headerFormat = keywordFormat;
141 
142         opcodeFormat.setForeground(QColor("#303F9F"));
143         opcodeFormat.setFontWeight(QFont::Bold);
144 
145         deprecatedFormat.setForeground(QColor("#880000"));
146         deprecatedFormat.setFontUnderline(true);
147 
148         singleLineCommentFormat.setForeground(QColor("#8F8F7F"));
149         singleLineCommentFormat.setFontItalic(true);
150 
151 
152         importantCommentFormat.setBackground(QColor("#FFFFA0"));
153         importantCommentFormat.setForeground(
154                     singleLineCommentFormat.foreground().color().darker(150));
155         // importantCommentFormat.setFontItalic(true);
156 
157         macroDefineFormat.setForeground(QColor("#00695C"));
158         macroDefineFormat.setFontWeight(QFont::Bold);
159 
160         pfieldFormat.setFontWeight(QFont::Bold);
161 
162 
163         krateFormat.setForeground(QColor("#10796C"));
164         irateFormat.setForeground(QColor("#303F9F"));
165 
166         // arateFormat.setForeground(QColor("#C62828"));
167         arateFormat.setForeground(QColor("#B71C1C"));
168         arateFormat.setFontWeight(QFont::Bold);
169 
170         stringVarFormat.setForeground(QColor(Qt::darkYellow));
171         stringVarFormat.setFontWeight(QFont::Normal);
172 
173         fsigFormat.setForeground(QColor("#Ad1457"));
174         fsigFormat.setFontWeight(QFont::Bold);
175 
176         labelFormat.setForeground(QColor(205,92,92));
177         labelFormat.setFontWeight(QFont::Bold);
178         labelFormat.setFontUnderline(true);
179 
180         nameFormat.setForeground(QColor("#F50057"));
181         nameFormat.setFontWeight(QFont::Bold);
182         // nameFormat.setFontUnderline(true);
183 
184         // quotationFormat.setForeground(QColor("#4CAf50"));
185         quotationFormat.setForeground(QColor("#827717"));
186         multiLineCommentFormat.setForeground(quotationFormat.foreground());
187 
188         ioFormat.setForeground(QColor("#8844BB"));
189         ioFormat.setFontWeight(QFont::Bold);
190         // ioFormat.setFontItalic(true);
191 
192         udoFormat = opcodeFormat;
193         udoFormat.setForeground(opcodeFormat.foreground().color().darker(150));
194 
195         csdtagFormat.setForeground(instFormat.foreground());
196 
197         operatorFormat.setForeground(instFormat.foreground().color());
198 
199     }
200     else if(theme == "dark") {
201         defaultFormat.setForeground(QColor("#FBFBFB"));
202         defaultFormat.setBackground(QColor("#161616"));
203 
204 
205         // csdtagFormat.setFontWeight(QFont::Bold);
206 
207         instFormat.setForeground(QColor("#B268AB"));
208         instFormat.setFontWeight(QFont::Bold);
209 
210         keywordFormat.setForeground(QColor("#FF9800"));
211         keywordFormat.setFontWeight(QFont::Bold);
212 
213         // headerFormat.setForeground(QColor("#FFD54F"));
214         // headerFormat.setFontWeight(QFont::Bold);
215         headerFormat = keywordFormat;
216 
217         opcodeFormat.setForeground(QColor("#4FC3F7"));
218         opcodeFormat.setFontWeight(QFont::Bold);
219 
220         deprecatedFormat.setForeground(saturateColor(opcodeFormat.foreground().color(), 50));
221         deprecatedFormat.setUnderlineColor("#880000");
222         deprecatedFormat.setFontUnderline(true);
223 
224 
225         // singleLineCommentFormat.setForeground(QColor("#9F9F8F"));
226         singleLineCommentFormat.setForeground(QColor("#755CB0"));
227         singleLineCommentFormat.setFontItalic(true);
228 
229         importantCommentFormat.setBackground(defaultFormat.background().color().lighter(140));
230         importantCommentFormat.setForeground(
231                     singleLineCommentFormat.foreground().color().lighter(175));
232 
233         macroDefineFormat.setForeground(QColor("#F06292"));
234         macroDefineFormat.setFontWeight(QFont::Bold);
235 
236         pfieldFormat.setFontWeight(QFont::Bold);
237 
238         // krateFormat.setForeground(QColor("#EF9A9A"));
239         krateFormat.setForeground(QColor("#66EEBB"));
240         irateFormat.setForeground(QColor("#FFFFFF"));
241 
242         // arateFormat.setForeground(QColor("#C62828"));
243         arateFormat.setForeground(QColor("#F75C5C"));
244         arateFormat.setFontWeight(QFont::Bold);
245 
246 
247         fsigFormat.setForeground(QColor("#Ad1457"));
248         fsigFormat.setFontWeight(QFont::Bold);
249 
250         labelFormat.setForeground(instFormat.foreground());
251         labelFormat.setFontWeight(QFont::Bold);
252         labelFormat.setFontUnderline(true);
253 
254         nameFormat.setForeground(QColor("#D298FB"));
255         nameFormat.setFontWeight(QFont::Bold);
256         // nameFormat.setFontUnderline(true);
257 
258         // quotationFormat.setForeground(QColor("#66FFAA"));
259         quotationFormat.setForeground(QColor("#91FF55"));
260         stringVarFormat.setForeground(quotationFormat.foreground());
261         stringVarFormat.setFontWeight(QFont::Normal);
262 
263         multiLineCommentFormat.setForeground(quotationFormat.foreground());
264 
265         ioFormat.setForeground(QColor("#FFD54F"));
266         ioFormat.setFontWeight(QFont::Bold);
267 
268         // udoFormat.setForeground(QColor("#60BBB4"));
269         // udoFormat.setFontWeight(opcodeFormat.fontWeight());
270         udoFormat.setForeground(opcodeFormat.foreground());
271         udoFormat.setFontWeight(QFont::Normal);
272 
273         // ioFormat.setFontItalic(true);
274         csdtagFormat.setForeground(instFormat.foreground());
275         operatorFormat.setForeground(nameFormat.foreground().color());
276 
277     }
278     else {
279         qDebug() << "Theme" << theme << "not defined, using default";
280         this->setTheme("classic");
281         return;
282     }
283     // --------------------------------------
284     girateFormat.setForeground(irateFormat.foreground());
285     // girateFormat.setFontWeight(QFont::Bold);
286     girateFormat.setFontItalic(true);
287 
288     gkrateFormat.setForeground(krateFormat.foreground());
289     // gkrateFormat.setFontWeight(QFont::Bold);
290     gkrateFormat.setFontItalic(true);
291 
292     garateFormat.setForeground(arateFormat.foreground());
293     garateFormat.setFontWeight(arateFormat.fontWeight());
294     garateFormat.setFontItalic(true);
295 
296     gfsigFormat.setForeground(fsigFormat.foreground());
297     gfsigFormat.setFontWeight(fsigFormat.fontWeight());
298     gfsigFormat.setFontItalic(true);
299 
300     gstringVarFormat.setForeground(stringVarFormat.foreground());
301     gstringVarFormat.setFontWeight(QFont::Bold);
302     gstringVarFormat.setFontItalic(true);
303 
304     // ioFormat.setFontItalic(true);
305 
306     QColor color = saturateColor(headerFormat.foreground().color().lighter(150), 150);
307     csoundOptionFormat.setForeground(color);
308     jsKeywordFormat = keywordFormat;
309 
310     m_theme = theme;
311     emit this->rehighlight();
312 }
313 
getFormat(QString tag)314 QTextCharFormat Highlighter::getFormat(QString tag) {
315     if(tag == "" || tag == "default") {
316         return defaultFormat;
317     }
318     if(tag == "csdtag")
319         return csdtagFormat;
320     if(tag == "inst")
321         return instFormat;
322     if(tag == "keyword")
323         return keywordFormat;
324     if(tag == "opcode")
325         return opcodeFormat;
326     if(tag == "singleLineComment")
327         return singleLineCommentFormat;
328     if(tag == "macroDefine")
329         return macroDefineFormat;
330     if(tag == "pfield")
331         return pfieldFormat;
332     QTextCharFormat fallback;
333     fallback.setForeground(QColor("black"));
334     fallback.setBackground(QColor(250, 250, 250));
335     return fallback;
336 }
337 
Highlighter(QTextDocument * parent)338 Highlighter::Highlighter(QTextDocument *parent)
339 	: QSyntaxHighlighter(parent)
340 {
341 	commentStartExpression = QRegExp("/\\*");
342 	commentEndExpression = QRegExp("\\*/");
343 
344 	//  b64encStartExpression = QRegExp("<CsFileB .*>");
345 	//  b64encEndExpression = QRegExp("<CsFileB>");
346 	colorVariables = true;
347 	m_mode = 0; // default to Csound mode
348 
349     tagPatterns << "<CsoundSynthesizer>" << "</CsoundSynthesizer>"
350 				<< "<CsInstruments>" << "</CsInstruments>"
351 				<< "<CsOptions>" << "</CsOptions>"
352 				<< "<CsScore>" << "</CsScore>"
353 				<< "<CsVersion>" << "</CsVersion>"
354 				<< "<MacOptions>" << "</MacOptions>"
355 				<< "<MacGUI>" << "</MacGUI>"
356 				<< "<csLADSPA>" << "</csLADSPA>"
357                 << "<Cabbage>" << "</Cabbage>"
358 				<< "<CsHtml5>" << "</CsHtml5>"
359                 << "<CsFileB>" << "</CsFileB>";
360 
361     instPatterns << "instr" << "endin" << "opcode" << "endop";
362     headerPatterns << "sr" << "kr" << "ksmps" << "nchnls" << "nchnls_i" << "0dbfs" << "A4"
363                    << "zakinit" << "massign";
364 
365     keywordLiterals << "init" << "if" << "then" << "else" << "endif" << "elseif"
366                     << "while" << "goto" << "igoto" << "kgoto"
367                     << "do" << "od"
368                     << "int" << "turnoff" << "xin" << "xout"
369                     << "passign";
370 
371     ioPatterns      << "in" << "ins" << "inch" << "out" << "outs" << "outch"
372                     << "outvalue" << "invalue" << "chnget" << "chnset" << "chn_k" << "chn_a"
373                     << "zaw" << "zar" << "zkw" << "zkr" << "zawm"
374                     << "OSCsend" << "OSClisten" << "OSCraw" << "OSCinit";
375 
376     deprecatedOpcodes << "array" << "compress" << "dcblock" << "diskin" << "flooper"
377                       << "in32" << "inh" << "ino" << "inq" << "inrq" << "inx"
378                       << "loop_ge" << "loop_gt" << "loop_le" << "loop_lt"
379                       << "nlfilt" << "outc" << "outo" << "outq" << "outx" << "vco"
380                       << "vincr" << "vbap16" << "vbap4" << "vbap8"
381                       << "vbap4move" << "vbap8move" << "balance"
382                       << "chani" << "chano" << "cigoto" << "ckgoto"
383                       << "cngoto" << "control" << "setctrl" << "flashtxt"
384                       << "vaset" << "vaget"
385                       << "button" << "checkbox";
386 
387     operatorPatterns << "&&" << "||";
388 
389     csoundOptions << "-+rtaudio=" << "-+rtmidi=" << "--nodisplays" << "--nosound"
390                   << "--syntax-check-only" << "--control-rate=" << "--messagelevel="
391                   << "--env:" << "--dither" << "--sched" << "--omacro:"
392                   << "--smacro:" << "--verbose" << "--sample-accurate" << "--realtime"
393                   << "--nchnls=" << "--nchnls_i=" << "--sinesize" << "--daemon"
394                   << "--port=" << "--use-system-sr" << "--ksmps=" << "-+jack_client="
395                   ;
396 
397     csoundOptionsRx = QRegExp("--(env|nodisplays|nosound|control-rate|messagelevel=|"
398                               "dither|sched|omacro:|smacro:|verbose|sample-accurate|"
399                               "realtime|nchnls|nchnls_i|sinesize=|daemon|port=|"
400                               "use-system-sr|ksmps|midi-key-cps=|midi-velocity=)");
401 
402     csoundOptionsRx2 = QRegExp("-+(rtaudio=|rtmidi=|jack_client=)");
403 
404     functionRegex = QRegExp("\\b\\w+(\\:a|\\:k|\\:i)?(?=\\()");
405 
406     // For Python
407     pythonKeywords << "and" << "or" << "not" << "is"
408                    << "global" << "with" << "from" << "import" << "as"
409                    << "if" << "else" << "elif"
410                    << "print" << "class" << "del" << "exec"
411                    << "for" << "in" << "while" << "continue" << "pass" << "break"
412                    << "def" << "return" << "lambda"
413                    << "yield" << "assert" << "try" << "except" << "finally" << "raise"
414                    << "True" << "False" << "None";
415 
416 	// for html
417     htmlKeywords << "<\\ba\\b" << "<\\babbr\\b" << "<\\bacronym\\b" << "<\\baddress\\b"
418                  << "<\\bapplet\\b" << "<\\barea\\b" << "<\\barticle\\b" << "<\\baside\\b"
419                  << "<\\baudio\\b" << "<\\bb\\b" << "<\\bbase\\b" << "<\\bbasefont\\b"
420                  << "<\\bbdi\\b" << "<\\bbdo\\b" << "<\\bbgsound\\b" << "<\\bblockquote\\b"
421                  << "<\\bbig\\b" << "<\\bbody\\b" << "<\\bblink\\b" << "<\\bbr\\b"
422                 << "<\\bbutton\\b" << "<\\bcanvas\\b" << "<\\bcaption\\b" << "<\\bcenter\\b"
423                 << "<\\bcite\\b" << "<\\bcode\\b" << "<\\bcol\\b" << "<\\bcolgroup\\b"
424                 << "<\\bcomment\\b" << "<\\bdata\\b" << "<\\bdatalist\\b" << "<\\bdd\\b"
425                 << "<\\bdel\\b" << "<\\bdetails\\b" << "<\\bdfn\\b" << "<\\bdialog\\b"
426                 << "<\\bdir\\b" << "<\\bdiv\\b" << "<\\bdl\\b" << "<\\bdt\\b"
427                 << "<\\bem\\b" << "<\\bembed\\b"
428                 << "<\\bfieldset\\b" << "<\\bfigcaption\\b" << "<\\bfigure\\b"
429                 << "<\\bfont\\b" << "<\\bfooter\\b" << "<\\bform\\b" << "<\\bframe\\b"
430 				<< "<\\bframeset\\b" << "<\\bh1\\b" << "<\\bh2\\b" << "<\\bh3\\b" << "<\\bh4\\b"
431 				<< "<\\bh5\\b" << "<\\bh6\\b" << "<\\bhead\\b" << "<\\bheader\\b" << "<\\bhgroup\\b"
432 				<< "<\\bhr\\b" << "<\\bhtml\\b" << "<\\bi\\b" << "<\\biframe\\b" << "<\\bimg\\b"
433                 << "<\\binput\\b" << "<\\bins\\b" << "<\\bisindex\\b" << "<\\bkbd\\b"
434                 << "<\\bkeygen\\b" << "<\\blabel\\b" << "<\\blegend\\b" << "<\\bli\\b"
435                 << "<\\blink\\b" << "<\\blisting\\b" << "<\\bmain\\b" << "<\\bmap\\b"
436                 << "<\\bmarquee\\b" << "<\\bmark\\b" << "<\\bmenu\\b"
437                 << "<\\bamenuitem\\b" << "<\\bmeta\\b" << "<\\bmeter\\b" << "<\\bmulticol\\b"
438                 << "<\\bnav\\b" << "<\\bnobr\\b" << "<\\bnoembed\\b" << "<\\bnoindex\\b"
439                 << "<\\bnoframes\\b" << "<\\bnoscript\\b" << "<\\bobject\\b" << "<\\bol\\b"
440                 << "<\\boptgroup\\b" << "<\\boption\\b" << "<\\boutput\\b"
441                 << "<\\bp\\b" << "<\\bparam\\b" << "<\\bpicture\\b" << "<\\bplaintext\\b"
442                 << "<\\bpre\\b" << "<\\bprogress\\b" << "<\\bq\\b" << "<\\brp\\b"
443                 << "<\\brt\\b" << "<\\brtc\\b" << "<\\bruby\\b" << "<\\bs\\b"
444                 << "<\\bsamp\\b" << "<\\bscript\\b" << "<\\bsection\\b" << "<\\bselect\\b"
445                 << "<\\bsmall\\b" << "<\\bsource\\b" << "<\\bspacer\\b" << "<\\bspan\\b"
446                 << "<\\bstrike\\b" << "<\\bstrong\\b" << "<\\bstyle\\b" << "<\\bsub\\b"
447                 << "<\\bsummary\\b" << "<\\bsup\\b" << "<\\btable\\b" << "<\\btbody\\b"
448                 << "<\\btd\\b" << "<\\btemplate\\b" << "<\\btextarea\\b"
449                 << "<\\btfoot\\b" << "<\\bth\\b" << "<\\bthead\\b" << "<\\btime\\b"
450                 << "<\\btitle\\b" << "<\\btr\\b" << "<\\btrack\\b" << "<\\btt\\b"
451                 << "<\\bu\\b" << "<\\bul\\b" << "<\\bvar\\b" << "<\\bwbr\\b" << "<\\bxmp\\b"
452                 << "<!\\bDOCTYPE\\b";
453 
454 	javascriptKeywords << "function" << "var" << "if" << "===" << "console.log"  << "console.warn";
455 
456     // this->setTheme("classic");
457 }
458 
459 
~Highlighter()460 Highlighter::~Highlighter()
461 {
462 }
463 
setOpcodeNameList(QStringList list)464 void Highlighter::setOpcodeNameList(QStringList list)
465 {
466 	m_opcodeList = list;
467 	//   setFirstRules();
468 	setLastRules();
469 }
470 
setMode(int mode)471 void Highlighter::setMode(int mode)
472 {
473 	m_mode = mode;
474 }
475 
476 
setColorVariables(bool color)477 void Highlighter::setColorVariables(bool color)
478 {
479 	colorVariables = color;
480 
481 	highlightingRules.clear();
482 	setLastRules();
483 }
484 
highlightBlock(const QString & text)485 void Highlighter::highlightBlock(const QString &text)
486 {
487 	switch (m_mode) {
488 	case 0:  // Csound mode
489 		highlightCsoundBlock(text);
490 		break;
491 	case 1:  // Python mode
492 		highlightPythonBlock(text);
493 		break;
494 	case 2:  // Xml mode
495 		highlightXmlBlock(text);
496 		break;
497 	case 3:  // Orc
498 		highlightCsoundBlock(text);
499 		break;
500     case 4:  // Sco
501 		highlightCsoundBlock(text);
502 		break;
503     case 5:  // Inc
504         highlightCsoundBlock(text); // maybe anything not python or xml should be higlighter as csound?
505         break;
506 	case 6:  // Html
507 		highlightHtmlBlock(text);
508 		break;
509 	}
510 	// for parenthesis
511 	TextBlockData *data = new TextBlockData;
512 
513 	int leftPos = text.indexOf('(');
514 	while (leftPos != -1) {
515 		ParenthesisInfo *info = new ParenthesisInfo;
516 		info->character = '(';
517 		info->position = leftPos;
518 
519 		data->insert(info);
520 		leftPos = text.indexOf('(', leftPos + 1);
521 	}
522 
523 	int rightPos = text.indexOf(')');
524 	while (rightPos != -1) {
525 		ParenthesisInfo *info = new ParenthesisInfo;
526 		info->character = ')';
527 		info->position = rightPos;
528 
529 		data->insert(info);
530 
531 		rightPos = text.indexOf(')', rightPos +1);
532 	}
533 
534 	setCurrentBlockUserData(data);
535 }
536 
537 
highlightCsoundBlock(const QString & text)538 void Highlighter::highlightCsoundBlock(const QString &text)
539 {
540     // TODO: rewrite this using QRegularExpression
541 
542 	// text is processed one line at a time
543     if(m_theme == "none")
544         return;
545 
546     int commentIndex = text.indexOf(';'); // try both comment markings
547 	if (commentIndex < 0) {
548         commentIndex = text.indexOf("//");
549 	}
550 
551     if (commentIndex >= 0) {
552         // if(QRegExp("^\\s*;;").indexIn(text) != -1) {
553         if (text.length() > commentIndex+1) {
554             if(text[commentIndex+1] == ';' && text.trimmed()[0] == ';') {
555                 // ;; pattern, which is picked up by inspector
556                 setFormat(commentIndex, text.size() - commentIndex, importantCommentFormat);
557             } else {
558                 setFormat(commentIndex, text.size() - commentIndex, singleLineCommentFormat);
559             }
560         }
561 	}
562 	else {
563 		commentIndex = text.size() + 1;
564     }
565 
566     QRegExp regexp;
567     int index = 0;
568     int length;
569 
570     // string
571     regexp = QRegExp("\"[^\"]*\"");
572 
573     index = text.indexOf(regexp);
574     while (index >= 0 && index < commentIndex) {
575         length = regexp.matchedLength();
576         setFormat(index, length, quotationFormat);
577         index = text.indexOf(regexp, index + length);
578     }
579 
580     // define
581     regexp = QRegExp("^\\s*#define\\s+[_\\w\\ \\t]*#.*#");
582     index = text.indexOf(regexp);
583     if(index >= 0 && index < commentIndex) {
584         int length = regexp.matchedLength();
585         setFormat(index, length, macroDefineFormat);
586         index = text.indexOf(regexp,index+length);
587     }
588 
589     regexp = QRegExp("\\b(instr|opcode)\\s+(\\w+)\\b");
590     index = 0;
591     while ((index = regexp.indexIn(text, index)) != -1) {
592         auto group = regexp.cap(2);
593         length = regexp.matchedLength();
594         setFormat(regexp.pos(2), group.length(), nameFormat);
595         index += length;
596     }
597 
598     index = 0;
599     while ((index = csoundOptionsRx.indexIn(text, index)) != -1 && index < commentIndex) {
600         length = csoundOptionsRx.matchedLength();
601         setFormat(index, length, csoundOptionFormat);
602         index += length;
603     }
604 
605     index = 0;
606     while ((index = csoundOptionsRx2.indexIn(text, index)) != -1 && index < commentIndex) {
607         length = csoundOptionsRx2.matchedLength();
608         setFormat(index, length, csoundOptionFormat);
609         index += length;
610     }
611 
612 
613     regexp = QRegExp(R"(&&|==|\|\||<|>|<=|>=|!=|\\)");
614     index = 0;
615     while ((index = regexp.indexIn(text, index)) != -1 && index < commentIndex) {
616         length = regexp.matchedLength();
617         setFormat(index, length, operatorFormat);
618         index += length;
619     }
620 
621 
622     /*
623     index = 0;
624     while ((index = functionRegex.indexIn(text, index)) != -1 && index < commentIndex) {
625         length = functionRegex.matchedLength();
626         setFormat(index, length, opcodeFormat);
627         index += length;
628     }
629     */
630 
631     QRegExp expression("\\b[\\w:]+\\b");
632     index = text.indexOf(expression, 0);
633     length = expression.matchedLength();
634 
635 	while (index >= 0 && index < commentIndex) {
636 		int wordStart = index;
637 		int wordEnd = wordStart + length;
638 
639 		if (index>0) {
640 			auto prev = text.at(index - 1);
641             if (prev == '$' || prev == '#') {
642                 // check if macro name - replacement for regexp solution which I could not find
643                 wordStart--;
644                 length++;
645                 setFormat(wordStart, wordEnd - wordStart, macroDefineFormat);
646                 index = text.indexOf(expression, wordEnd);
647 				length = expression.matchedLength();
648 				continue;
649 			}
650         }
651 		wordEnd = (wordEnd > 0 ? wordEnd : text.size());
652 		QString word = text.mid(wordStart, length);
653         if (word.indexOf(QRegExp("p[\\d]+\\b")) != -1) {
654 			setFormat(wordStart, wordEnd - wordStart, pfieldFormat);
655 		}
656 		if (instPatterns.contains(word)) {
657 			setFormat(wordStart, wordEnd - wordStart, instFormat);
658 			break; // was: return. FOr any case, to go through lines after while loop
659 		}
660 		else if (tagPatterns.contains("<" + word + ">") && wordStart > 0) {
661             setFormat(wordStart - (text[wordStart - 1] == '/'? 2: 1),
662                       wordEnd - wordStart + (text[wordStart - 1] == '/'? 3: 2),
663                       csdtagFormat);
664 		}
665 		else if (headerPatterns.contains(word)) {
666             setFormat(wordStart, wordEnd - wordStart, headerFormat);
667 		}
668         else if (keywordLiterals.contains(word)) {
669             setFormat(wordStart, wordEnd - wordStart, keywordFormat);
670 		}
671         else if (ioPatterns.contains(word)) {
672             setFormat(wordStart, wordEnd - wordStart, ioFormat);
673         }
674 
675 		else if (word.contains(":")) {
676 			QStringList parts = word.split(":");
677 			if (parts.size() == 2) {
678 				if (findOpcode(parts[0]) >= 0) {
679 					setFormat(wordStart, wordEnd - wordStart, opcodeFormat);
680 				}
681 			}
682 		}
683         else if (m_parsedUDOs.contains(word)) {
684             setFormat(wordStart, wordEnd - wordStart, udoFormat);
685         }
686         else if (deprecatedOpcodes.contains(word)) {
687             setFormat(wordStart, wordEnd - wordStart, deprecatedFormat);
688         }
689         else if (findOpcode(word) >= 0) {
690 			setFormat(wordStart, wordEnd - wordStart, opcodeFormat);
691 		}
692 		else if (word.startsWith('a') && colorVariables) {
693 			setFormat(wordStart, wordEnd - wordStart, arateFormat);
694 		}
695         else if (word.startsWith('k') && colorVariables) {
696 			setFormat(wordStart, wordEnd - wordStart, krateFormat);
697 		}
698         else if (word.startsWith('i') && colorVariables) {
699             setFormat(wordStart, wordEnd - wordStart, irateFormat);
700         }
701 		else if (word.startsWith("ga")  && colorVariables) {
702 			setFormat(wordStart, wordEnd - wordStart, garateFormat);
703 		}
704         else if (word.startsWith("gk")  && colorVariables) {
705 			setFormat(wordStart, wordEnd - wordStart, gkrateFormat);
706 		}
707         else if (word.startsWith("gi")  && colorVariables) {
708             setFormat(wordStart, wordEnd - wordStart, girateFormat);
709         }
710 		else if (word.startsWith("S")  && colorVariables) {
711 			setFormat(wordStart, wordEnd - wordStart, stringVarFormat);
712 		}
713 		else if (word.startsWith("gS")  && colorVariables) {
714 			setFormat(wordStart, wordEnd - wordStart, gstringVarFormat);
715 		}
716 		else if (word.startsWith("f") && colorVariables) {
717 			setFormat(wordStart, wordEnd - wordStart, fsigFormat);
718 		}
719 		else if (word.startsWith("gf") && colorVariables) {
720 			setFormat(wordStart, wordEnd - wordStart, gfsigFormat);
721 		}
722 		index = text.indexOf(expression, wordEnd);
723 		length = expression.matchedLength();
724 	}
725 	//last rules
726     for (int i = 0; i < lastHighlightingRules.size(); i++) {
727         QRegExp expression(lastHighlightingRules[i].pattern);
728         int index = 0, length;
729         while((index = expression.indexIn(text, index)) != -1 && index < commentIndex) {
730             int group = lastHighlightingRules[i].group;
731             if(group == 0) {
732                 length = expression.matchedLength();
733                 setFormat(index, length, lastHighlightingRules[i].format);
734             } else {
735                 auto capture = expression.cap(group);
736                 length = expression.matchedLength();
737                 setFormat(expression.pos(group), capture.length(), lastHighlightingRules[i].format);
738             }
739             index += length;
740         }
741     }
742 
743 	setCurrentBlockState(0);
744 
745 	int startIndex = 0;
746 	if (previousBlockState() != 1) {
747 		startIndex = text.indexOf(commentStartExpression);
748 	}
749 
750     // while (startIndex >= 0 && startIndex < commentIndex) {
751     while (startIndex >= 0) {
752 		int endIndex = text.indexOf(commentEndExpression, startIndex);
753 		if (format(startIndex) == quotationFormat) {
754 			startIndex = text.indexOf(commentStartExpression,
755 									  startIndex + 1);
756 			continue;
757 		}
758 		int commentLength;
759 		if (endIndex == -1) {
760 			setCurrentBlockState(1);
761 			commentLength = text.length() - startIndex;
762 		} else {
763 			commentLength = endIndex - startIndex
764 					+ commentEndExpression.matchedLength();
765 		}
766 		setFormat(startIndex, commentLength, multiLineCommentFormat);
767 		startIndex = text.indexOf(commentStartExpression,
768 								  startIndex + commentLength);
769 	}
770 }
771 
772 
highlightPythonBlock(const QString & text)773 void Highlighter::highlightPythonBlock(const QString &text)
774 {
775 	QRegExp expression("\\b+\\w\\b+");
776 	int index = text.indexOf(expression, 0);
777     for (int i = 0; i < pythonKeywords.size(); i++) {
778         QRegExp expression("\\b+" + pythonKeywords[i] + "\\b+");
779 		int index = text.indexOf(expression);
780 		while (index >= 0) {
781 			int length = expression.matchedLength();
782 			setFormat(index, length, keywordFormat);
783 			index = text.indexOf(expression, index + length);
784 		}
785 	}
786 	QRegExp strings( QRegExp("\"[^\"]*\""));
787 	index = text.indexOf(strings);
788 	while (index >= 0) {
789 		int length = strings.matchedLength();
790 		setFormat(index, length, quotationFormat);
791 		index = text.indexOf(strings, index + length);
792 	}
793 	strings = QRegExp("'[^'']*'");
794 	index = text.indexOf(strings);
795 	while (index >= 0) {
796 		int length = strings.matchedLength();
797 		setFormat(index, length, quotationFormat);
798 		index = text.indexOf(strings, index + length);
799 	}
800 	QRegExp expComment("#.*");
801 	index = text.indexOf(expComment);
802 	while (index >= 0) {
803 		int length = expComment.matchedLength();
804 		setFormat(index, length, singleLineCommentFormat);
805 		index = text.indexOf(expComment, index + length);
806 	}
807 }
808 
highlightXmlBlock(const QString &)809 void Highlighter::highlightXmlBlock(const QString &/*text*/)
810 {
811 }
812 
813 // for html
814 
highlightHtmlBlock(const QString & text)815 void Highlighter::highlightHtmlBlock(const QString &text)
816 {
817 	QRegExp expression("\\b+\\w\\b+");
818 	int index = text.indexOf(expression, 0);
819 	for (int i = 0; i < htmlKeywords.size(); i++) {
820 		QRegExp expression(htmlKeywords[i]);//expression("\\b+" + htmlKeywords[i] + "\\b+");
821 		int index = text.indexOf(expression);
822 		while (index >= 0) {
823 			int length = expression.matchedLength();
824 			setFormat(index, length, keywordFormat);
825 			index = text.indexOf(expression, index + length);
826 		}
827 	}
828 
829 	for (int i=0; i<javascriptKeywords.size(); i++) {
830 		QRegExp expression("\\b+" + javascriptKeywords[i] + "\\b+");
831 		int index = text.indexOf(expression);
832 		while (index >= 0) {
833 			int length = expression.matchedLength();
834 			setFormat(index, length, jsKeywordFormat); // TODO javascriptformat
835 			index = text.indexOf(expression, index + length);
836 		}
837 	}
838 
839 	QRegExp endTag( QRegExp(">$"));
840 	index = text.indexOf(endTag);
841 	while (index >= 0) {
842 		int length = endTag.matchedLength();
843 		setFormat(index, length, keywordFormat);
844 		index = text.indexOf(endTag, index + length);
845 	}
846 
847 	QRegExp strings( QRegExp("\"[^\"]*\""));
848 	index = text.indexOf(strings);
849 	while (index >= 0) {
850 		int length = strings.matchedLength();
851 		setFormat(index, length, quotationFormat);
852 		index = text.indexOf(strings, index + length);
853 	}
854 	strings = QRegExp("'[^'']*'");
855 	index = text.indexOf(strings);
856 	while (index >= 0) {
857 		int length = strings.matchedLength();
858 		setFormat(index, length, quotationFormat);
859 		index = text.indexOf(strings, index + length);
860 	}
861 	int commentIndex = -1;
862 	QRegExp expComment("//.*"); // TODO: avaoid https://
863 	index = text.indexOf(expComment);
864 	if (index>0 ) {
865 		if (text.at(index-1)!=':') { // clumsy way to avoid addresses like https://
866 			while (index >= 0) { // did not manage to do it with regular expression
867 				int length = expComment.matchedLength();
868 				setFormat(index, length, singleLineCommentFormat);
869 				index = text.indexOf(expComment, index + length);
870 			}
871 			commentIndex = index; // better do other way
872 		}
873 	}
874 
875 //	if (commentIndex >= 0) {
876 //		setFormat(commentIndex, text.size() - commentIndex, singleLineCommentFormat);
877 ////		return;
878 //	}
879 	if (commentIndex < 0) {
880 		commentIndex = text.size() + 1;
881 	}
882 
883 	// multiline
884 	setCurrentBlockState(0);
885 	QRegExp htmlCommentStartExpression = QRegExp("<!--");
886 	QRegExp htmlCommentEndExpression = QRegExp("-->");
887 
888 
889 	int startIndex = 0;
890 	if (previousBlockState() != 1) {
891 		startIndex = text.indexOf(htmlCommentStartExpression);
892 	}
893 
894 	while (startIndex >= 0 && startIndex < commentIndex) {
895 		int endIndex = text.indexOf(htmlCommentEndExpression, startIndex);
896 		if (format(startIndex) == quotationFormat) {
897 			startIndex = text.indexOf(htmlCommentStartExpression,
898 									  startIndex + 1);
899 			continue;
900 		}
901 		int commentLength;
902 		if (endIndex == -1) {
903 			setCurrentBlockState(1);
904 			commentLength = text.length() - startIndex;
905 		} else {
906 			commentLength = endIndex - startIndex
907 					+ htmlCommentEndExpression.matchedLength();
908 		}
909 		setFormat(startIndex, commentLength, multiLineCommentFormat);
910 		startIndex = text.indexOf(htmlCommentStartExpression,
911 								  startIndex + commentLength);
912 	}
913 }
914 
915 // void Highlighter::setFirstRules()
916 // {
917 //   highlightingRules.clear();
918 // }
919 
setLastRules()920 void Highlighter::setLastRules()
921 {
922 	HighlightingRule rule;
923 
924     rule = {QRegExp("^\\s*([a-zA-Z]\\w*):\\s*"), labelFormat, 1};
925     lastHighlightingRules.append(rule);
926 
927     // strings
928     rule = {QRegExp("\"[^\"]*\""), quotationFormat, 0};
929     lastHighlightingRules.append(rule);
930 
931     // multi line strings
932     rule = {QRegExp("\\{\\{.*"), quotationFormat, 0};
933 	lastHighlightingRules.append(rule);
934 
935     // end multi line strings
936     rule = {QRegExp(".*\\}\\}"), quotationFormat, 0};
937 	lastHighlightingRules.append(rule);
938 }
939 
findOpcode(QString opcodeName,int start,int end)940 int Highlighter::findOpcode(QString opcodeName, int start, int end)
941 {
942 	//   fprintf(stderr, "%i - %i\n", start, end);
943 	Q_ASSERT(m_opcodeList.size() > 0);
944 	if (end == -1) {
945 		end = m_opcodeList.size() -1;
946 	}
947 	int pos = ((end - start)/2) + start;
948 	if (opcodeName == m_opcodeList[pos])
949 		return pos;
950 	else if (start == end)
951 		return -1;
952 	else if (opcodeName < m_opcodeList[pos])
953 		return findOpcode(opcodeName, start, pos);
954 	else if (opcodeName > m_opcodeList[pos])
955 		return findOpcode(opcodeName, pos + 1, end);
956 	return -1;
957 }
958 
setUDOs(QStringList udos)959 void Highlighter::setUDOs(QStringList udos)
960 {
961        m_parsedUDOs = udos;
962 }
963