Skip to content

Commit e165929

Browse files
committed
Rewrite BridgeInlayHintCollector to provide hints for common sourceSets in MPP
1 parent d808bec commit e165929

File tree

16 files changed

+590
-143
lines changed

16 files changed

+590
-143
lines changed

ide-plugin/src/main/kotlin/me/him188/kotlin/jvm/blocking/bridge/ide/BridgeInlayHintsCollector.kt

Lines changed: 93 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,23 @@ import com.intellij.codeInsight.hints.presentation.InlayPresentation
88
import com.intellij.codeInsight.hints.presentation.MouseButton
99
import com.intellij.codeInsight.hints.presentation.PresentationFactory
1010
import com.intellij.ide.util.DefaultPsiElementCellRenderer
11+
import com.intellij.openapi.editor.BlockInlayPriority
1112
import com.intellij.openapi.editor.Editor
1213
import com.intellij.openapi.editor.ex.util.EditorUtil
1314
import com.intellij.openapi.editor.impl.EditorImpl
1415
import com.intellij.psi.*
15-
import com.intellij.psi.impl.source.PsiExtensibleClass
1616
import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
1717
import me.him188.kotlin.jvm.blocking.bridge.compiler.backend.ir.RuntimeIntrinsics
1818
import me.him188.kotlin.jvm.blocking.bridge.ide.line.marker.document
1919
import me.him188.kotlin.jvm.blocking.bridge.ide.line.marker.getLineNumber
20-
import org.jetbrains.kotlin.asJava.classes.KtLightClass
21-
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass
22-
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClassForFacade
23-
import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass
24-
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
25-
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
26-
import org.jetbrains.kotlin.psi.KtAnnotationEntry
27-
import org.jetbrains.kotlin.psi.KtClassOrObject
28-
import org.jetbrains.kotlin.psi.KtFile
29-
import org.jetbrains.kotlin.psi.KtObjectDeclaration
20+
import org.jetbrains.kotlin.psi.*
21+
import org.jetbrains.kotlin.psi.psiUtil.containingClass
22+
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
3023
import org.jetbrains.kotlin.psi.psiUtil.startOffset
31-
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
3224
import java.awt.event.MouseEvent
3325
import javax.swing.JComponent
3426
import javax.swing.JPanel
3527

