From e1d6cef6205e7de355870d72b5e047111aceaf75 Mon Sep 17 00:00:00 2001 From: Daniel Espendiller Date: Fri, 25 Mar 2022 15:35:13 +0100 Subject: [PATCH] add Twig "extends" generator action --- .../action/TwigBlockOverwriteGenerator.java | 26 +---- .../action/TwigExtendsGenerator.java | 102 ++++++++++++++++++ .../templating/util/TwigUtil.java | 21 ++++ .../TwigTranslationGeneratorAction.java | 25 +---- src/main/resources/META-INF/plugin.xml | 7 ++ 5 files changed, 134 insertions(+), 47 deletions(-) create mode 100644 src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/action/TwigExtendsGenerator.java diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/action/TwigBlockOverwriteGenerator.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/action/TwigBlockOverwriteGenerator.java index 3071ee4f6..e1d4789c1 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/action/TwigBlockOverwriteGenerator.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/action/TwigBlockOverwriteGenerator.java @@ -19,10 +19,8 @@ import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil; import fr.adrienbrault.idea.symfony2plugin.twig.utils.TwigFileUtil; -import icons.TwigIcons; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.List; @@ -44,34 +42,14 @@ protected boolean isValidForFile(@NotNull Project project, @NotNull Editor edito return Symfony2ProjectComponent.isEnabled(project) && ( file instanceof TwigFile || (file instanceof HtmlFileImpl && file.getName().toLowerCase().endsWith(".twig")) - || getInjectedTwigElement(file, editor) != null + || TwigUtil.getInjectedTwigElement(file, editor) != null ); } - @Nullable - private static PsiElement getInjectedTwigElement(@NotNull PsiFile psiFile, @NotNull Editor editor) { - int caretOffset = editor.getCaretModel().getOffset(); - if(caretOffset <= 0) { - return null; - } - - PsiElement psiElement = psiFile.findElementAt(caretOffset - 1); - if(psiElement == null) { - return null; - } - - return TwigUtil.getElementOnTwigViewProvider(psiElement); - } - private static class MyCodeInsightActionHandler implements CodeInsightActionHandler { @Override public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { - int caretOffset = editor.getCaretModel().getOffset(); - if(caretOffset <= 0) { - return; - } - - PsiElement psiElement = getInjectedTwigElement(file, editor); + PsiElement psiElement = TwigUtil.getInjectedTwigElement(file, editor); if(psiElement == null) { return; } 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 new file mode 100644 index 000000000..f34dbb9ac --- /dev/null +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/action/TwigExtendsGenerator.java @@ -0,0 +1,102 @@ +package fr.adrienbrault.idea.symfony2plugin.templating.action; + +import com.intellij.codeInsight.CodeInsightActionHandler; +import com.intellij.codeInsight.actions.CodeInsightAction; +import com.intellij.codeInsight.hint.HintManager; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.WriteCommandAction; +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.function.Function; +import java.util.stream.Collectors; + +/** + * @author Daniel Espendiller + */ +public class TwigExtendsGenerator extends CodeInsightAction { + @Override + protected @NotNull + CodeInsightActionHandler getHandler() { + return new TwigExtendsGenerator.MyCodeInsightActionHandler(); + } + + @Override + protected boolean isValidForFile(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { + return Symfony2ProjectComponent.isEnabled(project) && ( + file instanceof TwigFile + || (file instanceof HtmlFileImpl && file.getName().toLowerCase().endsWith(".twig")) + || TwigUtil.getInjectedTwigElement(file, editor) != null + ); + } + + private static class MyCodeInsightActionHandler implements CodeInsightActionHandler { + @Override + public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { + PsiElement psiElement = TwigUtil.getInjectedTwigElement(file, editor); + if(psiElement == null) { + 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()); + + if (prioritizedKeys.size() == 0) { + if (!ApplicationManager.getApplication().isHeadlessEnvironment()) { + HintManager.getInstance().showErrorHint(editor, "No extends found"); + } + + return; + } + + JBPopupFactory.getInstance().createPopupChooserBuilder(prioritizedKeys) + .setTitle("Symfony: Twig Extends") + .setItemsChosenCallback(strings -> { + try { + WriteCommandAction.writeCommandAction(editor.getProject()) + .withName("Twig Extends") + .run((ThrowableRunnable) () -> { + String content = strings.stream() + .map((Function) s -> "{% extends '" + s + "' %}") + .collect(Collectors.joining("\n")); + + PhpInsertHandlerUtil.insertStringAtCaret(editor, content); + }); + } catch (Throwable ignored) { + } + }) + .createPopup() + .showInBestPositionFor(editor); + } + } +} 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 b4470b489..8dcd71f40 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 @@ -2,6 +2,7 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; @@ -2439,6 +2440,26 @@ public static Collection getTwigExtensionClasses(@NotNull Project proj .collect(Collectors.toCollection(HashSet::new)); } + @Nullable + public static PsiElement getInjectedTwigElement(@NotNull PsiFile psiFile, @NotNull Editor editor) { + int caretOffset = editor.getCaretModel().getOffset(); + if(caretOffset < 0) { + return null; + } + + // last and first element of file are tricky :) + if (caretOffset == 0) { + return psiFile; + } + + PsiElement psiElement = psiFile.findElementAt(caretOffset - 1); + if(psiElement == null) { + return null; + } + + return TwigUtil.getElementOnTwigViewProvider(psiElement); + } + /** * Resolve html language injection */ diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/twig/action/TwigTranslationGeneratorAction.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/twig/action/TwigTranslationGeneratorAction.java index 80d4b6d97..8530dd3d8 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/twig/action/TwigTranslationGeneratorAction.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/twig/action/TwigTranslationGeneratorAction.java @@ -18,7 +18,6 @@ import fr.adrienbrault.idea.symfony2plugin.translation.dict.TranslationUtil; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.stream.Collectors; @@ -32,7 +31,7 @@ protected boolean isValidForFile(@NotNull Project project, @NotNull Editor edito return Symfony2ProjectComponent.isEnabled(project) && ( file instanceof TwigFile || (file instanceof HtmlFileImpl && file.getName().toLowerCase().endsWith(".twig")) - || getInjectedTwigElement(file, editor) != null + || TwigUtil.getInjectedTwigElement(file, editor) != null ); } @@ -42,30 +41,10 @@ protected CodeInsightActionHandler getHandler() { return new MyCodeInsightActionHandler(); } - @Nullable - private static PsiElement getInjectedTwigElement(@NotNull PsiFile psiFile, @NotNull Editor editor) { - int caretOffset = editor.getCaretModel().getOffset(); - if(caretOffset <= 0) { - return null; - } - - PsiElement psiElement = psiFile.findElementAt(caretOffset); - if(psiElement == null) { - return null; - } - - return TwigUtil.getElementOnTwigViewProvider(psiElement); - } - private static class MyCodeInsightActionHandler implements CodeInsightActionHandler { @Override public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile psiFile) { - int caretOffset = editor.getCaretModel().getOffset(); - if(caretOffset <= 0) { - return; - } - - PsiElement psiElement = getInjectedTwigElement(psiFile, editor); + PsiElement psiElement = TwigUtil.getInjectedTwigElement(psiFile, editor); if(psiElement == null) { return; } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 90fc07207..670149a80 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -725,6 +725,13 @@ + + + +