Skip to content

Commit c344904

Browse files
committed
feat: support container::invoke method, closing #26
1 parent 124d2ca commit c344904

File tree

9 files changed

+237
-1
lines changed

9 files changed

+237
-1
lines changed

src/main/kotlin/com/github/tempest/framework/TempestFrameworkClasses.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package com.github.tempest.framework
22

33
object TempestFrameworkClasses {
44
const val ConsoleCommand = "\\Tempest\\Console\\ConsoleCommand"
5-
const val FUNCTION_URI = "\\Tempest\\uri"
5+
const val CONTAINER = "\\Tempest\\Container\\Container"
6+
const val CONTAINER_INVOKE = "\\Tempest\\Container\\Container::invoke"
7+
8+
const val FUNCTION_URI = "\\Tempest\\Router\\uri"
69

710
const val ROUTER_GET = "\\Tempest\\Router\\Get"
811
const val ROUTER_POST = "\\Tempest\\Router\\Post"
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.github.tempest.framework.container.completion
2+
3+
import com.github.tempest.framework.container.references.ContainerParameterLookupElementBuilder
4+
import com.github.tempest.framework.container.references.ContainerPsiUtils
5+
import com.intellij.codeInsight.completion.CompletionContributor
6+
import com.intellij.codeInsight.completion.CompletionParameters
7+
import com.intellij.codeInsight.completion.CompletionProvider
8+
import com.intellij.codeInsight.completion.CompletionResultSet
9+
import com.intellij.codeInsight.completion.CompletionType
10+
import com.intellij.patterns.PlatformPatterns
11+
import com.intellij.util.ProcessingContext
12+
import com.jetbrains.php.lang.psi.elements.ConstantReference
13+
import com.jetbrains.php.lang.psi.elements.MethodReference
14+
import com.jetbrains.php.lang.psi.elements.ParameterList
15+
16+
class ContainerCompletionContributor : CompletionContributor() {
17+
init {
18+
extend(
19+
CompletionType.BASIC,
20+
PlatformPatterns.psiElement()
21+
.withParent(
22+
PlatformPatterns.or(
23+
PlatformPatterns.psiElement(ConstantReference::class.java)
24+
.withParent(PlatformPatterns.psiElement(ParameterList::class.java))
25+
.withSuperParent(
26+
2,
27+
PlatformPatterns.psiElement(MethodReference::class.java),
28+
),
29+
PlatformPatterns.psiElement(ParameterList::class.java)
30+
.withParent(PlatformPatterns.psiElement(MethodReference::class.java)),
31+
)
32+
),
33+
object : CompletionProvider<CompletionParameters>() {
34+
override fun addCompletions(
35+
parameters: CompletionParameters,
36+
context: ProcessingContext,
37+
result: CompletionResultSet
38+
) {
39+
// println("container completion: ${parameters.position}")
40+
val element = when (parameters.position.parent) {
41+
is ConstantReference -> parameters.position.parent
42+
is ParameterList -> parameters.position
43+
else -> return
44+
}
45+
val parameterList = element.parent as? ParameterList ?: return
46+
val function = parameterList.parent as? MethodReference ?: return
47+
if (!ContainerPsiUtils.isInvokeMethod(function)) return
48+
if (parameterList.parameters.isEmpty()) return
49+
50+
val firstParameter = function.parameters[0]
51+
if (firstParameter == element) return
52+
53+
com.github.tempest.framework.container.references.ContainerResolveUtils
54+
.resolveCached(firstParameter)
55+
.flatMap { ContainerParameterLookupElementBuilder.create(it) }
56+
.apply { result.addAllElements(this) }
57+
.apply { if (isNotEmpty()) result.stopHere() }
58+
}
59+
60+
}
61+
)
62+
}
63+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.github.tempest.framework.container.references
2+
3+
import com.github.tempest.framework.common.completion.TopPriorityLookupElement
4+
import com.github.tempest.framework.common.insertHandler.InsertTextInsertHandler
5+
import com.intellij.codeInsight.completion.DeclarativeInsertHandler
6+
import com.intellij.codeInsight.completion.PrioritizedLookupElement
7+
import com.intellij.codeInsight.lookup.LookupElementBuilder
8+
import com.jetbrains.php.PhpIcons
9+
import com.jetbrains.php.lang.PhpLangUtil
10+
import com.jetbrains.php.lang.psi.elements.Function
11+
12+
object ContainerParameterLookupElementBuilder {
13+
fun create(target: Function) = target
14+
.parameters
15+
.map { parameter ->
16+
val methodFqn = PhpLangUtil.toPresentableFQN(target.fqn.replace('.', ':'))
17+
LookupElementBuilder.create(parameter.name)
18+
.withIcon(PhpIcons.PARAMETER)
19+
.withTailText(" [$methodFqn]")
20+
.withTypeText(parameter.text)
21+
.withInsertHandler { context, element ->
22+
InsertTextInsertHandler(
23+
": ",
24+
DeclarativeInsertHandler.PopupOptions.MemberLookup
25+
)
26+
.handleInsert(context, element)
27+
}
28+
.let { PrioritizedLookupElement.withPriority(it, 10000.0) }
29+
.let { PrioritizedLookupElement.withExplicitProximity(it, 10000) }
30+
.let { TopPriorityLookupElement(it) }
31+
}
32+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.github.tempest.framework.container.references
2+
3+
import com.intellij.openapi.util.TextRange
4+
import com.intellij.psi.PsiElement
5+
import com.intellij.psi.PsiReferenceBase
6+
import com.jetbrains.php.lang.psi.PhpPsiElementFactory
7+
8+
class ContainerParameterReference(
9+
val hostElement: PsiElement,
10+
val routeReferenceElement: PsiElement,
11+
val parameterName: String,
12+
textRange: TextRange,
13+
) : PsiReferenceBase<PsiElement>(hostElement, textRange) {
14+
override fun resolve() = ContainerResolveUtils
15+
.resolveCached(routeReferenceElement)
16+
.firstOrNull()
17+
?.getParameter(parameterName)
18+
19+
override fun getVariants() = ContainerResolveUtils
20+
.resolveCached(routeReferenceElement)
21+
.flatMap { ContainerParameterLookupElementBuilder.create(it) }
22+
.toTypedArray()
23+
24+
override fun handleElementRename(newElementName: String): PsiElement? {
25+
return hostElement.findElementAt(rangeInElement.startOffset)
26+
?.replace(
27+
PhpPsiElementFactory.createNamedArgumentNameIdentifier(
28+
hostElement.project,
29+
newElementName,
30+
)
31+
)
32+
}
33+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.github.tempest.framework.container.references
2+
3+
import com.github.tempest.framework.TempestFrameworkClasses
4+
import com.intellij.psi.PsiElement
5+
import com.jetbrains.php.lang.psi.elements.MethodReference
6+
7+
object ContainerPsiUtils {
8+
fun isInvokeMethod(element: PsiElement): Boolean {
9+
val element = element as? MethodReference ?: return false
10+
return element.name == "invoke" && element.signature == "#M#C${TempestFrameworkClasses.CONTAINER}.invoke"
11+
}
12+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.github.tempest.framework.container.references
2+
3+
import com.intellij.patterns.PlatformPatterns
4+
import com.intellij.psi.PsiElement
5+
import com.intellij.psi.PsiReference
6+
import com.intellij.psi.PsiReferenceContributor
7+
import com.intellij.psi.PsiReferenceProvider
8+
import com.intellij.psi.PsiReferenceRegistrar
9+
import com.intellij.psi.tree.TokenSet
10+
import com.intellij.psi.util.startOffset
11+
import com.intellij.util.ProcessingContext
12+
import com.jetbrains.php.lang.lexer.PhpTokenTypes
13+
import com.jetbrains.php.lang.psi.elements.MethodReference
14+
import com.jetbrains.php.lang.psi.elements.ParameterList
15+
16+
class ContainerReferenceContributor : PsiReferenceContributor() {
17+
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
18+
registrar.registerReferenceProvider(
19+
PlatformPatterns.psiElement(ParameterList::class.java)
20+
.withParent(MethodReference::class.java),
21+
object : PsiReferenceProvider() {
22+
override fun getReferencesByElement(
23+
element: PsiElement,
24+
context: ProcessingContext
25+
): Array<out PsiReference> {
26+
val parameterList = element as? ParameterList ?: return emptyArray()
27+
val function = parameterList.parent as? MethodReference ?: return emptyArray()
28+
if (!ContainerPsiUtils.isInvokeMethod(function)) return emptyArray()
29+
if (parameterList.parameters.isEmpty()) return emptyArray()
30+
31+
val firstParameter = function.parameters[0]
32+
if (firstParameter == element) return emptyArray()
33+
34+
return parameterList
35+
.node
36+
.getChildren(IDENTIFIER_TOKENS)
37+
.map {
38+
ContainerParameterReference(
39+
element,
40+
firstParameter,
41+
it.text,
42+
it.textRange.shiftLeft(element.startOffset),
43+
)
44+
}
45+
.toTypedArray()
46+
// .apply { println("found references for ${function.name} ${this.joinToString { it.toString() }}") }
47+
}
48+
}
49+
)
50+
}
51+
52+
companion object {
53+
val IDENTIFIER_TOKENS = TokenSet.create(PhpTokenTypes.IDENTIFIER)
54+
}
55+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.github.tempest.framework.container.references
2+
3+
import com.intellij.psi.PsiElement
4+
import com.intellij.psi.util.CachedValueProvider
5+
import com.intellij.psi.util.CachedValuesManager
6+
import com.jetbrains.php.lang.PhpReferenceContributor
7+
import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression
8+
import com.jetbrains.php.lang.psi.elements.Function
9+
10+
object ContainerResolveUtils {
11+
fun resolveCached(firstParameter: PsiElement): Collection<Function> =
12+
CachedValuesManager.getCachedValue(firstParameter) {
13+
CachedValueProvider.Result.create(
14+
resolve(firstParameter),
15+
firstParameter.containingFile,
16+
)
17+
}
18+
19+
fun resolve(firstParameter: PsiElement): Collection<Function> =
20+
when (firstParameter) {
21+
is ArrayCreationExpression -> fromArrayCreation(firstParameter)
22+
else -> emptyList()
23+
}
24+
25+
26+
fun fromArrayCreation(firstParameter: ArrayCreationExpression) =
27+
PhpReferenceContributor
28+
.getCallbackRefFromArray(firstParameter)
29+
?.multiResolve(true)
30+
?.mapNotNull { it.element as? Function }
31+
?: emptyList()
32+
}

src/main/kotlin/com/github/tempest/framework/router/references/RouteElementLookup.kt renamed to src/main/kotlin/com/github/tempest/framework/router/references/RouteLookupElementBuilder.kt

File renamed without changes.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@
4444
<completion.contributor
4545
language="PHP"
4646
implementationClass="com.github.tempest.framework.router.completion.RouteParameterCompletionContributor"/>
47+
<completion.contributor
48+
language="PHP"
49+
implementationClass="com.github.tempest.framework.container.completion.ContainerCompletionContributor"/>
50+
<psi.referenceContributor
51+
language="PHP"
52+
implementation="com.github.tempest.framework.container.references.ContainerReferenceContributor"/>
4753
<weigher
4854
order="first"
4955
implementationClass="com.github.tempest.framework.common.completion.CompletionWeighter"

0 commit comments

Comments
 (0)