package org.xpect.xtext.lib.util;
import org.eclipse.xtext.IGrammarAccess;
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.impl.NodeIterator;
import org.eclipse.xtext.resource.XtextResource;
import org.xpect.XpectInvocation;
import org.xpect.expectation.impl.TargetSyntaxSupport;
import org.xpect.parameter.IStatementRelatedRegion;
import org.xpect.setup.XpectSetupFactory;
import org.xpect.state.Creates;
import org.xpect.text.IReplacement;
import org.xpect.text.Replacement;
import org.xpect.text.Text;
import org.xpect.xtext.lib.setup.ThisResource;
import org.xpect.xtext.lib.util.GrammarAnalyzer.CommentRule;
import org.xpect.xtext.lib.util.GrammarAnalyzer.MLCommentRule;
import org.xpect.xtext.lib.util.GrammarAnalyzer.SLCommentRule;
@XpectSetupFactory
public class XtextTargetSyntaxSupport extends TargetSyntaxSupport {
public class XtextTargetSyntaxLiteral extends TargetLiteralSupport {
private final CommentRule commentRule;
private final ILeafNode leaf;
public XtextTargetSyntaxLiteral(ILeafNode leaf, CommentRule commentRule) {
super();
this.leaf = leaf;
this.commentRule = commentRule;
}
public IReplacement adoptToTargetSyntax(IReplacement replacement, boolean enforceMultilineLiteral) {
if (leaf.getOffset() > replacement.getOffset())
return replacement;
if (leaf.getOffset() + leaf.getLength() < replacement.getOffset() + replacement.getLength())
return replacement;
if (commentRule instanceof SLCommentRule) {
if (enforceMultilineLiteral)
return convertToMultiLine(leaf, (SLCommentRule) commentRule, replacement);
} else if (commentRule instanceof MLCommentRule) {
MLCommentRule mlCommentRule = (MLCommentRule) commentRule;
if (replacement.getReplacement().contains(mlCommentRule.getEnd()))
return escapeTextInMultiLineComment(mlCommentRule, replacement);
}
return replacement;
}
public String escape(String value) {
if (commentRule instanceof MLCommentRule)
return escapeTextInMultiLineComment((MLCommentRule) commentRule, value);
return value;
}
public boolean isMultiline() {
return commentRule instanceof MLCommentRule;
}
}
private final GrammarAnalyzer grammarAnalyzer;
private final XtextResource resource;
public XtextTargetSyntaxSupport(@ThisResource XtextResource resource) {
this.resource = resource;
this.grammarAnalyzer = new GrammarAnalyzer(resource.getResourceServiceProvider().get(IGrammarAccess.class).getGrammar());
}
protected IReplacement convertToMultiLine(ILeafNode leaf, SLCommentRule slRule, IReplacement replacement) {
MLCommentRule mlRule = getFirstMLCommentRule();
if (mlRule != null) {
String text = leaf.getText();
int end = text.length();
while (text.charAt(end - 1) == '\n' || text.charAt(end - 1) == '\r')
end--;
int offsetInLeaf = replacement.getOffset() - leaf.getOffset();
StringBuilder newText = new StringBuilder();
newText.append(mlRule.getStart());
newText.append(text.substring(slRule.getStart().length(), offsetInLeaf));
newText.append(replacement.getReplacement());
int beginIndex = offsetInLeaf + replacement.getLength();
if (beginIndex < end)
newText.append(text.substring(beginIndex, end));
newText.append(" ");
newText.append(mlRule.getEnd());
return new Replacement(replacement.getDocument(), leaf.getOffset(), end, newText.toString());
} else {
String document = leaf.getRootNode().getText();
StringBuilder postIndentaiton = new StringBuilder();
String text = leaf.getText();
int i = slRule.getStart().length();
while (i < text.length() && Character.isWhitespace(text.charAt(i)))
postIndentaiton.append(text.charAt(i++));
String indentation = new Text(document).findIndentation(leaf.getOffset());
String oldIndentation = "\n" + indentation;
String newIndentation = "\n" + indentation + slRule.getStart() + postIndentaiton;
String newText = replacement.getReplacement().replace(oldIndentation, newIndentation);
return new Replacement(replacement.getDocument(), replacement.getOffset(), replacement.getLength(), newText);
}
}
@Creates
public TargetSyntaxSupport create() {
return this;
}
protected IReplacement escapeTextInMultiLineComment(MLCommentRule commentRule, IReplacement replacement) {
String text = escapeTextInMultiLineComment(commentRule, replacement.getReplacement());
return new Replacement(replacement.getDocument(), replacement.getOffset(), replacement.getLength(), text);
}
protected String escapeTextInMultiLineComment(MLCommentRule commentRule, String replacement) {
String escapedStart = getEscapeString(commentRule.getStart());
String escapedEnd = getEscapeString(commentRule.getEnd());
return replacement.replace(commentRule.getStart(), escapedStart).replace(commentRule.getEnd(), escapedEnd);
}
protected CommentRule findCommentRule(ILeafNode leaf) {
String text = leaf.getText();
for (CommentRule rule : grammarAnalyzer.getCommentRules())
if (text.startsWith(rule.getStart()))
return rule;
return null;
}
protected String getEscapeString(String startOrEnd) {
if (startOrEnd.length() == 1)
return "_";
if (startOrEnd.length() > 1)
return startOrEnd.charAt(0) + "\\" + startOrEnd.substring(1);
return startOrEnd;
}
protected MLCommentRule getFirstMLCommentRule() {
for (CommentRule rule : grammarAnalyzer.getCommentRules())
if (rule instanceof MLCommentRule)
return (MLCommentRule) rule;
return null;
}
public TargetLiteralSupport getLiteralSupport(int offset) {
ILeafNode leaf = NodeModelUtils.findLeafNodeAtOffset(resource.getParseResult().getRootNode(), offset);
CommentRule commentRule = findCommentRule(leaf);
return new XtextTargetSyntaxLiteral(leaf, commentRule);
}
public XtextResource getResource() {
return resource;
}
public boolean supportsMultiLineLiteral() {
return getFirstMLCommentRule() != null;
}
@Override
public int findFirstSemanticCharAfterStatement(XpectInvocation statement) {
IStatementRelatedRegion region = statement.getExtendedRegion();
int end = region.getOffset() + region.getLength();
ILeafNode node = NodeModelUtils.findLeafNodeAtOffset(resource.getParseResult().getRootNode(), end);
if (!node.isHidden())
return node.getOffset();
NodeIterator it = new NodeIterator(node);
while (it.hasNext()) {
INode next = it.next();
if (next instanceof ILeafNode && !((ILeafNode) next).isHidden())
return next.getOffset();
}
throw new RuntimeException("Reached end of file while looking for semantic element for OFFSET");
}
}