/* * Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC * All rights reserved. * * The source code of this document is proprietary work, and is not licensed for * distribution. For information about licensing, contact Sam Harwell at: * sam@tunnelvisionlabs.com */ package org.antlr.works.editor.grammar.formatting; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.logging.Level; import javax.swing.text.BadLocationException; import org.antlr.netbeans.editor.classification.TokenTag; import org.antlr.netbeans.editor.completion.Anchor; import org.antlr.netbeans.editor.completion.ReferenceAnchors; import org.antlr.netbeans.editor.tagging.Tagger; import org.antlr.netbeans.editor.text.DocumentSnapshot; import org.antlr.netbeans.editor.text.SnapshotPosition; import org.antlr.netbeans.parsing.spi.ParserData; import org.antlr.netbeans.parsing.spi.ParserDataOptions; import org.antlr.netbeans.parsing.spi.ParserTaskManager; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.Dependents; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.RuleDependencies; import org.antlr.v4.runtime.RuleDependency; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.atn.ATNConfig; import org.antlr.v4.runtime.atn.NotSetTransition; import org.antlr.v4.runtime.atn.Transition; import org.antlr.v4.runtime.misc.IntervalSet; import org.antlr.v4.runtime.misc.Tuple; import org.antlr.v4.runtime.misc.Tuple2; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.RuleNode; import org.antlr.v4.runtime.tree.TerminalNode; import org.antlr.works.editor.antlr4.completion.CaretReachedException; import org.antlr.works.editor.antlr4.completion.CodeCompletionErrorStrategy; import org.antlr.works.editor.antlr4.formatting.AbstractIndentTask; import org.antlr.works.editor.antlr4.formatting.AlignmentRequirement; import org.antlr.works.editor.antlr4.parsing.ParseTrees; import org.antlr.works.editor.grammar.GrammarEditorKit; import org.antlr.works.editor.grammar.GrammarParserDataDefinitions; import org.antlr.works.editor.grammar.completion.CodeCompletionGrammarParser; import org.antlr.works.editor.grammar.completion.CompletionParserATNSimulator; import org.antlr.works.editor.grammar.completion.GrammarForestParser; import org.antlr.works.editor.grammar.completion.ParserFactory; import org.antlr.works.editor.grammar.experimental.GrammarParser; import org.antlr.works.editor.grammar.experimental.GrammarParserAnchorListener; import org.antlr.works.editor.grammar.experimental.GrammarReferenceAnchors; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.ActionBlockContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.AltListContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.BlockContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.BlockSetContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.ChannelsSpecContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.DelegateGrammarContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.DelegateGrammarsContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.ElementsContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.LabeledAltContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.LexerAltContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.LexerAltListContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.LexerBlockContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.LexerElementsContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.LexerRuleContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.ModeSpecContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.OptionsSpecContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.ParserRuleSpecContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.RuleAltListContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.RulesContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.TokensSpecContext; import org.antlr.works.editor.grammar.experimental.generated.GrammarParserBaseVisitor; import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.editor.mimelookup.MimeRegistration; import org.netbeans.modules.editor.indent.spi.Context; import org.netbeans.modules.editor.indent.spi.IndentTask; import org.netbeans.modules.editor.indent.spi.IndentTask.Factory; import org.openide.util.NotImplementedException; /** * * @author Sam Harwell */ public class GrammarIndentTask extends AbstractIndentTask { private GrammarCodeStyle codeStyle; public GrammarIndentTask(Context context) { super(context); } @Override public boolean smartReindent() throws BadLocationException { if (GrammarEditorKit.isLegacyMode(getContext().document())) { return false; } return super.smartReindent(); } @Override protected ReferenceAnchors findNearestAnchors(ParserTaskManager taskManager, DocumentSnapshot snapshot, int endOffset) { List<Anchor> anchors = getDynamicAnchorPoints(); int grammarType = -1; // the innermost anchor enclosing the caret Anchor enclosing = null; // the last anchor starting before the caret Anchor previous = null; if (anchors != null) { /* * parse the current rule */ for (Anchor anchor : anchors) { if (anchor instanceof GrammarParserAnchorListener.GrammarTypeAnchor) { grammarType = ((GrammarParserAnchorListener.GrammarTypeAnchor)anchor).getGrammarType(); continue; } if (anchor.getSpan().getStartPosition(snapshot).getOffset() <= endOffset) { previous = anchor; if (anchor.getSpan().getEndPosition(snapshot).getOffset() > endOffset) { enclosing = anchor; } } else { break; } } } return new GrammarReferenceAnchors(grammarType, previous, enclosing); } @Override protected Tagger<TokenTag<Token>> getTagger() { Future<ParserData<Tagger<TokenTag<Token>>>> futureTokensData = getTaskManager().getData(getSnapshot(), GrammarParserDataDefinitions.LEXER_TOKENS, EnumSet.of(ParserDataOptions.SYNCHRONOUS)); Tagger<TokenTag<Token>> tagger = null; try { tagger = futureTokensData != null ? futureTokensData.get().getData() : null; } catch (InterruptedException | ExecutionException ex) { // Warning because a timeout keeps the UI responsive but still indicates a broken auto-indent feature LOGGER.log(Level.WARNING, "An exception occurred while getting the token tagger.", ex); } return tagger; } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleSpec, version=0, dependents=Dependents.SELF) protected Map<RuleContext, CaretReachedException> getParseTrees(CommonTokenStream tokens, ReferenceAnchors anchors) { CodeCompletionGrammarParser parser = ParserFactory.DEFAULT.getParser(tokens); parser.setBuildParseTree(true); parser.setErrorHandler(new CodeCompletionErrorStrategy()); GrammarForestParser forestParser; if (anchors.getPrevious() != null) { switch (anchors.getPrevious().getRule()) { case GrammarParser.RULE_ruleSpec: forestParser = GrammarForestParser.RULES; break; default: forestParser = null; } } else { forestParser = GrammarForestParser.GRAMMAR_SPEC; } if (forestParser == null) { return null; } CompletionParserATNSimulator originalInterpreter = parser.getInterpreter(); try { parser.setInterpreter(new CompletionParserATNSimulator(parser, GrammarParser._ATN) { IntervalSet wordlikeTokenTypes; { wordlikeTokenTypes = IntervalSet.of(0, GrammarParser._ATN.maxTokenType); //wordlikeTokenTypes.remove(GrammarParser.OR); //wordlikeTokenTypes.remove(GrammarParser.RPAREN); //wordlikeTokenTypes.remove(GrammarParser.RBRACE); //wordlikeTokenTypes.remove(GrammarParser.END_ACTION); //wordlikeTokenTypes.remove(GrammarParser.END_ARG_ACTION); //wordlikeTokenTypes.remove(GrammarParser.OPTIONS); //wordlikeTokenTypes.remove(GrammarParser.AT); //wordlikeTokenTypes.remove(GrammarParser.ASSIGN); //wordlikeTokenTypes.remove(GrammarParser.SEMI); //wordlikeTokenTypes.remove(GrammarParser.COMMA); //wordlikeTokenTypes.remove(GrammarParser.MODE); } @Override protected IntervalSet getWordlikeTokenTypes() { return wordlikeTokenTypes; } }); Map<RuleContext, CaretReachedException> parseTrees = forestParser.getParseTrees(parser); return parseTrees; } finally { parser.setInterpreter(originalInterpreter); } } @Override protected void fallbackReindent() throws BadLocationException { List<Anchor> anchors = getDynamicAnchorPoints(); SnapshotPosition contextEndPosition = new SnapshotPosition(getSnapshot(), getContext().endOffset()); SnapshotPosition endPosition = contextEndPosition.getContainingLine().getEndIncludingLineBreak(); SnapshotPosition endPositionOnLine = contextEndPosition.getContainingLine().getEnd(); int endOffset = endPosition.getOffset(); if (anchors != null) { Anchor previous = null; Anchor enclosing = null; for (Anchor anchor : anchors) { if (anchor.getSpan().getStartPosition(getSnapshot()).getOffset() < endOffset) { previous = anchor; } if (anchor.getSpan().getStartPosition(getSnapshot()).getOffset() <= endOffset) { if (anchor.getSpan().getEndPosition(getSnapshot()).getOffset() > endOffset) { enclosing = anchor; } } else { break; } } if (previous != null) { int anchorLineStartOffset = getContext().lineStartOffset(previous.getSpan().getStartPosition(getSnapshot()).getOffset()); getContext().modifyIndent(getContext().lineStartOffset(getContext().endOffset()), getContext().lineIndent(anchorLineStartOffset)); return; } } super.fallbackReindent(); } @Override @RuleDependencies({ @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_parserRuleSpec, version=0), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleAltList, version=0), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerRule, version=0), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerAltList, version=1), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_altList, version=0), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_blockSet, version=0), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerBlock, version=1), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_block, version=0), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_optionsSpec, version=6), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_tokensSpec, version=6), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_channelsSpec, version=6), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_modeSpec, version=3), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_delegateGrammars, version=6), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_actionBlock, version=5), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_elements, version=5), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerElements, version=3), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_delegateGrammar, version=0), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerAlt, version=3), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_labeledAlt, version=1), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_rules, version=0), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerCommands, version=1, dependents=Dependents.DESCENDANTS), }) protected Set<AlignmentRequirement> getAlignmentRequirement( Map.Entry<RuleContext, CaretReachedException> parseTree, @NonNull ParseTree targetElement, ParseTree ancestor) { TerminalNode ancestorStart = ParseTrees.getStartNode(ancestor); if (ancestorStart == null) { return EnumSet.of(AlignmentRequirement.USE_ANCESTOR); } if (targetElement instanceof TerminalNode) { if (ancestorStart == targetElement) { return EnumSet.of(AlignmentRequirement.USE_ANCESTOR); } } else if (ancestor == targetElement) { // special handling for predicted tokens that don't actually exist yet CaretReachedException ex = parseTree.getValue(); if (ex != null && ex.getTransitions() != null) { boolean validTransition = false; boolean selfTransition = false; // examine transitions for predictions that don't actually exist yet for (Map.Entry<ATNConfig, List<Transition>> entry : ex.getTransitions().entrySet()) { if (entry.getValue() == null) { continue; } for (Transition transition : entry.getValue()) { IntervalSet label = transition.label(); if (label == null) { continue; } boolean containsInvalid = label.contains(GrammarParser.OR) || label.contains(GrammarParser.RPAREN) || label.contains(GrammarParser.RBRACE) || label.contains(GrammarParser.END_ACTION) || label.contains(GrammarParser.END_ARG_ACTION) || label.contains(GrammarParser.OPTIONS) || label.contains(GrammarParser.AT) || label.contains(GrammarParser.ASSIGN) || label.contains(GrammarParser.SEMI) || label.contains(GrammarParser.COMMA) || label.contains(GrammarParser.MODE) || label.contains(GrammarParser.RARROW) || label.contains(GrammarParser.POUND); boolean containsInvalidSelf = label.contains(GrammarParser.LPAREN) || label.contains(GrammarParser.BEGIN_ACTION) || label.contains(GrammarParser.BEGIN_ARG_ACTION); if (transition instanceof NotSetTransition) { containsInvalid = !containsInvalid; containsInvalidSelf = !containsInvalidSelf; } validTransition |= !containsInvalid; selfTransition |= !containsInvalidSelf; } } if (!validTransition) { return EnumSet.of(AlignmentRequirement.IGNORE_TREE); } else if (!selfTransition) { return EnumSet.of(AlignmentRequirement.USE_ANCESTOR); } } } if (!(ancestor instanceof RuleNode)) { return EnumSet.of(AlignmentRequirement.USE_ANCESTOR); } RuleNode ruleNode = (RuleNode)ancestor; RuleContext ruleContext = ruleNode.getRuleContext(); switch (ruleContext.getRuleIndex()) { case GrammarParser.RULE_parserRuleSpec: case GrammarParser.RULE_ruleAltList: case GrammarParser.RULE_lexerRule: case GrammarParser.RULE_lexerAltList: case GrammarParser.RULE_altList: case GrammarParser.RULE_blockSet: case GrammarParser.RULE_lexerBlock: case GrammarParser.RULE_block: case GrammarParser.RULE_optionsSpec: case GrammarParser.RULE_tokensSpec: case GrammarParser.RULE_channelsSpec: case GrammarParser.RULE_modeSpec: case GrammarParser.RULE_delegateGrammars: case GrammarParser.RULE_actionBlock: case GrammarParser.RULE_elements: case GrammarParser.RULE_lexerElements: case GrammarParser.RULE_rules: //case GrammarParser.RULE_lexerCommands: return EnumSet.of(AlignmentRequirement.PRIOR_SIBLING); case GrammarParser.RULE_lexerAlt: LexerAltContext lexerAltContext = ParseTrees.getTypedRuleContext(ancestor, LexerAltContext.class); if (lexerAltContext != null && lexerAltContext.lexerCommands() != null && ParseTrees.isAncestorOf(lexerAltContext.lexerCommands(), targetElement)) { return EnumSet.of(AlignmentRequirement.PRIOR_SIBLING); } else { return EnumSet.of(AlignmentRequirement.USE_ANCESTOR); } case GrammarParser.RULE_labeledAlt: if (ParseTrees.getTerminalNodeType(targetElement) == GrammarParser.POUND) { return EnumSet.of(AlignmentRequirement.PRIOR_SIBLING); } else { return EnumSet.of(AlignmentRequirement.USE_ANCESTOR); } case GrammarParser.RULE_delegateGrammar: return EnumSet.noneOf(AlignmentRequirement.class); default: return EnumSet.of(AlignmentRequirement.USE_ANCESTOR); } } @Override protected Tuple2<? extends ParseTree, Integer> getAlignmentElement( Map.Entry<RuleContext, CaretReachedException> parseTree, ParseTree targetElement, ParseTree container, List<? extends ParseTree> priorSiblings) { AlignmentElementVisitor visitor = new AlignmentElementVisitor(parseTree, targetElement, priorSiblings); return visitor.visit(container); } @Override protected List<Anchor> getDynamicAnchorPoints() { List<Anchor> anchors = null; Future<ParserData<List<Anchor>>> result = getTaskManager().getData(getSnapshot(), GrammarParserDataDefinitions.DYNAMIC_ANCHOR_POINTS, EnumSet.of(ParserDataOptions.SYNCHRONOUS)); try { anchors = result != null ? result.get().getData() : null; } catch (InterruptedException | ExecutionException ex) { // Warning because a timeout keeps the UI responsive but still indicates a broken auto-indent feature LOGGER.log(Level.WARNING, "An exception occurred while getting the dynamic anchor points.", ex); } return anchors; } @Override protected GrammarCodeStyle getCodeStyle() { if (codeStyle == null) { codeStyle = GrammarCodeStyle.getDefault(getContext().document()); } return codeStyle; } protected class AlignmentElementVisitor extends GrammarParserBaseVisitor<Tuple2<? extends ParseTree, Integer>> { private final Map.Entry<RuleContext, CaretReachedException> parseTree; private final ParseTree targetElement; private final List<? extends ParseTree> priorSiblings; public AlignmentElementVisitor(Map.Entry<RuleContext, CaretReachedException> parseTree, ParseTree targetElement, List<? extends ParseTree> priorSiblings) { this.parseTree = parseTree; this.targetElement = targetElement; this.priorSiblings = priorSiblings; } @Override public Tuple2<? extends ParseTree, Integer> visitChildren(RuleNode node) { throw new UnsupportedOperationException("This visitor is designed for top-level nodes only."); } @RuleDependencies({ @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_elements, version=5, dependents=Dependents.PARENTS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerElements, version=3, dependents=Dependents.PARENTS), }) private Tuple2<? extends ParseTree, Integer> visitElements() { // the non-terminals under these rules are straightforward int firstElementIndex = -1; for (int i = 0; i < priorSiblings.size(); i++) { ParseTree sibling = priorSiblings.get(i); if (sibling instanceof RuleNode) { firstElementIndex = i; break; } } for (int i = priorSiblings.size() - 2; i >= 0; i--) { ParseTree sibling = priorSiblings.get(i); if (!(sibling instanceof RuleNode)) { continue; } if (i == firstElementIndex || ParseTrees.elementStartsLine(sibling)) { return Tuple.create(sibling, 0); } } // handle at the parent return null; } @RuleDependencies({ @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleAltList, version=0, dependents=Dependents.PARENTS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerAltList, version=1, dependents=Dependents.PARENTS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_altList, version=0, dependents=Dependents.PARENTS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_blockSet, version=0, dependents=Dependents.PARENTS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerBlock, version=1, dependents=Dependents.PARENTS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_block, version=0, dependents=Dependents.PARENTS), }) private Tuple2<? extends ParseTree, Integer> visitGenericBlock(ParserRuleContext container) { if (targetElement == ParseTrees.getStartNode(container)) { return null; } if (ParseTrees.getTerminalNodeType(targetElement) == GrammarParser.RPAREN) { return Tuple.create(container, 0); } // OR lines up with previous OR boolean orNode = ParseTrees.getTerminalNodeType(targetElement) == GrammarParser.OR; if (orNode) { for (int i = priorSiblings.size() - 2; i >= 0; i--) { ParseTree sibling = priorSiblings.get(i); if (!(sibling instanceof TerminalNode)) { continue; } if (i == 0 || ParseTrees.elementStartsLine(sibling)) { return Tuple.create(sibling, 0); } } if (ParseTrees.getTerminalNodeType(ParseTrees.getStartNode(container)) != GrammarParser.LPAREN) { // handle at the parent so it aligns at the ( return null; } return Tuple.create(container, 0); } // the non-terminals under these rules are straightforward int firstRuleIndex = -1; for (int i = 0; i < priorSiblings.size(); i++) { ParseTree sibling = priorSiblings.get(i); if (sibling instanceof RuleNode) { firstRuleIndex = i; break; } } for (int i = priorSiblings.size() - 2; i >= 0; i--) { ParseTree sibling = priorSiblings.get(i); if (!(sibling instanceof RuleNode)) { continue; } if (i == firstRuleIndex || ParseTrees.elementStartsLine(sibling)) { return Tuple.create(sibling, 0); } } // handle at the parent return null; } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_elements, version=5, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitElements(ElementsContext ctx) { return visitElements(); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerElements, version=3, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitLexerElements(LexerElementsContext ctx) { return visitElements(); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleAltList, version=0, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitRuleAltList(RuleAltListContext ctx) { return visitGenericBlock(ctx); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerAltList, version=1, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitLexerAltList(LexerAltListContext ctx) { return visitGenericBlock(ctx); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_altList, version=0, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitAltList(AltListContext ctx) { return visitGenericBlock(ctx); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_blockSet, version=0, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitBlockSet(BlockSetContext ctx) { return visitGenericBlock(ctx); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerBlock, version=1, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitLexerBlock(LexerBlockContext ctx) { return visitGenericBlock(ctx); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_block, version=0, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitBlock(BlockContext ctx) { return visitGenericBlock(ctx); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_parserRuleSpec, version=0, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitParserRuleSpec(ParserRuleSpecContext ctx) { if (ParseTrees.getTerminalNodeType(targetElement) == GrammarParser.AT) { return Tuple.create(ctx, 0); } if (ctx.COLON() != null) { if (ParseTrees.startsBeforeStartOf(ctx.COLON(), targetElement)) { switch (ParseTrees.getTerminalNodeType(targetElement)) { case GrammarParser.SEMI: case GrammarParser.OR: return Tuple.create(ctx.COLON(), 0); default: return Tuple.create(ctx.COLON(), getCodeStyle().getIndentSize()); } } } return Tuple.create(ctx, getCodeStyle().getIndentSize()); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerRule, version=0, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitLexerRule(LexerRuleContext ctx) { if (ctx.name == null) { return null; } if (ParseTrees.getTerminalNodeType(targetElement) == GrammarParser.AT) { return Tuple.create(ctx, 0); } if (ctx.COLON() != null) { if (ParseTrees.startsBeforeStartOf(ctx.COLON(), targetElement)) { switch (ParseTrees.getTerminalNodeType(targetElement)) { case GrammarParser.SEMI: case GrammarParser.OR: return Tuple.create(ctx.COLON(), 0); default: return Tuple.create(ctx.COLON(), getCodeStyle().getIndentSize()); } } } return Tuple.create(ctx, getCodeStyle().getIndentSize()); } @Override @RuleDependencies({ @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerAlt, version=3, dependents=Dependents.PARENTS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerElements, version=0, dependents=Dependents.SELF), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerCommands, version=1, dependents=Dependents.DESCENDANTS), }) public Tuple2<? extends ParseTree, Integer> visitLexerAlt(LexerAltContext ctx) { assert ctx.lexerCommands() != null && ParseTrees.isAncestorOf(ctx.lexerCommands(), targetElement); if (ctx.lexerElements() == null) { return null; } return Tuple.create(ctx.lexerElements(), 0); } @Override @RuleDependencies({ @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_labeledAlt, version=1, dependents=Dependents.PARENTS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_alternative, version=5, dependents=Dependents.SELF), }) public Tuple2<? extends ParseTree, Integer> visitLabeledAlt(LabeledAltContext ctx) { assert ParseTrees.getTerminalNodeType(targetElement) == GrammarParser.POUND; if (ctx.alternative() == null) { return null; } return Tuple.create(ctx.alternative(), 0); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_rules, version=0, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitRules(RulesContext ctx) { for (int i = priorSiblings.size() - 2; i >= 0; i--) { ParseTree sibling = priorSiblings.get(i); if (i == 0 || ParseTrees.elementStartsLine(sibling)) { return Tuple.create(sibling, 0); } } return null; } @Override @RuleDependencies({ @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_optionsSpec, version=6, dependents=Dependents.PARENTS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_option, version=3, dependents=Dependents.PARENTS), }) public Tuple2<? extends ParseTree, Integer> visitOptionsSpec(OptionsSpecContext ctx) { // use previous option if any, otherwise use the block. // special handling for closing } if (targetElement == ctx.RBRACE()) { return Tuple.create(ctx, 0); } int firstOptionIndex = -1; for (int i = 0; i < priorSiblings.size(); i++) { ParseTree sibling = priorSiblings.get(i); if (!(sibling instanceof RuleNode)) { continue; } if (((RuleNode)sibling).getRuleContext().getRuleIndex() == GrammarParser.RULE_option) { firstOptionIndex = i; break; } } boolean semi = ParseTrees.getTerminalNodeType(targetElement) == GrammarParser.SEMI; for (int i = priorSiblings.size() - 2; i >= 0; i--) { ParseTree sibling = priorSiblings.get(i); if (!(sibling instanceof RuleNode)) { continue; } RuleContext context = ((RuleNode)sibling).getRuleContext(); if (context.getRuleIndex() == GrammarParser.RULE_option) { if (i == firstOptionIndex || ParseTrees.elementStartsLine(sibling)) { return Tuple.create(sibling, semi ? getCodeStyle().getIndentSize() : 0); } } } return Tuple.create(ctx, getCodeStyle().getIndentSize()); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_tokensSpec, version=6, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitTokensSpec(TokensSpecContext ctx) { if (ctx.getChildCount() == 0 || targetElement == ctx.getChild(0)) { return null; } if (ParseTrees.getTerminalNodeType(targetElement) == GrammarParser.RBRACE) { return Tuple.create(ctx, 0); } // align to the previous element for (int i = priorSiblings.size() - 2; i >= 0; i--) { ParseTree sibling = priorSiblings.get(i); // stop at the first id rule, index 0 is the TOKENS terminal itself if (i == 1 || ParseTrees.elementStartsLine(sibling)) { return Tuple.create(sibling, 0); } } return Tuple.create(ctx, getCodeStyle().getIndentSize()); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_channelsSpec, version=6, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitChannelsSpec(ChannelsSpecContext ctx) { if (ctx.getChildCount() == 0 || targetElement == ctx.getChild(0)) { return null; } if (ParseTrees.getTerminalNodeType(targetElement) == GrammarParser.RBRACE) { return Tuple.create(ctx, 0); } // align to the previous element for (int i = priorSiblings.size() - 2; i >= 0; i--) { ParseTree sibling = priorSiblings.get(i); // stop at the first id rule, index 0 is the CHANNELS terminal itself if (i == 1 || ParseTrees.elementStartsLine(sibling)) { return Tuple.create(sibling, 0); } } return Tuple.create(ctx, getCodeStyle().getIndentSize()); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_actionBlock, version=5, dependents=Dependents.PARENTS) public Tuple2<? extends ParseTree, Integer> visitActionBlock(ActionBlockContext ctx) { if (ctx.getChildCount() == 0 || targetElement == ctx.getChild(0)) { return null; } if (ParseTrees.getTerminalNodeType(targetElement) == GrammarParser.RBRACE) { return Tuple.create(ctx, 0); } // align to the previous element for (int i = priorSiblings.size() - 2; i >= 0; i--) { ParseTree sibling = priorSiblings.get(i); // stop at the first id rule, index 0 is the TOKENS terminal itself if (i == 1 || ParseTrees.elementStartsLine(sibling)) { return Tuple.create(sibling, 0); } } return Tuple.create(ctx, getCodeStyle().getIndentSize()); } @Override public Tuple2<? extends ParseTree, Integer> visitDelegateGrammar(DelegateGrammarContext ctx) { throw new NotImplementedException(); } @Override public Tuple2<? extends ParseTree, Integer> visitDelegateGrammars(DelegateGrammarsContext ctx) { throw new NotImplementedException(); } @Override @RuleDependencies({ @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_modeSpec, version=3, dependents=Dependents.PARENTS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleSpec, version=3, dependents=Dependents.PARENTS), }) public Tuple2<? extends ParseTree, Integer> visitModeSpec(ModeSpecContext ctx) { // use the preceeding rule (if any), otherwise relative to mode for (int i = priorSiblings.size() - 2; i >= 0; i--) { ParseTree sibling = priorSiblings.get(i); if (!(sibling instanceof RuleNode)) { continue; } RuleContext context = ((RuleNode)sibling).getRuleContext(); if (context.getRuleIndex() == GrammarParser.RULE_ruleSpec) { return Tuple.create(context, 0); } } return Tuple.create(ctx, getCodeStyle().getIndentSize()); } } @MimeRegistration(mimeType=GrammarEditorKit.GRAMMAR_MIME_TYPE, service=IndentTask.Factory.class) public static final class FactoryImpl implements Factory { @Override public IndentTask createTask(Context context) { return new GrammarIndentTask(context); } } }