LexYAML.cpp 12 KB

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