aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/desktop-utils/lumina-textedit/syntaxSupport.h
blob: d168b70f003b32ba5940e2fc04dd76678851abd3 (plain)
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
//===========================================
//  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;
};
//Complicated/multi-line rules
struct SyntaxRuleSplit{
  QRegExp startPattern, endPattern;
  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();
	int tab_length();

	void SetupDocument(QPlainTextEdit *editor);
	bool supportsFile(QString file); //does this syntax set support the file?

	//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;
	//QString lasttype;
        SyntaxFile syntax;
	//QVector<SyntaxRule> rules;
	//QVector<SyntaxRuleSplit> splitrules;

public:
	Custom_Syntax(QSettings *set, QTextDocument *parent = 0) : QSyntaxHighlighter(parent){
	  settings = set;
	}
	~Custom_Syntax(){}

	static QStringList availableRules(QSettings *settings);
	static QStringList knownColors();
	static void SetupDefaultColors(QSettings *settings);
	static QString ruleForFile(QString filename, QSettings *settings);
	void loadRules(QString type);
	void loadRules(SyntaxFile sfile);

	void reloadRules(){
	  loadRules( syntax.name() );
	}

protected:
	void highlightBlock(const QString &text){
          //qDebug() << "Highlight Block:" << text;
	  //Now look for any multi-line patterns (starting/continuing/ending)
	  int start = 0;
	  int splitactive = previousBlockState();
	  if(splitactive>syntax.rules.length()-1){ splitactive = -1; } //just in case

	  while(start>=0 && start<=text.length()-1){
	    //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); }
		break; //stop looking for more multi-line patterns
	      }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 match
	    //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){
                //qDebug() << "Got Start of split rule:" << start << newstart << text;
		splitactive = i;
		start = newstart+1;
                if(start>=text.length()-1){
                  //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);
                }
	      }
	    }
	    if(splitactive<0){  break; } //no other rules found - go ahead and exit the loop
          } //end scan over line length and multi-line formats

	  setCurrentBlockState(splitactive);
          //Do all the single-line patterns
	  for(int i=0; i<syntax.rules.length(); 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(splitactive>=0 || 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

	  //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(); i++){
	      if(text[i]=='\t'){ len += syntax.tab_length(); }
	      else{ len++; }
              if(len>max){ setFormat(i, text.length()-i, fmt); break; }
            }
	  }
	  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
bgstack15