From 7c363fe6da844584e9ef58c25bf58c227d11229c Mon Sep 17 00:00:00 2001 From: Daniel Espendiller Date: Fri, 25 Mar 2022 16:00:38 +0100 Subject: [PATCH] add Twig "include" and "extends" lookup element weight sorting --- .../dic/ServiceCompletionProvider.java | 4 +- .../templating/TemplateLookupElement.java | 8 +++ .../TwigTemplateCompletionContributor.java | 30 ++++++++-- .../action/TwigExtendsGenerator.java | 24 +------- .../templating/util/TwigUtil.java | 58 ++++++++++++++++--- 5 files changed, 85 insertions(+), 39 deletions(-) diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/ServiceCompletionProvider.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/ServiceCompletionProvider.java index b6295bae3..d5fe7e1cf 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/ServiceCompletionProvider.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/ServiceCompletionProvider.java @@ -107,11 +107,11 @@ public List getTopStrings() { } } - private static class MyLookupElementWeigher extends LookupElementWeigher { + public static class MyLookupElementWeigher extends LookupElementWeigher { @NotNull private final List elements; - MyLookupElementWeigher(@NotNull List elements) { + public MyLookupElementWeigher(@NotNull List elements) { super("topElement"); this.elements = elements; diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TemplateLookupElement.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TemplateLookupElement.java index b23354710..1f8eb4cac 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TemplateLookupElement.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TemplateLookupElement.java @@ -23,6 +23,8 @@ public class TemplateLookupElement extends LookupElement { @NotNull private final String templateName; + private boolean bold = false; + @Nullable private InsertHandler insertHandler = null; @@ -32,6 +34,11 @@ public TemplateLookupElement(@NotNull String templateName, @NotNull VirtualFile this.projectBaseDir = projectBaseDir; } + public TemplateLookupElement(@NotNull String templateName, @NotNull VirtualFile virtualFile, @NotNull VirtualFile projectBaseDir, boolean bold) { + this(templateName, virtualFile, projectBaseDir); + this.bold = bold; + } + @NotNull @Override public String getLookupString() { @@ -49,5 +56,6 @@ public void renderElement(LookupElementPresentation presentation) { presentation.setIcon(this.virtualFile.getFileType().getIcon()); presentation.setTypeText(VfsUtil.getRelativePath(this.virtualFile, this.projectBaseDir, '/')); presentation.setTypeGrayed(true); + presentation.setItemTextBold(bold); } } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateCompletionContributor.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateCompletionContributor.java index 41f3e3335..e1be2f27f 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateCompletionContributor.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateCompletionContributor.java @@ -22,6 +22,7 @@ import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; import fr.adrienbrault.idea.symfony2plugin.asset.AssetDirectoryReader; import fr.adrienbrault.idea.symfony2plugin.asset.provider.AssetCompletionProvider; +import fr.adrienbrault.idea.symfony2plugin.dic.ServiceCompletionProvider; import fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper; import fr.adrienbrault.idea.symfony2plugin.templating.completion.QuotedInsertionLookupElement; import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigExtension; @@ -71,7 +72,7 @@ public TwigTemplateCompletionContributor() { TwigPattern.getPrintBlockOrTagFunctionPattern("include", "source"), TwigPattern.getIncludeTagArrayPattern(), TwigPattern.getTagTernaryPattern(TwigElementTypes.INCLUDE_TAG) - ), new TemplateCompletionProvider()); + ), new TemplateCompletionProvider()); // provides support for 'a'|trans({'%foo%' : bar|default}, 'Domain') // provides support for 'a'|transchoice(2, {'%foo%' : bar|default}, 'Domain') @@ -503,7 +504,7 @@ protected void addCompletions(@NotNull CompletionParameters parameters, Processi return; } - resultSet.addAllElements(TwigUtil.getTwigLookupElements(parameters.getPosition().getProject())); + resultSet.addAllElements(TwigUtil.getTwigLookupElements(parameters.getPosition().getProject(), Collections.emptyList())); } } @@ -573,16 +574,33 @@ protected void addCompletions(@NotNull CompletionParameters parameters, Processi } - private class TemplateCompletionProvider extends CompletionProvider { + private static class TemplateCompletionProvider extends CompletionProvider { public void addCompletions(@NotNull CompletionParameters parameters, - ProcessingContext context, - @NotNull CompletionResultSet resultSet) { + @NotNull ProcessingContext context, + @NotNull CompletionResultSet resultSet) { if(!Symfony2ProjectComponent.isEnabled(parameters.getPosition())) { return; } - resultSet.addAllElements(TwigUtil.getTwigLookupElements(parameters.getPosition().getProject())); + List prioritizedKeys = new ArrayList<>(); + Project project = parameters.getPosition().getProject(); + + if (TwigPattern.getTemplateFileReferenceTagPattern("extends").accepts(parameters.getPosition())) { + prioritizedKeys.addAll(TwigUtil.getExtendsTemplateUsageAsOrderedList(project, 50)); + } else if(TwigPattern.getTemplateFileReferenceTagPattern("include").accepts(parameters.getPosition())) { + prioritizedKeys.addAll(TwigUtil.getIncludeTemplateUsageAsOrderedList(project, 50)); + } + + if (prioritizedKeys.size() > 0) { + CompletionSorter completionSorter = CompletionService.getCompletionService() + .defaultSorter(parameters, resultSet.getPrefixMatcher()) + .weighBefore("priority", new ServiceCompletionProvider.MyLookupElementWeigher(prioritizedKeys)); + + resultSet = resultSet.withRelevanceSorter(completionSorter); + } + + resultSet.addAllElements(TwigUtil.getTwigLookupElements(project, new HashSet<>(prioritizedKeys))); } } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/action/TwigExtendsGenerator.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/action/TwigExtendsGenerator.java index f34dbb9ac..f9db98903 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/action/TwigExtendsGenerator.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/action/TwigExtendsGenerator.java @@ -8,21 +8,17 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.source.html.HtmlFileImpl; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.ThrowableRunnable; -import com.intellij.util.indexing.FileBasedIndex; import com.jetbrains.php.completion.insert.PhpInsertHandlerUtil; import com.jetbrains.twig.TwigFile; import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; -import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.TwigExtendsStubIndex; import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; @@ -53,23 +49,7 @@ public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull Ps return; } - Set allKeys = FileBasedIndex.getInstance().getAllKeys(TwigExtendsStubIndex.KEY, project) - .stream() - .filter(s -> !s.toLowerCase().contains("@webprofiler") && !s.toLowerCase().contains("/profiler/") && !s.toLowerCase().contains("@twig") && !s.equalsIgnoreCase("form_div_layout.html.twig")) - .collect(Collectors.toSet()); - - Map extendsWithFileCountUsage = new HashMap<>(); - for (String allKey : allKeys) { - Collection containingFiles = FileBasedIndex.getInstance().getContainingFiles(TwigExtendsStubIndex.KEY, allKey, GlobalSearchScope.allScope(project)); - extendsWithFileCountUsage.put(allKey, containingFiles.size()); - } - - List prioritizedKeys = extendsWithFileCountUsage.entrySet() - .stream() - .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) - .map(Map.Entry::getKey) - .limit(40) - .collect(Collectors.toList()); + List prioritizedKeys = TwigUtil.getExtendsTemplateUsageAsOrderedList(project, 40); if (prioritizedKeys.size() == 0) { if (!ApplicationManager.getApplication().isHeadlessEnvironment()) { diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigUtil.java index 8dcd71f40..8ab12cd83 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigUtil.java @@ -43,10 +43,7 @@ import fr.adrienbrault.idea.symfony2plugin.extension.TwigNamespaceExtensionParameter; import fr.adrienbrault.idea.symfony2plugin.stubs.SymfonyProcessors; import fr.adrienbrault.idea.symfony2plugin.stubs.dict.TemplateUsage; -import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.PhpTwigTemplateUsageStubIndex; -import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.TwigBlockIndexExtension; -import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.TwigExtendsStubIndex; -import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.TwigMacroFunctionStubIndex; +import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.*; import fr.adrienbrault.idea.symfony2plugin.templating.TemplateLookupElement; import fr.adrienbrault.idea.symfony2plugin.templating.TwigPattern; import fr.adrienbrault.idea.symfony2plugin.templating.dict.*; @@ -1527,17 +1524,20 @@ public static Collection getTwigMacroTargets(final Project project, /** * Lookup elements for Twig files + * @return */ @NotNull - public static Collection getTwigLookupElements(@NotNull Project project) { + public static Collection getTwigLookupElements(@NotNull Project project, @NotNull Collection highlight) { VirtualFile baseDir = ProjectUtil.getProjectDir(project); - return getTemplateMap(project).entrySet() .stream() .filter(entry -> entry.getValue().size() > 0) - .map((java.util.function.Function>, LookupElement>) entry -> - new TemplateLookupElement(entry.getKey(), entry.getValue().iterator().next(), baseDir) - ) + .map((java.util.function.Function>, LookupElement>) entry -> new TemplateLookupElement( + entry.getKey(), + entry.getValue().iterator().next(), + baseDir, + highlight.contains(entry.getKey()) + )) .collect(Collectors.toList()); } @@ -2787,6 +2787,46 @@ public void visitElement(PsiElement element) { }); } + public static List getIncludeTemplateUsageAsOrderedList(@NotNull Project project, int limit) { + Set allKeys = FileBasedIndex.getInstance().getAllKeys(TwigIncludeStubIndex.KEY, project) + .stream() + .filter(s -> !s.toLowerCase().contains("@webprofiler") && !s.toLowerCase().contains("/profiler/") && !s.toLowerCase().contains("@twig") && !s.equalsIgnoreCase("form_div_layout.html.twig")) + .collect(Collectors.toSet()); + + Map extendsWithFileCountUsage = new HashMap<>(); + for (String allKey : allKeys) { + Collection containingFiles = FileBasedIndex.getInstance().getContainingFiles(TwigIncludeStubIndex.KEY, allKey, GlobalSearchScope.allScope(project)); + extendsWithFileCountUsage.put(allKey, containingFiles.size()); + } + + return extendsWithFileCountUsage.entrySet() + .stream() + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .map(Map.Entry::getKey) + .limit(limit) + .collect(Collectors.toList()); + } + + public static List getExtendsTemplateUsageAsOrderedList(@NotNull Project project, int limit) { + Set allKeys = FileBasedIndex.getInstance().getAllKeys(TwigExtendsStubIndex.KEY, project) + .stream() + .filter(s -> !s.toLowerCase().contains("@webprofiler") && !s.toLowerCase().contains("/profiler/") && !s.toLowerCase().contains("@twig") && !s.equalsIgnoreCase("form_div_layout.html.twig")) + .collect(Collectors.toSet()); + + Map extendsWithFileCountUsage = new HashMap<>(); + for (String allKey : allKeys) { + Collection containingFiles = FileBasedIndex.getInstance().getContainingFiles(TwigExtendsStubIndex.KEY, allKey, GlobalSearchScope.allScope(project)); + extendsWithFileCountUsage.put(allKey, containingFiles.size()); + } + + return extendsWithFileCountUsage.entrySet() + .stream() + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .map(Map.Entry::getKey) + .limit(limit) + .collect(Collectors.toList()); + } + /** * Visit each "controller()" with its normalized parameter */