Refactoring

This commit is contained in:
Valentin Kipyatkov
2016-10-27 14:12:58 +03:00
parent 0fbbf1a340
commit 13ca110430
@@ -20,10 +20,10 @@ import com.intellij.codeInsight.template.postfix.templates.*
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.Condition
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil.findElementOfClassAtRange
import com.intellij.util.Function
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.idea.caches.resolve.analyze
@@ -111,52 +111,43 @@ internal fun createExpressionSelector(
checkCanBeUsedAsValue: Boolean = true,
statementsOnly: Boolean = false,
typePredicate: ((KotlinType) -> Boolean)? = null
): PostfixTemplateExpressionSelectorBase = KtExpressionPostfixTemplateSelector(statementsOnly, checkCanBeUsedAsValue, typePredicate)
): PostfixTemplateExpressionSelector = KtExpressionPostfixTemplateSelector(statementsOnly, checkCanBeUsedAsValue, typePredicate)
private class KtExpressionPostfixTemplateSelector(
statementsOnly: Boolean,
checkCanBeUsedAsValue: Boolean,
typePredicate: ((KotlinType) -> Boolean)?
) : PostfixTemplateExpressionSelectorBase(createFilter(statementsOnly, checkCanBeUsedAsValue, typePredicate)) {
companion object {
private fun createFilter(
statementsOnly: Boolean,
checkCanBeUsedAsValue: Boolean,
typePredicate: ((KotlinType) -> Boolean)?
): Condition<PsiElement> = Condition {
if (it !is KtExpression) return@Condition false
private val statementsOnly: Boolean,
private val checkCanBeUsedAsValue: Boolean,
private val typePredicate: ((KotlinType) -> Boolean)?
) : PostfixTemplateExpressionSelector {
// Can't be independent expressions
if (it.isSelector || it.parent is KtUserType || it.isOperationReference || it is KtBlockExpression) return@Condition false
private fun filterElement(element: PsiElement): Boolean {
if (element !is KtExpression) return false
// Both KtLambdaExpression and KtFunctionLiteral have the same offset, so we add only one of them -> KtLambdaExpression
if (it is KtFunctionLiteral) return@Condition false
// Can't be independent expressions
if (element.isSelector || element.parent is KtUserType || element.isOperationReference || element is KtBlockExpression) return false
val bindingContext by lazy { it.analyze(BodyResolveMode.PARTIAL) }
// Both KtLambdaExpression and KtFunctionLiteral have the same offset, so we add only one of them -> KtLambdaExpression
if (element is KtFunctionLiteral) return false
if (statementsOnly) {
val elementToCheck = it.getQualifiedExpressionForReceiverOrThis()
if (elementToCheck.parent !is KtBlockExpression && !elementToCheck.isUsedAsStatement(bindingContext)) return@Condition false
}
if (checkCanBeUsedAsValue && !it.canBeUsedAsValue()) return@Condition false
val bindingContext by lazy { element.analyze(BodyResolveMode.PARTIAL) }
typePredicate == null || it.getType(bindingContext)?.let { typePredicate(it) } ?: false
if (statementsOnly) {
val elementToCheck = element.getQualifiedExpressionForReceiverOrThis()
if (elementToCheck.parent !is KtBlockExpression && !elementToCheck.isUsedAsStatement(bindingContext)) return false
}
if (checkCanBeUsedAsValue && !element.canBeUsedAsValue()) return false
private fun KtExpression.canBeUsedAsValue() =
!KtPsiUtil.isAssignment(this) &&
!this.isNamedDeclaration &&
this !is KtLoopExpression &&
// if's only with else may be treated as expressions
!isIfWithoutElse
private val KtExpression.isIfWithoutElse: Boolean
get() = (this is KtIfExpression && this.elseKeyword == null)
return typePredicate == null || element.getType(bindingContext)?.let { typePredicate.invoke(it) } ?: false
}
override fun getFilters(offset: Int): Condition<PsiElement> {
return myAdditionalCondition
}
private fun KtExpression.canBeUsedAsValue() =
!KtPsiUtil.isAssignment(this) &&
!this.isNamedDeclaration &&
this !is KtLoopExpression &&
// if's only with else may be treated as expressions
!isIfWithoutElse
private val KtExpression.isIfWithoutElse: Boolean
get() = (this is KtIfExpression && this.elseKeyword == null)
override fun getExpressions(context: PsiElement, document: Document, offset: Int): List<PsiElement> {
val originalFile = context.containingFile.originalFile
@@ -175,12 +166,12 @@ private class KtExpressionPostfixTemplateSelector(
val qualifiedExpressionEnd = boundElementParent.endOffset
expressions
.dropWhile { it != boundElementParent }
.filter { it != boundElementParent }
.drop(1)
.takeWhile { it.endOffset == qualifiedExpressionEnd }
.toCollection(filteredByOffset)
}
val result = filteredByOffset.filter { myAdditionalCondition.value(it) }
val result = filteredByOffset.filter(this::filterElement)
if (ApplicationManager.getApplication().isUnitTestMode && result.size > 1) {
KtPostfixTemplateProvider.previouslySuggestedExpressions = result.map { it.text }
@@ -189,15 +180,11 @@ private class KtExpressionPostfixTemplateSelector(
return result
}
override fun getNonFilteredExpressions(
context: PsiElement,
document: Document,
offset: Int
) = context.parentsWithSelf
.filterIsInstance<KtExpression>()
.takeWhile {
!it.isBlockBodyInDeclaration
}.toList()
override fun hasExpression(context: PsiElement, copyDocument: Document, newOffset: Int): Boolean {
return !getExpressions(context, copyDocument, newOffset).isEmpty()
}
override fun getRenderer() = Function(PsiElement::getText)
}
private val KtExpression.isOperationReference: Boolean