PageRenderTime 84ms CodeModel.GetById 40ms app.highlight 25ms RepoModel.GetById 15ms app.codeStats 0ms

/src/armed/ScriptHighlighter.cpp

https://bitbucket.org/MnemonicWME/wme
C++ | 281 lines | 230 code | 37 blank | 14 comment | 76 complexity | dd4fbfcec0abe426f452fa1b5367d583 MD5 | raw file
  1// This file is part of Wintermute Engine
  2// For conditions of distribution and use, see copyright notice in license.txt
  3
  4#include "StdAfx.h"

  5#include "ScriptHighlighter.h"

  6
  7
  8namespace Armed
  9{
 10
 11
 12//////////////////////////////////////////////////////////////////////////
 13ScriptHighlighter::ScriptHighlighter(QTextDocument* document) : QSyntaxHighlighter(document)
 14{
 15	m_Formats[FORMAT_NUMBER].setForeground(QColor("#CC0000"));
 16	m_Formats[FORMAT_COMMENT].setForeground(QColor("#007F00"));
 17	m_Formats[FORMAT_STRING].setForeground(QColor("gray"));
 18	m_Formats[FORMAT_ESCAPE].setForeground(QColor("black"));
 19	m_Formats[FORMAT_KEYWORD].setForeground(QColor("blue"));
 20	m_Formats[FORMAT_FUNCTION].setForeground(QColor("maroon"));
 21	m_Formats[FORMAT_PROPERTY].setForeground(QColor("#2B91AF"));
 22	m_Formats[FORMAT_PREPROCESSOR].setForeground(QColor("purple"));
 23
 24	m_Keywords << "global";
 25	m_Keywords << "var";
 26	m_Keywords << "and";
 27	m_Keywords << "or";
 28	m_Keywords << "if";
 29	m_Keywords << "else";
 30	m_Keywords << "while";
 31	m_Keywords << "for";
 32	m_Keywords << "in";
 33	m_Keywords << "do";
 34	m_Keywords << "break";
 35	m_Keywords << "continue";
 36	m_Keywords << "null";
 37	m_Keywords << "return";
 38	m_Keywords << "function";
 39	m_Keywords << "method";
 40	m_Keywords << "new";
 41	m_Keywords << "true";
 42	m_Keywords << "false";
 43	m_Keywords << "switch";
 44	m_Keywords << "case";
 45	m_Keywords << "default";
 46	m_Keywords << "void";
 47	m_Keywords << "delete";
 48	m_Keywords << "this";
 49	m_Keywords << "typeof";
 50	m_Keywords << "with";
 51	m_Keywords << "reserved";
 52
 53	m_Preprocessor << "#include";
 54
 55
 56	int comment = 15;
 57	int bracket = 21;
 58	int state = 5;
 59}
 60
 61//////////////////////////////////////////////////////////////////////////
 62ScriptHighlighter::~ScriptHighlighter()
 63{
 64
 65}
 66
 67//////////////////////////////////////////////////////////////////////////
 68void ScriptHighlighter::highlightBlock(const QString& text)
 69{
 70	// states
 71	enum
 72	{
 73		Initial = 0,
 74		Number = 1,
 75		Identifier = 2,
 76		String = 3,
 77		Comment = 4,
 78	};
 79
 80	//restore previous state
 81	int state, bracketNest, commentNest;
 82	DecodeState(previousBlockState(), state, bracketNest, commentNest);
 83
 84
 85	int startPos = 0;
 86	int i = 0;
 87	while (i <= text.length())
 88	{
 89		QChar ch = (i < text.length()) ? text.at(i) : QChar();
 90		QChar next = (i < text.length() - 1) ? text.at(i + 1) : QChar();
 91		QChar prev = (i > 0) ? text.at(i - 1) : QChar();
 92
 93		switch (state)
 94		{
 95		case Initial:
 96			startPos = i;
 97			if (ch.isSpace())
 98			{
 99				i++;
100			}
101			else if (ch.isDigit())
102			{
103				i++;
104				state = Number;
105			}
106			else if (ch.isLetter() || ch == '_'  || ch == '#')
107			{
108				i++;
109				state = Identifier;
110			}
111			else if (ch == '\'' || ch == '\"')
112			{
113				setFormat(i, 1, m_Formats[FORMAT_STRING]);
114				i++;
115				state = String;
116			}
117			else if (ch == '/' && next == '*')
118			{
119				i += 2;
120				state = Comment;
121				commentNest++;
122			}
123			else if (ch == '/' && next == '/')
124			{
125				i = text.length();
126				setFormat(startPos, text.length() - startPos, m_Formats[FORMAT_COMMENT]);
127			}
128			else
129			{
130				if (!QString("(){}[]").contains(ch))
131					setFormat(startPos, 1, m_Formats[FORMAT_SYMBOL]);
132
133				if (ch =='{' || ch == '}')
134				{
135					//bracketPositions += i;
136					if (ch == '{')
137						bracketNest++;
138					else
139						bracketNest--;
140				}
141				i++;
142				state = Initial;
143			}
144			break;
145
146		case Number:
147			if (ch.isSpace() || !ch.isDigit())
148			{
149				setFormat(startPos, i - startPos, m_Formats[FORMAT_NUMBER]);
150				state = Initial;
151			}
152			else i++;
153			break;
154
155		case Identifier:
156			if (ch.isSpace() || !(ch.isDigit() || ch.isLetter() || ch == '_'))
157			{
158				bool isFunctionCall = (GetNextNonWhite(text, i) == '(');
159
160				QString token = text.mid(startPos, i - startPos).trimmed();
161
162				if (m_Keywords.contains(token))
163					setFormat(startPos, i - startPos, m_Formats[FORMAT_KEYWORD]);
164				else if (m_Preprocessor.contains(token))
165					setFormat(startPos, i - startPos, m_Formats[FORMAT_PREPROCESSOR]);
166				else
167				{
168					// todo known attributes and functions
169					if (isFunctionCall)
170					{
171						setFormat(startPos, i - startPos, m_Formats[FORMAT_FUNCTION]);
172					}
173				}
174
175
176				state = Initial;
177			}
178			else i++;
179			break;
180
181		case String:
182			if (ch == text.at(startPos))
183			{				
184				if (prev != '\\')
185				{
186					setFormat(i, 1, m_Formats[FORMAT_STRING]);
187					i++;					
188					state = Initial;
189				}
190				else
191				{
192					setFormat(i - 1, 2, m_Formats[FORMAT_ESCAPE]);
193					i++;
194				}
195			}
196			else if (prev == '\\' && ch != '\\')
197			{
198				setFormat(i - 1, 2, m_Formats[FORMAT_ESCAPE]);
199				i++;				
200			}
201			else
202			{
203				setFormat(i, 1, m_Formats[FORMAT_STRING]);
204				i++;
205			}
206			break;
207
208		case Comment:
209			if (ch == '*' && next == '/')
210			{
211				i += 2;
212				setFormat(startPos, i - startPos, m_Formats[FORMAT_COMMENT]);
213				commentNest--;
214				if (commentNest <= 0) state = Initial;
215			}
216			else if (ch == '/' && next == '*')
217			{
218				i += 2;
219				setFormat(startPos, i - startPos, m_Formats[FORMAT_COMMENT]);
220				commentNest++;
221			}
222			else i++;
223			break;
224
225		default:
226			state = Initial;
227			break;
228
229		}
230	}
231
232	if (state == Comment)
233		setFormat(startPos, text.length(), m_Formats[FORMAT_COMMENT]);
234	else
235		state = Initial;
236
237
238	// store current state
239	setCurrentBlockState(EncodeState(state, bracketNest, commentNest));
240}
241
242//////////////////////////////////////////////////////////////////////////
243void ScriptHighlighter::DecodeState(int encodedVal, int& state, int& bracketNest, int& commentNest)
244{
245	if (encodedVal < 0)
246	{
247		state = 0;
248		bracketNest = 0;
249		commentNest = 0;
250		return;
251	}
252
253	commentNest = encodedVal >> 12;
254	bracketNest = encodedVal >> 4 & 255;
255	state = encodedVal & 15;
256}
257
258//////////////////////////////////////////////////////////////////////////
259int ScriptHighlighter::EncodeState(int state, int bracketNest, int commentNest)
260{
261	// 000000000000CCCCCCCCBBBBBBBBSSSS
262	int encodedVal;
263    encodedVal = (state & 15) | (bracketNest << 4) | (commentNest << 12);
264
265	return encodedVal;
266}
267
268//////////////////////////////////////////////////////////////////////////
269QChar ScriptHighlighter::GetNextNonWhite(const QString& text, int pos)
270{
271	for (int i = pos; i < text.length(); i++)
272	{
273		QChar ret = text.at(i);
274		QString ch = ret;
275		if (ret != ' ' && ret != '\t' && ret != '\n') return ret;
276	}
277	return QChar();	
278}
279
280
281} // namespace Armed