1 // This module implements the QsciLexer class.
2 //
3 // Copyright (c) 2019 Riverbank Computing Limited <info@riverbankcomputing.com>
4 //
5 // This file is part of QScintilla.
6 //
7 // This file may be used under the terms of the GNU General Public License
8 // version 3.0 as published by the Free Software Foundation and appearing in
9 // the file LICENSE included in the packaging of this file.  Please review the
10 // following information to ensure the GNU General Public License version 3.0
11 // requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 //
13 // If you do not wish to use this file under the terms of the GPL version 3.0
14 // then you may purchase a commercial license.  For more information contact
15 // info@riverbankcomputing.com.
16 //
17 // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 
20 
21 #include "Qsci/qscilexer.h"
22 
23 #include <qapplication.h>
24 #include <qcolor.h>
25 #include <qfont.h>
26 #include <qsettings.h>
27 
28 #include "Qsci/qsciapis.h"
29 #include "Qsci/qsciscintilla.h"
30 #include "Qsci/qsciscintillabase.h"
31 
32 
33 // The ctor.
QsciLexer(QObject * parent)34 QsciLexer::QsciLexer(QObject *parent)
35     : QObject(parent),
36       autoIndStyle(-1), apiSet(0), attached_editor(0)
37 {
38 #if defined(Q_OS_WIN)
39     defFont = QFont("Verdana", 10);
40 #elif defined(Q_OS_MAC)
41     defFont = QFont("Menlo", 12);
42 #else
43     defFont = QFont("Bitstream Vera Sans", 9);
44 #endif
45 
46     // Set the default fore and background colours.
47     QPalette pal = QApplication::palette();
48     defColor = pal.text().color();
49     defPaper = pal.base().color();
50 
51     // Putting this on the heap means we can keep the style getters const.
52     style_map = new StyleDataMap;
53     style_map->style_data_set = false;
54 }
55 
56 
57 // The dtor.
~QsciLexer()58 QsciLexer::~QsciLexer()
59 {
60     delete style_map;
61 }
62 
63 
64 // Set the attached editor.
setEditor(QsciScintilla * editor)65 void QsciLexer::setEditor(QsciScintilla *editor)
66 {
67     attached_editor = editor;
68 }
69 
70 
71 // Return the lexer name.
lexer() const72 const char *QsciLexer::lexer() const
73 {
74     return 0;
75 }
76 
77 
78 // Return the lexer identifier.
lexerId() const79 int QsciLexer::lexerId() const
80 {
81     return QsciScintillaBase::SCLEX_CONTAINER;
82 }
83 
84 
85 // Return the number of style bits needed by the lexer.
styleBitsNeeded() const86 int QsciLexer::styleBitsNeeded() const
87 {
88     return 8;
89 }
90 
91 
92 // Make sure the style defaults have been set.
setStyleDefaults() const93 void QsciLexer::setStyleDefaults() const
94 {
95     if (!style_map->style_data_set)
96     {
97         for (int i = 0; i <= QsciScintillaBase::STYLE_MAX; ++i)
98             if (!description(i).isEmpty())
99                 styleData(i);
100 
101         style_map->style_data_set = true;
102     }
103 }
104 
105 
106 // Return a reference to a style's data, setting up the defaults if needed.
styleData(int style) const107 QsciLexer::StyleData &QsciLexer::styleData(int style) const
108 {
109     StyleData &sd = style_map->style_data[style];
110 
111     // See if this is a new style by checking if the colour is valid.
112     if (!sd.color.isValid())
113     {
114         sd.color = defaultColor(style);
115         sd.paper = defaultPaper(style);
116         sd.font = defaultFont(style);
117         sd.eol_fill = defaultEolFill(style);
118     }
119 
120     return sd;
121 }
122 
123 
124 // Set the APIs associated with the lexer.
setAPIs(QsciAbstractAPIs * apis)125 void QsciLexer::setAPIs(QsciAbstractAPIs *apis)
126 {
127     apiSet = apis;
128 }
129 
130 
131 // Return a pointer to the current APIs if there are any.
apis() const132 QsciAbstractAPIs *QsciLexer::apis() const
133 {
134     return apiSet;
135 }
136 
137 
138 // Default implementation to return the set of fill up characters that can end
139 // auto-completion.
autoCompletionFillups() const140 const char *QsciLexer::autoCompletionFillups() const
141 {
142     return "(";
143 }
144 
145 
146 // Default implementation to return the view used for indentation guides.
indentationGuideView() const147 int QsciLexer::indentationGuideView() const
148 {
149     return QsciScintillaBase::SC_IV_LOOKBOTH;
150 }
151 
152 
153 // Default implementation to return the list of character sequences that can
154 // separate auto-completion words.
autoCompletionWordSeparators() const155 QStringList QsciLexer::autoCompletionWordSeparators() const
156 {
157     return QStringList();
158 }
159 
160 
161 // Default implementation to return the list of keywords that can start a
162 // block.
blockStartKeyword(int *) const163 const char *QsciLexer::blockStartKeyword(int *) const
164 {
165     return 0;
166 }
167 
168 
169 // Default implementation to return the list of characters that can start a
170 // block.
blockStart(int *) const171 const char *QsciLexer::blockStart(int *) const
172 {
173     return 0;
174 }
175 
176 
177 // Default implementation to return the list of characters that can end a
178 // block.
blockEnd(int *) const179 const char *QsciLexer::blockEnd(int *) const
180 {
181     return 0;
182 }
183 
184 
185 // Default implementation to return the style used for braces.
braceStyle() const186 int QsciLexer::braceStyle() const
187 {
188     return -1;
189 }
190 
191 
192 // Default implementation to return the number of lines to look back when
193 // auto-indenting.
blockLookback() const194 int QsciLexer::blockLookback() const
195 {
196     return 20;
197 }
198 
199 
200 // Default implementation to return the case sensitivity of the language.
caseSensitive() const201 bool QsciLexer::caseSensitive() const
202 {
203     return true;
204 }
205 
206 
207 // Default implementation to return the characters that make up a word.
wordCharacters() const208 const char *QsciLexer::wordCharacters() const
209 {
210     return 0;
211 }
212 
213 
214 // Default implementation to return the style used for whitespace.
defaultStyle() const215 int QsciLexer::defaultStyle() const
216 {
217     return 0;
218 }
219 
220 
221 // Returns the foreground colour of the text for a style.
color(int style) const222 QColor QsciLexer::color(int style) const
223 {
224     return styleData(style).color;
225 }
226 
227 
228 // Returns the background colour of the text for a style.
paper(int style) const229 QColor QsciLexer::paper(int style) const
230 {
231     return styleData(style).paper;
232 }
233 
234 
235 // Returns the font for a style.
font(int style) const236 QFont QsciLexer::font(int style) const
237 {
238     return styleData(style).font;
239 }
240 
241 
242 // Returns the end-of-line fill for a style.
eolFill(int style) const243 bool QsciLexer::eolFill(int style) const
244 {
245     return styleData(style).eol_fill;
246 }
247 
248 
249 // Returns the set of keywords.
keywords(int) const250 const char *QsciLexer::keywords(int) const
251 {
252     return 0;
253 }
254 
255 
256 // Returns the default EOL fill for a style.
defaultEolFill(int) const257 bool QsciLexer::defaultEolFill(int) const
258 {
259     return false;
260 }
261 
262 
263 // Returns the default font for a style.
defaultFont(int) const264 QFont QsciLexer::defaultFont(int) const
265 {
266     return defaultFont();
267 }
268 
269 
270 // Returns the default font.
defaultFont() const271 QFont QsciLexer::defaultFont() const
272 {
273     return defFont;
274 }
275 
276 
277 // Sets the default font.
setDefaultFont(const QFont & f)278 void QsciLexer::setDefaultFont(const QFont &f)
279 {
280     defFont = f;
281 }
282 
283 
284 // Returns the default text colour for a style.
defaultColor(int) const285 QColor QsciLexer::defaultColor(int) const
286 {
287     return defaultColor();
288 }
289 
290 
291 // Returns the default text colour.
defaultColor() const292 QColor QsciLexer::defaultColor() const
293 {
294     return defColor;
295 }
296 
297 
298 // Sets the default text colour.
setDefaultColor(const QColor & c)299 void QsciLexer::setDefaultColor(const QColor &c)
300 {
301     defColor = c;
302 }
303 
304 
305 // Returns the default paper colour for a styles.
defaultPaper(int) const306 QColor QsciLexer::defaultPaper(int) const
307 {
308     return defaultPaper();
309 }
310 
311 
312 // Returns the default paper colour.
defaultPaper() const313 QColor QsciLexer::defaultPaper() const
314 {
315     return defPaper;
316 }
317 
318 
319 // Sets the default paper colour.
setDefaultPaper(const QColor & c)320 void QsciLexer::setDefaultPaper(const QColor &c)
321 {
322     defPaper = c;
323 
324     // Normally the default values are only intended to provide defaults when a
325     // lexer is first setup because once a style has been referenced then a
326     // copy of the default is made.  However the default paper is a special
327     // case because there is no other way to set the background colour used
328     // where there is no text.  Therefore we also actively set it.
329     setPaper(c, QsciScintillaBase::STYLE_DEFAULT);
330 }
331 
332 
333 // Read properties from the settings.
readProperties(QSettings &,const QString &)334 bool QsciLexer::readProperties(QSettings &,const QString &)
335 {
336     return true;
337 }
338 
339 
340 // Refresh all properties.
refreshProperties()341 void QsciLexer::refreshProperties()
342 {
343 }
344 
345 
346 // Write properties to the settings.
writeProperties(QSettings &,const QString &) const347 bool QsciLexer::writeProperties(QSettings &,const QString &) const
348 {
349     return true;
350 }
351 
352 
353 // Restore the user settings.
readSettings(QSettings & qs,const char * prefix)354 bool QsciLexer::readSettings(QSettings &qs,const char *prefix)
355 {
356     bool ok, flag, rc = true;
357     int num;
358     QString key, full_key;
359     QStringList fdesc;
360 
361     setStyleDefaults();
362 
363     // Read the styles.
364     for (int i = 0; i <= QsciScintillaBase::STYLE_MAX; ++i)
365     {
366         // Ignore invalid styles.
367         if (description(i).isEmpty())
368             continue;
369 
370         key.sprintf("%s/%s/style%d/",prefix,language(),i);
371 
372         // Read the foreground colour.
373         full_key = key + "color";
374 
375         ok = qs.contains(full_key);
376         num = qs.value(full_key).toInt();
377 
378         if (ok)
379             setColor(QColor((num >> 16) & 0xff, (num >> 8) & 0xff, num & 0xff), i);
380         else
381             rc = false;
382 
383         // Read the end-of-line fill.
384         full_key = key + "eolfill";
385 
386         ok = qs.contains(full_key);
387         flag = qs.value(full_key, false).toBool();
388 
389         if (ok)
390             setEolFill(flag, i);
391         else
392             rc = false;
393 
394         // Read the font.  First try the deprecated format that uses an integer
395         // point size.
396         full_key = key + "font";
397 
398         ok = qs.contains(full_key);
399         fdesc = qs.value(full_key).toStringList();
400 
401         if (ok && fdesc.count() == 5)
402         {
403             QFont f;
404 
405             f.setFamily(fdesc[0]);
406             f.setPointSize(fdesc[1].toInt());
407             f.setBold(fdesc[2].toInt());
408             f.setItalic(fdesc[3].toInt());
409             f.setUnderline(fdesc[4].toInt());
410 
411             setFont(f, i);
412         }
413         else
414             rc = false;
415 
416         // Now try the newer font format that uses a floating point point size.
417         // It is not an error if it doesn't exist.
418         full_key = key + "font2";
419 
420         ok = qs.contains(full_key);
421         fdesc = qs.value(full_key).toStringList();
422 
423         if (ok)
424         {
425             // Allow for future versions with more fields.
426             if (fdesc.count() >= 5)
427             {
428                 QFont f;
429 
430                 f.setFamily(fdesc[0]);
431                 f.setPointSizeF(fdesc[1].toDouble());
432                 f.setBold(fdesc[2].toInt());
433                 f.setItalic(fdesc[3].toInt());
434                 f.setUnderline(fdesc[4].toInt());
435 
436                 setFont(f, i);
437             }
438             else
439             {
440                 rc = false;
441             }
442         }
443 
444         // Read the background colour.
445         full_key = key + "paper";
446 
447         ok = qs.contains(full_key);
448         num = qs.value(full_key).toInt();
449 
450         if (ok)
451             setPaper(QColor((num >> 16) & 0xff, (num >> 8) & 0xff, num & 0xff), i);
452         else
453             rc = false;
454     }
455 
456     // Read the properties.
457     key.sprintf("%s/%s/properties/",prefix,language());
458 
459     if (!readProperties(qs,key))
460         rc = false;
461 
462     refreshProperties();
463 
464     // Read the rest.
465     key.sprintf("%s/%s/",prefix,language());
466 
467     // Read the default foreground colour.
468     full_key = key + "defaultcolor";
469 
470     ok = qs.contains(full_key);
471     num = qs.value(full_key).toInt();
472 
473     if (ok)
474         setDefaultColor(QColor((num >> 16) & 0xff, (num >> 8) & 0xff, num & 0xff));
475     else
476         rc = false;
477 
478     // Read the default background colour.
479     full_key = key + "defaultpaper";
480 
481     ok = qs.contains(full_key);
482     num = qs.value(full_key).toInt();
483 
484     if (ok)
485         setDefaultPaper(QColor((num >> 16) & 0xff, (num >> 8) & 0xff, num & 0xff));
486     else
487         rc = false;
488 
489     // Read the default font.  First try the deprecated format that uses an
490     // integer point size.
491     full_key = key + "defaultfont";
492 
493     ok = qs.contains(full_key);
494     fdesc = qs.value(full_key).toStringList();
495 
496     if (ok && fdesc.count() == 5)
497     {
498         QFont f;
499 
500         f.setFamily(fdesc[0]);
501         f.setPointSize(fdesc[1].toInt());
502         f.setBold(fdesc[2].toInt());
503         f.setItalic(fdesc[3].toInt());
504         f.setUnderline(fdesc[4].toInt());
505 
506         setDefaultFont(f);
507     }
508     else
509         rc = false;
510 
511     // Now try the newer font format that uses a floating point point size.  It
512     // is not an error if it doesn't exist.
513     full_key = key + "defaultfont2";
514 
515     ok = qs.contains(full_key);
516     fdesc = qs.value(full_key).toStringList();
517 
518     if (ok)
519     {
520         // Allow for future versions with more fields.
521         if (fdesc.count() >= 5)
522         {
523             QFont f;
524 
525             f.setFamily(fdesc[0]);
526             f.setPointSizeF(fdesc[1].toDouble());
527             f.setBold(fdesc[2].toInt());
528             f.setItalic(fdesc[3].toInt());
529             f.setUnderline(fdesc[4].toInt());
530 
531             setDefaultFont(f);
532         }
533         else
534         {
535             rc = false;
536         }
537     }
538 
539     full_key = key + "autoindentstyle";
540 
541     ok = qs.contains(full_key);
542     num = qs.value(full_key).toInt();
543 
544     if (ok)
545         setAutoIndentStyle(num);
546     else
547         rc = false;
548 
549     return rc;
550 }
551 
552 
553 // Save the user settings.
writeSettings(QSettings & qs,const char * prefix) const554 bool QsciLexer::writeSettings(QSettings &qs,const char *prefix) const
555 {
556     bool rc = true;
557     QString key, fmt("%1");
558     int num;
559     QStringList fdesc;
560 
561     setStyleDefaults();
562 
563     // Write the styles.
564     for (int i = 0; i <= QsciScintillaBase::STYLE_MAX; ++i)
565     {
566         // Ignore invalid styles.
567         if (description(i).isEmpty())
568             continue;
569 
570         QColor c;
571 
572         key.sprintf("%s/%s/style%d/",prefix,language(),i);
573 
574         // Write the foreground colour.
575         c = color(i);
576         num = (c.red() << 16) | (c.green() << 8) | c.blue();
577 
578         qs.setValue(key + "color", num);
579 
580         // Write the end-of-line fill.
581         qs.setValue(key + "eolfill", eolFill(i));
582 
583         // Write the font using the deprecated format.
584         QFont f = font(i);
585 
586         fdesc.clear();
587         fdesc += f.family();
588         fdesc += fmt.arg(f.pointSize());
589 
590         // The casts are for Borland.
591         fdesc += fmt.arg((int)f.bold());
592         fdesc += fmt.arg((int)f.italic());
593         fdesc += fmt.arg((int)f.underline());
594 
595         qs.setValue(key + "font", fdesc);
596 
597         // Write the font using the newer format.
598         fdesc[1] = fmt.arg(f.pointSizeF());
599 
600         qs.setValue(key + "font2", fdesc);
601 
602         // Write the background colour.
603         c = paper(i);
604         num = (c.red() << 16) | (c.green() << 8) | c.blue();
605 
606         qs.setValue(key + "paper", num);
607     }
608 
609     // Write the properties.
610     key.sprintf("%s/%s/properties/",prefix,language());
611 
612     if (!writeProperties(qs,key))
613         rc = false;
614 
615     // Write the rest.
616     key.sprintf("%s/%s/",prefix,language());
617 
618     // Write the default foreground colour.
619     num = (defColor.red() << 16) | (defColor.green() << 8) | defColor.blue();
620 
621     qs.setValue(key + "defaultcolor", num);
622 
623     // Write the default background colour.
624     num = (defPaper.red() << 16) | (defPaper.green() << 8) | defPaper.blue();
625 
626     qs.setValue(key + "defaultpaper", num);
627 
628     // Write the default font using the deprecated format.
629     fdesc.clear();
630     fdesc += defFont.family();
631     fdesc += fmt.arg(defFont.pointSize());
632 
633     // The casts are for Borland.
634     fdesc += fmt.arg((int)defFont.bold());
635     fdesc += fmt.arg((int)defFont.italic());
636     fdesc += fmt.arg((int)defFont.underline());
637 
638     qs.setValue(key + "defaultfont", fdesc);
639 
640     // Write the font using the newer format.
641     fdesc[1] = fmt.arg(defFont.pointSizeF());
642 
643     qs.setValue(key + "defaultfont2", fdesc);
644 
645     qs.setValue(key + "autoindentstyle", autoIndStyle);
646 
647     return rc;
648 }
649 
650 
651 // Return the auto-indentation style.
autoIndentStyle()652 int QsciLexer::autoIndentStyle()
653 {
654     // We can't do this in the ctor because we want the virtuals to work.
655     if (autoIndStyle < 0)
656         autoIndStyle = (blockStartKeyword() || blockStart() || blockEnd()) ?
657                     0 : QsciScintilla::AiMaintain;
658 
659     return autoIndStyle;
660 }
661 
662 
663 // Set the auto-indentation style.
setAutoIndentStyle(int autoindentstyle)664 void QsciLexer::setAutoIndentStyle(int autoindentstyle)
665 {
666     autoIndStyle = autoindentstyle;
667 }
668 
669 
670 // Set the foreground colour for a style.
setColor(const QColor & c,int style)671 void QsciLexer::setColor(const QColor &c, int style)
672 {
673     if (style >= 0)
674     {
675         styleData(style).color = c;
676         emit colorChanged(c, style);
677     }
678     else
679         for (int i = 0; i <= QsciScintillaBase::STYLE_MAX; ++i)
680             if (!description(i).isEmpty())
681                 setColor(c, i);
682 }
683 
684 
685 // Set the end-of-line fill for a style.
setEolFill(bool eolfill,int style)686 void QsciLexer::setEolFill(bool eolfill, int style)
687 {
688     if (style >= 0)
689     {
690         styleData(style).eol_fill = eolfill;
691         emit eolFillChanged(eolfill, style);
692     }
693     else
694         for (int i = 0; i <= QsciScintillaBase::STYLE_MAX; ++i)
695             if (!description(i).isEmpty())
696                 setEolFill(eolfill, i);
697 }
698 
699 
700 // Set the font for a style.
setFont(const QFont & f,int style)701 void QsciLexer::setFont(const QFont &f, int style)
702 {
703     if (style >= 0)
704     {
705         styleData(style).font = f;
706         emit fontChanged(f, style);
707     }
708     else
709         for (int i = 0; i <= QsciScintillaBase::STYLE_MAX; ++i)
710             if (!description(i).isEmpty())
711                 setFont(f, i);
712 }
713 
714 
715 // Set the background colour for a style.
setPaper(const QColor & c,int style)716 void QsciLexer::setPaper(const QColor &c, int style)
717 {
718     if (style >= 0)
719     {
720         styleData(style).paper = c;
721         emit paperChanged(c, style);
722     }
723     else
724     {
725         for (int i = 0; i <= QsciScintillaBase::STYLE_MAX; ++i)
726             if (!description(i).isEmpty())
727                 setPaper(c, i);
728 
729         emit paperChanged(c, QsciScintillaBase::STYLE_DEFAULT);
730     }
731 }
732