PageRenderTime 43ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/armed/ScriptHighlighter.cpp

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