package com.redhat.ceylon.eclipse.code.refactor; import static com.redhat.ceylon.eclipse.util.DocLinks.hasPackage; import static com.redhat.ceylon.eclipse.util.DocLinks.nameRegion; import static com.redhat.ceylon.eclipse.util.Nodes.findImport; import static com.redhat.ceylon.eclipse.util.Nodes.getAbstraction; import static org.eclipse.ltk.core.refactoring.RefactoringStatus.createWarningStatus; import java.util.List; import org.antlr.runtime.CommonToken; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.Region; import org.eclipse.ltk.core.refactoring.CompositeChange; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.TextChange; 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 org.eclipse.ui.IEditorPart; import com.redhat.ceylon.compiler.typechecker.parser.CeylonLexer; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.compiler.typechecker.tree.Tree.Alias; import com.redhat.ceylon.compiler.typechecker.tree.Visitor; import com.redhat.ceylon.model.typechecker.model.Declaration; public class EnterAliasRefactoring extends AbstractRefactoring { private final class EnterAliasVisitor extends Visitor { private final TextChange change; private EnterAliasVisitor(TextChange change) { this.change = change; } @Override public void visit( Tree.StaticMemberOrTypeExpression that) { super.visit(that); addEdit(document, that.getIdentifier(), that.getDeclaration()); } @Override public void visit(Tree.SimpleType that) { super.visit(that); addEdit(document, that.getIdentifier(), that.getDeclarationModel()); } @Override public void visit(Tree.MemberLiteral that) { super.visit(that); addEdit(document, that.getIdentifier(), that.getDeclaration()); } @Override public void visit(Tree.DocLink that) { super.visit(that); Declaration base = that.getBase(); if (!hasPackage(that) && isReference(base)) { Region region = nameRegion(that, 0); change.addEdit(new ReplaceEdit( region.getOffset(), region.getLength(), newName)); } } private void addEdit(IDocument document, Tree.Identifier id, Declaration d) { if (id!=null && isReference(d)) { int pos = id.getStartIndex(); int len = id.getDistance(); change.addEdit(new ReplaceEdit(pos, len, newName)); } } } private String newName; private Tree.ImportMemberOrType element; boolean isReference(Declaration declaration) { return declaration!=null && getElement().getDeclarationModel() .equals(getAbstraction(declaration)); } public Tree.ImportMemberOrType getElement() { return element; } public EnterAliasRefactoring(IEditorPart editor) { super(editor); element = findImport(rootNode, node); if (element!=null) { final Alias alias = element.getAlias(); Tree.Identifier id; if (alias==null) { id = element.getIdentifier(); } else { id = alias.getIdentifier(); } newName = id.getText(); if (id.getDistance() > newName.length()) { switch (id.getToken().getType()) { case CeylonLexer.UIDENTIFIER: newName = "\\I" + newName; break; case CeylonLexer.LIDENTIFIER: newName = "\\i" + newName; break; } } } } @Override public boolean getEnabled() { return element!=null && element.getDeclarationModel()!=null; } public String getName() { return "Enter Alias"; } public boolean forceWizardMode() { Declaration existing = element.getScope() .getMemberOrParameter(element.getUnit(), newName, null, false); return existing!=null; } public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { // Check parameters retrieved from editor context return new RefactoringStatus(); } public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { Declaration existing = element.getScope() .getMemberOrParameter(element.getUnit(), newName, null, false); if (null!=existing) { return createWarningStatus( "An existing declaration named '" + newName + "' already exists in the same scope"); } return new RefactoringStatus(); } public TextChange createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { TextChange tfc = newLocalChange(); refactorInFile(tfc); return tfc; } @Override void refactorInFile(TextChange textChange, CompositeChange compositChange, Tree.CompilationUnit rootNode, List<CommonToken> tokens) { throw new UnsupportedOperationException(); } int refactorInFile(final TextChange change) { change.setEdit(new MultiTextEdit()); Tree.Alias alias = element.getAlias(); Declaration dec = element.getDeclarationModel(); final int adjust; boolean same = newName.equals(dec.getName()); if (alias==null) { // if (!same) { change.addEdit(new InsertEdit( element.getStartIndex(), newName + "=")); adjust = newName.length()+1; // } // else { // adjust = 0; // } } else { Tree.Identifier id = alias.getIdentifier(); int start = id.getStartIndex(); int length = id.getDistance(); if (same) { int stop = element.getIdentifier() .getStartIndex(); change.addEdit(new DeleteEdit(start, stop-start)); adjust = start - stop; } else { change.addEdit(new ReplaceEdit(start, length, newName)); adjust = newName.length()-length; } } new EnterAliasVisitor(change).visit(rootNode); return adjust; } public void setNewName(String text) { newName = text; } public String getNewName() { return newName; } @Override protected boolean isAffectingOtherFiles() { return false; } }