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.
18 #pragma warning(disable: 4786)
25 #include "Scintilla.h"
29 #include "LexAccessor.h"
30 #include "StyleContext.h"
31 #include "CharacterSet.h"
32 #include "LexerModule.h"
33 #include "OptionSet.h"
36 using namespace Scintilla
;
39 /* Nested comments require keeping the value of the nesting level for every
40 position in the document. But since scintilla always styles line by line,
41 we only need to store one value per line. The non-negative number indicates
42 nesting level at the end of the line.
45 // Underscore, letter, digit and universal alphas from C99 Appendix D.
47 static bool IsWordStart(int ch
) {
48 return (isascii(ch
) && (isalpha(ch
) || ch
== '_')) || !isascii(ch
);
51 static bool IsWord(int ch
) {
52 return (isascii(ch
) && (isalnum(ch
) || ch
== '_')) || !isascii(ch
);
55 static bool IsDoxygen(int ch
) {
56 if (isascii(ch
) && islower(ch
))
58 if (ch
== '$' || ch
== '@' || ch
== '\\' ||
59 ch
== '&' || ch
== '#' || ch
== '<' || ch
== '>' ||
60 ch
== '{' || ch
== '}' || ch
== '[' || ch
== ']')
65 static bool IsStringSuffix(int ch
) {
66 return ch
== 'c' || ch
== 'w' || ch
== 'd';
69 static bool IsStreamCommentStyle(int style
) {
70 return style
== SCE_D_COMMENT
||
71 style
== SCE_D_COMMENTDOC
||
72 style
== SCE_D_COMMENTDOCKEYWORD
||
73 style
== SCE_D_COMMENTDOCKEYWORDERROR
;
76 // An individual named option for use in an OptionSet
78 // Options used for LexerD
83 bool foldCommentMultiline
;
84 bool foldCommentExplicit
;
85 std::string foldExplicitStart
;
86 std::string foldExplicitEnd
;
87 bool foldExplicitAnywhere
;
93 foldSyntaxBased
= true;
95 foldCommentMultiline
= true;
96 foldCommentExplicit
= true;
97 foldExplicitStart
= "";
99 foldExplicitAnywhere
= false;
106 static const char * const dWordLists
[] = {
107 "Primary keywords and identifiers",
108 "Secondary keywords and identifiers",
109 "Documentation comment keywords",
110 "Type definitions and aliases",
117 struct OptionSetD
: public OptionSet
<OptionsD
> {
119 DefineProperty("fold", &OptionsD::fold
);
121 DefineProperty("fold.d.syntax.based", &OptionsD::foldSyntaxBased
,
122 "Set this property to 0 to disable syntax based folding.");
124 DefineProperty("fold.comment", &OptionsD::foldComment
);
126 DefineProperty("fold.d.comment.multiline", &OptionsD::foldCommentMultiline
,
127 "Set this property to 0 to disable folding multi-line comments when fold.comment=1.");
129 DefineProperty("fold.d.comment.explicit", &OptionsD::foldCommentExplicit
,
130 "Set this property to 0 to disable folding explicit fold points when fold.comment=1.");
132 DefineProperty("fold.d.explicit.start", &OptionsD::foldExplicitStart
,
133 "The string to use for explicit fold start points, replacing the standard //{.");
135 DefineProperty("fold.d.explicit.end", &OptionsD::foldExplicitEnd
,
136 "The string to use for explicit fold end points, replacing the standard //}.");
138 DefineProperty("fold.d.explicit.anywhere", &OptionsD::foldExplicitAnywhere
,
139 "Set this property to 1 to enable explicit fold points anywhere, not just in line comments.");
141 DefineProperty("fold.compact", &OptionsD::foldCompact
);
143 DefineProperty("lexer.d.fold.at.else", &OptionsD::foldAtElseInt
,
144 "This option enables D folding on a \"} else {\" line of an if statement.");
146 DefineProperty("fold.at.else", &OptionsD::foldAtElse
);
148 DefineWordListSets(dWordLists
);
152 class LexerD
: public ILexer
{
164 LexerD(bool caseSensitive_
) :
165 caseSensitive(caseSensitive_
) {
169 void SCI_METHOD
Release() {
172 int SCI_METHOD
Version() const {
175 const char * SCI_METHOD
PropertyNames() {
176 return osD
.PropertyNames();
178 int SCI_METHOD
PropertyType(const char *name
) {
179 return osD
.PropertyType(name
);
181 const char * SCI_METHOD
DescribeProperty(const char *name
) {
182 return osD
.DescribeProperty(name
);
184 int SCI_METHOD
PropertySet(const char *key
, const char *val
);
185 const char * SCI_METHOD
DescribeWordListSets() {
186 return osD
.DescribeWordListSets();
188 int SCI_METHOD
WordListSet(int n
, const char *wl
);
189 void SCI_METHOD
Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
190 void SCI_METHOD
Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
);
192 void * SCI_METHOD
PrivateCall(int, void *) {
196 static ILexer
*LexerFactoryD() {
197 return new LexerD(true);
199 static ILexer
*LexerFactoryDInsensitive() {
200 return new LexerD(false);
204 int SCI_METHOD
LexerD::PropertySet(const char *key
, const char *val
) {
205 if (osD
.PropertySet(&options
, key
, val
)) {
211 int SCI_METHOD
LexerD::WordListSet(int n
, const char *wl
) {
212 WordList
*wordListN
= 0;
215 wordListN
= &keywords
;
218 wordListN
= &keywords2
;
221 wordListN
= &keywords3
;
224 wordListN
= &keywords4
;
227 wordListN
= &keywords5
;
230 wordListN
= &keywords6
;
233 wordListN
= &keywords7
;
236 int firstModification
= -1;
240 if (*wordListN
!= wlNew
) {
242 firstModification
= 0;
245 return firstModification
;
248 void SCI_METHOD
LexerD::Lex(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
249 LexAccessor
styler(pAccess
);
251 int styleBeforeDCKeyword
= SCE_D_DEFAULT
;
253 StyleContext
sc(startPos
, length
, initStyle
, styler
);
255 int curLine
= styler
.GetLine(startPos
);
256 int curNcLevel
= curLine
> 0? styler
.GetLineState(curLine
-1): 0;
257 bool numFloat
= false; // Float literals have '+' and '-' signs
260 for (; sc
.More(); sc
.Forward()) {
262 if (sc
.atLineStart
) {
263 curLine
= styler
.GetLine(sc
.currentPos
);
264 styler
.SetLineState(curLine
, curNcLevel
);
267 // Determine if the current state should terminate.
270 sc
.SetState(SCE_D_DEFAULT
);
273 // We accept almost anything because of hex. and number suffixes
274 if (isascii(sc
.ch
) && (isalnum(sc
.ch
) || sc
.ch
== '_')) {
276 } else if (sc
.ch
== '.' && sc
.chNext
!= '.' && !numFloat
) {
277 // Don't parse 0..2 as number.
280 } else if ( ( sc
.ch
== '-' || sc
.ch
== '+' ) && ( /*sign and*/
281 ( !numHex
&& ( sc
.chPrev
== 'e' || sc
.chPrev
== 'E' ) ) || /*decimal or*/
282 ( sc
.chPrev
== 'p' || sc
.chPrev
== 'P' ) ) ) { /*hex*/
283 // Parse exponent sign in float literals: 2e+10 0x2e+10
286 sc
.SetState(SCE_D_DEFAULT
);
289 case SCE_D_IDENTIFIER
:
290 if (!IsWord(sc
.ch
)) {
293 sc
.GetCurrent(s
, sizeof(s
));
295 sc
.GetCurrentLowered(s
, sizeof(s
));
297 if (keywords
.InList(s
)) {
298 sc
.ChangeState(SCE_D_WORD
);
299 } else if (keywords2
.InList(s
)) {
300 sc
.ChangeState(SCE_D_WORD2
);
301 } else if (keywords4
.InList(s
)) {
302 sc
.ChangeState(SCE_D_TYPEDEF
);
303 } else if (keywords5
.InList(s
)) {
304 sc
.ChangeState(SCE_D_WORD5
);
305 } else if (keywords6
.InList(s
)) {
306 sc
.ChangeState(SCE_D_WORD6
);
307 } else if (keywords7
.InList(s
)) {
308 sc
.ChangeState(SCE_D_WORD7
);
310 sc
.SetState(SCE_D_DEFAULT
);
314 if (sc
.Match('*', '/')) {
316 sc
.ForwardSetState(SCE_D_DEFAULT
);
319 case SCE_D_COMMENTDOC
:
320 if (sc
.Match('*', '/')) {
322 sc
.ForwardSetState(SCE_D_DEFAULT
);
323 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
324 // Verify that we have the conditions to mark a comment-doc-keyword
325 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '*') && (!IsASpace(sc
.chNext
))) {
326 styleBeforeDCKeyword
= SCE_D_COMMENTDOC
;
327 sc
.SetState(SCE_D_COMMENTDOCKEYWORD
);
331 case SCE_D_COMMENTLINE
:
332 if (sc
.atLineStart
) {
333 sc
.SetState(SCE_D_DEFAULT
);
336 case SCE_D_COMMENTLINEDOC
:
337 if (sc
.atLineStart
) {
338 sc
.SetState(SCE_D_DEFAULT
);
339 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
340 // Verify that we have the conditions to mark a comment-doc-keyword
341 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '/' || sc
.chPrev
== '!') && (!IsASpace(sc
.chNext
))) {
342 styleBeforeDCKeyword
= SCE_D_COMMENTLINEDOC
;
343 sc
.SetState(SCE_D_COMMENTDOCKEYWORD
);
347 case SCE_D_COMMENTDOCKEYWORD
:
348 if ((styleBeforeDCKeyword
== SCE_D_COMMENTDOC
) && sc
.Match('*', '/')) {
349 sc
.ChangeState(SCE_D_COMMENTDOCKEYWORDERROR
);
351 sc
.ForwardSetState(SCE_D_DEFAULT
);
352 } else if (!IsDoxygen(sc
.ch
)) {
355 sc
.GetCurrent(s
, sizeof(s
));
357 sc
.GetCurrentLowered(s
, sizeof(s
));
359 if (!IsASpace(sc
.ch
) || !keywords3
.InList(s
+ 1)) {
360 sc
.ChangeState(SCE_D_COMMENTDOCKEYWORDERROR
);
362 sc
.SetState(styleBeforeDCKeyword
);
365 case SCE_D_COMMENTNESTED
:
366 if (sc
.Match('+', '/')) {
369 curLine
= styler
.GetLine(sc
.currentPos
);
370 styler
.SetLineState(curLine
, curNcLevel
);
372 if (curNcLevel
== 0) {
373 sc
.ForwardSetState(SCE_D_DEFAULT
);
375 } else if (sc
.Match('/','+')) {
377 curLine
= styler
.GetLine(sc
.currentPos
);
378 styler
.SetLineState(curLine
, curNcLevel
);
384 if (sc
.chNext
== '"' || sc
.chNext
== '\\') {
387 } else if (sc
.ch
== '"') {
388 if(IsStringSuffix(sc
.chNext
))
390 sc
.ForwardSetState(SCE_D_DEFAULT
);
393 case SCE_D_CHARACTER
:
395 sc
.ChangeState(SCE_D_STRINGEOL
);
396 } else if (sc
.ch
== '\\') {
397 if (sc
.chNext
== '\'' || sc
.chNext
== '\\') {
400 } else if (sc
.ch
== '\'') {
401 // Char has no suffixes
402 sc
.ForwardSetState(SCE_D_DEFAULT
);
405 case SCE_D_STRINGEOL
:
406 if (sc
.atLineStart
) {
407 sc
.SetState(SCE_D_DEFAULT
);
412 if(IsStringSuffix(sc
.chNext
))
414 sc
.ForwardSetState(SCE_D_DEFAULT
);
419 if(IsStringSuffix(sc
.chNext
))
421 sc
.ForwardSetState(SCE_D_DEFAULT
);
426 // Determine if a new state should be entered.
427 if (sc
.state
== SCE_D_DEFAULT
) {
428 if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
429 sc
.SetState(SCE_D_NUMBER
);
430 numFloat
= sc
.ch
== '.';
431 // Remember hex literal
432 numHex
= sc
.ch
== '0' && ( sc
.chNext
== 'x' || sc
.chNext
== 'X' );
433 } else if ( (sc
.ch
== 'r' || sc
.ch
== 'x' || sc
.ch
== 'q')
434 && sc
.chNext
== '"' ) {
435 // Limited support for hex and delimited strings: parse as r""
436 sc
.SetState(SCE_D_STRINGR
);
438 } else if (IsWordStart(sc
.ch
) || sc
.ch
== '$') {
439 sc
.SetState(SCE_D_IDENTIFIER
);
440 } else if (sc
.Match('/','+')) {
442 curLine
= styler
.GetLine(sc
.currentPos
);
443 styler
.SetLineState(curLine
, curNcLevel
);
444 sc
.SetState(SCE_D_COMMENTNESTED
);
446 } else if (sc
.Match('/', '*')) {
447 if (sc
.Match("/**") || sc
.Match("/*!")) { // Support of Qt/Doxygen doc. style
448 sc
.SetState(SCE_D_COMMENTDOC
);
450 sc
.SetState(SCE_D_COMMENT
);
452 sc
.Forward(); // Eat the * so it isn't used for the end of the comment
453 } else if (sc
.Match('/', '/')) {
454 if ((sc
.Match("///") && !sc
.Match("////")) || sc
.Match("//!"))
455 // Support of Qt/Doxygen doc. style
456 sc
.SetState(SCE_D_COMMENTLINEDOC
);
458 sc
.SetState(SCE_D_COMMENTLINE
);
459 } else if (sc
.ch
== '"') {
460 sc
.SetState(SCE_D_STRING
);
461 } else if (sc
.ch
== '\'') {
462 sc
.SetState(SCE_D_CHARACTER
);
463 } else if (sc
.ch
== '`') {
464 sc
.SetState(SCE_D_STRINGB
);
465 } else if (isoperator(static_cast<char>(sc
.ch
))) {
466 sc
.SetState(SCE_D_OPERATOR
);
467 if (sc
.ch
== '.' && sc
.chNext
== '.') sc
.Forward(); // Range operator
474 // Store both the current line's fold level and the next lines in the
475 // level store to make it easy to pick up with each increment
476 // and to make it possible to fiddle the current level for "} else {".
478 void SCI_METHOD
LexerD::Fold(unsigned int startPos
, int length
, int initStyle
, IDocument
*pAccess
) {
483 LexAccessor
styler(pAccess
);
485 unsigned int endPos
= startPos
+ length
;
486 int visibleChars
= 0;
487 int lineCurrent
= styler
.GetLine(startPos
);
488 int levelCurrent
= SC_FOLDLEVELBASE
;
490 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
491 int levelMinCurrent
= levelCurrent
;
492 int levelNext
= levelCurrent
;
493 char chNext
= styler
[startPos
];
494 int styleNext
= styler
.StyleAt(startPos
);
495 int style
= initStyle
;
496 bool foldAtElse
= options
.foldAtElseInt
>= 0 ? options
.foldAtElseInt
!= 0 : options
.foldAtElse
;
497 const bool userDefinedFoldMarkers
= !options
.foldExplicitStart
.empty() && !options
.foldExplicitEnd
.empty();
498 for (unsigned int i
= startPos
; i
< endPos
; i
++) {
500 chNext
= styler
.SafeGetCharAt(i
+ 1);
501 int stylePrev
= style
;
503 styleNext
= styler
.StyleAt(i
+ 1);
504 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
505 if (options
.foldComment
&& options
.foldCommentMultiline
&& IsStreamCommentStyle(style
)) {
506 if (!IsStreamCommentStyle(stylePrev
)) {
508 } else if (!IsStreamCommentStyle(styleNext
) && !atEOL
) {
509 // Comments don't end at end of line and the next character may be unstyled.
513 if (options
.foldComment
&& options
.foldCommentExplicit
&& ((style
== SCE_D_COMMENTLINE
) || options
.foldExplicitAnywhere
)) {
514 if (userDefinedFoldMarkers
) {
515 if (styler
.Match(i
, options
.foldExplicitStart
.c_str())) {
517 } else if (styler
.Match(i
, options
.foldExplicitEnd
.c_str())) {
521 if ((ch
== '/') && (chNext
== '/')) {
522 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
523 if (chNext2
== '{') {
525 } else if (chNext2
== '}') {
531 if (options
.foldSyntaxBased
&& (style
== SCE_D_OPERATOR
)) {
533 // Measure the minimum before a '{' to allow
534 // folding on "} else {"
535 if (levelMinCurrent
> levelNext
) {
536 levelMinCurrent
= levelNext
;
539 } else if (ch
== '}') {
543 if (atEOL
|| (i
== endPos
-1)) {
544 if (options
.foldComment
&& options
.foldCommentMultiline
) { // Handle nested comments
546 nc
= styler
.GetLineState(lineCurrent
);
547 nc
-= lineCurrent
>0? styler
.GetLineState(lineCurrent
-1): 0;
550 int levelUse
= levelCurrent
;
551 if (options
.foldSyntaxBased
&& foldAtElse
) {
552 levelUse
= levelMinCurrent
;
554 int lev
= levelUse
| levelNext
<< 16;
555 if (visibleChars
== 0 && options
.foldCompact
)
556 lev
|= SC_FOLDLEVELWHITEFLAG
;
557 if (levelUse
< levelNext
)
558 lev
|= SC_FOLDLEVELHEADERFLAG
;
559 if (lev
!= styler
.LevelAt(lineCurrent
)) {
560 styler
.SetLevel(lineCurrent
, lev
);
563 levelCurrent
= levelNext
;
564 levelMinCurrent
= levelCurrent
;
572 LexerModule
lmD(SCLEX_D
, LexerD::LexerFactoryD
, "d", dWordLists
);