allow same-named template macros for different languages (IDEA-145973)

This commit is contained in:
peter
2016-02-01 18:16:21 +01:00
parent 7bad4583a4
commit db6312e5cd
7 changed files with 73 additions and 62 deletions
@@ -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);
@@ -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> T getProperty(Key<T> key);
@Nullable
PsiElement getPsiElementAtStartOffset();
/**
* @return all template context types matching the template invocation place
*/
@NotNull
TemplateContextType[] getCompatibleContexts();
}
@@ -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<Variable> myVariables = new ArrayList<Variable>();
@@ -108,23 +111,13 @@ class EditVariableDialog extends DialogWrapper {
myTable.getSelectionModel().setSelectionInterval(0, 0);
}
Predicate<Macro> isAcceptableInContext = macro -> myContextTypes.stream().anyMatch(macro::isAcceptableInContext);
Stream<String> availableMacroNames = Arrays.stream(MacroFactory.getMacros()).filter(isAcceptableInContext).map(Macro::getPresentableName).sorted();
Set<String> uniqueNames = availableMacroNames.collect(Collectors.toCollection(LinkedHashSet::new));
ComboBox comboField = new ComboBox();
Macro[] macros = MacroFactory.getMacros();
Arrays.sort(macros, new Comparator<Macro> () {
@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);
@@ -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<Expression> myParameters = new ArrayList<Expression>();
private final List<Macro> myMacros;
private final ArrayList<Expression> myParameters = new ArrayList<>();
public MacroCallNode(@NotNull Macro macro) {
myMacro = macro;
this(Collections.singletonList(macro));
}
public MacroCallNode(List<Macro> macros) {
myMacros = macros;
assert macros.size() > 0;
}
public void addParameter(Expression node) {
myParameters.add(node);
}
public Macro getMacro(TemplateContextType[] context) {
Predicate<Macro> 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() {
@@ -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<Macro> 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;
}
@@ -99,11 +99,19 @@ public class TemplateState implements Disposable {
@Nullable private PairProcessor<String, String> 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);
@@ -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<String,Macro> myMacroTable = null;
private MacroFactory() {
}
private static final MultiMap<String, Macro> 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<Macro> getMacros(@NonNls String name) {
return (List<Macro>)myMacroTable.get(name);
}
public static Macro[] getMacros() {
if(myMacroTable == null) {
init();
}
final Collection<Macro> values = myMacroTable.values();
final Collection<? extends Macro> values = myMacroTable.values();
return values.toArray(new Macro[values.size()]);
}
private static void init() {
myMacroTable = new HashMap<String, Macro>();
private static MultiMap<String, Macro> init() {
MultiMap<String, Macro> 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);
}
}