4 ** Copyright (c) 2006 by Waldemar Augustyn <waldemar@wdmsys.com>
6 // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
7 // The License.txt file describes the conditions under which this software may be distributed.
19 #include "StyleContext.h"
21 #include "Scintilla.h"
25 using namespace Scintilla
;
28 /* Nested comments require keeping the value of the nesting level for every
29 position in the document. But since scintilla always styles line by line,
30 we only need to store one value per line. The non-negative number indicates
31 nesting level at the end of the line.
34 // Underscore, letter, digit and universal alphas from C99 Appendix D.
36 static bool IsWordStart(int ch
) {
37 return (isascii(ch
) && (isalpha(ch
) || ch
== '_')) || !isascii(ch
);
40 static bool IsWord(int ch
) {
41 return (isascii(ch
) && (isalnum(ch
) || ch
== '_')) || !isascii(ch
);
44 static bool IsDoxygen(int ch
) {
45 if (isascii(ch
) && islower(ch
))
47 if (ch
== '$' || ch
== '@' || ch
== '\\' ||
48 ch
== '&' || ch
== '#' || ch
== '<' || ch
== '>' ||
49 ch
== '{' || ch
== '}' || ch
== '[' || ch
== ']')
54 static bool IsStringSuffix(int ch
) {
55 return ch
== 'c' || ch
== 'w' || ch
== 'd';
59 static void ColouriseDoc(unsigned int startPos
, int length
, int initStyle
,
60 WordList
*keywordlists
[], Accessor
&styler
, bool caseSensitive
) {
62 WordList
&keywords
= *keywordlists
[0];
63 WordList
&keywords2
= *keywordlists
[1];
64 WordList
&keywords3
= *keywordlists
[2]; //doxygen
65 WordList
&keywords4
= *keywordlists
[3];
66 WordList
&keywords5
= *keywordlists
[4];
67 WordList
&keywords6
= *keywordlists
[5];
68 WordList
&keywords7
= *keywordlists
[6];
70 int styleBeforeDCKeyword
= SCE_D_DEFAULT
;
72 StyleContext
sc(startPos
, length
, initStyle
, styler
);
74 int curLine
= styler
.GetLine(startPos
);
75 int curNcLevel
= curLine
> 0? styler
.GetLineState(curLine
-1): 0;
76 bool numFloat
= false; // Float literals have '+' and '-' signs
79 for (; sc
.More(); sc
.Forward()) {
82 curLine
= styler
.GetLine(sc
.currentPos
);
83 styler
.SetLineState(curLine
, curNcLevel
);
86 // Determine if the current state should terminate.
89 sc
.SetState(SCE_D_DEFAULT
);
92 // We accept almost anything because of hex. and number suffixes
93 if (isascii(sc
.ch
) && (isalnum(sc
.ch
) || sc
.ch
== '_')) {
95 } else if (sc
.ch
== '.' && sc
.chNext
!= '.' && !numFloat
) {
96 // Don't parse 0..2 as number.
99 } else if ( ( sc
.ch
== '-' || sc
.ch
== '+' ) && ( /*sign and*/
100 ( !numHex
&& ( sc
.chPrev
== 'e' || sc
.chPrev
== 'E' ) ) || /*decimal or*/
101 ( sc
.chPrev
== 'p' || sc
.chPrev
== 'P' ) ) ) { /*hex*/
102 // Parse exponent sign in float literals: 2e+10 0x2e+10
105 sc
.SetState(SCE_D_DEFAULT
);
108 case SCE_D_IDENTIFIER
:
109 if (!IsWord(sc
.ch
)) {
112 sc
.GetCurrent(s
, sizeof(s
));
114 sc
.GetCurrentLowered(s
, sizeof(s
));
116 if (keywords
.InList(s
)) {
117 sc
.ChangeState(SCE_D_WORD
);
118 } else if (keywords2
.InList(s
)) {
119 sc
.ChangeState(SCE_D_WORD2
);
120 } else if (keywords4
.InList(s
)) {
121 sc
.ChangeState(SCE_D_TYPEDEF
);
122 } else if (keywords5
.InList(s
)) {
123 sc
.ChangeState(SCE_D_WORD5
);
124 } else if (keywords6
.InList(s
)) {
125 sc
.ChangeState(SCE_D_WORD6
);
126 } else if (keywords7
.InList(s
)) {
127 sc
.ChangeState(SCE_D_WORD7
);
129 sc
.SetState(SCE_D_DEFAULT
);
133 if (sc
.Match('*', '/')) {
135 sc
.ForwardSetState(SCE_D_DEFAULT
);
138 case SCE_D_COMMENTDOC
:
139 if (sc
.Match('*', '/')) {
141 sc
.ForwardSetState(SCE_D_DEFAULT
);
142 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
143 // Verify that we have the conditions to mark a comment-doc-keyword
144 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '*') && (!IsASpace(sc
.chNext
))) {
145 styleBeforeDCKeyword
= SCE_D_COMMENTDOC
;
146 sc
.SetState(SCE_D_COMMENTDOCKEYWORD
);
150 case SCE_D_COMMENTLINE
:
151 if (sc
.atLineStart
) {
152 sc
.SetState(SCE_D_DEFAULT
);
155 case SCE_D_COMMENTLINEDOC
:
156 if (sc
.atLineStart
) {
157 sc
.SetState(SCE_D_DEFAULT
);
158 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
159 // Verify that we have the conditions to mark a comment-doc-keyword
160 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '/' || sc
.chPrev
== '!') && (!IsASpace(sc
.chNext
))) {
161 styleBeforeDCKeyword
= SCE_D_COMMENTLINEDOC
;
162 sc
.SetState(SCE_D_COMMENTDOCKEYWORD
);
166 case SCE_D_COMMENTDOCKEYWORD
:
167 if ((styleBeforeDCKeyword
== SCE_D_COMMENTDOC
) && sc
.Match('*', '/')) {
168 sc
.ChangeState(SCE_D_COMMENTDOCKEYWORDERROR
);
170 sc
.ForwardSetState(SCE_D_DEFAULT
);
171 } else if (!IsDoxygen(sc
.ch
)) {
174 sc
.GetCurrent(s
, sizeof(s
));
176 sc
.GetCurrentLowered(s
, sizeof(s
));
178 if (!IsASpace(sc
.ch
) || !keywords3
.InList(s
+ 1)) {
179 sc
.ChangeState(SCE_D_COMMENTDOCKEYWORDERROR
);
181 sc
.SetState(styleBeforeDCKeyword
);
184 case SCE_D_COMMENTNESTED
:
185 if (sc
.Match('+', '/')) {
188 curLine
= styler
.GetLine(sc
.currentPos
);
189 styler
.SetLineState(curLine
, curNcLevel
);
191 if (curNcLevel
== 0) {
192 sc
.ForwardSetState(SCE_D_DEFAULT
);
194 } else if (sc
.Match('/','+')) {
196 curLine
= styler
.GetLine(sc
.currentPos
);
197 styler
.SetLineState(curLine
, curNcLevel
);
203 if (sc
.chNext
== '"' || sc
.chNext
== '\\') {
206 } else if (sc
.ch
== '"') {
207 if(IsStringSuffix(sc
.chNext
))
209 sc
.ForwardSetState(SCE_D_DEFAULT
);
212 case SCE_D_CHARACTER
:
214 sc
.ChangeState(SCE_D_STRINGEOL
);
215 } else if (sc
.ch
== '\\') {
216 if (sc
.chNext
== '\'' || sc
.chNext
== '\\') {
219 } else if (sc
.ch
== '\'') {
220 // Char has no suffixes
221 sc
.ForwardSetState(SCE_D_DEFAULT
);
224 case SCE_D_STRINGEOL
:
225 if (sc
.atLineStart
) {
226 sc
.SetState(SCE_D_DEFAULT
);
231 if(IsStringSuffix(sc
.chNext
))
233 sc
.ForwardSetState(SCE_D_DEFAULT
);
238 if(IsStringSuffix(sc
.chNext
))
240 sc
.ForwardSetState(SCE_D_DEFAULT
);
245 // Determine if a new state should be entered.
246 if (sc
.state
== SCE_D_DEFAULT
) {
247 if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
248 sc
.SetState(SCE_D_NUMBER
);
249 numFloat
= sc
.ch
== '.';
250 // Remember hex literal
251 numHex
= sc
.ch
== '0' && ( sc
.chNext
== 'x' || sc
.chNext
== 'X' );
252 } else if ( (sc
.ch
== 'r' || sc
.ch
== 'x' || sc
.ch
== 'q')
253 && sc
.chNext
== '"' ) {
254 // Limited support for hex and delimited strings: parse as r""
255 sc
.SetState(SCE_D_STRINGR
);
257 } else if (IsWordStart(sc
.ch
) || sc
.ch
== '$') {
258 sc
.SetState(SCE_D_IDENTIFIER
);
259 } else if (sc
.Match('/','+')) {
261 curLine
= styler
.GetLine(sc
.currentPos
);
262 styler
.SetLineState(curLine
, curNcLevel
);
263 sc
.SetState(SCE_D_COMMENTNESTED
);
265 } else if (sc
.Match('/', '*')) {
266 if (sc
.Match("/**") || sc
.Match("/*!")) { // Support of Qt/Doxygen doc. style
267 sc
.SetState(SCE_D_COMMENTDOC
);
269 sc
.SetState(SCE_D_COMMENT
);
271 sc
.Forward(); // Eat the * so it isn't used for the end of the comment
272 } else if (sc
.Match('/', '/')) {
273 if ((sc
.Match("///") && !sc
.Match("////")) || sc
.Match("//!"))
274 // Support of Qt/Doxygen doc. style
275 sc
.SetState(SCE_D_COMMENTLINEDOC
);
277 sc
.SetState(SCE_D_COMMENTLINE
);
278 } else if (sc
.ch
== '"') {
279 sc
.SetState(SCE_D_STRING
);
280 } else if (sc
.ch
== '\'') {
281 sc
.SetState(SCE_D_CHARACTER
);
282 } else if (sc
.ch
== '`') {
283 sc
.SetState(SCE_D_STRINGB
);
284 } else if (isoperator(static_cast<char>(sc
.ch
))) {
285 sc
.SetState(SCE_D_OPERATOR
);
286 if (sc
.ch
== '.' && sc
.chNext
== '.') sc
.Forward(); // Range operator
293 static bool IsStreamCommentStyle(int style
) {
294 return style
== SCE_D_COMMENT
||
295 style
== SCE_D_COMMENTDOC
||
296 style
== SCE_D_COMMENTDOCKEYWORD
||
297 style
== SCE_D_COMMENTDOCKEYWORDERROR
;
300 // Store both the current line's fold level and the next lines in the
301 // level store to make it easy to pick up with each increment
302 // and to make it possible to fiddle the current level for "} else {".
303 static void FoldDoc(unsigned int startPos
, int length
, int initStyle
, Accessor
&styler
) {
304 bool foldComment
= styler
.GetPropertyInt("fold.comment") != 0;
305 bool foldCompact
= styler
.GetPropertyInt("fold.compact", 1) != 0;
307 // property lexer.d.fold.at.else
308 // This option enables D folding on a "} else {" line of an if statement.
309 bool foldAtElse
= styler
.GetPropertyInt("lexer.d.fold.at.else",
310 styler
.GetPropertyInt("fold.at.else", 0)) != 0;
311 unsigned int endPos
= startPos
+ length
;
312 int visibleChars
= 0;
313 int lineCurrent
= styler
.GetLine(startPos
);
314 int levelCurrent
= SC_FOLDLEVELBASE
;
316 levelCurrent
= styler
.LevelAt(lineCurrent
-1) >> 16;
317 int levelMinCurrent
= levelCurrent
;
318 int levelNext
= levelCurrent
;
319 char chNext
= styler
[startPos
];
320 int styleNext
= styler
.StyleAt(startPos
);
321 int style
= initStyle
;
322 for (unsigned int i
= startPos
; i
< endPos
; i
++) {
324 chNext
= styler
.SafeGetCharAt(i
+ 1);
325 int stylePrev
= style
;
327 styleNext
= styler
.StyleAt(i
+ 1);
328 bool atEOL
= (ch
== '\r' && chNext
!= '\n') || (ch
== '\n');
329 if (foldComment
&& IsStreamCommentStyle(style
)) {
330 if (!IsStreamCommentStyle(stylePrev
)) {
332 } else if (!IsStreamCommentStyle(styleNext
) && !atEOL
) {
333 // Comments don't end at end of line and the next character may be unstyled.
337 if (style
== SCE_D_OPERATOR
) {
339 // Measure the minimum before a '{' to allow
340 // folding on "} else {"
341 if (levelMinCurrent
> levelNext
) {
342 levelMinCurrent
= levelNext
;
345 } else if (ch
== '}') {
350 if (foldComment
) { // Handle nested comments
352 nc
= styler
.GetLineState(lineCurrent
);
353 nc
-= lineCurrent
>0? styler
.GetLineState(lineCurrent
-1): 0;
356 int levelUse
= levelCurrent
;
358 levelUse
= levelMinCurrent
;
360 int lev
= levelUse
| levelNext
<< 16;
361 if (visibleChars
== 0 && foldCompact
)
362 lev
|= SC_FOLDLEVELWHITEFLAG
;
363 if (levelUse
< levelNext
)
364 lev
|= SC_FOLDLEVELHEADERFLAG
;
365 if (lev
!= styler
.LevelAt(lineCurrent
)) {
366 styler
.SetLevel(lineCurrent
, lev
);
369 levelCurrent
= levelNext
;
370 levelMinCurrent
= levelCurrent
;
378 static void FoldDDoc(unsigned int startPos
, int length
, int initStyle
,
379 WordList
*[], Accessor
&styler
) {
380 FoldDoc(startPos
, length
, initStyle
, styler
);
383 static const char * const dWordLists
[] = {
384 "Primary keywords and identifiers",
385 "Secondary keywords and identifiers",
386 "Documentation comment keywords",
387 "Type definitions and aliases",
394 static void ColouriseDDoc(unsigned int startPos
, int length
,
395 int initStyle
, WordList
*keywordlists
[], Accessor
&styler
) {
396 ColouriseDoc(startPos
, length
, initStyle
, keywordlists
, styler
, true);
399 LexerModule
lmD(SCLEX_D
, ColouriseDDoc
, "d", FoldDDoc
, dWordLists
);