1 // Scintilla source code edit control
2 /** @file LexProps.cxx
3  ** Lexer for properties files.
4  **/
5 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
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 <stdio.h>
11 #include <stdarg.h>
12 #include <assert.h>
13 #include <ctype.h>
14 
15 #include <string>
16 
17 #include "ILexer.h"
18 #include "Scintilla.h"
19 #include "SciLexer.h"
20 
21 #include "WordList.h"
22 #include "LexAccessor.h"
23 #include "Accessor.h"
24 #include "StyleContext.h"
25 #include "CharacterSet.h"
26 #include "LexerModule.h"
27 
28 using namespace Scintilla;
29 
AtEOL(Accessor & styler,Sci_PositionU i)30 static inline bool AtEOL(Accessor &styler, Sci_PositionU i) {
31 	return (styler[i] == '\n') ||
32 	       ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n'));
33 }
34 
isassignchar(unsigned char ch)35 static inline bool isassignchar(unsigned char ch) {
36 	return (ch == '=') || (ch == ':');
37 }
38 
ColourisePropsLine(const char * lineBuffer,Sci_PositionU lengthLine,Sci_PositionU startLine,Sci_PositionU endPos,Accessor & styler,bool allowInitialSpaces)39 static void ColourisePropsLine(
40 	const char *lineBuffer,
41     Sci_PositionU lengthLine,
42     Sci_PositionU startLine,
43     Sci_PositionU endPos,
44     Accessor &styler,
45     bool allowInitialSpaces) {
46 
47 	Sci_PositionU i = 0;
48 	if (allowInitialSpaces) {
49 		while ((i < lengthLine) && isspacechar(lineBuffer[i]))	// Skip initial spaces
50 			i++;
51 	} else {
52 		if (isspacechar(lineBuffer[i])) // don't allow initial spaces
53 			i = lengthLine;
54 	}
55 
56 	if (i < lengthLine) {
57 		if (lineBuffer[i] == '#' || lineBuffer[i] == '!' || lineBuffer[i] == ';') {
58 			styler.ColourTo(endPos, SCE_PROPS_COMMENT);
59 		} else if (lineBuffer[i] == '[') {
60 			styler.ColourTo(endPos, SCE_PROPS_SECTION);
61 		} else if (lineBuffer[i] == '@') {
62 			styler.ColourTo(startLine + i, SCE_PROPS_DEFVAL);
63 			if (isassignchar(lineBuffer[i++]))
64 				styler.ColourTo(startLine + i, SCE_PROPS_ASSIGNMENT);
65 			styler.ColourTo(endPos, SCE_PROPS_DEFAULT);
66 		} else {
67 			// Search for the '=' character
68 			while ((i < lengthLine) && !isassignchar(lineBuffer[i]))
69 				i++;
70 			if ((i < lengthLine) && isassignchar(lineBuffer[i])) {
71 				styler.ColourTo(startLine + i - 1, SCE_PROPS_KEY);
72 				styler.ColourTo(startLine + i, SCE_PROPS_ASSIGNMENT);
73 				styler.ColourTo(endPos, SCE_PROPS_DEFAULT);
74 			} else {
75 				styler.ColourTo(endPos, SCE_PROPS_DEFAULT);
76 			}
77 		}
78 	} else {
79 		styler.ColourTo(endPos, SCE_PROPS_DEFAULT);
80 	}
81 }
82 
ColourisePropsDoc(Sci_PositionU startPos,Sci_Position length,int,WordList * [],Accessor & styler)83 static void ColourisePropsDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) {
84 	std::string lineBuffer;
85 	styler.StartAt(startPos);
86 	styler.StartSegment(startPos);
87 	Sci_PositionU startLine = startPos;
88 
89 	// property lexer.props.allow.initial.spaces
90 	//	For properties files, set to 0 to style all lines that start with whitespace in the default style.
91 	//	This is not suitable for SciTE .properties files which use indentation for flow control but
92 	//	can be used for RFC2822 text where indentation is used for continuation lines.
93 	const bool allowInitialSpaces = styler.GetPropertyInt("lexer.props.allow.initial.spaces", 1) != 0;
94 
95 	for (Sci_PositionU i = startPos; i < startPos + length; i++) {
96 		lineBuffer.push_back(styler[i]);
97 		if (AtEOL(styler, i)) {
98 			// End of line (or of line buffer) met, colourise it
99 			ColourisePropsLine(lineBuffer.c_str(), lineBuffer.length(), startLine, i, styler, allowInitialSpaces);
100 			lineBuffer.clear();
101 			startLine = i + 1;
102 		}
103 	}
104 	if (lineBuffer.length() > 0) {	// Last line does not have ending characters
105 		ColourisePropsLine(lineBuffer.c_str(), lineBuffer.length(), startLine, startPos + length - 1, styler, allowInitialSpaces);
106 	}
107 }
108 
109 // adaption by ksc, using the "} else {" trick of 1.53
110 // 030721
FoldPropsDoc(Sci_PositionU startPos,Sci_Position length,int,WordList * [],Accessor & styler)111 static void FoldPropsDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) {
112 	const bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
113 
114 	const Sci_PositionU endPos = startPos + length;
115 	int visibleChars = 0;
116 	Sci_Position lineCurrent = styler.GetLine(startPos);
117 
118 	char chNext = styler[startPos];
119 	int styleNext = styler.StyleAt(startPos);
120 	bool headerPoint = false;
121 	int lev;
122 
123 	for (Sci_PositionU i = startPos; i < endPos; i++) {
124 		const char ch = chNext;
125 		chNext = styler[i+1];
126 
127 		const int style = styleNext;
128 		styleNext = styler.StyleAt(i + 1);
129 		const bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
130 
131 		if (style == SCE_PROPS_SECTION) {
132 			headerPoint = true;
133 		}
134 
135 		if (atEOL) {
136 			lev = SC_FOLDLEVELBASE;
137 
138 			if (lineCurrent > 0) {
139 				const int levelPrevious = styler.LevelAt(lineCurrent - 1);
140 
141 				if (levelPrevious & SC_FOLDLEVELHEADERFLAG) {
142 					lev = SC_FOLDLEVELBASE + 1;
143 				} else {
144 					lev = levelPrevious & SC_FOLDLEVELNUMBERMASK;
145 				}
146 			}
147 
148 			if (headerPoint) {
149 				lev = SC_FOLDLEVELBASE;
150 			}
151 			if (visibleChars == 0 && foldCompact)
152 				lev |= SC_FOLDLEVELWHITEFLAG;
153 
154 			if (headerPoint) {
155 				lev |= SC_FOLDLEVELHEADERFLAG;
156 			}
157 			if (lev != styler.LevelAt(lineCurrent)) {
158 				styler.SetLevel(lineCurrent, lev);
159 			}
160 
161 			lineCurrent++;
162 			visibleChars = 0;
163 			headerPoint = false;
164 		}
165 		if (!isspacechar(ch))
166 			visibleChars++;
167 	}
168 
169 	if (lineCurrent > 0) {
170 		const int levelPrevious = styler.LevelAt(lineCurrent - 1);
171 		if (levelPrevious & SC_FOLDLEVELHEADERFLAG) {
172 			lev = SC_FOLDLEVELBASE + 1;
173 		} else {
174 			lev = levelPrevious & SC_FOLDLEVELNUMBERMASK;
175 		}
176 	} else {
177 		lev = SC_FOLDLEVELBASE;
178 	}
179 	int flagsNext = styler.LevelAt(lineCurrent);
180 	styler.SetLevel(lineCurrent, lev | (flagsNext & ~SC_FOLDLEVELNUMBERMASK));
181 }
182 
183 static const char *const emptyWordListDesc[] = {
184 	0
185 };
186 
187 LexerModule lmProps(SCLEX_PROPERTIES, ColourisePropsDoc, "props", FoldPropsDoc, emptyWordListDesc);
188