package com.redhat.ceylon.eclipse.code.correct; import static com.redhat.ceylon.eclipse.code.correct.ImportProposals.importProposals; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.utilJ2C; import static com.redhat.ceylon.eclipse.util.Nodes.getReferencedNodeInUnit; import java.util.Collection; import java.util.HashSet; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.ltk.core.refactoring.TextChange; import org.eclipse.ltk.core.refactoring.TextFileChange; import org.eclipse.text.edits.DeleteEdit; import org.eclipse.text.edits.InsertEdit; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.ReplaceEdit; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.Type; import com.redhat.ceylon.model.typechecker.model.TypedDeclaration; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.compiler.typechecker.tree.Tree.ParameterList; class SplitDeclarationProposal extends CorrectionProposal { private SplitDeclarationProposal(Declaration dec, int offset, TextChange change) { super("Split declaration of '" + dec.getName() + "'", change, new Region(offset, 0)); } private static void addSplitDeclarationProposal( IDocument doc, Tree.TypedDeclaration decNode, Tree.CompilationUnit rootNode, IFile file, Collection<ICompletionProposal> proposals) { TypedDeclaration dec = decNode.getDeclarationModel(); if (dec==null) return; if (dec.isToplevel()) return; Tree.Identifier id = decNode.getIdentifier(); if (id==null || id.getToken()==null) return; int idStartOffset = id.getStartIndex(); int idEndOffset = id.getEndIndex(); int startOffset = decNode.getStartIndex(); int paramsEndOffset = idEndOffset; String paramsString = ""; String typeString; Tree.Type type = decNode.getType(); if (type==null || type.getToken()==null) return; try { int typeStartOffset = type.getStartIndex(); int typeEndOffset = type.getEndIndex(); typeString = doc.get(typeStartOffset, typeEndOffset-typeStartOffset); if (decNode instanceof Tree.MethodDeclaration) { Tree.MethodDeclaration md = (Tree.MethodDeclaration) decNode; List<ParameterList> pls = md.getParameterLists(); if (pls.isEmpty()) { return; } else { int paramsOffset = pls.get(0) .getStartIndex(); paramsEndOffset = pls.get(pls.size()-1) .getEndIndex(); paramsString = doc.get(paramsOffset, paramsEndOffset - paramsOffset); } } } catch (BadLocationException e) { e.printStackTrace(); return; } TextChange change = new TextFileChange("Split Declaration", file); change.setEdit(new MultiTextEdit()); String delim = utilJ2C().indents().getDefaultLineDelimiter(doc); String indent = utilJ2C().indents().getIndent(decNode, doc); if (dec.isParameter()) { //TODO: does not handle default args correctly // for callable parameters change.addEdit(new DeleteEdit(startOffset, idStartOffset-startOffset)); change.addEdit(new DeleteEdit(idEndOffset, paramsEndOffset-idEndOffset)); Declaration container = (Declaration) dec.getContainer(); Node containerNode = getReferencedNodeInUnit(container, rootNode); Tree.Body body; if (containerNode instanceof Tree.ClassDefinition) { Tree.ClassDefinition cd = (Tree.ClassDefinition) containerNode; body = cd.getClassBody(); } else if (containerNode instanceof Tree.MethodDefinition) { Tree.MethodDefinition md = (Tree.MethodDefinition) containerNode; body = md.getBlock(); } else if (containerNode instanceof Tree.FunctionArgument) { Tree.FunctionArgument fa = (Tree.FunctionArgument) containerNode; body = fa.getBlock(); } else if (containerNode instanceof Tree.Constructor) { Tree.Constructor cd = (Tree.Constructor) containerNode; body = cd.getBlock(); } else { return; } if (body==null || body.getStatements() .contains(decNode)) { return; } Tree.AnnotationList al = decNode.getAnnotationList(); String annotations; if (al==null || al.getToken()==null) { annotations = ""; } else { try { int alstart = al.getStartIndex(); int allen = al.getDistance(); if (allen==0) { annotations = ""; } else { annotations = doc.get(alstart, allen) + " "; } } catch (BadLocationException e) { annotations = ""; } } String text = delim + indent + utilJ2C().indents().getDefaultIndent() + annotations + typeString + " " + dec.getName() + paramsString + ";"; int bstart = body.getStartIndex(); int bstop = body.getEndIndex(); if (bstop-1==bstart+1) { text += delim + indent; } change.addEdit(new InsertEdit(bstart+1, text)); } else { String text = paramsString +";" + delim + indent + dec.getName(); change.addEdit(new InsertEdit(idEndOffset, text)); } int il; if (type instanceof Tree.LocalModifier) { Type infType = type.getTypeModel(); String explicitType; if (infType==null) { explicitType = "Object"; il=0; } else { explicitType = infType.asSourceCodeString( decNode.getUnit()); HashSet<Declaration> decs = new HashSet<Declaration>(); importProposals().importType(decs, infType, rootNode); il= (int) importProposals().applyImports(change, decs, rootNode, doc); } int typeOffset = type.getStartIndex(); int typeLen = type.getDistance(); change.addEdit(new ReplaceEdit(typeOffset, typeLen, explicitType)); } else { il=0; } proposals.add(new SplitDeclarationProposal(dec, idEndOffset+il, change)); } static void addSplitDeclarationProposals( Collection<ICompletionProposal> proposals, IDocument doc, IFile file, Tree.CompilationUnit cu, Tree.Declaration decNode, Tree.Statement statement) { if (decNode==null) return; Declaration dec = decNode.getDeclarationModel(); if (dec!=null) { if (decNode instanceof Tree.AttributeDeclaration) { Tree.AttributeDeclaration attDecNode = (Tree.AttributeDeclaration) decNode; Tree.SpecifierOrInitializerExpression sie = attDecNode.getSpecifierOrInitializerExpression(); if (sie!=null || dec.isParameter()) { addSplitDeclarationProposal(doc, attDecNode, cu, file, proposals); } } if (decNode instanceof Tree.MethodDeclaration) { Tree.MethodDeclaration methDecNode = (Tree.MethodDeclaration) decNode; Tree.SpecifierExpression sie = methDecNode.getSpecifierExpression(); if (sie==null ? dec.isParameter() : !dec.isParameter()) { addSplitDeclarationProposal(doc, methDecNode, cu, file, proposals); } } if (decNode instanceof Tree.Variable && statement instanceof Tree.ControlStatement) { Tree.Variable varNode = (Tree.Variable) decNode; Tree.SpecifierExpression sie = varNode.getSpecifierExpression(); if (sie!=null) { addSplitDeclarationProposal(doc, varNode, (Tree.ControlStatement) statement, cu, file, proposals); } } } } private static void addSplitDeclarationProposal(IDocument doc, Tree.Variable varNode, Tree.ControlStatement statement, Tree.CompilationUnit cu, IFile file, Collection<ICompletionProposal> proposals) { Tree.SpecifierExpression sie = varNode.getSpecifierExpression(); Tree.Identifier id = varNode.getIdentifier(); if (sie!=null && id!=null && !(varNode.getType() instanceof Tree.SyntheticVariable)) { TextFileChange tfc = new TextFileChange("Split Variable", file); tfc.setEdit(new MultiTextEdit()); int vstart = varNode.getStartIndex(); int vlen = varNode.getDistance(); String text; try { text = "value " + doc.get(vstart, vlen) + ";" + utilJ2C().indents().getDefaultLineDelimiter(doc) + utilJ2C().indents().getIndent(statement, doc); } catch (BadLocationException e) { e.printStackTrace(); return; } int start = statement.getStartIndex(); tfc.addEdit(new InsertEdit(start, text)); int estart = id.getEndIndex(); int eend = sie.getEndIndex(); tfc.addEdit(new DeleteEdit(estart, eend-estart)); proposals.add(new SplitDeclarationProposal( varNode.getDeclarationModel(), start+6, tfc)); } } }