From e0f75a31dbe7f59237e7ed7e3535ec2031daf8d8 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Fri, 16 Aug 2013 20:51:59 +0400 Subject: [PATCH] IDEA-100392 Allow file template configuration from generate popup --- .../BaseGenerateTestSupportMethodAction.java | 126 ++++++++++++------ .../actions/BaseGenerateAction.java | 9 +- .../GenerateActionPopupTemplateInjector.java | 28 ++++ .../generation/actions/GenerateAction.java | 52 +++++++- .../impl/AllFileTemplatesConfigurable.java | 19 +++ 5 files changed, 193 insertions(+), 41 deletions(-) create mode 100644 platform/lang-api/src/com/intellij/codeInsight/generation/actions/GenerateActionPopupTemplateInjector.java diff --git a/java/java-impl/src/com/intellij/testIntegration/BaseGenerateTestSupportMethodAction.java b/java/java-impl/src/com/intellij/testIntegration/BaseGenerateTestSupportMethodAction.java index d60d08d926e8..baf03853e3bd 100644 --- a/java/java-impl/src/com/intellij/testIntegration/BaseGenerateTestSupportMethodAction.java +++ b/java/java-impl/src/com/intellij/testIntegration/BaseGenerateTestSupportMethodAction.java @@ -22,17 +22,23 @@ 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; @@ -48,13 +54,46 @@ public class BaseGenerateTestSupportMethodAction extends BaseGenerateAction { super(new MyHandler(methodKind)); } + @Nullable + @Override + public AnAction createEditTemplateAction(DataContext dataContext) { + final Project project = PlatformDataKeys.PROJECT.getData(dataContext); + final Editor editor = PlatformDataKeys.EDITOR.getData(dataContext); + final PsiFile file = LangDataKeys.PSI_FILE.getData(dataContext); + final PsiClass targetClass = editor == null || file == null ? null : getTargetClass(editor, file); + if (targetClass != null) { + final List 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() { + @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(Editor editor, PsiFile file) { + 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); @@ -86,6 +125,47 @@ public class BaseGenerateTestSupportMethodAction extends BaseGenerateAction { return true; } + private static void chooseAndPerform(Editor editor, List frameworks, final Consumer 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() { + @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; @@ -98,50 +178,18 @@ public class BaseGenerateTestSupportMethodAction extends BaseGenerateAction { final PsiClass targetClass = findTargetClass(editor, file); final List frameworks = TestIntegrationUtils.findSuitableFrameworks(targetClass); if (frameworks.isEmpty()) return; - - if (frameworks.size() == 1) { - doGenerate(editor, file, targetClass, frameworks.get(0)); - return; - } - - final JList list = new JBList(frameworks.toArray(new TestFramework[frameworks.size()])); - list.setCellRenderer(new DefaultListCellRenderer() { + final Consumer consumer = new Consumer() { @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; - } - }); - - final Runnable runnable = new Runnable() { - public void run() { - TestFramework selected = (TestFramework)list.getSelectedValue(); - if (selected == null) return; - doGenerate(editor, file, targetClass, selected); + public void consume(TestFramework framework) { + if (framework == null) return; + doGenerate(editor, file, targetClass, framework); } }; - PopupChooserBuilder builder = new PopupChooserBuilder(list); - builder.setFilteringEnabled(new Function() { - @Override - public String fun(Object o) { - return ((TestFramework)o).getName(); - } - }); - - builder - .setTitle("Choose Framework") - .setItemChoosenCallback(runnable) - .setMovable(true) - .createPopup().showInBestPositionFor(editor); + chooseAndPerform(editor, frameworks, consumer); } + private void doGenerate(final Editor editor, final PsiFile file, final PsiClass targetClass, final TestFramework framework) { if (!CommonRefactoringUtil.checkReadOnlyStatus(file)) return; diff --git a/java/openapi/src/com/intellij/codeInsight/generation/actions/BaseGenerateAction.java b/java/openapi/src/com/intellij/codeInsight/generation/actions/BaseGenerateAction.java index e8afbc66751e..742665350e31 100644 --- a/java/openapi/src/com/intellij/codeInsight/generation/actions/BaseGenerateAction.java +++ b/java/openapi/src/com/intellij/codeInsight/generation/actions/BaseGenerateAction.java @@ -18,6 +18,8 @@ package com.intellij.codeInsight.generation.actions; import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.actions.CodeInsightAction; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; @@ -25,13 +27,18 @@ import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class BaseGenerateAction extends CodeInsightAction { +public class BaseGenerateAction extends CodeInsightAction implements GenerateActionPopupTemplateInjector { private final CodeInsightActionHandler myHandler; public BaseGenerateAction(CodeInsightActionHandler handler) { myHandler = handler; } + @Nullable + public AnAction createEditTemplateAction(DataContext dataContext) { + return null; + } + @NotNull @Override protected final CodeInsightActionHandler getHandler() { diff --git a/platform/lang-api/src/com/intellij/codeInsight/generation/actions/GenerateActionPopupTemplateInjector.java b/platform/lang-api/src/com/intellij/codeInsight/generation/actions/GenerateActionPopupTemplateInjector.java new file mode 100644 index 000000000000..56868fed3e84 --- /dev/null +++ b/platform/lang-api/src/com/intellij/codeInsight/generation/actions/GenerateActionPopupTemplateInjector.java @@ -0,0 +1,28 @@ +/* + * 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.codeInsight.generation.actions; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.DataContext; +import org.jetbrains.annotations.Nullable; + +/** + * Generate action could provide an action to edit corresponding file template. It would appear then in the subMenu of the Generate... popup + */ +public interface GenerateActionPopupTemplateInjector { + @Nullable + AnAction createEditTemplateAction(DataContext dataContext); +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/GenerateAction.java b/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/GenerateAction.java index 81e16198ae4f..d909be614997 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/GenerateAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/generation/actions/GenerateAction.java @@ -24,6 +24,8 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListPopup; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class GenerateAction extends DumbAwareAction implements PreloadableAction { @Override @@ -33,7 +35,7 @@ public class GenerateAction extends DumbAwareAction implements PreloadableAction final ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup( CodeInsightBundle.message("generate.list.popup.title"), - getGroup(), + wrapGroup(getGroup(), dataContext), dataContext, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false); @@ -65,8 +67,56 @@ public class GenerateAction extends DumbAwareAction implements PreloadableAction return (DefaultActionGroup)ActionManager.getInstance().getAction(IdeActions.GROUP_GENERATE); } + private static DefaultActionGroup wrapGroup(DefaultActionGroup actionGroup, DataContext dataContext) { + final DefaultActionGroup copy = new DefaultActionGroup(); + for (final AnAction action : actionGroup.getChildren(null)) { + if (action instanceof GenerateActionPopupTemplateInjector) { + final AnAction editTemplateAction = ((GenerateActionPopupTemplateInjector)action).createEditTemplateAction(dataContext); + if (editTemplateAction != null) { + copy.add(new GenerateWrappingGroup(action, editTemplateAction)); + continue; + } + } + if (action instanceof DefaultActionGroup) { + copy.add(wrapGroup((DefaultActionGroup)action, dataContext)); + } else { + copy.add(action); + } + } + return copy; + } + @Override public void preload() { ((ActionManagerImpl) ActionManager.getInstance()).preloadActionGroup(IdeActions.GROUP_GENERATE); } + + private static class GenerateWrappingGroup extends ActionGroup { + + private final AnAction myAction; + private final AnAction myEditTemplateAction; + + public GenerateWrappingGroup(AnAction action, AnAction editTemplateAction) { + myAction = action; + myEditTemplateAction = editTemplateAction; + copyFrom(action); + setPopup(true); + } + + @Override + public boolean canBePerformed(DataContext context) { + return true; + } + + @NotNull + @Override + public AnAction[] getChildren(@Nullable AnActionEvent e) { + return new AnAction[] {myEditTemplateAction}; + } + + @Override + public void actionPerformed(AnActionEvent e) { + myAction.actionPerformed(e); + } + } } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/AllFileTemplatesConfigurable.java b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/AllFileTemplatesConfigurable.java index 0460ca310f15..c9cb262b46af 100644 --- a/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/AllFileTemplatesConfigurable.java +++ b/platform/lang-impl/src/com/intellij/ide/fileTemplates/impl/AllFileTemplatesConfigurable.java @@ -28,6 +28,8 @@ import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.options.ShowSettingsUtil; +import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.Splitter; @@ -630,4 +632,21 @@ public class AllFileTemplatesConfigurable implements SearchableConfigurable, Con public Runnable enableSearch(String option) { return null; } + + public static void editCodeTemplate(@NotNull final String templateId, Project project) { + final ShowSettingsUtil util = ShowSettingsUtil.getInstance(); + final AllFileTemplatesConfigurable configurable = new AllFileTemplatesConfigurable(); + util.editConfigurable(project, configurable, new Runnable() { + @Override + public void run() { + configurable.myTabbedPane.setSelectedIndex(ArrayUtil.indexOf(configurable.myTabs, configurable.myCodeTemplatesList)); + for (FileTemplate template : configurable.myCodeTemplatesList.getTemplates()) { + if (Comparing.equal(templateId, template.getName())) { + configurable.myCodeTemplatesList.selectTemplate(template); + break; + } + } + } + }); + } }