1 // Scintilla source code edit control
5 // Copyright 2003- by Sean O'Dell <sean@celsoft.com>
6 // Release under the same license as Scintilla/SciTE.
18 #include "StyleContext.h"
20 #include "Scintilla.h"
23 static const char * const yamlWordListDesc
[] = {
28 static inline bool AtEOL(Accessor
&styler
, unsigned int i
) {
29 return (styler
[i
] == '\n') ||
30 ((styler
[i
] == '\r') && (styler
.SafeGetCharAt(i
+ 1) != '\n'));
33 static unsigned int SpaceCount(char* lineBuffer
) {
34 if (lineBuffer
== NULL
)
37 char* headBuffer
= lineBuffer
;
39 while (*headBuffer
== ' ')
42 return headBuffer
- lineBuffer
;
45 #define YAML_STATE_BITSIZE 16
46 #define YAML_STATE_MASK (0xFFFF0000)
47 #define YAML_STATE_DOCUMENT (1 << YAML_STATE_BITSIZE)
48 #define YAML_STATE_VALUE (2 << YAML_STATE_BITSIZE)
49 #define YAML_STATE_COMMENT (3 << YAML_STATE_BITSIZE)
50 #define YAML_STATE_TEXT_PARENT (4 << YAML_STATE_BITSIZE)
51 #define YAML_STATE_TEXT (5 << YAML_STATE_BITSIZE)
53 static void ColouriseYAMLLine(
55 unsigned int currentLine
,
56 unsigned int lengthLine
,
57 unsigned int startLine
,
63 bool bInQuotes
= false;
64 unsigned int indentAmount
= SpaceCount(lineBuffer
);
66 if (currentLine
> 0) {
67 int parentLineState
= styler
.GetLineState(currentLine
- 1);
69 if ((parentLineState
&YAML_STATE_MASK
) == YAML_STATE_TEXT
|| (parentLineState
&YAML_STATE_MASK
) == YAML_STATE_TEXT_PARENT
) {
70 unsigned int parentIndentAmount
= parentLineState
&(~YAML_STATE_MASK
);
71 if (indentAmount
> parentIndentAmount
) {
72 styler
.SetLineState(currentLine
, YAML_STATE_TEXT
| parentIndentAmount
);
73 styler
.ColourTo(endPos
, SCE_YAML_TEXT
);
78 styler
.SetLineState(currentLine
, 0);
79 if (strncmp(lineBuffer
, "---", 3) == 0) { // Document marker
80 styler
.SetLineState(currentLine
, YAML_STATE_DOCUMENT
);
81 styler
.ColourTo(endPos
, SCE_YAML_DOCUMENT
);
84 // Skip initial spaces
85 while ((i
< lengthLine
) && lineBuffer
[i
] == ' ') { // YAML always uses space, never TABS or anything else
88 if (lineBuffer
[i
] == '\t') { // if we skipped all spaces, and we are NOT inside a text block, this is wrong
89 styler
.ColourTo(endPos
, SCE_YAML_ERROR
);
92 if (lineBuffer
[i
] == '#') { // Comment
93 styler
.SetLineState(currentLine
, YAML_STATE_COMMENT
);
94 styler
.ColourTo(endPos
, SCE_YAML_COMMENT
);
97 while (i
< lengthLine
) {
98 if (lineBuffer
[i
] == '\'' || lineBuffer
[i
] == '\"') {
99 bInQuotes
= !bInQuotes
;
100 } else if (lineBuffer
[i
] == ':' && !bInQuotes
) {
101 styler
.ColourTo(startLine
+ i
, SCE_YAML_IDENTIFIER
);
102 // Non-folding scalar
104 while ((i
< lengthLine
) && isspacechar(lineBuffer
[i
]))
106 unsigned int endValue
= lengthLine
- 1;
107 while ((endValue
>= i
) && isspacechar(lineBuffer
[endValue
]))
109 lineBuffer
[endValue
+ 1] = '\0';
110 if (lineBuffer
[i
] == '|' || lineBuffer
[i
] == '>') {
112 if (lineBuffer
[i
] == '+' || lineBuffer
[i
] == '-')
114 while ((i
< lengthLine
) && isspacechar(lineBuffer
[i
]))
116 if (lineBuffer
[i
] == '\0') {
117 styler
.SetLineState(currentLine
, YAML_STATE_TEXT_PARENT
| indentAmount
);
118 styler
.ColourTo(endPos
, SCE_YAML_DEFAULT
);
120 } else if (lineBuffer
[i
] == '#') {
121 styler
.SetLineState(currentLine
, YAML_STATE_TEXT_PARENT
| indentAmount
);
122 styler
.ColourTo(startLine
+ i
- 1, SCE_YAML_DEFAULT
);
123 styler
.ColourTo(endPos
, SCE_YAML_COMMENT
);
126 styler
.ColourTo(endPos
, SCE_YAML_ERROR
);
130 styler
.SetLineState(currentLine
, YAML_STATE_VALUE
);
131 if (lineBuffer
[i
] == '&' || lineBuffer
[i
] == '*') {
132 styler
.ColourTo(endPos
, SCE_YAML_REFERENCE
);
135 if (keywords
.InList(&lineBuffer
[i
])) { // Convertible value (true/false, etc.)
136 styler
.ColourTo(endPos
, SCE_YAML_KEYWORD
);
140 while ((i
< lengthLine
) && lineBuffer
[i
]) {
141 if (!isdigit(lineBuffer
[i
]) && lineBuffer
[i
] != '-' && lineBuffer
[i
] != '.' && lineBuffer
[i
] != ',') {
142 styler
.ColourTo(endPos
, SCE_YAML_DEFAULT
);
148 styler
.ColourTo(endPos
, SCE_YAML_NUMBER
);
152 break; // shouldn't get here, but just in case, the rest of the line is coloured the default
156 styler
.ColourTo(endPos
, SCE_YAML_DEFAULT
);
159 static void ColouriseYAMLDoc(unsigned int startPos
, int length
, int, WordList
*keywordLists
[], Accessor
&styler
) {
160 char lineBuffer
[1024];
161 styler
.StartAt(startPos
);
162 styler
.StartSegment(startPos
);
163 unsigned int linePos
= 0;
164 unsigned int startLine
= startPos
;
165 unsigned int endPos
= startPos
+ length
;
166 unsigned int maxPos
= styler
.Length();
167 unsigned int lineCurrent
= styler
.GetLine(startPos
);
169 for (unsigned int i
= startPos
; i
< maxPos
&& i
< endPos
; i
++) {
170 lineBuffer
[linePos
++] = styler
[i
];
171 if (AtEOL(styler
, i
) || (linePos
>= sizeof(lineBuffer
) - 1)) {
172 // End of line (or of line buffer) met, colourise it
173 lineBuffer
[linePos
] = '\0';
174 ColouriseYAMLLine(lineBuffer
, lineCurrent
, linePos
, startLine
, i
, *keywordLists
[0], styler
);
180 if (linePos
> 0) { // Last line does not have ending characters
181 ColouriseYAMLLine(lineBuffer
, lineCurrent
, linePos
, startLine
, startPos
+ length
- 1, *keywordLists
[0], styler
);
185 static bool IsCommentLine(int line
, Accessor
&styler
) {
186 int pos
= styler
.LineStart(line
);
187 if (styler
[pos
] == '#')
192 static void FoldYAMLDoc(unsigned int startPos
, int length
, int /*initStyle - unused*/,
193 WordList
*[], Accessor
&styler
) {
194 const int maxPos
= startPos
+ length
;
195 const int maxLines
= styler
.GetLine(maxPos
- 1); // Requested last line
196 const int docLines
= styler
.GetLine(styler
.Length() - 1); // Available last line
197 const bool foldComment
= styler
.GetPropertyInt("fold.comment.yaml") != 0;
199 // Backtrack to previous non-blank line so we can determine indent level
200 // for any white space lines
201 // and so we can fix any preceding fold level (which is why we go back
202 // at least one line in all cases)
204 int lineCurrent
= styler
.GetLine(startPos
);
205 int indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
206 while (lineCurrent
> 0) {
208 indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
209 if (!(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) &&
210 (!IsCommentLine(lineCurrent
, styler
)))
213 int indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
215 // Set up initial loop state
217 if (lineCurrent
>= 1)
218 prevComment
= foldComment
&& IsCommentLine(lineCurrent
- 1, styler
);
220 // Process all characters to end of requested range
221 // or comment that hangs over the end of the range. Cap processing in all cases
222 // to end of document (in case of unclosed comment at end).
223 while ((lineCurrent
<= docLines
) && ((lineCurrent
<= maxLines
) || prevComment
)) {
226 int lev
= indentCurrent
;
227 int lineNext
= lineCurrent
+ 1;
228 int indentNext
= indentCurrent
;
229 if (lineNext
<= docLines
) {
230 // Information about next line is only available if not at end of document
231 indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
233 const int comment
= foldComment
&& IsCommentLine(lineCurrent
, styler
);
234 const int comment_start
= (comment
&& !prevComment
&& (lineNext
<= docLines
) &&
235 IsCommentLine(lineNext
, styler
) && (lev
> SC_FOLDLEVELBASE
));
236 const int comment_continue
= (comment
&& prevComment
);
238 indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
239 if (indentNext
& SC_FOLDLEVELWHITEFLAG
)
240 indentNext
= SC_FOLDLEVELWHITEFLAG
| indentCurrentLevel
;
243 // Place fold point at start of a block of comments
244 lev
|= SC_FOLDLEVELHEADERFLAG
;
245 } else if (comment_continue
) {
246 // Add level to rest of lines in the block
250 // Skip past any blank lines for next indent level info; we skip also
251 // comments (all comments, not just those starting in column 0)
252 // which effectively folds them into surrounding code rather
253 // than screwing up folding.
255 while ((lineNext
< docLines
) &&
256 ((indentNext
& SC_FOLDLEVELWHITEFLAG
) ||
257 (lineNext
<= docLines
&& IsCommentLine(lineNext
, styler
)))) {
260 indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
263 const int levelAfterComments
= indentNext
& SC_FOLDLEVELNUMBERMASK
;
264 const int levelBeforeComments
= Platform::Maximum(indentCurrentLevel
,levelAfterComments
);
266 // Now set all the indent levels on the lines we skipped
267 // Do this from end to start. Once we encounter one line
268 // which is indented more than the line after the end of
269 // the comment-block, use the level of the block before
271 int skipLine
= lineNext
;
272 int skipLevel
= levelAfterComments
;
274 while (--skipLine
> lineCurrent
) {
275 int skipLineIndent
= styler
.IndentAmount(skipLine
, &spaceFlags
, NULL
);
277 if ((skipLineIndent
& SC_FOLDLEVELNUMBERMASK
) > levelAfterComments
)
278 skipLevel
= levelBeforeComments
;
280 int whiteFlag
= skipLineIndent
& SC_FOLDLEVELWHITEFLAG
;
282 styler
.SetLevel(skipLine
, skipLevel
| whiteFlag
);
285 // Set fold header on non-comment line
286 if (!comment
&& !(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) ) {
287 if ((indentCurrent
& SC_FOLDLEVELNUMBERMASK
) < (indentNext
& SC_FOLDLEVELNUMBERMASK
))
288 lev
|= SC_FOLDLEVELHEADERFLAG
;
291 // Keep track of block comment state of previous line
292 prevComment
= comment_start
|| comment_continue
;
294 // Set fold level for this line and move to next line
295 styler
.SetLevel(lineCurrent
, lev
);
296 indentCurrent
= indentNext
;
297 lineCurrent
= lineNext
;
300 // NOTE: Cannot set level of last line here because indentCurrent doesn't have
301 // header flag set; the loop above is crafted to take care of this case!
302 //styler.SetLevel(lineCurrent, indentCurrent);
305 LexerModule
lmYAML(SCLEX_YAML
, ColouriseYAMLDoc
, "yaml", FoldYAMLDoc
, yamlWordListDesc
);