1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
//===========================================
// Lumina-DE source code
// Copyright (c) 2015, Ken Moore
// Available under the 3-clause BSD license
// See the LICENSE file for full details
//===========================================
#ifndef _LUMINA_SYNTAX_HIGHLIGHER_CPP_H
#define _LUMINA_SYNTAX_HIGHLIGHER_CPP_H
#include <QSyntaxHighlighter>
#include <QTextDocument>
#include <QTextCharFormat>
#include <QString>
#include <QSettings>
#include <QDebug>
#include <QDateTime>
#include <QJsonObject>
#include <QPlainTextEdit>
//Simple syntax rules
struct SyntaxRule{
QRegExp pattern; //single-line rule
QRegExp startPattern, endPattern; //multi-line rules
QTextCharFormat format;
};
class SyntaxFile{
private:
QJsonObject metaObj;
QJsonObject formatObj;
QColor colorFromOption(QString opt, QSettings *settings);
public:
QVector<SyntaxRule> rules;
QDateTime lastLoaded;
QString fileLoaded;
SyntaxFile(){}
QString name();
int char_limit();
bool highlight_excess_whitespace();
bool check_spelling();
int tab_length();
void SetupDocument(QPlainTextEdit *editor);
bool supportsFile(QString file); //does this syntax set support the file?
bool supportsFirstLine(QString line); //is the type of file defined by the first line of the file? ("#!/bin/<something>" for instance)
//Main Loading routine (run this before other functions)
bool LoadFile(QString file, QSettings *settings);
//Main function for finding/loading all syntax files
static QList<SyntaxFile> availableFiles(QSettings *settings);
};
class Custom_Syntax : public QSyntaxHighlighter{
Q_OBJECT
private:
QSettings *settings;
SyntaxFile syntax;
public:
Custom_Syntax(QSettings *set, QTextDocument *parent = 0) : QSyntaxHighlighter(parent){
settings = set;
}
~Custom_Syntax(){}
QString loadedRules(){ return syntax.name(); }
static QStringList availableRules(QSettings *settings);
static QStringList knownColors();
static void SetupDefaultColors(QSettings *settings);
static QString ruleForFile(QString filename, QSettings *settings);
static QString ruleForFirstLine(QString line, QSettings *settings);
void loadRules(QString type);
void loadRules(SyntaxFile sfile);
void reloadRules(){
loadRules( syntax.name() );
}
void setupDocument(QPlainTextEdit *edit){ syntax.SetupDocument(edit); } //simple redirect for the function in the currently-loaded rules
protected:
void highlightBlock(const QString &text){
QTextCharFormat defaultFormat;
//qDebug() << "Highlight Block:" << text;
//Now finish any multi-line patterns
int start = 0;
int splitactive = previousBlockState();
if(splitactive>syntax.rules.length()-1){ splitactive = -1; } //just in case
//qDebug() << "split check:" << start << splitactive;
if(splitactive>=0){
//Find the end of the current rule
int end = syntax.rules[splitactive].endPattern.indexIn(text, start);
if(end==-1){
//qDebug() << "Highlight to end of line:" << text << start;
//rule did not finish - apply to all
if(start>0){ setFormat(start-1, text.length()-start+1, syntax.rules[splitactive].format); }
else{ setFormat(start, text.length()-start, syntax.rules[splitactive].format); }
}else{
//Found end point within the same line
//qDebug() << "Highlight to particular point:" << text << start << end;
int len = end-start+syntax.rules[splitactive].endPattern.matchedLength();
if(start>0){ start--; len++; } //need to include the first character as well
setFormat(start, len , syntax.rules[splitactive].format);
start+=len; //move pointer to the end of handled range
splitactive = -1; //done with this rule
}
} //end check for end of pre-existing multi-line block match
setCurrentBlockState(splitactive); //tag this block as continuing as well
//Do all the single-line patterns
for(int i=0; i<syntax.rules.length() && splitactive<0; i++){
if(syntax.rules[i].pattern.isEmpty()){ continue; } //not a single-line rule
QRegExp patt(syntax.rules[i].pattern); //need a copy of the rule's pattern (will be changing it below)
int index = patt.indexIn(text);
if( index<start ){ continue; } //skip this one - falls within a multi-line pattern above
while(index>=0){
int len = patt.matchedLength();
if(format(index)==currentBlock().charFormat()){ setFormat(index, len, syntax.rules[i].format); } //only apply highlighting if not within a section already
index = patt.indexIn(text, index+len); //go to the next match
}
}//end loop over normal (single-line) patterns
//Look for the start of any new split rules
//qDebug() << "Loop over multi-line rules";
for(int i=0; i<syntax.rules.length() && splitactive<0; i++){
//qDebug() << "Check Rule:" << i << syntax.rules[i].startPattern << syntax.rules[i].endPattern;
if(syntax.rules[i].startPattern.isEmpty()){ continue; }
//qDebug() << "Look for start of split rule:" << syntax.rules[i].startPattern << splitactive;
int newstart = syntax.rules[i].startPattern.indexIn(text,start);
if(newstart>=start && (format(newstart) == defaultFormat) ){ //only start multi-line formatting if it is not already contained in a single-line formatting
//qDebug() << "Got Start of split rule:" << start << newstart << text << (format(newstart) != defaultFormat);
splitactive = i;
start = newstart+1;
int end = syntax.rules[splitactive].endPattern.indexIn(text, start);
if(end>0){ //end of multi-line comment in the same block
setFormat(start-1, end-start+1, syntax.rules[splitactive].format);
start = end+1;
}else{
setCurrentBlockState(splitactive);
}
if(start>=text.length()-1 || splitactive>=0){
//qDebug() << "Special case: start now greater than line length";
//Need to apply highlighting to this section too - start matches the end of the line
setFormat(start-1, text.length()-start+1, syntax.rules[splitactive].format);
break; //this goes to the end of the text block
}
}
}
//Now go through and apply any document-wide formatting rules
QTextCharFormat fmt;
fmt.setBackground( QColor( settings->value("colors/bracket-missing").toString() ) );
int max = syntax.char_limit();
if(max >= 0 && ( (text.length()+(text.count("\t")*(syntax.tab_length()-1)) )> max) ) {
//Line longer than it should be - highlight the offending characters
//Need to be careful about where tabs show up in the line
int len = 0;
for(int i=0; i<text.length() and len<=max; i++){
len += (text[i]=='\t') ? syntax.tab_length() : 1;
if(len>max)
setFormat(i, text.length()-i, fmt);
}
}
if(syntax.highlight_excess_whitespace()){
int last = text.length()-1;
while(last>=0 && (text[last]==' ' || text[last]=='\t' ) ){ last--; }
if(last < text.length()-1){
setFormat(last+1, text.length()-1-last, fmt);
}
}
}
};
#endif
|