Skip to content
Merged
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ before_install:
- "export ORG_GRADLE_PROJECT_annotationPluginVersion=${ANNOTATION_PLUGIN_VERSION}"

env:
- PHPSTORM_ENV="skip incomplete" IDEA_VERSION="IU-2019.2" PHP_PLUGIN_VERSION="192.5728.108" TWIG_PLUGIN_VERSION="192.5728.26" TOOLBOX_PLUGIN_VERSION="0.4.6" ANNOTATION_PLUGIN_VERSION="5.3" DQL_PLUGIN_VERSION="192.5728.12"
- PHPSTORM_ENV="skip incomplete" IDEA_VERSION="IU-2020.1" PHP_PLUGIN_VERSION="201.6668.153" TWIG_PLUGIN_VERSION="201.6668.153" TOOLBOX_PLUGIN_VERSION="0.4.6" ANNOTATION_PLUGIN_VERSION="5.3" DQL_PLUGIN_VERSION="201.6668.60"

script:
- "./gradlew check verifyPlugin buildPlugin"
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ intellij {
}

patchPluginXml {
sinceBuild '192'
sinceBuild '201'
changeNotes = htmlFixer('src/main/resources/META-INF/change-notes.html')
}

Expand Down
8 changes: 4 additions & 4 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ideaVersion = IU-2019.2
phpPluginVersion = 192.5728.108
twigPluginVersion = 192.5728.26
dqlPluginVersion = 192.5728.12
ideaVersion = IU-2020.1
phpPluginVersion = 201.6668.153
twigPluginVersion = 201.6668.153
dqlPluginVersion = 201.6668.60
toolboxPluginVersion = 0.4.6
annotationPluginVersion = 5.3
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ private Map<String, LookupElement> getModelLookupElements() {
}

