1 // Scintilla source code edit control 
   3  ** Lexer for C++, C, Java, and Javascript. 
   5 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org> 
   6 // The License.txt file describes the conditions under which this software may be distributed. 
  19 #include "Scintilla.h" 
  22 static bool IsOKBeforeRE(int ch
) { 
  23         return (ch 
== '(') || (ch 
== '=') || (ch 
== ','); 
  26 static void getRange(unsigned int start
, 
  32         while ((i 
< end 
- start 
+ 1) && (i 
< len
-1)) { 
  33                 s
[i
] = styler
[start 
+ i
]; 
  39 inline bool IsASpace(int ch
) { 
  40     return (ch 
== ' ') || ((ch 
>= 0x09) && (ch 
<= 0x0d)); 
  43 inline bool IsAWordChar(int  ch
) { 
  44         return (ch 
< 0x80) && (isalnum(ch
) || ch 
== '.' || ch 
== '_'); 
  47 inline bool IsAWordStart(int ch
) { 
  48         return (ch 
< 0x80) && (isalnum(ch
) || ch 
== '_'); 
  51 inline bool IsADigit(int ch
) { 
  52         return (ch 
>= '0') && (ch 
<= '9'); 
  55 // All languages handled so far can treat all characters >= 0x80 as one class 
  56 // which just continues the current token or starts an identifier if in default. 
  57 // DBCS treated specially as the second character can be < 0x80 and hence  
  58 // syntactically significant. UTF-8 avoids this as all trail bytes are >= 0x80 
  59 class ColouriseContext 
{ 
  63         ColouriseContext
& operator=(const ColouriseContext
&) { 
  73         ColouriseContext(unsigned int startPos
, int length
, 
  74                         int initStyle
, Accessor 
&styler_
) :  
  76                 lengthDoc(startPos 
+ length
), 
  83                 styler
.StartAt(startPos
); 
  84                 styler
.StartSegment(startPos
); 
  86                 ch 
= static_cast<unsigned char>(styler
.SafeGetCharAt(pos
)); 
  87                 if (styler
.IsLeadByte(static_cast<char>(ch
))) { 
  90                         ch 
|= static_cast<unsigned char>(styler
.SafeGetCharAt(pos
)); 
  92                 chNext 
= static_cast<unsigned char>(styler
.SafeGetCharAt(pos
+1)); 
  93                 if (styler
.IsLeadByte(static_cast<char>(chNext
))) { 
  95                         chNext 
|= static_cast<unsigned char>(styler
.SafeGetCharAt(pos
+2)); 
  97                 atEOL 
= (ch 
== '\r' && chNext 
!= '\n') || (ch 
== '\n'); 
 100                 styler
.ColourTo(currentPos 
- 1, state
); 
 103                 return currentPos 
<= lengthDoc
; 
 106                 // A lot of this is repeated from the constructor - TODO: merge code 
 112                 chNext 
= static_cast<unsigned char>(styler
.SafeGetCharAt(currentPos
+1)); 
 113                 if (styler
.IsLeadByte(static_cast<char>(chNext
))) { 
 114                         chNext 
= chNext 
<< 8; 
 115                         chNext 
|= static_cast<unsigned char>(styler
.SafeGetCharAt(currentPos 
+ 2)); 
 117                 // Trigger on CR only (Mac style) or either on LF from CR+LF (Dos/Win) or on LF alone (Unix) 
 118                 // Avoid triggering two times on Dos/Win 
 120                 atEOL 
= (ch 
== '\r' && chNext 
!= '\n') || (ch 
== '\n'); 
 122         void ChangeState(int state_
) { 
 125         void SetState(int state_
) { 
 126                 styler
.ColourTo(currentPos 
- 1, state
); 
 129         void ForwardSetState(int state_
) { 
 131                 styler
.ColourTo(currentPos 
- 1, state
); 
 134         void GetCurrent(char *s
, int len
) { 
 135                 getRange(styler
.GetStartSegment(), currentPos 
- 1, styler
, s
, len
); 
 137         int LengthCurrent() { 
 138                 return currentPos 
- styler
.GetStartSegment(); 
 140         bool Match(char ch0
) { 
 143         bool Match(char ch0
, char ch1
) { 
 144                 return (ch 
== ch0
) && (chNext 
== ch1
); 
 146         bool Match(const char *s
) { 
 153                 for (int n
=2; *s
; n
++) { 
 154                         if (*s 
!= styler
.SafeGetCharAt(currentPos
+n
)) 
 162 static void ColouriseCppDoc(unsigned int startPos
, int length
, int initStyle
, WordList 
*keywordlists
[], 
 165         WordList 
&keywords 
= *keywordlists
[0]; 
 166         WordList 
&keywords2 
= *keywordlists
[1]; 
 168         bool stylingWithinPreprocessor 
= styler
.GetPropertyInt("styling.within.preprocessor"); 
 170         if (initStyle 
== SCE_C_STRINGEOL
)       // Does not leak onto next line 
 171                 initStyle 
= SCE_C_DEFAULT
; 
 173         int chPrevNonWhite 
= ' '; 
 174         int visibleChars 
= 0; 
 175         bool lastWordWasUUID 
= false; 
 177         ColouriseContext 
cc(startPos
, length
, initStyle
, styler
); 
 179         for (; cc
.More(); cc
.Forward()) { 
 181                 if (cc
.state 
== SCE_C_STRINGEOL
) { 
 183                                 cc
.SetState(SCE_C_DEFAULT
); 
 185                 } else if (cc
.state 
== SCE_C_OPERATOR
) { 
 186                         cc
.SetState(SCE_C_DEFAULT
); 
 187                 } else if (cc
.state 
== SCE_C_NUMBER
) { 
 188                         if (!IsAWordChar(cc
.ch
)) { 
 189                                 cc
.SetState(SCE_C_DEFAULT
); 
 191                 } else if (cc
.state 
== SCE_C_IDENTIFIER
) { 
 192                         if (!IsAWordChar(cc
.ch
) || (cc
.ch 
== '.')) { 
 194                                 cc
.GetCurrent(s
, sizeof(s
)); 
 195                                 if (keywords
.InList(s
)) { 
 196                                         lastWordWasUUID 
= strcmp(s
, "uuid") == 0; 
 197                                         cc
.ChangeState(SCE_C_WORD
); 
 198                                 } else if (keywords2
.InList(s
)) { 
 199                                         cc
.ChangeState(SCE_C_WORD2
); 
 201                                 cc
.SetState(SCE_C_DEFAULT
); 
 203                 } if (cc
.state 
== SCE_C_PREPROCESSOR
) { 
 204                         if (stylingWithinPreprocessor
) { 
 205                                 if (IsASpace(cc
.ch
)) { 
 206                                         cc
.SetState(SCE_C_DEFAULT
); 
 209                                 if (cc
.atEOL 
&& (cc
.chPrev 
!= '\\')) { 
 210                                         cc
.SetState(SCE_C_DEFAULT
); 
 213                 } else if (cc
.state 
== SCE_C_COMMENT
) { 
 214                         if (cc
.Match('*', '/')) { 
 216                                 cc
.ForwardSetState(SCE_C_DEFAULT
); 
 218                 } else if (cc
.state 
== SCE_C_COMMENTDOC
) { 
 219                         if (cc
.Match('*', '/')) { 
 221                                 cc
.ForwardSetState(SCE_C_DEFAULT
); 
 223                 } else if (cc
.state 
== SCE_C_COMMENTLINE 
|| cc
.state 
== SCE_C_COMMENTLINEDOC
) { 
 224                         if (cc
.ch 
== '\r' || cc
.ch 
== '\n') { 
 225                                 cc
.SetState(SCE_C_DEFAULT
); 
 227                 } else if (cc
.state 
== SCE_C_STRING
) { 
 229                                 if (cc
.chNext 
== '\"' || cc
.chNext 
== '\'' || cc
.chNext 
== '\\') { 
 232                         } else if (cc
.ch 
== '\"') { 
 233                                 cc
.ForwardSetState(SCE_C_DEFAULT
); 
 234                         } else if ((cc
.atEOL
) && (cc
.chPrev 
!= '\\')) { 
 235                                 cc
.ChangeState(SCE_C_STRINGEOL
); 
 237                 } else if (cc
.state 
== SCE_C_CHARACTER
) { 
 238                         if ((cc
.ch 
== '\r' || cc
.ch 
== '\n') && (cc
.chPrev 
!= '\\')) { 
 239                                 cc
.ChangeState(SCE_C_STRINGEOL
); 
 240                         } else if (cc
.ch 
== '\\') { 
 241                                 if (cc
.chNext 
== '\"' || cc
.chNext 
== '\'' || cc
.chNext 
== '\\') { 
 244                         } else if (cc
.ch 
== '\'') { 
 245                                 cc
.ForwardSetState(SCE_C_DEFAULT
); 
 247                 } else if (cc
.state 
== SCE_C_REGEX
) { 
 248                         if (cc
.ch 
== '\r' || cc
.ch 
== '\n' || cc
.ch 
== '/') { 
 249                                 cc
.ForwardSetState(SCE_C_DEFAULT
); 
 250                         } else if (cc
.ch 
== '\\') { 
 251                                 // Gobble up the quoted character 
 252                                 if (cc
.chNext 
== '\\' || cc
.chNext 
== '/') { 
 256                 } else if (cc
.state 
== SCE_C_VERBATIM
) { 
 258                                 if (cc
.chNext 
== '\"') { 
 261                                         cc
.ForwardSetState(SCE_C_DEFAULT
); 
 264                 } else if (cc
.state 
== SCE_C_UUID
) { 
 265                         if (cc
.ch 
== '\r' || cc
.ch 
== '\n' || cc
.ch 
== ')') { 
 266                                 cc
.SetState(SCE_C_DEFAULT
); 
 270                 if (cc
.state 
== SCE_C_DEFAULT
) { 
 271                         if (cc
.Match('@', '\"')) { 
 272                                 cc
.SetState(SCE_C_VERBATIM
); 
 274                         } else if (IsADigit(cc
.ch
) || (cc
.ch 
== '.' && IsADigit(cc
.chNext
))) { 
 275                                 if (lastWordWasUUID
) { 
 276                                         cc
.SetState(SCE_C_UUID
); 
 277                                         lastWordWasUUID 
= false; 
 279                                         cc
.SetState(SCE_C_NUMBER
); 
 281                         } else if (IsAWordStart(cc
.ch
) || (cc
.ch 
== '@')) { 
 282                                 if (lastWordWasUUID
) { 
 283                                         cc
.SetState(SCE_C_UUID
); 
 284                                         lastWordWasUUID 
= false; 
 286                                         cc
.SetState(SCE_C_IDENTIFIER
); 
 288                         } else if (cc
.Match('/', '*')) { 
 289                                 if (cc
.Match("/**") || cc
.Match("/*!")) // Support of Qt/Doxygen doc. style 
 290                                         cc
.SetState(SCE_C_COMMENTDOC
); 
 292                                         cc
.SetState(SCE_C_COMMENT
); 
 293                                 cc
.Forward();   // Eat the * so it isn't used for the end of the comment 
 294                         } else if (cc
.Match('/', '/')) { 
 295                                 if (cc
.Match("///") || cc
.Match("//!")) // Support of Qt/Doxygen doc. style 
 296                                         cc
.SetState(SCE_C_COMMENTLINEDOC
); 
 298                                         cc
.SetState(SCE_C_COMMENTLINE
); 
 299                         } else if (cc
.ch 
== '/' && IsOKBeforeRE(chPrevNonWhite
)) { 
 300                                 cc
.SetState(SCE_C_REGEX
); 
 301                         } else if (cc
.ch 
== '\"') { 
 302                                 cc
.SetState(SCE_C_STRING
); 
 303                         } else if (cc
.ch 
== '\'') { 
 304                                 cc
.SetState(SCE_C_CHARACTER
); 
 305                         } else if (cc
.ch 
== '#' && visibleChars 
== 0) { 
 306                                 // Preprocessor commands are alone on their line 
 307                                 cc
.SetState(SCE_C_PREPROCESSOR
); 
 308                                 // Skip whitespace between # and preprocessor word 
 311                                 } while (IsASpace(cc
.ch
) && cc
.More()); 
 312                         } else if (isoperator(static_cast<char>(cc
.ch
))) { 
 313                                 cc
.SetState(SCE_C_OPERATOR
); 
 317                         // Reset states to begining of colourise so no surprises  
 318                         // if different sets of lines lexed. 
 319                         chPrevNonWhite 
= ' '; 
 321                         lastWordWasUUID 
= false; 
 323                 if (!IsASpace(cc
.ch
)) { 
 324                         chPrevNonWhite 
= cc
.ch
; 
 331 static void FoldCppDoc(unsigned int startPos
, int length
, int initStyle
, WordList 
*[], 
 333         bool foldComment 
= styler
.GetPropertyInt("fold.comment"); 
 334         bool foldCompact 
= styler
.GetPropertyInt("fold.compact", 1); 
 335         unsigned int endPos 
= startPos 
+ length
; 
 336         int visibleChars 
= 0; 
 337         int lineCurrent 
= styler
.GetLine(startPos
); 
 338         int levelPrev 
= styler
.LevelAt(lineCurrent
) & SC_FOLDLEVELNUMBERMASK
; 
 339         int levelCurrent 
= levelPrev
; 
 340         char chNext 
= styler
[startPos
]; 
 341         int styleNext 
= styler
.StyleAt(startPos
); 
 342         int style 
= initStyle
; 
 343         for (unsigned int i 
= startPos
; i 
< endPos
; i
++) { 
 345                 chNext 
= styler
.SafeGetCharAt(i 
+ 1); 
 346                 int stylePrev 
= style
; 
 348                 styleNext 
= styler
.StyleAt(i 
+ 1); 
 349                 bool atEOL 
= (ch 
== '\r' && chNext 
!= '\n') || (ch 
== '\n'); 
 351                         (style 
== SCE_C_COMMENT 
|| style 
== SCE_C_COMMENTDOC
)) { 
 352                         if (style 
!= stylePrev
) { 
 354                         } else if ((style 
!= styleNext
) && !atEOL
) { 
 355                                 // Comments don't end at end of line and the next character may be unstyled. 
 359                 if (style 
== SCE_C_OPERATOR
) { 
 362                         } else if (ch 
== '}') { 
 368                         if (visibleChars 
== 0 && foldCompact
) 
 369                                 lev 
|= SC_FOLDLEVELWHITEFLAG
; 
 370                         if ((levelCurrent 
> levelPrev
) && (visibleChars 
> 0)) 
 371                                 lev 
|= SC_FOLDLEVELHEADERFLAG
; 
 372                         if (lev 
!= styler
.LevelAt(lineCurrent
)) { 
 373                                 styler
.SetLevel(lineCurrent
, lev
); 
 376                         levelPrev 
= levelCurrent
; 
 379                 if (!isspacechar(ch
)) 
 382         // Fill in the real level of the next line, keeping the current flags as they will be filled in later 
 383         int flagsNext 
= styler
.LevelAt(lineCurrent
) & ~SC_FOLDLEVELNUMBERMASK
; 
 384         styler
.SetLevel(lineCurrent
, levelPrev 
| flagsNext
); 
 387 LexerModule 
lmCPP(SCLEX_CPP
, ColouriseCppDoc
, "cpp", FoldCppDoc
); 
 388 LexerModule 
lmTCL(SCLEX_TCL
, ColouriseCppDoc
, "tcl", FoldCppDoc
);