void SCI_METHOD LexerABL::Lex()

in ext/scintilla/lexers/LexProgress.cxx [217:492]


void SCI_METHOD LexerABL::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) {
   LexAccessor styler(pAccess);

   setWordStart = CharacterSet(CharacterSet::setAlpha, "_", 0x80, true);

   int visibleChars = 0;
   int visibleChars1 = 0;
   int styleBeforeTaskMarker = SCE_ABL_DEFAULT;
   bool continuationLine = false;
   int commentNestingLevel = 0;
   bool isSentenceStart = true;
   bool possibleOOLChange = false;

   Sci_Position lineCurrent = styler.GetLine(startPos);
   if (initStyle == SCE_ABL_PREPROCESSOR) {
      // Set continuationLine if last character of previous line is '~'
      if (lineCurrent > 0) {
         Sci_Position endLinePrevious = styler.LineEnd(lineCurrent-1);
         if (endLinePrevious > 0) {
            continuationLine = styler.SafeGetCharAt(endLinePrevious-1) == '~';
         }
      }
   }

    // Look back to set variables that are actually invisible secondary states. The reason to avoid formal states is to cut down on state's bits
   if (startPos > 0) {
      Sci_Position back = startPos;
      bool checkCommentNestingLevel = (initStyle == SCE_ABL_COMMENT);
      bool checkIsSentenceStart = (initStyle == SCE_ABL_DEFAULT || initStyle == SCE_ABL_IDENTIFIER);
      char ch;
      char st;
      char chPrev;
      char chPrev_1;
      char chPrev_2;
      char chPrev_3;

      while (back >= 0 && (checkCommentNestingLevel || checkIsSentenceStart)) {
         ch = styler.SafeGetCharAt(back);
         styler.Flush();  // looking at styles so need to flush
         st = styler.StyleAt(back);

         chPrev = styler.SafeGetCharAt(back-1);
         // isSentenceStart is a non-visible state, used to identify where statements and preprocessor declerations can start
         if (checkIsSentenceStart && st != SCE_ABL_COMMENT && st != SCE_ABL_LINECOMMENT && st != SCE_ABL_CHARACTER  && st != SCE_ABL_STRING ) {
            chPrev_1 = styler.SafeGetCharAt(back-2);
            chPrev_2 = styler.SafeGetCharAt(back-3);
            chPrev_3 = styler.SafeGetCharAt(back-4);
            if ((chPrev == '.' || chPrev == ':' || chPrev == '}' ||
               (chPrev_3 == 'e' && chPrev_2 == 'l' && chPrev_1 == 's' &&  chPrev == 'e') ||
               (chPrev_3 == 't' && chPrev_2 == 'h' && chPrev_1 == 'e' &&  chPrev == 'n')) &&
               (IsASpace(ch) || (ch == '/' && styler.SafeGetCharAt(back+1) == '*'))
               ) {
                  checkIsSentenceStart = false;
                  isSentenceStart = true;
            }
            else if (IsASpace(chPrev) && ch == '{') {
               checkIsSentenceStart = false;
               isSentenceStart = false;
            }
         }

         // commentNestingLevel is a non-visible state, used to identify the nesting level of a comment
         if (checkCommentNestingLevel) {
            if (chPrev == '/' && ch == '*') {
               commentNestingLevel++;
               // eat the '/' so we don't miscount a */ if we see /*/*
               --back;
            }
            if (chPrev == '*' && ch == '/') {
               commentNestingLevel--;
               // eat the '*' so we don't miscount a /* if we see */*/
               --back;
            }
         }
         --back;
      }
   }

   StyleContext sc(startPos, length, initStyle, styler, static_cast<unsigned char>(0xff));
   Sci_Position lineEndNext = styler.LineEnd(lineCurrent);

   for (; sc.More();) {
      if (sc.atLineStart) {
         visibleChars = 0;
         visibleChars1 = 0;
      }
      if (sc.atLineEnd) {
         lineCurrent++;
         lineEndNext = styler.LineEnd(lineCurrent);
      }
      // Handle line continuation generically.
      if (sc.ch == '~') {
         if (static_cast<Sci_Position>((sc.currentPos+1)) >= lineEndNext) {
            lineCurrent++;
            lineEndNext = styler.LineEnd(lineCurrent);
            sc.Forward();
            if (sc.ch == '\r' && sc.chNext == '\n') {
               sc.Forward();
            }
            continuationLine = true;
            sc.Forward();
            continue;
         }
      }

      const bool atLineEndBeforeSwitch = sc.atLineEnd;
      // Determine if the current state should terminate.
      switch (sc.state) {
         case SCE_ABL_OPERATOR:
            sc.SetState(SCE_ABL_DEFAULT);
            break;
         case SCE_ABL_NUMBER:
            // We accept almost anything because of hex. and maybe number suffixes and scientific notations in the future
            if (!(setWord.Contains(sc.ch)
				   || ((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E' ||
				                                          sc.chPrev == 'p' || sc.chPrev == 'P')))) {
               sc.SetState(SCE_ABL_DEFAULT);
            }
            break;
         case SCE_ABL_IDENTIFIER:
            if (sc.atLineStart || sc.atLineEnd || (!setWord.Contains(sc.ch) && sc.ch != '-')) {
               char s[1000];
               sc.GetCurrentLowered(s, sizeof(s));
               bool isLastWordEnd = (s[0] == 'e' && s[1] =='n' && s[2] == 'd' && !IsAlphaNumeric(s[3]) && s[3] != '-');  // helps to identify "end trigger" phrase
               if ((isSentenceStart && keywords2.InListAbbreviated (s,'(')) || (!isLastWordEnd && keywords3.InListAbbreviated (s,'('))) {
                  sc.ChangeState(SCE_ABL_BLOCK);
                  isSentenceStart = false;
               }
               else if (keywords1.InListAbbreviated (s,'(')) {
                  if (isLastWordEnd ||
                     (s[0] == 'f' && s[1] =='o' && s[2] == 'r' && s[3] == 'w' && s[4] =='a' && s[5] == 'r' && s[6] == 'd'&& !IsAlphaNumeric(s[7]))) {
                     sc.ChangeState(SCE_ABL_END);
                     isSentenceStart = false;
                  }
                  else if ((s[0] == 'e' && s[1] =='l' && s[2] == 's' && s[3] == 'e') ||
                         (s[0] == 't' && s[1] =='h' && s[2] == 'e' && s[3] == 'n')) {
                     sc.ChangeState(SCE_ABL_WORD);
                     isSentenceStart = true;
                  }
                  else {
                     sc.ChangeState(SCE_ABL_WORD);
                     isSentenceStart = false;
                  }
               }
               sc.SetState(SCE_ABL_DEFAULT);
            }
            break;
         case SCE_ABL_PREPROCESSOR:
            if (sc.atLineStart && !continuationLine) {
               sc.SetState(SCE_ABL_DEFAULT);
               // Force Scintilla to acknowledge changed stated even though this change might happen outside of the current line
               possibleOOLChange = true;
               isSentenceStart = true;
            }
            break;
         case SCE_ABL_LINECOMMENT:
            if (sc.atLineStart && !continuationLine) {
               sc.SetState(SCE_ABL_DEFAULT);
               isSentenceStart = true;
            } else {
               styleBeforeTaskMarker = SCE_ABL_LINECOMMENT;
               highlightTaskMarker(sc, styler, keywords4);
            }
            break;
         case SCE_ABL_TASKMARKER:
            if (isoperator(sc.ch) || IsASpace(sc.ch)) {
               sc.SetState(styleBeforeTaskMarker);
               styleBeforeTaskMarker = SCE_ABL_DEFAULT;
            }
            // fall through
         case SCE_ABL_COMMENT:
            if (sc.Match('*', '/')) {
               sc.Forward();
               commentNestingLevel--;
               if (commentNestingLevel == 0) {
                  sc.ForwardSetState(SCE_ABL_DEFAULT);
                  possibleOOLChange = true;
               }
            } else if (sc.Match('/', '*')) {
               commentNestingLevel++;
               sc.Forward();
            }
            if (commentNestingLevel > 0) {
               styleBeforeTaskMarker = SCE_ABL_COMMENT;
               possibleOOLChange = true;
               highlightTaskMarker(sc, styler, keywords4);
            }
            break;
         case SCE_ABL_STRING:
            if (sc.ch == '~') {
               sc.Forward(); // Skip a character after a tilde
            } else if (sc.ch == '\"') {
                  sc.ForwardSetState(SCE_ABL_DEFAULT);
            }
            break;
         case SCE_ABL_CHARACTER:
            if (sc.ch == '~') {
               sc.Forward(); // Skip a character after a tilde
            } else if (sc.ch == '\'') {
                  sc.ForwardSetState(SCE_ABL_DEFAULT);
            }
            break;
      }

      if (sc.atLineEnd && !atLineEndBeforeSwitch) {
         // State exit processing consumed characters up to end of line.
         lineCurrent++;
         lineEndNext = styler.LineEnd(lineCurrent);
      }

      // Determine if a new state should be entered.
      if (sc.state == SCE_ABL_DEFAULT) {
         if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
               sc.SetState(SCE_ABL_NUMBER);
               isSentenceStart = false;
         } else if (!sc.atLineEnd && (setWordStart.Contains(sc.ch)) && sc.chPrev != '&') {
               sc.SetState(SCE_ABL_IDENTIFIER);
         } else if (sc.Match('/', '*')) {
            if (sc.chPrev == '.' || sc.chPrev == ':' || sc.chPrev == '}') {
               isSentenceStart = true;
            }
            sc.SetState(SCE_ABL_COMMENT);
            possibleOOLChange = true;
            commentNestingLevel++;
            sc.Forward();   // Eat the * so it isn't used for the end of the comment
         } else if (sc.ch == '\"') {
               sc.SetState(SCE_ABL_STRING);
               isSentenceStart = false;
         } else if (sc.ch == '\'') {
            sc.SetState(SCE_ABL_CHARACTER);
            isSentenceStart = false;
         } else if (sc.ch == '&' && visibleChars1 == 0 && isSentenceStart) {
            // Preprocessor commands are alone on their line
            sc.SetState(SCE_ABL_PREPROCESSOR);
            // Force Scintilla to acknowledge changed stated even though this change might happen outside of the current line
            possibleOOLChange = true;
            // Skip whitespace between & and preprocessor word
            do {
               sc.Forward();
            } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
            if (sc.atLineEnd) {
               sc.SetState(SCE_ABL_DEFAULT);
            }
         } else if (sc.Match('/','/') && (IsASpace(sc.chPrev) || isSentenceStart)) {
            // Line comments are valid after a white space or EOL
            sc.SetState(SCE_ABL_LINECOMMENT);
            // Skip whitespace between // and preprocessor word
            do {
               sc.Forward();
            } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
            if (sc.atLineEnd) {
               sc.SetState(SCE_ABL_DEFAULT);
            }
         } else if (isoperator(sc.ch)) {
            sc.SetState(SCE_ABL_OPERATOR);
            /*    This code allows highlight of handles. Alas, it would cause the phrase "last-event:function"
               to be recognized as a BlockBegin */
               isSentenceStart = false;
         }
         else if ((sc.chPrev == '.' || sc.chPrev == ':' || sc.chPrev == '}') && (IsASpace(sc.ch))) {
            isSentenceStart = true;
         }
      }
      if (!IsASpace(sc.ch)) {
         visibleChars1++;
      }
      if (!IsASpace(sc.ch) && !IsSpaceEquiv(sc.state)) {
         visibleChars++;
      }
      continuationLine = false;
      sc.Forward();
   }
	if (possibleOOLChange)
		styler.ChangeLexerState(startPos, startPos + length);
   sc.Complete();
}