From 325f7ad461360225d533d521409b62e57eb41054 Mon Sep 17 00:00:00 2001 From: Daniel Espendiller Date: Fri, 9 Jul 2021 17:49:06 +0200 Subject: [PATCH] smarter public asset folder, fix twig form linemarker psi pattern, remove symfony check from notication window --- .../asset/AssetDirectoryReader.java | 78 ++++++++++--------- .../templating/TwigLineMarkerProvider.java | 20 ++--- .../templating/util/TwigUtil.java | 23 ++++++ .../webpack/SymfonyWebpackUtil.java | 25 +++++- .../WebpackEncoreGotoCompletionRegistrar.java | 10 --- .../idea/symfony2plugin/util/IdeHelper.java | 5 +- .../tests/templating/util/TwigUtilTest.java | 14 ++++ .../webpack/SymfonyWebpackUtilTest.java | 2 +- .../webpack/fixtures/webpack.config.js | 1 + 9 files changed, 115 insertions(+), 63 deletions(-) diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/asset/AssetDirectoryReader.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/asset/AssetDirectoryReader.java index 966f63c49..f465632d7 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/asset/AssetDirectoryReader.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/asset/AssetDirectoryReader.java @@ -11,14 +11,11 @@ import fr.adrienbrault.idea.symfony2plugin.util.dict.SymfonyBundle; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * @author Daniel Espendiller @@ -41,30 +38,40 @@ public AssetDirectoryReader(@NotNull String[] filterExtension, boolean includeBu this.filterExtension.addAll(Arrays.asList(filterExtension)); } - @Nullable - private static VirtualFile getProjectAssetRoot(@NotNull Project project) { - String webDirectoryName = Settings.getInstance(project).directoryToWeb; - return VfsUtil.findRelativeFile(ProjectUtil.getProjectDir(project), webDirectoryName.split("/")); + @NotNull + private static Collection getProjectAssetRoot(@NotNull Project project) { + Set paths = new HashSet<>(); + + // custom config + String directoryToWeb = Settings.getInstance(project).directoryToWeb; + if (StringUtils.isNotBlank(directoryToWeb)) { + paths.add(directoryToWeb); + } + + paths.add("public"); // latest Symfony structure + paths.add("web"); // old Symfony structure + + return paths.stream() + .map(path -> VfsUtil.findRelativeFile(ProjectUtil.getProjectDir(project), path.split("/"))) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); } @NotNull public Collection getAssetFiles(@NotNull Project project) { Collection files = new ArrayList<>(); - VirtualFile webDirectory = getProjectAssetRoot(project); - if (null == webDirectory) { - return files; - } - - VfsUtil.visitChildrenRecursively(webDirectory, new VirtualFileVisitor() { - @Override - public boolean visitFile(@NotNull VirtualFile virtualFile) { - if(isValidFile(virtualFile)) { - files.add(new AssetFile(virtualFile, AssetEnum.Position.Web, webDirectory)); + for (VirtualFile webDirectory : getProjectAssetRoot(project)) { + VfsUtil.visitChildrenRecursively(webDirectory, new VirtualFileVisitor() { + @Override + public boolean visitFile(@NotNull VirtualFile virtualFile) { + if(isValidFile(virtualFile)) { + files.add(new AssetFile(virtualFile, AssetEnum.Position.Web, webDirectory)); + } + return super.visitFile(virtualFile); } - return super.visitFile(virtualFile); - } - }); + }); + } if(!this.includeBundleDir) { return files; @@ -81,7 +88,7 @@ public boolean visitFile(@NotNull VirtualFile virtualFile) { VirtualFile resourceDirectory = VfsUtil.findRelativeFile(bundleDirectoryVirtual, "Resources"); if (null != resourceDirectory) { - VfsUtil.visitChildrenRecursively(resourceDirectory, new VirtualFileVisitor() { + VfsUtil.visitChildrenRecursively(resourceDirectory, new VirtualFileVisitor() { @Override public boolean visitFile(@NotNull VirtualFile virtualFile) { if(isValidFile(virtualFile)) { @@ -139,21 +146,18 @@ public Collection resolveAssetFile(@NotNull Project project, @NotNu Collection files = new ArrayList<>(); - VirtualFile webDirectory = getProjectAssetRoot(project); - if (null == webDirectory) { - return files; - } - - Matcher matcher = Pattern.compile("^(.*[/\\\\])\\*([.\\w+]*)$").matcher(assetName); - if (!matcher.find()) { - VirtualFile assetFile = VfsUtil.findRelativeFile(webDirectory, assetName.split("/")); - if(assetFile != null) { - files.add(assetFile); + for (VirtualFile webDirectory : getProjectAssetRoot(project)) { + Matcher matcher = Pattern.compile("^(.*[/\\\\])\\*([.\\w+]*)$").matcher(assetName); + if (!matcher.find()) { + VirtualFile assetFile = VfsUtil.findRelativeFile(webDirectory, assetName.split("/")); + if(assetFile != null) { + files.add(assetFile); + } + } else { + // "/*" + // "/*.js" + files.addAll(collectWildcardDirectories(matcher, webDirectory)); } - } else { - // "/*" - // "/*.js" - files.addAll(collectWildcardDirectories(matcher, webDirectory)); } return files; diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigLineMarkerProvider.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigLineMarkerProvider.java index 89f65f3e1..36e940eaa 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigLineMarkerProvider.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigLineMarkerProvider.java @@ -17,6 +17,7 @@ import com.intellij.psi.PsiManager; import com.intellij.psi.presentation.java.SymbolPresentationUtil; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ConstantFunction; import com.intellij.util.indexing.FileBasedIndex; import com.jetbrains.php.PhpIcons; @@ -26,6 +27,9 @@ import com.jetbrains.twig.TwigFileType; import com.jetbrains.twig.TwigTokenTypes; import com.jetbrains.twig.elements.TwigElementTypes; +import com.jetbrains.twig.elements.TwigFieldReference; +import com.jetbrains.twig.elements.TwigPsiReference; +import com.jetbrains.twig.elements.TwigVariableReference; import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons; import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; import fr.adrienbrault.idea.symfony2plugin.dic.RelatedPopupGotoLineMarker; @@ -105,7 +109,7 @@ public void collectSlowLineMarkers(@NotNull List psiElemen if(lineOverwrites != null) { results.add(lineOverwrites); } - } else if(TwigPattern.getFunctionPattern("form_start", "form").accepts(psiElement)) { + } else if(TwigPattern.getFunctionPattern("form_start", "form", "form_end", "form_rest").accepts(psiElement)) { LineMarkerInfo lineOverwrites = attachFormType(psiElement); if(lineOverwrites != null) { results.add(lineOverwrites); @@ -307,17 +311,13 @@ private LineMarkerInfo attachBlockOverwrites(@NotNull PsiElement psiElement, @Nullable private LineMarkerInfo attachFormType(@NotNull PsiElement psiElement) { - PsiElement firstChild = psiElement.getFirstChild(); - if (firstChild == null) { + // form(theform); + PsiElement twigFunctionParameterIdentifierPsi = TwigUtil.getTwigFunctionParameterIdentifierPsi(psiElement); + if (twigFunctionParameterIdentifierPsi == null) { return null; } - PsiElement nextSiblingOfType = PsiElementUtils.getNextSiblingOfType(firstChild, PlatformPatterns.psiElement().withElementType(TwigTokenTypes.IDENTIFIER).afterLeaf(PlatformPatterns.psiElement(TwigTokenTypes.LBRACE))); - if (nextSiblingOfType == null) { - return null; - } - - Collection twigTypeContainers = TwigTypeResolveUtil.resolveTwigMethodName(nextSiblingOfType, TwigTypeResolveUtil.formatPsiTypeNameWithCurrent(nextSiblingOfType)); + Collection twigTypeContainers = TwigTypeResolveUtil.resolveTwigMethodName(twigFunctionParameterIdentifierPsi, TwigTypeResolveUtil.formatPsiTypeNameWithCurrent(twigFunctionParameterIdentifierPsi)); Collection phpClasses = new HashSet<>(); @@ -337,7 +337,7 @@ private LineMarkerInfo attachFormType(@NotNull PsiElement psiElement) { .setTooltipText("Navigate to Form") .setCellRenderer(new MyBlockListCellRenderer()); - return builder.createLineMarkerInfo(firstChild); + return builder.createLineMarkerInfo(psiElement.getFirstChild()); } @Nullable 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 30935b0c2..b4470b489 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 @@ -310,6 +310,29 @@ public void visitElement(PsiElement element) { return null; } + /** + * "form(form.name)" => "form" + */ + @Nullable + public static PsiElement getTwigFunctionParameterIdentifierPsi(@NotNull PsiElement psiElement) { + // "form(form.name)" => "form" + TwigFieldReference childOfType = PsiTreeUtil.findChildOfType(psiElement, TwigFieldReference.class); + if (childOfType != null) { + TwigPsiReference owner = childOfType.getOwner(); + if (owner != null) { + return owner.getFirstChild(); + } + } + + // "form(form)" => "form" + TwigVariableReference childOfType1 = PsiTreeUtil.findChildOfType(psiElement, TwigVariableReference.class); + if (childOfType1 != null) { + return childOfType1.getFirstChild(); + } + + return null; + } + /** * Search Twig element to find use trans_default_domain and returns given string parameter */ diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/webpack/SymfonyWebpackUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/webpack/SymfonyWebpackUtil.java index ace79736d..c545a359c 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/webpack/SymfonyWebpackUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/webpack/SymfonyWebpackUtil.java @@ -3,9 +3,11 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.StreamUtil; +import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.search.FilenameIndex; import com.intellij.psi.search.GlobalSearchScope; +import fr.adrienbrault.idea.symfony2plugin.util.ProjectUtil; import org.jetbrains.annotations.NotNull; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; @@ -27,11 +29,15 @@ public class SymfonyWebpackUtil { */ public static void visitAllEntryFileTypes(@NotNull Project project, @NotNull Consumer> consumer) { for (VirtualFile virtualFile : FilenameIndex.getVirtualFilesByName(project, "webpack.config.js", GlobalSearchScope.allScope(project))) { - visitWebpackConfiguration(virtualFile, consumer); + if (!isTestFile(project, virtualFile)) { + visitWebpackConfiguration(virtualFile, consumer); + } } for (VirtualFile virtualFile : FilenameIndex.getVirtualFilesByName(project, "entrypoints.json", GlobalSearchScope.allScope(project))) { - visitEntryPointJson(virtualFile, consumer); + if (!isTestFile(project, virtualFile)) { + visitEntryPointJson(virtualFile, consumer); + } } } @@ -94,9 +100,20 @@ private static void visitWebpackConfiguration(@NotNull VirtualFile virtualFile, } // if adding "javascript" plugin; would resolve better but not for now - Matcher matcher = Pattern.compile("addEntry\\s*\\(\\s*['\"]([^'\"]+)['\"]").matcher(s); + Matcher matcher = Pattern.compile("(addEntry|addStyleEntry)\\s*\\(\\s*['\"]([^'\"]+)['\"]").matcher(s); while(matcher.find()){ - consumer.accept(Pair.create(virtualFile, matcher.group(1))); + consumer.accept(Pair.create(virtualFile, matcher.group(2))); + } + } + + private static boolean isTestFile(@NotNull Project project, @NotNull VirtualFile virtualFile) { + // ignore: "vendor/symonfy/.../tests/fixtures/build/entrypoints.json" + String path = VfsUtil.getRelativePath(virtualFile, ProjectUtil.getProjectDir(project), '/'); + if (path != null) { + String lowerCase = path.toLowerCase(); + return lowerCase.contains("/test/") || lowerCase.contains("/tests/"); } + + return false; } } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/webpack/WebpackEncoreGotoCompletionRegistrar.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/webpack/WebpackEncoreGotoCompletionRegistrar.java index 1e459b3f4..630c82323 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/webpack/WebpackEncoreGotoCompletionRegistrar.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/webpack/WebpackEncoreGotoCompletionRegistrar.java @@ -65,16 +65,6 @@ public Collection getPsiTargets(PsiElement element) { public void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arguments) { SymfonyWebpackUtil.visitAllEntryFileTypes(getProject(), pair -> { - // ignore: "vendor/symonfy/.../tests/fixtures/build/entrypoints.json" - String path = VfsUtil.getRelativePath(pair.first, ProjectUtil.getProjectDir(getElement()), '/'); - if (path != null) { - String lowerCase = path.toLowerCase(); - - if (lowerCase.contains("/test/") || lowerCase.contains("/tests/")) { - return; - } - } - LookupElementBuilder lookupElement = LookupElementBuilder.create(pair.getSecond()) .withIcon(Symfony2Icons.SYMFONY) .withTypeText(pair.first.getName()); diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/IdeHelper.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/IdeHelper.java index 9af19bd26..002f6dd30 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/IdeHelper.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/IdeHelper.java @@ -26,7 +26,6 @@ import java.net.URISyntaxException; import java.util.*; import java.util.List; -import java.util.function.Function; import java.util.stream.Collectors; /** @@ -161,10 +160,12 @@ public static Collection enablePluginAndConfigure(@NotNull Project proje Collection messages = new ArrayList<>(); + /* Remove version info; prevent index issues Set versions = SymfonyUtil.getVersions(project); if (!versions.isEmpty()) { messages.add("Symfony Version: " + versions.iterator().next()); } + */ // Symfony 3.0 structure if (VfsUtil.findRelativeFile(ProjectUtil.getProjectDir(project), "var", "cache") != null) { @@ -180,10 +181,12 @@ public static Collection enablePluginAndConfigure(@NotNull Project proje // There no clean version when "FooBar:Foo:foo.html.twig" was dropped or deprecated // So we disable it in the 4 branch by default; following with a default switch to "false" soon + /* Remove version info; prevent index issues if (SymfonyUtil.isVersionGreaterThenEquals(project, "4.0")) { Settings.getInstance(project).twigBundleNamespaceSupport = false; messages.add("Twig: Bundle names disabled"); } + */ return messages; } 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 ac14cba57..33b77a558 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 @@ -875,6 +875,20 @@ public void testGetBlocksForFile() { assertDoesntContain(blocks, "foobar_2"); } + public void testGetTwigFunctionParameterIdentifierPsi() { + PsiElement psiElement = TwigElementFactory.createPsiElement(getProject(), "{{ form(form.name) }}", TwigElementTypes.FUNCTION_CALL); + PsiElement twigFunctionParameter = TwigUtil.getTwigFunctionParameterIdentifierPsi(psiElement); + + assertEquals("form", twigFunctionParameter.getText()); + assertEquals(TwigTokenTypes.IDENTIFIER, twigFunctionParameter.getNode().getElementType()); + + psiElement = TwigElementFactory.createPsiElement(getProject(), "{{ form_start(\n form, {attr: {'novalidate': 'novalidate'}}) }}", TwigElementTypes.FUNCTION_CALL); + twigFunctionParameter = TwigUtil.getTwigFunctionParameterIdentifierPsi(psiElement); + + assertEquals("form", twigFunctionParameter.getText()); + assertEquals(TwigTokenTypes.IDENTIFIER, twigFunctionParameter.getNode().getElementType()); + } + public void testGetBlockLookupElements() { PsiFile psiFile = myFixture.configureByText("foo.html.twig", "{% block name %}{% endblock %}"); PsiFile psiFile2 = myFixture.configureByText("foo_2.html.twig", "{% block foobar %}{% endblock %}"); diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/webpack/SymfonyWebpackUtilTest.java b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/webpack/SymfonyWebpackUtilTest.java index 9d8c89146..8b5cede5e 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/webpack/SymfonyWebpackUtilTest.java +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/webpack/SymfonyWebpackUtilTest.java @@ -25,6 +25,6 @@ public void testVisitEntries() { Set entries = new HashSet<>(); SymfonyWebpackUtil.visitAllEntryFileTypes(myFixture.getProject(), pair -> entries.add(pair.second)); - assertContainsElements(entries, "foo", "foobar", "entry_foobar_2"); + assertContainsElements(entries, "foo", "foobar", "entry_foobar_2", "addStyleEntryFoobar"); } } diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/webpack/fixtures/webpack.config.js b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/webpack/fixtures/webpack.config.js index df195757b..25d08144f 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/webpack/fixtures/webpack.config.js +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/webpack/fixtures/webpack.config.js @@ -22,6 +22,7 @@ Encore */ .addEntry('foobar', './assets/app.js') .addEntry("foo") + .addStyleEntry('addStyleEntryFoobar') .enableVueLoader(() => {}, { runtimeCompilerBuild: false })