From 754452fc370e210dca5c32c57ce783c22645d716 Mon Sep 17 00:00:00 2001 From: Daniel Espendiller Date: Sun, 17 Sep 2023 16:03:13 +0200 Subject: [PATCH] provide block support inside Symfony UX component tag --- .../templating/util/TwigUtil.java | 64 +++++++++++++++++-- .../idea/symfony2plugin/util/UxUtil.java | 17 +++++ .../tests/templating/util/TwigUtilTest.java | 41 ++++++++++++ 3 files changed, 117 insertions(+), 5 deletions(-) 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 131299499..a6a35e6a3 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 @@ -412,6 +412,42 @@ public static PsiElement getTransDefaultDomainScope(@NotNull PsiElement psiEleme ); } + /** + * File Scope: + * {% trans_default_domain "foo" %} + * + * Embed: + * {embed 'foo.html.twig'}{% trans_default_domain "foo" %}{% endembed %} + */ + @Nullable + public static PsiElement getBlockExtendsScope(@NotNull PsiElement psiElement) { + return PsiTreeUtil.findFirstParent(psiElement, psiElement1 -> { + if (psiElement1 instanceof PsiFile) { + return true; + } + + // {% embed "test.html.twig" %} + if (psiElement1 instanceof TwigCompositeElement && psiElement1.getNode().getElementType() == TwigElementTypes.EMBED_STATEMENT) { + return true; + } + + // {% component DataTable with + if (psiElement1 instanceof TwigStatement && psiElement1.getNode().getElementType() instanceof TwigTag) { + PsiElement firstChild = psiElement1.getFirstChild(); + if (firstChild != null && firstChild.getNode().getElementType() == TwigElementTypes.TAG) { + PsiElement firstChild1 = firstChild.getFirstChild(); + if (firstChild1 != null) { + PsiElement tagName = PsiElementUtils.getNextSiblingAndSkip(firstChild1, TwigTokenTypes.TAG_NAME); + return tagName != null && "component".equals(tagName.getText()); + } + } + } + + return false; + } + ); + } + /** * need a twig translation print block and search for default domain on parameter or trans_default_domain * @@ -1843,19 +1879,37 @@ private static Collection getTernaryStrings(@NotNull PsiElement psiQuest public static Pair, Boolean> findScopedFile(@NotNull PsiElement psiElement) { // {% embed "template.twig" %}{% block %} - PsiElement firstParent = getTransDefaultDomainScope(psiElement); + PsiElement firstParent = getBlockExtendsScope(psiElement); // {% embed "template.twig" %} - if(firstParent != null && firstParent.getNode().getElementType() == TwigElementTypes.EMBED_STATEMENT) { + if (firstParent != null && firstParent.getNode().getElementType() == TwigElementTypes.EMBED_STATEMENT) { PsiElement embedTag = firstParent.getFirstChild(); - if(embedTag != null) { + if (embedTag != null) { String templateName = getTemplateNameForEmbedTag(embedTag); - if(templateName != null) { + if (templateName != null) { return Pair.create(getTemplatePsiElements(psiElement.getProject(), templateName), true); } } return Pair.create(Collections.emptyList(), true); + } else if (firstParent != null) { + // {% component RandomNumber + PsiElement firstChild = firstParent.getFirstChild(); + if (firstChild != null) { + PsiElement tagStart = firstChild.getFirstChild(); + if (tagStart != null) { + PsiElement tagName = PsiElementUtils.getNextSiblingAndSkip(tagStart, TwigTokenTypes.TAG_NAME); + if (tagName != null && "component".equals(tagName.getText())) { + PsiElement filterName = PsiTreeUtil.nextVisibleLeaf(tagName); + if (filterName != null) { + String componentName = PsiElementUtils.trimQuote(filterName.getText()); + if (!componentName.isBlank()) { + return Pair.create(UxUtil.getComponentTemplates(psiElement.getProject(), componentName), true); + } + } + } + } + } } return Pair.create(Collections.singletonList(psiElement.getContainingFile()), false); @@ -2826,7 +2880,7 @@ public static Collection getBlockLookupElements(@NotNull Project .withIcon(TwigIcons.TwigFileIcon); Collection names = templateNames.getOrDefault(virtualFile, Collections.emptyList()); - if(names.size() > 0) { + if(!names.isEmpty()) { lookupElementBuilder = lookupElementBuilder.withTypeText(names.iterator().next(), true); } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/UxUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/UxUtil.java index 6c17b011e..49249dd9e 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/UxUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/UxUtil.java @@ -24,6 +24,7 @@ import java.util.*; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -128,6 +129,22 @@ private static Collection getAllUxComponents(@NotNull Project proje ); } + public static Collection getComponentTemplates(@NotNull Project project, @NotNull String component) { + Collection psiFiles = new HashSet<>(); + + for (UxComponent allUxComponent : getAllUxComponents(project).stream().filter(uxComponent -> component.equals(uxComponent.name())).toList()) { + String template = allUxComponent.template(); + + if (template != null) { + psiFiles.addAll(TwigUtil.getTemplatePsiElements(project, template)); + } else { + psiFiles.addAll(TwigUtil.getTemplatePsiElements(project, "components/" + allUxComponent.name() + ".html.twig")); + } + } + + return psiFiles; + } + @NotNull public static Collection getComponentClassesForTemplateFile(@NotNull Project project, @NotNull PsiFile psiFile) { Collection phpClasses = new HashSet<>(); diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/util/TwigUtilTest.java b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/util/TwigUtilTest.java index 5cf8e9620..aec6547fc 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/util/TwigUtilTest.java +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/util/TwigUtilTest.java @@ -983,6 +983,47 @@ public void testGetBlockLookupElements() { assertNotNull(lookupElements.stream().filter(lookupElement -> "foobar".equals(lookupElement.getLookupString())).findFirst().orElse(null)); } + public void testGetBlockExtendsScopeInsideEmbed() { + PsiElement insideEmbed = TwigElementFactory.createPsiElement(getProject(), "" + + "{% block body %}\n" + + " {% embed \"test.html.twig\" %}\n" + + " {% block my_foobar %}" + + " {% macro foo %}{% endmacro %}\n" + + " {% endblock %}\n" + + " {% endembed %}\n" + + "{% endblock body %}\n", TwigElementTypes.MACRO_TAG); + + TwigCompositeElement blockExtendsScope = (TwigCompositeElement) TwigUtil.getBlockExtendsScope(insideEmbed); + assertTrue(blockExtendsScope.getText().startsWith("{% embed")); + } + + public void testGetBlockExtendsScopeInsideComponent() { + PsiElement insideEmbed = TwigElementFactory.createPsiElement(getProject(), "" + + "{% block body %}\n" + + " {% component DataTable with {headers: ['key', 'value'], data: [[1, 2], [3, 4]]} %}\n" + + " {% block my_foobar %}" + + " {% macro foo %}{% endmacro %}\n" + + " {% endblock %}\n" + + " {% endcomponent %}\n" + + "{% endblock body %}\n", TwigElementTypes.MACRO_TAG); + + TwigCompositeElement blockExtendsScope = (TwigCompositeElement) TwigUtil.getBlockExtendsScope(insideEmbed); + assertTrue(blockExtendsScope.getText().startsWith("{% component")); + } + + public void testFindScopedFile() { + PsiElement insideEmbed = TwigElementFactory.createPsiElement(getProject(), "" + + "{% block body %}\n" + + " {% component DataTable with {headers: ['key', 'value'], data: [[1, 2], [3, 4]]} %}\n" + + " {% block my_foobar %}" + + " {% macro foo %}{% endmacro %}\n" + + " {% endblock %}\n" + + " {% endcomponent %}\n" + + "{% endblock body %}\n", TwigElementTypes.MACRO_TAG); + + TwigUtil.findScopedFile(insideEmbed); + } + private void assertEqual(Collection c, String... values) { if(!StringUtils.join(c, ",").equals(StringUtils.join(Arrays.asList(values), ","))) { fail(String.format("Fail that '%s' is equal '%s'", StringUtils.join(c, ","), StringUtils.join(Arrays.asList(values), ",")));