Skip to content

Commit 6d98ca8

Browse files
committed
mass refactoring of twig type completion to not lose origin psielement source, add additional resolver event to provide non psi elements
1 parent 8bccb54 commit 6d98ca8

11 files changed

+173
-35
lines changed

src/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateCompletionContributor.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import com.jetbrains.php.lang.psi.elements.Field;
1515
import com.jetbrains.php.lang.psi.elements.Method;
1616
import com.jetbrains.php.lang.psi.elements.PhpClass;
17-
import com.jetbrains.php.lang.psi.elements.PhpNamedElement;
1817
import com.jetbrains.php.lang.psi.stubs.indexes.PhpClassIndex;
1918
import com.jetbrains.twig.TwigFile;
2019
import com.jetbrains.twig.TwigLanguage;
@@ -35,6 +34,7 @@
3534
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
3635
import fr.adrienbrault.idea.symfony2plugin.templating.variable.TwigTypeContainer;
3736
import fr.adrienbrault.idea.symfony2plugin.templating.variable.collector.ControllerDocVariableCollector;
37+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.dict.PsiVariable;
3838
import fr.adrienbrault.idea.symfony2plugin.translation.TranslationIndex;
3939
import fr.adrienbrault.idea.symfony2plugin.translation.TranslatorLookupElement;
4040
import fr.adrienbrault.idea.symfony2plugin.translation.parser.TranslationStringMap;
@@ -253,8 +253,8 @@ public void addCompletions(@NotNull CompletionParameters parameters,
253253
resultSet.addElement(LookupElementBuilder.create(twigSet.getName()).withTypeText("set"));
254254
}
255255

256-
for(Map.Entry<String, Set<String>> entry: TwigTypeResolveUtil.collectScopeVariables(parameters.getOriginalPosition()).entrySet()) {
257-
resultSet.addElement(LookupElementBuilder.create(entry.getKey()).withTypeText(TwigTypeResolveUtil.getTypeDisplayName(psiElement.getProject(), entry.getValue())).withIcon(PhpIcons.CLASS));
256+
for(Map.Entry<String, PsiVariable> entry: TwigTypeResolveUtil.collectScopeVariables(parameters.getOriginalPosition()).entrySet()) {
257+
resultSet.addElement(LookupElementBuilder.create(entry.getKey()).withTypeText(TwigTypeResolveUtil.getTypeDisplayName(psiElement.getProject(), entry.getValue().getTypes())).withIcon(PhpIcons.CLASS));
258258
}
259259

260260
for(Map.Entry<String, TwigGlobalVariable> entry: ServiceXmlParserFactory.getInstance(psiElement.getProject(), TwigGlobalsServiceParser.class).getTwigGlobals().entrySet()) {
@@ -285,8 +285,8 @@ public void addCompletions(@NotNull CompletionParameters parameters,
285285
return;
286286
}
287287

288-
for(Map.Entry<String, Set<String>> entry: TwigTypeResolveUtil.collectScopeVariables(parameters.getOriginalPosition()).entrySet()) {
289-
resultSet.addElement(LookupElementBuilder.create(entry.getKey()).withTypeText(TwigTypeResolveUtil.getTypeDisplayName(psiElement.getProject(), entry.getValue())).withIcon(PhpIcons.CLASS));
288+
for(Map.Entry<String, PsiVariable> entry: TwigTypeResolveUtil.collectScopeVariables(parameters.getOriginalPosition()).entrySet()) {
289+
resultSet.addElement(LookupElementBuilder.create(entry.getKey()).withTypeText(TwigTypeResolveUtil.getTypeDisplayName(psiElement.getProject(), entry.getValue().getTypes())).withIcon(PhpIcons.CLASS));
290290
}
291291

292292
}
@@ -470,6 +470,10 @@ protected void addCompletions(@NotNull CompletionParameters parameters, Processi
470470
}
471471

472472
}
473+
474+
if(twigTypeContainer.getStringElement() != null) {
475+
resultSet.addElement(LookupElementBuilder.create(twigTypeContainer.getStringElement()));
476+
}
473477
}
474478

475479
}

src/fr/adrienbrault/idea/symfony2plugin/templating/util/PhpMethodVariableResolveUtil.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.jetbrains.php.lang.psi.PhpPsiUtil;
99
import com.jetbrains.php.lang.psi.elements.*;
1010
import fr.adrienbrault.idea.symfony2plugin.Symfony2InterfacesUtil;
11+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.dict.PsiVariable;
1112
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
1213

