/* * 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.experimental; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Deque; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.atomic.AtomicBoolean; import org.antlr.netbeans.editor.completion.AbstractAnchor; import org.antlr.netbeans.editor.completion.Anchor; import org.antlr.netbeans.editor.text.DocumentSnapshot; import org.antlr.netbeans.editor.text.TrackingPositionRegion; import org.antlr.v4.runtime.Dependents; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.RuleDependency; import org.antlr.v4.runtime.misc.Interval; import org.antlr.works.editor.antlr4.parsing.ParseTrees; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.GrammarTypeContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.RuleSpecContext; import org.antlr.works.editor.grammar.experimental.generated.GrammarParserBaseListener; import org.netbeans.api.annotations.common.NonNull; import org.openide.util.Parameters; /** * * @author Sam Harwell */ public class GrammarParserAnchorListener extends GrammarParserBaseListener { private final Deque<Integer> anchorPositions = new ArrayDeque<>(); private final List<Anchor> anchors = new ArrayList<>(); private final DocumentSnapshot snapshot; private final AtomicBoolean cancel; public GrammarParserAnchorListener(DocumentSnapshot snapshot) { Parameters.notNull("snapshot", snapshot); this.snapshot = snapshot; this.cancel = null; } public GrammarParserAnchorListener(DocumentSnapshot snapshot, AtomicBoolean cancel) { Parameters.notNull("snapshot", snapshot); this.snapshot = snapshot; this.cancel = cancel; } public List<Anchor> getAnchors() { return anchors; } private void checkCancellation() { boolean cancelled; if (cancel != null) { cancelled = cancel.get(); } else { cancelled = Thread.interrupted(); } if (cancelled) { throw new CancellationException(); } } @Override public void enterEveryRule(ParserRuleContext ctx) { checkCancellation(); super.enterEveryRule(ctx); } @Override public void exitEveryRule(ParserRuleContext ctx) { checkCancellation(); super.exitEveryRule(ctx); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_grammarType, version=0, dependents=Dependents.PARENTS) public void enterGrammarType(GrammarTypeContext ctx) { enterAnchor(ctx); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_grammarType, version=0, dependents=Dependents.PARENTS) public void exitGrammarType(GrammarTypeContext ctx) { exitAnchor(ctx, GrammarParser.RULE_grammarType); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleSpec, version=3, dependents=Dependents.PARENTS) public void enterRuleSpec(RuleSpecContext ctx) { enterAnchor(ctx); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleSpec, version=3, dependents=Dependents.PARENTS) public void exitRuleSpec(RuleSpecContext ctx) { exitAnchor(ctx, GrammarParser.RULE_ruleSpec); } private void enterAnchor(ParserRuleContext ctx) { anchorPositions.push(ctx.getStart().getStartIndex()); } private void exitAnchor(ParserRuleContext ctx, int anchorId) { int start = anchorPositions.pop(); Interval sourceInterval = ParseTrees.getSourceInterval(ctx); int stop = sourceInterval.b + 1; TrackingPositionRegion.Bias trackingMode = ctx.getStop() != null ? TrackingPositionRegion.Bias.Exclusive : TrackingPositionRegion.Bias.Forward; if (stop >= start) { anchors.add(createAnchor(ctx, start, stop, trackingMode, anchorId)); } } @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_grammarType, version=0, dependents=Dependents.PARENTS) private Anchor createAnchor(ParserRuleContext ctx, int start, int stop, TrackingPositionRegion.Bias trackingMode, int rule) { TrackingPositionRegion trackingSpan = snapshot.createTrackingRegion(start, stop - start, trackingMode); if (rule == GrammarParser.RULE_grammarType) { return new GrammarTypeAnchor((GrammarParser.GrammarTypeContext)ctx, trackingSpan); } else { return new GrammarAnchor(trackingSpan, rule); } } public static class GrammarAnchor extends AbstractAnchor { private GrammarAnchor(@NonNull TrackingPositionRegion span, int rule) { super(span, rule); } @Override protected List<String> getRuleNames() { return Arrays.asList(GrammarParser.ruleNames); } } public static class GrammarTypeAnchor extends GrammarAnchor { private final int grammarType; @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_grammarType, version=0, dependents=Dependents.SELF) private GrammarTypeAnchor(GrammarTypeContext ctx, TrackingPositionRegion span) { super(span, GrammarParser.RULE_grammarType); if (ctx.LEXER() != null) { grammarType = GrammarParser.LEXER; } else if (ctx.PARSER() != null) { grammarType = GrammarParser.PARSER; } else { grammarType = GrammarParser.COMBINED; } } public int getGrammarType() { return grammarType; } } }