Source:
<code>private boolean parseSingleRedirect(BashPsiBuilder builder, boolean inCheckMode, boolean allowHeredoc) { PsiBuilder.Marker marker = builder.mark(); IElementType firstToken = builder.getTokenType(); boolean firstIsInt = firstToken == INTEGER_LITERAL; IElementType secondToken; if (firstIsInt) { builder.advanceLexer(); secondToken = builder.rawLookup(0); } else { secondToken = firstToken; } if (!redirectionSet.contains(secondToken)) { marker.drop(); return false; } if (validBeforeFiledescriptor.contains(secondToken) && ParserUtil.hasNextTokens(builder, true, secondToken, FILEDESCRIPTOR)) { ParserUtil.getTokenAndAdvance(builder, true); PsiBuilder.Marker descriptorMarker = builder.mark(); ParserUtil.getTokenAndAdvance(builder, true); descriptorMarker.done(FILEDESCRIPTOR); marker.done(REDIRECT_ELEMENT); return true; } builder.advanceLexer(); if (allowHeredoc && heredocStarters.contains(secondToken)) { if (inCheckMode) { marker.drop(); return builder.getTokenType() == HEREDOC_MARKER_START; } if (builder.getTokenType() != HEREDOC_MARKER_START) { marker.drop(); return false; } marker.drop(); ParserUtil.markTokenAndAdvance(builder, HEREDOC_START_ELEMENT); <START> if (!builder.getParsingState().getHeredocMarkersIndexSet().contains(builder.rawTokenIndex())) { <END> builder.getParsingState().pushHeredocMarker(builder.rawTokenIndex()); } return true; } boolean ok = Parsing.word.parseWord(builder); if (ok) { marker.done(REDIRECT_ELEMENT); } else { marker.drop(); builder.error("Invalid redirect"); if (builder.getTokenType() != LINE_FEED) { builder.advanceLexer(); } } return true; }</code><technical_language>is sense move logic of add if added pushHeredocMarker? allow remove getHeredocMarkersIndexSet simplify this part</technical_language>

Target:
private boolean parseSingleRedirect(BashPsiBuilder builder, boolean inCheckMode, boolean allowHeredoc) { PsiBuilder.Marker marker = builder.mark(); IElementType firstToken = builder.getTokenType(); boolean firstIsInt = firstToken == INTEGER_LITERAL; IElementType secondToken; if (firstIsInt) { builder.advanceLexer(); secondToken = builder.rawLookup(0); } else { secondToken = firstToken; } if (!redirectionSet.contains(secondToken)) { marker.drop(); return false; } if (validBeforeFiledescriptor.contains(secondToken) && ParserUtil.hasNextTokens(builder, true, secondToken, FILEDESCRIPTOR)) { ParserUtil.getTokenAndAdvance(builder, true); PsiBuilder.Marker descriptorMarker = builder.mark(); ParserUtil.getTokenAndAdvance(builder, true); descriptorMarker.done(FILEDESCRIPTOR); marker.done(REDIRECT_ELEMENT); return true; } builder.advanceLexer(); if (allowHeredoc && heredocStarters.contains(secondToken)) { if (inCheckMode) { marker.drop(); return builder.getTokenType() == HEREDOC_MARKER_START; } if (builder.getTokenType() != HEREDOC_MARKER_START) { marker.drop(); return false; } marker.drop(); ParserUtil.markTokenAndAdvance(builder, HEREDOC_START_ELEMENT); builder.getParsingState().pushHeredocMarker(builder.rawTokenIndex()); return true; } boolean ok = Parsing.word.parseWord(builder); if (ok) { marker.done(REDIRECT_ELEMENT); } else { marker.drop(); builder.error("Invalid redirect"); if (builder.getTokenType() != LINE_FEED) { builder.advanceLexer(); } } return true; }