updated Scintilla to 2.29
[TortoiseGit.git] / ext / scintilla / lexers / LexD.cxx
blob810d95257ecce07001cdcc407d31bab4325bffc1
1 /** @file LexD.cxx
2 ** Lexer for D.
3 **
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>
6 **/
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.
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <assert.h>
15 #include <ctype.h>
17 #ifdef _MSC_VER
18 #pragma warning(disable: 4786)
19 #endif
21 #include <string>
22 #include <map>
24 #include "ILexer.h"
25 #include "Scintilla.h"
26 #include "SciLexer.h"
28 #include "WordList.h"
29 #include "LexAccessor.h"
30 #include "StyleContext.h"
31 #include "CharacterSet.h"
32 #include "LexerModule.h"
33 #include "OptionSet.h"
35 #ifdef SCI_NAMESPACE
36 using namespace Scintilla;
37 #endif
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))
57 return true;
58 if (ch == '$' || ch == '@' || ch == '\\' ||
59 ch == '&' || ch == '#' || ch == '<' || ch == '>' ||
60 ch == '{' || ch == '}' || ch == '[' || ch == ']')
61 return true;
62 return false;
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
79 struct OptionsD {
80 bool fold;
81 bool foldSyntaxBased;
82 bool foldComment;
83 bool foldCommentMultiline;
84 bool foldCommentExplicit;
85 std::string foldExplicitStart;
86 std::string foldExplicitEnd;
87 bool foldExplicitAnywhere;
88 bool foldCompact;
89 int foldAtElseInt;
90 bool foldAtElse;
91 OptionsD() {
92 fold = false;
93 foldSyntaxBased = true;
94 foldComment = false;
95 foldCommentMultiline = true;
96 foldCommentExplicit = true;
97 foldExplicitStart = "";
98 foldExplicitEnd = "";
99 foldExplicitAnywhere = false;
100 foldCompact = true;
101 foldAtElseInt = -1;
102 foldAtElse = 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",
111 "Keywords 5",
112 "Keywords 6",
113 "Keywords 7",
117 struct OptionSetD : public OptionSet<OptionsD> {
118 OptionSetD() {
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 {
153 bool caseSensitive;
154 WordList keywords;
155 WordList keywords2;
156 WordList keywords3;
157 WordList keywords4;
158 WordList keywords5;
159 WordList keywords6;
160 WordList keywords7;
161 OptionsD options;
162 OptionSetD osD;
163 public:
164 LexerD(bool caseSensitive_) :
165 caseSensitive(caseSensitive_) {
167 ~LexerD() {
169 void SCI_METHOD Release() {
170 delete this;
172 int SCI_METHOD Version() const {
173 return lvOriginal;
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 *) {
193 return 0;
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)) {
206 return 0;
208 return -1;
211 int SCI_METHOD LexerD::WordListSet(int n, const char *wl) {
212 WordList *wordListN = 0;
213 switch (n) {
214 case 0:
215 wordListN = &keywords;
216 break;
217 case 1:
218 wordListN = &keywords2;
219 break;
220 case 2:
221 wordListN = &keywords3;
222 break;
223 case 3:
224 wordListN = &keywords4;
225 break;
226 case 4:
227 wordListN = &keywords5;
228 break;
229 case 5:
230 wordListN = &keywords6;
231 break;
232 case 6:
233 wordListN = &keywords7;
234 break;
236 int firstModification = -1;
237 if (wordListN) {
238 WordList wlNew;
239 wlNew.Set(wl);
240 if (*wordListN != wlNew) {
241 wordListN->Set(wl);
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
258 bool numHex = false;
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.
268 switch (sc.state) {
269 case SCE_D_OPERATOR:
270 sc.SetState(SCE_D_DEFAULT);
271 break;
272 case SCE_D_NUMBER:
273 // We accept almost anything because of hex. and number suffixes
274 if (isascii(sc.ch) && (isalnum(sc.ch) || sc.ch == '_')) {
275 continue;
276 } else if (sc.ch == '.' && sc.chNext != '.' && !numFloat) {
277 // Don't parse 0..2 as number.
278 numFloat=true;
279 continue;
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
284 continue;
285 } else {
286 sc.SetState(SCE_D_DEFAULT);
288 break;
289 case SCE_D_IDENTIFIER:
290 if (!IsWord(sc.ch)) {
291 char s[1000];
292 if (caseSensitive) {
293 sc.GetCurrent(s, sizeof(s));
294 } else {
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);
312 break;
313 case SCE_D_COMMENT:
314 if (sc.Match('*', '/')) {
315 sc.Forward();
316 sc.ForwardSetState(SCE_D_DEFAULT);
318 break;
319 case SCE_D_COMMENTDOC:
320 if (sc.Match('*', '/')) {
321 sc.Forward();
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);
330 break;
331 case SCE_D_COMMENTLINE:
332 if (sc.atLineStart) {
333 sc.SetState(SCE_D_DEFAULT);
335 break;
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);
346 break;
347 case SCE_D_COMMENTDOCKEYWORD:
348 if ((styleBeforeDCKeyword == SCE_D_COMMENTDOC) && sc.Match('*', '/')) {
349 sc.ChangeState(SCE_D_COMMENTDOCKEYWORDERROR);
350 sc.Forward();
351 sc.ForwardSetState(SCE_D_DEFAULT);
352 } else if (!IsDoxygen(sc.ch)) {
353 char s[100];
354 if (caseSensitive) {
355 sc.GetCurrent(s, sizeof(s));
356 } else {
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);
364 break;
365 case SCE_D_COMMENTNESTED:
366 if (sc.Match('+', '/')) {
367 if (curNcLevel > 0)
368 curNcLevel -= 1;
369 curLine = styler.GetLine(sc.currentPos);
370 styler.SetLineState(curLine, curNcLevel);
371 sc.Forward();
372 if (curNcLevel == 0) {
373 sc.ForwardSetState(SCE_D_DEFAULT);
375 } else if (sc.Match('/','+')) {
376 curNcLevel += 1;
377 curLine = styler.GetLine(sc.currentPos);
378 styler.SetLineState(curLine, curNcLevel);
379 sc.Forward();
381 break;
382 case SCE_D_STRING:
383 if (sc.ch == '\\') {
384 if (sc.chNext == '"' || sc.chNext == '\\') {
385 sc.Forward();
387 } else if (sc.ch == '"') {
388 if(IsStringSuffix(sc.chNext))
389 sc.Forward();
390 sc.ForwardSetState(SCE_D_DEFAULT);
392 break;
393 case SCE_D_CHARACTER:
394 if (sc.atLineEnd) {
395 sc.ChangeState(SCE_D_STRINGEOL);
396 } else if (sc.ch == '\\') {
397 if (sc.chNext == '\'' || sc.chNext == '\\') {
398 sc.Forward();
400 } else if (sc.ch == '\'') {
401 // Char has no suffixes
402 sc.ForwardSetState(SCE_D_DEFAULT);
404 break;
405 case SCE_D_STRINGEOL:
406 if (sc.atLineStart) {
407 sc.SetState(SCE_D_DEFAULT);
409 break;
410 case SCE_D_STRINGB:
411 if (sc.ch == '`') {
412 if(IsStringSuffix(sc.chNext))
413 sc.Forward();
414 sc.ForwardSetState(SCE_D_DEFAULT);
416 break;
417 case SCE_D_STRINGR:
418 if (sc.ch == '"') {
419 if(IsStringSuffix(sc.chNext))
420 sc.Forward();
421 sc.ForwardSetState(SCE_D_DEFAULT);
423 break;
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);
437 sc.Forward();
438 } else if (IsWordStart(sc.ch) || sc.ch == '$') {
439 sc.SetState(SCE_D_IDENTIFIER);
440 } else if (sc.Match('/','+')) {
441 curNcLevel += 1;
442 curLine = styler.GetLine(sc.currentPos);
443 styler.SetLineState(curLine, curNcLevel);
444 sc.SetState(SCE_D_COMMENTNESTED);
445 sc.Forward();
446 } else if (sc.Match('/', '*')) {
447 if (sc.Match("/**") || sc.Match("/*!")) { // Support of Qt/Doxygen doc. style
448 sc.SetState(SCE_D_COMMENTDOC);
449 } else {
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);
457 else
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
471 sc.Complete();
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) {
480 if (!options.fold)
481 return;
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;
489 if (lineCurrent > 0)
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++) {
499 char ch = chNext;
500 chNext = styler.SafeGetCharAt(i + 1);
501 int stylePrev = style;
502 style = styleNext;
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)) {
507 levelNext++;
508 } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
509 // Comments don't end at end of line and the next character may be unstyled.
510 levelNext--;
513 if (options.foldComment && options.foldCommentExplicit && ((style == SCE_D_COMMENTLINE) || options.foldExplicitAnywhere)) {
514 if (userDefinedFoldMarkers) {
515 if (styler.Match(i, options.foldExplicitStart.c_str())) {
516 levelNext++;
517 } else if (styler.Match(i, options.foldExplicitEnd.c_str())) {
518 levelNext--;
520 } else {
521 if ((ch == '/') && (chNext == '/')) {
522 char chNext2 = styler.SafeGetCharAt(i + 2);
523 if (chNext2 == '{') {
524 levelNext++;
525 } else if (chNext2 == '}') {
526 levelNext--;
531 if (options.foldSyntaxBased && (style == SCE_D_OPERATOR)) {
532 if (ch == '{') {
533 // Measure the minimum before a '{' to allow
534 // folding on "} else {"
535 if (levelMinCurrent > levelNext) {
536 levelMinCurrent = levelNext;
538 levelNext++;
539 } else if (ch == '}') {
540 levelNext--;
543 if (atEOL || (i == endPos-1)) {
544 if (options.foldComment && options.foldCommentMultiline) { // Handle nested comments
545 int nc;
546 nc = styler.GetLineState(lineCurrent);
547 nc -= lineCurrent>0? styler.GetLineState(lineCurrent-1): 0;
548 levelNext += nc;
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);
562 lineCurrent++;
563 levelCurrent = levelNext;
564 levelMinCurrent = levelCurrent;
565 visibleChars = 0;
567 if (!IsASpace(ch))
568 visibleChars++;
572 LexerModule lmD(SCLEX_D, LexerD::LexerFactoryD, "d", dWordLists);