private boolean parseCaptures(@NotNull Queue<Token> output,
                                Constants.CaptureKey captureKey,
                                SyntaxNodeDescriptor rule,
                                MatchData matchData,
                                StringWithId string,
                                CharSequence line,
                                int startLineOffset,
                                FList<TextMateLexerState> states) {
    TextMateCapture @Nullable [] captures = rule.getCaptureRules(captureKey);
    if (captures == null) {
      return false;
    }

    Deque<TextMateRange> activeCaptureRanges = new LinkedList<>();
    for (int group = 0; group < matchData.count(); group++) {
      TextMateCapture capture = group < captures.length ? captures[group] : null;
      if (capture == null) {
        continue;
      }

      TextMateRange byteRange = matchData.byteOffset(group);
      if (byteRange.isEmpty()) {
        continue;
      }

      TextMateRange captureRange = matchData.charRange(line, string.bytes, group);

      while (!activeCaptureRanges.isEmpty() && activeCaptureRanges.peek().end <= captureRange.start) {
        closeScopeSelector(output, startLineOffset + activeCaptureRanges.pop().end);
      }

      if (capture instanceof TextMateCapture.Name) {
        CharSequence captureName = ((TextMateCapture.Name)capture).getName();
        CharSequence scopeName = rule.hasBackReference(captureKey, group)
                                    ? SyntaxMatchUtils.replaceGroupsWithMatchDataInCaptures(captureName, string, matchData)
                                    : captureName;
        int selectorStartOffset = 0;
        int indexOfSpace = Strings.indexOf(scopeName, ' ', selectorStartOffset);
        if (indexOfSpace == -1) {
          openScopeSelector(output, scopeName, startLineOffset + captureRange.start);
          activeCaptureRanges.push(captureRange);
        }
        else {
          while (indexOfSpace >= 0) {
            openScopeSelector(output, scopeName.subSequence(selectorStartOffset, indexOfSpace), startLineOffset + captureRange.start);
            selectorStartOffset = indexOfSpace + 1;
            indexOfSpace = Strings.indexOf(scopeName, ' ', selectorStartOffset);
            activeCaptureRanges.push(captureRange);
          }
          openScopeSelector(output, scopeName.subSequence(selectorStartOffset, scopeName.length()), startLineOffset + captureRange.start);
          activeCaptureRanges.push(captureRange);
        }
      }
      else if (capture instanceof TextMateCapture.Rule) {
        CharSequence capturedString = line.subSequence(0, captureRange.end);
        StringWithId capturedStringWithId = new StringWithId(capturedString);
        TextMateLexerState captureState = new TextMateLexerState(((TextMateCapture.Rule)capture).getNode(),
                                                                 matchData,
                                                                 TextMateWeigh.Priority.NORMAL,
                                                                 byteRange.start,
                                                                 capturedStringWithId);
        parseLine(capturedString, output, states.prepend(captureState), startLineOffset, captureRange.start, byteRange.start);
      }
      else {
        throw new IllegalStateException("unknown capture type: " + capture);
      }
    }
    while (!activeCaptureRanges.isEmpty()) {
      closeScopeSelector(output, startLineOffset + activeCaptureRanges.pop().end);
    }
    return true;
  }
private boolean parseCaptures(@NotNull Queue<Token> output,
                                Constants.CaptureKey captureKey,
                                SyntaxNodeDescriptor rule,
                                MatchData matchData,
                                StringWithId string,
                                CharSequence line,
                                int startLineOffset,
                                FList<TextMateLexerState> states)