blob: bb70ddcf6ca2c9a873ba3f9002686aea912519fd [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.testIntegration;
import com.intellij.codeInsight.CodeInsightActionHandler;
import com.intellij.codeInsight.CodeInsightUtilCore;
import com.intellij.codeInsight.generation.GenerateMembersUtil;
import com.intellij.codeInsight.generation.OverrideImplementUtil;
import com.intellij.codeInsight.generation.PsiGenerationInfo;
import com.intellij.codeInsight.generation.actions.BaseGenerateAction;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.ide.fileTemplates.FileTemplateDescriptor;
import com.intellij.ide.fileTemplates.impl.AllFileTemplatesConfigurable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.ui.components.JBList;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class BaseGenerateTestSupportMethodAction extends BaseGenerateAction {
protected static final Logger LOG = Logger.getInstance("#" + BaseGenerateTestSupportMethodAction.class.getName());
public BaseGenerateTestSupportMethodAction(TestIntegrationUtils.MethodKind methodKind) {
super(new MyHandler(methodKind));
}
@Nullable
@Override
public AnAction createEditTemplateAction(DataContext dataContext) {
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
final Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
final PsiFile file = CommonDataKeys.PSI_FILE.getData(dataContext);
final PsiClass targetClass = editor == null || file == null ? null : getTargetClass(editor, file);
if (targetClass != null) {
final List<TestFramework> frameworks = TestIntegrationUtils.findSuitableFrameworks(targetClass);
final TestIntegrationUtils.MethodKind methodKind = ((MyHandler)getHandler()).myMethodKind;
if (!frameworks.isEmpty()) {
return new AnAction("Edit Template") {
@Override
public void actionPerformed(AnActionEvent e) {
chooseAndPerform(editor, frameworks, new Consumer<TestFramework>() {
@Override
public void consume(TestFramework framework) {
final FileTemplateDescriptor descriptor = methodKind.getFileTemplateDescriptor(framework);
if (descriptor != null) {
final String fileName = descriptor.getFileName();
AllFileTemplatesConfigurable.editCodeTemplate(FileUtil.getNameWithoutExtension(fileName), project);
} else {
HintManager.getInstance().showErrorHint(editor, "No template found for " + framework.getName() + ":" + BaseGenerateTestSupportMethodAction.this.getTemplatePresentation().getText());
}
}
});
}
};
}
}
return null;
}
@Override
protected PsiClass getTargetClass(Editor editor, PsiFile file) {
return findTargetClass(editor, file);
}
@Nullable
private static PsiClass findTargetClass(@NotNull Editor editor, @NotNull PsiFile file) {
int offset = editor.getCaretModel().getOffset();
PsiElement element = file.findElementAt(offset);
return element == null ? null : TestIntegrationUtils.findOuterClass(element);
}
@Override
protected boolean isValidForClass(PsiClass targetClass) {
List<TestFramework> frameworks = TestIntegrationUtils.findSuitableFrameworks(targetClass);
if (frameworks.isEmpty()) return false;
for (TestFramework each : frameworks) {
if (isValidFor(targetClass, each)) return true;
}
return false;
}
@Override
protected boolean isValidForFile(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
if (file instanceof PsiCompiledElement) return false;
PsiDocumentManager.getInstance(project).commitAllDocuments();
PsiClass targetClass = getTargetClass(editor, file);
return targetClass != null && isValidForClass(targetClass);
}
protected boolean isValidFor(PsiClass targetClass, TestFramework framework) {
return true;
}
private static void chooseAndPerform(Editor editor, List<TestFramework> frameworks, final Consumer<TestFramework> consumer) {
if (frameworks.size() == 1) {
consumer.consume(frameworks.get(0));
return;
}
final JList list = new JBList(frameworks.toArray(new TestFramework[frameworks.size()]));
list.setCellRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component result = super.getListCellRendererComponent(list, "", index, isSelected, cellHasFocus);
if (value == null) return result;
TestFramework framework = (TestFramework)value;
setIcon(framework.getIcon());
setText(framework.getName());
return result;
}
});
PopupChooserBuilder builder = new PopupChooserBuilder(list);
builder.setFilteringEnabled(new Function<Object, String>() {
@Override
public String fun(Object o) {
return ((TestFramework)o).getName();
}
});
builder
.setTitle("Choose Framework")
.setItemChoosenCallback(new Runnable() {
@Override
public void run() {
consumer.consume((TestFramework)list.getSelectedValue());
}
})
.setMovable(true)
.createPopup().showInBestPositionFor(editor);
}
private static class MyHandler implements CodeInsightActionHandler {
private TestIntegrationUtils.MethodKind myMethodKind;
private MyHandler(TestIntegrationUtils.MethodKind methodKind) {
myMethodKind = methodKind;
}
public void invoke(@NotNull Project project, @NotNull final Editor editor, @NotNull final PsiFile file) {
final PsiClass targetClass = findTargetClass(editor, file);
final List<TestFramework> frameworks = new ArrayList<TestFramework>(TestIntegrationUtils.findSuitableFrameworks(targetClass));
for (Iterator<TestFramework> iterator = frameworks.iterator(); iterator.hasNext(); ) {
if (myMethodKind.getFileTemplateDescriptor(iterator.next()) == null) {
iterator.remove();
}
}
if (frameworks.isEmpty()) return;
final Consumer<TestFramework> consumer = new Consumer<TestFramework>() {
@Override
public void consume(TestFramework framework) {
if (framework == null) return;
doGenerate(editor, file, targetClass, framework);
}
};
chooseAndPerform(editor, frameworks, consumer);
}
private void doGenerate(final Editor editor, final PsiFile file, final PsiClass targetClass, final TestFramework framework) {
if (!CommonRefactoringUtil.checkReadOnlyStatus(file)) return;
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
try {
PsiDocumentManager.getInstance(file.getProject()).commitAllDocuments();
PsiMethod method = generateDummyMethod(editor, file);
if (method == null) return;
TestIntegrationUtils.runTestMethodTemplate(myMethodKind, framework, editor, targetClass, method, "name", false, null);
}
catch (IncorrectOperationException e) {
HintManager.getInstance().showErrorHint(editor, "Cannot generate method: " + e.getMessage());
LOG.warn(e);
}
}
});
}
@Nullable
private static PsiMethod generateDummyMethod(Editor editor, PsiFile file) throws IncorrectOperationException {
final PsiMethod method = TestIntegrationUtils.createDummyMethod(file);
final PsiGenerationInfo<PsiMethod> info = OverrideImplementUtil.createGenerationInfo(method);
int offset = findOffsetToInsertMethodTo(editor, file);
GenerateMembersUtil.insertMembersAtOffset(file, offset, Collections.singletonList(info));
final PsiMethod member = info.getPsiMember();
return member != null ? CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(member) : null;
}
private static int findOffsetToInsertMethodTo(Editor editor, PsiFile file) {
int result = editor.getCaretModel().getOffset();
PsiClass classAtCursor = PsiTreeUtil.getParentOfType(file.findElementAt(result), PsiClass.class, false);
while (classAtCursor != null && !(classAtCursor.getParent() instanceof PsiFile)) {
result = classAtCursor.getTextRange().getEndOffset();
classAtCursor = PsiTreeUtil.getParentOfType(classAtCursor, PsiClass.class);
}
return result;
}
public boolean startInWriteAction() {
return false;
}
}
}