FIR IDE: Move ReplaceCallFix to idea-frontend-independent.
This commit is contained in:
committed by
Ilya Kirillov
parent
482c677274
commit
05deecaafa
+116
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.quickfix
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
|
|
||||||
|
abstract class ReplaceCallFix(
|
||||||
|
expression: KtQualifiedExpression,
|
||||||
|
private val operation: String,
|
||||||
|
private val notNullNeeded: Boolean = false
|
||||||
|
) : KotlinPsiOnlyQuickFixAction<KtQualifiedExpression>(expression) {
|
||||||
|
|
||||||
|
override fun getFamilyName() = text
|
||||||
|
|
||||||
|
override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean {
|
||||||
|
val element = element ?: return false
|
||||||
|
return element.selectorExpression != null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
|
val element = element ?: return
|
||||||
|
val elvis = element.elvisOrEmpty(notNullNeeded)
|
||||||
|
val betweenReceiverAndOperation = element.elementsBetweenReceiverAndOperation().joinToString(separator = "") { it.text }
|
||||||
|
val newExpression = KtPsiFactory(element).createExpressionByPattern(
|
||||||
|
"$0$betweenReceiverAndOperation$operation$1$elvis",
|
||||||
|
element.receiverExpression,
|
||||||
|
element.selectorExpression!!
|
||||||
|
)
|
||||||
|
val replacement = element.replace(newExpression)
|
||||||
|
if (elvis.isNotEmpty()) {
|
||||||
|
replacement.moveCaretToEnd(editor, project)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtQualifiedExpression.elementsBetweenReceiverAndOperation(): List<PsiElement> {
|
||||||
|
val receiver = receiverExpression
|
||||||
|
val operation = operationTokenNode as? PsiElement ?: return emptyList()
|
||||||
|
val start = receiver.nextSibling?.takeIf { it != operation } ?: return emptyList()
|
||||||
|
val end = operation.prevSibling?.takeIf { it != receiver } ?: return emptyList()
|
||||||
|
return PsiTreeUtil.getElementsOfRange(start, end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReplaceImplicitReceiverCallFix(
|
||||||
|
expression: KtExpression,
|
||||||
|
private val notNullNeeded: Boolean
|
||||||
|
) : KotlinPsiOnlyQuickFixAction<KtExpression>(expression) {
|
||||||
|
override fun getFamilyName() = text
|
||||||
|
|
||||||
|
override fun getText() = KotlinBundle.message("replace.with.safe.this.call")
|
||||||
|
|
||||||
|
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
|
val element = element ?: return
|
||||||
|
val elvis = element.elvisOrEmpty(notNullNeeded)
|
||||||
|
val newExpression = KtPsiFactory(element).createExpressionByPattern("this?.$0$elvis", element)
|
||||||
|
val replacement = element.replace(newExpression)
|
||||||
|
if (elvis.isNotEmpty()) {
|
||||||
|
replacement.moveCaretToEnd(editor, project)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReplaceWithSafeCallFix(
|
||||||
|
expression: KtDotQualifiedExpression,
|
||||||
|
notNullNeeded: Boolean
|
||||||
|
) : ReplaceCallFix(expression, "?.", notNullNeeded) {
|
||||||
|
override fun getText() = KotlinBundle.message("replace.with.safe.call")
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReplaceWithSafeCallForScopeFunctionFix(
|
||||||
|
expression: KtDotQualifiedExpression,
|
||||||
|
notNullNeeded: Boolean
|
||||||
|
) : ReplaceCallFix(expression, "?.", notNullNeeded) {
|
||||||
|
override fun getText() = KotlinBundle.message("replace.scope.function.with.safe.call")
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReplaceWithDotCallFix(expression: KtSafeQualifiedExpression) : ReplaceCallFix(expression, "."), CleanupFix {
|
||||||
|
override fun getText() = KotlinBundle.message("replace.with.dot.call")
|
||||||
|
|
||||||
|
companion object : KotlinSingleIntentionActionFactory() {
|
||||||
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
|
val qualifiedExpression = diagnostic.psiElement.getParentOfType<KtSafeQualifiedExpression>(strict = false) ?: return null
|
||||||
|
return ReplaceWithDotCallFix(qualifiedExpression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtExpression.elvisOrEmpty(notNullNeeded: Boolean): String {
|
||||||
|
if (!notNullNeeded) return ""
|
||||||
|
val binaryExpression = getStrictParentOfType<KtBinaryExpression>()
|
||||||
|
return if (binaryExpression?.left == this && binaryExpression.operationToken == KtTokens.ELVIS) "" else "?:"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PsiElement.moveCaretToEnd(editor: Editor?, project: Project) {
|
||||||
|
editor?.run {
|
||||||
|
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document)
|
||||||
|
val endOffset = if (text.endsWith(")")) endOffset - 1 else endOffset
|
||||||
|
document.insertString(endOffset, " ")
|
||||||
|
caretModel.moveToOffset(endOffset + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -241,7 +241,7 @@ class QuickFixRegistrar : QuickFixContributor {
|
|||||||
SENSELESS_COMPARISON.registerFactory(SimplifyComparisonFix)
|
SENSELESS_COMPARISON.registerFactory(SimplifyComparisonFix)
|
||||||
|
|
||||||
UNNECESSARY_SAFE_CALL.registerFactory(ReplaceWithDotCallFix)
|
UNNECESSARY_SAFE_CALL.registerFactory(ReplaceWithDotCallFix)
|
||||||
UNSAFE_CALL.registerFactory(ReplaceWithSafeCallFix)
|
UNSAFE_CALL.registerFactory(ReplaceWithSafeCallFixFactory)
|
||||||
|
|
||||||
UNSAFE_CALL.registerFactory(SurroundWithNullCheckFix)
|
UNSAFE_CALL.registerFactory(SurroundWithNullCheckFix)
|
||||||
UNSAFE_IMPLICIT_INVOKE_CALL.registerFactory(SurroundWithNullCheckFix)
|
UNSAFE_IMPLICIT_INVOKE_CALL.registerFactory(SurroundWithNullCheckFix)
|
||||||
@@ -264,7 +264,7 @@ class QuickFixRegistrar : QuickFixContributor {
|
|||||||
UNSAFE_OPERATOR_CALL.registerFactory(ReplaceInfixOrOperatorCallFix)
|
UNSAFE_OPERATOR_CALL.registerFactory(ReplaceInfixOrOperatorCallFix)
|
||||||
UNSAFE_CALL.registerFactory(ReplaceInfixOrOperatorCallFix) // [] only
|
UNSAFE_CALL.registerFactory(ReplaceInfixOrOperatorCallFix) // [] only
|
||||||
UNSAFE_IMPLICIT_INVOKE_CALL.registerFactory(ReplaceInfixOrOperatorCallFix)
|
UNSAFE_IMPLICIT_INVOKE_CALL.registerFactory(ReplaceInfixOrOperatorCallFix)
|
||||||
UNSAFE_CALL.registerFactory(ReplaceWithSafeCallForScopeFunctionFix)
|
UNSAFE_CALL.registerFactory(ReplaceWithSafeCallForScopeFunctionFixFactory)
|
||||||
|
|
||||||
AMBIGUOUS_ANONYMOUS_TYPE_INFERRED.registerActions(SpecifyTypeExplicitlyFix())
|
AMBIGUOUS_ANONYMOUS_TYPE_INFERRED.registerActions(SpecifyTypeExplicitlyFix())
|
||||||
PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.registerActions(SpecifyTypeExplicitlyFix())
|
PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.registerActions(SpecifyTypeExplicitlyFix())
|
||||||
|
|||||||
@@ -17,12 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.idea.quickfix
|
package org.jetbrains.kotlin.idea.quickfix
|
||||||
|
|
||||||
import com.intellij.codeInsight.intention.IntentionAction
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||||
@@ -32,150 +27,67 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
|||||||
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getImplicitReceiverValue
|
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getImplicitReceiverValue
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
|
|
||||||
abstract class ReplaceCallFix(
|
object ReplaceWithSafeCallFixFactory : KotlinSingleIntentionActionFactory() {
|
||||||
expression: KtQualifiedExpression,
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
private val operation: String,
|
val psiElement = diagnostic.psiElement
|
||||||
private val notNullNeeded: Boolean = false
|
val qualifiedExpression = psiElement.parent as? KtDotQualifiedExpression
|
||||||
) : KotlinQuickFixAction<KtQualifiedExpression>(expression) {
|
if (qualifiedExpression != null) {
|
||||||
|
return ReplaceWithSafeCallFix(qualifiedExpression, qualifiedExpression.shouldHaveNotNullType())
|
||||||
override fun getFamilyName() = text
|
} else {
|
||||||
|
if (psiElement !is KtNameReferenceExpression) return null
|
||||||
override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean {
|
if (psiElement.getResolvedCall(psiElement.analyze())?.getImplicitReceiverValue() != null) {
|
||||||
val element = element ?: return false
|
val expressionToReplace: KtExpression = psiElement.parent as? KtCallExpression ?: psiElement
|
||||||
return element.selectorExpression != null
|
return ReplaceImplicitReceiverCallFix(expressionToReplace, expressionToReplace.shouldHaveNotNullType())
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
object ReplaceWithSafeCallForScopeFunctionFixFactory : KotlinSingleIntentionActionFactory() {
|
||||||
val element = element ?: return
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
val elvis = element.elvisOrEmpty(notNullNeeded)
|
val element = diagnostic.psiElement
|
||||||
val betweenReceiverAndOperation = element.elementsBetweenReceiverAndOperation().joinToString(separator = "") { it.text }
|
val scopeFunctionLiteral = element.getStrictParentOfType<KtFunctionLiteral>() ?: return null
|
||||||
val newExpression = KtPsiFactory(element).createExpressionByPattern(
|
val scopeCallExpression = scopeFunctionLiteral.getStrictParentOfType<KtCallExpression>() ?: return null
|
||||||
"$0$betweenReceiverAndOperation$operation$1$elvis",
|
val scopeDotQualifiedExpression = scopeCallExpression.getStrictParentOfType<KtDotQualifiedExpression>() ?: return null
|
||||||
element.receiverExpression,
|
|
||||||
element.selectorExpression!!
|
val context = scopeCallExpression.analyze()
|
||||||
|
val scopeFunctionLiteralDescriptor = context[BindingContext.FUNCTION, scopeFunctionLiteral] ?: return null
|
||||||
|
val scopeFunctionKind = scopeCallExpression.scopeFunctionKind(context) ?: return null
|
||||||
|
|
||||||
|
val internalReceiver = (element.parent as? KtDotQualifiedExpression)?.receiverExpression
|
||||||
|
val internalReceiverDescriptor = internalReceiver.getResolvedCall(context)?.candidateDescriptor
|
||||||
|
val internalResolvedCall = (element.getParentOfType<KtElement>(strict = false))?.getResolvedCall(context)
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
when (scopeFunctionKind) {
|
||||||
|
ScopeFunctionKind.WITH_PARAMETER -> {
|
||||||
|
if (internalReceiverDescriptor != scopeFunctionLiteralDescriptor.valueParameters.singleOrNull()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ScopeFunctionKind.WITH_RECEIVER -> {
|
||||||
|
if (internalReceiverDescriptor != scopeFunctionLiteralDescriptor.extensionReceiverParameter &&
|
||||||
|
internalResolvedCall.getImplicitReceiverValue() == null
|
||||||
|
) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReplaceWithSafeCallForScopeFunctionFix(
|
||||||
|
scopeDotQualifiedExpression, scopeDotQualifiedExpression.shouldHaveNotNullType()
|
||||||
)
|
)
|
||||||
val replacement = element.replace(newExpression)
|
|
||||||
if (elvis.isNotEmpty()) {
|
|
||||||
replacement.moveCaretToEnd(editor, project)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtQualifiedExpression.elementsBetweenReceiverAndOperation(): List<PsiElement> {
|
private fun KtCallExpression.scopeFunctionKind(context: BindingContext): ScopeFunctionKind? {
|
||||||
val receiver = receiverExpression
|
val methodName = getResolvedCall(context)?.resultingDescriptor?.fqNameUnsafe?.asString()
|
||||||
val operation = operationTokenNode as? PsiElement ?: return emptyList()
|
return ScopeFunctionKind.values().firstOrNull { kind -> kind.names.contains(methodName) }
|
||||||
val start = receiver.nextSibling?.takeIf { it != operation } ?: return emptyList()
|
}
|
||||||
val end = operation.prevSibling?.takeIf { it != receiver } ?: return emptyList()
|
|
||||||
return PsiTreeUtil.getElementsOfRange(start, end)
|
private enum class ScopeFunctionKind(vararg val names: String) {
|
||||||
}
|
WITH_PARAMETER("kotlin.let", "kotlin.also"),
|
||||||
}
|
WITH_RECEIVER("kotlin.apply", "kotlin.run")
|
||||||
|
|
||||||
class ReplaceImplicitReceiverCallFix(
|
|
||||||
expression: KtExpression,
|
|
||||||
private val notNullNeeded: Boolean
|
|
||||||
) : KotlinQuickFixAction<KtExpression>(expression) {
|
|
||||||
override fun getFamilyName() = text
|
|
||||||
|
|
||||||
override fun getText() = KotlinBundle.message("replace.with.safe.this.call")
|
|
||||||
|
|
||||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
|
||||||
val element = element ?: return
|
|
||||||
val elvis = element.elvisOrEmpty(notNullNeeded)
|
|
||||||
val newExpression = KtPsiFactory(element).createExpressionByPattern("this?.$0$elvis", element)
|
|
||||||
val replacement = element.replace(newExpression)
|
|
||||||
if (elvis.isNotEmpty()) {
|
|
||||||
replacement.moveCaretToEnd(editor, project)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReplaceWithSafeCallFix(
|
|
||||||
expression: KtDotQualifiedExpression,
|
|
||||||
notNullNeeded: Boolean
|
|
||||||
) : ReplaceCallFix(expression, "?.", notNullNeeded) {
|
|
||||||
|
|
||||||
override fun getText() = KotlinBundle.message("replace.with.safe.call")
|
|
||||||
|
|
||||||
companion object : KotlinSingleIntentionActionFactory() {
|
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
|
||||||
val psiElement = diagnostic.psiElement
|
|
||||||
val qualifiedExpression = psiElement.parent as? KtDotQualifiedExpression
|
|
||||||
if (qualifiedExpression != null) {
|
|
||||||
return ReplaceWithSafeCallFix(qualifiedExpression, qualifiedExpression.shouldHaveNotNullType())
|
|
||||||
} else {
|
|
||||||
if (psiElement !is KtNameReferenceExpression) return null
|
|
||||||
if (psiElement.getResolvedCall(psiElement.analyze())?.getImplicitReceiverValue() != null) {
|
|
||||||
val expressionToReplace: KtExpression = psiElement.parent as? KtCallExpression ?: psiElement
|
|
||||||
return ReplaceImplicitReceiverCallFix(expressionToReplace, expressionToReplace.shouldHaveNotNullType())
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReplaceWithSafeCallForScopeFunctionFix(
|
|
||||||
expression: KtDotQualifiedExpression,
|
|
||||||
notNullNeeded: Boolean
|
|
||||||
) : ReplaceCallFix(expression, "?.", notNullNeeded) {
|
|
||||||
|
|
||||||
override fun getText() = KotlinBundle.message("replace.scope.function.with.safe.call")
|
|
||||||
|
|
||||||
companion object : KotlinSingleIntentionActionFactory() {
|
|
||||||
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtExpression>? {
|
|
||||||
val element = diagnostic.psiElement
|
|
||||||
val scopeFunctionLiteral = element.getStrictParentOfType<KtFunctionLiteral>() ?: return null
|
|
||||||
val scopeCallExpression = scopeFunctionLiteral.getStrictParentOfType<KtCallExpression>() ?: return null
|
|
||||||
val scopeDotQualifiedExpression = scopeCallExpression.getStrictParentOfType<KtDotQualifiedExpression>() ?: return null
|
|
||||||
|
|
||||||
val context = scopeCallExpression.analyze()
|
|
||||||
val scopeFunctionLiteralDescriptor = context[BindingContext.FUNCTION, scopeFunctionLiteral] ?: return null
|
|
||||||
val scopeFunctionKind = scopeCallExpression.scopeFunctionKind(context) ?: return null
|
|
||||||
|
|
||||||
val internalReceiver = (element.parent as? KtDotQualifiedExpression)?.receiverExpression
|
|
||||||
val internalReceiverDescriptor = internalReceiver.getResolvedCall(context)?.candidateDescriptor
|
|
||||||
val internalResolvedCall = (element.getParentOfType<KtElement>(strict = false))?.getResolvedCall(context)
|
|
||||||
?: return null
|
|
||||||
|
|
||||||
when (scopeFunctionKind) {
|
|
||||||
ScopeFunctionKind.WITH_PARAMETER -> {
|
|
||||||
if (internalReceiverDescriptor != scopeFunctionLiteralDescriptor.valueParameters.singleOrNull()) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ScopeFunctionKind.WITH_RECEIVER -> {
|
|
||||||
if (internalReceiverDescriptor != scopeFunctionLiteralDescriptor.extensionReceiverParameter &&
|
|
||||||
internalResolvedCall.getImplicitReceiverValue() == null
|
|
||||||
) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ReplaceWithSafeCallForScopeFunctionFix(
|
|
||||||
scopeDotQualifiedExpression, scopeDotQualifiedExpression.shouldHaveNotNullType()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KtCallExpression.scopeFunctionKind(context: BindingContext): ScopeFunctionKind? {
|
|
||||||
val methodName = getResolvedCall(context)?.resultingDescriptor?.fqNameUnsafe?.asString()
|
|
||||||
return ScopeFunctionKind.values().firstOrNull { kind -> kind.names.contains(methodName) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum class ScopeFunctionKind(vararg val names: String) {
|
|
||||||
WITH_PARAMETER("kotlin.let", "kotlin.also"),
|
|
||||||
WITH_RECEIVER("kotlin.apply", "kotlin.run")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReplaceWithDotCallFix(expression: KtSafeQualifiedExpression) : ReplaceCallFix(expression, "."), CleanupFix {
|
|
||||||
override fun getText() = KotlinBundle.message("replace.with.dot.call")
|
|
||||||
|
|
||||||
companion object : KotlinSingleIntentionActionFactory() {
|
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
|
||||||
val qualifiedExpression = diagnostic.psiElement.getParentOfType<KtSafeQualifiedExpression>(strict = false) ?: return null
|
|
||||||
return ReplaceWithDotCallFix(qualifiedExpression)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,26 +5,13 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.quickfix
|
package org.jetbrains.kotlin.idea.quickfix
|
||||||
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.psi.PsiDocumentManager
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
|
||||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.psi.KtProperty
|
import org.jetbrains.kotlin.psi.KtProperty
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||||
|
|
||||||
fun KtExpression.elvisOrEmpty(notNullNeeded: Boolean): String {
|
|
||||||
if (!notNullNeeded) return ""
|
|
||||||
val binaryExpression = getStrictParentOfType<KtBinaryExpression>()
|
|
||||||
return if (binaryExpression?.left == this && binaryExpression.operationToken == KtTokens.ELVIS) "" else "?:"
|
|
||||||
}
|
|
||||||
|
|
||||||
fun KtExpression.shouldHaveNotNullType(): Boolean {
|
fun KtExpression.shouldHaveNotNullType(): Boolean {
|
||||||
val type = when (val parent = parent) {
|
val type = when (val parent = parent) {
|
||||||
is KtBinaryExpression -> parent.left?.let { it.getType(it.analyze()) }
|
is KtBinaryExpression -> parent.left?.let { it.getType(it.analyze()) }
|
||||||
@@ -33,12 +20,3 @@ fun KtExpression.shouldHaveNotNullType(): Boolean {
|
|||||||
} ?: return false
|
} ?: return false
|
||||||
return !type.isMarkedNullable
|
return !type.isMarkedNullable
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PsiElement.moveCaretToEnd(editor: Editor?, project: Project) {
|
|
||||||
editor?.run {
|
|
||||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document)
|
|
||||||
val endOffset = if (text.endsWith(")")) endOffset - 1 else endOffset
|
|
||||||
document.insertString(endOffset, " ")
|
|
||||||
caretModel.moveToOffset(endOffset + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user