Skip to content

Commit e006a90

Browse files
committed
feat: add references to uri parameters, close #25
1 parent 5048058 commit e006a90

File tree

5 files changed

+156
-67
lines changed

5 files changed

+156
-67
lines changed

src/main/kotlin/com/github/tempest/framework/router/completion/RouteParameterCompletionContributor.kt

Lines changed: 6 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,18 @@
11
package com.github.tempest.framework.router.completion
22

33
import com.github.tempest.framework.TempestFrameworkClasses
4-
import com.github.tempest.framework.common.completion.TopPriorityLookupElement
5-
import com.github.tempest.framework.common.insertHandler.InsertTextInsertHandler
6-
import com.github.tempest.framework.router.index.Route
7-
import com.github.tempest.framework.router.index.RouterIndexUtils
4+
import com.github.tempest.framework.router.references.RouteLookupElementBuilder
5+
import com.github.tempest.framework.router.references.RouteResolveUtils
86
import com.intellij.codeInsight.completion.CompletionContributor
97
import com.intellij.codeInsight.completion.CompletionParameters
108
import com.intellij.codeInsight.completion.CompletionProvider
119
import com.intellij.codeInsight.completion.CompletionResultSet
1210
import com.intellij.codeInsight.completion.CompletionType
13-
import com.intellij.codeInsight.completion.DeclarativeInsertHandler
14-
import com.intellij.codeInsight.completion.PrioritizedLookupElement
15-
import com.intellij.codeInsight.lookup.LookupElementBuilder
1611
import com.intellij.patterns.PlatformPatterns
1712
import com.intellij.util.ProcessingContext
18-
import com.jetbrains.php.PhpIcons
19-
import com.jetbrains.php.lang.PhpReferenceContributor
20-
import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression
2113
import com.jetbrains.php.lang.psi.elements.ConstantReference
2214
import com.jetbrains.php.lang.psi.elements.FunctionReference
23-
import com.jetbrains.php.lang.psi.elements.Method
2415
import com.jetbrains.php.lang.psi.elements.ParameterList
25-
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression
2616

