]> git.saurik.com Git - wxWidgets.git/blame - src/stc/scintilla/lexers/LexD.cxx
Added ability to switch off more components of the size page UI
[wxWidgets.git] / src / stc / scintilla / lexers / LexD.cxx
CommitLineData
7e0c58e9
RD
1/** @file LexD.cxx
2 ** Lexer for D.
3 **
4 ** Copyright (c) 2006 by Waldemar Augustyn <waldemar@wdmsys.com>
1dcf666d 5 ** Converted to lexer object and added further folding features/properties by "Udo Lechner" <dlchnr(at)gmx(dot)net>
7e0c58e9
RD
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.
9
10#include <stdlib.h>
11#include <string.h>
7e0c58e9
RD
12#include <stdio.h>
13#include <stdarg.h>
1dcf666d
RD
14#include <assert.h>
15#include <ctype.h>
7e0c58e9 16
1dcf666d
RD
17#include <string>
18#include <map>
7e0c58e9 19
1dcf666d 20#include "ILexer.h"
7e0c58e9
RD
21#include "Scintilla.h"
22#include "SciLexer.h"
23
1dcf666d
RD
24#include "WordList.h"
25#include "LexAccessor.h"
26#include "StyleContext.h"
27#include "CharacterSet.h"
28#include "LexerModule.h"
29#include "OptionSet.h"
30
7e0c58e9
RD
31#ifdef SCI_NAMESPACE
32using namespace Scintilla;
33#endif
34
9e96e16f
RD
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.
39*/
7e0c58e9 40
9e96e16f 41// Underscore, letter, digit and universal alphas from C99 Appendix D.
7e0c58e9
RD
42
43static bool IsWordStart(int ch) {
9e96e16f 44 return (isascii(ch) && (isalpha(ch) || ch == '_')) || !isascii(ch);
7e0c58e9
RD
45}
46
47static bool IsWord(int ch) {
9e96e16f 48 return (isascii(ch) && (isalnum(ch) || ch == '_')) || !isascii(ch);
7e0c58e9
RD
49}
50
51static bool IsDoxygen(int ch) {
52 if (isascii(ch) && islower(ch))
53 return true;
54 if (ch == '$' || ch == '@' || ch == '\\' ||
55 ch == '&' || ch == '#' || ch == '<' || ch == '>' ||
56 ch == '{' || ch == '}' || ch == '[' || ch == ']')
57 return true;
58 return false;
59}
60
9e96e16f
RD
61static bool IsStringSuffix(int ch) {
62 return ch == 'c' || ch == 'w' || ch == 'd';
63}
7e0c58e9 64
1dcf666d
RD
65static 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;
70}
71
72// An individual named option for use in an OptionSet
73
74// Options used for LexerD
75struct OptionsD {
76 bool fold;
77 bool foldSyntaxBased;
78 bool foldComment;
79 bool foldCommentMultiline;
80 bool foldCommentExplicit;
81 std::string foldExplicitStart;
82 std::string foldExplicitEnd;
83 bool foldExplicitAnywhere;
84 bool foldCompact;
85 int foldAtElseInt;
86 bool foldAtElse;
87 OptionsD() {
88 fold = false;
89 foldSyntaxBased = true;
90 foldComment = false;
91 foldCommentMultiline = true;
92 foldCommentExplicit = true;
93 foldExplicitStart = "";
94 foldExplicitEnd = "";
95 foldExplicitAnywhere = false;
96 foldCompact = true;
97 foldAtElseInt = -1;
98 foldAtElse = false;
99 }
100};
101
102static const char * const dWordLists[] = {
103 "Primary keywords and identifiers",
104 "Secondary keywords and identifiers",
105 "Documentation comment keywords",
106 "Type definitions and aliases",
107 "Keywords 5",
108 "Keywords 6",
109 "Keywords 7",
110 0,
111 };
112
113struct OptionSetD : public OptionSet<OptionsD> {
114 OptionSetD() {
115 DefineProperty("fold", &OptionsD::fold);
116
117 DefineProperty("fold.d.syntax.based", &OptionsD::foldSyntaxBased,
118 "Set this property to 0 to disable syntax based folding.");
119
120 DefineProperty("fold.comment", &OptionsD::foldComment);
121
122 DefineProperty("fold.d.comment.multiline", &OptionsD::foldCommentMultiline,
123 "Set this property to 0 to disable folding multi-line comments when fold.comment=1.");
124
125 DefineProperty("fold.d.comment.explicit", &OptionsD::foldCommentExplicit,
126 "Set this property to 0 to disable folding explicit fold points when fold.comment=1.");
127
128 DefineProperty("fold.d.explicit.start", &OptionsD::foldExplicitStart,
129 "The string to use for explicit fold start points, replacing the standard //{.");
130
131 DefineProperty("fold.d.explicit.end", &OptionsD::foldExplicitEnd,
132 "The string to use for explicit fold end points, replacing the standard //}.");
133
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.");
7e0c58e9 136
1dcf666d 137 DefineProperty("fold.compact", &OptionsD::foldCompact);
7e0c58e9 138
1dcf666d
RD
139 DefineProperty("lexer.d.fold.at.else", &OptionsD::foldAtElseInt,
140 "This option enables D folding on a \"} else {\" line of an if statement.");
141
142 DefineProperty("fold.at.else", &OptionsD::foldAtElse);
143
144 DefineWordListSets(dWordLists);
145 }
146};
147
148class LexerD : public ILexer {
149 bool caseSensitive;
150 WordList keywords;
151 WordList keywords2;
152 WordList keywords3;
153 WordList keywords4;
154 WordList keywords5;
155 WordList keywords6;
156 WordList keywords7;
157 OptionsD options;
158 OptionSetD osD;
159public:
160 LexerD(bool caseSensitive_) :
161 caseSensitive(caseSensitive_) {
162 }
163 virtual ~LexerD() {
164 }
165 void SCI_METHOD Release() {
166 delete this;
167 }
168 int SCI_METHOD Version() const {
169 return lvOriginal;
170 }
171 const char * SCI_METHOD PropertyNames() {
172 return osD.PropertyNames();
173 }
174 int SCI_METHOD PropertyType(const char *name) {
175 return osD.PropertyType(name);
176 }
177 const char * SCI_METHOD DescribeProperty(const char *name) {
178 return osD.DescribeProperty(name);
179 }
180 int SCI_METHOD PropertySet(const char *key, const char *val);
181 const char * SCI_METHOD DescribeWordListSets() {
182 return osD.DescribeWordListSets();
183 }
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);
187
188 void * SCI_METHOD PrivateCall(int, void *) {
189 return 0;
190 }
191
192 static ILexer *LexerFactoryD() {
193 return new LexerD(true);
194 }
195 static ILexer *LexerFactoryDInsensitive() {
196 return new LexerD(false);
197 }
198};
199
200int SCI_METHOD LexerD::PropertySet(const char *key, const char *val) {
201 if (osD.PropertySet(&options, key, val)) {
202 return 0;
203 }
204 return -1;
205}
206
207int SCI_METHOD LexerD::WordListSet(int n, const char *wl) {
208 WordList *wordListN = 0;
209 switch (n) {
210 case 0:
211 wordListN = &keywords;
212 break;
213 case 1:
214 wordListN = &keywords2;
215 break;
216 case 2:
217 wordListN = &keywords3;
218 break;
219 case 3:
220 wordListN = &keywords4;
221 break;
222 case 4:
223 wordListN = &keywords5;
224 break;
225 case 5:
226 wordListN = &keywords6;
227 break;
228 case 6:
229 wordListN = &keywords7;
230 break;
231 }
232 int firstModification = -1;
233 if (wordListN) {
234 WordList wlNew;
235 wlNew.Set(wl);
236 if (*wordListN != wlNew) {
237 wordListN->Set(wl);
238 firstModification = 0;
239 }
240 }
241 return firstModification;
242}
243
244void SCI_METHOD LexerD::Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
245 LexAccessor styler(pAccess);
7e0c58e9 246
9e96e16f 247 int styleBeforeDCKeyword = SCE_D_DEFAULT;
7e0c58e9 248
9e96e16f 249 StyleContext sc(startPos, length, initStyle, styler);
7e0c58e9 250
9e96e16f
RD
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
254 bool numHex = false;
7e0c58e9 255
9e96e16f 256 for (; sc.More(); sc.Forward()) {
7e0c58e9 257
9e96e16f
RD
258 if (sc.atLineStart) {
259 curLine = styler.GetLine(sc.currentPos);
260 styler.SetLineState(curLine, curNcLevel);
261 }
7e0c58e9 262
9e96e16f
RD
263 // Determine if the current state should terminate.
264 switch (sc.state) {
265 case SCE_D_OPERATOR:
266 sc.SetState(SCE_D_DEFAULT);
267 break;
268 case SCE_D_NUMBER:
269 // We accept almost anything because of hex. and number suffixes
270 if (isascii(sc.ch) && (isalnum(sc.ch) || sc.ch == '_')) {
271 continue;
272 } else if (sc.ch == '.' && sc.chNext != '.' && !numFloat) {
273 // Don't parse 0..2 as number.
274 numFloat=true;
275 continue;
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
280 continue;
281 } else {
282 sc.SetState(SCE_D_DEFAULT);
283 }
284 break;
285 case SCE_D_IDENTIFIER:
286 if (!IsWord(sc.ch)) {
287 char s[1000];
288 if (caseSensitive) {
289 sc.GetCurrent(s, sizeof(s));
290 } else {
291 sc.GetCurrentLowered(s, sizeof(s));
292 }
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);
305 }
306 sc.SetState(SCE_D_DEFAULT);
307 }
308 break;
309 case SCE_D_COMMENT:
310 if (sc.Match('*', '/')) {
311 sc.Forward();
312 sc.ForwardSetState(SCE_D_DEFAULT);
313 }
314 break;
315 case SCE_D_COMMENTDOC:
316 if (sc.Match('*', '/')) {
317 sc.Forward();
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);
324 }
325 }
326 break;
327 case SCE_D_COMMENTLINE:
328 if (sc.atLineStart) {
329 sc.SetState(SCE_D_DEFAULT);
330 }
331 break;
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);
340 }
341 }
342 break;
343 case SCE_D_COMMENTDOCKEYWORD:
344 if ((styleBeforeDCKeyword == SCE_D_COMMENTDOC) && sc.Match('*', '/')) {
345 sc.ChangeState(SCE_D_COMMENTDOCKEYWORDERROR);
346 sc.Forward();
347 sc.ForwardSetState(SCE_D_DEFAULT);
348 } else if (!IsDoxygen(sc.ch)) {
349 char s[100];
350 if (caseSensitive) {
351 sc.GetCurrent(s, sizeof(s));
352 } else {
353 sc.GetCurrentLowered(s, sizeof(s));
354 }
355 if (!IsASpace(sc.ch) || !keywords3.InList(s + 1)) {
356 sc.ChangeState(SCE_D_COMMENTDOCKEYWORDERROR);
357 }
358 sc.SetState(styleBeforeDCKeyword);
359 }
360 break;
361 case SCE_D_COMMENTNESTED:
362 if (sc.Match('+', '/')) {
363 if (curNcLevel > 0)
364 curNcLevel -= 1;
365 curLine = styler.GetLine(sc.currentPos);
366 styler.SetLineState(curLine, curNcLevel);
367 sc.Forward();
368 if (curNcLevel == 0) {
369 sc.ForwardSetState(SCE_D_DEFAULT);
370 }
371 } else if (sc.Match('/','+')) {
372 curNcLevel += 1;
373 curLine = styler.GetLine(sc.currentPos);
374 styler.SetLineState(curLine, curNcLevel);
375 sc.Forward();
376 }
377 break;
378 case SCE_D_STRING:
379 if (sc.ch == '\\') {
380 if (sc.chNext == '"' || sc.chNext == '\\') {
381 sc.Forward();
382 }
383 } else if (sc.ch == '"') {
384 if(IsStringSuffix(sc.chNext))
385 sc.Forward();
386 sc.ForwardSetState(SCE_D_DEFAULT);
387 }
388 break;
389 case SCE_D_CHARACTER:
390 if (sc.atLineEnd) {
391 sc.ChangeState(SCE_D_STRINGEOL);
392 } else if (sc.ch == '\\') {
393 if (sc.chNext == '\'' || sc.chNext == '\\') {
394 sc.Forward();
395 }
396 } else if (sc.ch == '\'') {
397 // Char has no suffixes
398 sc.ForwardSetState(SCE_D_DEFAULT);
399 }
400 break;
401 case SCE_D_STRINGEOL:
402 if (sc.atLineStart) {
403 sc.SetState(SCE_D_DEFAULT);
404 }
405 break;
406 case SCE_D_STRINGB:
407 if (sc.ch == '`') {
408 if(IsStringSuffix(sc.chNext))
409 sc.Forward();
410 sc.ForwardSetState(SCE_D_DEFAULT);
411 }
412 break;
413 case SCE_D_STRINGR:
414 if (sc.ch == '"') {
415 if(IsStringSuffix(sc.chNext))
416 sc.Forward();
417 sc.ForwardSetState(SCE_D_DEFAULT);
418 }
419 break;
420 }
7e0c58e9 421
9e96e16f
RD
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);
433 sc.Forward();
434 } else if (IsWordStart(sc.ch) || sc.ch == '$') {
435 sc.SetState(SCE_D_IDENTIFIER);
436 } else if (sc.Match('/','+')) {
437 curNcLevel += 1;
438 curLine = styler.GetLine(sc.currentPos);
439 styler.SetLineState(curLine, curNcLevel);
440 sc.SetState(SCE_D_COMMENTNESTED);
441 sc.Forward();
442 } else if (sc.Match('/', '*')) {
443 if (sc.Match("/**") || sc.Match("/*!")) { // Support of Qt/Doxygen doc. style
444 sc.SetState(SCE_D_COMMENTDOC);
445 } else {
446 sc.SetState(SCE_D_COMMENT);
447 }
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);
453 else
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
464 }
465 }
466 }
467 sc.Complete();
7e0c58e9
RD
468}
469
7e0c58e9
RD
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 {".
1dcf666d
RD
473
474void SCI_METHOD LexerD::Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess) {
475
476 if (!options.fold)
477 return;
478
479 LexAccessor styler(pAccess);
480
9e96e16f
RD
481 unsigned int endPos = startPos + length;
482 int visibleChars = 0;
483 int lineCurrent = styler.GetLine(startPos);
484 int levelCurrent = SC_FOLDLEVELBASE;
485 if (lineCurrent > 0)
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;
1dcf666d
RD
492 bool foldAtElse = options.foldAtElseInt >= 0 ? options.foldAtElseInt != 0 : options.foldAtElse;
493 const bool userDefinedFoldMarkers = !options.foldExplicitStart.empty() && !options.foldExplicitEnd.empty();
9e96e16f
RD
494 for (unsigned int i = startPos; i < endPos; i++) {
495 char ch = chNext;
496 chNext = styler.SafeGetCharAt(i + 1);
497 int stylePrev = style;
498 style = styleNext;
499 styleNext = styler.StyleAt(i + 1);
500 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
1dcf666d 501 if (options.foldComment && options.foldCommentMultiline && IsStreamCommentStyle(style)) {
9e96e16f
RD
502 if (!IsStreamCommentStyle(stylePrev)) {
503 levelNext++;
504 } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
505 // Comments don't end at end of line and the next character may be unstyled.
506 levelNext--;
507 }
508 }
1dcf666d
RD
509 if (options.foldComment && options.foldCommentExplicit && ((style == SCE_D_COMMENTLINE) || options.foldExplicitAnywhere)) {
510 if (userDefinedFoldMarkers) {
511 if (styler.Match(i, options.foldExplicitStart.c_str())) {
512 levelNext++;
513 } else if (styler.Match(i, options.foldExplicitEnd.c_str())) {
514 levelNext--;
515 }
516 } else {
517 if ((ch == '/') && (chNext == '/')) {
518 char chNext2 = styler.SafeGetCharAt(i + 2);
519 if (chNext2 == '{') {
520 levelNext++;
521 } else if (chNext2 == '}') {
522 levelNext--;
523 }
524 }
525 }
526 }
527 if (options.foldSyntaxBased && (style == SCE_D_OPERATOR)) {
9e96e16f
RD
528 if (ch == '{') {
529 // Measure the minimum before a '{' to allow
530 // folding on "} else {"
531 if (levelMinCurrent > levelNext) {
532 levelMinCurrent = levelNext;
533 }
534 levelNext++;
535 } else if (ch == '}') {
536 levelNext--;
537 }
538 }
1dcf666d
RD
539 if (atEOL || (i == endPos-1)) {
540 if (options.foldComment && options.foldCommentMultiline) { // Handle nested comments
9e96e16f
RD
541 int nc;
542 nc = styler.GetLineState(lineCurrent);
543 nc -= lineCurrent>0? styler.GetLineState(lineCurrent-1): 0;
544 levelNext += nc;
545 }
546 int levelUse = levelCurrent;
1dcf666d 547 if (options.foldSyntaxBased && foldAtElse) {
9e96e16f
RD
548 levelUse = levelMinCurrent;
549 }
550 int lev = levelUse | levelNext << 16;
1dcf666d 551 if (visibleChars == 0 && options.foldCompact)
9e96e16f
RD
552 lev |= SC_FOLDLEVELWHITEFLAG;
553 if (levelUse < levelNext)
554 lev |= SC_FOLDLEVELHEADERFLAG;
555 if (lev != styler.LevelAt(lineCurrent)) {
556 styler.SetLevel(lineCurrent, lev);
557 }
558 lineCurrent++;
559 levelCurrent = levelNext;
560 levelMinCurrent = levelCurrent;
561 visibleChars = 0;
562 }
563 if (!IsASpace(ch))
564 visibleChars++;
565 }
7e0c58e9
RD
566}
567
1dcf666d 568LexerModule lmD(SCLEX_D, LexerD::LexerFactoryD, "d", dWordLists);