package com.chrisfolger.needsmoredojo.core.refactoring; import com.chrisfolger.needsmoredojo.core.amd.CompletionCallback; import com.chrisfolger.needsmoredojo.core.amd.objectmodel.DeclareResolver; import com.chrisfolger.needsmoredojo.core.amd.objectmodel.DeclareStatementItems; import com.chrisfolger.needsmoredojo.core.util.JSUtil; import com.intellij.lang.javascript.psi.*; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; public class UtilToClassConverter implements CompletionCallback { @Override public void run(Object[] result) { final DeclareStatementItems item = new DeclareResolver().getDeclareStatementFromParsedStatement(result); if(item == null) { Notifications.Bus.notify(new Notification("needsmoredojo", "Convert util module to class module", "Valid declare block was not found", NotificationType.WARNING)); return; } final List<JSExpressionStatement> methods = (List<JSExpressionStatement>) result[2]; final JSVarStatement declarationVariable = (JSVarStatement) result[3]; CommandProcessor.getInstance().executeCommand(item.getDeclareContainingStatement().getProject(), new Runnable() { @Override public void run() { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { PsiElement parent = item.getDeclareContainingStatement().getParent(); doRefactor(item.getClassName(), item.getExpressionsToMixin(), methods, item.getDeclareContainingStatement(), declarationVariable); // all of those deletions creates a ton of whitespace for some reason. So, delete it! cleanupWhiteSpace(parent); } }); } }, "Convert util module to class module", "Convert util module to class module"); } public void cleanupWhiteSpace(PsiElement parent) { String result = "{\n" + parent.getText().substring(1).trim(); parent.replace(JSUtil.createStatement(parent, result)); } public @NotNull String buildUtilPatternString(@Nullable JSLiteralExpression className, @NotNull JSExpression[] mixins, @NotNull List<JSExpressionStatement> methods, @NotNull String utilVariableName) { // build an array of mixins for the new declare statement StringBuilder mixinArray = new StringBuilder(); for (JSExpression mixin : mixins) { if (mixinArray.toString().equals("")) { mixinArray.append(mixin.getText()); } else { mixinArray.append(", " + mixin.getText()); } } // convert each method to a property in the new object literal StringBuilder properties = new StringBuilder(); for(int i=0;i<methods.size();i++) { JSExpressionStatement method = methods.get(i); JSAssignmentExpression expression = (JSAssignmentExpression) method.getExpression(); JSExpression reference = ((JSDefinitionExpression) expression.getChildren()[0]).getExpression(); String definition = ""; /* a util property can be two things: util.a = b; util['a'] = b; */ if(reference instanceof JSReferenceExpression) { definition = ((JSReferenceExpression)reference).getReferencedName(); } else if (reference instanceof JSIndexedPropertyAccessExpression) { definition = ((JSIndexedPropertyAccessExpression)reference).getIndexExpression().getText(); } String content = expression.getChildren()[1].getText().replaceAll(utilVariableName, "this"); if(i < methods.size() - 1) { properties.append(String.format("%s: %s,\n\n", definition, content)); } else { properties.append(String.format("%s: %s", definition, content)); } } String classPrefix = ""; if(className != null) { classPrefix = String.format("%s, ", className.getText()); } String declareStatement = String.format("return declare(%s[%s], {\n%s\n});", classPrefix, mixinArray.toString(), properties.toString()); return declareStatement; } public void doRefactor(JSLiteralExpression className, JSExpression[] mixins, List<JSExpressionStatement> methods, JSElement originalReturnStatement, JSVarStatement declarationVariable) { PsiElement parent = originalReturnStatement.getParent(); JSVariable variable = declarationVariable.getVariables()[0]; String declareStatement = buildUtilPatternString(className, mixins, methods, variable.getName()); PsiElement declareExpression = JSUtil.createStatement(parent, declareStatement); parent.addBefore(declareExpression, originalReturnStatement); // delete all of the old code for(JSExpressionStatement method : methods) { method.delete(); } originalReturnStatement.delete(); declarationVariable.delete(); } public void convertToClassPattern(PsiFile file) { file.acceptChildren(new UtilFinder().getDefineVisitor()); } }