1 // Scintilla source code edit control
3 * @file LexRegistry.cxx
5 * @brief Lexer for Windows registration files(.reg)
8 * The License.txt file describes the conditions under which this software may be
22 #include "Scintilla.h"
25 #include "LexAccessor.h"
26 #include "StyleContext.h"
27 #include "CharacterSet.h"
28 #include "LexerModule.h"
29 #include "OptionSet.h"
32 using namespace Scintilla
;
35 static const char *const RegistryWordListDesc
[] = {
39 struct OptionsRegistry
{
48 struct OptionSetRegistry
: public OptionSet
<OptionsRegistry
> {
50 DefineProperty("fold.compact", &OptionsRegistry::foldCompact
);
51 DefineProperty("fold", &OptionsRegistry::fold
);
52 DefineWordListSets(RegistryWordListDesc
);
56 class LexerRegistry
: public ILexer
{
57 OptionsRegistry options
;
58 OptionSetRegistry optSetRegistry
;
60 static bool IsStringState(int state
) {
61 return (state
== SCE_REG_VALUENAME
|| state
== SCE_REG_STRING
);
64 static bool IsKeyPathState(int state
) {
65 return (state
== SCE_REG_ADDEDKEY
|| state
== SCE_REG_DELETEDKEY
);
68 static bool AtValueType(LexAccessor
&styler
, int start
) {
72 char curr
= styler
.SafeGetCharAt(start
+i
, '\0');
82 static bool IsNextNonWhitespace(LexAccessor
&styler
, int start
, char ch
) {
86 char curr
= styler
.SafeGetCharAt(start
+i
, '\0');
87 char next
= styler
.SafeGetCharAt(start
+i
+1, '\0');
88 bool atEOL
= (curr
== '\r' && next
!= '\n') || (curr
== '\n');
91 } else if (!isspacechar(curr
) || atEOL
) {
98 // Looks for the equal sign at the end of the string
99 static bool AtValueName(LexAccessor
&styler
, int start
) {
102 bool escaped
= false;
105 char curr
= styler
.SafeGetCharAt(start
+i
, '\0');
106 char next
= styler
.SafeGetCharAt(start
+i
+1, '\0');
107 atEOL
= (curr
== '\r' && next
!= '\n') || (curr
== '\n');
112 escaped
= curr
== '\\';
114 return IsNextNonWhitespace(styler
, start
+i
, '=');
122 static bool AtKeyPathEnd(LexAccessor
&styler
, int start
) {
127 char curr
= styler
.SafeGetCharAt(start
+i
, '\0');
128 char next
= styler
.SafeGetCharAt(start
+i
+1, '\0');
129 atEOL
= (curr
== '\r' && next
!= '\n') || (curr
== '\n');
130 if (curr
== ']' || !curr
) {
131 // There's still at least one or more square brackets ahead
138 static bool AtGUID(LexAccessor
&styler
, int start
) {
143 while (portion
< 5) {
146 digit
= styler
.SafeGetCharAt(start
+offset
);
147 if (!(isxdigit(digit
) || digit
== '-')) {
154 count
= (portion
== 4) ? 13 : 5;
156 digit
= styler
.SafeGetCharAt(start
+offset
);
166 virtual ~LexerRegistry() {}
167 virtual int SCI_METHOD
Version() const {
170 virtual void SCI_METHOD
Release() {
173 virtual const char *SCI_METHOD
PropertyNames() {
174 return optSetRegistry
.PropertyNames();
176 virtual int SCI_METHOD
PropertyType(const char *name
) {
177 return optSetRegistry
.PropertyType(name
);
179 virtual const char *SCI_METHOD
DescribeProperty(const char *name
) {
180 return optSetRegistry
.DescribeProperty(name
);
182 virtual int SCI_METHOD
PropertySet(const char *key
, const char *val
) {
183 if (optSetRegistry
.PropertySet(&options
, key
, val
)) {
188 virtual int SCI_METHOD
WordListSet(int, const char *) {
191 virtual void *SCI_METHOD
PrivateCall(int, void *) {
194 static ILexer
*LexerFactoryRegistry() {
195 return new LexerRegistry
;
197 virtual const char *SCI_METHOD
DescribeWordListSets() {
198 return optSetRegistry
.DescribeWordListSets();
200 virtual void SCI_METHOD
Lex(unsigned startPos
,
204 virtual void SCI_METHOD
Fold(unsigned startPos
,
210 void SCI_METHOD
LexerRegistry::Lex(unsigned startPos
,
213 IDocument
*pAccess
) {
214 int beforeGUID
= SCE_REG_DEFAULT
;
215 int beforeEscape
= SCE_REG_DEFAULT
;
216 CharacterSet setOperators
= CharacterSet(CharacterSet::setNone
, "-,.=:\\@()");
217 LexAccessor
styler(pAccess
);
218 StyleContext
context(startPos
, length
, initStyle
, styler
);
219 bool highlight
= true;
220 bool afterEqualSign
= false;
221 while (context
.More()) {
222 if (context
.atLineStart
) {
223 int currPos
= static_cast<int>(context
.currentPos
);
224 bool continued
= styler
[currPos
-3] == '\\';
225 highlight
= continued
? true : false;
227 switch (context
.state
) {
228 case SCE_REG_COMMENT
:
229 if (context
.atLineEnd
) {
230 context
.SetState(SCE_REG_DEFAULT
);
233 case SCE_REG_VALUENAME
:
234 case SCE_REG_STRING
: {
235 int currPos
= static_cast<int>(context
.currentPos
);
236 if (context
.ch
== '"') {
237 context
.ForwardSetState(SCE_REG_DEFAULT
);
238 } else if (context
.ch
== '\\') {
239 beforeEscape
= context
.state
;
240 context
.SetState(SCE_REG_ESCAPED
);
242 } else if (context
.ch
== '{') {
243 if (AtGUID(styler
, currPos
)) {
244 beforeGUID
= context
.state
;
245 context
.SetState(SCE_REG_STRING_GUID
);
248 if (context
.state
== SCE_REG_STRING
&&
250 (isdigit(context
.chNext
) || context
.chNext
== '*')) {
251 context
.SetState(SCE_REG_PARAMETER
);
255 case SCE_REG_PARAMETER
:
256 context
.ForwardSetState(SCE_REG_STRING
);
257 if (context
.ch
== '"') {
258 context
.ForwardSetState(SCE_REG_DEFAULT
);
261 case SCE_REG_VALUETYPE
:
262 if (context
.ch
== ':') {
263 context
.SetState(SCE_REG_DEFAULT
);
264 afterEqualSign
= false;
267 case SCE_REG_HEXDIGIT
:
268 case SCE_REG_OPERATOR
:
269 context
.SetState(SCE_REG_DEFAULT
);
271 case SCE_REG_DELETEDKEY
:
272 case SCE_REG_ADDEDKEY
: {
273 int currPos
= static_cast<int>(context
.currentPos
);
274 if (context
.ch
== ']' && AtKeyPathEnd(styler
, currPos
)) {
275 context
.ForwardSetState(SCE_REG_DEFAULT
);
276 } else if (context
.ch
== '{') {
277 if (AtGUID(styler
, currPos
)) {
278 beforeGUID
= context
.state
;
279 context
.SetState(SCE_REG_KEYPATH_GUID
);
284 case SCE_REG_ESCAPED
:
285 if (context
.ch
== '"') {
286 context
.SetState(beforeEscape
);
287 context
.ForwardSetState(SCE_REG_DEFAULT
);
288 } else if (context
.ch
== '\\') {
291 context
.SetState(beforeEscape
);
292 beforeEscape
= SCE_REG_DEFAULT
;
295 case SCE_REG_STRING_GUID
:
296 case SCE_REG_KEYPATH_GUID
: {
297 if (context
.ch
== '}') {
298 context
.ForwardSetState(beforeGUID
);
299 beforeGUID
= SCE_REG_DEFAULT
;
301 int currPos
= static_cast<int>(context
.currentPos
);
302 if (context
.ch
== '"' && IsStringState(context
.state
)) {
303 context
.ForwardSetState(SCE_REG_DEFAULT
);
304 } else if (context
.ch
== ']' &&
305 AtKeyPathEnd(styler
, currPos
) &&
306 IsKeyPathState(context
.state
)) {
307 context
.ForwardSetState(SCE_REG_DEFAULT
);
308 } else if (context
.ch
== '\\' && IsStringState(context
.state
)) {
309 beforeEscape
= context
.state
;
310 context
.SetState(SCE_REG_ESCAPED
);
316 // Determine if a new state should be entered.
317 if (context
.state
== SCE_REG_DEFAULT
) {
318 int currPos
= static_cast<int>(context
.currentPos
);
319 if (context
.ch
== ';') {
320 context
.SetState(SCE_REG_COMMENT
);
321 } else if (context
.ch
== '"') {
322 if (AtValueName(styler
, currPos
)) {
323 context
.SetState(SCE_REG_VALUENAME
);
325 context
.SetState(SCE_REG_STRING
);
327 } else if (context
.ch
== '[') {
328 if (IsNextNonWhitespace(styler
, currPos
, '-')) {
329 context
.SetState(SCE_REG_DELETEDKEY
);
331 context
.SetState(SCE_REG_ADDEDKEY
);
333 } else if (context
.ch
== '=') {
334 afterEqualSign
= true;
336 } else if (afterEqualSign
) {
337 bool wordStart
= isalpha(context
.ch
) && !isalpha(context
.chPrev
);
338 if (wordStart
&& AtValueType(styler
, currPos
)) {
339 context
.SetState(SCE_REG_VALUETYPE
);
341 } else if (isxdigit(context
.ch
) && highlight
) {
342 context
.SetState(SCE_REG_HEXDIGIT
);
344 highlight
= (context
.ch
== '@') ? true : highlight
;
345 if (setOperators
.Contains(context
.ch
) && highlight
) {
346 context
.SetState(SCE_REG_OPERATOR
);
354 // Folding similar to that of FoldPropsDoc in LexOthers
355 void SCI_METHOD
LexerRegistry::Fold(unsigned startPos
,
358 IDocument
*pAccess
) {
362 LexAccessor
styler(pAccess
);
363 int currLine
= styler
.GetLine(startPos
);
364 int visibleChars
= 0;
365 unsigned endPos
= startPos
+ length
;
366 bool atKeyPath
= false;
367 for (unsigned i
= startPos
; i
< endPos
; i
++) {
368 atKeyPath
= IsKeyPathState(styler
.StyleAt(i
)) ? true : atKeyPath
;
369 char curr
= styler
.SafeGetCharAt(i
);
370 char next
= styler
.SafeGetCharAt(i
+1);
371 bool atEOL
= (curr
== '\r' && next
!= '\n') || (curr
== '\n');
372 if (atEOL
|| i
== (endPos
-1)) {
373 int level
= SC_FOLDLEVELBASE
;
375 int prevLevel
= styler
.LevelAt(currLine
-1);
376 if (prevLevel
& SC_FOLDLEVELHEADERFLAG
) {
382 if (!visibleChars
&& options
.foldCompact
) {
383 level
|= SC_FOLDLEVELWHITEFLAG
;
384 } else if (atKeyPath
) {
385 level
= SC_FOLDLEVELBASE
| SC_FOLDLEVELHEADERFLAG
;
387 if (level
!= styler
.LevelAt(currLine
)) {
388 styler
.SetLevel(currLine
, level
);
394 if (!isspacechar(curr
)) {
399 // Make the folding reach the last line in the file
400 int level
= SC_FOLDLEVELBASE
;
402 int prevLevel
= styler
.LevelAt(currLine
-1);
403 if (prevLevel
& SC_FOLDLEVELHEADERFLAG
) {
409 styler
.SetLevel(currLine
, level
);
412 LexerModule
lmRegistry(SCLEX_REGISTRY
,
413 LexerRegistry::LexerFactoryRegistry
,
415 RegistryWordListDesc
);