Skip to content

Commit 51489ed

Browse files
authored
Make type hint annotations clickable (#17)
* Minor refactoring for binary expressions * Add support for click on inlay hint and move to referenced element in editor * Fix condition for ignore __ parameters * Remove empty space * Remove unused variable * Fix render for dict types * Fix async functions resolving * Remove debug printl
1 parent 9b1d335 commit 51489ed

8 files changed

Lines changed: 274 additions & 49 deletions

File tree

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
pluginGroup = space.whitememory.pythoninlayparams
22
pluginName = Python Inlay Params
3-
pluginVersion = 0.2.1
3+
pluginVersion = 0.3.0
44

55
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
66
# for insight into build numbers and IntelliJ Platform versions.

src/main/kotlin/space/whitememory/pythoninlayparams/PythonInlayParameterHintsProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class PythonInlayParameterHintsProvider : InlayParameterHintsProvider {
156156
* If the argument is very similar, we don't need to show it.
157157
*/
158158
private fun isHintNameValid(paramName: String, argument: PyExpression): Boolean {
159-
if (paramName.startsWith("__") && paramName.length == 1) return false
159+
if (paramName.startsWith("__") || paramName.length == 1) return false
160160

161161
val argumentName = if (argument is PySubscriptionExpression) {
162162
// It's a __getitem__ call (subscription), let's take the argument name from it

src/main/kotlin/space/whitememory/pythoninlayparams/types/AbstractPythonInlayTypeHintsCollector.kt

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ package space.whitememory.pythoninlayparams.types
22

33
import com.intellij.codeInsight.hints.FactoryInlayHintsCollector
44
import com.intellij.codeInsight.hints.InlayHintsSink
5+
import com.intellij.codeInsight.hints.presentation.InlayPresentation
56
import com.intellij.openapi.editor.Editor
67
import com.intellij.psi.PsiElement
78
import com.jetbrains.python.psi.PyElement
89
import com.jetbrains.python.psi.types.TypeEvalContext
910
import space.whitememory.pythoninlayparams.types.hints.HintGenerator
1011
import space.whitememory.pythoninlayparams.types.hints.HintResolver
12+
import space.whitememory.pythoninlayparams.types.hints.InlayInfoDetails
13+
import space.whitememory.pythoninlayparams.types.hints.PsiInlayInfoDetail
1114

1215
@Suppress("UnstableApiUsage")
1316
abstract class AbstractPythonInlayTypeHintsCollector(editor: Editor, open val settings: Any) :
@@ -24,10 +27,93 @@ abstract class AbstractPythonInlayTypeHintsCollector(editor: Editor, open val se
2427
val typeAnnotation = HintResolver.getExpressionAnnotationType(element, typeEvalContext)
2528
val hintName = HintGenerator.generateTypeHintText(element, typeAnnotation, typeEvalContext)
2629

27-
displayTypeHint(element, sink, hintName)
30+
if (hintName.isEmpty()) {
31+
return
32+
}
33+
34+
val resolvedHintName = resolveInlayPresentation(hintName)
35+
36+
displayTypeHint(element, sink, resolvedHintName)
37+
}
38+
39+
private fun resolveInlayPresentation(
40+
infoDetails: List<InlayInfoDetails>,
41+
separator: String = " | ",
42+
limit: Int? = 3
43+
): InlayPresentation {
44+
val convertedInlayInfoDetails = infoDetails.map { getInlayPresentationForInlayInfoDetails(it) }
45+
46+
return factory.seq(*separatePresentation(convertedInlayInfoDetails, separator, limit).toTypedArray())
47+
}
48+
49+
private fun getInlayPresentationForInlayInfoDetails(infoDetail: InlayInfoDetails): InlayPresentation {
50+
if (infoDetail.rootInlayInfo == null) {
51+
return resolveInlayPresentation(infoDetail.details, separator = infoDetail.separator, limit = infoDetail.limit)
52+
}
53+
54+
val textPresentation = factory.smallText(infoDetail.rootInlayInfo.text)
55+
val navigationElementProvider: (() -> PsiElement?)? = when(infoDetail.rootInlayInfo) {
56+
is PsiInlayInfoDetail -> {{ infoDetail.rootInlayInfo.element }}
57+
else -> null
58+
}
59+
60+
val basePresentation = navigationElementProvider?.let {
61+
factory.psiSingleReference(textPresentation, it)
62+
} ?: textPresentation
63+
64+
if (infoDetail.details.isEmpty()) {
65+
return basePresentation
66+
}
67+
68+
val childDetails = infoDetail.details.map { getInlayPresentationForInlayInfoDetails(it) }
69+
70+
return factory.seq(
71+
basePresentation,
72+
factory.smallText("["),
73+
*separatePresentation(
74+
childDetails,
75+
separator = infoDetail.separator,
76+
limit = infoDetail.limit
77+
).toTypedArray(),
78+
factory.smallText("]")
79+
)
80+
}
81+
82+
private fun separatePresentation(
83+
presentations: List<InlayPresentation>,
84+
separator: String,
85+
limit: Int?
86+
): List<InlayPresentation> {
87+
if (presentations.size <= 1) {
88+
return presentations
89+
}
90+
91+
val separatedInlayPresentation = mutableListOf<InlayPresentation>()
92+
93+
val iterator = presentations.iterator()
94+
var count = 0
95+
96+
while (iterator.hasNext()) {
97+
if (limit != null && count == limit) {
98+
if (iterator.hasNext()) {
99+
separatedInlayPresentation.add(factory.smallText("..."))
100+
}
101+
break
102+
}
103+
104+
separatedInlayPresentation.add(iterator.next())
105+
106+
count = count.inc()
107+
108+
if (iterator.hasNext() && (limit != null && count != limit)) {
109+
separatedInlayPresentation.add(factory.smallText(separator))
110+
}
111+
}
112+
113+
return separatedInlayPresentation
28114
}
29115

30-
abstract fun displayTypeHint(element: PyElement, sink: InlayHintsSink, hintName: String)
116+
abstract fun displayTypeHint(element: PyElement, sink: InlayHintsSink, hintName: InlayPresentation)
31117

32118
override fun collect(element: PsiElement, editor: Editor, sink: InlayHintsSink): Boolean {
33119
if (!element.isValid || element.project.isDefault) {

src/main/kotlin/space/whitememory/pythoninlayparams/types/functions/PythonFunctionInlayTypeHintsCollector.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package space.whitememory.pythoninlayparams.types.functions
22

33
import com.intellij.codeInsight.hints.InlayHintsSink
4+
import com.intellij.codeInsight.hints.presentation.InlayPresentation
45
import com.intellij.openapi.editor.Editor
56
import com.intellij.psi.PsiElement
67
import com.intellij.psi.tree.TokenSet
@@ -53,14 +54,14 @@ class PythonFunctionInlayTypeHintsCollector(editor: Editor, settings: Any) :
5354
return true
5455
}
5556

56-
override fun displayTypeHint(element: PyElement, sink: InlayHintsSink, hintName: String) {
57+
override fun displayTypeHint(element: PyElement, sink: InlayHintsSink, hintName: InlayPresentation) {
5758
val statementList = PsiTreeUtil.getChildOfType(element, PyParameterList::class.java)
5859

5960
statementList?.let {
6061
sink.addInlineElement(
6162
it.endOffset,
6263
false,
63-
factory.roundWithBackground(factory.smallText("$textBeforeTypeHint $hintName")),
64+
factory.roundWithBackground(factory.seq(factory.smallText("$textBeforeTypeHint "), hintName)),
6465
false
6566
)
6667
}

0 commit comments

Comments
 (0)