@Override protected void createContextsForLastCompleteNode(EObject previousModel, boolean strict) { String currentNodePrefix = getPrefix(currentNode); if (!Strings.isEmpty(currentNodePrefix) && !currentNode.getText().equals(currentNodePrefix)) { lexer.setCharStream(new ANTLRStringStream(currentNodePrefix)); Token token = lexer.nextToken(); if (token == Token.EOF_TOKEN) { // error case - nothing could be parsed return; } while (token != Token.EOF_TOKEN) { if (isErrorToken(token)) return; token = lexer.nextToken(); } } String prefix = ""; Collection<FollowElement> followElements = getParser().getFollowElements(rootNode, 0, completionOffset, strict); doCreateContexts(lastCompleteNode, currentNode, prefix, previousModel, followElements); }
@Override protected void handleLastCompleteNodeIsAtEndOfDatatypeNode() { String prefix = getPrefix(lastCompleteNode); INode previousNode = getLastCompleteNodeByOffset(rootNode, lastCompleteNode.getOffset()); EObject previousModel = previousNode.getSemanticElement(); INode currentDatatypeNode = getContainingDatatypeRuleNode(currentNode); Collection<FollowElement> followElements = getParser().getFollowElements(rootNode, 0, lastCompleteNode.getOffset(), true); int prevSize = contextBuilders.size(); doCreateContexts(previousNode, currentDatatypeNode, prefix, previousModel, followElements); if (lastCompleteNode instanceof ILeafNode && lastCompleteNode.getGrammarElement() == null && contextBuilders.size() != prevSize) { handleLastCompleteNodeHasNoGrammarElement(contextBuilders.subList(prevSize, contextBuilders.size()), previousModel); } }
/** * First pass. Use the tokens that have been computed from the production parser's result and collect the follow * elements from those. */ private CustomInternalN4JSParser collectFollowElements(TokenSource tokens, boolean strict, Set<FollowElement> result) { CustomInternalN4JSParser parser = createParser(); parser.setStrict(strict); try { ObservableXtextTokenStream tokenStream = new ObservableXtextTokenStream(tokens, parser); result.addAll(doGetFollowElements(parser, tokenStream)); } catch (InfiniteRecursion infinite) { // this guards against erroneous infinite recovery loops in Antlr. // Grammar dependent and not expected something that is expected for N4JS // is used in the base class thus also used here. result.addAll(parser.getFollowElements()); } return parser; }
@Override protected Collection<FollowElement> getFollowElements(AbstractInternalContentAssistParser parser) { if (rule == null || rule.eIsProxy()) return Collections.emptyList(); String methodName = "entryRule" + rule.getName(); PolymorphicDispatcher<Collection<FollowElement>> dispatcher = new PolymorphicDispatcher<Collection<FollowElement>>(methodName, 0, 0, Collections.singletonList(parser)); dispatcher.invoke(); return parser.getFollowElements(); }
@Override protected Collection<FollowElement> getFollowElements(AbstractInternalContentAssistParser parser) { try { InternalN4JSParser typedParser = (InternalN4JSParser) parser; typedParser.entryRuleScript(); return typedParser.getFollowElements(); } catch(RecognitionException ex) { throw new RuntimeException(ex); } }
@Override public void computeFollowElements(Collection<FollowElement> followElements, final Collection<AbstractElement> result) { computeFollowElements(followElements, new IFollowElementAcceptor() { @Override public void accept(AbstractElement element) { result.add(element); } }); }
@Override protected void handleLastCompleteNodeAsPartOfDatatypeNode() { String prefix = getPrefix(datatypeNode); Collection<FollowElement> followElements = getParser().getFollowElements(rootNode, 0, datatypeNode.getOffset(), true); INode lastCompleteNodeBeforeDatatype = getLastCompleteNodeByOffset(rootNode, datatypeNode.getTotalOffset()); doCreateContexts(lastCompleteNodeBeforeDatatype, datatypeNode, prefix, currentModel, followElements); }
/** * @param node * the root node of the model to parse * @param startOffset * the start offset to consider * @param endOffset * the exclusive end offset * @param strict * if true the parser will not use error recovery on the very last token of the input. * @return a collection of follow elements. */ public Collection<FollowElement> getFollowElements(INode node, int startOffset, int endOffset, boolean strict) { Set<FollowElement> result = Sets.newLinkedHashSet(); TokenSource tokenSource = tokenSourceFactory.toTokenSource(node, startOffset, endOffset); CustomInternalN4JSParser parser = collectFollowElements(tokenSource, strict, result); adjustASIAndCollectFollowElements(parser, strict, result); /* * Lists are easier to debug */ return Lists.newArrayList(result); }
/** * The second pass over the given input. If the input ends with an ASI, the semicolon is removed and it is parsed * again since the production may have skipped the ASI if more input was present. Same applies for the opposite * direction, e.g if it does not end with an ASI but the prev token suggests that there may have been a semicolon, * it is inserted. */ private void adjustASIAndCollectFollowElements(CustomInternalN4JSParser previousParser, boolean strict, Set<FollowElement> result) { ObservableXtextTokenStream tokens = (ObservableXtextTokenStream) previousParser.getTokenStream(); int lastTokenIndex = tokens.size() - 1; if (lastTokenIndex >= 0) { CommonToken lastToken = (CommonToken) tokens.LT(-1); @SuppressWarnings("unchecked") List<Token> tokenList = tokens.getTokens(); if (lastToken == null) { return; // TODO ask Sebastian why and how this can happen... it happens! } if (shouldSkipASI(lastToken)) { // Some statements may not span multiple lines, e.g between the return keyword // and the returned expression, there may not be an ASI. Filter these locations. if (maySkipASI(lastToken, tokens)) { tokenList.remove(lastTokenIndex); result.addAll(resetAndGetFollowElements(tokens, strict)); // If a postfix operator sneaked into the result, remove it since // we'd have produce an ASI before that removePostfixOperator(result); } } else if (shouldAddSyntheticSemicolon(previousParser, lastTokenIndex, lastToken)) { CommonToken token = new CommonToken(semi); tokenList.add(token); result.addAll(resetAndGetFollowElements(tokens, strict)); // Same here, if we had added an ASI, the postfix operator would be rendered // invalid, remove it. removePostfixOperator(result); } } }
/** * Filter all follow elements that point to a postfix operator. */ private void removePostfixOperator(Set<FollowElement> result) { Iterator<FollowElement> iter = result.iterator(); while (iter.hasNext()) { FollowElement fe = iter.next(); if (postfixGroup == fe.getGrammarElement()) { iter.remove(); } } }
/** * Create a fresh parser instance and process the tokens for a second pass. */ private Collection<FollowElement> resetAndGetFollowElements(ObservableXtextTokenStream tokens, boolean strict) { CustomInternalN4JSParser parser = createParser(); parser.setStrict(strict); tokens.reset(); return doGetFollowElements(parser, tokens); }
/** * Initialize the parser properly with the given tokens and process it. */ private Collection<FollowElement> doGetFollowElements(AbstractInternalContentAssistParser parser, ObservableXtextTokenStream tokens) { tokens.setInitialHiddenTokens(getInitialHiddenTokens()); parser.setTokenStream(tokens); IUnorderedGroupHelper helper = getUnorderedGroupHelper().get(); parser.setUnorderedGroupHelper(helper); helper.initializeWith(parser); tokens.setListener(parser); Collection<FollowElement> followElements = getFollowElements(parser); return followElements; }
@Override protected Collection<FollowElement> getFollowElements(AbstractInternalContentAssistParser parser) { try { InternalMyDslParser typedParser = (InternalMyDslParser) parser; typedParser.entryRuleMensamodel(); return typedParser.getFollowElements(); } catch(RecognitionException ex) { throw new RuntimeException(ex); } }
@Override protected Collection<FollowElement> getFollowElements(AbstractInternalContentAssistParser parser) { try { InternalMinitlParser typedParser = (InternalMinitlParser) parser; typedParser.entryRuleTransformation(); return typedParser.getFollowElements(); } catch(RecognitionException ex) { throw new RuntimeException(ex); } }
private void assertFollowers(String input, Set<AbstractElement> expected) throws RecognitionException { Collection<FollowElement> followSet = getFollowSet(input); // Collection<FollowElement> followList = com.google.common.collect.Lists.newArrayList(getFollowSet(input)); assertEquals(expected.size(), followSet.size()); Set<AbstractElement> grammarElements = computeSearchElements(followSet); // Collection<AbstractElement> followElementList = com.google.common.collect.Lists.newArrayList(grammarElements); assertEquals(expected, grammarElements); }
private Set<AbstractElement> computeSearchElements(Collection<FollowElement> followSet) { return Sets.newHashSet( Iterables.transform(followSet, new Function<FollowElement, AbstractElement>(){ @Override public AbstractElement apply(FollowElement from) { return from.getGrammarElement(); } })); }
@Override protected Collection<FollowElement> getFollowElements(AbstractInternalContentAssistParser parser) { try { InternalModelEditorParser typedParser = (InternalModelEditorParser) parser; typedParser.entryRuleModel(); return typedParser.getFollowElements(); } catch(RecognitionException ex) { throw new RuntimeException(ex); } }