1 // Scintilla source code edit control
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.
16 #include "Scintilla.h"
20 #include "LexAccessor.h"
22 #include "StyleContext.h"
23 #include "CharacterSet.h"
24 #include "LexerModule.h"
27 using namespace Scintilla
;
30 static const char * const yamlWordListDesc
[] = {
35 static inline bool AtEOL(Accessor
&styler
, Sci_PositionU i
) {
36 return (styler
[i
] == '\n') ||
37 ((styler
[i
] == '\r') && (styler
.SafeGetCharAt(i
+ 1) != '\n'));
40 static unsigned int SpaceCount(char* lineBuffer
) {
41 if (lineBuffer
== NULL
)
44 char* headBuffer
= lineBuffer
;
46 while (*headBuffer
== ' ')
49 return static_cast<unsigned int>(headBuffer
- lineBuffer
);
52 static bool KeywordAtChar(char* lineBuffer
, char* startComment
, const WordList
&keywords
) {
53 if (lineBuffer
== NULL
|| startComment
<= lineBuffer
)
55 char* endValue
= startComment
- 1;
56 while (endValue
>= lineBuffer
&& *endValue
== ' ')
58 Sci_PositionU len
= static_cast<Sci_PositionU
>(endValue
- lineBuffer
) + 1;
60 if (len
> (sizeof(s
) / sizeof(s
[0]) - 1))
62 strncpy(s
, lineBuffer
, len
);
64 return (keywords
.InList(s
));
67 #define YAML_STATE_BITSIZE 16
68 #define YAML_STATE_MASK (0xFFFF0000)
69 #define YAML_STATE_DOCUMENT (1 << YAML_STATE_BITSIZE)
70 #define YAML_STATE_VALUE (2 << YAML_STATE_BITSIZE)
71 #define YAML_STATE_COMMENT (3 << YAML_STATE_BITSIZE)
72 #define YAML_STATE_TEXT_PARENT (4 << YAML_STATE_BITSIZE)
73 #define YAML_STATE_TEXT (5 << YAML_STATE_BITSIZE)
75 static void ColouriseYAMLLine(
77 Sci_PositionU currentLine
,
78 Sci_PositionU lengthLine
,
79 Sci_PositionU startLine
,
85 bool bInQuotes
= false;
86 unsigned int indentAmount
= SpaceCount(lineBuffer
);
88 if (currentLine
> 0) {
89 int parentLineState
= styler
.GetLineState(currentLine
- 1);
91 if ((parentLineState
&YAML_STATE_MASK
) == YAML_STATE_TEXT
|| (parentLineState
&YAML_STATE_MASK
) == YAML_STATE_TEXT_PARENT
) {
92 unsigned int parentIndentAmount
= parentLineState
&(~YAML_STATE_MASK
);
93 if (indentAmount
> parentIndentAmount
) {
94 styler
.SetLineState(currentLine
, YAML_STATE_TEXT
| parentIndentAmount
);
95 styler
.ColourTo(endPos
, SCE_YAML_TEXT
);
100 styler
.SetLineState(currentLine
, 0);
101 if (strncmp(lineBuffer
, "---", 3) == 0 || strncmp(lineBuffer
, "...", 3) == 0) { // Document marker
102 styler
.SetLineState(currentLine
, YAML_STATE_DOCUMENT
);
103 styler
.ColourTo(endPos
, SCE_YAML_DOCUMENT
);
106 // Skip initial spaces
107 while ((i
< lengthLine
) && lineBuffer
[i
] == ' ') { // YAML always uses space, never TABS or anything else
110 if (lineBuffer
[i
] == '\t') { // if we skipped all spaces, and we are NOT inside a text block, this is wrong
111 styler
.ColourTo(endPos
, SCE_YAML_ERROR
);
114 if (lineBuffer
[i
] == '#') { // Comment
115 styler
.SetLineState(currentLine
, YAML_STATE_COMMENT
);
116 styler
.ColourTo(endPos
, SCE_YAML_COMMENT
);
119 while (i
< lengthLine
) {
120 if (lineBuffer
[i
] == '\'' || lineBuffer
[i
] == '\"') {
121 bInQuotes
= !bInQuotes
;
122 } else if (lineBuffer
[i
] == '#' && isspacechar(lineBuffer
[i
- 1]) && !bInQuotes
) {
123 styler
.ColourTo(startLine
+ i
- 1, SCE_YAML_DEFAULT
);
124 styler
.ColourTo(endPos
, SCE_YAML_COMMENT
);
126 } else if (lineBuffer
[i
] == ':' && !bInQuotes
) {
127 styler
.ColourTo(startLine
+ i
- 1, SCE_YAML_IDENTIFIER
);
128 styler
.ColourTo(startLine
+ i
, SCE_YAML_OPERATOR
);
129 // Non-folding scalar
131 while ((i
< lengthLine
) && isspacechar(lineBuffer
[i
]))
133 Sci_PositionU endValue
= lengthLine
- 1;
134 while ((endValue
>= i
) && isspacechar(lineBuffer
[endValue
]))
136 lineBuffer
[endValue
+ 1] = '\0';
137 if (lineBuffer
[i
] == '|' || lineBuffer
[i
] == '>') {
139 if (lineBuffer
[i
] == '+' || lineBuffer
[i
] == '-')
141 while ((i
< lengthLine
) && isspacechar(lineBuffer
[i
]))
143 if (lineBuffer
[i
] == '\0') {
144 styler
.SetLineState(currentLine
, YAML_STATE_TEXT_PARENT
| indentAmount
);
145 styler
.ColourTo(endPos
, SCE_YAML_DEFAULT
);
147 } else if (lineBuffer
[i
] == '#') {
148 styler
.SetLineState(currentLine
, YAML_STATE_TEXT_PARENT
| indentAmount
);
149 styler
.ColourTo(startLine
+ i
- 1, SCE_YAML_DEFAULT
);
150 styler
.ColourTo(endPos
, SCE_YAML_COMMENT
);
153 styler
.ColourTo(endPos
, SCE_YAML_ERROR
);
156 } else if (lineBuffer
[i
] == '#') {
157 styler
.ColourTo(startLine
+ i
- 1, SCE_YAML_DEFAULT
);
158 styler
.ColourTo(endPos
, SCE_YAML_COMMENT
);
161 Sci_PositionU startComment
= i
;
163 while (startComment
< lengthLine
) { // Comment must be space padded
164 if (lineBuffer
[startComment
] == '\'' || lineBuffer
[startComment
] == '\"')
165 bInQuotes
= !bInQuotes
;
166 if (lineBuffer
[startComment
] == '#' && isspacechar(lineBuffer
[startComment
- 1]) && !bInQuotes
)
170 styler
.SetLineState(currentLine
, YAML_STATE_VALUE
);
171 if (lineBuffer
[i
] == '&' || lineBuffer
[i
] == '*') {
172 styler
.ColourTo(startLine
+ startComment
- 1, SCE_YAML_REFERENCE
);
173 if (startComment
< lengthLine
)
174 styler
.ColourTo(endPos
, SCE_YAML_COMMENT
);
177 if (KeywordAtChar(&lineBuffer
[i
], &lineBuffer
[startComment
], keywords
)) { // Convertible value (true/false, etc.)
178 styler
.ColourTo(startLine
+ startComment
- 1, SCE_YAML_KEYWORD
);
179 if (startComment
< lengthLine
)
180 styler
.ColourTo(endPos
, SCE_YAML_COMMENT
);
183 Sci_PositionU i2
= i
;
184 while ((i
< startComment
) && lineBuffer
[i
]) {
185 if (!(IsASCII(lineBuffer
[i
]) && isdigit(lineBuffer
[i
])) && lineBuffer
[i
] != '-'
186 && lineBuffer
[i
] != '.' && lineBuffer
[i
] != ',' && lineBuffer
[i
] != ' ') {
187 styler
.ColourTo(startLine
+ startComment
- 1, SCE_YAML_DEFAULT
);
188 if (startComment
< lengthLine
)
189 styler
.ColourTo(endPos
, SCE_YAML_COMMENT
);
195 styler
.ColourTo(startLine
+ startComment
- 1, SCE_YAML_NUMBER
);
196 if (startComment
< lengthLine
)
197 styler
.ColourTo(endPos
, SCE_YAML_COMMENT
);
200 break; // shouldn't get here, but just in case, the rest of the line is coloured the default
204 styler
.ColourTo(endPos
, SCE_YAML_DEFAULT
);
207 static void ColouriseYAMLDoc(Sci_PositionU startPos
, Sci_Position length
, int, WordList
*keywordLists
[], Accessor
&styler
) {
208 char lineBuffer
[1024] = "";
209 styler
.StartAt(startPos
);
210 styler
.StartSegment(startPos
);
211 Sci_PositionU linePos
= 0;
212 Sci_PositionU startLine
= startPos
;
213 Sci_PositionU endPos
= startPos
+ length
;
214 Sci_PositionU maxPos
= styler
.Length();
215 Sci_PositionU lineCurrent
= styler
.GetLine(startPos
);
217 for (Sci_PositionU i
= startPos
; i
< maxPos
&& i
< endPos
; i
++) {
218 lineBuffer
[linePos
++] = styler
[i
];
219 if (AtEOL(styler
, i
) || (linePos
>= sizeof(lineBuffer
) - 1)) {
220 // End of line (or of line buffer) met, colourise it
221 lineBuffer
[linePos
] = '\0';
222 ColouriseYAMLLine(lineBuffer
, lineCurrent
, linePos
, startLine
, i
, *keywordLists
[0], styler
);
228 if (linePos
> 0) { // Last line does not have ending characters
229 ColouriseYAMLLine(lineBuffer
, lineCurrent
, linePos
, startLine
, startPos
+ length
- 1, *keywordLists
[0], styler
);
233 static bool IsCommentLine(Sci_Position line
, Accessor
&styler
) {
234 Sci_Position pos
= styler
.LineStart(line
);
235 if (styler
[pos
] == '#')
240 static void FoldYAMLDoc(Sci_PositionU startPos
, Sci_Position length
, int /*initStyle - unused*/,
241 WordList
*[], Accessor
&styler
) {
242 const Sci_Position maxPos
= startPos
+ length
;
243 const Sci_Position maxLines
= styler
.GetLine(maxPos
- 1); // Requested last line
244 const Sci_Position docLines
= styler
.GetLine(styler
.Length() - 1); // Available last line
245 const bool foldComment
= styler
.GetPropertyInt("fold.comment.yaml") != 0;
247 // Backtrack to previous non-blank line so we can determine indent level
248 // for any white space lines
249 // and so we can fix any preceding fold level (which is why we go back
250 // at least one line in all cases)
252 Sci_Position lineCurrent
= styler
.GetLine(startPos
);
253 int indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
254 while (lineCurrent
> 0) {
256 indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
257 if (!(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) &&
258 (!IsCommentLine(lineCurrent
, styler
)))
261 int indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
263 // Set up initial loop state
265 if (lineCurrent
>= 1)
266 prevComment
= foldComment
&& IsCommentLine(lineCurrent
- 1, styler
);
268 // Process all characters to end of requested range
269 // or comment that hangs over the end of the range. Cap processing in all cases
270 // to end of document (in case of unclosed comment at end).
271 while ((lineCurrent
<= docLines
) && ((lineCurrent
<= maxLines
) || prevComment
)) {
274 int lev
= indentCurrent
;
275 Sci_Position lineNext
= lineCurrent
+ 1;
276 int indentNext
= indentCurrent
;
277 if (lineNext
<= docLines
) {
278 // Information about next line is only available if not at end of document
279 indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
281 const int comment
= foldComment
&& IsCommentLine(lineCurrent
, styler
);
282 const int comment_start
= (comment
&& !prevComment
&& (lineNext
<= docLines
) &&
283 IsCommentLine(lineNext
, styler
) && (lev
> SC_FOLDLEVELBASE
));
284 const int comment_continue
= (comment
&& prevComment
);
286 indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
287 if (indentNext
& SC_FOLDLEVELWHITEFLAG
)
288 indentNext
= SC_FOLDLEVELWHITEFLAG
| indentCurrentLevel
;
291 // Place fold point at start of a block of comments
292 lev
|= SC_FOLDLEVELHEADERFLAG
;
293 } else if (comment_continue
) {
294 // Add level to rest of lines in the block
298 // Skip past any blank lines for next indent level info; we skip also
299 // comments (all comments, not just those starting in column 0)
300 // which effectively folds them into surrounding code rather
301 // than screwing up folding.
303 while ((lineNext
< docLines
) &&
304 ((indentNext
& SC_FOLDLEVELWHITEFLAG
) ||
305 (lineNext
<= docLines
&& IsCommentLine(lineNext
, styler
)))) {
308 indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
311 const int levelAfterComments
= indentNext
& SC_FOLDLEVELNUMBERMASK
;
312 const int levelBeforeComments
= Maximum(indentCurrentLevel
,levelAfterComments
);
314 // Now set all the indent levels on the lines we skipped
315 // Do this from end to start. Once we encounter one line
316 // which is indented more than the line after the end of
317 // the comment-block, use the level of the block before
319 Sci_Position skipLine
= lineNext
;
320 int skipLevel
= levelAfterComments
;
322 while (--skipLine
> lineCurrent
) {
323 int skipLineIndent
= styler
.IndentAmount(skipLine
, &spaceFlags
, NULL
);
325 if ((skipLineIndent
& SC_FOLDLEVELNUMBERMASK
) > levelAfterComments
)
326 skipLevel
= levelBeforeComments
;
328 int whiteFlag
= skipLineIndent
& SC_FOLDLEVELWHITEFLAG
;
330 styler
.SetLevel(skipLine
, skipLevel
| whiteFlag
);
333 // Set fold header on non-comment line
334 if (!comment
&& !(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) ) {
335 if ((indentCurrent
& SC_FOLDLEVELNUMBERMASK
) < (indentNext
& SC_FOLDLEVELNUMBERMASK
))
336 lev
|= SC_FOLDLEVELHEADERFLAG
;
339 // Keep track of block comment state of previous line
340 prevComment
= comment_start
|| comment_continue
;
342 // Set fold level for this line and move to next line
343 styler
.SetLevel(lineCurrent
, lev
);
344 indentCurrent
= indentNext
;
345 lineCurrent
= lineNext
;
348 // NOTE: Cannot set level of last line here because indentCurrent doesn't have
349 // header flag set; the loop above is crafted to take care of this case!
350 //styler.SetLevel(lineCurrent, indentCurrent);
353 LexerModule
lmYAML(SCLEX_YAML
, ColouriseYAMLDoc
, "yaml", FoldYAMLDoc
, yamlWordListDesc
);