From 98c0c636b35848257a32f56a70336790440c095e Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Wed, 24 Jun 2015 11:22:45 +0200 Subject: [PATCH] Smaller availability range for add argument name intention --- .../jetbrains/kotlin/psi/psiUtil/psiUtils.kt | 2 ++ .../intentions/JetSelfTargetingIntention.kt | 29 +++++++++---------- .../intentions/AddNameToArgumentIntention.kt | 5 ++++ .../intentions/ConvertToBlockBodyIntention.kt | 5 +++- .../ConvertToExpressionBodyIntention.kt | 4 ++- .../MoveLambdaInsideParenthesesIntention.kt | 1 + .../MoveLambdaOutsideParenthesesIntention.kt | 1 + .../RemoveUnnecessaryParenthesesIntention.kt | 1 + .../jetbrains/kotlin/idea/intentions/Utils.kt | 2 -- .../addNameToArgument/notInsideIndices.kt | 6 ++++ .../notInsideNestedArgumentList.kt | 6 ++++ .../notInsideNestedArgumentList2.kt | 5 ++++ .../notInsideNestedArgumentList2.kt.after | 5 ++++ .../intentions/IntentionTestGenerated.java | 18 ++++++++++++ 14 files changed, 71 insertions(+), 19 deletions(-) create mode 100644 idea/testData/intentions/addNameToArgument/notInsideIndices.kt create mode 100644 idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList.kt create mode 100644 idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList2.kt create mode 100644 idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList2.kt.after diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt index 9f96a3da45c..5d1332a9105 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt @@ -216,6 +216,8 @@ public fun PsiElement.getStartOffsetIn(ancestor: PsiElement): Int { return offset } +public fun TextRange.containsInside(offset: Int): Boolean = getStartOffset() < offset && offset < getEndOffset() + public val PsiChildRange.textRange: TextRange? get() { if (isEmpty) return null diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/intentions/JetSelfTargetingIntention.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/intentions/JetSelfTargetingIntention.kt index 26f0ddaee94..c373ade19d4 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/intentions/JetSelfTargetingIntention.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/intentions/JetSelfTargetingIntention.kt @@ -25,14 +25,14 @@ import com.intellij.psi.PsiFile import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.idea.JetBundle import org.jetbrains.kotlin.psi.JetElement +import org.jetbrains.kotlin.psi.psiUtil.containsInside import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf public abstract class JetSelfTargetingIntention( public val elementType: Class, private var text: String, - private val familyName: String = text, - private val firstElementOfTypeOnly: Boolean = false + private val familyName: String = text ) : IntentionAction { protected val defaultText: String = text @@ -65,16 +65,17 @@ public abstract class JetSelfTargetingIntention( elementsToCheck += commonParent.parentsWithSelf } - val elementsOfType = elementsToCheck.filterIsInstance(elementType) - if (firstElementOfTypeOnly) { - val candidate = elementsOfType.firstOrNull() ?: return null - return if (isApplicableTo(candidate, offset)) candidate else null - } - else { - return elementsOfType.firstOrNull { isApplicableTo(it, offset) } + for (element in elementsToCheck) { + if (elementType.isInstance(element) && isApplicableTo(element as TElement, offset)) { + return element as TElement + } + if (!allowCaretInsideElement(element) && element.getTextRange().containsInside(offset)) break } + return null } + protected open fun allowCaretInsideElement(element: PsiElement): Boolean = true + final override fun isAvailable(project: Project, editor: Editor, file: PsiFile) = getTarget(editor, file) != null @@ -91,9 +92,8 @@ public abstract class JetSelfTargetingIntention( public abstract class JetSelfTargetingRangeIntention( elementType: Class, text: String, - familyName: String = text, - firstElementOfTypeOnly: Boolean = false -) : JetSelfTargetingIntention(elementType, text, familyName, firstElementOfTypeOnly) { + familyName: String = text +) : JetSelfTargetingIntention(elementType, text, familyName) { public abstract fun applicabilityRange(element: TElement): TextRange? @@ -106,9 +106,8 @@ public abstract class JetSelfTargetingRangeIntention( public abstract class JetSelfTargetingOffsetIndependentIntention( elementType: Class, text: String, - familyName: String = text, - firstElementOfTypeOnly: Boolean = false -) : JetSelfTargetingRangeIntention(elementType, text, familyName, firstElementOfTypeOnly) { + familyName: String = text +) : JetSelfTargetingRangeIntention(elementType, text, familyName) { public abstract fun isApplicableTo(element: TElement): Boolean diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/AddNameToArgumentIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/AddNameToArgumentIntention.kt index 6caec544d9f..abf9069171f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/AddNameToArgumentIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/AddNameToArgumentIntention.kt @@ -18,11 +18,13 @@ package org.jetbrains.kotlin.idea.intentions import com.intellij.codeInsight.intention.LowPriorityAction import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.conversion.copy.end import org.jetbrains.kotlin.idea.conversion.copy.start import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatchStatus @@ -44,6 +46,9 @@ public class AddNameToArgumentIntention return true } + override fun allowCaretInsideElement(element: PsiElement) + = element !is JetValueArgumentList && element !is JetContainerNode + override fun applyTo(element: JetValueArgument, editor: Editor) { val name = detectNameToAdd(element)!! val newArgument = JetPsiFactory(element).createArgument(element.getArgumentExpression()!!, name, element.getSpreadElement() != null) diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToBlockBodyIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToBlockBodyIntention.kt index 70b0c6e5d8e..3756c63a4c2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToBlockBodyIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertToBlockBodyIntention.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.idea.intentions import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze @@ -25,7 +26,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.types.JetType public class ConvertToBlockBodyIntention : JetSelfTargetingIntention( - javaClass(), "Convert to block body", firstElementOfTypeOnly = true + javaClass(), "Convert to block body" ) { override fun isApplicableTo(element: JetDeclarationWithBody, caretOffset: Int): Boolean { if (element is JetFunctionLiteral || element.hasBlockBody() || !element.hasBody()) return false @@ -43,6 +44,8 @@ public class ConvertToBlockBodyIntention : JetSelfTargetingIntention( - javaClass(), "Convert to expression body", firstElementOfTypeOnly = true + javaClass(), "Convert to expression body" ) { override fun isApplicableTo(element: JetDeclarationWithBody): Boolean { val value = calcValue(element) return value != null && !containsReturn(value) } + override fun allowCaretInsideElement(element: PsiElement) = element !is JetDeclaration + override fun applyTo(element: JetDeclarationWithBody, editor: Editor) { applyToInternal(element) { val typeRef = it.getTypeReference()!! diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaInsideParenthesesIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaInsideParenthesesIntention.kt index 23d5bb08d74..64e898e2a3e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaInsideParenthesesIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaInsideParenthesesIntention.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully import org.jetbrains.kotlin.idea.core.moveInsideParentheses import org.jetbrains.kotlin.psi.JetFunctionLiteralArgument +import org.jetbrains.kotlin.psi.psiUtil.containsInside public class MoveLambdaInsideParenthesesIntention : JetSelfTargetingIntention(javaClass(), "Move lambda argument into parentheses"), LowPriorityAction { override fun isApplicableTo(element: JetFunctionLiteralArgument, caretOffset: Int): Boolean { diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaOutsideParenthesesIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaOutsideParenthesesIntention.kt index 77b6572e8ab..53a9a56b0cc 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaOutsideParenthesesIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaOutsideParenthesesIntention.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.core.moveFunctionLiteralOutsideParentheses import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containsInside import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentsInParentheses diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveUnnecessaryParenthesesIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveUnnecessaryParenthesesIntention.kt index aa10c22ff98..12e6d5e250e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveUnnecessaryParenthesesIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveUnnecessaryParenthesesIntention.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.intentions import com.intellij.openapi.editor.Editor import org.jetbrains.kotlin.psi.JetParenthesizedExpression import org.jetbrains.kotlin.psi.JetPsiUtil +import org.jetbrains.kotlin.psi.psiUtil.containsInside public class RemoveUnnecessaryParenthesesIntention : JetSelfTargetingIntention(javaClass(), "Remove unnecessary parentheses") { override fun isApplicableTo(element: JetParenthesizedExpression, caretOffset: Int): Boolean { diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt index c4f38d23d4a..4965b9972b0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt @@ -63,8 +63,6 @@ fun JetContainerNode.description(): String? { return null } -fun TextRange.containsInside(offset: Int) = getStartOffset() < offset && offset < getEndOffset() - fun isAutoCreatedItUsage(expression: JetSimpleNameExpression): Boolean { if (expression.getReferencedName() != "it") return false val context = expression.analyze() diff --git a/idea/testData/intentions/addNameToArgument/notInsideIndices.kt b/idea/testData/intentions/addNameToArgument/notInsideIndices.kt new file mode 100644 index 00000000000..73fa7f08d73 --- /dev/null +++ b/idea/testData/intentions/addNameToArgument/notInsideIndices.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +fun foo(p: Int){} + +fun bar(list: List) { + foo(list[1]) +} \ No newline at end of file diff --git a/idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList.kt b/idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList.kt new file mode 100644 index 00000000000..0951d1efc13 --- /dev/null +++ b/idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +fun foo(p: Int){} + +fun bar() { + foo("".hashCode()) +} \ No newline at end of file diff --git a/idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList2.kt b/idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList2.kt new file mode 100644 index 00000000000..b1c76b20509 --- /dev/null +++ b/idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList2.kt @@ -0,0 +1,5 @@ +fun foo(p: Int){} + +fun bar() { + foo("".hashCode()) +} \ No newline at end of file diff --git a/idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList2.kt.after b/idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList2.kt.after new file mode 100644 index 00000000000..919b7a36dbb --- /dev/null +++ b/idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList2.kt.after @@ -0,0 +1,5 @@ +fun foo(p: Int){} + +fun bar() { + foo(p = "".hashCode()) +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index e0b489741cf..e905b23ebbd 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -130,6 +130,24 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("notInsideIndices.kt") + public void testNotInsideIndices() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/notInsideIndices.kt"); + doTest(fileName); + } + + @TestMetadata("notInsideNestedArgumentList.kt") + public void testNotInsideNestedArgumentList() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList.kt"); + doTest(fileName); + } + + @TestMetadata("notInsideNestedArgumentList2.kt") + public void testNotInsideNestedArgumentList2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/notInsideNestedArgumentList2.kt"); + doTest(fileName); + } + @TestMetadata("notLast.kt") public void testNotLast() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/addNameToArgument/notLast.kt");