Skip to content

Commit c47a2dd

Browse files
authored
Merge pull request #1786 from Haehnchen/feature/for-incomplete
provide incomplete "for" completion for Twig based on variable scope
2 parents a92dad6 + 21382a2 commit c47a2dd

File tree

1 file changed

+90
-1
lines changed

1 file changed

+90
-1
lines changed

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

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
import com.intellij.codeInsight.completion.*;
44
import com.intellij.codeInsight.lookup.LookupElement;
55
import com.intellij.codeInsight.lookup.LookupElementBuilder;
6+
import com.intellij.codeInsight.lookup.LookupElementPresentation;
67
import com.intellij.openapi.project.Project;
78
import com.intellij.openapi.util.Pair;
8-
import com.intellij.patterns.*;
9+
import com.intellij.openapi.util.text.StringUtil;
10+
import com.intellij.patterns.PatternCondition;
11+
import com.intellij.patterns.PlatformPatterns;
12+
import com.intellij.patterns.StandardPatterns;
913
import com.intellij.psi.PsiElement;
1014
import com.intellij.psi.PsiFile;
1115
import com.intellij.psi.PsiWhiteSpace;
@@ -16,6 +20,8 @@
1620
import com.jetbrains.php.lang.psi.elements.Field;
1721
import com.jetbrains.php.lang.psi.elements.Method;
1822
import com.jetbrains.php.lang.psi.elements.PhpClass;
23+
import com.jetbrains.php.lang.psi.elements.PhpTypedElement;
24+
import com.jetbrains.php.lang.psi.resolve.types.PhpType;
1925
import com.jetbrains.twig.TwigTokenTypes;
2026
import com.jetbrains.twig.elements.TwigElementTypes;
2127
import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons;
@@ -427,6 +433,13 @@ public void addCompletions(@NotNull CompletionParameters parameters, ProcessingC
427433
TwigPattern.getCompletablePattern(),
428434
new IncompleteIncludePrintBlockCompletionProvider()
429435
);
436+
437+
// {% for => "for flash in app.flashes"
438+
extend(
439+
CompletionType.BASIC,
440+
PlatformPatterns.psiElement(TwigTokenTypes.TAG_NAME),
441+
new IncompleteForCompletionProvider()
442+
);
430443
}
431444

432445
private boolean isCompletionStartingMatch(@NotNull String fullText, @NotNull CompletionParameters completionParameters, int minLength) {
@@ -801,5 +814,81 @@ public boolean accepts(@NotNull String s, ProcessingContext processingContext) {
801814
}
802815
}
803816
}
817+
818+
/**
819+
* {% for => "for flash in app.flashes"
820+
*/
821+
private class IncompleteForCompletionProvider extends CompletionProvider<CompletionParameters> {
822+
@Override
823+
protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet resultSet) {
824+
if(!Symfony2ProjectComponent.isEnabled(completionParameters.getPosition())) {
825+
return;
826+
}
827+
828+
resultSet.restartCompletionOnPrefixChange(StandardPatterns.string().longerThan(1).with(new PatternCondition<>("for startsWith") {
829+
@Override
830+
public boolean accepts(@NotNull String s, ProcessingContext processingContext) {
831+
return "for".startsWith(s);
832+
}
833+
}));
834+
835+
if (!isCompletionStartingMatch("for", completionParameters, 2)) {
836+
return;
837+
}
838+
839+
Set<Map.Entry<String, PsiVariable>> entries = TwigTypeResolveUtil.collectScopeVariables(completionParameters.getPosition()).entrySet();
840+
841+
Map<String, Pair<String, LookupElement>> arrays = new HashMap<>();
842+
843+
Project project = completionParameters.getPosition().getProject();
844+
845+
for(Map.Entry<String, PsiVariable> entry: entries) {
846+
Collection<PhpClass> classFromPhpTypeSet = PhpElementsUtil.getClassFromPhpTypeSet(project, entry.getValue().getTypes());
847+
for (PhpClass phpClass : classFromPhpTypeSet) {
848+
for(Method method: phpClass.getMethods()) {
849+
if(!(!method.getModifier().isPublic() || method.getName().startsWith("set") || method.getName().startsWith("__"))) {
850+
if (PhpType.isArray(PhpIndex.getInstance(project).completeType(project, method.getType(), new HashSet<>()))) {
851+
String propertyShortcutMethodName = TwigTypeResolveUtil.getPropertyShortcutMethodName(method);
852+
arrays.put(entry.getKey() + "." + propertyShortcutMethodName, Pair.create(propertyShortcutMethodName, new PhpTwigMethodLookupElement(method)));
853+
}
854+
}
855+
}
856+
857+
for(Field field: phpClass.getFields()) {
858+
if(field.getModifier().isPublic()) {
859+
if (PhpType.isArray(PhpIndex.getInstance(project).completeType(project, field.getType(), new HashSet<>()))) {
860+
arrays.put(entry.getKey() + "." + field.getName(), Pair.create(field.getName(), new PhpTwigMethodLookupElement(field)));
861+
}
862+
}
863+
}
864+
}
865+
}
866+
867+
for (Map.Entry<String, Pair<String, LookupElement>> entry : arrays.entrySet()) {
868+
String var = entry.getValue().getFirst();
869+
String unpluralize = StringUtil.unpluralize(var);
870+
if (unpluralize != null) {
871+
var = unpluralize;
872+
}
873+
874+
LookupElementPresentation lookupElementPresentation = new LookupElementPresentation();
875+
entry.getValue().getSecond().renderElement(lookupElementPresentation);
876+
877+
Set<String> types = new HashSet<>();
878+
PsiElement psiElement = entry.getValue().getSecond().getPsiElement();
879+
if (psiElement instanceof PhpTypedElement) {
880+
types.addAll(((PhpTypedElement) psiElement).getType().getTypes());
881+
}
882+
883+
String content = String.format("for %s in %s", var, entry.getKey());
884+
LookupElementBuilder lookupElement = LookupElementBuilder.create(content)
885+
.withIcon(lookupElementPresentation.getIcon())
886+
.withStrikeoutness(lookupElementPresentation.isStrikeout())
887+
.withTypeText(StringUtils.stripStart(TwigTypeResolveUtil.getTypeDisplayName(project, types), "\\"));
888+
889+
resultSet.addElement(lookupElement);
890+
}
891+
}
892+
}
804893
}
805894

0 commit comments

Comments
 (0)