36-
internal val PsiElement.containingKtFile: KtFile?
37-
get() = (containingFile as? FakeFileForLightClass)?.ktFile
38-
?: (this as? KtLightDeclaration<*, *>)?.kotlinOrigin?.containingKtFile
39-
40-
internal val PsiMember.containingKtClass: KtClassOrObject?
41-
get() = (containingClass as? KtLightClass)?.kotlinOrigin
42-
4328
class BridgeInlayHintsCollector :
4429
InlayHintsProvider<NoSettings>,
4530
// KotlinAbstractHintsProvider<NoSettings>(),
@@ -48,122 +33,89 @@ class BridgeInlayHintsCollector :
4833
override fun collect(element: PsiElement, editor: Editor, sink: InlayHintsSink): Boolean = kotlin.runCatching {
4934
// wrapped with runCatching in case binary changes. it's better not to provide feature than throwing exceptions
5035

51-
if (element is KtFile) {
52-
var anyChanged = false
36+
if (element !is KtFile) return false
37+
if (editor !is EditorImpl) return false
38+
if (!element.isBridgeCompilerEnabled) return false
5339

54-
fun collectClass(clazz: PsiClass): Boolean {
55-
anyChanged = collect(clazz, editor, sink) || anyChanged
56-
for (inner in clazz.innerClasses) {
57-
anyChanged = collectClass(inner) || anyChanged
58-
}
59-
return anyChanged
60-
}
40+
var anyChanged = false
41+
for (clazz in element.ktClassOrObjects()) {
42+
anyChanged = collectInlayHintsForClass(clazz, editor, sink) || anyChanged
43+
}
6144

62-
for (clazz in element.classes) {
63-
collectClass(clazz)
45+
for (declaration in element.declarations) {
46+
if (declaration is KtNamedFunction) {
47+
anyChanged = collectInlayHintsForFunction(declaration, editor, sink) || anyChanged
6448
}
65-
return anyChanged
6649
}
50+
return anyChanged
51+
}.getOrElse { false }
6752

68-
if (element !is KtUltraLightClass && element !is KtUltraLightClassForFacade) return false
69-
if (element !is PsiExtensibleClass) return false
70-
71-
if (editor !is EditorImpl) return false
72-
53+
private fun collectInlayHintsForClass(element: KtClassOrObject, editor: EditorImpl, sink: InlayHintsSink): Boolean {
7354
var anyChanged = false
74-
val factory = PresentationFactory(editor)
75-
76-
if (!element.isBridgeCompilerEnabled) return false
55+
element.ktClassOrObjects().forEach {
56+
anyChanged = collectInlayHintsForClass(it, editor, sink) || anyChanged
57+
}
7758

78-
val generated = mutableSetOf<PsiElement>()
79-
80-
for (method in element.methods) {
81-
if (method is BlockingBridgeStubMethod) continue
82-
if (method.containingClass !== element) continue
83-
if (!generated.add(method.navigationElement)) continue
84-
85-
if (method.canHaveBridgeFunctions().inlayHints) {
86-
anyChanged = true
87-
sink.addBlockElement(
88-
offset = method.identifyingElement?.startOffset ?: method.startOffset,
89-
relatesToPrecedingText = false,
90-
showAbove = true,
91-
priority = 1,
92-
presentation = createPresentation(factory, method, editor) ?: continue,
93-
)
94-
}
59+
for (function in element.declarations.asSequence().filterIsInstance<KtFunction>()) {
60+
if (function.containingClass() !== element) return false
61+
anyChanged = collectInlayHintsForFunction(function, editor, sink) || anyChanged
9562
}
9663

9764
return anyChanged
98-
}.getOrElse { false }
65+
}
66+
67+
private fun collectInlayHintsForFunction(
68+
method: KtFunction,
69+
editor: EditorImpl,
70+
sink: InlayHintsSink,
71+
): Boolean {
72+
if (method.canHaveBridgeFunctions().inlayHints) {
73+
val factory = PresentationFactory(editor)
74+
val presentation = createPresentation(factory, method, editor) ?: return false
75+
sink.addBlockElement(
76+
offset = method.identifyingElement?.startOffset ?: method.startOffset,
77+
relatesToPrecedingText = false,
78+
showAbove = true,
79+
priority = BlockInlayPriority.ANNOTATIONS,
80+
presentation = presentation,
81+
)
82+
return true
83+
}
84+
return false
85+
}
9986

10087

10188
private fun createPresentation(
10289
factory: PresentationFactory,
103-
method: PsiMethod,
90+
method: KtFunction,
10491
editor: Editor,
10592
): InlayPresentation? {
10693
var hint =
10794
factory.text("@${JvmBlockingBridge::class.simpleName}")
10895

109-
fun createNavigation(mouseEvent: MouseEvent, target: NavigatablePsiElement) {
110-
PsiElementListNavigator.openTargets(
111-
mouseEvent, arrayOf(target),
112-
"Navigate To Annotation Source",
113-
"Find Navigation Target",
114-
DefaultPsiElementCellRenderer()
115-
)
116-
}
117-
118-
var annotation: KtAnnotationEntry?
119-
if (method !is KtLightMethod) return null
120-
if (method.lightMemberOrigin?.originKind != JvmDeclarationOriginKind.OTHER) return null
121-
if (method.isJvmStatic() && method.containingKtClass !is KtObjectDeclaration) return null
122-
123-
when {
124-
method.containingKtClass?.findAnnotation(RuntimeIntrinsics.JvmBlockingBridgeFqName)
125-
.also { annotation = it } != null -> {
126-
127-
val containingClass = method.containingClass
128-
hint = factory.withTooltip(
129-
"From ${annotation!!.text} on class ${containingClass.name}",
130-
hint
131-
)
132-
hint = factory.onClick(hint, MouseButton.Middle) { mouseEvent, _ ->
133-
createNavigation(
134-
mouseEvent,
135-
annotation!!
136-
)
137-
}
138-
}
96+
if (method.isJvmStatic() && method.containingClassOrObject !is KtObjectDeclaration) return null
13997

140-
method.containingKtFile?.findAnnotation(RuntimeIntrinsics.JvmBlockingBridgeFqName)
141-
.also { annotation = it } != null -> {
142-
143-
val containingKtFile = method.containingKtFile!!
144-
hint = factory.withTooltip(
145-
"From ${annotation!!.text} on file ${containingKtFile.name}",
146-
hint
147-
)
148-
hint = factory.onClick(hint, MouseButton.Middle) { mouseEvent, _ ->
149-
createNavigation(
150-
mouseEvent,
151-
annotation!!
152-
)
153-
}
154-
}
155-
156-
else -> {
157-
hint = factory.withTooltip(
158-
"From enableForModule",
159-
hint
160-
)
161-
}
98+
method.containingClassOrObject?.findAnnotation(RuntimeIntrinsics.JvmBlockingBridgeFqName)?.let { annotation ->
99+
val containingClass = method.containingClassOrObject
100+
hint = factory.withTooltip(
101+
"From ${annotation.text} on class ${containingClass?.name}",
102+
hint
103+
)
104+
hint = factory.withNavigation(hint, annotation)
105+
} ?: method.containingKtFile.findAnnotation(RuntimeIntrinsics.JvmBlockingBridgeFqName)?.let { annotation ->
106+
val containingKtFile = method.containingKtFile
107+
hint = factory.withTooltip(
108+
"From ${annotation.text} on file ${containingKtFile.name}",
109+
hint
110+
)
111+
hint = factory.withNavigation(hint, annotation)
112+
} ?: kotlin.run {
113+
hint = factory.withTooltip("From enableForModule", hint)
162114
}
163115

164-
val alignmentElement = method.modifierList
116+
val alignmentElement = method.modifierList ?: return null
165117
val lineStart =
166-
method.document?.getLineStartOffset((alignmentElement).getLineNumber()) ?: return null
118+
method.document?.getLineStartOffset(alignmentElement.getLineNumber()) ?: return null
167119

168120
hint = factory.inset(
169121
hint,
@@ -178,6 +130,31 @@ class BridgeInlayHintsCollector :
178130
return hint
179131
}
180132

133+
private fun PresentationFactory.withNavigation(
134+
base: InlayPresentation,
135+
target: NavigatablePsiElement,
136+
): InlayPresentation {
137+
fun createNavigation(mouseEvent: MouseEvent) {
138+
PsiElementListNavigator.openTargets(
139+
mouseEvent, arrayOf(target),
140+
"Navigate To Annotation Source",
141+
"Find Navigation Target",
142+
DefaultPsiElementCellRenderer()
143+
)
144+
}
145+
146+
var hint = base
147+
148+
hint = onClick(hint, MouseButton.Middle) { mouseEvent, _ ->
149+
createNavigation(mouseEvent)
150+
}
151+
hint = onClick(hint, MouseButton.Left) { mouseEvent, _ ->
152+
if (!mouseEvent.isControlDown) return@onClick
153+
createNavigation(mouseEvent)
154+
}
155+
return hint
156+
}
157+
181158
override val name: String get() = "JvmBlockingBridge hints"
182159
override val previewText: String get() = ""
183160

@@ -202,4 +179,7 @@ class BridgeInlayHintsCollector :
202179
}
203180

204181
override val key: SettingsKey<NoSettings> get() = SettingsKey("blocking.bridge.hints")
205-
}
182+
}
183+
184+
private fun KtDeclarationContainer.ktClassOrObjects() =
185+
declarations.asSequence().filterIsInstance<KtClassOrObject>()

ide-plugin/src/main/kotlin/me/him188/kotlin/jvm/blocking/bridge/ide/JvmBlockingBridgePsiAugmentProvider.kt

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
1919
import org.jetbrains.kotlin.asJava.classes.*
2020
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
2121
import org.jetbrains.kotlin.asJava.elements.KtLightMethodImpl
22+
import org.jetbrains.kotlin.backend.jvm.ir.psiElement
2223
import org.jetbrains.kotlin.descriptors.*
2324
import org.jetbrains.kotlin.idea.caches.resolve.analyze
2425
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
2526
import org.jetbrains.kotlin.name.FqName
26-
import org.jetbrains.kotlin.name.Name
2727
import org.jetbrains.kotlin.psi.*
2828
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
29+
import org.jetbrains.kotlin.psi.psiUtil.hasSuspendModifier
2930
import org.jetbrains.kotlin.resolve.BindingContext
3031
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
3132

@@ -36,7 +37,7 @@ class JvmBlockingBridgePsiAugmentProvider : PsiAugmentProvider() {
3637
override fun <Psi : PsiElement?> getAugments(
3738
element: PsiElement,
3839
type: Class<Psi>,
39-
nameHint: String?
40+
nameHint: String?,
4041
): MutableList<Psi> {
4142
if (element !is KtUltraLightClass && element !is KtUltraLightClassForFacade) return mutableListOf()
4243
if (type != PsiMethod::class.java) {
@@ -100,7 +101,7 @@ internal fun PsiElement.generateAugmentElements(ownMethods: List<PsiMethod>): Li
100101

101102
return ownMethods.asSequence()
102103
.filterIsInstance<KtLightMethod>()
103-
.filter { it.canHaveBridgeFunctions().generate }
104+
.filter { (it.kotlinOrigin as? KtNamedFunction)?.canHaveBridgeFunctions()?.generate == true }
104105
.flatMap { it.generateLightMethod(it.containingClass).asSequence() }
105106
.toList()
106107
}
@@ -115,61 +116,56 @@ internal fun KtAnnotated.findAnnotation(fqName: FqName): KtAnnotationEntry? {
115116
return null
116117
}
117118

118-
internal val KtLightMethod.isTopLevel get() = this.kotlinOrigin?.containingClassOrObject == null
119+
@Suppress("PrivatePropertyName")
120+
private val JVM_SYNTHETIC = FqName("kotlin.jvm.JvmSynthetic")
119121

120-
internal fun PsiMethod.canHaveBridgeFunctions(): HasJvmBlockingBridgeAnnotation {
121-
if (this is BlockingBridgeStubMethod) return HasJvmBlockingBridgeAnnotation.NONE
122-
if (this !is KtLightMethod) return HasJvmBlockingBridgeAnnotation.NONE
123-
if (!isSuspend()) return HasJvmBlockingBridgeAnnotation.NONE
124-
if (!Name.isValidIdentifier(this.name)) return HasJvmBlockingBridgeAnnotation.NONE
122+
internal fun KtFunction.canHaveBridgeFunctions(): HasJvmBlockingBridgeAnnotation {
123+
if (this !is KtNamedFunction) return HasJvmBlockingBridgeAnnotation.NONE
124+
if (this.modifierList?.hasSuspendModifier() != true) return HasJvmBlockingBridgeAnnotation.NONE
125+
if (this.name == null) return HasJvmBlockingBridgeAnnotation.NONE
125126

126-
if (this.hasAnnotation(RuntimeIntrinsics.JvmBlockingBridgeFqName.asString())) return HasJvmBlockingBridgeAnnotation.FROM_FUNCTION
127+
if (this.hasAnnotation(RuntimeIntrinsics.JvmBlockingBridgeFqName)) return HasJvmBlockingBridgeAnnotation.FROM_FUNCTION
128+
if (this.hasAnnotation(JVM_SYNTHETIC)) return HasJvmBlockingBridgeAnnotation.NONE
127129

128130
// no @JvmBlockingBridge on function, check if it has on class or file.
129131

130-
val descriptor = this.kotlinOrigin?.descriptor as? SimpleFunctionDescriptor
132+
val descriptor = this.descriptor as? SimpleFunctionDescriptor
131133
if (descriptor != null) {
132134
if (!descriptor.effectiveVisibility(checkPublishedApi = true).publicApi) {
133135
return HasJvmBlockingBridgeAnnotation.NONE
134136
}
135137
}
136138

137139
if (!this.isTopLevel) {
138-
if (containingClass.hasAnnotation(RuntimeIntrinsics.JvmBlockingBridgeFqName.asString()))
140+
val containingClass = this.containingClassOrObject
141+
if (containingClass?.hasAnnotation(RuntimeIntrinsics.JvmBlockingBridgeFqName) == true)
139142
return HasJvmBlockingBridgeAnnotation.FROM_CONTAINING_DECLARATION
140143
}
141144

142145
if (bridgeConfiguration.enableForModule) return HasJvmBlockingBridgeAnnotation.ENABLE_FOR_MODULE
143146

144-
if (containingKtFile?.findAnnotation(RuntimeIntrinsics.JvmBlockingBridgeFqName) != null) return HasJvmBlockingBridgeAnnotation.FROM_CONTAINING_DECLARATION
147+
if (containingKtFile.findAnnotation(RuntimeIntrinsics.JvmBlockingBridgeFqName) != null) return HasJvmBlockingBridgeAnnotation.FROM_CONTAINING_DECLARATION
145148

146-
val fromSuper = findOverrides()?.map { it.canHaveBridgeFunctions() }?.firstOrNull { it.generate }
149+
val fromSuper = findOverrides()
150+
.mapNotNull { (it.psiElement as? KtNamedFunction)?.canHaveBridgeFunctions() }
151+
.firstOrNull { it.generate }
147152
if (fromSuper?.generate == true) return fromSuper
148153

149154
return HasJvmBlockingBridgeAnnotation.NONE
150155
}
151156

157+
private fun KtAnnotated.hasAnnotation(fqName: FqName): Boolean = this.findAnnotation(fqName) != null
158+
152159
/**
153160
* @return `null` if top-level method
154161
*/
155-
internal fun PsiMethod.findOverrides(): Sequence<PsiMethod>? {
156-
return containingClass?.superClasses
157-
?.flatMap { it.methods.asSequence() }
158-
?.filter {
159-
it.hasSameSignatureWith(this)
160-
}
161-
}
162-
163-
internal fun PsiMethod.hasSameSignatureWith(another: PsiMethod): Boolean {
164-
return this.hierarchicalMethodSignature == another.hierarchicalMethodSignature
162+
internal fun KtNamedFunction.findOverrides(): Collection<FunctionDescriptor> {
163+
val overriden = (this.descriptor as? SimpleFunctionDescriptor)?.overriddenDescriptors
164+
return overriden.orEmpty()
165165
}
166166

167-
internal val PsiClass.superClasses: Sequence<PsiClass> get() = this.superTypes.asSequence().mapNotNull { it.resolve() }
168-
169-
internal fun PsiMethod.isSuspend(): Boolean =
170-
this.modifierList.text.contains("suspend")
171-
172-
internal fun PsiMethod.isJvmStatic(): Boolean = hasAnnotation(JvmStatic::class.qualifiedName!!)
167+
internal fun PsiMethod.isJvmStatic(): Boolean = hasAnnotation("kotlin.jvm.JvmStatic")
168+
internal fun KtAnnotated.isJvmStatic(): Boolean = hasAnnotation(FqName("kotlin.jvm.JvmStatic"))
173169

174170
internal fun KtLightMethod.generateLightMethod(
175171
containingClass: KtLightClass,

ide-plugin/src/main/resources/messages/BlockingBridgeBundle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ remove.jvm.blocking.bridge.fix=Remove JvmBlockingBridge
22
remove.jvm.blocking.bridge=Remove @JvmBlockingBridge
33
remove.jvm.synthetic.fix=Remove JvmSynthetic
44
remove.jvm.synthetic=Remove @JvmSynthetic
5+
blocking.bridge.hints=Blocking Bridge Hints

0 commit comments

Comments
 (0)