PageRenderTime 46ms CodeModel.GetById 15ms app.highlight 25ms RepoModel.GetById 2ms app.codeStats 0ms

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