1314
import java.util.*;
@@ -17,9 +18,9 @@ public class PhpMethodVariableResolveUtil {
1718
/**
1819
* search for twig template variable on common use cases
1920
*/
20-
public static HashMap<String, Set<String>> collectMethodVariables(Method method) {
21+
public static HashMap<String, PsiVariable> collectMethodVariables(Method method) {
2122

22-
HashMap<String, Set<String>> collectedTypes = new HashMap<String, Set<String>>();
23+
HashMap<String, PsiVariable> collectedTypes = new HashMap<String, PsiVariable>();
2324

2425
ArrayList<PsiElement> psiElements = collectPossibleTemplateArrays(method);
2526
for(PsiElement templateVariablePsi: psiElements) {
@@ -87,9 +88,9 @@ private static ArrayList<PsiElement> collectPossibleTemplateArrays(Method method
8788
* @param searchScope should be method scope
8889
* @param variable the variable declaration psi $var = array();
8990
*/
90-
private static HashMap<String, Set<String>> collectOnVariableReferences(SearchScope searchScope, Variable variable) {
91+
private static HashMap<String, PsiVariable> collectOnVariableReferences(SearchScope searchScope, Variable variable) {
9192

92-
final HashMap<String, Set<String>> collectedTypes = new HashMap<String, Set<String>>();
93+
final HashMap<String, PsiVariable> collectedTypes = new HashMap<String, PsiVariable>();
9394

9495
PhpPsiUtil.hasReferencesInSearchScope(searchScope, variable, new CommonProcessors.FindProcessor<PsiReference>() {
9596
@Override
@@ -120,9 +121,9 @@ protected boolean accept(PsiReference psiReference) {
120121
/**
121122
* $template['var'] = $foo
122123
*/
123-
private static HashMap<String, Set<String>> getTypesOnArrayIndex(ArrayAccessExpression arrayAccessExpression) {
124+
private static Map<String, PsiVariable> getTypesOnArrayIndex(ArrayAccessExpression arrayAccessExpression) {
124125

125-
HashMap<String, Set<String>> collectedTypes = new HashMap<String, Set<String>>();
126+
HashMap<String, PsiVariable> collectedTypes = new HashMap<String, PsiVariable>();
126127

127128
ArrayIndex arrayIndex = arrayAccessExpression.getIndex();
128129
if(arrayIndex != null && arrayIndex.getValue() instanceof StringLiteralExpression) {
@@ -137,7 +138,7 @@ private static HashMap<String, Set<String>> getTypesOnArrayIndex(ArrayAccessExpr
137138
}
138139
}
139140

140-
collectedTypes.put(variableName, variableTypes);
141+
collectedTypes.put(variableName, new PsiVariable(variableTypes, ((AssignmentExpression) arrayAccessExpression.getParent()).getValue()));
141142

142143
}
143144

@@ -147,8 +148,9 @@ private static HashMap<String, Set<String>> getTypesOnArrayIndex(ArrayAccessExpr
147148
/**
148149
* array('foo' => $var, 'bar' => $bar)
149150
*/
150-
public static HashMap<String, Set<String>> getTypesOnArrayHash(ArrayCreationExpression arrayCreationExpression) {
151-
HashMap<String, Set<String>> collectedTypes = new HashMap<String, Set<String>>();
151+
public static Map<String, PsiVariable> getTypesOnArrayHash(ArrayCreationExpression arrayCreationExpression) {
152+
153+
HashMap<String, PsiVariable> collectedTypes = new HashMap<String, PsiVariable>();
152154

153155
for(ArrayHashElement arrayHashElement: arrayCreationExpression.getHashElements()) {
154156
if(arrayHashElement.getKey() instanceof StringLiteralExpression) {
@@ -160,7 +162,7 @@ public static HashMap<String, Set<String>> getTypesOnArrayHash(ArrayCreationExpr
160162
variableTypes = ((PhpTypedElement) arrayHashElement.getValue()).getType().getTypes();
161163
}
162164

163-
collectedTypes.put(variableName, variableTypes);
165+
collectedTypes.put(variableName, new PsiVariable(variableTypes, arrayHashElement.getValue()));
164166

165167
}
166168
}

src/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigTypeResolveUtil.java

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
import fr.adrienbrault.idea.symfony2plugin.templating.variable.TwigFileVariableCollectorParameter;
2323
import fr.adrienbrault.idea.symfony2plugin.templating.variable.TwigTypeContainer;
2424
import fr.adrienbrault.idea.symfony2plugin.templating.variable.collector.*;
25+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.dict.PsiVariable;
26+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.resolver.FormFieldResolver;
27+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.resolver.FormVarsResolver;
28+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.resolver.TwigTypeResolver;
2529
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
2630
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
2731
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper;
2832
import org.apache.commons.lang.StringUtils;
29-
import org.jetbrains.annotations.Nullable;
3033

3134
import java.util.*;
3235
import java.util.regex.Matcher;
@@ -46,6 +49,11 @@ public class TwigTypeResolveUtil {
4649
new ControllerVariableCollector(),
4750
};
4851

52+
private static TwigTypeResolver[] twigTypeResolvers = new TwigTypeResolver[] {
53+
new FormVarsResolver(),
54+
new FormFieldResolver(),
55+
};
56+
4957
public static String[] formatPsiTypeName(PsiElement psiElement, boolean includeCurrent) {
5058
ArrayList<String> strings = new ArrayList<String>(Arrays.asList(formatPsiTypeName(psiElement)));
5159
strings.add(psiElement.getText());
@@ -81,12 +89,23 @@ public static Collection<TwigTypeContainer> resolveTwigMethodName(PsiElement psi
8189

8290
Collection<? extends PhpNamedElement> rootVariable = getRootVariableByName(psiElement, typeName[0]);
8391
if(typeName.length == 1) {
84-
return TwigTypeContainer.fromCollection(rootVariable);
92+
93+
Collection<TwigTypeContainer> twigTypeContainers = TwigTypeContainer.fromCollection(rootVariable);
94+
95+
for(TwigTypeResolver twigTypeResolver: twigTypeResolvers) {
96+
twigTypeResolver.resolve(twigTypeContainers, twigTypeContainers, typeName[0], new ArrayList<List<TwigTypeContainer>>());
97+
}
98+
99+
return twigTypeContainers;
85100
}
86101

87102
Collection<TwigTypeContainer> type = TwigTypeContainer.fromCollection(rootVariable);
103+
Collection<List<TwigTypeContainer>> previousElements = new ArrayList<List<TwigTypeContainer>> ();
104+
previousElements.add(new ArrayList<TwigTypeContainer>(type));
105+
88106
for (int i = 1; i <= typeName.length - 1; i++ ) {
89-
type = resolveTwigMethodName(type, typeName[i]);
107+
type = resolveTwigMethodName(type, typeName[i], previousElements);
108+
previousElements.add(new ArrayList<TwigTypeContainer>(type));
90109

91110
// we can stop on empty list
92111
if(type.size() == 0) {
@@ -95,7 +114,7 @@ public static Collection<TwigTypeContainer> resolveTwigMethodName(PsiElement psi
95114

96115
}
97116

98-
return TwigTypeContainer.fromCollection(rootVariable);
117+
return type;
99118
}
100119

101120
/**
@@ -165,26 +184,36 @@ private static HashMap<String, Set<String>> convertHashMapToTypeSet(HashMap<Stri
165184
return globalVars;
166185
}
167186

168-
public static HashMap<String, Set<String>> collectScopeVariables(PsiElement psiElement) {
187+
public static HashMap<String, PsiVariable> collectScopeVariables(PsiElement psiElement) {
169188

170189
HashMap<String, Set<String>> globalVars = new HashMap<String, Set<String>>();
190+
HashMap<String, PsiVariable> controllerVars = new HashMap<String, PsiVariable>();
171191

172192
TwigFileVariableCollectorParameter collectorParameter = new TwigFileVariableCollectorParameter(psiElement);
173193
for(TwigFileVariableCollector collector: twigFileVariableCollectors) {
174194
collector.collect(collectorParameter, globalVars);
195+
196+
if(collector instanceof TwigFileVariableCollector.TwigFileVariableCollectorExt) {
197+
((TwigFileVariableCollector.TwigFileVariableCollectorExt) collector).collectVars(collectorParameter, controllerVars);
198+
}
199+
175200
}
176201

177202
// globals first
178203
globalVars.putAll(convertHashMapToTypeSet(findInlineStatementVariableDocBlock(psiElement, TwigElementTypes.BLOCK_STATEMENT)));
179204
globalVars.putAll(convertHashMapToTypeSet(findInlineStatementVariableDocBlock(psiElement, TwigElementTypes.FOR_STATEMENT)));
180205

181206
// check if we are in "for" scope and resolve types ending with []
182-
collectForArrayScopeVariables(psiElement, globalVars);
207+
collectForArrayScopeVariables(psiElement, controllerVars);
183208

184-
return globalVars;
209+
for(Map.Entry<String, Set<String>> entry: globalVars.entrySet()) {
210+
controllerVars.put(entry.getKey(), new PsiVariable(entry.getValue(), null));
211+
}
212+
213+
return controllerVars;
185214
}
186215

187-
private static void collectForArrayScopeVariables(PsiElement psiElement, HashMap<String, Set<String>> globalVars) {
216+
private static void collectForArrayScopeVariables(PsiElement psiElement, HashMap<String, PsiVariable> globalVars) {
188217

189218
PsiElement twigCompositeElement = PsiTreeUtil.findFirstParent(psiElement, new Condition<PsiElement>() {
190219
@Override
@@ -226,24 +255,24 @@ public boolean value(PsiElement psiElement) {
226255
Set<String> types = new HashSet<String>();
227256

228257
PhpType phpType = new PhpType();
229-
phpType.add(globalVars.get(variableName));
258+
phpType.add(globalVars.get(variableName).getTypes());
230259

231260
for(String arrayType: PhpIndex.getInstance(psiElement.getProject()).completeType(psiElement.getProject(), phpType, new HashSet<String>()).getTypes()) {
232261
if(arrayType.endsWith("[]")) {
233262
types.add(arrayType.substring(0, arrayType.length() -2));
234263
}
235264
}
236265

237-
globalVars.put(scopeVariable, types);
266+
globalVars.put(scopeVariable, new PsiVariable(types));
238267

239268
}
240269

241270
private static Collection<? extends PhpNamedElement> getRootVariableByName(PsiElement psiElement, String variableName) {
242271

243272
ArrayList<PhpNamedElement> phpNamedElements = new ArrayList<PhpNamedElement>();
244-
for(Map.Entry<String, Set<String>> variable : collectScopeVariables(psiElement).entrySet()) {
273+
for(Map.Entry<String, PsiVariable> variable : collectScopeVariables(psiElement).entrySet()) {
245274
if(variable.getKey().equals(variableName)) {
246-
phpNamedElements.addAll(PhpElementsUtil.getClassFromPhpTypeSet(psiElement.getProject(), variable.getValue()));
275+
phpNamedElements.addAll(PhpElementsUtil.getClassFromPhpTypeSet(psiElement.getProject(), variable.getValue().getTypes()));
247276
}
248277

249278
}
@@ -252,7 +281,7 @@ private static Collection<? extends PhpNamedElement> getRootVariableByName(PsiEl
252281

253282
}
254283

255-
private static Collection<TwigTypeContainer> resolveTwigMethodName(Collection<TwigTypeContainer> previousElement, String typeName) {
284+
private static Collection<TwigTypeContainer> resolveTwigMethodName(Collection<TwigTypeContainer> previousElement, String typeName, Collection<List<TwigTypeContainer>> twigTypeContainer) {
256285

257286
ArrayList<TwigTypeContainer> phpNamedElements = new ArrayList<TwigTypeContainer>();
258287

@@ -270,6 +299,10 @@ private static Collection<TwigTypeContainer> resolveTwigMethodName(Collection<Tw
270299
}
271300
}
272301

302+
for(TwigTypeResolver twigTypeResolver: twigTypeResolvers) {
303+
twigTypeResolver.resolve(phpNamedElements, previousElement, typeName, twigTypeContainer);
304+
}
305+
273306
}
274307

275308
return phpNamedElements;

src/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigUtil.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigMacro;
1818
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigMarcoParser;
1919
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigSet;
20+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.dict.PsiVariable;
2021
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
2122
import fr.adrienbrault.idea.symfony2plugin.util.SymfonyBundleUtil;
2223
import fr.adrienbrault.idea.symfony2plugin.util.dict.SymfonyBundle;
@@ -283,9 +284,9 @@ public static Method findTwigFileController(TwigFile twigFile) {
283284

284285
}
285286

286-
public static HashMap<String, Set<String>> collectControllerTemplateVariables(PsiElement psiElement) {
287+
public static HashMap<String, PsiVariable> collectControllerTemplateVariables(PsiElement psiElement) {
287288

288-
HashMap<String, Set<String>> vars = new HashMap<String, Set<String>>();
289+
HashMap<String, PsiVariable> vars = new HashMap<String, PsiVariable>();
289290

290291
PsiFile psiFile = psiElement.getContainingFile();
291292
if(!(psiFile instanceof TwigFile)) {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package fr.adrienbrault.idea.symfony2plugin.templating.variable;
22

3+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.dict.PsiVariable;
4+
35
import java.util.HashMap;
46
import java.util.Set;
57

68
public interface TwigFileVariableCollector {
79

810
public void collect(TwigFileVariableCollectorParameter parameter, HashMap<String, Set<String>> variables);
911

12+
public interface TwigFileVariableCollectorExt {
13+
public void collectVars(TwigFileVariableCollectorParameter parameter, HashMap<String, PsiVariable> variables);
14+
}
15+
1016
}

src/fr/adrienbrault/idea/symfony2plugin/templating/variable/collector/ControllerDocVariableCollector.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import fr.adrienbrault.idea.symfony2plugin.templating.util.PhpMethodVariableResolveUtil;
99
import fr.adrienbrault.idea.symfony2plugin.templating.variable.TwigFileVariableCollector;
1010
import fr.adrienbrault.idea.symfony2plugin.templating.variable.TwigFileVariableCollectorParameter;
11+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.dict.PsiVariable;
1112
import fr.adrienbrault.idea.symfony2plugin.util.controller.ControllerIndex;
1213

1314
import java.util.ArrayList;
@@ -16,13 +17,13 @@
1617
import java.util.regex.Matcher;
1718
import java.util.regex.Pattern;
1819

19-
public class ControllerDocVariableCollector implements TwigFileVariableCollector {
20+
public class ControllerDocVariableCollector implements TwigFileVariableCollector, TwigFileVariableCollector.TwigFileVariableCollectorExt {
2021

2122
public static String DOC_PATTERN = "\\{#[\\s]+@[C|c]ontroller[\\s]+([\\w\\\\\\[\\]:]+)[\\s]+#}";
2223
public static String DOC_PATTERN_COMPLETION = "\\{#[\\s]+@[C|c]ontroller[\\s]+.*#}";
2324

2425
@Override
25-
public void collect(TwigFileVariableCollectorParameter parameter, HashMap<String, Set<String>> variables) {
26+
public void collectVars(TwigFileVariableCollectorParameter parameter, HashMap<String, PsiVariable> variables) {
2627

2728
PsiFile psiFile = parameter.getElement().getContainingFile();
2829
if(!(psiFile instanceof TwigFile)) {
@@ -61,4 +62,8 @@ private static ArrayList<String> findFileControllerDocBlocks(TwigFile twigFile)
6162
return controller;
6263
}
6364

65+
@Override
66+
public void collect(TwigFileVariableCollectorParameter parameter, HashMap<String, Set<String>> variables) {
67+
68+
}
6469
}

src/fr/adrienbrault/idea/symfony2plugin/templating/variable/collector/ControllerVariableCollector.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@
33
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
44
import fr.adrienbrault.idea.symfony2plugin.templating.variable.TwigFileVariableCollector;
55
import fr.adrienbrault.idea.symfony2plugin.templating.variable.TwigFileVariableCollectorParameter;
6+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.dict.PsiVariable;
67

78
import java.util.HashMap;
89
import java.util.Set;
910

10-
public class ControllerVariableCollector implements TwigFileVariableCollector {
11+
public class ControllerVariableCollector implements TwigFileVariableCollector, TwigFileVariableCollector.TwigFileVariableCollectorExt {
1112

1213
@Override
1314
public void collect(TwigFileVariableCollectorParameter parameter, HashMap<String, Set<String>> variables) {
15+
//variables.putAll(TwigUtil.collectControllerTemplateVariables(parameter.getElement()));
16+
}
17+
18+
@Override
19+
public void collectVars(TwigFileVariableCollectorParameter parameter, HashMap<String, PsiVariable> variables) {
1420
variables.putAll(TwigUtil.collectControllerTemplateVariables(parameter.getElement()));
1521
}
1622

0 commit comments

Comments
 (0)