From 973e8374401b0024f218b769f6705f63e21ac226 Mon Sep 17 00:00:00 2001 From: Daniel Espendiller Date: Thu, 9 Sep 2021 19:13:19 +0200 Subject: [PATCH] smarter Doctrine querybuilder "where" condition navigation to fields --- .../QueryBuilderGotoDeclarationHandler.java | 45 +++++++++++++++---- .../querybuilder/util/MatcherUtil.java | 9 +++- .../querybuilder/util/QueryBuilderUtil.java | 34 ++++++++++++++ .../util/QueryBuilderUtilTest.java | 24 ++++++++++ 4 files changed, 103 insertions(+), 9 deletions(-) diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/QueryBuilderGotoDeclarationHandler.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/QueryBuilderGotoDeclarationHandler.java index a4da63695..8ee5fe59a 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/QueryBuilderGotoDeclarationHandler.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/QueryBuilderGotoDeclarationHandler.java @@ -11,9 +11,11 @@ import fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.dict.QueryBuilderPropertyAlias; import fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.dict.QueryBuilderRelation; import fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.util.MatcherUtil; +import fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.util.QueryBuilderUtil; import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher; import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil; import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -27,7 +29,7 @@ public class QueryBuilderGotoDeclarationHandler implements GotoDeclarationHandle @Nullable @Override - public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Editor editor) { + public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int offset, Editor editor) { if (!Symfony2ProjectComponent.isEnabled(psiElement) || !(psiElement.getContext() instanceof StringLiteralExpression)) { return new PsiElement[0]; @@ -35,18 +37,48 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Edit List psiElements = new ArrayList<>(); - attachPropertyGoto((StringLiteralExpression) psiElement.getContext(), psiElements); - attachJoinGoto((StringLiteralExpression) psiElement.getContext(), psiElements); + StringLiteralExpression context = (StringLiteralExpression) psiElement.getContext(); + attachPropertyGoto(context, psiElements); + attachJoinGoto(context, psiElements); + attachPartialGoto(context, psiElements, offset); // $qb->expr()->in('') - attachExprGoto((StringLiteralExpression) psiElement.getContext(), psiElements); + attachExprGoto(context, psiElements); // $qb->from('', '', ''); - attachFromIndexGoto((StringLiteralExpression) psiElement.getContext(), psiElements); + attachFromIndexGoto(context, psiElements); return psiElements.toArray(new PsiElement[psiElements.size()]); } + private void attachPartialGoto(@NotNull StringLiteralExpression psiElement, @NotNull List targets, int offset) { + MethodMatcher.MethodMatchParameter methodMatchParameter = MatcherUtil.matchWhere(psiElement); + if(methodMatchParameter == null) { + return; + } + + int calulatedOffset = offset - psiElement.getTextRange().getStartOffset(); + if (calulatedOffset < 0) { + calulatedOffset = 0; + } + + String contents = psiElement.getContents(); + String fieldString = QueryBuilderUtil.getFieldString(contents, calulatedOffset); + if (fieldString != null) { + QueryBuilderMethodReferenceParser qb = QueryBuilderCompletionContributor.getQueryBuilderParser(methodMatchParameter.getMethodReference()); + if(qb == null) { + return; + } + + QueryBuilderScopeContext collect = qb.collect(); + for(Map.Entry entry: collect.getPropertyAliasMap().entrySet()) { + if(entry.getKey().equals(fieldString)) { + targets.addAll(entry.getValue().getPsiTargets()); + } + } + } + } + private void attachJoinGoto(StringLiteralExpression psiElement, List targets) { MethodMatcher.MethodMatchParameter methodMatchParameter = MatcherUtil.matchJoin(psiElement); @@ -76,11 +108,8 @@ private void attachJoinGoto(StringLiteralExpression psiElement, List if(phpClass != null) { targets.add(phpClass); } - } } - - } private void attachPropertyGoto(StringLiteralExpression psiElement, List targets) { diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/util/MatcherUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/util/MatcherUtil.java index 06037503c..5e443f8c9 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/util/MatcherUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/util/MatcherUtil.java @@ -60,5 +60,12 @@ public static MethodMatcher.MethodMatchParameter matchJoin(PsiElement psiElement } - + @Nullable + public static MethodMatcher.MethodMatchParameter matchWhere(PsiElement psiElement) { + return new MethodMatcher.StringParameterMatcher(psiElement, 0) + .withSignature("\\Doctrine\\ORM\\QueryBuilder", "andWhere") + .withSignature("\\Doctrine\\ORM\\QueryBuilder", "where") + .withSignature("\\Doctrine\\ORM\\QueryBuilder", "orWhere") + .match(); + } } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/util/QueryBuilderUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/util/QueryBuilderUtil.java index 5a95be04f..4d54883e0 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/util/QueryBuilderUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/doctrine/querybuilder/util/QueryBuilderUtil.java @@ -2,9 +2,12 @@ import fr.adrienbrault.idea.symfony2plugin.doctrine.ObjectRepositoryTypeProvider; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.HashSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @author Daniel Espendiller @@ -32,4 +35,35 @@ public static Collection extractQueryBuilderRepositoryParameters(@NotNul return results; } + + /** + * test "test.fooTeaa = aa" + */ + @Nullable + public static String getFieldString(@NotNull String content, int offset) { + if (offset > content.length()) { + return null; + } + + String substring1 = content.substring(0, offset); + Matcher matcherBefore = Pattern.compile("([\\w.]+)[\\s|>=<]?$").matcher(substring1); + if (!matcherBefore.find()) { + return null; + } + + String substring = content.substring(offset); + Matcher matcherAfter = Pattern.compile("^[\\s|>=<]?([\\w.]+)").matcher(substring); + if (!matcherAfter.find()) { + return null; + } + + String field = matcherBefore.group(1) + matcherAfter.group(1); + + // invalid field + if (!field.contains(".")) { + return null; + } + + return field; + } } diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/doctrine/querybuilder/util/QueryBuilderUtilTest.java b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/doctrine/querybuilder/util/QueryBuilderUtilTest.java index da67e2b60..4b1c47632 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/doctrine/querybuilder/util/QueryBuilderUtilTest.java +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/doctrine/querybuilder/util/QueryBuilderUtilTest.java @@ -1,8 +1,10 @@ package fr.adrienbrault.idea.symfony2plugin.tests.doctrine.querybuilder.util; +import com.intellij.openapi.util.Pair; import fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.util.QueryBuilderUtil; import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase; +import java.util.ArrayList; import java.util.Collection; /** @@ -25,4 +27,26 @@ public void testExtractQueryBuilderRepositoryParametersForClassConstant() { assertContainsElements(strings, "#K#C\\espend\\Doctrine\\ModelBundle\\Entity\\Car.class"); } + + public void testGetFieldString() { + Collection> items = new ArrayList<>() {{ + add(Pair.create("user.foo = :foo AND user.bar AND bar.foo", "user.bar")); + add(Pair.create("user.foo = :foo AND user.bar AND bar.foo", "user.bar")); + add(Pair.create("user.bar>=", "user.bar")); + add(Pair.create(">=user.bar", "user.bar")); + add(Pair.create("user.bar", "user.bar")); + add(Pair.create("user.bar", "user.bar")); + add(Pair.create("user.barÄ", "user.bar")); // nice usability but should this really match? + add(Pair.create("user.barÄ", "user.bar")); // nice usability but should this really match? + add(Pair.create("user", null)); + // add(Pair.create("user.bar AND", null)); // ??? + }}; + + for (Pair item : items) { + String content = item.getFirst(); + + String string = QueryBuilderUtil.getFieldString(content.replace("", ""), content.indexOf("")); + assertEquals(item.getSecond(), string); + } + } }