A bit more correct fix of KT-14665
This commit is contained in:
+40
-14
@@ -21,15 +21,13 @@ import com.intellij.codeInsight.completion.InsertionContext
|
|||||||
import com.intellij.codeInsight.lookup.LookupElement
|
import com.intellij.codeInsight.lookup.LookupElement
|
||||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||||
import com.intellij.openapi.module.ModuleUtilCore
|
import com.intellij.openapi.module.ModuleUtilCore
|
||||||
import com.intellij.psi.PsiComment
|
import com.intellij.psi.*
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.PsiErrorElement
|
|
||||||
import com.intellij.psi.PsiWhiteSpace
|
|
||||||
import com.intellij.psi.filters.*
|
import com.intellij.psi.filters.*
|
||||||
import com.intellij.psi.filters.position.LeftNeighbour
|
import com.intellij.psi.filters.position.LeftNeighbour
|
||||||
import com.intellij.psi.filters.position.PositionElementFilter
|
import com.intellij.psi.filters.position.PositionElementFilter
|
||||||
import com.intellij.psi.tree.IElementType
|
import com.intellij.psi.tree.IElementType
|
||||||
import com.intellij.psi.tree.TokenSet
|
import com.intellij.psi.tree.TokenSet
|
||||||
|
import org.jetbrains.kotlin.KtNodeTypes
|
||||||
import org.jetbrains.kotlin.config.LanguageFeature
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||||
@@ -206,18 +204,35 @@ object KeywordCompletion {
|
|||||||
is KtBlockExpression -> {
|
is KtBlockExpression -> {
|
||||||
var prefixText = "fun foo() { "
|
var prefixText = "fun foo() { "
|
||||||
if (prevParent is KtExpression) {
|
if (prevParent is KtExpression) {
|
||||||
// check that we are right after a try-expression without finally-block
|
// check that we are right after a try-expression without finally-block or after if-expression without else
|
||||||
val prevLeaf = prevParent.prevLeaf { it !is PsiWhiteSpace && it !is PsiComment && it !is PsiErrorElement }
|
val prevLeaf = prevParent.prevLeaf { it !is PsiWhiteSpace && it !is PsiComment && it !is PsiErrorElement }
|
||||||
if (prevLeaf?.node?.elementType == KtTokens.RBRACE) {
|
if (prevLeaf != null) {
|
||||||
val blockParent = (prevLeaf?.parent as? KtBlockExpression)?.parent
|
val isAfterThen = prevLeaf.goUpWhileIsLastChild().any { it.node.elementType == KtNodeTypes.THEN }
|
||||||
when (blockParent) {
|
|
||||||
is KtTryExpression -> prefixText += "try {}\n"
|
|
||||||
is KtCatchClause -> prefixText += "try {} catch (e: E) {}\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevLeaf?.getParentOfType<KtIfExpression>(strict = false) != null) {
|
var isAfterTry = false
|
||||||
prefixText += "if(true){}\n"
|
var isAfterCatch = false
|
||||||
|
if (prevLeaf.node.elementType == KtTokens.RBRACE) {
|
||||||
|
val blockParent = (prevLeaf.parent as? KtBlockExpression)?.parent
|
||||||
|
when (blockParent) {
|
||||||
|
is KtTryExpression -> isAfterTry = true
|
||||||
|
is KtCatchClause -> { isAfterTry = true; isAfterCatch = true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAfterThen) {
|
||||||
|
if (isAfterTry) {
|
||||||
|
prefixText += "if (a)\n"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
prefixText += "if (a) {}\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isAfterTry) {
|
||||||
|
prefixText += "try {}\n"
|
||||||
|
}
|
||||||
|
if (isAfterCatch) {
|
||||||
|
prefixText += "catch (e: E) {}\n"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildFilterWithContext(prefixText, prevParent, position)
|
return buildFilterWithContext(prefixText, prevParent, position)
|
||||||
@@ -460,4 +475,15 @@ object KeywordCompletion {
|
|||||||
if (ancestor == this) return 0
|
if (ancestor == this) return 0
|
||||||
return parent!!.getStartOffsetInAncestor(ancestor) + startOffsetInParent
|
return parent!!.getStartOffsetInAncestor(ancestor) + startOffsetInParent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun PsiElement.goUpWhileIsLastChild(): Sequence<PsiElement> {
|
||||||
|
return generateSequence(this) {
|
||||||
|
if (it is PsiFile)
|
||||||
|
null
|
||||||
|
else if (it != it.parent.lastChild)
|
||||||
|
null
|
||||||
|
else
|
||||||
|
it.parent
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ fun some() {
|
|||||||
|
|
||||||
|
|
||||||
// EXIST: do
|
// EXIST: do
|
||||||
// EXIST: as
|
|
||||||
// EXIST: class
|
// EXIST: class
|
||||||
// EXIST: else
|
|
||||||
// EXIST: false
|
// EXIST: false
|
||||||
// EXIST: for
|
// EXIST: for
|
||||||
// EXIST: fun
|
// EXIST: fun
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
fun foo(p: Int) {
|
||||||
|
if (p > 0) {
|
||||||
|
|
||||||
|
}
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: else
|
||||||
|
// EXIST: class
|
||||||
|
// EXIST: do
|
||||||
|
// EXIST: false
|
||||||
|
// EXIST: for
|
||||||
|
// EXIST: fun
|
||||||
|
// EXIST: if
|
||||||
|
// EXIST: null
|
||||||
|
// EXIST: object
|
||||||
|
// EXIST: return
|
||||||
|
// EXIST: super
|
||||||
|
// EXIST: throw
|
||||||
|
// EXIST: interface
|
||||||
|
// EXIST: true
|
||||||
|
// EXIST: try
|
||||||
|
// EXIST: val
|
||||||
|
// EXIST: var
|
||||||
|
// EXIST: when
|
||||||
|
// EXIST: while
|
||||||
|
// EXIST: typealias
|
||||||
|
// EXIST: as
|
||||||
|
// NOTHING_ELSE
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
fun foo(p: Int) {
|
||||||
|
if (p > 0)
|
||||||
|
foo()
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: else
|
||||||
|
// EXIST: class
|
||||||
|
// EXIST: do
|
||||||
|
// EXIST: false
|
||||||
|
// EXIST: for
|
||||||
|
// EXIST: fun
|
||||||
|
// EXIST: if
|
||||||
|
// EXIST: null
|
||||||
|
// EXIST: object
|
||||||
|
// EXIST: return
|
||||||
|
// EXIST: super
|
||||||
|
// EXIST: throw
|
||||||
|
// EXIST: interface
|
||||||
|
// EXIST: true
|
||||||
|
// EXIST: try
|
||||||
|
// EXIST: val
|
||||||
|
// EXIST: var
|
||||||
|
// EXIST: when
|
||||||
|
// EXIST: while
|
||||||
|
// EXIST: typealias
|
||||||
|
// EXIST: as
|
||||||
|
// NOTHING_ELSE
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fun foo(p: Int) {
|
|
||||||
var x = 0
|
|
||||||
if (p > 0)
|
|
||||||
x += p
|
|
||||||
el<caret>
|
|
||||||
}
|
|
||||||
|
|
||||||
//EXIST: else
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fun foo(p: Int) {
|
|
||||||
if (p > 0) {
|
|
||||||
|
|
||||||
}
|
|
||||||
el<caret>
|
|
||||||
}
|
|
||||||
|
|
||||||
//EXIST: else
|
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
fun foo(p: Int) {
|
||||||
|
if (p > 0)
|
||||||
|
try {
|
||||||
|
}
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: catch
|
||||||
|
// EXIST: finally
|
||||||
|
// EXIST: false
|
||||||
|
// EXIST: null
|
||||||
|
// EXIST: true
|
||||||
|
// NOTHING_ELSE
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
fun foo(p: Int) {
|
||||||
|
if (p > 0)
|
||||||
|
try {
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
}
|
||||||
|
<caret>
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: catch
|
||||||
|
// EXIST: finally
|
||||||
|
// EXIST: else
|
||||||
|
// EXIST: class
|
||||||
|
// EXIST: do
|
||||||
|
// EXIST: false
|
||||||
|
// EXIST: for
|
||||||
|
// EXIST: fun
|
||||||
|
// EXIST: if
|
||||||
|
// EXIST: null
|
||||||
|
// EXIST: object
|
||||||
|
// EXIST: return
|
||||||
|
// EXIST: super
|
||||||
|
// EXIST: throw
|
||||||
|
// EXIST: interface
|
||||||
|
// EXIST: true
|
||||||
|
// EXIST: try
|
||||||
|
// EXIST: val
|
||||||
|
// EXIST: var
|
||||||
|
// EXIST: when
|
||||||
|
// EXIST: while
|
||||||
|
// EXIST: typealias
|
||||||
|
// EXIST: as
|
||||||
|
// NOTHING_ELSE
|
||||||
@@ -10,9 +10,7 @@ fun some() {
|
|||||||
|
|
||||||
|
|
||||||
// EXIST: do
|
// EXIST: do
|
||||||
// EXIST: as
|
|
||||||
// EXIST: class
|
// EXIST: class
|
||||||
// EXIST: else
|
|
||||||
// EXIST: false
|
// EXIST: false
|
||||||
// EXIST: for
|
// EXIST: for
|
||||||
// EXIST: fun
|
// EXIST: fun
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ fun some() {
|
|||||||
|
|
||||||
|
|
||||||
// EXIST: do
|
// EXIST: do
|
||||||
// EXIST: as
|
|
||||||
// EXIST: class
|
// EXIST: class
|
||||||
// EXIST: else
|
|
||||||
// EXIST: false
|
// EXIST: false
|
||||||
// EXIST: for
|
// EXIST: for
|
||||||
// EXIST: fun
|
// EXIST: fun
|
||||||
|
|||||||
+18
-6
@@ -150,15 +150,15 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("ElseAfterBlocklessIf.kt")
|
@TestMetadata("Else1.kt")
|
||||||
public void testElseAfterBlocklessIf() throws Exception {
|
public void testElse1() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ElseAfterBlocklessIf.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/Else1.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("ElseOnOtherLine.kt")
|
@TestMetadata("Else2.kt")
|
||||||
public void testElseOnOtherLine() throws Exception {
|
public void testElse2() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/ElseOnOtherLine.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/Else2.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +174,18 @@ public class KeywordCompletionTestGenerated extends AbstractKeywordCompletionTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IfTry.kt")
|
||||||
|
public void testIfTry() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/IfTry.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IfTryCatch.kt")
|
||||||
|
public void testIfTryCatch() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/IfTryCatch.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("InAnnotationClassScope.kt")
|
@TestMetadata("InAnnotationClassScope.kt")
|
||||||
public void testInAnnotationClassScope() throws Exception {
|
public void testInAnnotationClassScope() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/InAnnotationClassScope.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/keywords/InAnnotationClassScope.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user