Skip to content

Commit 745d152

Browse files
authored
Merge pull request #68 from cedricziel/signal-slot-foo
[T3CMS] Contribute Signal/Slot References to slot methods
2 parents 8cb78e0 + 60fc738 commit 745d152

File tree

8 files changed

+350
-0
lines changed

8 files changed

+350
-0
lines changed

typo3-cms/src/main/java/com/cedricziel/idea/typo3/TYPO3CMSIcons.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ public interface TYPO3CMSIcons {
88
Icon TYPO3_ICON = IconLoader.getIcon("/icons/dist/icon-typo3.png");
99
Icon ROUTE_ICON = IconLoader.getIcon("/icons/dist/icon-navigate-route-definition.png");
1010
Icon ICON_NOT_RESOLVED = IconLoader.getIcon("/icons/dist/icon-icon-not-resolved.png");
11+
Icon SIGNAL_COMPLETED_METHOD = IconLoader.getIcon("/icons/dist/icon-typo3.png");
1112
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.cedricziel.idea.typo3;
2+
3+
import com.intellij.patterns.ElementPattern;
4+
import com.intellij.patterns.PlatformPatterns;
5+
import com.intellij.psi.PsiElement;
6+
import com.jetbrains.php.lang.psi.elements.MethodReference;
7+
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
8+
9+
public class TYPO3Patterns {
10+
/*
11+
* \TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class->connect(,,,'<caret>')
12+
*/
13+
public static ElementPattern<? extends PsiElement> connectSignalMethodName() {
14+
return PlatformPatterns
15+
.psiElement().withParent(
16+
PlatformPatterns.psiElement(StringLiteralExpression.class)
17+
)
18+
.withSuperParent(3, PlatformPatterns.psiElement(MethodReference.class));
19+
}
20+
21+
public static ElementPattern<? extends PsiElement> connectSignalMethodNameString() {
22+
return PlatformPatterns.psiElement(StringLiteralExpression.class)
23+
.withSuperParent(2, PlatformPatterns.psiElement(MethodReference.class));
24+
}
25+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.cedricziel.idea.typo3.dispatcher;
2+
3+
import com.cedricziel.idea.typo3.TYPO3Patterns;
4+
import com.intellij.openapi.util.TextRange;
5+
import com.intellij.psi.*;
6+
import com.intellij.psi.util.PsiTreeUtil;
7+
import com.intellij.util.ProcessingContext;
8+
import com.jetbrains.php.lang.psi.elements.ClassConstantReference;
9+
import com.jetbrains.php.lang.psi.elements.ParameterList;
10+
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
11+
import org.jetbrains.annotations.NotNull;
12+
13+
import java.util.Arrays;
14+
import java.util.List;
15+
16+
public class SignalDispatcherReferenceContributor extends PsiReferenceContributor {
17+
@Override
18+
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
19+
20+
registrar.registerReferenceProvider(
21+
TYPO3Patterns.connectSignalMethodNameString(),
22+
new PsiReferenceProvider() {
23+
@NotNull
24+
@Override
25+
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
26+
StringLiteralExpression subject = (StringLiteralExpression) element;
27+
ParameterList parameterList = (ParameterList) PsiTreeUtil.findFirstParent(subject, e -> e instanceof ParameterList);
28+
if (parameterList == null) {
29+
return PsiReference.EMPTY_ARRAY;
30+
}
31+
32+
List<PsiElement> parameters = Arrays.asList(parameterList.getParameters());
33+
if (parameters.indexOf(subject) == 3) {
34+
PsiElement className = parameters.get(2);
35+
if (className instanceof ClassConstantReference) {
36+
if (subject.getContents().length() > subject.getText().length()) {
37+
return new SignalSlotMethodReference[]{
38+
new SignalSlotMethodReference((ClassConstantReference) className, subject, new TextRange(1, subject.getContents().length()))
39+
};
40+
}
41+
42+
return new SignalSlotMethodReference[]{
43+
new SignalSlotMethodReference((ClassConstantReference) className, subject)
44+
};
45+
}
46+
if (className instanceof StringLiteralExpression) {
47+
if (subject.getContents().length() > subject.getText().length()) {
48+
return new SignalSlotMethodReference[]{
49+
new SignalSlotMethodReference((StringLiteralExpression) className, subject, new TextRange(1, subject.getContents().length()))
50+
};
51+
}
52+
53+
return new SignalSlotMethodReference[]{
54+
new SignalSlotMethodReference((StringLiteralExpression) className, subject)
55+
};
56+
}
57+
}
58+
59+
return PsiReference.EMPTY_ARRAY;
60+
}
61+
}
62+
);
63+
}
64+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.cedricziel.idea.typo3.dispatcher;
2+
3+
import com.cedricziel.idea.typo3.util.SignalSlotDispatcherUtil;
4+
import com.intellij.codeInsight.lookup.LookupElement;
5+
import com.intellij.openapi.util.TextRange;
6+
import com.intellij.psi.PsiElement;
7+
import com.intellij.psi.PsiElementResolveResult;
8+
import com.intellij.psi.PsiPolyVariantReferenceBase;
9+
import com.intellij.psi.ResolveResult;
10+
import com.intellij.psi.util.PsiTreeUtil;
11+
import com.jetbrains.php.lang.psi.elements.ClassConstantReference;
12+
import com.jetbrains.php.lang.psi.elements.ClassReference;
13+
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
14+
import org.jetbrains.annotations.NotNull;
15+
16+
import java.util.ArrayList;
17+
import java.util.List;
18+
19+
public class SignalSlotMethodReference extends PsiPolyVariantReferenceBase<PsiElement> {
20+
21+
private final String methodName;
22+
private final String classFqn;
23+
24+
public SignalSlotMethodReference(@NotNull ClassConstantReference classConstantReference, @NotNull StringLiteralExpression subject) {
25+
super(subject);
26+
27+
this.methodName = subject.getContents();
28+
29+
ClassReference classReference = PsiTreeUtil.findChildOfType(classConstantReference, ClassReference.class);
30+
if (classReference == null || classReference.getFQN() == null) {
31+
classFqn = "";
32+
subject.getContents();
33+
34+
return;
35+
}
36+
37+
this.classFqn = classReference.getFQN();
38+
}
39+
40+
public SignalSlotMethodReference(@NotNull ClassConstantReference classConstantReference, @NotNull StringLiteralExpression subject, TextRange range) {
41+
super(subject, range);
42+
43+
this.methodName = subject.getContents();
44+
45+
ClassReference classReference = PsiTreeUtil.findChildOfType(classConstantReference, ClassReference.class);
46+
if (classReference == null || classReference.getFQN() == null) {
47+
classFqn = "";
48+
49+
return;
50+
}
51+
52+
this.classFqn = classReference.getFQN();
53+
}
54+
55+
public SignalSlotMethodReference(@NotNull StringLiteralExpression className, @NotNull StringLiteralExpression subject) {
56+
super(subject);
57+
58+
this.classFqn = className.getContents();
59+
this.methodName = subject.getContents();
60+
}
61+
62+
public SignalSlotMethodReference(@NotNull StringLiteralExpression className, @NotNull StringLiteralExpression subject, TextRange range) {
63+
super(subject, range);
64+
65+
this.classFqn = className.getContents();
66+
this.methodName = subject.getContents();
67+
}
68+
69+
@NotNull
70+
@Override
71+
public ResolveResult[] multiResolve(boolean incompleteCode) {
72+
if (classFqn.isEmpty()) {
73+
return ResolveResult.EMPTY_ARRAY;
74+
}
75+
76+
List<ResolveResult> results = new ArrayList<>();
77+
PsiElement[] psiElements = SignalSlotDispatcherUtil.getSignalPsiElements(myElement.getProject(), classFqn, methodName);
78+
79+
for (PsiElement psiElement : psiElements) {
80+
results.add(new PsiElementResolveResult(psiElement));
81+
}
82+
83+
return results.toArray(new ResolveResult[0]);
84+
}
85+
86+
@NotNull
87+
@Override
88+
public Object[] getVariants() {
89+
if (classFqn.isEmpty()) {
90+
return LookupElement.EMPTY_ARRAY;
91+
}
92+
93+
return SignalSlotDispatcherUtil.getPossibleSlotMethodLookupElements(myElement.getProject(), classFqn);
94+
}
95+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.cedricziel.idea.typo3.util;
2+
3+
import com.intellij.codeInsight.lookup.LookupElement;
4+
import com.intellij.codeInsight.lookup.LookupElementBuilder;
5+
import com.intellij.openapi.project.Project;
6+
import com.intellij.psi.PsiElement;
7+
import com.jetbrains.php.PhpIndex;
8+
import com.jetbrains.php.lang.psi.elements.Method;
9+
import org.jetbrains.annotations.NotNull;
10+
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
14+
public class SignalSlotDispatcherUtil {
15+
@NotNull
16+
public static PsiElement[] getSignalPsiElements(final Project project, String fqn, final String methodName) {
17+
List<PsiElement> foundElements = new ArrayList<>();
18+
19+
PhpIndex.getInstance(project).getClassesByFQN(fqn).forEach(c -> {
20+
Method methodByName = c.findMethodByName(methodName);
21+
if (methodByName != null) {
22+
foundElements.add(methodByName);
23+
}
24+
});
25+
26+
return foundElements.toArray(new PsiElement[0]);
27+
}
28+
29+
public static LookupElement[] getPossibleSlotMethodLookupElements(@NotNull Project project, @NotNull String fqn) {
30+
List<LookupElement> lookupElements = new ArrayList<>();
31+
PhpIndex.getInstance(project).getClassesByFQN(fqn).forEach(c -> {
32+
for (Method method : c.getMethods()) {
33+
if (method.getModifier().isPublic()) {
34+
lookupElements.add(LookupElementBuilder.createWithIcon(method));
35+
}
36+
}
37+
});
38+
39+
return lookupElements.toArray(new LookupElement[0]);
40+
}
41+
}

typo3-cms/src/main/resources/META-INF/plugin.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ It is a great inspiration for possible solutions and parts of the code.</p>
221221

222222
<!-- userFunc -->
223223
<psi.referenceContributor implementation="com.cedricziel.idea.typo3.userFunc.UserFuncReferenceContributor"/>
224+
225+
<!-- signal / slot -->
226+
<psi.referenceContributor language="PHP"
227+
implementation="com.cedricziel.idea.typo3.dispatcher.SignalDispatcherReferenceContributor"/>
224228
</extensions>
225229

226230
<actions>
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.cedricziel.idea.typo3.dispatcher;
2+
3+
import com.cedricziel.idea.typo3.AbstractTestCase;
4+
import com.intellij.psi.PsiElement;
5+
import com.jetbrains.php.lang.PhpFileType;
6+
import com.jetbrains.php.lang.psi.elements.Method;
7+
8+
import java.util.List;
9+
10+
public class SignalDispatcherReferenceContributorTest extends AbstractTestCase {
11+
@Override
12+
protected String getTestDataPath() {
13+
return "testData/com/cedricziel/idea/typo3/dispatcher";
14+
}
15+
16+
public void testReferenceCanBeResolvedWithClassConstantReference() {
17+
myFixture.copyFileToProject("classes.php");
18+
19+
configureSignalSlotConnect(
20+
"\\TYPO3\\CMS\\Core\\Core\\ClassLoadingInformation::class",
21+
"'dumpClassLoadingInformat<caret>ion'"
22+
);
23+
24+
PsiElement elementAtCaret = myFixture.getElementAtCaret();
25+
assertNotNull(elementAtCaret);
26+
assertInstanceOf(elementAtCaret, Method.class);
27+
assertEquals("dumpClassLoadingInformation", ((Method) elementAtCaret).getName());
28+
}
29+
30+
public void testReferenceCanBeResolvedWithStringClassReference() {
31+
myFixture.copyFileToProject("classes.php");
32+
33+
configureSignalSlotConnect(
34+
"'\\TYPO3\\CMS\\Core\\Core\\ClassLoadingInformation'",
35+
"'dumpClassLoadingInformat<caret>ion'"
36+
);
37+
38+
PsiElement elementAtCaret = myFixture.getElementAtCaret();
39+
assertNotNull(elementAtCaret);
40+
assertInstanceOf(elementAtCaret, Method.class);
41+
assertEquals("dumpClassLoadingInformation", ((Method) elementAtCaret).getName());
42+
}
43+
44+
private void configureSignalSlotConnect(String classParameters, String methodParameter) {
45+
myFixture.configureByText(PhpFileType.INSTANCE, "<?php\n" +
46+
"/** @var \\TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher $signalSlotDispatcher */\n" +
47+
"$signalSlotDispatcher = \\TYPO3\\CMS\\Core\\Utility\\GeneralUtility::makeInstance(\\TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher::class);\n" +
48+
"$signalSlotDispatcher->connect(\n" +
49+
" \\TYPO3\\CMS\\Extensionmanager\\Utility\\InstallUtility::class, // Signal class name\n" +
50+
" 'foo', // Signal name\n" +
51+
" " + classParameters + ", // Slot class name\n" +
52+
" " + methodParameter + " // Slot name\n" +
53+
");"
54+
);
55+
}
56+
57+
public void testCanProvideVariantsWithClassConstantReference() {
58+
myFixture.copyFileToProject("classes.php");
59+
60+
configureSignalSlotConnect("\\TYPO3\\CMS\\Core\\Core\\ClassLoadingInformation::class", "'<caret>'");
61+
62+
myFixture.completeBasic();
63+
64+
List<String> lookupElementStrings = myFixture.getLookupElementStrings();
65+
assertTrue(lookupElementStrings.contains("dumpClassLoadingInformation"));
66+
assertTrue(lookupElementStrings.contains("anotherOne"));
67+
assertFalse(lookupElementStrings.contains("aPrivateOne"));
68+
assertFalse(lookupElementStrings.contains("aProtectedOne"));
69+
}
70+
71+
public void testCanProvideVariantsWithStringClassReference() {
72+
myFixture.copyFileToProject("classes.php");
73+
74+
configureSignalSlotConnect("'\\TYPO3\\CMS\\Core\\Core\\ClassLoadingInformation'", "'<caret>'");
75+
76+
myFixture.completeBasic();
77+
78+
List<String> lookupElementStrings = myFixture.getLookupElementStrings();
79+
assertTrue(lookupElementStrings.contains("dumpClassLoadingInformation"));
80+
assertTrue(lookupElementStrings.contains("anotherOne"));
81+
assertFalse(lookupElementStrings.contains("aPrivateOne"));
82+
assertFalse(lookupElementStrings.contains("aProtectedOne"));
83+
}
84+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace TYPO3\CMS\Extbase\SignalSlot {
4+
class Dispatcher
5+
{
6+
public function connect()
7+
{
8+
9+
}
10+
}
11+
}
12+
13+
namespace TYPO3\CMS\Core\Core {
14+
class ClassLoadingInformation
15+
{
16+
public function dumpClassLoadingInformation()
17+
{
18+
19+
}
20+
21+
public function anotherOne()
22+
{
23+
24+
}
25+
26+
private function aPrivateOne()
27+
{
28+
29+
}
30+
31+
private function aProtectedOne()
32+
{
33+
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)