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