@Override
public void processNames(@NotNull Processor<String> processor, @NotNull GlobalSearchScope scope, @Nullable IdFilter filter) {
public void processNames(@NotNull Processor<? super String> processor, @NotNull GlobalSearchScope scope, @Nullable IdFilter filter) {
for(String name: getServiceCollector().getServices().keySet()) {
processor.process(name);
}
Expand Down Expand Up @@ -174,7 +174,7 @@ public void processNames(@NotNull Processor<String> processor, @NotNull GlobalSe
}

@Override
public void processElementsWithName(@NotNull String name, @NotNull Processor<NavigationItem> processor, @NotNull FindSymbolParameters parameters) {
public void processElementsWithName(@NotNull String name, @NotNull Processor<? super NavigationItem> processor, @NotNull FindSymbolParameters parameters) {

for(ContainerService containerService: getServiceCollector().collect()) {
if(containerService.getName().equals(name)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,35 @@

import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.php.lang.parser.PhpElementTypes;
import com.jetbrains.php.lang.psi.elements.Method;
import com.jetbrains.php.lang.psi.elements.MethodReference;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import com.jetbrains.php.lang.psi.elements.PhpNamedElement;
import com.jetbrains.php.lang.psi.resolve.types.PhpType;
import com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider3;
import com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider4;
import fr.adrienbrault.idea.symfony2plugin.Settings;
import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher;
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
import fr.adrienbrault.idea.symfony2plugin.util.PhpTypeProviderUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

/**
* Resolve "find*" and attach the entity from the getRepository method
*
* "$om->getRepository('\Foo\Bar')->find('foobar')->get<caret>Id()"
*
* @author Daniel Espendiller <[email protected]>
*/
public class ObjectRepositoryResultTypeProvider implements PhpTypeProvider3 {
private static MethodMatcher.CallToSignature[] FIND_SIGNATURES = new MethodMatcher.CallToSignature[] {
public class ObjectRepositoryResultTypeProvider implements PhpTypeProvider4 {
private static final MethodMatcher.CallToSignature[] FIND_SIGNATURES = new MethodMatcher.CallToSignature[] {
new MethodMatcher.CallToSignature("\\Doctrine\\Common\\Persistence\\ObjectRepository", "find"),
new MethodMatcher.CallToSignature("\\Doctrine\\Common\\Persistence\\ObjectRepository", "findOneBy"),
new MethodMatcher.CallToSignature("\\Doctrine\\Common\\Persistence\\ObjectRepository", "findAll"),
Expand All @@ -48,18 +51,7 @@ public char getKey() {
@Nullable
@Override
public PhpType getType(PsiElement e) {
if (!Settings.getInstance(e.getProject()).pluginEnabled) {
return null;
}

// filter out method calls without parameter
// $this->get('service_name')
if(!PlatformPatterns
.psiElement(PhpElementTypes.METHOD_REFERENCE)
.withChild(PlatformPatterns
.psiElement(PhpElementTypes.PARAMETER_LIST)
).accepts(e)) {

if (!(e instanceof MethodReference) || !Settings.getInstance(e.getProject()).pluginEnabled) {
return null;
}

Expand All @@ -75,16 +67,6 @@ public PhpType getType(PsiElement e) {
return null;
}

// at least one parameter is necessary on some finds
PsiElement[] parameters = methodRef.getParameters();
if(!methodRefName.equals("findAll")) {
if(parameters.length == 0) {
return null;
}
} else if(parameters.length != 0) {
return null;
}

// we can get the repository name from the signature calls
// #M#?#M#?#M#C\Foo\Bar\Controller\BarController.get?doctrine.getRepository?EntityBundle:User.find
String repositorySignature = methodRef.getSignature();
Expand All @@ -105,57 +87,64 @@ public PhpType getType(PsiElement e) {
return new PhpType().add("#" + this.getKey() + refSignature + TRIM_KEY + repositorySignature);
}

@Nullable
@Override
public Collection<? extends PhpNamedElement> getBySignature(String expression, Set<String> visited, int depth, Project project) {
// get back our original call
int endIndex = expression.lastIndexOf(TRIM_KEY);
public PhpType complete(String s, Project project) {
int endIndex = s.lastIndexOf(TRIM_KEY);
if(endIndex == -1) {
return Collections.emptySet();
}

String originalSignature = expression.substring(0, endIndex);
String parameter = expression.substring(endIndex + 1);

// search for called method
PhpIndex phpIndex = PhpIndex.getInstance(project);
Collection<? extends PhpNamedElement> phpNamedElementCollections = PhpTypeProviderUtil.getTypeSignature(phpIndex, originalSignature);
if(phpNamedElementCollections.size() == 0) {
return Collections.emptySet();
}

Method method = getObjectRepositoryCall(phpNamedElementCollections);
if(method == null) {
return Collections.emptySet();
return null;
}

// we can also pipe php references signatures and resolve them here
// overwrite parameter to get string value
parameter = PhpTypeProviderUtil.getResolvedParameter(phpIndex, parameter);
String originalSignature = s.substring(0, endIndex);
String parameter = s.substring(endIndex + 1);
parameter = PhpTypeProviderUtil.getResolvedParameter(PhpIndex.getInstance(project), parameter);
if(parameter == null) {
return Collections.emptySet();
return null;
}

PhpClass phpClass = EntityHelper.resolveShortcutName(project, parameter);
if(phpClass == null) {
return Collections.emptySet();
return null;
}

String name = method.getName();
if(name.equals("findAll") || name.equals("findBy")) {
method.getType().add(phpClass.getFQN() + "[]");
return phpNamedElementCollections;
PhpIndex phpIndex = PhpIndex.getInstance(project);

Collection<? extends PhpNamedElement> typeSignature = PhpTypeProviderUtil.getTypeSignature(phpIndex, originalSignature);

// ->getRepository(SecondaryMarket::class)->findAll() => "findAll", but only if its a instance of this method;
// so non Doctrine method are already filtered
Set<String> resolveMethods = getObjectRepositoryCall(typeSignature).stream()
.map(PhpNamedElement::getName)
.collect(Collectors.toSet());

if (resolveMethods.isEmpty()) {
return null;
}

return PhpTypeProviderUtil.mergeSignatureResults(phpNamedElementCollections, phpClass);
PhpType phpType = new PhpType();

resolveMethods.stream()
.map(name -> name.equals("findAll") || name.equals("findBy") ? phpClass.getFQN() + "[]" : phpClass.getFQN())
.collect(Collectors.toSet())
.forEach(phpType::add);

return phpType;
}

@Override
public Collection<? extends PhpNamedElement> getBySignature(String expression, Set<String> visited, int depth, Project project) {
return null;
}

private Method getObjectRepositoryCall(Collection<? extends PhpNamedElement> phpNamedElements) {
@NotNull
private Collection<Method> getObjectRepositoryCall(Collection<? extends PhpNamedElement> phpNamedElements) {
Collection<Method> methods = new HashSet<>();
for (PhpNamedElement phpNamedElement: phpNamedElements) {
if(phpNamedElement instanceof Method && PhpElementsUtil.isMethodInstanceOf((Method) phpNamedElement, FIND_SIGNATURES)) {
return (Method) phpNamedElement;
methods.add((Method) phpNamedElement);
}
}

return null;
return methods;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.intellij.lang.Language;
import com.intellij.lang.injection.MultiHostInjector;
import com.intellij.lang.injection.MultiHostRegistrar;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLanguageInjectionHost;
import com.jetbrains.php.lang.psi.elements.*;
Expand All @@ -12,6 +11,7 @@
import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher;
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -41,10 +41,10 @@ public class ParameterLanguageInjector implements MultiHostInjector {
};

private final MethodLanguageInjection[] LANGUAGE_INJECTIONS = {
new MethodLanguageInjection(LANGUAGE_ID_CSS, "@media all { ", " }", CSS_SELECTOR_SIGNATURES),
new MethodLanguageInjection(LANGUAGE_ID_XPATH, null, null, XPATH_SIGNATURES),
new MethodLanguageInjection(LANGUAGE_ID_JSON, null, null, JSON_SIGNATURES),
new MethodLanguageInjection(LANGUAGE_ID_DQL, null, null, DQL_SIGNATURES),
new MethodLanguageInjection(LANGUAGE_ID_CSS, "@media all { ", " }", CSS_SELECTOR_SIGNATURES),
new MethodLanguageInjection(LANGUAGE_ID_XPATH, null, null, XPATH_SIGNATURES),
new MethodLanguageInjection(LANGUAGE_ID_JSON, null, null, JSON_SIGNATURES),
new MethodLanguageInjection(LANGUAGE_ID_DQL, null, null, DQL_SIGNATURES),
};

public static final String LANGUAGE_ID_CSS = "CSS";
Expand Down Expand Up @@ -88,26 +88,23 @@ public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull
}

for (MethodLanguageInjection languageInjection : LANGUAGE_INJECTIONS) {
Language language = languageInjection.getLanguage();
if (language == null) {
continue;
}
// $crawler->filter('...')
// $em->createQuery('...')
// JsonResponse::fromJsonString('...')
if (parent instanceof MethodReference) {
if (PhpElementsUtil.isMethodReferenceInstanceOf((MethodReference) parent, languageInjection.getSignatures())) {
injectLanguage(registrar, expr, language, languageInjection);
injectLanguage(registrar, expr, languageInjection);
return;
}
}
// $dql = "...";
else if (parent instanceof AssignmentExpression) {
if (LANGUAGE_ID_DQL.equals(language.getID())) {
Language language = languageInjection.getLanguage();
if (language != null && LANGUAGE_ID_DQL.equals(language.getID())) {
PhpPsiElement variable = ((AssignmentExpression) parent).getVariable();
if (variable instanceof Variable) {
if (DQL_VARIABLE_NAME.equals(variable.getName())) {
injectLanguage(registrar, expr, language, languageInjection);
injectLanguage(registrar, expr, languageInjection);
return;
}
}
Expand All @@ -117,28 +114,33 @@ else if (parent instanceof AssignmentExpression) {

}

private void injectLanguage(@NotNull MultiHostRegistrar registrar, @NotNull StringLiteralExpressionImpl element, Language language, MethodLanguageInjection languageInjection) {
private void injectLanguage(@NotNull MultiHostRegistrar registrar, @NotNull StringLiteralExpressionImpl element, MethodLanguageInjection languageInjection) {
Language language = languageInjection.getLanguage();
if (language == null) {
return;
}

registrar.startInjecting(language)
.addPlace(languageInjection.getPrefix(), languageInjection.getSuffix(), element, element.getValueRange())
.doneInjecting();
.addPlace(languageInjection.getPrefix(), languageInjection.getSuffix(), element, element.getValueRange())
.doneInjecting();
}

private class MethodLanguageInjection {
private final Language language;
private static class MethodLanguageInjection {
private final String language;
private final String prefix;
private final String suffix;
private final MethodMatcher.CallToSignature[] signatures;

MethodLanguageInjection(@NotNull String languageId, String prefix, String suffix, MethodMatcher.CallToSignature[] signatures) {

this.language = Language.findLanguageByID(languageId);
MethodLanguageInjection(String languageId, String prefix, String suffix, MethodMatcher.CallToSignature[] signatures) {
this.language = languageId;
this.prefix = prefix;
this.suffix = suffix;
this.signatures = signatures;
}

@Nullable
public Language getLanguage() {
return language;
return Language.findLanguageByID(this.language);
}

public String getPrefix() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,17 +393,17 @@ public static Map<String, Route> getRoutesInsideUrlGeneratorFile(@NotNull PsiFil

// Symfony < 2.8
// static private $declaredRoutes = array(...)
for(Field field: phpClass.getFields()) {
if(!field.getName().equals("declaredRoutes")) {
continue;
}
// only "getOwnFields" is uncached and dont breaks; find* methods are cached resulting in exceptions
Field[] ownFields = phpClass.getOwnFields();
for (Field ownField : ownFields) {
if ("declaredRoutes".equals(ownField.getName())) {
PsiElement defaultValue = ownField.getDefaultValue();
if(!(defaultValue instanceof ArrayCreationExpression)) {
continue;
}

PsiElement defaultValue = field.getDefaultValue();
if(!(defaultValue instanceof ArrayCreationExpression)) {
continue;
collectRoutesOnArrayCreation(routes, (ArrayCreationExpression) defaultValue);
}

collectRoutesOnArrayCreation(routes, (ArrayCreationExpression) defaultValue);
}

// Symfony >= 2.8
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,13 @@
]]></description>

<!-- please see http://confluence.jetbrains.net/display/IDEADEV/Build+Number+Ranges for description -->
<idea-version since-build="182.0"/>
<idea-version since-build="201.0"/>

<extensions defaultExtensionNs="com.jetbrains.php">
<typeProvider3 implementation="fr.adrienbrault.idea.symfony2plugin.dic.SymfonyContainerTypeProvider"/>
<typeProvider3 implementation="fr.adrienbrault.idea.symfony2plugin.util.EventDispatcherTypeProvider"/>
<typeProvider3 implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.ObjectRepositoryTypeProvider"/>
<typeProvider3 implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.ObjectRepositoryResultTypeProvider"/>
<typeProvider4 implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.ObjectRepositoryResultTypeProvider"/>
<typeProvider3 implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.ObjectManagerFindTypeProvider"/>
<typeProvider3 implementation="fr.adrienbrault.idea.symfony2plugin.assistant.signature.MethodSignatureTypeProvider"/>
<libraryRoot id="symfony_meta" path="/symfony-meta/" runtime="false"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ public void assertLineMarker(@NotNull PsiElement psiElement, @NotNull LineMarker

final List<PsiElement> elements = collectPsiElementsRecursive(psiElement);

for (LineMarkerProvider lineMarkerProvider : LineMarkerProviders.INSTANCE.allForLanguage(psiElement.getLanguage())) {
for (LineMarkerProvider lineMarkerProvider : LineMarkerProviders.getInstance().allForLanguage(psiElement.getLanguage())) {
Collection<LineMarkerInfo> lineMarkerInfos = new ArrayList<LineMarkerInfo>();
lineMarkerProvider.collectSlowLineMarkers(elements, lineMarkerInfos);

Expand All @@ -600,7 +600,7 @@ public void assertLineMarkerIsEmpty(@NotNull PsiElement psiElement) {

final List<PsiElement> elements = collectPsiElementsRecursive(psiElement);

for (LineMarkerProvider lineMarkerProvider : LineMarkerProviders.INSTANCE.allForLanguage(psiElement.getLanguage())) {
for (LineMarkerProvider lineMarkerProvider : LineMarkerProviders.getInstance().allForLanguage(psiElement.getLanguage())) {
Collection<LineMarkerInfo> lineMarkerInfos = new ArrayList<LineMarkerInfo>();
lineMarkerProvider.collectSlowLineMarkers(elements, lineMarkerInfos);

Expand Down
Loading