diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/PhpMethodVariableResolveUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/PhpMethodVariableResolveUtil.java index 622b806ab..fb1c070af 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/PhpMethodVariableResolveUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/PhpMethodVariableResolveUtil.java @@ -1,5 +1,6 @@ package fr.adrienbrault.idea.symfony2plugin.templating.util; +import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiRecursiveElementWalkingVisitor; import com.intellij.psi.util.PsiTreeUtil; @@ -171,11 +172,15 @@ private static Map collectOnVariableReferences(@NotNull Fun PsiElement parent = scopeVar.getParent(); if (parent instanceof ArrayAccessExpression) { // $template['variable'] = $foo - collectedTypes.putAll(getTypesOnArrayIndex((ArrayAccessExpression) parent)); + Pair pair = getTypesOnArrayIndex((ArrayAccessExpression) parent); + if (pair != null) { + collectedTypes.put(pair.getFirst(), pair.getSecond()); + } } else if (parent instanceof AssignmentExpression) { // array('foo' => $var) - if (((AssignmentExpression) parent).getValue() instanceof ArrayCreationExpression) { - collectedTypes.putAll(getTypesOnArrayHash((ArrayCreationExpression) ((AssignmentExpression) parent).getValue())); + PhpPsiElement value = ((AssignmentExpression) parent).getValue(); + if (value instanceof ArrayCreationExpression) { + collectedTypes.putAll(getTypesOnArrayHash((ArrayCreationExpression) value)); } } } @@ -186,10 +191,8 @@ private static Map collectOnVariableReferences(@NotNull Fun /** * $template['var'] = $foo */ - private static Map getTypesOnArrayIndex(ArrayAccessExpression arrayAccessExpression) { - - Map collectedTypes = new HashMap<>(); - + @Nullable + private static Pair getTypesOnArrayIndex(@NotNull ArrayAccessExpression arrayAccessExpression) { ArrayIndex arrayIndex = arrayAccessExpression.getIndex(); if(arrayIndex != null && arrayIndex.getValue() instanceof StringLiteralExpression) { @@ -203,28 +206,23 @@ private static Map getTypesOnArrayIndex(ArrayAccessExpressi variableTypes.addAll(((PhpTypedElement) arrayValue).getType().getTypes()); } - collectedTypes.put(variableName, new PsiVariable(variableTypes, ((AssignmentExpression) parent).getValue())); - + return Pair.create(variableName, new PsiVariable(variableTypes, ((AssignmentExpression) parent).getValue())); } else { - collectedTypes.put(variableName, new PsiVariable(variableTypes, null)); + return Pair.create(variableName, new PsiVariable(variableTypes)); } - - } - return collectedTypes; + return null; } /** * array('foo' => $var, 'bar' => $bar) */ - public static Map getTypesOnArrayHash(ArrayCreationExpression arrayCreationExpression) { - + public static Map getTypesOnArrayHash(@NotNull ArrayCreationExpression arrayCreationExpression) { Map collectedTypes = new HashMap<>(); for(ArrayHashElement arrayHashElement: arrayCreationExpression.getHashElements()) { if(arrayHashElement.getKey() instanceof StringLiteralExpression) { - String variableName = ((StringLiteralExpression) arrayHashElement.getKey()).getContents(); Set variableTypes = new HashSet<>(); @@ -233,7 +231,6 @@ public static Map getTypesOnArrayHash(ArrayCreationExpressi } collectedTypes.put(variableName, new PsiVariable(variableTypes, arrayHashElement.getValue())); - } } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigTypeResolveUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigTypeResolveUtil.java index 52c74fa33..80be58640 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigTypeResolveUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigTypeResolveUtil.java @@ -224,16 +224,6 @@ private static Map getInlineCommentDocsVars(@NotNull PsiElement return variables; } - private static Map> convertHashMapToTypeSet(Map hashMap) { - Map> globalVars = new HashMap<>(); - - for(final Map.Entry entry: hashMap.entrySet()) { - globalVars.put(entry.getKey(), new HashSet<>(Collections.singletonList(entry.getValue()))); - } - - return globalVars; - } - @NotNull public static Map collectScopeVariables(@NotNull PsiElement psiElement) { return collectScopeVariables(psiElement, new HashSet<>()); @@ -241,16 +231,15 @@ public static Map collectScopeVariables(@NotNull PsiElement @NotNull public static Map collectScopeVariables(@NotNull PsiElement psiElement, @NotNull Set visitedFiles) { - Map> globalVars = new HashMap<>(); - Map controllerVars = new HashMap<>(); - VirtualFile virtualFile = psiElement.getContainingFile().getVirtualFile(); if(visitedFiles.contains(virtualFile)) { - return controllerVars; + return Collections.emptyMap(); } visitedFiles.add(virtualFile); + Map controllerVars = new HashMap<>(); + TwigFileVariableCollectorParameter collectorParameter = new TwigFileVariableCollectorParameter(psiElement, visitedFiles); for(TwigFileVariableCollector collector: TWIG_FILE_VARIABLE_COLLECTORS.getExtensions()) { Map> globalVarsScope = new HashMap<>(); @@ -258,27 +247,43 @@ public static Map collectScopeVariables(@NotNull PsiElement // @TODO: resolve this in change extension point, so that its only possible to provide data and dont give full scope to break / overwrite other variables globalVarsScope.forEach((s, strings) -> { - globalVars.putIfAbsent(s, new HashSet<>()); - globalVars.get(s).addAll(strings); + controllerVars.putIfAbsent(s, new PsiVariable()); + controllerVars.get(s).addTypes(strings); }); - // @TODO: provide merge for multiple matches - collector.collectPsiVariables(collectorParameter, controllerVars); + // merging elements + Map controllerVars1 = new HashMap<>(); + collector.collectPsiVariables(collectorParameter, controllerVars1); + + controllerVars1.forEach((s, psiVariable) -> { + controllerVars.putIfAbsent(s, new PsiVariable()); + controllerVars.get(s).addTypes(psiVariable.getTypes()); + + PsiElement context = psiVariable.getElement(); + if (context != null) { + controllerVars.get(s).addElements(context); + } + }); } // globals first - globalVars.putAll(convertHashMapToTypeSet(findInlineStatementVariableDocBlock(psiElement, TwigElementTypes.BLOCK_STATEMENT, true))); - globalVars.putAll(convertHashMapToTypeSet(findInlineStatementVariableDocBlock(psiElement, TwigElementTypes.MACRO_STATEMENT, false))); - globalVars.putAll(convertHashMapToTypeSet(findInlineStatementVariableDocBlock(psiElement, TwigElementTypes.FOR_STATEMENT, false))); - - for(Map.Entry> entry: globalVars.entrySet()) { - Set types = entry.getValue(); + Collection> vars = Arrays.asList( + findInlineStatementVariableDocBlock(psiElement, TwigElementTypes.BLOCK_STATEMENT, true), + findInlineStatementVariableDocBlock(psiElement, TwigElementTypes.MACRO_STATEMENT, false), + findInlineStatementVariableDocBlock(psiElement, TwigElementTypes.FOR_STATEMENT, false) + ); - // collect iterator - types.addAll(collectIteratorReturns(psiElement, entry.getValue())); + for (Map entry : vars) { + entry.forEach((s, s2) -> { + controllerVars.putIfAbsent(s, new PsiVariable()); + controllerVars.get(s).addType(s2); + }); + } - // convert to variable model - controllerVars.put(entry.getKey(), new PsiVariable(types, null)); + // collect iterator + for(Map.Entry entry: controllerVars.entrySet()) { + PsiVariable psiVariable = entry.getValue(); + psiVariable.addTypes(collectIteratorReturns(psiElement, psiVariable.getTypes())); } // check if we are in "for" scope and resolve types ending with [] @@ -347,7 +352,7 @@ private static Collection collectForArrayScopeVariablesFoo(@NotNull Proj return previousElements; } - private static void collectForArrayScopeVariables(PsiElement psiElement, Map globalVars) { + private static void collectForArrayScopeVariables(@NotNull PsiElement psiElement, @NotNull Map globalVars) { PsiElement twigCompositeElement = PsiTreeUtil.findFirstParent(psiElement, psiElement1 -> { if (psiElement1 instanceof TwigCompositeElement) { if (PlatformPatterns.psiElement(TwigElementTypes.FOR_STATEMENT).accepts(psiElement1)) { @@ -412,12 +417,12 @@ private static void collectForArrayScopeVariables(PsiElement psiElement, Map types; + final private Set types = new HashSet<>(); - @Nullable - private PsiElement psiElement; + @NotNull + final private Collection psiElements = new HashSet<>(); public PsiVariable(@NotNull Set types, @Nullable PsiElement psiElement) { - this.types = types; - this.psiElement = psiElement; + this.types.addAll(types); + this.psiElements.add(psiElement); } public PsiVariable(@NotNull Set types) { - this.types = types; + this.types.addAll(types); } public PsiVariable(@NotNull String type) { - this.types = Collections.singleton(type); + this.types.add(type); + } + + public PsiVariable() { } @NotNull @@ -37,6 +41,22 @@ public Set getTypes() { @Nullable public PsiElement getElement() { - return psiElement; + if (psiElements.size() > 0) { + return psiElements.iterator().next(); + } + + return null; + } + + public void addElements(@NotNull PsiElement psiElement) { + this.psiElements.add(psiElement); + } + + public void addTypes(@NotNull Collection types) { + this.types.addAll(types); + } + + public void addType(@NotNull String type) { + this.types.add(type); } } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/twig/variable/collector/ServiceContainerGlobalVariableCollector.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/twig/variable/collector/ServiceContainerGlobalVariableCollector.java index df9b053c2..cc00fa07e 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/twig/variable/collector/ServiceContainerGlobalVariableCollector.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/twig/variable/collector/ServiceContainerGlobalVariableCollector.java @@ -11,10 +11,7 @@ import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @author Daniel Espendiller @@ -23,17 +20,21 @@ public class ServiceContainerGlobalVariableCollector implements TwigFileVariable @Override public void collect(@NotNull TwigFileVariableCollectorParameter parameter, @NotNull Map> variables) { + Map> map = new HashMap<>(); + TwigGlobalsServiceParser twigPathServiceParser = ServiceXmlParserFactory.getInstance(parameter.getProject(), TwigGlobalsServiceParser.class); for(Map.Entry globalVariableEntry: twigPathServiceParser.getTwigGlobals().entrySet()) { if(globalVariableEntry.getValue().getTwigGlobalEnum() == TwigGlobalEnum.SERVICE) { String serviceName = globalVariableEntry.getValue().getValue(); PhpClass phpClass = ServiceUtil.getServiceClass(parameter.getProject(), serviceName); if(phpClass != null) { - variables.put(globalVariableEntry.getKey(), new HashSet<>(Collections.singletonList( - StringUtils.stripStart(phpClass.getFQN(), "\\") - ))); + String key = globalVariableEntry.getKey(); + map.putIfAbsent(key, new HashSet<>()); + map.get(key).add("\\" + StringUtils.stripStart(phpClass.getFQN(), "\\")); } } } + + variables.putAll(map); } }