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);