1 /*
2 	Actiona
3 	Copyright (C) 2005 Jonathan Mercier-Ganady
4 
5 	Actiona is free software: you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation, either version 3 of the License, or
8 	(at your option) any later version.
9 
10 	Actiona is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18 	Contact : jmgr@jmgr.info
19 */
20 // This file uses some code lines from the Ofi Labs X2 project
21 // Copyright (C) 2010 Ariya Hidayat <ariya.hidayat@gmail.com>
22 // Licensed under GNU/GPLv3
23 
24 #include "codehighlighter.h"
25 #include "keywords.h"
26 
27 #include <QFile>
28 #include <QDebug>
29 
30 namespace ActionTools
31 {
CodeHighlighter(QTextDocument * parent)32 	CodeHighlighter::CodeHighlighter(QTextDocument *parent)
33 		: QSyntaxHighlighter(parent)
34 	{
35 		mFormats[KeywordFormat].setForeground(Qt::darkBlue);
36 		mFormats[KeywordFormat].setFontWeight(QFont::Bold);
37 
38 		mFormats[ReservedFormat].setForeground(Qt::red);
39 		mFormats[ReservedFormat].setFontWeight(QFont::Bold);
40 		mFormats[ReservedFormat].setFontStrikeOut(true);
41 
42 		mFormats[CodeObjectsFormat].setForeground(Qt::darkBlue);
43 		mFormats[CodeObjectsFormat].setFontWeight(QFont::Bold);
44 
45 		mFormats[OperatorFormat].setForeground(Qt::red);
46 
47 		mFormats[NumberFormat].setForeground(Qt::darkMagenta);
48 
49 		mFormats[CommentFormat].setForeground(Qt::darkGreen);
50 
51 		mFormats[StringFormat].setForeground(Qt::darkRed);
52 
53         for(const QString &keyword: usedKeywords)
54 			mUsedKeywords.insert(keyword);
55 
56         for(const QString &keyword: reservedKeywords)
57 			mReservedKeywords.insert(keyword);
58 	}
59 
highlightBlock(const QString & text)60 	void CodeHighlighter::highlightBlock(const QString &text)
61 	{
62 		// parsing state
63 		enum
64 		{
65 			Start = 0,
66 			Number = 1,
67 			Identifier = 2,
68 			String = 3,
69 			Comment = 4,
70 			Regex = 5
71 		};
72 
73 		QList<int> bracketPositions;
74 
75 		int blockState = previousBlockState();
76 		int bracketLevel = blockState >> 4;
77 		int state = blockState & 15;
78 		if (blockState < 0)
79 		{
80 			bracketLevel = 0;
81 			state = Start;
82 		}
83 
84 		int start = 0;
85 		int i = 0;
86 		while (i <= text.length())
87 		{
88 			QChar ch = (i < text.length()) ? text.at(i) : QChar();
89 			QChar next = (i < text.length() - 1) ? text.at(i + 1) : QChar();
90 
91 			switch (state)
92 			{
93 
94 			case Start:
95 				start = i;
96 				if (ch.isSpace())
97 				{
98 					++i;
99 				}
100 				else if (ch.isDigit())
101 				{
102 					++i;
103 					state = Number;
104 				}
105 				else if (ch.isLetter() || ch == QLatin1Char('_'))
106 				{
107 					++i;
108 					state = Identifier;
109 				}
110 				else if (ch == QLatin1Char('\'') || ch == QLatin1Char('\"'))
111 				{
112 					++i;
113 					state = String;
114 				}
115 				else if (ch == QLatin1Char('/') && next == QLatin1Char('*'))
116 				{
117 					++i;
118 					++i;
119 					state = Comment;
120 				}
121 				else if (ch == QLatin1Char('/') && next == QLatin1Char('/'))
122 				{
123 					i = text.length();
124 					setFormat(start, text.length(), mFormats[CommentFormat]);
125 				}
126 				else if (ch == QLatin1Char('/') && next != QLatin1Char('*'))
127 				{
128 					++i;
129 					state = Regex;
130 				}
131 				else
132 				{
133 					if (!QStringLiteral("(){}[]").contains(ch))
134 						setFormat(start, 1, mFormats[OperatorFormat]);
135 					if (ch == QLatin1Char('{') || ch == QLatin1Char('}'))
136 					{
137 						bracketPositions += i;
138 						if (ch == QLatin1Char('{'))
139 							bracketLevel++;
140 						else
141 							bracketLevel--;
142 					}
143 					++i;
144 					state = Start;
145 				}
146 				break;
147 
148 			case Number:
149 				if (ch.isSpace() || !ch.isDigit())
150 				{
151 					setFormat(start, i - start, mFormats[NumberFormat]);
152 					state = Start;
153 				}
154 				else
155 					++i;
156 				break;
157 
158 			case Identifier:
159 				if (ch.isSpace() || !(ch.isDigit() || ch.isLetter() || ch == QLatin1Char('_')))
160 				{
161 					QString token = text.mid(start, i - start).trimmed();
162 					if (mUsedKeywords.contains(token))
163 						setFormat(start, i - start, mFormats[KeywordFormat]);
164 					else if (mReservedKeywords.contains(token))
165 						setFormat(start, i - start, mFormats[ReservedFormat]);
166 					else if (mCodeObjects.contains(token))
167 						setFormat(start, i - start, mFormats[CodeObjectsFormat]);
168 					state = Start;
169 				}
170 				else
171 					++i;
172 				break;
173 
174 			case String:
175 				if (ch == text.at(start))
176 				{
177 					QChar prev = (i > 0) ? text.at(i - 1) : QChar();
178 					if (prev != QLatin1Char('\\'))
179 					{
180 						++i;
181 						setFormat(start, i - start, mFormats[StringFormat]);
182 						state = Start;
183 					}
184 					else
185 						++i;
186 				}
187 				else
188 					++i;
189 				break;
190 
191 			case Comment:
192 				if (ch == QLatin1Char('*') && next == QLatin1Char('/'))
193 				{
194 					++i;
195 					++i;
196 					setFormat(start, i - start, mFormats[CommentFormat]);
197 					state = Start;
198 				}
199 				else
200 					++i;
201 				break;
202 
203 			case Regex:
204 				if (ch == QLatin1Char('/'))
205 				{
206 					QChar prev = (i > 0) ? text.at(i - 1) : QChar();
207 					if (prev != QLatin1Char('\\'))
208 					{
209 						++i;
210 						setFormat(start, i - start, mFormats[StringFormat]);
211 						state = Start;
212 					}
213 					else
214 						++i;
215 				}
216 				else
217 					++i;
218 				break;
219 
220 			default:
221 				state = Start;
222 				break;
223 			}
224 		}
225 
226 		if (state == Comment)
227 			setFormat(start, text.length(), mFormats[CommentFormat]);
228 	}
229 
addCodeObject(const QString & name)230 	void CodeHighlighter::addCodeObject(const QString &name)
231 	{
232 		mCodeObjects.insert(name);
233 	}
234 }
235