2717
class RouteParameterCompletionContributor : CompletionContributor() {
2818
init {
@@ -60,63 +50,13 @@ class RouteParameterCompletionContributor : CompletionContributor() {
6050
val firstParameter = function.parameters[0]
6151
if (firstParameter == element) return
6252

63-
when (firstParameter) {
64-
is ArrayCreationExpression -> fromArrayCreation(firstParameter, result)
65-
is StringLiteralExpression -> fromStringLiteral(firstParameter, result)
66-
}
67-
}
68-
69-
private fun fromArrayCreation(
70-
firstParameter: ArrayCreationExpression,
71-
result: CompletionResultSet
72-
) {
73-
PhpReferenceContributor
74-
.getCallbackRefFromArray(firstParameter)
75-
?.resolve()
76-
?.let { it as? Method }
77-
?.attributes
78-
?.filter { it.fqn in TempestFrameworkClasses.ROUTES }
79-
?.mapNotNull { RouterIndexUtils.createRouteFromAttribute(it) }
80-
?.apply { fromRoutes(this, result) }
81-
}
82-
83-
private fun fromStringLiteral(
84-
routePattern: StringLiteralExpression,
85-
result: CompletionResultSet
86-
) {
87-
RouterIndexUtils
88-
.getRoutesByPattern(routePattern.contents, routePattern.project)
89-
.apply { fromRoutes(this, result) }
90-
}
91-
92-
private fun fromRoutes(
93-
routes: Collection<Route>,
94-
result: CompletionResultSet
95-
) {
96-
routes
97-
.flatMap { route ->
98-
route
99-
.parameters
100-
.map { parameter ->
101-
LookupElementBuilder.create(parameter.name)
102-
.withIcon(PhpIcons.PARAMETER)
103-
.withTailText(" Pattern: ${parameter.pattern}".takeIf { parameter.pattern.isNotEmpty() })
104-
.withTypeText(route.pattern)
105-
.withInsertHandler { context, element ->
106-
InsertTextInsertHandler(
107-
": ",
108-
DeclarativeInsertHandler.PopupOptions.MemberLookup
109-
)
110-
.handleInsert(context, element)
111-
}
112-
.let { PrioritizedLookupElement.withPriority(it, 10000.0) }
113-
.let { PrioritizedLookupElement.withExplicitProximity(it, 10000) }
114-
.let { TopPriorityLookupElement(it) }
115-
}
116-
}
53+
RouteResolveUtils
54+
.resolve(firstParameter)
55+
.flatMap { RouteLookupElementBuilder.create(it) }
11756
.apply { result.addAllElements(this) }
11857
.apply { if (isNotEmpty()) result.stopHere() }
11958
}
59+
12060
}
12161
)
12262
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.github.tempest.framework.router.references
2+
3+
import com.github.tempest.framework.common.completion.TopPriorityLookupElement
4+
import com.github.tempest.framework.common.insertHandler.InsertTextInsertHandler
5+
import com.github.tempest.framework.router.index.Route
6+
import com.intellij.codeInsight.completion.DeclarativeInsertHandler
7+
import com.intellij.codeInsight.completion.PrioritizedLookupElement
8+
import com.intellij.codeInsight.lookup.LookupElement
9+
import com.intellij.codeInsight.lookup.LookupElementBuilder
10+
import com.jetbrains.php.PhpIcons
11+
12+
object RouteLookupElementBuilder {
13+
fun create(route: Route): Collection<LookupElement> {
14+
return route
15+
.parameters
16+
.map { parameter ->
17+
LookupElementBuilder.create(parameter.name)
18+
.withIcon(PhpIcons.PARAMETER)
19+
.withTailText(" Pattern: ${parameter.pattern}".takeIf { parameter.pattern.isNotEmpty() })
20+
.withTypeText(route.pattern)
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+
}
33+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.github.tempest.framework.router.references
2+
3+
import com.github.tempest.framework.php.getMethodsByFQN
4+
import com.intellij.openapi.util.TextRange
5+
import com.intellij.psi.PsiElement
6+
import com.intellij.psi.PsiReferenceBase
7+
import com.jetbrains.php.PhpIndex
8+
import com.jetbrains.php.lang.psi.PhpPsiElementFactory
9+
10+
class RouteParameterReference(
11+
val hostElement: PsiElement,
12+
val routeReferenceElement: PsiElement,
13+
val parameterName: String,
14+
textRange: TextRange,
15+
) : PsiReferenceBase<PsiElement>(hostElement, textRange) {
16+
override fun resolve(): PsiElement? {
17+
val phpIndex = PhpIndex.getInstance(routeReferenceElement.project)
18+
return RouteResolveUtils
19+
.resolve(routeReferenceElement)
20+
.firstOrNull()
21+
?.let { phpIndex.getMethodsByFQN(it.action) }
22+
?.firstOrNull()
23+
?.getParameter(parameterName)
24+
}
25+
26+
override fun getVariants() = RouteResolveUtils
27+
.resolve(routeReferenceElement)
28+
.flatMap { RouteLookupElementBuilder.create(it) }
29+
.toTypedArray()
30+
31+
override fun handleElementRename(newElementName: String): PsiElement? {
32+
return hostElement.findElementAt(rangeInElement.startOffset)
33+
?.replace(
34+
PhpPsiElementFactory.createNamedArgumentNameIdentifier(
35+
hostElement.project,
36+
newElementName,
37+
)
38+
)
39+
}
40+
}

src/main/kotlin/com/github/tempest/framework/router/references/RouteParametersReferenceContributor.kt

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,16 @@ import com.intellij.psi.PsiReference
1111
import com.intellij.psi.PsiReferenceContributor
1212
import com.intellij.psi.PsiReferenceProvider
1313
import com.intellij.psi.PsiReferenceRegistrar
14+
import com.intellij.psi.tree.TokenSet
15+
import com.intellij.psi.util.startOffset
1416
import com.intellij.util.ProcessingContext
17+
import com.jetbrains.php.lang.lexer.PhpTokenTypes
18+
import com.jetbrains.php.lang.psi.elements.FunctionReference
1519
import com.jetbrains.php.lang.psi.elements.Method
1620
import com.jetbrains.php.lang.psi.elements.ParameterList
1721
import com.jetbrains.php.lang.psi.elements.PhpAttribute
1822
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression
23+
import com.jetbrains.php.lang.psi.elements.impl.FunctionReferenceImpl
1924

2025
class RouteParametersReferenceContributor : PsiReferenceContributor() {
2126
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
@@ -43,7 +48,7 @@ class RouteParametersReferenceContributor : PsiReferenceContributor() {
4348
val nameGroup = alias.groups[1] ?: return@mapNotNull null
4449
val rangeInElement = TextRange(
4550
nameGroup.range.first,
46-
nameGroup.range.last+1
51+
nameGroup.range.last + 1
4752
)
4853

4954
val parameter = method.getParameter(alias.groupValues[1])
@@ -54,5 +59,41 @@ class RouteParametersReferenceContributor : PsiReferenceContributor() {
5459

5560
}
5661
)
62+
registrar.registerReferenceProvider(
63+
PlatformPatterns.psiElement(ParameterList::class.java)
64+
.withParent(FunctionReference::class.java),
65+
object : PsiReferenceProvider() {
66+
override fun getReferencesByElement(
67+
element: PsiElement,
68+
context: ProcessingContext
69+
): Array<out PsiReference> {
70+
val parameterList = element as? ParameterList ?: return emptyArray()
71+
val function = parameterList.parent as? FunctionReferenceImpl ?: return emptyArray()
72+
if (function.fqn != TempestFrameworkClasses.FUNCTION_URI) return emptyArray()
73+
if (parameterList.parameters.isEmpty()) return emptyArray()
74+
75+
val firstParameter = function.parameters[0]
76+
if (firstParameter == element) return emptyArray()
77+
78+
return parameterList
79+
.node
80+
.getChildren(IDENTIFIER_TOKENS)
81+
.map {
82+
RouteParameterReference(
83+
element,
84+
firstParameter,
85+
it.text,
86+
it.textRange.shiftLeft(element.startOffset),
87+
)
88+
}
89+
.toTypedArray()
90+
// .apply { println("found references for ${function.name} ${this.joinToString { it.toString() }}") }
91+
}
92+
}
93+
)
94+
}
95+
96+
companion object {
97+
val IDENTIFIER_TOKENS = TokenSet.create(PhpTokenTypes.IDENTIFIER)
5798
}
5899
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.github.tempest.framework.router.references
2+
3+
import com.github.tempest.framework.TempestFrameworkClasses
4+
import com.github.tempest.framework.router.index.Route
5+
import com.github.tempest.framework.router.index.RouterIndexUtils
6+
import com.intellij.psi.PsiElement
7+
import com.jetbrains.php.lang.PhpReferenceContributor
8+
import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression
9+
import com.jetbrains.php.lang.psi.elements.Method
10+
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression
11+
12+
object RouteResolveUtils {
13+
fun resolve(firstParameter: PsiElement): Collection<Route> =
14+
when (firstParameter) {
15+
is ArrayCreationExpression -> fromArrayCreation(firstParameter)
16+
is StringLiteralExpression -> fromStringLiteral(firstParameter)
17+
else -> emptyList()
18+
}
19+
20+
21+
fun fromArrayCreation(firstParameter: ArrayCreationExpression) =
22+
PhpReferenceContributor
23+
.getCallbackRefFromArray(firstParameter)
24+
?.resolve()
25+
?.let { it as? Method }
26+
?.attributes
27+
?.filter { it.fqn in TempestFrameworkClasses.ROUTES }
28+
?.mapNotNull { RouterIndexUtils.createRouteFromAttribute(it) }
29+
?: emptyList()
30+
31+
fun fromStringLiteral(routePattern: StringLiteralExpression) =
32+
RouterIndexUtils
33+
.getRoutesByPattern(routePattern.contents, routePattern.project)
34+
35+
}

0 commit comments

Comments
 (0)