/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.intentions; import com.google.common.base.Function; import com.intellij.codeInsight.template.Template; import com.intellij.codeInsight.template.TemplateBuilderImpl; import com.intellij.codeInsight.template.TemplateManager; import com.intellij.codeInsight.template.impl.TextExpression; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.SuggestedNameInfo; import com.intellij.psi.util.PsiMatcherImpl; import com.intellij.util.IncorrectOperationException; import gw.lang.parser.IHasType; import gw.lang.parser.expressions.IArgumentListClause; import gw.lang.reflect.IType; import gw.plugin.ij.lang.psi.IGosuPsiElement; import gw.plugin.ij.lang.psi.api.expressions.IGosuIdentifier; import gw.plugin.ij.lang.psi.api.statements.params.IGosuParameter; import gw.plugin.ij.lang.psi.api.statements.params.IGosuParameterList; import gw.plugin.ij.lang.psi.api.statements.typedef.IGosuMethod; import gw.plugin.ij.lang.psi.impl.expressions.GosuMethodCallExpressionImpl; import gw.plugin.ij.lang.psi.impl.expressions.GosuTypeLiteralImpl; import gw.plugin.ij.lang.psi.util.HasParsedElement; import gw.plugin.ij.lang.psi.util.LeafPsiMatcher; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import static com.google.common.base.Joiner.on; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Lists.transform; import static com.intellij.psi.codeStyle.VariableKind.PARAMETER; import static com.intellij.psi.util.ClassUtil.extractClassName; import static com.intellij.psi.util.PsiMatchers.hasClass; import static gw.plugin.ij.completion.GosuClassNameInsertHandler.addImportForItem; import static gw.plugin.ij.lang.psi.util.GosuPsiParseUtil.parseProgramm; import static gw.plugin.ij.util.ClassLord.purgeClassName; import static java.beans.Introspector.decapitalize; public class CreateMethodFix extends BaseIntentionAction { private final GosuMethodCallExpressionImpl callExpr; private final PsiClass targetClass; public CreateMethodFix(GosuMethodCallExpressionImpl callExpr, PsiClass targetClass) { checkArgument(targetClass instanceof IGosuPsiElement); this.callExpr = callExpr; this.targetClass = targetClass; } private static final Function<String, String> FQN_TO_IMPORT = new Function<String, String>() { public String apply(String fqn) { return "uses " + fqn; } }; private static final Function<String, String> TO_ARG_DEF = new Function<String, String>() { public String apply(String typeName) { return "_" + decapitalize(extractClassName(purgeClassName(typeName))) + " : " + typeName; } }; private int bestPlaceToInsert() { // PsiClass exprClass = (PsiClass) new PsiMatcherImpl(callExpr).ancestor(hasClass(PsiClass.class)).getElement(); // if (targetClass.equals(exprClass)) { // PsiElement methodCtx = new PsiMatcherImpl(targetClass).ancestor(hasClass(IGosuMethod.class)).getElement(); // if (methodCtx != null) { // return methodCtx.getTextRange().getEndOffset(); // } // } // PsiField[] fields = targetClass.getAllFields(); // PsiMethod[] methods = targetClass.getAllMethods(); // if (methods.length != 0) { // return methods[methods.length - 1].getParent() .getTextRange().getEndOffset(); // } else if (fields.length != 0) { // return fields[fields.length - 1].getTextRange().getEndOffset(); // } else { return targetClass.getLastChild().getPrevSibling().getTextOffset(); // } } private List<IType> findArgTypes() { List<IType> result = new ArrayList<>(); PsiElement args = new PsiMatcherImpl(callExpr).descendant(new HasParsedElement(IArgumentListClause.class)).getElement(); if (args != null) { for (PsiElement child : args.getChildren()) { if (child instanceof IGosuPsiElement) { result.add(((IHasType)((IGosuPsiElement)child).getParsedElement()).getType()); } } } return result; } @Override public void invokeImpl(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { PsiFile targetFile = targetClass.getContainingFile(); Document document = PsiDocumentManager.getInstance(project).getDocument(targetFile); if (document == null) { return; } String name = getMethodName(); List<IType> argTypes = findArgTypes(); List<String> typeNames = new ArrayList<>(argTypes.size()); List<String> imports = new ArrayList<>(); for (IType type : argTypes) { ImportClassHelper importHelper = new ImportClassHelper(type, (IGosuPsiElement) targetClass); importHelper.resolveType(); importHelper.analizeFQNUsing(); if (importHelper.needImport()) { String purgedName = purgeClassName(type.getName()); addImportForItem(targetFile, purgedName, purgeClassName(type.getRelativeName())); importHelper.stickImport(); imports.add(purgedName); } typeNames.add(importHelper.useFQN() ? type.getName() : type.getRelativeName()); } String stubStr = on('\n').join(transform(imports, FQN_TO_IMPORT)) + "\n function " + name + "(" + on(", ").join(transform(typeNames, TO_ARG_DEF)) + "){\n}"; PsiElement stub = parseProgramm(stubStr, file.getManager(), null); PsiElement method = new PsiMatcherImpl(stub).descendant(hasClass(IGosuMethod.class)).getElement(); // targetClass.add(method); TemplateBuilderImpl builder = new TemplateBuilderImpl(method); PsiElement[] children = new PsiMatcherImpl(method).descendant(hasClass(IGosuParameterList.class)).getElement().getChildren(); for (int i = 0; i < children.length; ++i) { PsiElement e = children[i]; if (e instanceof IGosuParameter) { PsiElement id = new LeafPsiMatcher(e).descendant(hasClass(IGosuIdentifier.class)).getElement(); PsiElement typeLiteral = new LeafPsiMatcher(e).descendant(hasClass(GosuTypeLiteralImpl.class)).getElement(); if (id != null && typeLiteral != null) { JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project); SuggestedNameInfo suggestedInfo = styleManager.suggestVariableName(PARAMETER, null, null, ((GosuTypeLiteralImpl)typeLiteral).getType()); String[] names = suggestedInfo.names; String pName; if (names.length == 0) { pName = "p" + (i + 1); } else { pName = names[0]; } builder.replaceElement(id, new TextExpression(pName)); builder.replaceElement(typeLiteral, new TextExpression(typeLiteral.getText())); } } } Editor newEditor; if (targetFile.equals(file)) { newEditor = editor; } else { OpenFileDescriptor descriptor = new OpenFileDescriptor(project, targetFile.getVirtualFile()); newEditor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true); // RangeMarker rangeMarker = document.createRangeMarker(method.getTextRange()); // newEditor.getCaretModel().moveToOffset(rangeMarker.getStartOffset()); // newEditor.getDocument().deleteString(rangeMarker.getStartOffset(), rangeMarker.getEndOffset()); } Template template = builder.buildTemplate(); template.setToReformat(true); template.setToIndent(true); int offset = targetClass.getLastChild().getPrevSibling().getTextOffset(); newEditor.getDocument().insertString(offset, "\n"); editor.getCaretModel().moveToOffset(offset + 1); TemplateManager.getInstance(project).startTemplate(newEditor, template); // targetClass.add(method); // CodeStyleManager csManager = CodeStyleManager.getInstance(project); // csManager.reformat(method); } @NotNull @Override public String getText() { return "Create Method " + getMethodName(); } @NotNull @Override public String getFamilyName() { return "gosu"; } public String getMethodName() { String name = callExpr.getCanonicalText(); return name.replaceAll("\\(.*$", ""); } }