4 ** Copyright (c) 2006 by Waldemar Augustyn <waldemar@wdmsys.com>
5 ** Converted to lexer object and added further folding features/properties by "Udo Lechner" <dlchnr(at)gmx(dot)net>
7 // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
8 // The License.txt file describes the conditions under which this software may be distributed.
21 #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 /* Nested comments require keeping the value of the nesting level for every
36 position in the document. But since scintilla always styles line by line,
37 we only need to store one value per line. The non-negative number indicates
38 nesting level at the end of the line.
41 // Underscore, letter, digit and universal alphas from C99 Appendix D.
43 static bool IsWordStart(int ch
) {
44 return (isascii(ch
) && (isalpha(ch
) || ch
== '_')) || !isascii(ch
);
47 static bool IsWord(int ch
) {
48 return (isascii(ch
) && (isalnum(ch
) || ch
== '_')) || !isascii(ch
);
51 static bool IsDoxygen(int ch
) {
52 if (isascii(ch
) && islower(ch
))
54 if (ch
== '$' || ch
== '@' || ch
== '\\' ||
55 ch
== '&' || ch
== '#' || ch
== '<' || ch
== '>' ||
56 ch
== '{' || ch
== '}' || ch
== '[' || ch
== ']')
61 static bool IsStringSuffix(int ch
) {
62 return ch
== 'c' || ch
== 'w' || ch
== 'd';
65 static bool IsStreamCommentStyle(int style
) {
66 return style
== SCE_D_COMMENT
||
67 style
== SCE_D_COMMENTDOC
||
68 style
== SCE_D_COMMENTDOCKEYWORD
||
69 style
== SCE_D_COMMENTDOCKEYWORDERROR
;
72 // An individual named option for use in an OptionSet
74 // Options used for LexerD
79 bool foldCommentMultiline
;
80 bool foldCommentExplicit
;
81 std::string foldExplicitStart
;
82 std::string foldExplicitEnd
;
83 bool foldExplicitAnywhere
;
89 foldSyntaxBased
= true;
91 foldCommentMultiline
= true;
92 foldCommentExplicit
= true;
93 foldExplicitStart
= "";
95 foldExplicitAnywhere
= false;
102 static const char * const dWordLists
[] = {
103 "Primary keywords and identifiers",
104 "Secondary keywords and identifiers",
105 "Documentation comment keywords",
106 "Type definitions and aliases",
113 struct OptionSetD
: public OptionSet
<OptionsD
> {
115 DefineProperty("fold", &OptionsD::fold
);
117 DefineProperty("fold.d.syntax.based", &OptionsD::foldSyntaxBased
,
118 "Set this property to 0 to disable syntax based folding.");
120 DefineProperty("fold.comment", &OptionsD::foldComment
);
122 DefineProperty("fold.d.comment.multiline", &OptionsD::foldCommentMultiline
,
123 "Set this property to 0 to disable folding multi-line comments when fold.comment=1.");
125 DefineProperty("fold.d.comment.explicit", &OptionsD::foldCommentExplicit
,
126 "Set this property to 0 to disable folding explicit fold points when fold.comment=1.");
128 DefineProperty("fold.d.explicit.start", &OptionsD::foldExplicitStart
,
129 "The string to use for explicit fold start points, replacing the standard //{.");
131 DefineProperty("fold.d.explicit.end", &OptionsD::foldExplicitEnd
,
132 "The string to use for explicit fold end points, replacing the standard //}.");
134 DefineProperty("fold.d.explicit.anywhere", &OptionsD::foldExplicitAnywhere
,
135 "Set this property to 1 to enable explicit fold points anywhere, not just in line comments.");
137 DefineProperty("fold.compact", &OptionsD::foldCompact
);
139 DefineProperty("lexer.d.fold.at.else", &OptionsD::foldAtElseInt
,
140 "This option enables D folding on a \"} else {\" line of an if statement.");
142 DefineProperty("fold.at.else", &OptionsD::foldAtElse
);
144 DefineWordListSets(dWordLists
);
148 class LexerD
: public ILexer
{
160 LexerD(bool caseSensitive_
) :
161 caseSensitive(caseSensitive_
) {
165 void SCI_METHOD
Release() {
168 int SCI_METHOD
Version() const {
171 const char * SCI_METHOD
PropertyNames() {
172 return osD
.PropertyNames();
174 int SCI_METHOD
PropertyType(const char *name
) {
175 return osD
.PropertyType(name
);
177 const char * SCI_METHOD
DescribeProperty(const char *name
) {
178 return osD
.DescribeProperty(name
);
180 int SCI_METHOD
PropertySet(const char *key
, const char *val
);
181 const char * SCI_METHOD
DescribeWordListSets() {
182 return osD
.DescribeWordListSets();
184 int SCI_METHOD
WordListSet(int n
, const char *wl
);
185 void SCI_METHOD
Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
186 void SCI_METHOD
Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
188 void * SCI_METHOD
PrivateCall(int, void *) {
192 static ILexer
*LexerFactoryD() {
193 return new LexerD(true);
195 static ILexer
*LexerFactoryDInsensitive() {
196 return new LexerD(false);
200 int SCI_METHOD
LexerD::PropertySet(const char *key
, const char *val
) {
201 if (osD
.PropertySet(&options
, key
, val
)) {
207 int SCI_METHOD
LexerD::WordListSet(int n
, const char *wl
) {
208 WordList
*wordListN
= 0;
211 wordListN
= &keywords
;
214 wordListN
= &keywords2
;
217 wordListN
= &keywords3
;
220 wordListN
= &keywords4
;
223 wordListN
= &keywords5
;
226 wordListN
= &keywords6
;
229 wordListN
= &keywords7
;
232 int firstModification
= -1;
236 if (*wordListN
!= wlNew
) {
238 firstModification
= 0;
241 return firstModification
;
244 void SCI_METHOD
LexerD::Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
245 LexAccessor
styler(pAccess
);
247 int styleBeforeDCKeyword
= SCE_D_DEFAULT
;
249 StyleContext
sc(startPos
, length
, initStyle
, styler
);
251 int curLine
= styler
.GetLine(startPos
);
252 int curNcLevel
= curLine
> 0? styler
.GetLineState(curLine
-1): 0;
253 bool numFloat
= false; // Float literals have '+' and '-' signs
256 for (; sc
.More(); sc
.Forward()) {
258 if (sc
.atLineStart
) {
259 curLine
= styler
.GetLine(sc
.currentPos
);
260 styler
.SetLineState(curLine
, curNcLevel
);
263 // Determine if the current state should terminate.
266 sc
.SetState(SCE_D_DEFAULT
);
269 // We accept almost anything because of hex. and number suffixes
270 if (isascii(sc
.ch
) && (isalnum(sc
.ch
) || sc
.ch
== '_')) {
272 } else if (sc
.ch
== '.' && sc
.chNext
!= '.' && !numFloat
) {
273 // Don't parse 0..2 as number.
276 } else if ( ( sc
.ch
== '-' || sc
.ch
== '+' ) && ( /*sign and*/
277 ( !numHex
&& ( sc
.chPrev
== 'e' || sc
.chPrev
== 'E' ) ) || /*decimal or*/
278 ( sc
.chPrev
== 'p' || sc
.chPrev
== 'P' ) ) ) { /*hex*/
279 // Parse exponent sign in float literals: 2e+10 0x2e+10
282 sc
.SetState(SCE_D_DEFAULT
);
285 case SCE_D_IDENTIFIER
:
286 if (!IsWord(sc
.ch
)) {
289 sc
.GetCurrent(s
, sizeof(s
));
291 sc
.GetCurrentLowered(s
, sizeof(s
));
293 if (keywords
.InList(s
)) {
294 sc
.ChangeState(SCE_D_WORD
);
295 } else if (keywords2
.InList(s
)) {
296 sc
.ChangeState(SCE_D_WORD2
);
297 } else if (keywords4
.InList(s
)) {
298 sc
.ChangeState(SCE_D_TYPEDEF
);
299 } else if (keywords5
.InList(s
)) {
300 sc
.ChangeState(SCE_D_WORD5
);
301 } else if (keywords6
.InList(s
)) {
302 sc
.ChangeState(SCE_D_WORD6
);
303 } else if (keywords7
.InList(s
)) {
304 sc
.ChangeState(SCE_D_WORD7
);
306 sc
.SetState(SCE_D_DEFAULT
);
310 if (sc
.Match('*', '/')) {
312 sc
.ForwardSetState(SCE_D_DEFAULT
);
315 case SCE_D_COMMENTDOC
:
316 if (sc
.Match('*', '/')) {
318 sc
.ForwardSetState(SCE_D_DEFAULT
);
319 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
320 // Verify that we have the conditions to mark a comment-doc-keyword
321 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '*') && (!IsASpace(sc
.chNext
))) {
322 styleBeforeDCKeyword
= SCE_D_COMMENTDOC
;
323 sc
.SetState(SCE_D_COMMENTDOCKEYWORD
);
327 case SCE_D_COMMENTLINE
:
328 if (sc
.atLineStart
) {
329 sc
.SetState(SCE_D_DEFAULT
);
332 case SCE_D_COMMENTLINEDOC
:
333 if (sc
.atLineStart
) {
334 sc
.SetState(SCE_D_DEFAULT
);
335 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
336 // Verify that we have the conditions to mark a comment-doc-keyword
337 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '/' || sc
.chPrev
== '!') && (!IsASpace(sc
.chNext
))) {
338 styleBeforeDCKeyword
= SCE_D_COMMENTLINEDOC
;
339 sc
.SetState(SCE_D_COMMENTDOCKEYWORD
);
343 case SCE_D_COMMENTDOCKEYWORD
:
344 if ((styleBeforeDCKeyword
== SCE_D_COMMENTDOC
) && sc
.Match('*', '/')) {
345 sc
.ChangeState(SCE_D_COMMENTDOCKEYWORDERROR
);
347 sc
.ForwardSetState(SCE_D_DEFAULT
);
348 } else if (!IsDoxygen(sc
.ch
)) {
351 sc
.GetCurrent(s
, sizeof(s
));
353 sc
.GetCurrentLowered(s
, sizeof(s
));
355 if (!IsASpace(sc
.ch
) || !keywords3
.InList(s
+ 1)) {
356 sc
.ChangeState(SCE_D_COMMENTDOCKEYWORDERROR
);
358 sc
.SetState(styleBeforeDCKeyword
);
361 case SCE_D_COMMENTNESTED
:
362 if (sc
.Match('+', '/')) {
365 curLine
= styler
.GetLine(sc
.currentPos
);
366 styler
.SetLineState(curLine
, curNcLevel
);
368 if (curNcLevel
== 0) {
369 sc
.ForwardSetState(SCE_D_DEFAULT
);
371 } else if (sc
.Match('/','+')) {
373 curLine
= styler
.GetLine(sc
.currentPos
);
374 styler
.SetLineState(curLine
, curNcLevel
);
380 if (sc
.chNext
== '"' || sc
.chNext
== '\\') {
383 } else if (sc
.ch
== '"') {
384 if(IsStringSuffix(sc
.chNext
))
386 sc
.ForwardSetState(SCE_D_DEFAULT
);
389 case SCE_D_CHARACTER
:
391 sc
.ChangeState(SCE_D_STRINGEOL
);
392 } else if (sc
.ch
== '\\') {
393 if (sc
.chNext
== '\'' || sc
.chNext
== '\\') {
396 } else if (sc
.ch
== '\'') {
397 // Char has no suffixes
398 sc
.ForwardSetState(SCE_D_DEFAULT
);
401 case SCE_D_STRINGEOL
:
402 if (sc
.atLineStart
) {
403 sc
.SetState(SCE_D_DEFAULT
);
408 if(IsStringSuffix(sc
.chNext
))
410 sc
.ForwardSetState(SCE_D_DEFAULT
);
415 if(IsStringSuffix(sc
.chNext
))
417 sc
.ForwardSetState(SCE_D_DEFAULT
);
422 // Determine if a new state should be entered.
423 if (sc
.state
== SCE_D_DEFAULT
) {
424 if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
425 sc
.SetState(SCE_D_NUMBER
);
426 numFloat
= sc
.ch
== '.';
427 // Remember hex literal
428 numHex
= sc
.ch
== '0' && ( sc
.chNext
== 'x' || sc
.chNext
== 'X' );
429 } else if ( (sc
.ch
== 'r' || sc
.ch
== 'x' || sc
.ch
== 'q')
430 && sc
.chNext
== '"' ) {
431 // Limited support for hex and delimited strings: parse as r""
432 sc
.SetState(SCE_D_STRINGR
);
434 } else if (IsWordStart(sc
.ch
) || sc
.ch
== '$') {
435 sc
.SetState(SCE_D_IDENTIFIER
);
436 } else if (sc
.Match('/','+')) {
438 curLine
= styler
.GetLine(sc
.currentPos
);
439 styler
.SetLineState(curLine
, curNcLevel
);
440 sc
.SetState(SCE_D_COMMENTNESTED
);
442 } else if (sc
.Match('/', '*')) {
443 if (sc
.Match("/**") || sc
.Match("/*!")) { // Support of Qt/Doxygen doc. style
444 sc
.SetState(SCE_D_COMMENTDOC
);
446 sc
.SetState(SCE_D_COMMENT
);
448 sc
.Forward(); // Eat the * so it isn't used for the end of the comment
449 } else if (sc
.Match('/', '/')) {
450 if ((sc
.Match("///") && !sc
.Match("////")) || sc
.Match("//!"))
451 // Support of Qt/Doxygen doc. style
452 sc
.SetState(SCE_D_COMMENTLINEDOC
);
454 sc
.SetState(SCE_D_COMMENTLINE
);
455 } else if (sc
.ch
== '"') {
456 sc
.SetState(SCE_D_STRING
);
457 } else if (sc
.ch
== '\'') {
458 sc
.SetState(SCE_D_CHARACTER
);
459 } else if (sc
.ch
== '`') {
460 sc
.SetState(SCE_D_STRINGB
);
461 } else if (isoperator(static_cast<char>(sc
.ch
))) {
462 sc
.SetState(SCE_D_OPERATOR
);
463 if (sc
.ch
== '.' && sc
.chNext
== '.') sc
.Forward(); // Range operator
470 // Store both the current line's fold level and the next lines in the
471 // level store to make it easy to pick up with each increment
472 // and to make it possible to fiddle the current level for "} else {".
474 void SCI_METHOD
LexerD::Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
479 LexAccessor
styler(pAccess
);
481 unsigned int endPos
= startPos
+ length
;
482 int visibleChars
= 0;
483 int lineCurrent
= styler
.GetLine(startPos
);
484 int levelCurrent
= SC_FOLDLEVELBASE
;
486 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
487 int levelMinCurrent
= levelCurrent
;
488 int levelNext
= levelCurrent
;
489 char chNext
= styler
[startPos
];
490 int styleNext
= styler
.StyleAt(startPos
);
491 int style
= initStyle
;
492 bool foldAtElse
= options
.foldAtElseInt
>= 0 ? options
.foldAtElseInt
!= 0 : options
.foldAtElse
;
493 const bool userDefinedFoldMarkers
= !options
.foldExplicitStart
.empty() && !options
.foldExplicitEnd
.empty();
494 for (unsigned int i
= startPos
; i
< endPos
; i
++) {
496 chNext
= styler
.SafeGetCharAt(i
+ 1);
497 int stylePrev
= style
;
499 styleNext
= styler
.StyleAt(i
+ 1);
500 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
501 if (options
.foldComment
&& options
.foldCommentMultiline
&& IsStreamCommentStyle(style
)) {
502 if (!IsStreamCommentStyle(stylePrev
)) {
504 } else if (!IsStreamCommentStyle(styleNext
) && !atEOL
) {
505 // Comments don't end at end of line and the next character may be unstyled.
509 if (options
.foldComment
&& options
.foldCommentExplicit
&& ((style
== SCE_D_COMMENTLINE
) || options
.foldExplicitAnywhere
)) {
510 if (userDefinedFoldMarkers
) {
511 if (styler
.Match(i
, options
.foldExplicitStart
.c_str())) {
513 } else if (styler
.Match(i
, options
.foldExplicitEnd
.c_str())) {
517 if ((ch
== '/') && (chNext
== '/')) {
518 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
519 if (chNext2
== '{') {
521 } else if (chNext2
== '}') {
527 if (options
.foldSyntaxBased
&& (style
== SCE_D_OPERATOR
)) {
529 // Measure the minimum before a '{' to allow
530 // folding on "} else {"
531 if (levelMinCurrent
> levelNext
) {
532 levelMinCurrent
= levelNext
;
535 } else if (ch
== '}') {
539 if (atEOL
|| (i
== endPos
-1)) {
540 if (options
.foldComment
&& options
.foldCommentMultiline
) { // Handle nested comments
542 nc
= styler
.GetLineState(lineCurrent
);
543 nc
-= lineCurrent
>0? styler
.GetLineState(lineCurrent
-1): 0;
546 int levelUse
= levelCurrent
;
547 if (options
.foldSyntaxBased
&& foldAtElse
) {
548 levelUse
= levelMinCurrent
;
550 int lev
= levelUse
| levelNext
<< 16;
551 if (visibleChars
== 0 && options
.foldCompact
)
552 lev
|= SC_FOLDLEVELWHITEFLAG
;
553 if (levelUse
< levelNext
)
554 lev
|= SC_FOLDLEVELHEADERFLAG
;
555 if (lev
!= styler
.LevelAt(lineCurrent
)) {
556 styler
.SetLevel(lineCurrent
, lev
);
559 levelCurrent
= levelNext
;
560 levelMinCurrent
= levelCurrent
;
568 LexerModule
lmD(SCLEX_D
, LexerD::LexerFactoryD
, "d", dWordLists
);