-
-
Couldn't load subscription status.
- Fork 142
[WIP]Feature/doctrine support #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
88be504 dd46f64 a6fbfe1 059b0c1 60ed439 24ed394 b42231a a9f63cc 8bb6f31 11d3af2 ca2b58f f00493e 4117de1 dec9b34 0ba8adc 846f5f2 File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| package fr.adrienbrault.idea.symfony2plugin.doctrine; | ||
| | ||
| import com.intellij.patterns.PlatformPatterns; | ||
| import com.intellij.psi.*; | ||
| import com.intellij.util.ProcessingContext; | ||
| import com.jetbrains.php.lang.psi.elements.MethodReference; | ||
| import com.jetbrains.php.lang.psi.elements.ParameterList; | ||
| import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; | ||
| import fr.adrienbrault.idea.symfony2plugin.Symfony2InterfacesUtil; | ||
| import org.jetbrains.annotations.NotNull; | ||
| | ||
| /** | ||
| * @author Daniel Espendiller <daniel@espendiller.net> | ||
| */ | ||
| public class DoctrineEntityReferenceContributor extends PsiReferenceContributor { | ||
| | ||
| @Override | ||
| public void registerReferenceProviders(PsiReferenceRegistrar psiReferenceRegistrar) { | ||
| psiReferenceRegistrar.registerReferenceProvider( | ||
| PlatformPatterns.psiElement(StringLiteralExpression.class), | ||
| new PsiReferenceProvider() { | ||
| @NotNull | ||
| @Override | ||
| public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement, @NotNull ProcessingContext processingContext) { | ||
| if (!(psiElement.getContext() instanceof ParameterList)) { | ||
| return new PsiReference[0]; | ||
| } | ||
| ParameterList parameterList = (ParameterList) psiElement.getContext(); | ||
| | ||
| if (!(parameterList.getContext() instanceof MethodReference)) { | ||
| return new PsiReference[0]; | ||
| } | ||
| MethodReference method = (MethodReference) parameterList.getContext(); | ||
| Symfony2InterfacesUtil interfacesUtil = new Symfony2InterfacesUtil(); | ||
| if (!interfacesUtil.isGetRepositoryCall(method)) { | ||
| return new PsiReference[0]; | ||
| } | ||
| | ||
| return new PsiReference[]{ new EntityReference((StringLiteralExpression) psiElement) }; | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| package fr.adrienbrault.idea.symfony2plugin.doctrine; | ||
| | ||
| import com.intellij.openapi.project.Project; | ||
| import com.jetbrains.php.PhpIndex; | ||
| import com.jetbrains.php.lang.psi.elements.PhpClass; | ||
| import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; | ||
| | ||
| import java.util.Collection; | ||
| import java.util.Map; | ||
| | ||
| /** | ||
| * @author Daniel Espendiller <daniel@espendiller.net> | ||
| */ | ||
| public class EntityHelper { | ||
| | ||
| /** | ||
| * | ||
| * @param project PHPStorm projects | ||
| * @param shortcutName name as MyBundle\Entity\Model or MyBundle:Model | ||
| * @return null|PhpClass | ||
| */ | ||
| public static PhpClass resolveShortcutName(Project project, String shortcutName) { | ||
| | ||
| if(shortcutName == null) { | ||
| return null; | ||
| } | ||
| | ||
| String entity_name = shortcutName; | ||
| | ||
| // resolve: | ||
| // MyBundle:Model -> MyBundle\Entity\Model | ||
| // MyBundle:Folder\Model -> MyBundle\Entity\Folder\Model | ||
| if (shortcutName.contains(":")) { | ||
| | ||
| Symfony2ProjectComponent symfony2ProjectComponent = project.getComponent(Symfony2ProjectComponent.class); | ||
| Map<String, String> em = symfony2ProjectComponent.getEntityNamespacesMap(); | ||
| | ||
| int firstDirectorySeparatorIndex = shortcutName.indexOf(":"); | ||
| | ||
| String bundlename = shortcutName.substring(0, firstDirectorySeparatorIndex); | ||
| String entityName = shortcutName.substring(firstDirectorySeparatorIndex + 1); | ||
| | ||
| String namespace = em.get(bundlename); | ||
| | ||
| if(namespace == null) { | ||
| return null; | ||
| } | ||
| | ||
| entity_name = namespace + "\\" + entityName; | ||
| } | ||
| | ||
| // only use them on entity namespace | ||
| if(!entity_name.contains("\\")) { | ||
| return null; | ||
| } | ||
| | ||
| // dont we have any unique class getting method here? | ||
| PhpIndex phpIndex = PhpIndex.getInstance(project); | ||
| Collection<PhpClass> entity_classes = phpIndex.getClassesByFQN(entity_name); | ||
| if(!entity_classes.isEmpty()){ | ||
| return entity_classes.iterator().next(); | ||
| } | ||
| | ||
| return null; | ||
| } | ||
| | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| package fr.adrienbrault.idea.symfony2plugin.doctrine; | ||
| | ||
| import com.intellij.psi.*; | ||
| import com.intellij.codeInsight.lookup.LookupElement; | ||
| import com.intellij.psi.PsiElement; | ||
| import com.jetbrains.php.PhpIndex; | ||
| import com.jetbrains.php.lang.psi.elements.PhpClass; | ||
| import com.jetbrains.php.lang.psi.elements.PhpNamespace; | ||
| import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; | ||
| import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; | ||
| import fr.adrienbrault.idea.symfony2plugin.doctrine.dict.DoctrineEntityLookupElement; | ||
| import org.jetbrains.annotations.NotNull; | ||
| import org.jetbrains.annotations.Nullable; | ||
| | ||
| import java.util.*; | ||
| | ||
| /** | ||
| * @author Daniel Espendiller <daniel@espendiller.net> | ||
| */ | ||
| public class EntityReference extends PsiReferenceBase<PsiElement> implements PsiReference { | ||
| private String entityName; | ||
| | ||
| public EntityReference(@NotNull StringLiteralExpression element) { | ||
| super(element); | ||
| | ||
| entityName = element.getText().substring( | ||
| element.getValueRange().getStartOffset(), | ||
| element.getValueRange().getEndOffset() | ||
| ); | ||
| } | ||
| | ||
| @Nullable | ||
| @Override | ||
| public PsiElement resolve() { | ||
| | ||
| PhpClass entity = EntityHelper.resolveShortcutName(getElement().getProject(), this.entityName); | ||
| if(entity != null) { | ||
| return new PsiElementResolveResult(entity).getElement(); | ||
| } | ||
| | ||
| return null; | ||
| } | ||
| | ||
| @NotNull | ||
| @Override | ||
| public Object[] getVariants() { | ||
| | ||
| PhpIndex phpIndex = PhpIndex.getInstance(getElement().getProject()); | ||
| | ||
| Symfony2ProjectComponent symfony2ProjectComponent = getElement().getProject().getComponent(Symfony2ProjectComponent.class); | ||
| Map<String, String> em = symfony2ProjectComponent.getEntityNamespacesMap(); | ||
| | ||
| List<LookupElement> results = new ArrayList<LookupElement>(); | ||
| for (String shortcutName : em.keySet()) { | ||
| | ||
| // search for classes that match the symfony2 namings | ||
| Collection<PhpNamespace> entities = phpIndex.getNamespacesByName(em.get(shortcutName)); | ||
| | ||
| // @TODO: it looks like PhpIndex cant search for classes like \ns\Path\*\... | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replacing | ||
| // temporary only use flat entities and dont support "MyBundle:Folder\Entity" | ||
| for (PhpNamespace entity_files : entities) { | ||
| | ||
| // build our symfony2 shortcut | ||
| String filename = entity_files.getContainingFile().getName(); | ||
| String className = filename.substring(0, filename.lastIndexOf('.')); | ||
| String repoName = shortcutName + ':' + className; | ||
| | ||
| // dont add Repository classes and abstract entities | ||
| if(!className.endsWith("Repository") && !className.equals("Repository")) { | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if the user has an entity named Repository because he is managing a Repository ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you really want to exclude the repositories, maybe using | ||
| for (PhpClass entityClass : phpIndex.getClassesByFQN(em.get(shortcutName) + "\\" + className)) { | ||
| if(!entityClass.isAbstract()) { | ||
| results.add(new DoctrineEntityLookupElement(repoName, entityClass)); | ||
| } | ||
| } | ||
| } | ||
| | ||
| } | ||
| | ||
| } | ||
| | ||
| return results.toArray(); | ||
| } | ||
| | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| package fr.adrienbrault.idea.symfony2plugin.doctrine; | ||
| | ||
| import com.intellij.openapi.project.DumbService; | ||
| import com.intellij.psi.PsiElement; | ||
| import com.jetbrains.php.lang.psi.elements.MethodReference; | ||
| import com.jetbrains.php.lang.psi.elements.PhpClass; | ||
| import com.jetbrains.php.lang.psi.resolve.types.PhpType; | ||
| import com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider; | ||
| import fr.adrienbrault.idea.symfony2plugin.Symfony2InterfacesUtil; | ||
| import org.jetbrains.annotations.Nullable; | ||
| | ||
| /** | ||
| * @author Daniel Espendiller <daniel@espendiller.net> | ||
| */ | ||
| public class ObjectRepositoryResultTypeProvider implements PhpTypeProvider { | ||
| | ||
| @Nullable | ||
| @Override | ||
| public PhpType getType(PsiElement e) { | ||
| if (DumbService.getInstance(e.getProject()).isDumb()) { | ||
| return null; | ||
| } | ||
| | ||
| Symfony2InterfacesUtil interfacesUtil = new Symfony2InterfacesUtil(); | ||
| if (!interfacesUtil.isObjectRepositoryCall(e)) { | ||
| return null; | ||
| } | ||
| | ||
| MethodReference met = (MethodReference) e; | ||
| String methodName = met.getName(); | ||
| | ||
| // at least one parameter is necessary on some finds | ||
| if(null != methodName && !methodName.equals("findAll")) { | ||
| PsiElement[] parameters = met.getParameters(); | ||
| if(parameters.length == 0) { | ||
| return null; | ||
| } | ||
| } | ||
| | ||
| // @TODO: find the previously defined type instead of try it on the parameter, we now can rely on it! | ||
| // find the called repository name on method before | ||
| if(!(met.getFirstChild() instanceof MethodReference)) { | ||
| return null; | ||
| } | ||
| | ||
| String repositoryName = Symfony2InterfacesUtil.getFirstArgumentStringValue((MethodReference) met.getFirstChild()); | ||
| PhpClass phpClass = EntityHelper.resolveShortcutName(e.getProject(), repositoryName); | ||
| | ||
| if(phpClass == null) { | ||
| return null; | ||
| } | ||
| | ||
| | ||
| if(null != methodName && (methodName.equals("findAll") || methodName.equals("findBy"))) { | ||
| return new PhpType().add(phpClass.getFQN() + "[]"); | ||
| } | ||
| | ||
| return new PhpType().add(phpClass); | ||
| } | ||
| | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| package fr.adrienbrault.idea.symfony2plugin.doctrine; | ||
| | ||
| import com.intellij.openapi.project.DumbService; | ||
| import com.intellij.psi.PsiElement; | ||
| import com.jetbrains.php.lang.psi.elements.MethodReference; | ||
| import com.jetbrains.php.lang.psi.elements.PhpClass; | ||
| import com.jetbrains.php.lang.psi.resolve.types.PhpType; | ||
| import com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider; | ||
| import fr.adrienbrault.idea.symfony2plugin.Symfony2InterfacesUtil; | ||
| import org.jetbrains.annotations.Nullable; | ||
| | ||
| /** | ||
| * @author Daniel Espendiller <daniel@espendiller.net> | ||
| */ | ||
| public class ObjectRepositoryTypeProvider implements PhpTypeProvider { | ||
| | ||
| | ||
| @Nullable | ||
| @Override | ||
| public PhpType getType(PsiElement e) { | ||
| if (DumbService.getInstance(e.getProject()).isDumb()) { | ||
| return null; | ||
| } | ||
| | ||
| Symfony2InterfacesUtil interfacesUtil = new Symfony2InterfacesUtil(); | ||
| if (!interfacesUtil.isGetRepositoryCall(e)) { | ||
| return null; | ||
| } | ||
| | ||
| String repositoryName = Symfony2InterfacesUtil.getFirstArgumentStringValue((MethodReference) e); | ||
| if (null == repositoryName) { | ||
| return null; | ||
| } | ||
| | ||
| // @TODO: parse xml or yml for repositoryClass? | ||
| PhpClass phpClass = EntityHelper.resolveShortcutName(e.getProject(), repositoryName + "Repository"); | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't concatenate "Repository" to repositoryName here, it is confusing. I'd assign the result to repositoryName above. | ||
| | ||
| if(phpClass == null) { | ||
| return null; | ||
| } | ||
| | ||
| return new PhpType().add(phpClass); | ||
| } | ||
| | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you should also support
\\Doctrine\\Common\\Persistence\\ObjectManager::getRepositoryfor people getting the repository from the EntityManager