/* * Copyright 2014 * * Licensed under the Eclipse Public License version 1.0, available at * http://opensource.org/licenses/eclipse-1.0.txt */ package de.lynorics.eclipse.jangaroo.ui; import java.util.Collections; import java.util.List; import java.util.Set; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.AbstractRule; import org.eclipse.xtext.Action; import org.eclipse.xtext.CrossReference; import org.eclipse.xtext.GrammarUtil; import org.eclipse.xtext.Keyword; import org.eclipse.xtext.RuleCall; import org.eclipse.xtext.nodemodel.BidiTreeIterator; import org.eclipse.xtext.nodemodel.ICompositeNode; import org.eclipse.xtext.nodemodel.ILeafNode; import org.eclipse.xtext.nodemodel.INode; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.parsetree.reconstr.IHiddenTokenHelper; import org.eclipse.xtext.parsetree.reconstr.impl.NodeIterator; import org.eclipse.xtext.parsetree.reconstr.impl.TokenUtil; import org.eclipse.xtext.serializer.acceptor.ISequenceAcceptor; import org.eclipse.xtext.serializer.acceptor.ISyntacticSequenceAcceptor; import org.eclipse.xtext.serializer.diagnostic.ISerializationDiagnostic.Acceptor; import org.eclipse.xtext.serializer.sequencer.IHiddenTokenSequencer; import org.eclipse.xtext.serializer.sequencer.ISyntacticSequencer; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.inject.Inject; /** * @author Moritz Eysholdt - Initial contribution and API */ public class HiddenTokenSequencer implements IHiddenTokenSequencer, ISyntacticSequenceAcceptor { // protected Set<INode> comments; protected ISequenceAcceptor delegate; @Inject protected IHiddenTokenHelper hiddenTokenHelper; protected INode lastNode; protected INode rootNode; protected ISyntacticSequencer sequencer; @Inject protected TokenUtil tokenUtil; public void acceptAssignedCrossRefDatatype(RuleCall rc, String tkn, EObject val, int index, ICompositeNode node) { emitHiddenTokens(getHiddenNodesBetween(lastNode, node)); lastNode = getLastLeaf(node); delegate.acceptAssignedCrossRefDatatype(rc, tkn, val, index, node); } public void acceptAssignedCrossRefEnum(RuleCall rc, String token, EObject value, int index, ICompositeNode node) { emitHiddenTokens(getHiddenNodesBetween(lastNode, node)); lastNode = getLastLeaf(node); delegate.acceptAssignedCrossRefEnum(rc, token, value, index, node); } public void acceptAssignedCrossRefKeyword(Keyword kw, String token, EObject value, int index, ILeafNode node) { emitHiddenTokens(getHiddenNodesBetween(lastNode, node)); lastNode = node; delegate.acceptAssignedCrossRefKeyword(kw, token, value, index, node); } public void acceptAssignedCrossRefTerminal(RuleCall rc, String token, EObject value, int index, ILeafNode node) { emitHiddenTokens(getHiddenNodesBetween(lastNode, node)); lastNode = node; delegate.acceptAssignedCrossRefTerminal(rc, token, value, index, node); } public void acceptAssignedDatatype(RuleCall rc, String token, Object value, int index, ICompositeNode node) { emitHiddenTokens(getHiddenNodesBetween(lastNode, node)); lastNode = getLastLeaf(node); delegate.acceptAssignedDatatype(rc, token, value, index, node); } public void acceptAssignedEnum(RuleCall enumRC, String token, Object value, int index, ICompositeNode node) { emitHiddenTokens(getHiddenNodesBetween(lastNode, node)); lastNode = getLastLeaf(node); delegate.acceptAssignedEnum(enumRC, token, value, index, node); } public void acceptAssignedKeyword(Keyword keyword, String token, Object value, int index, ILeafNode node) { emitHiddenTokens(getHiddenNodesBetween(lastNode, node)); lastNode = node; delegate.acceptAssignedKeyword(keyword, token, value, index, node); } public void acceptAssignedTerminal(RuleCall terminalRC, String token, Object value, int index, ILeafNode node) { emitHiddenTokens(getHiddenNodesBetween(lastNode, node)); lastNode = node; delegate.acceptAssignedTerminal(terminalRC, token, value, index, node); } public void acceptUnassignedAction(Action action) { delegate.acceptUnassignedAction(action); } public void acceptUnassignedDatatype(RuleCall datatypeRC, String token, ICompositeNode node) { emitHiddenTokens(getHiddenNodesBetween(lastNode, node)); lastNode = getLastLeaf(node); delegate.acceptUnassignedDatatype(datatypeRC, token, node); } public void acceptUnassignedEnum(RuleCall enumRC, String token, ICompositeNode node) { emitHiddenTokens(getHiddenNodesBetween(lastNode, node)); lastNode = getLastLeaf(node); delegate.acceptUnassignedEnum(enumRC, token, node); } public void acceptUnassignedKeyword(Keyword keyword, String token, ILeafNode node) { emitHiddenTokens(getHiddenNodesBetween(lastNode, node)); lastNode = node; delegate.acceptUnassignedKeyword(keyword, token, node); } public void acceptUnassignedTerminal(RuleCall terminalRC, String token, ILeafNode node) { emitHiddenTokens(getHiddenNodesBetween(lastNode, node)); lastNode = node; delegate.acceptUnassignedTerminal(terminalRC, token, node); } protected void emitHiddenTokens(List<INode> hiddens /* Set<INode> comments, */) { if (hiddens == null) return; boolean lastNonWhitespace = true; for (INode node : hiddens) if (tokenUtil.isCommentNode(node)) { if (lastNonWhitespace) delegate.acceptWhitespace(hiddenTokenHelper.getWhitespaceRuleFor(null, ""), "", null); lastNonWhitespace = true; // comments.remove(node); delegate.acceptComment((AbstractRule) node.getGrammarElement(), node.getText(), (ILeafNode) node); } else { delegate.acceptWhitespace((AbstractRule) node.getGrammarElement(), node.getText(), (ILeafNode) node); lastNonWhitespace = false; } if (lastNonWhitespace)// FIXME: determine the whitespace rule correctly delegate.acceptWhitespace(hiddenTokenHelper.getWhitespaceRuleFor(null, ""), "", null); } public boolean enterAssignedAction(Action action, EObject semanticChild, ICompositeNode node) { return delegate.enterAssignedAction(action, semanticChild, node); } public boolean enterAssignedParserRuleCall(RuleCall rc, EObject semanticChild, ICompositeNode node) { return delegate.enterAssignedParserRuleCall(rc, semanticChild, node); } public void enterUnassignedParserRuleCall(RuleCall rc) { delegate.enterUnassignedParserRuleCall(rc); } public void finish() { if (rootNode != null && rootNode == rootNode.getRootNode()) { List<INode> hidden = getRemainingHiddenNodesInContainer(lastNode, rootNode); if (!hidden.isEmpty()) { emitHiddenTokens(hidden); lastNode = rootNode; } } delegate.finish(); } protected Set<INode> getCommentsForEObject(EObject semanticObject, INode node) { if (node == null) return Collections.emptySet(); Set<INode> result = Sets.newHashSet(); BidiTreeIterator<INode> ti = node.getAsTreeIterable().iterator(); while (ti.hasNext()) { INode next = ti.next(); if (next.getSemanticElement() != null && next.getSemanticElement() != semanticObject) { ti.prune(); continue; } if (tokenUtil.isCommentNode(next)) result.add(next); } return result; } protected List<INode> getHiddenNodesBetween(INode from, INode to) { if (from == null || to == null) return null; List<INode> out = Lists.newArrayList(); NodeIterator ni = new NodeIterator(from); while (ni.hasNext()) { INode next = ni.next(); if (tokenUtil.isWhitespaceOrCommentNode(next)) { out.add(next); } else if (next.equals(to)) { if (next instanceof ICompositeNode && (GrammarUtil.isDatatypeRuleCall(next.getGrammarElement()) || GrammarUtil.isEnumRuleCall(next.getGrammarElement()) || next.getGrammarElement() instanceof CrossReference)) while (ni.hasNext()) { INode next2 = ni.next(); if (tokenUtil.isWhitespaceOrCommentNode(next2)) { out.add(next2); } else if (next2 instanceof ILeafNode) return out; } else return out; } else if (tokenUtil.isToken(next)) return null; } return out; } protected INode getLastLeaf(INode node) { while (node instanceof ICompositeNode) node = ((ICompositeNode) node).getLastChild(); return node; } protected List<INode> getRemainingHiddenNodesInContainer(INode from, INode root) { if (from == null || root == null) return Collections.emptyList(); List<INode> out = Lists.newArrayList(); NodeIterator ni = new NodeIterator(from); while (ni.hasNext()) { INode next = ni.next(); if (next.getTotalOffset() > root.getTotalEndOffset()) return out; else if (tokenUtil.isWhitespaceOrCommentNode(next)) { out.add(next); } else if (tokenUtil.isToken(next)) return Collections.emptyList(); } return out; } public void init(EObject context, EObject semanticObject, ISequenceAcceptor sequenceAcceptor, Acceptor errorAcceptor) { this.delegate = sequenceAcceptor; this.lastNode = NodeModelUtils.findActualNodeFor(semanticObject); this.rootNode = lastNode; } public void leaveAssignedAction(Action action, EObject semanticChild) { delegate.leaveAssignedAction(action, semanticChild); } public void leaveAssignedParserRuleCall(RuleCall rc, EObject semanticChild) { delegate.leaveAssignedParserRuleCall(rc, semanticChild); } public void leaveUnssignedParserRuleCall(RuleCall rc) { delegate.leaveUnssignedParserRuleCall(rc); } }