diff --git a/python/pluginResources/intellij.python.community.impl.xml b/python/pluginResources/intellij.python.community.impl.xml
index 42b63ccd893f..dda298f9c4d9 100644
--- a/python/pluginResources/intellij.python.community.impl.xml
+++ b/python/pluginResources/intellij.python.community.impl.xml
@@ -70,6 +70,9 @@
optionId="python.type.inlays.function.return"/>
+
+
StreamEx.of(ContainerUtil.notNullize(t.resolveMember(keywordPattern.getKeyword(),
- null, AccessDirection.READ, resolveContext))))
+ .flatMap(t -> StreamEx.of(ContainerUtil.notNullize(t.resolveMember(keyword, null, AccessDirection.READ, resolveContext))))
.toArray(ResolveResult.EMPTY_ARRAY);
}
diff --git a/python/src/com/jetbrains/python/inlayHints/PyTypeInlayHintsProvider.kt b/python/src/com/jetbrains/python/inlayHints/PyTypeInlayHintsProvider.kt
index 4d132d711614..5ef9cc7a9a28 100644
--- a/python/src/com/jetbrains/python/inlayHints/PyTypeInlayHintsProvider.kt
+++ b/python/src/com/jetbrains/python/inlayHints/PyTypeInlayHintsProvider.kt
@@ -1,11 +1,20 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.inlayHints
+import com.intellij.codeInsight.CodeInsightBundle
+import com.intellij.codeInsight.hint.HintManager
import com.intellij.codeInsight.hints.declarative.*
import com.intellij.codeInsight.hints.declarative.impl.PresentationTreeBuilderImpl.Companion.MAX_SEGMENT_TEXT_LENGTH
+import com.intellij.openapi.application.EDT
+import com.intellij.openapi.application.readAction
import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.event.EditorMouseEvent
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
+import com.intellij.psi.SmartPointerManager
+import com.intellij.psi.SmartPsiElementPointer
+import com.intellij.util.PsiNavigateUtil
+import com.intellij.util.concurrency.annotations.RequiresEdt
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider.REVEAL_TYPE
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider.REVEAL_TYPE_EXT
import com.jetbrains.python.documentation.PythonDocumentationProvider
@@ -14,9 +23,14 @@ import com.jetbrains.python.psi.PyClassPattern
import com.jetbrains.python.psi.PyFunction
import com.jetbrains.python.psi.PyKeywordPattern
import com.jetbrains.python.psi.impl.PyClassPatternImpl
+import com.jetbrains.python.psi.impl.references.PyKeywordPatternReference
import com.jetbrains.python.psi.resolve.PyResolveContext
import com.jetbrains.python.psi.types.PyClassType
import com.jetbrains.python.psi.types.TypeEvalContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
class PyTypeInlayHintsProvider : InlayHintsProvider {
companion object {
@@ -28,7 +42,7 @@ class PyTypeInlayHintsProvider : InlayHintsProvider {
override fun createCollector(file: PsiFile, editor: Editor): InlayHintsCollector? = Collector()
private class Collector : SharedBypassCollector {
- val hintFormat = HintFormat.Companion.default
+ val hintFormat = HintFormat.default
.withFontSize(HintFontSize.ABitSmallerThanInEditor)
.withHorizontalMargin(HintMarginPadding.MarginAndSmallerPadding)
@@ -96,13 +110,51 @@ class PyTypeInlayHintsProvider : InlayHintsProvider {
val classType = typeEvalContext.getType(classPattern) as? PyClassType ?: return
val matchArgs = PyClassPatternImpl.getMatchArgs(classType, typeEvalContext) ?: return
classPattern.argumentList.patterns.mapIndexed { idx, pattern ->
- if (pattern is PyKeywordPattern) return
+ if (pattern is PyKeywordPattern) return // positional patterns can only be before keyword patterns
val name = matchArgs.getOrNull(idx) ?: return@mapIndexed
sink.addPresentation(position = InlineInlayPosition(pattern.textRange.startOffset, false), hintFormat = hintFormat) {
- text(name)
+ val navigationData = InlayActionData(
+ PyPseudoKeywordPatternInlayActionHandler.Payload(SmartPointerManager.createPointer(classPattern), name),
+ PyPseudoKeywordPatternInlayActionHandler.HANDLER_NAME
+ )
+ text(name, actionData = navigationData)
text("=")
}
}
}
}
+}
+
+
+
+class PyPseudoKeywordPatternInlayActionHandler(private val cs: CoroutineScope) : InlayActionHandler {
+ companion object {
+ const val HANDLER_NAME: String = "py.pseudo.keyword.pattern"
+ }
+
+ data class Payload(val classPattern: SmartPsiElementPointer, val keyword: String) : InlayActionPayload
+
+ @RequiresEdt
+ override fun handleClick(e: EditorMouseEvent, payload: InlayActionPayload) {
+ payload as Payload
+
+ cs.launch {
+ val resolveResults = readAction {
+ val classPattern = payload.classPattern.element ?: return@readAction null
+ val keyword = payload.keyword
+ val typeEvalContext = TypeEvalContext.userInitiated(classPattern.project, classPattern.containingFile)
+ PyKeywordPatternReference.resolveKeyword(classPattern, keyword, PyResolveContext.defaultContext(typeEvalContext))
+ }
+ val element = resolveResults?.firstOrNull()?.element ?: run {
+ withContext(Dispatchers.EDT) {
+ HintManager.getInstance().showInformationHint(e.editor, CodeInsightBundle.message("declaration.navigation.nowhere.to.go"))
+ }
+ return@launch
+ }
+
+ withContext(Dispatchers.EDT) {
+ PsiNavigateUtil.navigate(element)
+ }
+ }
+ }
}
\ No newline at end of file