scan method

  1. @override
bool scan(
  1. RubyExternalScannerState state,
  2. TreeSitterExternalLexer lexer,
  3. List<bool> validSymbols
)
override

Implementation

@override
bool scan(
  RubyExternalScannerState state,
  TreeSitterExternalLexer lexer,
  List<bool> validSymbols,
) {
  state.hasLeadingWhitespace = false;
  bool valid(RubyExternalToken token) =>
      token.index < validSymbols.length && validSymbols[token.index];

  if (!valid(RubyExternalToken.stringStart)) {
    if ((valid(RubyExternalToken.stringContent) ||
            valid(RubyExternalToken.stringEnd)) &&
        state.literalStack.isNotEmpty) {
      return _scanLiteralContent(state, lexer);
    }
    if ((valid(RubyExternalToken.heredocContent) ||
            valid(RubyExternalToken.heredocBodyEnd)) &&
        state.openHeredocs.isNotEmpty) {
      return _scanHeredocContent(state, lexer);
    }
  }

  final whitespaceResult = _scanWhitespace(state, lexer, validSymbols);
  if (!whitespaceResult.$1) return false;
  if (whitespaceResult.$2 != null) {
    return _result(lexer, whitespaceResult.$2!);
  }

  switch (lexer.lookahead) {
    case 0x26: // &
      if (valid(RubyExternalToken.blockAmpersand)) {
        _advance(lexer);
        if (lexer.lookahead != 0x26 &&
            lexer.lookahead != 0x2e &&
            lexer.lookahead != 0x3d &&
            !_isSpace(lexer.lookahead)) {
          return _result(lexer, RubyExternalToken.blockAmpersand);
        }
        return false;
      }
    case 0x3c: // <
      if (valid(RubyExternalToken.singletonClassLeftAngleLeftAngle)) {
        _advance(lexer);
        if (lexer.lookahead == 0x3c) {
          _advance(lexer);
          return _result(
            lexer,
            RubyExternalToken.singletonClassLeftAngleLeftAngle,
          );
        }
        return false;
      }
    case 0x2a: // *
      if (valid(RubyExternalToken.splatStar) ||
          valid(RubyExternalToken.binaryStar) ||
          valid(RubyExternalToken.hashSplatStarStar) ||
          valid(RubyExternalToken.binaryStarStar)) {
        _advance(lexer);
        if (lexer.lookahead == 0x3d) return false;
        if (lexer.lookahead == 0x2a) {
          if (!valid(RubyExternalToken.hashSplatStarStar) &&
              !valid(RubyExternalToken.binaryStarStar)) {
            return false;
          }
          _advance(lexer);
          if (lexer.lookahead == 0x3d) return false;
          if (valid(RubyExternalToken.binaryStarStar) &&
              !state.hasLeadingWhitespace) {
            return _result(lexer, RubyExternalToken.binaryStarStar);
          }
          if (valid(RubyExternalToken.hashSplatStarStar) &&
              !_isSpace(lexer.lookahead)) {
            return _result(lexer, RubyExternalToken.hashSplatStarStar);
          }
          if (valid(RubyExternalToken.binaryStarStar)) {
            return _result(lexer, RubyExternalToken.binaryStarStar);
          }
          return _result(lexer, RubyExternalToken.hashSplatStarStar);
        }
        if (valid(RubyExternalToken.binaryStar) &&
            !state.hasLeadingWhitespace) {
          return _result(lexer, RubyExternalToken.binaryStar);
        }
        if (valid(RubyExternalToken.splatStar) &&
            !_isSpace(lexer.lookahead)) {
          return _result(lexer, RubyExternalToken.splatStar);
        }
        if (valid(RubyExternalToken.binaryStar)) {
          return _result(lexer, RubyExternalToken.binaryStar);
        }
        return _result(lexer, RubyExternalToken.splatStar);
      }
    case 0x2d: // -
      if (valid(RubyExternalToken.unaryMinus) ||
          valid(RubyExternalToken.unaryMinusNum) ||
          valid(RubyExternalToken.binaryMinus)) {
        _advance(lexer);
        if (lexer.lookahead == 0x3d || lexer.lookahead == 0x3e) {
          return false;
        }
        if (valid(RubyExternalToken.unaryMinusNum) &&
            (!valid(RubyExternalToken.binaryStar) ||
                state.hasLeadingWhitespace) &&
            _isDigit(lexer.lookahead)) {
          return _result(lexer, RubyExternalToken.unaryMinusNum);
        }
        if (valid(RubyExternalToken.unaryMinus) &&
            state.hasLeadingWhitespace &&
            !_isSpace(lexer.lookahead)) {
          return _result(lexer, RubyExternalToken.unaryMinus);
        }
        if (valid(RubyExternalToken.binaryMinus)) {
          return _result(lexer, RubyExternalToken.binaryMinus);
        }
        return _result(lexer, RubyExternalToken.unaryMinus);
      }
    case 0x3a: // :
      if (valid(RubyExternalToken.symbolStart)) {
        _advance(lexer);
        if (lexer.lookahead == 0x22 || lexer.lookahead == 0x27) {
          final delimiter = lexer.lookahead;
          _advance(lexer);
          state.literalStack.add(
            RubyLiteral(
              type: RubyExternalToken.symbolStart,
              openDelimiter: delimiter,
              closeDelimiter: delimiter,
              allowsInterpolation: delimiter == 0x22,
            ),
          );
          return _result(lexer, RubyExternalToken.symbolStart);
        }
        if (_scanSymbolIdentifier(lexer)) {
          return _result(lexer, RubyExternalToken.simpleSymbol);
        }
        return false;
      }
    case 0x5b: // [
      if (valid(RubyExternalToken.elementReferenceBracket) &&
          (!state.hasLeadingWhitespace ||
              !valid(RubyExternalToken.stringStart))) {
        _advance(lexer);
        return _result(lexer, RubyExternalToken.elementReferenceBracket);
      }
    default:
      break;
  }

  final identifierCandidate =
      ((valid(RubyExternalToken.hashKeySymbol) ||
              valid(RubyExternalToken.identifierSuffix)) &&
          (_isAlpha(lexer.lookahead) || lexer.lookahead == 0x5f)) ||
      (valid(RubyExternalToken.constantSuffix) && _isUpper(lexer.lookahead));
  if (identifierCandidate) {
    final identifierToken = _isUpper(lexer.lookahead)
        ? RubyExternalToken.constantSuffix
        : RubyExternalToken.identifierSuffix;
    while (_isAlphanumeric(lexer.lookahead) || lexer.lookahead == 0x5f) {
      _advance(lexer);
    }
    if (valid(RubyExternalToken.hashKeySymbol) && lexer.lookahead == 0x3a) {
      lexer.markEnd();
      _advance(lexer);
      if (lexer.lookahead != 0x3a) {
        return _result(lexer, RubyExternalToken.hashKeySymbol);
      }
    } else if (valid(identifierToken) && lexer.lookahead == 0x21) {
      _advance(lexer);
      if (lexer.lookahead != 0x3d) {
        return _result(lexer, identifierToken);
      }
    }
    return false;
  }

  if (valid(RubyExternalToken.stringStart)) {
    if (lexer.lookahead == 0x3c) {
      _advance(lexer);
      if (lexer.lookahead != 0x3c) return false;
      _advance(lexer);
      final heredoc = RubyHeredoc();
      if (lexer.lookahead == 0x2d || lexer.lookahead == 0x7e) {
        _advance(lexer);
        heredoc.endWordIndentationAllowed = true;
      }
      _scanHeredocWord(lexer, heredoc);
      if (heredoc.word.isEmpty) return false;
      state.openHeredocs.add(heredoc);
      return _result(lexer, RubyExternalToken.heredocStart);
    }

    final literal = _scanOpenDelimiter(state, lexer, validSymbols);
    if (literal == null) return false;
    state.literalStack.add(literal);
    return _result(lexer, literal.type);
  }

  return false;
}