package com.redhat.ceylon.eclipse.code.correct;
import static com.redhat.ceylon.eclipse.util.EditorUtil.getDocument;
import static java.lang.Character.isWhitespace;
import java.util.Collection;
import java.util.List;
import org.antlr.runtime.CommonToken;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.text.edits.ReplaceEdit;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Expression;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
import com.redhat.ceylon.eclipse.code.editor.CeylonEditor;
import com.redhat.ceylon.eclipse.util.Nodes;
import com.redhat.ceylon.model.typechecker.model.Parameter;
import com.redhat.ceylon.model.typechecker.model.Unit;
class ConvertToNamedArgumentsProposal extends CorrectionProposal {
public ConvertToNamedArgumentsProposal(int offset, Change change) {
super("Convert to named arguments", change, new Region(offset, 0));
}
public static void addConvertToNamedArgumentsProposal(
Collection<ICompletionProposal> proposals,
IFile file, Tree.CompilationUnit cu,
CeylonEditor editor, int currentOffset) {
Tree.PositionalArgumentList pal =
findPositionalArgumentList(currentOffset, cu);
if (canConvert(pal)) {
final TextChange tc =
new TextFileChange("Convert to Named Arguments", file);
Integer start = pal.getStartIndex();
int length = pal.getDistance();
StringBuilder result = new StringBuilder();
try {
if (!isWhitespace(getDocument(tc).getChar(start-1))) {
result.append(" ");
}
}
catch (BadLocationException e1) {
e1.printStackTrace();
}
result.append("{ ");
boolean sequencedArgs = false;
List<CommonToken> tokens =
editor.getParseController().getTokens();
final List<Tree.PositionalArgument> args =
pal.getPositionalArguments();
int i=0;
for (Tree.PositionalArgument arg: args) {
Parameter param = arg.getParameter();
if (param==null) {
return;
}
if (param.isSequenced()) {
if (sequencedArgs) {
result.append(", ");
}
else {
//TODO: if we _only_ have a single spread
// argument we don't need to wrap it
// in a sequence, we only need to
// get rid of the * operator
result.append(param.getName())
.append(" = [");
sequencedArgs=true;
}
result.append(Nodes.text(arg, tokens));
}
else {
if (sequencedArgs) {
return;
}
if (arg instanceof Tree.ListedArgument) {
final Expression e =
((Tree.ListedArgument) arg).getExpression();
if (e!=null) {
Tree.Term term = e.getTerm();
if (term instanceof Tree.FunctionArgument) {
Tree.FunctionArgument fa =
(Tree.FunctionArgument) term;
if (fa.getType() instanceof Tree.VoidModifier) {
result.append("void ");
}
else {
result.append("function ");
}
result.append(param.getName());
Unit unit = cu.getUnit();
Nodes.appendParameters(result, fa, unit, tokens);
if (fa.getBlock()!=null) {
result.append(" ")
.append(Nodes.text(fa.getBlock(), tokens))
.append(" ");
}
else {
result.append(" => ");
}
if (fa.getExpression()!=null) {
result.append(Nodes.text(fa.getExpression(), tokens))
.append("; ");
}
continue;
}
if (++i==args.size() &&
term instanceof Tree.SequenceEnumeration) {
Tree.SequenceEnumeration se =
(Tree.SequenceEnumeration) term;
Tree.SequencedArgument sa = se.getSequencedArgument();
if (sa!=null) {
result.append(Nodes.text(sa, tokens))
.append(" ");
}
continue;
}
}
}
result.append(param.getName())
.append(" = ")
.append(Nodes.text(arg, tokens))
.append("; ");
}
}
if (sequencedArgs) {
result.append("]; ");
}
result.append("}");
tc.setEdit(new ReplaceEdit(start, length, result.toString()));
int offset = start+result.toString().length();
proposals.add(new ConvertToNamedArgumentsProposal(offset, tc));
}
}
public static boolean canConvert(Tree.PositionalArgumentList pal) {
if (pal==null) {
return false;
}
else {
//if it is an indirect invocations, or an
//invocation of an overloaded Java method
//or constructor, we can't call it using
//named arguments!
for (Tree.PositionalArgument arg: pal.getPositionalArguments()) {
Parameter param = arg.getParameter();
if (param==null) return false;
}
return true;
}
}
private static Tree.PositionalArgumentList findPositionalArgumentList(
int currentOffset, Tree.CompilationUnit cu) {
FindPositionalArgumentsVisitor fpav =
new FindPositionalArgumentsVisitor(currentOffset);
fpav.visit(cu);
return fpav.getArgumentList();
}
private static class FindPositionalArgumentsVisitor
extends Visitor {
Tree.PositionalArgumentList argumentList;
int offset;
private Tree.PositionalArgumentList getArgumentList() {
return argumentList;
}
private FindPositionalArgumentsVisitor(int offset) {
this.offset = offset;
}
@Override
public void visit(Tree.ExtendedType that) {
//don't add proposals for extends clause
}
@Override
public void visit(Tree.PositionalArgumentList that) {
Integer start = that.getStartIndex();
Integer stop = that.getEndIndex();
if (start!=null && offset>=start &&
stop!=null && offset<=stop) {
argumentList = that;
}
super.visit(that);
}
}
}