From db6312e5cddf2734984ff7d57a93e924f1cfe23a Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 1 Feb 2016 17:06:19 +0100 Subject: [PATCH] allow same-named template macros for different languages (IDEA-145973) --- .../codeInsight/template/MacroParserTest.java | 4 +- .../template/ExpressionContext.java | 7 ++++ .../template/impl/EditVariableDialog.java | 25 +++++------- .../template/impl/MacroCallNode.java | 35 ++++++++++------- .../template/impl/MacroParser.java | 8 ++-- .../template/impl/TemplateState.java | 18 ++++++++- .../template/macro/MacroFactory.java | 38 +++++++------------ 7 files changed, 73 insertions(+), 62 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/template/MacroParserTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/template/MacroParserTest.java index 18655337b98e..d05067b23d0c 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/template/MacroParserTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/template/MacroParserTest.java @@ -19,7 +19,7 @@ public class MacroParserTest extends LightIdeaTestCase { Expression e = MacroParser.parse(" variableOfType( \"java.util.Collection\" ) "); assertTrue(e instanceof MacroCallNode); MacroCallNode n = (MacroCallNode) e; - assertTrue(n.getMacro() instanceof VariableOfTypeMacro); + assertTrue(n.getMacro(new TemplateContextType[0]) instanceof VariableOfTypeMacro); Expression[] parameters = n.getParameters(); assertEquals(1, parameters.length); assertTrue(parameters [0] instanceof ConstantNode); @@ -46,7 +46,7 @@ public class MacroParserTest extends LightIdeaTestCase { Expression e = MacroParser.parse("variableOfType(\"A\", \"B\")"); assertTrue(e instanceof MacroCallNode); MacroCallNode n = (MacroCallNode) e; - assertTrue(n.getMacro() instanceof VariableOfTypeMacro); + assertTrue(n.getMacro(new TemplateContextType[0]) instanceof VariableOfTypeMacro); Expression[] parameters = n.getParameters(); assertEquals(2, parameters.length); assertTrue(parameters [0] instanceof ConstantNode); diff --git a/platform/lang-api/src/com/intellij/codeInsight/template/ExpressionContext.java b/platform/lang-api/src/com/intellij/codeInsight/template/ExpressionContext.java index b86edbedbe1f..ced75119175d 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/template/ExpressionContext.java +++ b/platform/lang-api/src/com/intellij/codeInsight/template/ExpressionContext.java @@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public interface ExpressionContext { @@ -36,5 +37,11 @@ public interface ExpressionContext { T getProperty(Key key); @Nullable PsiElement getPsiElementAtStartOffset(); + + /** + * @return all template context types matching the template invocation place + */ + @NotNull + TemplateContextType[] getCompatibleContexts(); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditVariableDialog.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditVariableDialog.java index 8cb9ab24a98b..e33d8ea5b5cc 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditVariableDialog.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditVariableDialog.java @@ -40,6 +40,9 @@ import javax.swing.table.TableModel; import java.awt.*; import java.util.*; import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; class EditVariableDialog extends DialogWrapper { private ArrayList myVariables = new ArrayList(); @@ -108,23 +111,13 @@ class EditVariableDialog extends DialogWrapper { myTable.getSelectionModel().setSelectionInterval(0, 0); } + Predicate isAcceptableInContext = macro -> myContextTypes.stream().anyMatch(macro::isAcceptableInContext); + Stream availableMacroNames = Arrays.stream(MacroFactory.getMacros()).filter(isAcceptableInContext).map(Macro::getPresentableName).sorted(); + Set uniqueNames = availableMacroNames.collect(Collectors.toCollection(LinkedHashSet::new)); + ComboBox comboField = new ComboBox(); - Macro[] macros = MacroFactory.getMacros(); - Arrays.sort(macros, new Comparator () { - @Override - public int compare(@NotNull Macro m1, @NotNull Macro m2) { - return m1.getPresentableName().compareTo(m2.getPresentableName()); - } - }); - eachMacro: - for (Macro macro : macros) { - for (TemplateContextType contextType : myContextTypes) { - if (macro.isAcceptableInContext(contextType)) { - comboField.addItem(macro.getPresentableName()); - continue eachMacro; - } - } - } + uniqueNames.forEach(comboField::addItem); + comboField.setEditable(true); DefaultCellEditor cellEditor = new DefaultCellEditor(comboField); cellEditor.setClickCountToStart(1); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/MacroCallNode.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/MacroCallNode.java index cd2a3ccfcc35..b11356ad0426 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/MacroCallNode.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/MacroCallNode.java @@ -17,46 +17,53 @@ package com.intellij.codeInsight.template.impl; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.template.Expression; -import com.intellij.codeInsight.template.ExpressionContext; -import com.intellij.codeInsight.template.Macro; -import com.intellij.codeInsight.template.Result; +import com.intellij.codeInsight.template.*; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; public class MacroCallNode extends Expression { - public Macro getMacro() { - return myMacro; - } - - private final Macro myMacro; - private final ArrayList myParameters = new ArrayList(); + private final List myMacros; + private final ArrayList myParameters = new ArrayList<>(); public MacroCallNode(@NotNull Macro macro) { - myMacro = macro; + this(Collections.singletonList(macro)); + } + + public MacroCallNode(List macros) { + myMacros = macros; + assert macros.size() > 0; } public void addParameter(Expression node) { myParameters.add(node); } + public Macro getMacro(TemplateContextType[] context) { + Predicate isAcceptableInContext = macro -> Arrays.stream(context).anyMatch(macro::isAcceptableInContext); + return myMacros.stream().filter(isAcceptableInContext).findFirst().orElse(myMacros.get(0)); + } + @Override public Result calculateResult(ExpressionContext context) { Expression[] parameters = myParameters.toArray(new Expression[myParameters.size()]); - return myMacro.calculateResult(parameters, context); + return getMacro(context.getCompatibleContexts()).calculateResult(parameters, context); } @Override public Result calculateQuickResult(ExpressionContext context) { Expression[] parameters = myParameters.toArray(new Expression[myParameters.size()]); - return myMacro.calculateQuickResult(parameters, context); + return getMacro(context.getCompatibleContexts()).calculateQuickResult(parameters, context); } @Override public LookupElement[] calculateLookupItems(ExpressionContext context) { Expression[] parameters = myParameters.toArray(new Expression[myParameters.size()]); - return myMacro.calculateLookupItems(parameters, context); + return getMacro(context.getCompatibleContexts()).calculateLookupItems(parameters, context); } public Expression[] getParameters() { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/MacroParser.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/MacroParser.java index 7b41550ee73c..d6f0ac72b766 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/MacroParser.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/MacroParser.java @@ -27,6 +27,8 @@ import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + @VisibleForTesting public class MacroParser { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.template.impl.MacroParser"); @@ -76,13 +78,13 @@ public class MacroParser { return new ConstantNode(""); } - Macro macro = MacroFactory.createMacro(token); - if (macro == null) { + List macros = MacroFactory.getMacros(token); + if (macros.isEmpty()) { return parseVariable(lexer, expression); } advance(lexer); - MacroCallNode macroCallNode = new MacroCallNode(macro); + MacroCallNode macroCallNode = new MacroCallNode(macros); if (lexer.getTokenType() == null) { return macroCallNode; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java index b3d8c5f2eec5..c64329f0e1c4 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java @@ -99,11 +99,19 @@ public class TemplateState implements Disposable { @Nullable private PairProcessor myProcessor; private boolean mySelectionCalculated = false; private boolean myStarted; + private final TemplateContextType[] myCompatibleContexts; - public TemplateState(@NotNull Project project, final Editor editor) { + TemplateState(@NotNull Project project, @NotNull final Editor editor) { myProject = project; myEditor = editor; myDocument = myEditor.getDocument(); + + PsiFile file = getPsiFile(); + if (file != null) { + myCompatibleContexts = TemplateManagerImpl.getApplicableContextTypes(file, editor.getCaretModel().getOffset()).toArray(new TemplateContextType[0]); + } else { + myCompatibleContexts = new TemplateContextType[0]; + } } private void initListeners() { @@ -927,6 +935,12 @@ public class TemplateState implements Disposable { PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); return file == null ? null : file.findElementAt(offset); } + + @NotNull + @Override + public TemplateContextType[] getCompatibleContexts() { + return myCompatibleContexts; + } }; } @@ -1071,7 +1085,7 @@ public class TemplateState implements Disposable { Expression e = myTemplate.getExpressionAt(j); @NonNls String marker = "a"; if (e instanceof MacroCallNode) { - marker = ((MacroCallNode)e).getMacro().getDefaultValue(); + marker = ((MacroCallNode)e).getMacro(myCompatibleContexts).getDefaultValue(); } replaceString(marker, mySegments.getSegmentStart(i), mySegments.getSegmentEnd(i), i); indices.add(i); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/macro/MacroFactory.java b/platform/lang-impl/src/com/intellij/codeInsight/template/macro/MacroFactory.java index c4aaa40d9bdd..0d13fcc88474 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/macro/MacroFactory.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/macro/MacroFactory.java @@ -18,48 +18,36 @@ package com.intellij.codeInsight.template.macro; import com.intellij.codeInsight.template.Macro; import com.intellij.openapi.extensions.Extensions; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NonNls; import java.util.Collection; -import java.util.HashMap; +import java.util.List; public class MacroFactory { - private static HashMap myMacroTable = null; - - private MacroFactory() { - } + private static final MultiMap myMacroTable = init(); public static Macro createMacro(@NonNls String name) { - if(myMacroTable == null) { - init(); - } + return ContainerUtil.getFirstItem(myMacroTable.get(name)); + } - return myMacroTable.get(name); + public static List getMacros(@NonNls String name) { + return (List)myMacroTable.get(name); } public static Macro[] getMacros() { - if(myMacroTable == null) { - init(); - } - - final Collection values = myMacroTable.values(); + final Collection values = myMacroTable.values(); return values.toArray(new Macro[values.size()]); } - private static void init() { - myMacroTable = new HashMap(); - + private static MultiMap init() { + MultiMap result = MultiMap.create(); for(Macro macro: Extensions.getExtensions(Macro.EP_NAME)) { - myMacroTable.put(macro.getName(), macro); + result.putValue(macro.getName(), macro); } + return result; } - /** - * @deprecated use com.intellij.liveTemplateMacro extension point instead - */ - public static void register(Macro macro) { - if (myMacroTable == null) init(); - myMacroTable.put(macro.getName(), macro); - } }