diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/findOperationGeneration.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/findOperationGeneration.kt
deleted file mode 100644
index 4bef0f032e1..00000000000
--- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/findOperationGeneration.kt
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright 2010-2016 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jetbrains.kotlin.idea.intentions.loopToCallChain
-
-import org.jetbrains.kotlin.descriptors.VariableDescriptor
-import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
-import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
-import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.FilterTransformation
-import org.jetbrains.kotlin.psi.*
-import org.jetbrains.kotlin.types.typeUtil.TypeNullability
-import org.jetbrains.kotlin.types.typeUtil.nullability
-
-interface FindOperatorGenerator {
- val functionName: String
-
- val hasFilter: Boolean
-
- val presentation: String
- get() = functionName + (if (hasFilter) "{}" else "()")
-
- val shouldUseInputVariable: Boolean
-
- fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression
-
- val chainCallCount: Int
- get() = 1
-}
-
-fun buildFindOperationGenerator(
- valueIfFound: KtExpression,
- valueIfNotFound: KtExpression,
- inputVariable: KtCallableDeclaration,
- indexVariable: KtCallableDeclaration?,
- filterTransformation: FilterTransformation?,
- findFirst: Boolean
-): FindOperatorGenerator? {
- assert(valueIfFound.isPhysical)
- assert(valueIfNotFound.isPhysical)
-
- val filterCondition = filterTransformation?.effectiveCondition()
-
- fun generateChainedCall(stdlibFunName: String, chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?): KtExpression {
- return if (filter == null) {
- chainedCallGenerator.generate("$stdlibFunName()")
- }
- else {
- val lambda = generateLambda(inputVariable, filter)
- chainedCallGenerator.generate("$stdlibFunName $0:'{}'", lambda)
- }
- }
-
- class SimpleGenerator(
- override val functionName: String,
- override val shouldUseInputVariable: Boolean
- ) : FindOperatorGenerator {
- override val hasFilter: Boolean
- get() = filterCondition != null
-
- override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
- return generateChainedCall(functionName, chainedCallGenerator, filterCondition)
- }
- }
-
- if (indexVariable != null) {
- if (filterTransformation == null) return null // makes no sense, indexVariable must be always 0
- if (filterTransformation.indexVariable != null) return null // cannot use index in condition for indexOfFirst/indexOfLast
-
- //TODO: what if value when not found is not "-1"?
- if (valueIfFound.isVariableReference(indexVariable) && valueIfNotFound.text == "-1") {
- return SimpleGenerator(if (findFirst) "indexOfFirst" else "indexOfLast", shouldUseInputVariable = false)
- }
-
- return null
-
- }
- else {
- val inputVariableCanHoldNull = (inputVariable.resolveToDescriptor() as VariableDescriptor).type.nullability() != TypeNullability.NOT_NULL
-
- fun FindOperatorGenerator.useElvisOperatorIfNeeded(): FindOperatorGenerator? {
- if (valueIfNotFound.isNullExpression()) return this
-
- // we cannot use ?: if found value can be null
- if (inputVariableCanHoldNull) return null
-
- return object: FindOperatorGenerator by this {
- override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
- val generated = this@useElvisOperatorIfNeeded.generate(chainedCallGenerator)
- return KtPsiFactory(generated).createExpressionByPattern("$0 ?: $1", generated, valueIfNotFound)
- }
- }
- }
-
- when {
- valueIfFound.isVariableReference(inputVariable) -> {
- val generator = SimpleGenerator(if (findFirst) "firstOrNull" else "lastOrNull", shouldUseInputVariable = true)
- return generator.useElvisOperatorIfNeeded()
- }
-
- valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> return SimpleGenerator("any", shouldUseInputVariable = false)
-
- valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> return SimpleGenerator("none", shouldUseInputVariable = false)
-
- inputVariable.hasUsages(valueIfFound) -> {
- if (!findFirst) return null // too dangerous because of side effects
-
- // specially handle the case when the result expression is "." or "?."
- val qualifiedExpression = valueIfFound as? KtQualifiedExpression
- if (qualifiedExpression != null) {
- val receiver = qualifiedExpression.receiverExpression
- val selector = qualifiedExpression.selectorExpression
- if (receiver.isVariableReference(inputVariable) && selector != null && !inputVariable.hasUsages(selector)) {
- return object: FindOperatorGenerator {
- override val functionName: String
- get() = "firstOrNull"
-
- override val hasFilter: Boolean
- get() = filterCondition != null
-
- override val shouldUseInputVariable: Boolean
- get() = true
-
- override val chainCallCount: Int
- get() = 2
-
- override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
- val findFirstCall = generateChainedCall(functionName, chainedCallGenerator, filterCondition)
- return chainedCallGenerator.generate("$0", selector, receiver = findFirstCall, safeCall = true)
- }
- }.useElvisOperatorIfNeeded()
- }
- }
-
- // in case of nullable input variable we cannot distinguish by the result of "firstOrNull" whether nothing was found or 'null' was found
- if (inputVariableCanHoldNull) return null
-
- return object: FindOperatorGenerator {
- override val functionName: String
- get() = "firstOrNull"
-
- override val hasFilter: Boolean
- get() = filterCondition != null
-
- override val shouldUseInputVariable: Boolean
- get() = true
-
- override val chainCallCount: Int
- get() = 2 // also includes "let"
-
- override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
- val findFirstCall = generateChainedCall(functionName, chainedCallGenerator, filterCondition)
- val letBody = generateLambda(inputVariable, valueIfFound)
- return chainedCallGenerator.generate("let $0:'{}'", letBody, receiver = findFirstCall, safeCall = true)
- }
- }.useElvisOperatorIfNeeded()
- }
-
- else -> {
- return object: FindOperatorGenerator {
- override val functionName: String
- get() = "any"
-
- override val hasFilter: Boolean
- get() = filterCondition != null
-
- override val shouldUseInputVariable: Boolean
- get() = false
-
- override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
- val chainedCall = generateChainedCall(functionName, chainedCallGenerator, filterCondition)
- return KtPsiFactory(chainedCall).createExpressionByPattern("if ($0) $1 else $2", chainedCall, valueIfFound, valueIfNotFound)
- }
- }
- }
- }
- }
-}
diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndAssignTransformation.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndAssignTransformation.kt
deleted file mode 100644
index 0448608f9c8..00000000000
--- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndAssignTransformation.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2010-2016 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jetbrains.kotlin.idea.intentions.loopToCallChain.result
-
-import org.jetbrains.kotlin.idea.intentions.loopToCallChain.AssignToVariableResultTransformation
-import org.jetbrains.kotlin.idea.intentions.loopToCallChain.ChainedCallGenerator
-import org.jetbrains.kotlin.idea.intentions.loopToCallChain.FindOperatorGenerator
-import org.jetbrains.kotlin.idea.intentions.loopToCallChain.VariableInitialization
-import org.jetbrains.kotlin.psi.KtExpression
-import org.jetbrains.kotlin.psi.KtForExpression
-
-class FindAndAssignTransformation(
- loop: KtForExpression,
- private val generator: FindOperatorGenerator,
- initialization: VariableInitialization
-) : AssignToVariableResultTransformation(loop, initialization) {
-
- override val presentation: String
- get() = generator.presentation
-
- override val chainCallCount: Int
- get() = generator.chainCallCount
-
- override val shouldUseInputVariable: Boolean
- get() = generator.shouldUseInputVariable
-
- override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
- return generator.generate(chainedCallGenerator)
- }
-}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndReturnTransformation.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndReturnTransformation.kt
deleted file mode 100644
index 8975109e309..00000000000
--- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndReturnTransformation.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2010-2016 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jetbrains.kotlin.idea.intentions.loopToCallChain.result
-
-import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
-import org.jetbrains.kotlin.psi.KtExpression
-import org.jetbrains.kotlin.psi.KtForExpression
-import org.jetbrains.kotlin.psi.KtReturnExpression
-import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
-
-class FindAndReturnTransformation(
- override val loop: KtForExpression,
- private val generator: FindOperatorGenerator,
- private val endReturn: KtReturnExpression
-) : ResultTransformation {
-
- override val commentSavingRange = PsiChildRange(loop.unwrapIfLabeled(), endReturn)
-
- override val presentation: String
- get() = generator.presentation
-
- override val chainCallCount: Int
- get() = generator.chainCallCount
-
- override val shouldUseInputVariable: Boolean
- get() = generator.shouldUseInputVariable
-
- override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
- return generator.generate(chainedCallGenerator)
- }
-
- override fun convertLoop(resultCallChain: KtExpression, commentSavingRangeHolder: CommentSavingRangeHolder): KtExpression {
- endReturn.returnedExpression!!.replace(resultCallChain)
- loop.deleteWithLabels()
- return endReturn
- }
-}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindTransformationMatcher.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindTransformationMatcher.kt
index 2ea55bc1ef3..ac40a980eee 100644
--- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindTransformationMatcher.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindTransformationMatcher.kt
@@ -16,12 +16,16 @@
package org.jetbrains.kotlin.idea.intentions.loopToCallChain.result
+import org.jetbrains.kotlin.descriptors.VariableDescriptor
+import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
+import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.FilterTransformation
import org.jetbrains.kotlin.lexer.KtTokens
-import org.jetbrains.kotlin.psi.KtBinaryExpression
-import org.jetbrains.kotlin.psi.KtBreakExpression
-import org.jetbrains.kotlin.psi.KtReturnExpression
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
+import org.jetbrains.kotlin.types.typeUtil.TypeNullability
+import org.jetbrains.kotlin.types.typeUtil.nullability
/**
* Matches:
@@ -101,4 +105,217 @@ object FindTransformationMatcher : TransformationMatcher {
val transformation = FindAndReturnTransformation(state.outerLoop, generator, returnAfterLoop)
return TransformationMatch.Result(transformation)
}
-}
\ No newline at end of file
+
+ private class FindAndReturnTransformation(
+ override val loop: KtForExpression,
+ private val generator: FindOperationGenerator,
+ private val endReturn: KtReturnExpression
+ ) : ResultTransformation {
+
+ override val commentSavingRange = PsiChildRange(loop.unwrapIfLabeled(), endReturn)
+
+ override val presentation: String
+ get() = generator.presentation
+
+ override val chainCallCount: Int
+ get() = generator.chainCallCount
+
+ override val shouldUseInputVariable: Boolean
+ get() = generator.shouldUseInputVariable
+
+ override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
+ return generator.generate(chainedCallGenerator)
+ }
+
+ override fun convertLoop(resultCallChain: KtExpression, commentSavingRangeHolder: CommentSavingRangeHolder): KtExpression {
+ endReturn.returnedExpression!!.replace(resultCallChain)
+ loop.deleteWithLabels()
+ return endReturn
+ }
+ }
+
+ private class FindAndAssignTransformation(
+ loop: KtForExpression,
+ private val generator: FindOperationGenerator,
+ initialization: VariableInitialization
+ ) : AssignToVariableResultTransformation(loop, initialization) {
+
+ override val presentation: String
+ get() = generator.presentation
+
+ override val chainCallCount: Int
+ get() = generator.chainCallCount
+
+ override val shouldUseInputVariable: Boolean
+ get() = generator.shouldUseInputVariable
+
+ override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
+ return generator.generate(chainedCallGenerator)
+ }
+ }
+
+ private interface FindOperationGenerator {
+ val functionName: String
+
+ val hasFilter: Boolean
+
+ val presentation: String
+ get() = functionName + (if (hasFilter) "{}" else "()")
+
+ val shouldUseInputVariable: Boolean
+
+ fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression
+
+ val chainCallCount: Int
+ get() = 1
+ }
+
+ private fun buildFindOperationGenerator(
+ valueIfFound: KtExpression,
+ valueIfNotFound: KtExpression,
+ inputVariable: KtCallableDeclaration,
+ indexVariable: KtCallableDeclaration?,
+ filterTransformation: FilterTransformation?,
+ findFirst: Boolean
+ ): FindOperationGenerator? {
+ assert(valueIfFound.isPhysical)
+ assert(valueIfNotFound.isPhysical)
+
+ val filterCondition = filterTransformation?.effectiveCondition()
+
+ fun generateChainedCall(stdlibFunName: String, chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?): KtExpression {
+ return if (filter == null) {
+ chainedCallGenerator.generate("$stdlibFunName()")
+ }
+ else {
+ val lambda = org.jetbrains.kotlin.idea.intentions.loopToCallChain.generateLambda(inputVariable, filter)
+ chainedCallGenerator.generate("$stdlibFunName $0:'{}'", lambda)
+ }
+ }
+
+ class SimpleGenerator(
+ override val functionName: String,
+ override val shouldUseInputVariable: Boolean
+ ) : FindOperationGenerator {
+ override val hasFilter: Boolean
+ get() = filterCondition != null
+
+ override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
+ return generateChainedCall(functionName, chainedCallGenerator, filterCondition)
+ }
+ }
+
+ if (indexVariable != null) {
+ if (filterTransformation == null) return null // makes no sense, indexVariable must be always 0
+ if (filterTransformation.indexVariable != null) return null // cannot use index in condition for indexOfFirst/indexOfLast
+
+ //TODO: what if value when not found is not "-1"?
+ if (valueIfFound.isVariableReference(indexVariable) && valueIfNotFound.text == "-1") {
+ return SimpleGenerator(if (findFirst) "indexOfFirst" else "indexOfLast", shouldUseInputVariable = false)
+ }
+
+ return null
+
+ }
+ else {
+ val inputVariableCanHoldNull = (inputVariable.resolveToDescriptor() as VariableDescriptor).type.nullability() != TypeNullability.NOT_NULL
+
+ fun FindOperationGenerator.useElvisOperatorIfNeeded(): FindOperationGenerator? {
+ if (valueIfNotFound.isNullExpression()) return this
+
+ // we cannot use ?: if found value can be null
+ if (inputVariableCanHoldNull) return null
+
+ return object: FindOperationGenerator by this {
+ override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
+ val generated = this@useElvisOperatorIfNeeded.generate(chainedCallGenerator)
+ return KtPsiFactory(generated).createExpressionByPattern("$0 ?: $1", generated, valueIfNotFound)
+ }
+ }
+ }
+
+ when {
+ valueIfFound.isVariableReference(inputVariable) -> {
+ val generator = SimpleGenerator(if (findFirst) "firstOrNull" else "lastOrNull", shouldUseInputVariable = true)
+ return generator.useElvisOperatorIfNeeded()
+ }
+
+ valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> return SimpleGenerator("any", shouldUseInputVariable = false)
+
+ valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> return SimpleGenerator("none", shouldUseInputVariable = false)
+
+ inputVariable.hasUsages(valueIfFound) -> {
+ if (!findFirst) return null // too dangerous because of side effects
+
+ // specially handle the case when the result expression is "." or "?."
+ val qualifiedExpression = valueIfFound as? KtQualifiedExpression
+ if (qualifiedExpression != null) {
+ val receiver = qualifiedExpression.receiverExpression
+ val selector = qualifiedExpression.selectorExpression
+ if (receiver.isVariableReference(inputVariable) && selector != null && !inputVariable.hasUsages(selector)) {
+ return object: FindOperationGenerator {
+ override val functionName: String
+ get() = "firstOrNull"
+
+ override val hasFilter: Boolean
+ get() = filterCondition != null
+
+ override val shouldUseInputVariable: Boolean
+ get() = true
+
+ override val chainCallCount: Int
+ get() = 2
+
+ override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
+ val findFirstCall = generateChainedCall(functionName, chainedCallGenerator, filterCondition)
+ return chainedCallGenerator.generate("$0", selector, receiver = findFirstCall, safeCall = true)
+ }
+ }.useElvisOperatorIfNeeded()
+ }
+ }
+
+ // in case of nullable input variable we cannot distinguish by the result of "firstOrNull" whether nothing was found or 'null' was found
+ if (inputVariableCanHoldNull) return null
+
+ return object: FindOperationGenerator {
+ override val functionName: String
+ get() = "firstOrNull"
+
+ override val hasFilter: Boolean
+ get() = filterCondition != null
+
+ override val shouldUseInputVariable: Boolean
+ get() = true
+
+ override val chainCallCount: Int
+ get() = 2 // also includes "let"
+
+ override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
+ val findFirstCall = generateChainedCall(functionName, chainedCallGenerator, filterCondition)
+ val letBody = generateLambda(inputVariable, valueIfFound)
+ return chainedCallGenerator.generate("let $0:'{}'", letBody, receiver = findFirstCall, safeCall = true)
+ }
+ }.useElvisOperatorIfNeeded()
+ }
+
+ else -> {
+ return object: FindOperationGenerator {
+ override val functionName: String
+ get() = "any"
+
+ override val hasFilter: Boolean
+ get() = filterCondition != null
+
+ override val shouldUseInputVariable: Boolean
+ get() = false
+
+ override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression {
+ val chainedCall = generateChainedCall(functionName, chainedCallGenerator, filterCondition)
+ return KtPsiFactory(chainedCall).createExpressionByPattern("if ($0) $1 else $2", chainedCall, valueIfFound, valueIfNotFound)
+ }
+ }
+ }
+ }
+ }
+ }
+}