package com.redhat.ceylon.eclipse.code.correct; import static com.redhat.ceylon.eclipse.ui.CeylonResources.ATTRIBUTE; import static com.redhat.ceylon.eclipse.ui.CeylonResources.INTERFACE; import static com.redhat.ceylon.eclipse.util.EditorUtil.getDocument; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.utilJ2C; import static com.redhat.ceylon.eclipse.util.Nodes.findDeclaration; import java.util.Collection; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; 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.TextFileChange; import org.eclipse.swt.graphics.Image; import org.eclipse.text.edits.InsertEdit; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.eclipse.ui.CeylonResources; import com.redhat.ceylon.eclipse.util.Nodes; import com.redhat.ceylon.model.typechecker.model.Unit; import com.redhat.ceylon.ide.common.model.ModifiableSourceFile; import com.redhat.ceylon.ide.common.typechecker.ModifiablePhasedUnit; class CreateEnumProposal extends CorrectionProposal { CreateEnumProposal(String def, String desc, Image image, int offset, TextFileChange change) { super(desc, change, new Region(offset, 0), image); } static void addCreateEnumProposal( Tree.CompilationUnit rootNode, Node node, ProblemLocation problem, Collection<ICompletionProposal> proposals, IProject project) { Node idn = Nodes.getIdentifyingNode(node); if (idn==null) return; String brokenName = idn.getText(); if (brokenName.isEmpty()) return; Tree.Declaration dec = findDeclaration(rootNode, node); if (dec instanceof Tree.ClassDefinition) { Tree.ClassDefinition cd = (Tree.ClassDefinition) dec; Tree.CaseTypes caseTypes = cd.getCaseTypes(); if (caseTypes!=null) { Tree.TypeParameterList tpl = cd.getTypeParameterList(); if (caseTypes.getTypes().contains(node)) { addCreateEnumProposal(proposals, project, "class " + brokenName + parameters(tpl) + parameters(cd.getParameterList()) + " extends " + cd.getDeclarationModel().getName() + parameters(tpl) + arguments(cd.getParameterList()) + " {}", "class '"+ brokenName + parameters(tpl) + parameters(cd.getParameterList()) + "'", CeylonResources.CLASS, rootNode, cd); } if (caseTypes.getBaseMemberExpressions().contains(node)) { addCreateEnumProposal(proposals, project, "object " + brokenName + " extends " + cd.getDeclarationModel().getName() + parameters(tpl) + arguments(cd.getParameterList()) + " {}", "object '"+ brokenName + "'", ATTRIBUTE, rootNode, cd); } } } if (dec instanceof Tree.InterfaceDefinition) { Tree.InterfaceDefinition cd = (Tree.InterfaceDefinition) dec; Tree.CaseTypes caseTypes = cd.getCaseTypes(); if (caseTypes!=null) { Tree.TypeParameterList tpl = cd.getTypeParameterList(); if (caseTypes.getTypes().contains(node)) { addCreateEnumProposal(proposals, project, "interface " + brokenName + parameters(tpl) + " satisfies " + cd.getDeclarationModel().getName() + parameters(tpl) + " {}", "interface '"+ brokenName + parameters(tpl) + "'", INTERFACE, rootNode, cd); } if (caseTypes.getBaseMemberExpressions().contains(node)) { addCreateEnumProposal(proposals, project, "object " + brokenName + " satisfies " + cd.getDeclarationModel().getName() + parameters(tpl) + " {}", "object '"+ brokenName + "'", ATTRIBUTE, rootNode, cd); } } } } private static void addCreateEnumProposal( Collection<ICompletionProposal> proposals, String def, String desc, Image image, ModifiablePhasedUnit<IProject,IResource,IFolder,IFile> unit, Tree.Statement statement) { IFile file = unit.getResourceFile(); if (file != null) { TextFileChange change = new TextFileChange("Create Enumerated", file); IDocument doc = getDocument(change); String indent = utilJ2C().indents().getIndent(statement, doc); String s = indent + def + utilJ2C().indents().getDefaultLineDelimiter(doc); int offset = statement.getEndIndex()+1; if (offset>doc.getLength()) { offset = doc.getLength(); s = utilJ2C().indents().getDefaultLineDelimiter(doc) + s; } change.setEdit(new InsertEdit(offset, s)); proposals.add(new CreateEnumProposal(def, "Create enumerated " + desc, image, offset + def.indexOf("{}")+1, change)); } } private static void addCreateEnumProposal( Collection<ICompletionProposal> proposals, IProject project, String def, String desc, Image image, Tree.CompilationUnit rootNode, Tree.TypeDeclaration cd) { Unit u = rootNode.getUnit(); if (u instanceof ModifiableSourceFile) { ModifiableSourceFile cu = (ModifiableSourceFile) u; addCreateEnumProposal(proposals, def, desc, image, cu.getPhasedUnit(), cd); } } private static String parameters(Tree.ParameterList pl) { StringBuilder result = new StringBuilder(); if (pl==null || pl.getParameters().isEmpty()) { result.append("()"); } else { result.append("("); int len = pl.getParameters().size(), i=0; for (Tree.Parameter p: pl.getParameters()) { if (p!=null) { if (p instanceof Tree.ParameterDeclaration) { Tree.ParameterDeclaration pd = (Tree.ParameterDeclaration) p; Tree.TypedDeclaration td = pd.getTypedDeclaration(); result.append(td.getType().getTypeModel().asString()) .append(" ") .append(td.getIdentifier().getText()); } else if (p instanceof Tree.InitializerParameter) { Tree.InitializerParameter ip = (Tree.InitializerParameter) p; result.append(p.getParameterModel().getType().asString()) .append(" ") .append(ip.getIdentifier().getText()); } //TODO: easy to add back in: /*if (p instanceof Tree.FunctionalParameterDeclaration) { Tree.FunctionalParameterDeclaration fp = (Tree.FunctionalParameterDeclaration) p; for (Tree.ParameterList ipl: fp.getParameterLists()) { parameters(ipl, label); } }*/ } if (++i<len) result.append(", "); } result.append(")"); } return result.toString(); } private static String parameters(Tree.TypeParameterList tpl) { StringBuilder result = new StringBuilder(); if (tpl!=null && !tpl.getTypeParameterDeclarations().isEmpty()) { result.append("<"); int len = tpl.getTypeParameterDeclarations().size(); int i=0; for (Tree.TypeParameterDeclaration p: tpl.getTypeParameterDeclarations()) { result.append(p.getIdentifier().getText()); if (++i<len) result.append(", "); } result.append(">"); } return result.toString(); } private static String arguments(Tree.ParameterList pl) { StringBuilder result = new StringBuilder(); if (pl==null || pl.getParameters().isEmpty()) { result.append("()"); } else { result.append("("); int len = pl.getParameters().size(), i=0; for (Tree.Parameter p: pl.getParameters()) { if (p!=null) { Tree.Identifier id; if (p instanceof Tree.InitializerParameter) { Tree.InitializerParameter ip = (Tree.InitializerParameter) p; id = ip.getIdentifier(); } else if (p instanceof Tree.ParameterDeclaration) { Tree.ParameterDeclaration pd = (Tree.ParameterDeclaration) p; id = pd.getTypedDeclaration().getIdentifier(); } else { continue; } result.append(id.getText()); //TODO: easy to add back in: /*if (p instanceof Tree.FunctionalParameterDeclaration) { Tree.FunctionalParameterDeclaration fp = (Tree.FunctionalParameterDeclaration) p; for (Tree.ParameterList ipl: fp.getParameterLists()) { parameters(ipl, label); } }*/ } if (++i<len) result.append(", "); } result.append(")"); } return result.toString(); } }