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