PageRenderTime 61ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/ExtLibs/wxWidgets/src/stc/scintilla/src/LexCPP.cxx

https://bitbucket.org/lennonchan/cafu
C++ | 506 lines | 421 code | 34 blank | 51 comment | 282 complexity | 1647ffcd2269c780e256f010efdb06e8 MD5 | raw file
  1. // Scintilla source code edit control
  2. /** @file LexCPP.cxx
  3. ** Lexer for C++, C, Java, and JavaScript.
  4. **/
  5. // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
  6. // The License.txt file describes the conditions under which this software may be distributed.
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <ctype.h>
  10. #include <stdio.h>
  11. #include <stdarg.h>
  12. #include "Platform.h"
  13. #include "PropSet.h"
  14. #include "Accessor.h"
  15. #include "StyleContext.h"
  16. #include "KeyWords.h"
  17. #include "Scintilla.h"
  18. #include "SciLexer.h"
  19. #include "CharacterSet.h"
  20. #ifdef SCI_NAMESPACE
  21. using namespace Scintilla;
  22. #endif
  23. static bool IsSpaceEquiv(int state) {
  24. return (state <= SCE_C_COMMENTDOC) ||
  25. // including SCE_C_DEFAULT, SCE_C_COMMENT, SCE_C_COMMENTLINE
  26. (state == SCE_C_COMMENTLINEDOC) || (state == SCE_C_COMMENTDOCKEYWORD) ||
  27. (state == SCE_C_COMMENTDOCKEYWORDERROR);
  28. }
  29. // Preconditions: sc.currentPos points to a character after '+' or '-'.
  30. // The test for pos reaching 0 should be redundant,
  31. // and is in only for safety measures.
  32. // Limitation: this code will give the incorrect answer for code like
  33. // a = b+++/ptn/...
  34. // Putting a space between the '++' post-inc operator and the '+' binary op
  35. // fixes this, and is highly recommended for readability anyway.
  36. static bool FollowsPostfixOperator(StyleContext &sc, Accessor &styler) {
  37. int pos = (int) sc.currentPos;
  38. while (--pos > 0) {
  39. char ch = styler[pos];
  40. if (ch == '+' || ch == '-') {
  41. return styler[pos - 1] == ch;
  42. }
  43. }
  44. return false;
  45. }
  46. static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
  47. Accessor &styler, bool caseSensitive) {
  48. WordList &keywords = *keywordlists[0];
  49. WordList &keywords2 = *keywordlists[1];
  50. WordList &keywords3 = *keywordlists[2];
  51. WordList &keywords4 = *keywordlists[3];
  52. // property styling.within.preprocessor
  53. // For C++ code, determines whether all preprocessor code is styled in the preprocessor style (0, the default)
  54. // or only from the initial # to the end of the command word(1).
  55. bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0;
  56. CharacterSet setOKBeforeRE(CharacterSet::setNone, "([{=,:;!%^&*|?~+-");
  57. CharacterSet setCouldBePostOp(CharacterSet::setNone, "+-");
  58. CharacterSet setDoxygen(CharacterSet::setAlpha, "$@\\&<>#{}[]");
  59. CharacterSet setWordStart(CharacterSet::setAlpha, "_", 0x80, true);
  60. CharacterSet setWord(CharacterSet::setAlphaNum, "._", 0x80, true);
  61. // property lexer.cpp.allow.dollars
  62. // Set to 0 to disallow the '$' character in identifiers with the cpp lexer.
  63. if (styler.GetPropertyInt("lexer.cpp.allow.dollars", 1) != 0) {
  64. setWordStart.Add('$');
  65. setWord.Add('$');
  66. }
  67. int chPrevNonWhite = ' ';
  68. int visibleChars = 0;
  69. bool lastWordWasUUID = false;
  70. int styleBeforeDCKeyword = SCE_C_DEFAULT;
  71. bool continuationLine = false;
  72. bool isIncludePreprocessor = false;
  73. if (initStyle == SCE_C_PREPROCESSOR) {
  74. // Set continuationLine if last character of previous line is '\'
  75. int lineCurrent = styler.GetLine(startPos);
  76. if (lineCurrent > 0) {
  77. int chBack = styler.SafeGetCharAt(startPos-1, 0);
  78. int chBack2 = styler.SafeGetCharAt(startPos-2, 0);
  79. int lineEndChar = '!';
  80. if (chBack2 == '\r' && chBack == '\n') {
  81. lineEndChar = styler.SafeGetCharAt(startPos-3, 0);
  82. } else if (chBack == '\n' || chBack == '\r') {
  83. lineEndChar = chBack2;
  84. }
  85. continuationLine = lineEndChar == '\\';
  86. }
  87. }
  88. // look back to set chPrevNonWhite properly for better regex colouring
  89. if (startPos > 0) {
  90. int back = startPos;
  91. while (--back && IsSpaceEquiv(styler.StyleAt(back)))
  92. ;
  93. if (styler.StyleAt(back) == SCE_C_OPERATOR) {
  94. chPrevNonWhite = styler.SafeGetCharAt(back);
  95. }
  96. }
  97. StyleContext sc(startPos, length, initStyle, styler);
  98. for (; sc.More(); sc.Forward()) {
  99. if (sc.atLineStart) {
  100. if (sc.state == SCE_C_STRING) {
  101. // Prevent SCE_C_STRINGEOL from leaking back to previous line which
  102. // ends with a line continuation by locking in the state upto this position.
  103. sc.SetState(SCE_C_STRING);
  104. }
  105. // Reset states to begining of colourise so no surprises
  106. // if different sets of lines lexed.
  107. visibleChars = 0;
  108. lastWordWasUUID = false;
  109. isIncludePreprocessor = false;
  110. }
  111. // Handle line continuation generically.
  112. if (sc.ch == '\\') {
  113. if (sc.chNext == '\n' || sc.chNext == '\r') {
  114. sc.Forward();
  115. if (sc.ch == '\r' && sc.chNext == '\n') {
  116. sc.Forward();
  117. }
  118. continuationLine = true;
  119. continue;
  120. }
  121. }
  122. // Determine if the current state should terminate.
  123. switch (sc.state) {
  124. case SCE_C_OPERATOR:
  125. sc.SetState(SCE_C_DEFAULT);
  126. break;
  127. case SCE_C_NUMBER:
  128. // We accept almost anything because of hex. and number suffixes
  129. if (!setWord.Contains(sc.ch)) {
  130. sc.SetState(SCE_C_DEFAULT);
  131. }
  132. break;
  133. case SCE_C_IDENTIFIER:
  134. if (!setWord.Contains(sc.ch) || (sc.ch == '.')) {
  135. char s[1000];
  136. if (caseSensitive) {
  137. sc.GetCurrent(s, sizeof(s));
  138. } else {
  139. sc.GetCurrentLowered(s, sizeof(s));
  140. }
  141. if (keywords.InList(s)) {
  142. lastWordWasUUID = strcmp(s, "uuid") == 0;
  143. sc.ChangeState(SCE_C_WORD);
  144. } else if (keywords2.InList(s)) {
  145. sc.ChangeState(SCE_C_WORD2);
  146. } else if (keywords4.InList(s)) {
  147. sc.ChangeState(SCE_C_GLOBALCLASS);
  148. }
  149. sc.SetState(SCE_C_DEFAULT);
  150. }
  151. break;
  152. case SCE_C_PREPROCESSOR:
  153. if (sc.atLineStart && !continuationLine) {
  154. sc.SetState(SCE_C_DEFAULT);
  155. } else if (stylingWithinPreprocessor) {
  156. if (IsASpace(sc.ch)) {
  157. sc.SetState(SCE_C_DEFAULT);
  158. }
  159. } else {
  160. if (sc.Match('/', '*') || sc.Match('/', '/')) {
  161. sc.SetState(SCE_C_DEFAULT);
  162. }
  163. }
  164. break;
  165. case SCE_C_COMMENT:
  166. if (sc.Match('*', '/')) {
  167. sc.Forward();
  168. sc.ForwardSetState(SCE_C_DEFAULT);
  169. }
  170. break;
  171. case SCE_C_COMMENTDOC:
  172. if (sc.Match('*', '/')) {
  173. sc.Forward();
  174. sc.ForwardSetState(SCE_C_DEFAULT);
  175. } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
  176. // Verify that we have the conditions to mark a comment-doc-keyword
  177. if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {
  178. styleBeforeDCKeyword = SCE_C_COMMENTDOC;
  179. sc.SetState(SCE_C_COMMENTDOCKEYWORD);
  180. }
  181. }
  182. break;
  183. case SCE_C_COMMENTLINE:
  184. if (sc.atLineStart) {
  185. sc.SetState(SCE_C_DEFAULT);
  186. }
  187. break;
  188. case SCE_C_COMMENTLINEDOC:
  189. if (sc.atLineStart) {
  190. sc.SetState(SCE_C_DEFAULT);
  191. } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
  192. // Verify that we have the conditions to mark a comment-doc-keyword
  193. if ((IsASpace(sc.chPrev) || sc.chPrev == '/' || sc.chPrev == '!') && (!IsASpace(sc.chNext))) {
  194. styleBeforeDCKeyword = SCE_C_COMMENTLINEDOC;
  195. sc.SetState(SCE_C_COMMENTDOCKEYWORD);
  196. }
  197. }
  198. break;
  199. case SCE_C_COMMENTDOCKEYWORD:
  200. if ((styleBeforeDCKeyword == SCE_C_COMMENTDOC) && sc.Match('*', '/')) {
  201. sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
  202. sc.Forward();
  203. sc.ForwardSetState(SCE_C_DEFAULT);
  204. } else if (!setDoxygen.Contains(sc.ch)) {
  205. char s[100];
  206. if (caseSensitive) {
  207. sc.GetCurrent(s, sizeof(s));
  208. } else {
  209. sc.GetCurrentLowered(s, sizeof(s));
  210. }
  211. if (!IsASpace(sc.ch) || !keywords3.InList(s + 1)) {
  212. sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
  213. }
  214. sc.SetState(styleBeforeDCKeyword);
  215. }
  216. break;
  217. case SCE_C_STRING:
  218. if (sc.atLineEnd) {
  219. sc.ChangeState(SCE_C_STRINGEOL);
  220. } else if (isIncludePreprocessor) {
  221. if (sc.ch == '>') {
  222. sc.ForwardSetState(SCE_C_DEFAULT);
  223. isIncludePreprocessor = false;
  224. }
  225. } else if (sc.ch == '\\') {
  226. if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
  227. sc.Forward();
  228. }
  229. } else if (sc.ch == '\"') {
  230. sc.ForwardSetState(SCE_C_DEFAULT);
  231. }
  232. break;
  233. case SCE_C_CHARACTER:
  234. if (sc.atLineEnd) {
  235. sc.ChangeState(SCE_C_STRINGEOL);
  236. } else if (sc.ch == '\\') {
  237. if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
  238. sc.Forward();
  239. }
  240. } else if (sc.ch == '\'') {
  241. sc.ForwardSetState(SCE_C_DEFAULT);
  242. }
  243. break;
  244. case SCE_C_REGEX:
  245. if (sc.atLineStart) {
  246. sc.SetState(SCE_C_DEFAULT);
  247. } else if (sc.ch == '/') {
  248. sc.Forward();
  249. while ((sc.ch < 0x80) && islower(sc.ch))
  250. sc.Forward(); // gobble regex flags
  251. sc.SetState(SCE_C_DEFAULT);
  252. } else if (sc.ch == '\\') {
  253. // Gobble up the quoted character
  254. if (sc.chNext == '\\' || sc.chNext == '/') {
  255. sc.Forward();
  256. }
  257. }
  258. break;
  259. case SCE_C_STRINGEOL:
  260. if (sc.atLineStart) {
  261. sc.SetState(SCE_C_DEFAULT);
  262. }
  263. break;
  264. case SCE_C_VERBATIM:
  265. if (sc.ch == '\"') {
  266. if (sc.chNext == '\"') {
  267. sc.Forward();
  268. } else {
  269. sc.ForwardSetState(SCE_C_DEFAULT);
  270. }
  271. }
  272. break;
  273. case SCE_C_UUID:
  274. if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') {
  275. sc.SetState(SCE_C_DEFAULT);
  276. }
  277. }
  278. // Determine if a new state should be entered.
  279. if (sc.state == SCE_C_DEFAULT) {
  280. if (sc.Match('@', '\"')) {
  281. sc.SetState(SCE_C_VERBATIM);
  282. sc.Forward();
  283. } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
  284. if (lastWordWasUUID) {
  285. sc.SetState(SCE_C_UUID);
  286. lastWordWasUUID = false;
  287. } else {
  288. sc.SetState(SCE_C_NUMBER);
  289. }
  290. } else if (setWordStart.Contains(sc.ch) || (sc.ch == '@')) {
  291. if (lastWordWasUUID) {
  292. sc.SetState(SCE_C_UUID);
  293. lastWordWasUUID = false;
  294. } else {
  295. sc.SetState(SCE_C_IDENTIFIER);
  296. }
  297. } else if (sc.Match('/', '*')) {
  298. if (sc.Match("/**") || sc.Match("/*!")) { // Support of Qt/Doxygen doc. style
  299. sc.SetState(SCE_C_COMMENTDOC);
  300. } else {
  301. sc.SetState(SCE_C_COMMENT);
  302. }
  303. sc.Forward(); // Eat the * so it isn't used for the end of the comment
  304. } else if (sc.Match('/', '/')) {
  305. if ((sc.Match("///") && !sc.Match("////")) || sc.Match("//!"))
  306. // Support of Qt/Doxygen doc. style
  307. sc.SetState(SCE_C_COMMENTLINEDOC);
  308. else
  309. sc.SetState(SCE_C_COMMENTLINE);
  310. } else if (sc.ch == '/' && setOKBeforeRE.Contains(chPrevNonWhite) &&
  311. (!setCouldBePostOp.Contains(chPrevNonWhite) || !FollowsPostfixOperator(sc, styler))) {
  312. sc.SetState(SCE_C_REGEX); // JavaScript's RegEx
  313. } else if (sc.ch == '\"') {
  314. sc.SetState(SCE_C_STRING);
  315. isIncludePreprocessor = false; // ensure that '>' won't end the string
  316. } else if (isIncludePreprocessor && sc.ch == '<') {
  317. sc.SetState(SCE_C_STRING);
  318. } else if (sc.ch == '\'') {
  319. sc.SetState(SCE_C_CHARACTER);
  320. } else if (sc.ch == '#' && visibleChars == 0) {
  321. // Preprocessor commands are alone on their line
  322. sc.SetState(SCE_C_PREPROCESSOR);
  323. // Skip whitespace between # and preprocessor word
  324. do {
  325. sc.Forward();
  326. } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
  327. if (sc.atLineEnd) {
  328. sc.SetState(SCE_C_DEFAULT);
  329. } else if (sc.Match("include")) {
  330. isIncludePreprocessor = true;
  331. }
  332. } else if (isoperator(static_cast<char>(sc.ch))) {
  333. sc.SetState(SCE_C_OPERATOR);
  334. }
  335. }
  336. if (!IsASpace(sc.ch) && !IsSpaceEquiv(sc.state)) {
  337. chPrevNonWhite = sc.ch;
  338. visibleChars++;
  339. }
  340. continuationLine = false;
  341. }
  342. sc.Complete();
  343. }
  344. static bool IsStreamCommentStyle(int style) {
  345. return style == SCE_C_COMMENT ||
  346. style == SCE_C_COMMENTDOC ||
  347. style == SCE_C_COMMENTDOCKEYWORD ||
  348. style == SCE_C_COMMENTDOCKEYWORDERROR;
  349. }
  350. // Store both the current line's fold level and the next lines in the
  351. // level store to make it easy to pick up with each increment
  352. // and to make it possible to fiddle the current level for "} else {".
  353. static void FoldCppDoc(unsigned int startPos, int length, int initStyle,
  354. WordList *[], Accessor &styler) {
  355. // property fold.comment
  356. // This option enables folding multi-line comments and explicit fold points when using the C++ lexer.
  357. // Explicit fold points allows adding extra folding by placing a //{ comment at the start and a //}
  358. // at the end of a section that should fold.
  359. bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
  360. // property fold.preprocessor
  361. // This option enables folding preprocessor directives when using the C++ lexer.
  362. // Includes C#'s explicit #region and #endregion folding directives.
  363. bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
  364. bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
  365. // property fold.at.else
  366. // This option enables C++ folding on a "} else {" line of an if statement.
  367. bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
  368. unsigned int endPos = startPos + length;
  369. int visibleChars = 0;
  370. int lineCurrent = styler.GetLine(startPos);
  371. int levelCurrent = SC_FOLDLEVELBASE;
  372. if (lineCurrent > 0)
  373. levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
  374. int levelMinCurrent = levelCurrent;
  375. int levelNext = levelCurrent;
  376. char chNext = styler[startPos];
  377. int styleNext = styler.StyleAt(startPos);
  378. int style = initStyle;
  379. for (unsigned int i = startPos; i < endPos; i++) {
  380. char ch = chNext;
  381. chNext = styler.SafeGetCharAt(i + 1);
  382. int stylePrev = style;
  383. style = styleNext;
  384. styleNext = styler.StyleAt(i + 1);
  385. bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
  386. if (foldComment && IsStreamCommentStyle(style)) {
  387. if (!IsStreamCommentStyle(stylePrev) && (stylePrev != SCE_C_COMMENTLINEDOC)) {
  388. levelNext++;
  389. } else if (!IsStreamCommentStyle(styleNext) && (styleNext != SCE_C_COMMENTLINEDOC) && !atEOL) {
  390. // Comments don't end at end of line and the next character may be unstyled.
  391. levelNext--;
  392. }
  393. }
  394. if (foldComment && (style == SCE_C_COMMENTLINE)) {
  395. if ((ch == '/') && (chNext == '/')) {
  396. char chNext2 = styler.SafeGetCharAt(i + 2);
  397. if (chNext2 == '{') {
  398. levelNext++;
  399. } else if (chNext2 == '}') {
  400. levelNext--;
  401. }
  402. }
  403. }
  404. if (foldPreprocessor && (style == SCE_C_PREPROCESSOR)) {
  405. if (ch == '#') {
  406. unsigned int j = i + 1;
  407. while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
  408. j++;
  409. }
  410. if (styler.Match(j, "region") || styler.Match(j, "if")) {
  411. levelNext++;
  412. } else if (styler.Match(j, "end")) {
  413. levelNext--;
  414. }
  415. }
  416. }
  417. if (style == SCE_C_OPERATOR) {
  418. if (ch == '{') {
  419. // Measure the minimum before a '{' to allow
  420. // folding on "} else {"
  421. if (levelMinCurrent > levelNext) {
  422. levelMinCurrent = levelNext;
  423. }
  424. levelNext++;
  425. } else if (ch == '}') {
  426. levelNext--;
  427. }
  428. }
  429. if (!IsASpace(ch))
  430. visibleChars++;
  431. if (atEOL || (i == endPos-1)) {
  432. int levelUse = levelCurrent;
  433. if (foldAtElse) {
  434. levelUse = levelMinCurrent;
  435. }
  436. int lev = levelUse | levelNext << 16;
  437. if (visibleChars == 0 && foldCompact)
  438. lev |= SC_FOLDLEVELWHITEFLAG;
  439. if (levelUse < levelNext)
  440. lev |= SC_FOLDLEVELHEADERFLAG;
  441. if (lev != styler.LevelAt(lineCurrent)) {
  442. styler.SetLevel(lineCurrent, lev);
  443. }
  444. lineCurrent++;
  445. levelCurrent = levelNext;
  446. levelMinCurrent = levelCurrent;
  447. if (atEOL && (i == static_cast<unsigned int>(styler.Length()-1))) {
  448. // There is an empty line at end of file so give it same level and empty
  449. styler.SetLevel(lineCurrent, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG);
  450. }
  451. visibleChars = 0;
  452. }
  453. }
  454. }
  455. static const char * const cppWordLists[] = {
  456. "Primary keywords and identifiers",
  457. "Secondary keywords and identifiers",
  458. "Documentation comment keywords",
  459. "Unused",
  460. "Global classes and typedefs",
  461. 0,
  462. };
  463. static void ColouriseCppDocSensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
  464. Accessor &styler) {
  465. ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, true);
  466. }
  467. static void ColouriseCppDocInsensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
  468. Accessor &styler) {
  469. ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, false);
  470. }
  471. LexerModule lmCPP(SCLEX_CPP, ColouriseCppDocSensitive, "cpp", FoldCppDoc, cppWordLists);
  472. LexerModule lmCPPNoCase(SCLEX_CPPNOCASE, ColouriseCppDocInsensitive, "cppnocase", FoldCppDoc, cppWordLists);