Skip to content

Commit 6ae00a6

Browse files
committed
feat: add route parameters completion
1 parent 10cd518 commit 6ae00a6

File tree

7 files changed

+147
-2
lines changed

7 files changed

+147
-2
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.github.tempest.framework.common.completion
2+
3+
import com.intellij.codeInsight.completion.CompletionLocation
4+
import com.intellij.codeInsight.completion.CompletionWeigher
5+
import com.intellij.codeInsight.lookup.LookupElement
6+
7+
class CompletionWeighter : CompletionWeigher() {
8+
override fun weigh(
9+
element: LookupElement,
10+
location: CompletionLocation
11+
): Comparable<*>? {
12+
println("completion weigher: $element")
13+
return when {
14+
element is TopPriorityLookupElement -> 10
15+
else -> null
16+
}.apply { println("weigh: $this") }
17+
}
18+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.github.tempest.framework.common.completion
2+
3+
import com.intellij.codeInsight.lookup.LookupElement
4+
import com.intellij.codeInsight.lookup.LookupElementDecorator
5+
6+
class TopPriorityLookupElement(myDelegate: LookupElement) : LookupElementDecorator<LookupElement>(myDelegate)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.github.tempest.framework.common.insertHandler
2+
3+
import com.intellij.codeInsight.completion.DeclarativeInsertHandler
4+
import com.intellij.codeInsight.completion.InsertionContext
5+
import com.intellij.codeInsight.lookup.LookupElement
6+
import com.intellij.openapi.editor.Editor
7+
import com.intellij.openapi.util.TextRange
8+
9+
class InsertTextInsertHandler(
10+
private val stringToInsert: String,
11+
popupOptions: PopupOptions
12+
) : DeclarativeInsertHandler(
13+
listOf(RelativeTextEdit(0, 0, stringToInsert)),
14+
stringToInsert.length,
15+
null,
16+
null,
17+
popupOptions
18+
) {
19+
20+
override fun handleInsert(context: InsertionContext, item: LookupElement) {
21+
val applyTextOperations = !isValueAlreadyHere(context.editor)
22+
conditionalHandleInsert(context, item, applyTextOperations)
23+
}
24+
25+
private fun isValueAlreadyHere(editor: Editor): Boolean {
26+
val startOffset = editor.caretModel.offset
27+
val valueLength = stringToInsert.length
28+
return editor.document.textLength >= startOffset + valueLength &&
29+
editor.document.getText(TextRange.create(startOffset, startOffset + valueLength)) == stringToInsert
30+
}
31+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.github.tempest.framework.router.completion
2+
3+
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.RouterIndexUtils
7+
import com.intellij.codeInsight.completion.CompletionContributor
8+
import com.intellij.codeInsight.completion.CompletionParameters
9+
import com.intellij.codeInsight.completion.CompletionProvider
10+
import com.intellij.codeInsight.completion.CompletionResultSet
11+
import com.intellij.codeInsight.completion.CompletionType
12+
import com.intellij.codeInsight.completion.DeclarativeInsertHandler
13+
import com.intellij.codeInsight.completion.PrioritizedLookupElement
14+
import com.intellij.codeInsight.lookup.LookupElementBuilder
15+
import com.intellij.patterns.PlatformPatterns
16+
import com.intellij.util.ProcessingContext
17+
import com.jetbrains.php.PhpIcons
18+
import com.jetbrains.php.lang.psi.elements.ConstantReference
19+
import com.jetbrains.php.lang.psi.elements.FunctionReference
20+
import com.jetbrains.php.lang.psi.elements.ParameterList
21+
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression
22+
23+
class RouteParameterCompletionContributor : CompletionContributor() {
24+
init {
25+
extend(
26+
CompletionType.BASIC,
27+
PlatformPatterns.psiElement()
28+
.withParent(
29+
PlatformPatterns.or(
30+
PlatformPatterns.psiElement(ConstantReference::class.java)
31+
.withParent(PlatformPatterns.psiElement(ParameterList::class.java))
32+
.withSuperParent(
33+
2,
34+
PlatformPatterns.psiElement(FunctionReference::class.java),
35+
),
36+
PlatformPatterns.psiElement(ParameterList::class.java)
37+
.withParent(PlatformPatterns.psiElement(FunctionReference::class.java)),
38+
)
39+
),
40+
object : CompletionProvider<CompletionParameters>() {
41+
override fun addCompletions(
42+
parameters: CompletionParameters,
43+
context: ProcessingContext,
44+
result: CompletionResultSet
45+
) {
46+
val element = when (parameters.position.parent) {
47+
is ConstantReference -> parameters.position.parent
48+
is ParameterList -> parameters.position
49+
else -> return
50+
}
51+
val parameterList = element.parent as? ParameterList ?: return
52+
val function = parameterList.parent as? FunctionReference ?: return
53+
if (function.fqn != TempestFrameworkClasses.FUNCTION_URI) return
54+
if (parameterList.parameters.isEmpty()) return
55+
if (parameterList.parameters[0] == element) return
56+
57+
val routePattern = function.parameters[0] as? StringLiteralExpression ?: return
58+
59+
RouterIndexUtils.getRoutes(routePattern.contents, element.project)
60+
.flatMap { it.parameters }
61+
.map { parameter ->
62+
LookupElementBuilder.create(parameter.name)
63+
.withIcon(PhpIcons.PARAMETER)
64+
.withTailText(" Pattern: ${parameter.pattern}".takeIf { parameter.pattern.isNotEmpty() })
65+
.withTypeText(routePattern.contents)
66+
.withInsertHandler { context, element ->
67+
InsertTextInsertHandler(
68+
": ",
69+
DeclarativeInsertHandler.PopupOptions.MemberLookup
70+
)
71+
.handleInsert(context, element)
72+
}
73+
.let { PrioritizedLookupElement.withPriority(it, 10000.0) }
74+
.let { PrioritizedLookupElement.withExplicitProximity(it, 10000) }
75+
.let { TopPriorityLookupElement(it) }
76+
}
77+
.apply { result.addAllElements(this) }
78+
.apply { if (isNotEmpty()) result.stopHere() }
79+
}
80+
}
81+
)
82+
}
83+
}

src/main/kotlin/com/github/tempest/framework/router/index/RouterIndexUtils.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ object RouterIndexUtils {
1414
.filterNotNull()
1515
}
1616

17-
fun getRoute(pattern: String, project: Project): Collection<Route> {
17+
fun getRoutes(pattern: String, project: Project): Collection<Route> {
1818
val fileBasedIndex = FileBasedIndex.getInstance()
1919

2020
return fileBasedIndex

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class RouteReference(
3535
val phpIndex = PhpIndexImpl.getInstance(project)
3636

3737
return RouterIndexUtils
38-
.getRoute(myElement.contents, project)
38+
.getRoutes(myElement.contents, project)
3939
.flatMap { phpIndex.getMethodsByFQN(it.action) }
4040
.let { PsiElementResolveResult.createResults(it) }
4141
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@
4141
<lang.inspectionSuppressor
4242
language="HTML"
4343
implementationClass="com.github.tempest.framework.views.TempestComponentsInspectionSuppressor"/>
44+
<completion.contributor
45+
language="PHP"
46+
implementationClass="com.github.tempest.framework.router.completion.RouteParameterCompletionContributor"/>
47+
<weigher
48+
order="first"
49+
implementationClass="com.github.tempest.framework.common.completion.CompletionWeighter"
50+
key="completion" />
4451

4552
<fileBasedIndex
4653
implementation="com.github.tempest.framework.console.index.ConsoleCommandsIndex" />

0 commit comments

Comments
 (0)