docs: private int processNextToken(RawTextNode node, final int offset, String text) {
      // Find the transition whose pattern matches earliest in the raw text (and is applicable)
      int numCharsConsumed;
      Context next;
      int earliestStart = Integer.MAX_VALUE;
      int earliestEnd = -1;
      Transition earliestTransition = null;
      Matcher earliestMatcher = null;
      List<Transition> ts = TRANSITIONS.get(context.state);
      if (ts == null) {
        throw new NullPointerException(
            "no transitions for state: "
                + context.state
                + " @"
                + node.substringLocation(offset, offset + 1));
      }
      for (Transition transition : ts) {
        if (transition.pattern != null) {
          Matcher matcher = transition.pattern.matcher(text);
          // For each transition:
          // look for matches, if the match is later than the current earliest match, give up
          // otherwise if the match is applicable, store it.
          // NOTE: matcher.find() returns matches in sequential order.
          try {
            while (matcher.find() && matcher.start() < earliestStart) {
              int start = matcher.start();
              int end = matcher.end();
              if (transition.isApplicableTo(context, matcher)) {
                earliestStart = start;
                earliestEnd = end;
                earliestTransition = transition;
                earliestMatcher = matcher;
                break;
              }
            }
          } catch (StackOverflowError soe) {
            // catch and annotate with the pattern.
            throw new RuntimeException(
                String.format(
                    "StackOverflow while trying to match: '%s' in context %s starting @ %s",
                    transition.pattern, context, node.substringLocation(offset, offset + 1)),
                soe);
          }
        } else if (transition.literal != null) {
          int start = 0;
          int index;
          String needle = transition.literal;
          while ((index = text.indexOf(needle, start)) != -1 && index < earliestStart) {
            if (transition.isApplicableTo(context, null)) {
              earliestStart = index;
              earliestEnd = index + needle.length();
              earliestTransition = transition;
              earliestMatcher = null;
              break;
            }
          }
        } else {
          if (text.length() < earliestStart && transition.isApplicableTo(context, null)) {
            earliestStart = text.length();
            earliestEnd = text.length();
            earliestTransition = transition;
            earliestMatcher = null;
          }
        }
      }
  
      if (earliestTransition != null) {
        int transitionOffset = offset;
        // the earliest start might be at the end for null transitions.
        if (earliestStart < text.length()) {
          transitionOffset += earliestStart;
        }
        next =
            earliestTransition.computeNextContext(node, transitionOffset, context, earliestMatcher);
        numCharsConsumed = earliestEnd;
      } else {
        throw SoyAutoescapeException.createWithNode(
            "Error determining next state when encountering \"" + text + "\" in " + context,
            // calculate a raw text node that points at the beginning of the string that couldn't
            // bet matched.
            node.substring(Integer.MAX_VALUE /* bogus id */, offset));
      }
      if (numCharsConsumed == 0 && next.state == context.state) {
        throw new IllegalStateException("Infinite loop at `" + text + "` / " + context);
      }
      this.context = next;
      return numCharsConsumed;
    }