diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java index d439d9c5eb98..615b05a229c3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java @@ -4,8 +4,8 @@ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.codeInsight.completion.CompletionInitializationContext; import com.intellij.codeInsight.completion.JavaCompletionContributor; import com.intellij.codeInsight.template.impl.TemplateImpl; -import com.intellij.codeInsight.template.impl.TemplateSettings; import com.intellij.codeInsight.template.postfix.templates.editable.*; +import com.intellij.codeInsight.template.postfix.templates.editable.JavaPostfixTemplateExpressionCondition.JavaPostfixTemplateExpressionFqnCondition; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Document; @@ -17,25 +17,18 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; -import kotlin.LazyKt; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.java.JpsJavaSdkType; -import java.util.Collections; -import java.util.LinkedHashSet; import java.util.Set; +import static com.intellij.codeInsight.template.postfix.templates.PostfixTemplatesUtils.*; + public class JavaPostfixTemplateProvider implements PostfixTemplateProvider { private static final String LANGUAGE_LEVEL_ATTR = "language-level"; - private static final String CONDITIONS_TAG = "conditions"; - private static final String TEMPLATE_TAG = "template"; - private static final String CONDITION_TAG = "condition"; - private static final String ID_ATTR = "id"; - private static final String FQN_ATTR = "fqn"; - private static final String TOPMOST_ATTR = "topmost"; private final Set myBuiltinTemplates = ContainerUtil.newHashSet( new AssertStatementPostfixTemplate(this), @@ -140,7 +133,10 @@ public class JavaPostfixTemplateProvider implements PostfixTemplateProvider { !(templateToEdit instanceof ForIndexedPostfixTemplate) && !(templateToEdit instanceof ArgumentPostfixTemplate) && !(templateToEdit instanceof OptionalPostfixTemplate)) { - return new JavaPostfixTemplateEditor(this, templateToEdit); + JavaPostfixTemplateEditor editor = new JavaPostfixTemplateEditor(this); + editor.setTemplate(templateToEdit); + + return editor; } return null; } @@ -148,46 +144,30 @@ public class JavaPostfixTemplateProvider implements PostfixTemplateProvider { @Nullable @Override public JavaEditablePostfixTemplate readExternalTemplate(@NotNull String id, @NotNull String name, @NotNull Element template) { - boolean useTopmostExpression = Boolean.parseBoolean(template.getAttributeValue(TOPMOST_ATTR)); + TemplateImpl liveTemplate = readExternalLiveTemplate(template, this); + if (liveTemplate == null) return null; + Set conditions = readExternalConditions(template, JavaPostfixTemplateProvider::readExternal); + boolean useTopmostExpression = readExternalTopmostAttribute(template); String languageLevelAttributeValue = template.getAttributeValue(LANGUAGE_LEVEL_ATTR); LanguageLevel languageLevel = ObjectUtils.notNull(LanguageLevel.parse(languageLevelAttributeValue), LanguageLevel.JDK_1_6); - Set conditions = new LinkedHashSet<>(); - Element conditionsElement = template.getChild(CONDITIONS_TAG); - if (conditionsElement != null) { - for (Element conditionElement : conditionsElement.getChildren(CONDITION_TAG)) { - ContainerUtil.addIfNotNull(conditions, readExternal(conditionElement)); - } - } - Element templateChild = template.getChild(TemplateSettings.TEMPLATE); - if (templateChild == null) { - return null; - } - TemplateImpl liveTemplate = TemplateSettings.readTemplateFromElement("", templateChild, getClass().getClassLoader()); return new JavaEditablePostfixTemplate(id, name, liveTemplate, "", conditions, languageLevel, useTopmostExpression, this); } @Override public void writeExternalTemplate(@NotNull PostfixTemplate template, @NotNull Element parentElement) { if (template instanceof JavaEditablePostfixTemplate) { - parentElement.setAttribute(TOPMOST_ATTR, String.valueOf(((JavaEditablePostfixTemplate)template).isUseTopmostExpression())); LanguageLevel languageLevel = ((JavaEditablePostfixTemplate)template).getMinimumLanguageLevel(); parentElement.setAttribute(LANGUAGE_LEVEL_ATTR, JpsJavaSdkType.complianceOption(languageLevel.toJavaVersion())); - Element conditionsTag = new Element(CONDITIONS_TAG); - for (JavaPostfixTemplateExpressionCondition condition : ((JavaEditablePostfixTemplate)template).getExpressionConditions()) { - writeExternal(condition, conditionsTag); - } - Element templateTag = TemplateSettings.serializeTemplate(((EditablePostfixTemplate)template).getLiveTemplate(), null, - LazyKt.lazyOf(Collections.emptyMap())); - parentElement.addContent(conditionsTag).addContent(templateTag); + PostfixTemplatesUtils.writeExternalTemplate(template, parentElement); } } @Nullable private static JavaPostfixTemplateExpressionCondition readExternal(@NotNull Element condition) { - String id = condition.getAttributeValue(ID_ATTR); + String id = condition.getAttributeValue(PostfixTemplateExpressionCondition.ID_ATTR); if (JavaPostfixTemplateExpressionCondition.JavaPostfixTemplateArrayExpressionCondition.ID.equals(id)) { return new JavaPostfixTemplateExpressionCondition.JavaPostfixTemplateArrayExpressionCondition(); } @@ -206,22 +186,12 @@ public class JavaPostfixTemplateProvider implements PostfixTemplateProvider { if (JavaPostfixTemplateExpressionCondition.JavaPostfixTemplateNotPrimitiveTypeExpressionCondition.ID.equals(id)) { return new JavaPostfixTemplateExpressionCondition.JavaPostfixTemplateNotPrimitiveTypeExpressionCondition(); } - if (JavaPostfixTemplateExpressionCondition.JavaPostfixTemplateExpressionFqnCondition.ID.equals(id)) { - String fqn = condition.getAttributeValue(FQN_ATTR); + if (JavaPostfixTemplateExpressionFqnCondition.ID.equals(id)) { + String fqn = condition.getAttributeValue(JavaPostfixTemplateExpressionFqnCondition.FQN_ATTR); if (StringUtil.isNotEmpty(fqn)) { - return new JavaPostfixTemplateExpressionCondition.JavaPostfixTemplateExpressionFqnCondition(fqn); + return new JavaPostfixTemplateExpressionFqnCondition(fqn); } } return null; } - - private static void writeExternal(@NotNull JavaPostfixTemplateExpressionCondition condition, @NotNull Element parentElement) { - Element element = new Element(CONDITION_TAG); - element.setAttribute(ID_ATTR, condition.getId()); - if (condition instanceof JavaPostfixTemplateExpressionCondition.JavaPostfixTemplateExpressionFqnCondition) { - String fqn = ((JavaPostfixTemplateExpressionCondition.JavaPostfixTemplateExpressionFqnCondition)condition).getFqn(); - element.setAttribute(FQN_ATTR, fqn); - } - parentElement.addContent(element); - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaEditablePostfixTemplate.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaEditablePostfixTemplate.java index 410ed3743687..2aa167d218b7 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaEditablePostfixTemplate.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaEditablePostfixTemplate.java @@ -7,6 +7,7 @@ import com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplatesUtils; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiExpression; @@ -24,12 +25,11 @@ import java.util.List; import java.util.Objects; import java.util.Set; -public class JavaEditablePostfixTemplate extends EditablePostfixTemplate { +public class JavaEditablePostfixTemplate + extends EditablePostfixTemplateWithConditions { private static final Condition PSI_ERROR_FILTER = element -> !PsiTreeUtil.hasErrorElements(element); - @NotNull private final Set myExpressionConditions; @NotNull private final LanguageLevel myMinimumLanguageLevel; - private final boolean myUseTopmostExpression; public JavaEditablePostfixTemplate(@NotNull String templateName, @NotNull String templateText, @@ -38,7 +38,8 @@ public class JavaEditablePostfixTemplate extends EditablePostfixTemplate { @NotNull LanguageLevel minimumLanguageLevel, boolean useTopmostExpression, @NotNull PostfixTemplateProvider provider) { - this(templateName, templateName, createTemplate(templateText), example, expressionConditions, minimumLanguageLevel, useTopmostExpression, provider); + this(templateName, templateName, createTemplate(templateText), example, expressionConditions, minimumLanguageLevel, + useTopmostExpression, provider); } public JavaEditablePostfixTemplate(@NotNull String templateId, @@ -49,18 +50,8 @@ public class JavaEditablePostfixTemplate extends EditablePostfixTemplate { @NotNull LanguageLevel minimumLanguageLevel, boolean useTopmostExpression, @NotNull PostfixTemplateProvider provider) { - super(templateId, templateName, createTemplate(templateText), example, provider); - myExpressionConditions = expressionConditions; + super(templateId, templateName, createTemplate(templateText), example, expressionConditions, useTopmostExpression, provider); myMinimumLanguageLevel = minimumLanguageLevel; - myUseTopmostExpression = useTopmostExpression; - } - - @NotNull - private static TemplateImpl createTemplate(@NotNull String templateText) { - TemplateImpl template = new TemplateImpl("fakeKey", templateText, ""); - template.setToReformat(true); - template.parseSegments(); - return template; } public JavaEditablePostfixTemplate(@NotNull String templateId, @@ -71,42 +62,16 @@ public class JavaEditablePostfixTemplate extends EditablePostfixTemplate { @NotNull LanguageLevel minimumLanguageLevel, boolean useTopmostExpression, @NotNull PostfixTemplateProvider provider) { - super(templateId, templateName, liveTemplate, example, provider); - myExpressionConditions = expressionConditions; + super(templateId, templateName, liveTemplate, example, expressionConditions, useTopmostExpression, provider); myMinimumLanguageLevel = minimumLanguageLevel; - myUseTopmostExpression = useTopmostExpression; } - @NotNull - public Set getExpressionConditions() { - return myExpressionConditions; - } @NotNull public LanguageLevel getMinimumLanguageLevel() { return myMinimumLanguageLevel; } - public boolean isUseTopmostExpression() { - return myUseTopmostExpression; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof JavaEditablePostfixTemplate)) return false; - if (!super.equals(o)) return false; - JavaEditablePostfixTemplate template = (JavaEditablePostfixTemplate)o; - return myUseTopmostExpression == template.myUseTopmostExpression && - Objects.equals(myExpressionConditions, template.myExpressionConditions) && - myMinimumLanguageLevel == template.myMinimumLanguageLevel; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), myExpressionConditions, myMinimumLanguageLevel); - } - @Override protected List getExpressions(@NotNull PsiElement context, @NotNull Document document, int offset) { if (DumbService.getInstance(context.getProject()).isDumb()) return Collections.emptyList(); @@ -122,28 +87,23 @@ public class JavaEditablePostfixTemplate extends EditablePostfixTemplate { expressions = ContainerUtil.newArrayList(IntroduceVariableBase.collectExpressions(file, document, Math.max(offset - 1, 0), false)); } - return ContainerUtil.filter(expressions, e -> { + + return ContainerUtil.filter(expressions, Conditions.and(e -> { if (!PSI_ERROR_FILTER.value(e) || !(e instanceof PsiExpression) || e.getTextRange().getEndOffset() != offset) { return false; } - for (JavaPostfixTemplateExpressionCondition condition : myExpressionConditions) { - if (condition.value((PsiExpression)e)) { - return true; - } - } - return myExpressionConditions.isEmpty(); - }); + return true; + }, getExpressionCompositeCondition())); } @NotNull @Override - protected PsiElement getElementToRemove(@NotNull PsiElement element) { - if (myUseTopmostExpression) { - PsiElement parent = element.getParent(); - if (parent instanceof PsiExpressionStatement) { - return parent; - } + protected PsiElement getTopmostExpression(@NotNull PsiElement element) { + PsiElement parent = element.getParent(); + if (parent instanceof PsiExpressionStatement) { + return parent; } + return element; } @@ -157,4 +117,20 @@ public class JavaEditablePostfixTemplate extends EditablePostfixTemplate { public boolean isBuiltin() { return false; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + JavaEditablePostfixTemplate template = (JavaEditablePostfixTemplate)o; + return myMinimumLanguageLevel == template.myMinimumLanguageLevel; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), myMinimumLanguageLevel); + } + + } diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaPostfixTemplateEditor.form b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaPostfixTemplateEditor.form deleted file mode 100644 index 2a73567f1f5c..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaPostfixTemplateEditor.form +++ /dev/null @@ -1,74 +0,0 @@ - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaPostfixTemplateEditor.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaPostfixTemplateEditor.java index e1e09c245b7f..d66c00ee4475 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaPostfixTemplateEditor.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaPostfixTemplateEditor.java @@ -2,16 +2,13 @@ package com.intellij.codeInsight.template.postfix.templates.editable; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; -import com.intellij.codeInsight.template.impl.TemplateEditorUtil; +import com.intellij.codeInsight.template.postfix.settings.PostfixTemplateEditorBase; import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate; import com.intellij.codeInsight.template.postfix.templates.PostfixTemplateProvider; -import com.intellij.ide.DataManager; import com.intellij.ide.util.TreeClassChooser; import com.intellij.ide.util.TreeClassChooserFactory; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; @@ -20,75 +17,25 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; -import com.intellij.ui.AnActionButton; import com.intellij.ui.ColoredListCellRenderer; -import com.intellij.ui.ListUtil; -import com.intellij.ui.ToolbarDecorator; -import com.intellij.ui.components.JBCheckBox; -import com.intellij.ui.components.JBLabel; -import com.intellij.ui.components.JBList; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.FormBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.awt.*; import java.util.Set; -public class JavaPostfixTemplateEditor implements PostfixTemplateEditor { - @NotNull private final PostfixTemplateProvider myProvider; - @NotNull private final Editor myTemplateEditor; - @NotNull private final JBList myExpressionTypesList; - @NotNull private final DefaultListModel myExpressionTypesListModel; +public class JavaPostfixTemplateEditor extends PostfixTemplateEditorBase { - private JPanel myPanel; - private JBCheckBox myApplyToTheTopmostJBCheckBox; - private ComboBox myLanguageLevelCombo; - private JBLabel myExpressionVariableHint; - private JPanel myExpressionTypesPanel; - private JPanel myTemplateEditorPanel; + @NotNull private final JPanel myPanel; + @NotNull private final ComboBox myLanguageLevelCombo; - public JavaPostfixTemplateEditor(@NotNull PostfixTemplateProvider provider, @Nullable PostfixTemplate template) { - myProvider = provider; - Project defaultProject = ProjectManager.getInstance().getDefaultProject(); - myTemplateEditor = TemplateEditorUtil.createEditor(false, createDocument(defaultProject), defaultProject); - - myExpressionTypesListModel = JBList.createDefaultListModel(); - myExpressionTypesList = new JBList<>(myExpressionTypesListModel); - myExpressionTypesList.setCellRenderer(new ColoredListCellRenderer() { - @Override - protected void customizeCellRenderer(@NotNull JList list, - JavaPostfixTemplateExpressionCondition value, - int index, - boolean selected, - boolean hasFocus) { - append(value.getPresentableName()); - } - }); - myExpressionTypesPanel.setLayout(new BorderLayout()); - myExpressionTypesPanel.add(ToolbarDecorator.createDecorator(myExpressionTypesList) - .setAddAction(button -> addExpressionType(button)) - .setRemoveAction(button -> ListUtil.removeSelectedItems(myExpressionTypesList)) - .disableUpDownActions() - .createPanel()); - myExpressionTypesPanel.setMinimumSize(new Dimension(-1, 100)); - myTemplateEditorPanel.setLayout(new BorderLayout()); - myTemplateEditorPanel.add(myTemplateEditor.getComponent()); - UIUtil.applyStyle(UIUtil.ComponentStyle.SMALL, myExpressionVariableHint); - myExpressionVariableHint.setFontColor(UIUtil.FontColor.BRIGHTER); - - if (template instanceof JavaEditablePostfixTemplate) { - setTemplate((JavaEditablePostfixTemplate)template); - } - } - - private void createUIComponents() { + public JavaPostfixTemplateEditor(@NotNull PostfixTemplateProvider provider) { + super(provider, createEditor()); myLanguageLevelCombo = new ComboBox<>(LanguageLevel.values()); myLanguageLevelCombo.setRenderer(new ColoredListCellRenderer() { @Override @@ -96,11 +43,17 @@ public class JavaPostfixTemplateEditor implements PostfixTemplateEditor { append(value.getPresentableText()); } }); + + myPanel = FormBuilder.createFormBuilder() + .addLabeledComponent("Minimum language level:", myLanguageLevelCombo) + .addComponent(myEditTemplateAndConditionsPanel) + .getPanel(); } - @Override - public void dispose() { - TemplateEditorUtil.disposeTemplateEditor(myTemplateEditor); + @NotNull + private static Editor createEditor() { + Project defaultProject = ProjectManager.getInstance().getDefaultProject(); + return createEditor(defaultProject, createDocument(defaultProject)); } @NotNull @@ -116,28 +69,12 @@ public class JavaPostfixTemplateEditor implements PostfixTemplateEditor { myProvider); } - private void setTemplate(@NotNull JavaEditablePostfixTemplate template) { - myExpressionTypesListModel.clear(); - for (JavaPostfixTemplateExpressionCondition condition : template.getExpressionConditions()) { - myExpressionTypesListModel.addElement(condition); - } - myLanguageLevelCombo.setSelectedItem(template.getMinimumLanguageLevel()); - myApplyToTheTopmostJBCheckBox.setSelected(template.isUseTopmostExpression()); - ApplicationManager.getApplication() - .runWriteAction(() -> myTemplateEditor.getDocument().setText(template.getLiveTemplate().getString())); - } - @NotNull @Override public JComponent getComponent() { return myPanel; } - @Override - public String getHelpId() { - return "reference.custom.postfix.templates"; - } - private static Document createDocument(@Nullable Project project) { if (project == null) { return EditorFactory.getInstance().createDocument(""); @@ -149,8 +86,8 @@ public class JavaPostfixTemplateEditor implements PostfixTemplateEditor { return PsiDocumentManager.getInstance(project).getDocument(fragment); } - private void addExpressionType(@NotNull AnActionButton button) { - DefaultActionGroup group = new DefaultActionGroup(); + @Override + protected void fillConditions(@NotNull DefaultActionGroup group) { group.add(new AddConditionAction(new JavaPostfixTemplateExpressionCondition.JavaPostfixTemplateVoidExpressionCondition())); group.add(new AddConditionAction(new JavaPostfixTemplateExpressionCondition.JavaPostfixTemplateNonVoidExpressionCondition())); group.add(new AddConditionAction(new JavaPostfixTemplateExpressionCondition.JavaPostfixTemplateBooleanExpressionCondition())); @@ -162,10 +99,14 @@ public class JavaPostfixTemplateEditor implements PostfixTemplateEditor { group.add(new ChooseClassAction(project)); } group.add(new ChooseClassAction(null)); - DataContext context = DataManager.getInstance().getDataContext(button.getContextComponent()); - ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup(null, group, context, - JBPopupFactory.ActionSelectionAid.ALPHA_NUMBERING, true, null); - popup.show(ObjectUtils.assertNotNull(button.getPreferredPopupPoint())); + } + + @Override + public void setTemplate(@Nullable PostfixTemplate template) { + super.setTemplate(template); + if (template instanceof JavaEditablePostfixTemplate) { + myLanguageLevelCombo.setSelectedItem(((JavaEditablePostfixTemplate)template).getMinimumLanguageLevel()); + } } private class AddConditionAction extends DumbAwareAction { diff --git a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaPostfixTemplateExpressionCondition.java b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaPostfixTemplateExpressionCondition.java index 10499b0ec694..1d47218ebd89 100644 --- a/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaPostfixTemplateExpressionCondition.java +++ b/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaPostfixTemplateExpressionCondition.java @@ -2,25 +2,19 @@ package com.intellij.codeInsight.template.postfix.templates.editable; import com.intellij.codeInsight.template.postfix.util.JavaPostfixTemplatesUtils; -import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiType; import com.intellij.psi.util.InheritanceUtil; +import org.jdom.Element; import org.jetbrains.annotations.NotNull; import java.util.Objects; -public interface JavaPostfixTemplateExpressionCondition extends Condition { - String getId(); - - String getPresentableName(); - - boolean equals(Object o); - - int hashCode(); +public interface JavaPostfixTemplateExpressionCondition extends PostfixTemplateExpressionCondition { class JavaPostfixTemplateExpressionFqnCondition implements JavaPostfixTemplateExpressionCondition { public static final String ID = "fqn"; + public static final String FQN_ATTR = "fqn"; private final String myFqn; @@ -38,11 +32,13 @@ public interface JavaPostfixTemplateExpressionCondition extends Condition additionalFilter) { + @NotNull + public static PostfixTemplateExpressionSelector selectorAllExpressionsWithCurrentOffset(@Nullable Condition additionalFilter) { return new PostfixTemplateExpressionSelectorBase(additionalFilter) { @Override protected List getNonFilteredExpressions(@NotNull PsiElement context, @NotNull Document document, int offset) { @@ -98,7 +99,7 @@ public abstract class JavaPostfixTemplatesUtils { @Override public List getExpressions(@NotNull PsiElement context, @NotNull Document document, int offset) { if (DumbService.getInstance(context.getProject()).isDumb()) return Collections.emptyList(); - + List expressions = super.getExpressions(context, document, offset); if (!expressions.isEmpty()) return expressions; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/settings/PostfixTemplateEditorBase.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/settings/PostfixTemplateEditorBase.java new file mode 100644 index 000000000000..113ccbcd1400 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/settings/PostfixTemplateEditorBase.java @@ -0,0 +1,149 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.codeInsight.template.postfix.settings; + +import com.intellij.codeInsight.template.impl.TemplateEditorUtil; +import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate; +import com.intellij.codeInsight.template.postfix.templates.PostfixTemplateProvider; +import com.intellij.codeInsight.template.postfix.templates.editable.EditablePostfixTemplateWithConditions; +import com.intellij.codeInsight.template.postfix.templates.editable.PostfixTemplateEditor; +import com.intellij.codeInsight.template.postfix.templates.editable.PostfixTemplateExpressionCondition; +import com.intellij.ide.DataManager; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.ListPopup; +import com.intellij.psi.PsiElement; +import com.intellij.ui.AnActionButton; +import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.ListUtil; +import com.intellij.ui.ToolbarDecorator; +import com.intellij.ui.components.JBCheckBox; +import com.intellij.ui.components.JBLabel; +import com.intellij.ui.components.JBList; +import com.intellij.util.ObjectUtils; +import com.intellij.util.ui.DialogUtil; +import com.intellij.util.ui.FormBuilder; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; + +public abstract class PostfixTemplateEditorBase> implements PostfixTemplateEditor { + + @NotNull protected final PostfixTemplateProvider myProvider; + @NotNull protected final Editor myTemplateEditor; + @NotNull protected final JBList myExpressionTypesList; + @NotNull protected final DefaultListModel myExpressionTypesListModel; + + @NotNull protected final JPanel myTemplateEditorPanel; + @NotNull protected final JPanel myExpressionTypesPanel; + @NotNull protected final JBCheckBox myApplyToTheTopmostJBCheckBox; + @NotNull protected final JPanel myEditTemplateAndConditionsPanel; + @NotNull protected final JBLabel myExpressionVariableHint; + + + public PostfixTemplateEditorBase(@NotNull PostfixTemplateProvider provider) { + this(provider, createSimpleEditor()); + } + + public PostfixTemplateEditorBase(@NotNull PostfixTemplateProvider provider, + @NotNull Editor templateEditor) { + myProvider = provider; + myTemplateEditor = templateEditor; + + myApplyToTheTopmostJBCheckBox = new JBCheckBox("Apply to the &topmost expression"); + DialogUtil.registerMnemonic(myApplyToTheTopmostJBCheckBox, '&'); + myTemplateEditorPanel = new JPanel(new BorderLayout()); + myTemplateEditorPanel.add(myTemplateEditor.getComponent()); + + myExpressionVariableHint = new JBLabel("Use $EXPR$ variable to refer target expression"); + UIUtil.applyStyle(UIUtil.ComponentStyle.SMALL, myExpressionVariableHint); + myExpressionVariableHint.setFontColor(UIUtil.FontColor.BRIGHTER); + + myExpressionTypesListModel = JBList.createDefaultListModel(); + myExpressionTypesList = new JBList<>(myExpressionTypesListModel); + myExpressionTypesList.setCellRenderer(new ColoredListCellRenderer>() { + + @Override + protected void customizeCellRenderer(@NotNull JList> list, + PostfixTemplateExpressionCondition value, + int index, + boolean selected, + boolean hasFocus) { + append(value.getPresentableName()); + } + }); + + myExpressionTypesPanel = new JPanel(new BorderLayout()); + myExpressionTypesPanel.add(ToolbarDecorator.createDecorator(myExpressionTypesList) + .setAddAction(button -> showAddExpressionTypePopup(button)) + .setRemoveAction(button -> ListUtil.removeSelectedItems(myExpressionTypesList)) + .disableUpDownActions() + .createPanel()); + myExpressionTypesPanel.setMinimumSize(new Dimension(-1, 100)); + + FormBuilder builder = FormBuilder.createFormBuilder(); + builder.addLabeledComponent("Applicable expression types:", myExpressionTypesPanel, true); + builder.addComponent(myApplyToTheTopmostJBCheckBox); + builder.addComponent(myTemplateEditorPanel); + builder.addComponent(myExpressionVariableHint); + + myEditTemplateAndConditionsPanel = builder.getPanel(); + } + + @NotNull + protected static Editor createEditor(@NotNull Project project, @NotNull Document document) { + return TemplateEditorUtil.createEditor(false, document, project); + } + + @NotNull + private static Editor createSimpleEditor() { + Project defaultProject = ProjectManager.getInstance().getDefaultProject(); + return createEditor(defaultProject, EditorFactory.getInstance().createDocument("")); + } + + protected final void showAddExpressionTypePopup(@NotNull AnActionButton button) { + DefaultActionGroup group = new DefaultActionGroup(); + fillConditions(group); + DataContext context = DataManager.getInstance().getDataContext(button.getContextComponent()); + ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup(null, group, context, + JBPopupFactory.ActionSelectionAid.ALPHA_NUMBERING, true, null); + popup.show(ObjectUtils.assertNotNull(button.getPreferredPopupPoint())); + } + + protected abstract void fillConditions(@NotNull DefaultActionGroup group); + + public void setTemplate(@Nullable PostfixTemplate rawTemplate) { + if (!(rawTemplate instanceof EditablePostfixTemplateWithConditions)) return; + + //noinspection unchecked + EditablePostfixTemplateWithConditions template = (EditablePostfixTemplateWithConditions)rawTemplate; + + myExpressionTypesListModel.clear(); + for (Condition condition : template.getExpressionConditions()) { + myExpressionTypesListModel.addElement(condition); + } + myApplyToTheTopmostJBCheckBox.setSelected(template.isUseTopmostExpression()); + ApplicationManager.getApplication() + .runWriteAction(() -> myTemplateEditor.getDocument().setText(template.getLiveTemplate().getString())); + } + + @Override + public String getHelpId() { + return "reference.custom.postfix.templates"; + } + + @Override + public void dispose() { + TemplateEditorUtil.disposeTemplateEditor(myTemplateEditor); + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatesUtils.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatesUtils.java index ff1dc24a3d42..fedf0fe5703b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatesUtils.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatesUtils.java @@ -1,23 +1,38 @@ // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.codeInsight.template.postfix.templates; +import com.intellij.codeInsight.template.impl.TemplateImpl; +import com.intellij.codeInsight.template.impl.TemplateSettings; import com.intellij.codeInsight.template.postfix.settings.PostfixTemplateStorage; +import com.intellij.codeInsight.template.postfix.templates.editable.EditablePostfixTemplate; +import com.intellij.codeInsight.template.postfix.templates.editable.EditablePostfixTemplateWithConditions; import com.intellij.codeInsight.template.postfix.templates.editable.PostfixChangedBuiltinTemplate; +import com.intellij.codeInsight.template.postfix.templates.editable.PostfixTemplateExpressionCondition; import com.intellij.lang.surroundWith.Surrounder; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.refactoring.util.CommonRefactoringUtil; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.UniqueNameGenerator; +import kotlin.LazyKt; +import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; public abstract class PostfixTemplatesUtils { + public static final String CONDITION_TAG = "condition"; + public static final String CONDITIONS_TAG = "conditions"; + public static final String TOPMOST_ATTR = "topmost"; + + private PostfixTemplatesUtils() { } @@ -66,4 +81,58 @@ public abstract class PostfixTemplatesUtils { } return UniqueNameGenerator.generateUniqueName(templateKey + "@userDefined", usedIds); } + + public static void writeExternalTemplate(@NotNull PostfixTemplate template, @NotNull Element parentElement) { + if (template instanceof EditablePostfixTemplateWithConditions) { + parentElement.setAttribute(TOPMOST_ATTR, String.valueOf(((EditablePostfixTemplateWithConditions)template).isUseTopmostExpression())); + Element conditionsTag = new Element(CONDITIONS_TAG); + + //noinspection unchecked + Set> conditions = + (Set>)((EditablePostfixTemplateWithConditions)template) + .getExpressionConditions(); + + for (PostfixTemplateExpressionCondition condition : conditions) { + Element element = new Element(CONDITION_TAG); + condition.serializeTo(element); + conditionsTag.addContent(element); + } + parentElement.addContent(conditionsTag); + } + + Element templateTag = TemplateSettings.serializeTemplate(((EditablePostfixTemplate)template).getLiveTemplate(), null, + LazyKt.lazyOf(Collections.emptyMap())); + parentElement.addContent(templateTag); + } + + @NotNull + public static Set readExternalConditions(@NotNull Element template, + @NotNull Function conditionFactory) { + Element conditionsElement = template.getChild(CONDITIONS_TAG); + if (conditionsElement != null) { + Set conditions = new LinkedHashSet<>(); + for (Element conditionElement : conditionsElement.getChildren(CONDITION_TAG)) { + T fun = conditionFactory.fun(conditionElement); + if (fun != null) { + ContainerUtil.addIfNotNull(conditions, fun); + } + } + + return conditions; + } + + return Collections.emptySet(); + } + + @Nullable + public static TemplateImpl readExternalLiveTemplate(@NotNull Element template, @NotNull PostfixTemplateProvider provider) { + Element templateChild = template.getChild(TemplateSettings.TEMPLATE); + if (templateChild == null) return null; + + return TemplateSettings.readTemplateFromElement("", templateChild, provider.getClass().getClassLoader()); + } + + public static boolean readExternalTopmostAttribute(@NotNull Element template) { + return Boolean.parseBoolean(template.getAttributeValue(TOPMOST_ATTR)); + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/EditablePostfixTemplateWithConditions.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/EditablePostfixTemplateWithConditions.java new file mode 100644 index 000000000000..ef0013b5fdd1 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/EditablePostfixTemplateWithConditions.java @@ -0,0 +1,90 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.codeInsight.template.postfix.templates.editable; + +import com.intellij.codeInsight.template.impl.TemplateImpl; +import com.intellij.codeInsight.template.postfix.templates.PostfixTemplateProvider; +import com.intellij.openapi.util.Condition; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.Set; + +public abstract class EditablePostfixTemplateWithConditions> + extends EditablePostfixTemplate { + + @NotNull protected final Set myExpressionConditions; + protected final boolean myUseTopmostExpression; + + protected EditablePostfixTemplateWithConditions(@NotNull String templateId, + @NotNull String templateName, + @NotNull TemplateImpl liveTemplate, + @NotNull String example, + @NotNull Set expressionConditions, + boolean useTopmostExpression, + @NotNull PostfixTemplateProvider provider) { + super(templateId, templateName, liveTemplate, example, provider); + myExpressionConditions = expressionConditions; + myUseTopmostExpression = useTopmostExpression; + } + + + @NotNull + protected static TemplateImpl createTemplate(@NotNull String templateText) { + TemplateImpl template = new TemplateImpl("fakeKey", templateText, ""); + template.setToReformat(true); + template.parseSegments(); + return template; + } + + + @NotNull + @Override + protected PsiElement getElementToRemove(@NotNull PsiElement element) { + if (myUseTopmostExpression) { + return getTopmostExpression(element); + } + return element; + } + + @NotNull + protected abstract PsiElement getTopmostExpression(@NotNull PsiElement element); + + @NotNull + public Set getExpressionConditions() { + return myExpressionConditions; + } + + public boolean isUseTopmostExpression() { + return myUseTopmostExpression; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + EditablePostfixTemplateWithConditions that = (EditablePostfixTemplateWithConditions)o; + return myUseTopmostExpression == that.myUseTopmostExpression && + Objects.equals(myExpressionConditions, that.myExpressionConditions); + } + + @NotNull + protected Condition getExpressionCompositeCondition() { + return e -> { + for (ConditionType condition : myExpressionConditions) { + //noinspection unchecked + if (condition.value((ExpressionType)e)) { + return true; + } + } + return myExpressionConditions.isEmpty(); + }; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), myExpressionConditions, myUseTopmostExpression); + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/PostfixTemplateExpressionCondition.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/PostfixTemplateExpressionCondition.java new file mode 100644 index 000000000000..c8ef1f7519f4 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/PostfixTemplateExpressionCondition.java @@ -0,0 +1,33 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.codeInsight.template.postfix.templates.editable; + +import com.intellij.openapi.util.Condition; +import com.intellij.psi.PsiElement; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; + +public interface PostfixTemplateExpressionCondition extends Condition { + + String ID_ATTR = "id"; + /** + * + * @return + */ + @NotNull + String getPresentableName(); + + + /** + * @return id for serialization + */ + @NotNull + String getId(); + + boolean equals(Object o); + + int hashCode(); + + default void serializeTo(@NotNull Element element) { + element.setAttribute(ID_ATTR, getId()); + } +}