1 // Scintilla source code edit control
2 /**
3 * @file LexRegistry.cxx
4 * @date July 26 2014
5 * @brief Lexer for Windows registration files(.reg)
6 * @author nkmathew
7 *
8 * The License.txt file describes the conditions under which this software may be
9 * distributed.
10 *
11 */
12
13 #include <cstdlib>
14 #include <cassert>
15 #include <cctype>
16 #include <cstdio>
17 #include <string>
18 #include <vector>
19 #include <map>
20
21 #include "ILexer.h"
22 #include "Scintilla.h"
23 #include "SciLexer.h"
24 #include "WordList.h"
25 #include "LexAccessor.h"
26 #include "StyleContext.h"
27 #include "CharacterSet.h"
28 #include "LexerModule.h"
29 #include "OptionSet.h"
30 #include "DefaultLexer.h"
31
32 using namespace Scintilla;
33
34 static const char *const RegistryWordListDesc[] = {
35 0
36 };
37
38 struct OptionsRegistry {
39 bool foldCompact;
40 bool fold;
OptionsRegistryOptionsRegistry41 OptionsRegistry() {
42 foldCompact = false;
43 fold = false;
44 }
45 };
46
47 struct OptionSetRegistry : public OptionSet<OptionsRegistry> {
OptionSetRegistryOptionSetRegistry48 OptionSetRegistry() {
49 DefineProperty("fold.compact", &OptionsRegistry::foldCompact);
50 DefineProperty("fold", &OptionsRegistry::fold);
51 DefineWordListSets(RegistryWordListDesc);
52 }
53 };
54
55 class LexerRegistry : public DefaultLexer {
56 OptionsRegistry options;
57 OptionSetRegistry optSetRegistry;
58
IsStringState(int state)59 static bool IsStringState(int state) {
60 return (state == SCE_REG_VALUENAME || state == SCE_REG_STRING);
61 }
62
IsKeyPathState(int state)63 static bool IsKeyPathState(int state) {
64 return (state == SCE_REG_ADDEDKEY || state == SCE_REG_DELETEDKEY);
65 }
66
AtValueType(LexAccessor & styler,Sci_Position start)67 static bool AtValueType(LexAccessor &styler, Sci_Position start) {
68 Sci_Position i = 0;
69 while (i < 10) {
70 i++;
71 char curr = styler.SafeGetCharAt(start+i, '\0');
72 if (curr == ':') {
73 return true;
74 } else if (!curr) {
75 return false;
76 }
77 }
78 return false;
79 }
80
IsNextNonWhitespace(LexAccessor & styler,Sci_Position start,char ch)81 static bool IsNextNonWhitespace(LexAccessor &styler, Sci_Position start, char ch) {
82 Sci_Position i = 0;
83 while (i < 100) {
84 i++;
85 char curr = styler.SafeGetCharAt(start+i, '\0');
86 char next = styler.SafeGetCharAt(start+i+1, '\0');
87 bool atEOL = (curr == '\r' && next != '\n') || (curr == '\n');
88 if (curr == ch) {
89 return true;
90 } else if (!isspacechar(curr) || atEOL) {
91 return false;
92 }
93 }
94 return false;
95 }
96
97 // Looks for the equal sign at the end of the string
AtValueName(LexAccessor & styler,Sci_Position start)98 static bool AtValueName(LexAccessor &styler, Sci_Position start) {
99 bool atEOL = false;
100 Sci_Position i = 0;
101 bool escaped = false;
102 while (!atEOL) {
103 i++;
104 char curr = styler.SafeGetCharAt(start+i, '\0');
105 char next = styler.SafeGetCharAt(start+i+1, '\0');
106 atEOL = (curr == '\r' && next != '\n') || (curr == '\n');
107 if (escaped) {
108 escaped = false;
109 continue;
110 }
111 escaped = curr == '\\';
112 if (curr == '"') {
113 return IsNextNonWhitespace(styler, start+i, '=');
114 } else if (!curr) {
115 return false;
116 }
117 }
118 return false;
119 }
120
AtKeyPathEnd(LexAccessor & styler,Sci_Position start)121 static bool AtKeyPathEnd(LexAccessor &styler, Sci_Position start) {
122 bool atEOL = false;
123 Sci_Position i = 0;
124 while (!atEOL) {
125 i++;
126 char curr = styler.SafeGetCharAt(start+i, '\0');
127 char next = styler.SafeGetCharAt(start+i+1, '\0');
128 atEOL = (curr == '\r' && next != '\n') || (curr == '\n');
129 if (curr == ']' || !curr) {
130 // There's still at least one or more square brackets ahead
131 return false;
132 }
133 }
134 return true;
135 }
136
AtGUID(LexAccessor & styler,Sci_Position start)137 static bool AtGUID(LexAccessor &styler, Sci_Position start) {
138 int count = 8;
139 int portion = 0;
140 int offset = 1;
141 char digit = '\0';
142 while (portion < 5) {
143 int i = 0;
144 while (i < count) {
145 digit = styler.SafeGetCharAt(start+offset);
146 if (!(isxdigit(digit) || digit == '-')) {
147 return false;
148 }
149 offset++;
150 i++;
151 }
152 portion++;
153 count = (portion == 4) ? 13 : 5;
154 }
155 digit = styler.SafeGetCharAt(start+offset);
156 if (digit == '}') {
157 return true;
158 } else {
159 return false;
160 }
161 }
162
163 public:
LexerRegistry()164 LexerRegistry() : DefaultLexer("registry", SCLEX_REGISTRY) {}
~LexerRegistry()165 virtual ~LexerRegistry() {}
Version() const166 int SCI_METHOD Version() const override {
167 return lvRelease5;
168 }
Release()169 void SCI_METHOD Release() override {
170 delete this;
171 }
PropertyNames()172 const char *SCI_METHOD PropertyNames() override {
173 return optSetRegistry.PropertyNames();
174 }
PropertyType(const char * name)175 int SCI_METHOD PropertyType(const char *name) override {
176 return optSetRegistry.PropertyType(name);
177 }
DescribeProperty(const char * name)178 const char *SCI_METHOD DescribeProperty(const char *name) override {
179 return optSetRegistry.DescribeProperty(name);
180 }
PropertySet(const char * key,const char * val)181 Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) override {
182 if (optSetRegistry.PropertySet(&options, key, val)) {
183 return 0;
184 }
185 return -1;
186 }
PropertyGet(const char * key)187 const char * SCI_METHOD PropertyGet(const char *key) override {
188 return optSetRegistry.PropertyGet(key);
189 }
190
WordListSet(int,const char *)191 Sci_Position SCI_METHOD WordListSet(int, const char *) override {
192 return -1;
193 }
PrivateCall(int,void *)194 void *SCI_METHOD PrivateCall(int, void *) override {
195 return 0;
196 }
LexerFactoryRegistry()197 static ILexer5 *LexerFactoryRegistry() {
198 return new LexerRegistry;
199 }
DescribeWordListSets()200 const char *SCI_METHOD DescribeWordListSets() override {
201 return optSetRegistry.DescribeWordListSets();
202 }
203 void SCI_METHOD Lex(Sci_PositionU startPos,
204 Sci_Position length,
205 int initStyle,
206 IDocument *pAccess) override;
207 void SCI_METHOD Fold(Sci_PositionU startPos,
208 Sci_Position length,
209 int initStyle,
210 IDocument *pAccess) override;
211 };
212
Lex(Sci_PositionU startPos,Sci_Position length,int initStyle,IDocument * pAccess)213 void SCI_METHOD LexerRegistry::Lex(Sci_PositionU startPos,
214 Sci_Position length,
215 int initStyle,
216 IDocument *pAccess) {
217 int beforeGUID = SCE_REG_DEFAULT;
218 int beforeEscape = SCE_REG_DEFAULT;
219 CharacterSet setOperators = CharacterSet(CharacterSet::setNone, "-,.=:\\@()");
220 LexAccessor styler(pAccess);
221 StyleContext context(startPos, length, initStyle, styler);
222 bool highlight = true;
223 bool afterEqualSign = false;
224 while (context.More()) {
225 if (context.atLineStart) {
226 Sci_Position currPos = static_cast<Sci_Position>(context.currentPos);
227 bool continued = styler[currPos-3] == '\\';
228 highlight = continued ? true : false;
229 }
230 switch (context.state) {
231 case SCE_REG_COMMENT:
232 if (context.atLineEnd) {
233 context.SetState(SCE_REG_DEFAULT);
234 }
235 break;
236 case SCE_REG_VALUENAME:
237 case SCE_REG_STRING: {
238 Sci_Position currPos = static_cast<Sci_Position>(context.currentPos);
239 if (context.ch == '"') {
240 context.ForwardSetState(SCE_REG_DEFAULT);
241 } else if (context.ch == '\\') {
242 beforeEscape = context.state;
243 context.SetState(SCE_REG_ESCAPED);
244 context.Forward();
245 } else if (context.ch == '{') {
246 if (AtGUID(styler, currPos)) {
247 beforeGUID = context.state;
248 context.SetState(SCE_REG_STRING_GUID);
249 }
250 }
251 if (context.state == SCE_REG_STRING &&
252 context.ch == '%' &&
253 (isdigit(context.chNext) || context.chNext == '*')) {
254 context.SetState(SCE_REG_PARAMETER);
255 }
256 }
257 break;
258 case SCE_REG_PARAMETER:
259 context.ForwardSetState(SCE_REG_STRING);
260 if (context.ch == '"') {
261 context.ForwardSetState(SCE_REG_DEFAULT);
262 }
263 break;
264 case SCE_REG_VALUETYPE:
265 if (context.ch == ':') {
266 context.SetState(SCE_REG_DEFAULT);
267 afterEqualSign = false;
268 }
269 break;
270 case SCE_REG_HEXDIGIT:
271 case SCE_REG_OPERATOR:
272 context.SetState(SCE_REG_DEFAULT);
273 break;
274 case SCE_REG_DELETEDKEY:
275 case SCE_REG_ADDEDKEY: {
276 Sci_Position currPos = static_cast<Sci_Position>(context.currentPos);
277 if (context.ch == ']' && AtKeyPathEnd(styler, currPos)) {
278 context.ForwardSetState(SCE_REG_DEFAULT);
279 } else if (context.ch == '{') {
280 if (AtGUID(styler, currPos)) {
281 beforeGUID = context.state;
282 context.SetState(SCE_REG_KEYPATH_GUID);
283 }
284 }
285 }
286 break;
287 case SCE_REG_ESCAPED:
288 if (context.ch == '"') {
289 context.SetState(beforeEscape);
290 context.ForwardSetState(SCE_REG_DEFAULT);
291 } else if (context.ch == '\\') {
292 context.Forward();
293 } else {
294 context.SetState(beforeEscape);
295 beforeEscape = SCE_REG_DEFAULT;
296 }
297 break;
298 case SCE_REG_STRING_GUID:
299 case SCE_REG_KEYPATH_GUID: {
300 if (context.ch == '}') {
301 context.ForwardSetState(beforeGUID);
302 beforeGUID = SCE_REG_DEFAULT;
303 }
304 Sci_Position currPos = static_cast<Sci_Position>(context.currentPos);
305 if (context.ch == '"' && IsStringState(context.state)) {
306 context.ForwardSetState(SCE_REG_DEFAULT);
307 } else if (context.ch == ']' &&
308 AtKeyPathEnd(styler, currPos) &&
309 IsKeyPathState(context.state)) {
310 context.ForwardSetState(SCE_REG_DEFAULT);
311 } else if (context.ch == '\\' && IsStringState(context.state)) {
312 beforeEscape = context.state;
313 context.SetState(SCE_REG_ESCAPED);
314 context.Forward();
315 }
316 }
317 break;
318 }
319 // Determine if a new state should be entered.
320 if (context.state == SCE_REG_DEFAULT) {
321 Sci_Position currPos = static_cast<Sci_Position>(context.currentPos);
322 if (context.ch == ';') {
323 context.SetState(SCE_REG_COMMENT);
324 } else if (context.ch == '"') {
325 if (AtValueName(styler, currPos)) {
326 context.SetState(SCE_REG_VALUENAME);
327 } else {
328 context.SetState(SCE_REG_STRING);
329 }
330 } else if (context.ch == '[') {
331 if (IsNextNonWhitespace(styler, currPos, '-')) {
332 context.SetState(SCE_REG_DELETEDKEY);
333 } else {
334 context.SetState(SCE_REG_ADDEDKEY);
335 }
336 } else if (context.ch == '=') {
337 afterEqualSign = true;
338 highlight = true;
339 } else if (afterEqualSign) {
340 bool wordStart = isalpha(context.ch) && !isalpha(context.chPrev);
341 if (wordStart && AtValueType(styler, currPos)) {
342 context.SetState(SCE_REG_VALUETYPE);
343 }
344 } else if (isxdigit(context.ch) && highlight) {
345 context.SetState(SCE_REG_HEXDIGIT);
346 }
347 highlight = (context.ch == '@') ? true : highlight;
348 if (setOperators.Contains(context.ch) && highlight) {
349 context.SetState(SCE_REG_OPERATOR);
350 }
351 }
352 context.Forward();
353 }
354 context.Complete();
355 }
356
357 // Folding similar to that of FoldPropsDoc in LexOthers
Fold(Sci_PositionU startPos,Sci_Position length,int,IDocument * pAccess)358 void SCI_METHOD LexerRegistry::Fold(Sci_PositionU startPos,
359 Sci_Position length,
360 int,
361 IDocument *pAccess) {
362 if (!options.fold) {
363 return;
364 }
365 LexAccessor styler(pAccess);
366 Sci_Position currLine = styler.GetLine(startPos);
367 int visibleChars = 0;
368 Sci_PositionU endPos = startPos + length;
369 bool atKeyPath = false;
370 for (Sci_PositionU i = startPos; i < endPos; i++) {
371 atKeyPath = IsKeyPathState(styler.StyleAt(i)) ? true : atKeyPath;
372 char curr = styler.SafeGetCharAt(i);
373 char next = styler.SafeGetCharAt(i+1);
374 bool atEOL = (curr == '\r' && next != '\n') || (curr == '\n');
375 if (atEOL || i == (endPos-1)) {
376 int level = SC_FOLDLEVELBASE;
377 if (currLine > 0) {
378 int prevLevel = styler.LevelAt(currLine-1);
379 if (prevLevel & SC_FOLDLEVELHEADERFLAG) {
380 level += 1;
381 } else {
382 level = prevLevel;
383 }
384 }
385 if (!visibleChars && options.foldCompact) {
386 level |= SC_FOLDLEVELWHITEFLAG;
387 } else if (atKeyPath) {
388 level = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
389 }
390 if (level != styler.LevelAt(currLine)) {
391 styler.SetLevel(currLine, level);
392 }
393 currLine++;
394 visibleChars = 0;
395 atKeyPath = false;
396 }
397 if (!isspacechar(curr)) {
398 visibleChars++;
399 }
400 }
401
402 // Make the folding reach the last line in the file
403 int level = SC_FOLDLEVELBASE;
404 if (currLine > 0) {
405 int prevLevel = styler.LevelAt(currLine-1);
406 if (prevLevel & SC_FOLDLEVELHEADERFLAG) {
407 level += 1;
408 } else {
409 level = prevLevel;
410 }
411 }
412 styler.SetLevel(currLine, level);
413 }
414
415 LexerModule lmRegistry(SCLEX_REGISTRY,
416 LexerRegistry::LexerFactoryRegistry,
417 "registry",
418 RegistryWordListDesc);
419
420