/QScintilla/src/LexYAML.cpp

http://qtluapad.googlecode.com/ · C++ · 314 lines · 240 code · 39 blank · 35 comment · 106 complexity · feb09b2547ba544cf4893948589920e8 MD5 · raw file

  1. // Scintilla source code edit control
  2. /** @file LexYAML.cxx
  3. ** Lexer for YAML.
  4. **/
  5. // Copyright 2003- by Sean O'Dell <sean@celsoft.com>
  6. // The License.txt file describes the conditions under which this software may be distributed.
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <ctype.h>
  10. #include <stdio.h>
  11. #include <stdarg.h>
  12. #include "Platform.h"
  13. #include "PropSet.h"
  14. #include "Accessor.h"
  15. #include "StyleContext.h"
  16. #include "KeyWords.h"
  17. #include "Scintilla.h"
  18. #include "SciLexer.h"
  19. #ifdef SCI_NAMESPACE
  20. using namespace Scintilla;
  21. #endif
  22. static const char * const yamlWordListDesc[] = {
  23. "Keywords",
  24. 0
  25. };
  26. static inline bool AtEOL(Accessor &styler, unsigned int i) {
  27. return (styler[i] == '\n') ||
  28. ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n'));
  29. }
  30. static unsigned int SpaceCount(char* lineBuffer) {
  31. if (lineBuffer == NULL)
  32. return 0;
  33. char* headBuffer = lineBuffer;
  34. while (*headBuffer == ' ')
  35. headBuffer++;
  36. return headBuffer - lineBuffer;
  37. }
  38. #define YAML_STATE_BITSIZE 16
  39. #define YAML_STATE_MASK (0xFFFF0000)
  40. #define YAML_STATE_DOCUMENT (1 << YAML_STATE_BITSIZE)
  41. #define YAML_STATE_VALUE (2 << YAML_STATE_BITSIZE)
  42. #define YAML_STATE_COMMENT (3 << YAML_STATE_BITSIZE)
  43. #define YAML_STATE_TEXT_PARENT (4 << YAML_STATE_BITSIZE)
  44. #define YAML_STATE_TEXT (5 << YAML_STATE_BITSIZE)
  45. static void ColouriseYAMLLine(
  46. char *lineBuffer,
  47. unsigned int currentLine,
  48. unsigned int lengthLine,
  49. unsigned int startLine,
  50. unsigned int endPos,
  51. WordList &keywords,
  52. Accessor &styler) {
  53. unsigned int i = 0;
  54. bool bInQuotes = false;
  55. unsigned int indentAmount = SpaceCount(lineBuffer);
  56. if (currentLine > 0) {
  57. int parentLineState = styler.GetLineState(currentLine - 1);
  58. if ((parentLineState&YAML_STATE_MASK) == YAML_STATE_TEXT || (parentLineState&YAML_STATE_MASK) == YAML_STATE_TEXT_PARENT) {
  59. unsigned int parentIndentAmount = parentLineState&(~YAML_STATE_MASK);
  60. if (indentAmount > parentIndentAmount) {
  61. styler.SetLineState(currentLine, YAML_STATE_TEXT | parentIndentAmount);
  62. styler.ColourTo(endPos, SCE_YAML_TEXT);
  63. return;
  64. }
  65. }
  66. }
  67. styler.SetLineState(currentLine, 0);
  68. if (strncmp(lineBuffer, "---", 3) == 0) { // Document marker
  69. styler.SetLineState(currentLine, YAML_STATE_DOCUMENT);
  70. styler.ColourTo(endPos, SCE_YAML_DOCUMENT);
  71. return;
  72. }
  73. // Skip initial spaces
  74. while ((i < lengthLine) && lineBuffer[i] == ' ') { // YAML always uses space, never TABS or anything else
  75. i++;
  76. }
  77. if (lineBuffer[i] == '\t') { // if we skipped all spaces, and we are NOT inside a text block, this is wrong
  78. styler.ColourTo(endPos, SCE_YAML_ERROR);
  79. return;
  80. }
  81. if (lineBuffer[i] == '#') { // Comment
  82. styler.SetLineState(currentLine, YAML_STATE_COMMENT);
  83. styler.ColourTo(endPos, SCE_YAML_COMMENT);
  84. return;
  85. }
  86. while (i < lengthLine) {
  87. if (lineBuffer[i] == '\'' || lineBuffer[i] == '\"') {
  88. bInQuotes = !bInQuotes;
  89. } else if (lineBuffer[i] == ':' && !bInQuotes) {
  90. styler.ColourTo(startLine + i - 1, SCE_YAML_IDENTIFIER);
  91. styler.ColourTo(startLine + i, SCE_YAML_OPERATOR);
  92. // Non-folding scalar
  93. i++;
  94. while ((i < lengthLine) && isspacechar(lineBuffer[i]))
  95. i++;
  96. unsigned int endValue = lengthLine - 1;
  97. while ((endValue >= i) && isspacechar(lineBuffer[endValue]))
  98. endValue--;
  99. lineBuffer[endValue + 1] = '\0';
  100. if (lineBuffer[i] == '|' || lineBuffer[i] == '>') {
  101. i++;
  102. if (lineBuffer[i] == '+' || lineBuffer[i] == '-')
  103. i++;
  104. while ((i < lengthLine) && isspacechar(lineBuffer[i]))
  105. i++;
  106. if (lineBuffer[i] == '\0') {
  107. styler.SetLineState(currentLine, YAML_STATE_TEXT_PARENT | indentAmount);
  108. styler.ColourTo(endPos, SCE_YAML_DEFAULT);
  109. return;
  110. } else if (lineBuffer[i] == '#') {
  111. styler.SetLineState(currentLine, YAML_STATE_TEXT_PARENT | indentAmount);
  112. styler.ColourTo(startLine + i - 1, SCE_YAML_DEFAULT);
  113. styler.ColourTo(endPos, SCE_YAML_COMMENT);
  114. return;
  115. } else {
  116. styler.ColourTo(endPos, SCE_YAML_ERROR);
  117. return;
  118. }
  119. } else if (lineBuffer[i] == '#') {
  120. styler.ColourTo(startLine + i - 1, SCE_YAML_DEFAULT);
  121. styler.ColourTo(endPos, SCE_YAML_COMMENT);
  122. return;
  123. }
  124. styler.SetLineState(currentLine, YAML_STATE_VALUE);
  125. if (lineBuffer[i] == '&' || lineBuffer[i] == '*') {
  126. styler.ColourTo(endPos, SCE_YAML_REFERENCE);
  127. return;
  128. }
  129. if (keywords.InList(&lineBuffer[i])) { // Convertible value (true/false, etc.)
  130. styler.ColourTo(endPos, SCE_YAML_KEYWORD);
  131. return;
  132. } else {
  133. unsigned int i2 = i;
  134. while ((i < lengthLine) && lineBuffer[i]) {
  135. if (!(isascii(lineBuffer[i]) && isdigit(lineBuffer[i])) && lineBuffer[i] != '-' && lineBuffer[i] != '.' && lineBuffer[i] != ',') {
  136. styler.ColourTo(endPos, SCE_YAML_DEFAULT);
  137. return;
  138. }
  139. i++;
  140. }
  141. if (i > i2) {
  142. styler.ColourTo(endPos, SCE_YAML_NUMBER);
  143. return;
  144. }
  145. }
  146. break; // shouldn't get here, but just in case, the rest of the line is coloured the default
  147. }
  148. i++;
  149. }
  150. styler.ColourTo(endPos, SCE_YAML_DEFAULT);
  151. }
  152. static void ColouriseYAMLDoc(unsigned int startPos, int length, int, WordList *keywordLists[], Accessor &styler) {
  153. char lineBuffer[1024];
  154. styler.StartAt(startPos);
  155. styler.StartSegment(startPos);
  156. unsigned int linePos = 0;
  157. unsigned int startLine = startPos;
  158. unsigned int endPos = startPos + length;
  159. unsigned int maxPos = styler.Length();
  160. unsigned int lineCurrent = styler.GetLine(startPos);
  161. for (unsigned int i = startPos; i < maxPos && i < endPos; i++) {
  162. lineBuffer[linePos++] = styler[i];
  163. if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
  164. // End of line (or of line buffer) met, colourise it
  165. lineBuffer[linePos] = '\0';
  166. ColouriseYAMLLine(lineBuffer, lineCurrent, linePos, startLine, i, *keywordLists[0], styler);
  167. linePos = 0;
  168. startLine = i + 1;
  169. lineCurrent++;
  170. }
  171. }
  172. if (linePos > 0) { // Last line does not have ending characters
  173. ColouriseYAMLLine(lineBuffer, lineCurrent, linePos, startLine, startPos + length - 1, *keywordLists[0], styler);
  174. }
  175. }
  176. static bool IsCommentLine(int line, Accessor &styler) {
  177. int pos = styler.LineStart(line);
  178. if (styler[pos] == '#')
  179. return true;
  180. return false;
  181. }
  182. static void FoldYAMLDoc(unsigned int startPos, int length, int /*initStyle - unused*/,
  183. WordList *[], Accessor &styler) {
  184. const int maxPos = startPos + length;
  185. const int maxLines = styler.GetLine(maxPos - 1); // Requested last line
  186. const int docLines = styler.GetLine(styler.Length() - 1); // Available last line
  187. const bool foldComment = styler.GetPropertyInt("fold.comment.yaml") != 0;
  188. // Backtrack to previous non-blank line so we can determine indent level
  189. // for any white space lines
  190. // and so we can fix any preceding fold level (which is why we go back
  191. // at least one line in all cases)
  192. int spaceFlags = 0;
  193. int lineCurrent = styler.GetLine(startPos);
  194. int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);
  195. while (lineCurrent > 0) {
  196. lineCurrent--;
  197. indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);
  198. if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG) &&
  199. (!IsCommentLine(lineCurrent, styler)))
  200. break;
  201. }
  202. int indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK;
  203. // Set up initial loop state
  204. int prevComment = 0;
  205. if (lineCurrent >= 1)
  206. prevComment = foldComment && IsCommentLine(lineCurrent - 1, styler);
  207. // Process all characters to end of requested range
  208. // or comment that hangs over the end of the range. Cap processing in all cases
  209. // to end of document (in case of unclosed comment at end).
  210. while ((lineCurrent <= docLines) && ((lineCurrent <= maxLines) || prevComment)) {
  211. // Gather info
  212. int lev = indentCurrent;
  213. int lineNext = lineCurrent + 1;
  214. int indentNext = indentCurrent;
  215. if (lineNext <= docLines) {
  216. // Information about next line is only available if not at end of document
  217. indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);
  218. }
  219. const int comment = foldComment && IsCommentLine(lineCurrent, styler);
  220. const int comment_start = (comment && !prevComment && (lineNext <= docLines) &&
  221. IsCommentLine(lineNext, styler) && (lev > SC_FOLDLEVELBASE));
  222. const int comment_continue = (comment && prevComment);
  223. if (!comment)
  224. indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK;
  225. if (indentNext & SC_FOLDLEVELWHITEFLAG)
  226. indentNext = SC_FOLDLEVELWHITEFLAG | indentCurrentLevel;
  227. if (comment_start) {
  228. // Place fold point at start of a block of comments
  229. lev |= SC_FOLDLEVELHEADERFLAG;
  230. } else if (comment_continue) {
  231. // Add level to rest of lines in the block
  232. lev = lev + 1;
  233. }
  234. // Skip past any blank lines for next indent level info; we skip also
  235. // comments (all comments, not just those starting in column 0)
  236. // which effectively folds them into surrounding code rather
  237. // than screwing up folding.
  238. while ((lineNext < docLines) &&
  239. ((indentNext & SC_FOLDLEVELWHITEFLAG) ||
  240. (lineNext <= docLines && IsCommentLine(lineNext, styler)))) {
  241. lineNext++;
  242. indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);
  243. }
  244. const int levelAfterComments = indentNext & SC_FOLDLEVELNUMBERMASK;
  245. const int levelBeforeComments = Platform::Maximum(indentCurrentLevel,levelAfterComments);
  246. // Now set all the indent levels on the lines we skipped
  247. // Do this from end to start. Once we encounter one line
  248. // which is indented more than the line after the end of
  249. // the comment-block, use the level of the block before
  250. int skipLine = lineNext;
  251. int skipLevel = levelAfterComments;
  252. while (--skipLine > lineCurrent) {
  253. int skipLineIndent = styler.IndentAmount(skipLine, &spaceFlags, NULL);
  254. if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > levelAfterComments)
  255. skipLevel = levelBeforeComments;
  256. int whiteFlag = skipLineIndent & SC_FOLDLEVELWHITEFLAG;
  257. styler.SetLevel(skipLine, skipLevel | whiteFlag);
  258. }
  259. // Set fold header on non-comment line
  260. if (!comment && !(indentCurrent & SC_FOLDLEVELWHITEFLAG) ) {
  261. if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK))
  262. lev |= SC_FOLDLEVELHEADERFLAG;
  263. }
  264. // Keep track of block comment state of previous line
  265. prevComment = comment_start || comment_continue;
  266. // Set fold level for this line and move to next line
  267. styler.SetLevel(lineCurrent, lev);
  268. indentCurrent = indentNext;
  269. lineCurrent = lineNext;
  270. }
  271. // NOTE: Cannot set level of last line here because indentCurrent doesn't have
  272. // header flag set; the loop above is crafted to take care of this case!
  273. //styler.SetLevel(lineCurrent, indentCurrent);
  274. }
  275. LexerModule lmYAML(SCLEX_YAML, ColouriseYAMLDoc, "yaml", FoldYAMLDoc, yamlWordListDesc);