From 0a6243fc67593bf2a7dfc332b00f5f106d1667dd Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Wed, 19 May 2021 16:05:47 +0200 Subject: [PATCH] FIR IDE: implement super qualifier completion --- .../basic/common/super/QualifierType1.kt | 1 + .../basic/common/super/QualifierType2.kt | 1 + .../basic/common/super/QualifierType3.kt | 1 + .../testData/keywords/AfterIf.fir.kt | 33 ++++++++ .../testData/keywords/AfterIf.kt | 1 - .../testData/keywords/AfterTryCatch.fir.kt | 33 ++++++++ .../testData/keywords/AfterTryCatch.kt | 1 - .../testData/keywords/AfterTryFinally.fir.kt | 30 ++++++++ .../testData/keywords/AfterTryFinally.kt | 1 - .../testData/keywords/Else1.fir.kt | 29 +++++++ .../testData/keywords/Else1.kt | 1 - .../testData/keywords/Else2.fir.kt | 28 +++++++ .../testData/keywords/Else2.kt | 1 - .../keywords/GlobalPropertyAccessors.fir.kt | 59 +++++++++++++++ .../testData/keywords/IfTryCatch.fir.kt | 33 ++++++++ .../testData/keywords/IfTryCatch.kt | 1 - .../testData/keywords/InArgumentList.fir.kt | 22 ++++++ .../testData/keywords/InArgumentList.kt | 1 - .../testData/keywords/InCodeBlock.fir.kt | 24 ++++++ .../testData/keywords/InCodeBlock.kt | 1 - .../testData/keywords/InElse.fir.kt | 32 ++++++++ .../testData/keywords/InElse.kt | 1 - .../keywords/InFunctionExpressionBody.fir.kt | 16 ++++ .../keywords/InFunctionExpressionBody.kt | 1 - .../keywords/InGetterExpressionBody.fir.kt | 17 +++++ .../keywords/InGetterExpressionBody.kt | 1 - .../testData/keywords/InIf.fir.kt | 32 ++++++++ .../idea-completion/testData/keywords/InIf.kt | 1 - .../keywords/InParameterDefaultValue.fir.kt | 17 +++++ .../keywords/InParameterDefaultValue.kt | 1 - .../keywords/InPropertyInitializer.fir.kt | 16 ++++ .../keywords/InPropertyInitializer.kt | 1 - .../KotlinFirCompletionContributor.kt | 5 ++ .../KotlinFirLookupElementFactory.kt | 8 +- .../context/FirPositionCompletionContext.kt | 34 ++++++--- .../FirKeywordCompletionContributor.kt | 2 + .../contributors/FirSuperEntryContributor.kt | 42 +++++++++++ .../helpers/FirSuperEntriesProvider.kt | 75 +++++++++++++++++++ .../keywords/SuperKeywordHandler.kt | 62 +++++++++++++++ 39 files changed, 639 insertions(+), 27 deletions(-) create mode 100644 idea/idea-completion/testData/keywords/AfterIf.fir.kt create mode 100644 idea/idea-completion/testData/keywords/AfterTryCatch.fir.kt create mode 100644 idea/idea-completion/testData/keywords/AfterTryFinally.fir.kt create mode 100644 idea/idea-completion/testData/keywords/Else1.fir.kt create mode 100644 idea/idea-completion/testData/keywords/Else2.fir.kt create mode 100644 idea/idea-completion/testData/keywords/GlobalPropertyAccessors.fir.kt create mode 100644 idea/idea-completion/testData/keywords/IfTryCatch.fir.kt create mode 100644 idea/idea-completion/testData/keywords/InArgumentList.fir.kt create mode 100644 idea/idea-completion/testData/keywords/InCodeBlock.fir.kt create mode 100644 idea/idea-completion/testData/keywords/InElse.fir.kt create mode 100644 idea/idea-completion/testData/keywords/InFunctionExpressionBody.fir.kt create mode 100644 idea/idea-completion/testData/keywords/InGetterExpressionBody.fir.kt create mode 100644 idea/idea-completion/testData/keywords/InIf.fir.kt create mode 100644 idea/idea-completion/testData/keywords/InParameterDefaultValue.fir.kt create mode 100644 idea/idea-completion/testData/keywords/InPropertyInitializer.fir.kt create mode 100644 idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirSuperEntryContributor.kt create mode 100644 idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/helpers/FirSuperEntriesProvider.kt create mode 100644 idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/keywords/SuperKeywordHandler.kt diff --git a/idea/idea-completion/testData/basic/common/super/QualifierType1.kt b/idea/idea-completion/testData/basic/common/super/QualifierType1.kt index a7636fd08df..1fbbc67bc7a 100644 --- a/idea/idea-completion/testData/basic/common/super/QualifierType1.kt +++ b/idea/idea-completion/testData/basic/common/super/QualifierType1.kt @@ -1,3 +1,4 @@ +// FIR_COMPARISON open class Base open class A : Base diff --git a/idea/idea-completion/testData/basic/common/super/QualifierType2.kt b/idea/idea-completion/testData/basic/common/super/QualifierType2.kt index cf9c4cf1f91..9d825e8a737 100644 --- a/idea/idea-completion/testData/basic/common/super/QualifierType2.kt +++ b/idea/idea-completion/testData/basic/common/super/QualifierType2.kt @@ -1,3 +1,4 @@ +// FIR_COMPARISON open class A interface I diff --git a/idea/idea-completion/testData/basic/common/super/QualifierType3.kt b/idea/idea-completion/testData/basic/common/super/QualifierType3.kt index 916d0ebd5d2..c19fd5eebc8 100644 --- a/idea/idea-completion/testData/basic/common/super/QualifierType3.kt +++ b/idea/idea-completion/testData/basic/common/super/QualifierType3.kt @@ -1,3 +1,4 @@ +// FIR_COMPARISON open class AA interface AI diff --git a/idea/idea-completion/testData/keywords/AfterIf.fir.kt b/idea/idea-completion/testData/keywords/AfterIf.fir.kt new file mode 100644 index 00000000000..6fd391adc15 --- /dev/null +++ b/idea/idea-completion/testData/keywords/AfterIf.fir.kt @@ -0,0 +1,33 @@ +// FIR_COMPARISON +fun some() { + + if (true) { + + } else { + + } + + +} + + +// EXIST: do +// EXIST: class +// EXIST: false +// EXIST: for +// EXIST: fun +// EXIST: if +// EXIST: interface +// EXIST: null +// EXIST: object +// EXIST: return +// EXIST: throw +// EXIST: true +// EXIST: try +// EXIST: typealias +// EXIST: val +// EXIST: var +// EXIST: when +// EXIST: while + +// NOTHING_ELSE \ No newline at end of file diff --git a/idea/idea-completion/testData/keywords/AfterIf.kt b/idea/idea-completion/testData/keywords/AfterIf.kt index a88d8ab7c05..dcd5cf434e7 100644 --- a/idea/idea-completion/testData/keywords/AfterIf.kt +++ b/idea/idea-completion/testData/keywords/AfterIf.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON fun some() { diff --git a/idea/idea-completion/testData/keywords/AfterTryCatch.fir.kt b/idea/idea-completion/testData/keywords/AfterTryCatch.fir.kt new file mode 100644 index 00000000000..788064cc180 --- /dev/null +++ b/idea/idea-completion/testData/keywords/AfterTryCatch.fir.kt @@ -0,0 +1,33 @@ +// FIR_COMPARISON +fun foo() { + try { + + } + catch(e: Exception) { + + } + +} + +// EXIST: catch +// EXIST: finally +// EXIST: class +// EXIST: do +// EXIST: false +// EXIST: for +// EXIST: fun +// EXIST: if +// EXIST: null +// EXIST: object +// EXIST: return +// EXIST: throw +// EXIST: interface +// EXIST: true +// EXIST: try +// EXIST: val +// EXIST: var +// EXIST: when +// EXIST: while +// EXIST: as +// EXIST: typealias +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/AfterTryCatch.kt b/idea/idea-completion/testData/keywords/AfterTryCatch.kt index 668f707382e..42e442aafbf 100644 --- a/idea/idea-completion/testData/keywords/AfterTryCatch.kt +++ b/idea/idea-completion/testData/keywords/AfterTryCatch.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON fun foo() { try { diff --git a/idea/idea-completion/testData/keywords/AfterTryFinally.fir.kt b/idea/idea-completion/testData/keywords/AfterTryFinally.fir.kt new file mode 100644 index 00000000000..7d025f655bf --- /dev/null +++ b/idea/idea-completion/testData/keywords/AfterTryFinally.fir.kt @@ -0,0 +1,30 @@ +// FIR_COMPARISON +fun foo() { + try { + + } + finally { + + } + +} + +// EXIST: class +// EXIST: do +// EXIST: false +// EXIST: for +// EXIST: fun +// EXIST: if +// EXIST: null +// EXIST: object +// EXIST: return +// EXIST: throw +// EXIST: interface +// EXIST: true +// EXIST: try +// EXIST: val +// EXIST: var +// EXIST: when +// EXIST: while +// EXIST: typealias +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/AfterTryFinally.kt b/idea/idea-completion/testData/keywords/AfterTryFinally.kt index 8f6eee4521c..787755a52cc 100644 --- a/idea/idea-completion/testData/keywords/AfterTryFinally.kt +++ b/idea/idea-completion/testData/keywords/AfterTryFinally.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON fun foo() { try { diff --git a/idea/idea-completion/testData/keywords/Else1.fir.kt b/idea/idea-completion/testData/keywords/Else1.fir.kt new file mode 100644 index 00000000000..5af8a094aa7 --- /dev/null +++ b/idea/idea-completion/testData/keywords/Else1.fir.kt @@ -0,0 +1,29 @@ +// FIR_COMPARISON +fun foo(p: Int) { + if (p > 0) { + + } + +} + +// EXIST: else +// EXIST: class +// EXIST: do +// EXIST: false +// EXIST: for +// EXIST: fun +// EXIST: if +// EXIST: null +// EXIST: object +// EXIST: return +// EXIST: throw +// EXIST: interface +// EXIST: true +// EXIST: try +// EXIST: val +// EXIST: var +// EXIST: when +// EXIST: while +// EXIST: typealias +// EXIST: as +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/Else1.kt b/idea/idea-completion/testData/keywords/Else1.kt index 536737de0f2..382caee4664 100644 --- a/idea/idea-completion/testData/keywords/Else1.kt +++ b/idea/idea-completion/testData/keywords/Else1.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON fun foo(p: Int) { if (p > 0) { diff --git a/idea/idea-completion/testData/keywords/Else2.fir.kt b/idea/idea-completion/testData/keywords/Else2.fir.kt new file mode 100644 index 00000000000..e350e1f227e --- /dev/null +++ b/idea/idea-completion/testData/keywords/Else2.fir.kt @@ -0,0 +1,28 @@ +// FIR_COMPARISON +fun foo(p: Int) { + if (p > 0) + foo() + +} + +// EXIST: else +// EXIST: class +// EXIST: do +// EXIST: false +// EXIST: for +// EXIST: fun +// EXIST: if +// EXIST: null +// EXIST: object +// EXIST: return +// EXIST: throw +// EXIST: interface +// EXIST: true +// EXIST: try +// EXIST: val +// EXIST: var +// EXIST: when +// EXIST: while +// EXIST: typealias +// EXIST: as +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/Else2.kt b/idea/idea-completion/testData/keywords/Else2.kt index 0f62d67a2e6..2101e3e6c15 100644 --- a/idea/idea-completion/testData/keywords/Else2.kt +++ b/idea/idea-completion/testData/keywords/Else2.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON fun foo(p: Int) { if (p > 0) diff --git a/idea/idea-completion/testData/keywords/GlobalPropertyAccessors.fir.kt b/idea/idea-completion/testData/keywords/GlobalPropertyAccessors.fir.kt new file mode 100644 index 00000000000..e5627de13c4 --- /dev/null +++ b/idea/idea-completion/testData/keywords/GlobalPropertyAccessors.fir.kt @@ -0,0 +1,59 @@ +// FIR_COMPARISON +// COMPILER_ARGUMENTS: -XXLanguage:+SealedInterfaces -XXLanguage:+MultiPlatformProjects + +fun foo() { + bar() +} + +fun bar() { + foo() +} + +var a : Int + + +// EXIST: abstract +// EXIST: by +// EXIST: class +// EXIST: class GlobalPropertyAccessors +// EXIST: enum class +// EXIST: enum class GlobalPropertyAccessors +// EXIST: final +// EXIST: fun +// EXIST: { itemText: "get", tailText: null } +// EXIST: { itemText: "get", tailText: "() = ..." } +// EXIST: { itemText: "get", tailText: "() {...}" } +// EXIST: internal +// EXIST: object +// EXIST: object GlobalPropertyAccessors +// EXIST: open +// EXIST: private +// EXIST: public +// EXIST: { itemText: "set", tailText: null } +// EXIST: { itemText: "set", tailText: "(value) = ..." } +// EXIST: { itemText: "set", tailText: "(value) {...}" } +// EXIST: interface +// EXIST: interface GlobalPropertyAccessors +// EXIST: val +// EXIST: var +// EXIST: operator +// EXIST: infix +// EXIST: sealed class +// EXIST: sealed class GlobalPropertyAccessors +// EXIST: sealed interface GlobalPropertyAccessors +// EXIST: sealed interface +// EXIST: data class +// EXIST: { "lookupString":"data class", "itemText":"data class", "tailText":" GlobalPropertyAccessors(...)", "attributes":"bold" } +// EXIST: inline +// EXIST: value +// EXIST: tailrec +// EXIST: external +// EXIST: annotation class +// EXIST: annotation class GlobalPropertyAccessors +// EXIST: const val +// EXIST: suspend fun +// EXIST: typealias +// EXIST: expect +// EXIST: actual +// EXIST: lateinit var +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/IfTryCatch.fir.kt b/idea/idea-completion/testData/keywords/IfTryCatch.fir.kt new file mode 100644 index 00000000000..f74b2d0d7fd --- /dev/null +++ b/idea/idea-completion/testData/keywords/IfTryCatch.fir.kt @@ -0,0 +1,33 @@ +// FIR_COMPARISON +fun foo(p: Int) { + if (p > 0) + try { + } + catch (e: Exception) { + } + +} + +// 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: throw +// EXIST: interface +// EXIST: true +// EXIST: try +// EXIST: val +// EXIST: var +// EXIST: when +// EXIST: while +// EXIST: typealias +// EXIST: as +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/IfTryCatch.kt b/idea/idea-completion/testData/keywords/IfTryCatch.kt index b3abd2c7947..b1ae9d0b9b5 100644 --- a/idea/idea-completion/testData/keywords/IfTryCatch.kt +++ b/idea/idea-completion/testData/keywords/IfTryCatch.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON fun foo(p: Int) { if (p > 0) diff --git a/idea/idea-completion/testData/keywords/InArgumentList.fir.kt b/idea/idea-completion/testData/keywords/InArgumentList.fir.kt new file mode 100644 index 00000000000..4f0fc905037 --- /dev/null +++ b/idea/idea-completion/testData/keywords/InArgumentList.fir.kt @@ -0,0 +1,22 @@ +// FIR_COMPARISON +fun foo(p: Int) { + x() + y() + val edge = a.b() + z() +} + +// EXIST: do +// EXIST: false +// EXIST: for +// EXIST: fun +// EXIST: if +// EXIST: null +// EXIST: object +// EXIST: return +// EXIST: throw +// EXIST: true +// EXIST: try +// EXIST: when +// EXIST: while +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/InArgumentList.kt b/idea/idea-completion/testData/keywords/InArgumentList.kt index 71cf832eb3e..72cee4bc6e8 100644 --- a/idea/idea-completion/testData/keywords/InArgumentList.kt +++ b/idea/idea-completion/testData/keywords/InArgumentList.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON fun foo(p: Int) { x() diff --git a/idea/idea-completion/testData/keywords/InCodeBlock.fir.kt b/idea/idea-completion/testData/keywords/InCodeBlock.fir.kt new file mode 100644 index 00000000000..535ab75fc61 --- /dev/null +++ b/idea/idea-completion/testData/keywords/InCodeBlock.fir.kt @@ -0,0 +1,24 @@ +// FIR_COMPARISON +fun foo() { + +} + +// EXIST: class +// EXIST: do +// EXIST: false +// EXIST: for +// EXIST: fun +// EXIST: if +// EXIST: null +// EXIST: object +// EXIST: return +// EXIST: throw +// EXIST: interface +// EXIST: true +// EXIST: try +// EXIST: val +// EXIST: var +// EXIST: when +// EXIST: while +// EXIST: typealias +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/InCodeBlock.kt b/idea/idea-completion/testData/keywords/InCodeBlock.kt index 4edb5c61503..f5f0481f1af 100644 --- a/idea/idea-completion/testData/keywords/InCodeBlock.kt +++ b/idea/idea-completion/testData/keywords/InCodeBlock.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON fun foo() { diff --git a/idea/idea-completion/testData/keywords/InElse.fir.kt b/idea/idea-completion/testData/keywords/InElse.fir.kt new file mode 100644 index 00000000000..7b142f1355a --- /dev/null +++ b/idea/idea-completion/testData/keywords/InElse.fir.kt @@ -0,0 +1,32 @@ +// FIR_COMPARISON +fun some() { + + if (true) { + + } else { + + } + +} + + +// EXIST: do +// EXIST: class +// EXIST: false +// EXIST: for +// EXIST: fun +// EXIST: if +// EXIST: interface +// EXIST: null +// EXIST: object +// EXIST: return +// EXIST: throw +// EXIST: true +// EXIST: try +// EXIST: typealias +// EXIST: val +// EXIST: var +// EXIST: when +// EXIST: while + +// NOTHING_ELSE \ No newline at end of file diff --git a/idea/idea-completion/testData/keywords/InElse.kt b/idea/idea-completion/testData/keywords/InElse.kt index 841cc14bb68..ee867bb2f12 100644 --- a/idea/idea-completion/testData/keywords/InElse.kt +++ b/idea/idea-completion/testData/keywords/InElse.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON fun some() { diff --git a/idea/idea-completion/testData/keywords/InFunctionExpressionBody.fir.kt b/idea/idea-completion/testData/keywords/InFunctionExpressionBody.fir.kt new file mode 100644 index 00000000000..ad36e9b3f93 --- /dev/null +++ b/idea/idea-completion/testData/keywords/InFunctionExpressionBody.fir.kt @@ -0,0 +1,16 @@ +// FIR_COMPARISON +fun foo() = + +// EXIST: do +// EXIST: false +// EXIST: for +// EXIST: fun +// EXIST: if +// EXIST: null +// EXIST: object +// EXIST: throw +// EXIST: true +// EXIST: try +// EXIST: when +// EXIST: while +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/InFunctionExpressionBody.kt b/idea/idea-completion/testData/keywords/InFunctionExpressionBody.kt index ea971bee725..6370f714fa7 100644 --- a/idea/idea-completion/testData/keywords/InFunctionExpressionBody.kt +++ b/idea/idea-completion/testData/keywords/InFunctionExpressionBody.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON fun foo() = diff --git a/idea/idea-completion/testData/keywords/InGetterExpressionBody.fir.kt b/idea/idea-completion/testData/keywords/InGetterExpressionBody.fir.kt new file mode 100644 index 00000000000..18f30a89aca --- /dev/null +++ b/idea/idea-completion/testData/keywords/InGetterExpressionBody.fir.kt @@ -0,0 +1,17 @@ +// FIR_COMPARISON +val prop: Int + get() = + +// EXIST: do +// EXIST: false +// EXIST: for +// EXIST: fun +// EXIST: if +// EXIST: null +// EXIST: object +// EXIST: throw +// EXIST: true +// EXIST: try +// EXIST: when +// EXIST: while +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/InGetterExpressionBody.kt b/idea/idea-completion/testData/keywords/InGetterExpressionBody.kt index 72b0eaf9774..fd4ff547f02 100644 --- a/idea/idea-completion/testData/keywords/InGetterExpressionBody.kt +++ b/idea/idea-completion/testData/keywords/InGetterExpressionBody.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON val prop: Int get() = diff --git a/idea/idea-completion/testData/keywords/InIf.fir.kt b/idea/idea-completion/testData/keywords/InIf.fir.kt new file mode 100644 index 00000000000..fcf343921d2 --- /dev/null +++ b/idea/idea-completion/testData/keywords/InIf.fir.kt @@ -0,0 +1,32 @@ +// FIR_COMPARISON +fun some() { + + if (true) { + + } else { + + } + +} + + +// EXIST: do +// EXIST: class +// EXIST: false +// EXIST: for +// EXIST: fun +// EXIST: if +// EXIST: interface +// EXIST: null +// EXIST: object +// EXIST: return +// EXIST: throw +// EXIST: true +// EXIST: try +// EXIST: typealias +// EXIST: val +// EXIST: var +// EXIST: when +// EXIST: while + +// NOTHING_ELSE \ No newline at end of file diff --git a/idea/idea-completion/testData/keywords/InIf.kt b/idea/idea-completion/testData/keywords/InIf.kt index 27d5895e60a..9c36c4eeeb8 100644 --- a/idea/idea-completion/testData/keywords/InIf.kt +++ b/idea/idea-completion/testData/keywords/InIf.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON fun some() { diff --git a/idea/idea-completion/testData/keywords/InParameterDefaultValue.fir.kt b/idea/idea-completion/testData/keywords/InParameterDefaultValue.fir.kt new file mode 100644 index 00000000000..2b7f36e95c1 --- /dev/null +++ b/idea/idea-completion/testData/keywords/InParameterDefaultValue.fir.kt @@ -0,0 +1,17 @@ +// FIR_COMPARISON +fun foo(p: Int = ) + +// EXIST: do +// EXIST: false +// EXIST: for +// EXIST: fun +// EXIST: if +// EXIST: null +// EXIST: object +// EXIST: return +// EXIST: throw +// EXIST: true +// EXIST: try +// EXIST: when +// EXIST: while +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/InParameterDefaultValue.kt b/idea/idea-completion/testData/keywords/InParameterDefaultValue.kt index dc687aa76ad..a1ae2d84809 100644 --- a/idea/idea-completion/testData/keywords/InParameterDefaultValue.kt +++ b/idea/idea-completion/testData/keywords/InParameterDefaultValue.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON fun foo(p: Int = ) diff --git a/idea/idea-completion/testData/keywords/InPropertyInitializer.fir.kt b/idea/idea-completion/testData/keywords/InPropertyInitializer.fir.kt new file mode 100644 index 00000000000..63d45f2503a --- /dev/null +++ b/idea/idea-completion/testData/keywords/InPropertyInitializer.fir.kt @@ -0,0 +1,16 @@ +// FIR_COMPARISON +var a : Int = + +// EXIST: do +// EXIST: false +// EXIST: for +// EXIST: fun +// EXIST: if +// EXIST: null +// EXIST: object +// EXIST: throw +// EXIST: true +// EXIST: try +// EXIST: when +// EXIST: while +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/keywords/InPropertyInitializer.kt b/idea/idea-completion/testData/keywords/InPropertyInitializer.kt index a4efb635a2a..2131fe0d179 100644 --- a/idea/idea-completion/testData/keywords/InPropertyInitializer.kt +++ b/idea/idea-completion/testData/keywords/InPropertyInitializer.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL // FIR_COMPARISON var a : Int = diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt index 87c7e124d18..0ce23521ae5 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt @@ -78,6 +78,7 @@ private object KotlinFirCompletionProvider : CompletionProvider { complete(keywordContributor, positionContext) complete(packageCompletionContributor, positionContext) @@ -90,6 +91,10 @@ private object KotlinFirCompletionProvider : CompletionProvider { + complete(FirSuperEntryContributor(basicContext), positionContext) + } + is FirUnknownPositionContext -> { complete(keywordContributor, positionContext) } diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt index 74e08db2579..078c1b9f5fe 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt @@ -232,7 +232,7 @@ private object ClassifierInsertionHandler : InsertHandler { context.document.replaceString(context.startOffset, context.tailOffset, fqName.render()) context.commitDocument() - shortenReferences(targetFile, TextRange(context.startOffset, context.tailOffset)) + shortenReferencesForFirCompletion(targetFile, TextRange(context.startOffset, context.tailOffset)) } } } @@ -338,7 +338,7 @@ private object FunctionInsertionHandler : QuotedNamesAwareInsertionHandler() { addArguments(context, element, lookupObject) context.commitDocument() - shortenReferences(targetFile, TextRange(context.startOffset, context.tailOffset)) + shortenReferencesForFirCompletion(targetFile, TextRange(context.startOffset, context.tailOffset)) } else { addArguments(context, element, lookupObject) context.commitDocument() @@ -368,7 +368,7 @@ private object VariableInsertionHandler : InsertHandler { ) context.commitDocument() - shortenReferences(targetFile, TextRange(context.startOffset, context.tailOffset)) + shortenReferencesForFirCompletion(targetFile, TextRange(context.startOffset, context.tailOffset)) } is CallableImportStrategy.DoNothing -> {} @@ -435,7 +435,7 @@ private fun CharSequence.indexOfSkippingSpace(c: Char, startIndex: Int): Int? { return null } -private fun shortenReferences(targetFile: KtFile, textRange: TextRange) { +internal fun shortenReferencesForFirCompletion(targetFile: KtFile, textRange: TextRange) { val shortenings = withAllowedResolve { analyse(targetFile) { collectPossibleReferenceShortenings(targetFile, textRange) diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/context/FirPositionCompletionContext.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/context/FirPositionCompletionContext.kt index a75685958d3..03012c55725 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/context/FirPositionCompletionContext.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/context/FirPositionCompletionContext.kt @@ -39,6 +39,14 @@ internal class FirAnnotationTypeNameReferencePositionContext( val annotationEntry: KtAnnotationEntry, ) : FirNameReferencePositionContext() +internal class FirSuperTypeCallNameReferencePositionContext( + override val position: PsiElement, + override val reference: KtSimpleNameReference, + override val nameExpression: KtSimpleNameExpression, + override val explicitReceiver: KtExpression?, + val superExpression: KtSuperExpression, +) : FirNameReferencePositionContext() + internal class FirExpressionNameReferencePositionContext( override val position: PsiElement, @@ -78,15 +86,23 @@ internal object FirPositionCompletionContextDetector { nameExpression: KtSimpleNameExpression, explicitReceiver: KtExpression? ): FirNameReferencePositionContext { - val annotationEntry = userType.annotationEntry() - ?: return FirTypeNameReferencePositionContext(position, reference, nameExpression, explicitReceiver) - return FirAnnotationTypeNameReferencePositionContext(position, reference, nameExpression, explicitReceiver, annotationEntry) - } - - private fun KtUserType.annotationEntry(): KtAnnotationEntry? { - val typeReference = (parent as? KtTypeReference)?.takeIf { it.typeElement == this } - val constructorCall = (typeReference?.parent as? KtConstructorCalleeExpression)?.takeIf { it.typeReference == typeReference } - return (constructorCall?.parent as? KtAnnotationEntry)?.takeIf { it.calleeExpression == constructorCall } + val typeReference = (userType.parent as? KtTypeReference)?.takeIf { it.typeElement == userType } + return when (val typeReferenceOwner = typeReference?.parent) { + is KtConstructorCalleeExpression -> { + val constructorCall = typeReferenceOwner.takeIf { it.typeReference == typeReference } + val annotationEntry = (constructorCall?.parent as? KtAnnotationEntry)?.takeIf { it.calleeExpression == constructorCall } + annotationEntry?.let { + FirAnnotationTypeNameReferencePositionContext(position, reference, nameExpression, explicitReceiver, it) + } + } + is KtSuperExpression -> { + val superTypeCallEntry = typeReferenceOwner.takeIf { it.superTypeQualifier == typeReference } + superTypeCallEntry?.let { + FirSuperTypeCallNameReferencePositionContext(position, reference, nameExpression, explicitReceiver, it) + } + } + else -> null + } ?: FirTypeNameReferencePositionContext(position, reference, nameExpression, explicitReceiver) } inline fun analyseInContext( diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirKeywordCompletionContributor.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirKeywordCompletionContributor.kt index 5521d9787b3..06467d3ddf1 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirKeywordCompletionContributor.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirKeywordCompletionContributor.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.idea.completion.context.FirRawPositionCompletionCont import org.jetbrains.kotlin.idea.completion.context.FirUnknownPositionContext import org.jetbrains.kotlin.idea.completion.contributors.keywords.OverrideKeywordHandler import org.jetbrains.kotlin.idea.completion.contributors.keywords.ReturnKeywordHandler +import org.jetbrains.kotlin.idea.completion.contributors.keywords.SuperKeywordHandler import org.jetbrains.kotlin.idea.completion.contributors.keywords.ThisKeywordHandler import org.jetbrains.kotlin.idea.completion.keywords.CompletionKeywordHandlerProvider import org.jetbrains.kotlin.idea.completion.keywords.CompletionKeywordHandlers @@ -76,5 +77,6 @@ private class ResolveDependentCompletionKeywordHandlerProvider( ReturnKeywordHandler, OverrideKeywordHandler(basicContext), ThisKeywordHandler(basicContext), + SuperKeywordHandler, ) } \ No newline at end of file diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirSuperEntryContributor.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirSuperEntryContributor.kt new file mode 100644 index 00000000000..11047e69111 --- /dev/null +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirSuperEntryContributor.kt @@ -0,0 +1,42 @@ +/* + * 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.completion.contributors + +import com.intellij.codeInsight.lookup.LookupElementBuilder +import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext +import org.jetbrains.kotlin.idea.completion.context.FirSuperTypeCallNameReferenceRawPositionContext +import org.jetbrains.kotlin.idea.completion.contributors.helpers.FirSuperEntriesProvider.getSuperClassesAvailableForSuperCall +import org.jetbrains.kotlin.idea.completion.contributors.helpers.SuperCallLookupObject +import org.jetbrains.kotlin.idea.completion.contributors.helpers.SuperCallInsertionHandler +import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name + + +internal class FirSuperEntryContributor( + basicContext: FirBasicCompletionContext, +) : FirContextCompletionContributorBase(basicContext) { + override fun KtAnalysisSession.complete(positionContext: FirSuperTypeCallNameReferenceRawPositionContext) { + getSuperClassesAvailableForSuperCall(positionContext.nameExpression).forEach { superType -> + val tailText = superType.classIdIfNonLocal?.asString()?.let { "($it)" } + LookupElementBuilder.create(SuperLookupObject(superType.name, superType.classIdIfNonLocal), superType.name.asString()) + .withTailText(tailText) + .withInsertHandler(SuperCallInsertionHandler) + .let { result.addElement(it) } + } + } +} + +private class SuperLookupObject(val className: Name, val classId: ClassId?) : SuperCallLookupObject { + override val replaceTo: String + get() = when { + classId != null -> "${classId.asSingleFqName().asString()}>" + else -> "${className.asString()}>" + } + + override val shortenReferencesInReplaced: Boolean + get() = classId != null +} diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/helpers/FirSuperEntriesProvider.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/helpers/FirSuperEntriesProvider.kt new file mode 100644 index 00000000000..98800cef432 --- /dev/null +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/helpers/FirSuperEntriesProvider.kt @@ -0,0 +1,75 @@ +/* + * 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.completion.contributors.helpers + +import com.intellij.codeInsight.completion.CodeCompletionHandlerBase +import com.intellij.codeInsight.completion.CompletionType +import com.intellij.codeInsight.completion.InsertHandler +import com.intellij.codeInsight.completion.InsertionContext +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.idea.completion.shortenReferencesForFirCompletion +import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession +import org.jetbrains.kotlin.idea.frontend.api.symbols.KtNamedClassOrObjectSymbol +import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType + +internal object FirSuperEntriesProvider { + fun KtAnalysisSession.getSuperClassesAvailableForSuperCall(context: PsiElement): List { + val containingClass = context.getStrictParentOfType() ?: return emptyList() + val containingClassSymbol = containingClass.getClassOrObjectSymbol() + return containingClassSymbol.superTypes.mapNotNull { superType -> + val classType = superType.type as? KtClassType ?: return@mapNotNull null + classType.classSymbol as? KtNamedClassOrObjectSymbol + } + } +} + +internal object SuperCallInsertionHandler : InsertHandler { + override fun handleInsert(context: InsertionContext, item: LookupElement) { + val lookupObject = item.`object` as SuperCallLookupObject + + replaceWithClassIdAndShorten(lookupObject, context) + addDot(context) + invokeCompletion(context) + } + + private fun replaceWithClassIdAndShorten( + lookupObject: SuperCallLookupObject, + context: InsertionContext + ) { + val replaceTo = lookupObject.replaceTo ?: return + context.document.replaceString(context.startOffset, context.tailOffset, replaceTo) + context.commitDocument() + + if (lookupObject.shortenReferencesInReplaced) { + val targetFile = context.file as KtFile + shortenReferencesForFirCompletion(targetFile, TextRange(context.startOffset, context.tailOffset)) + } + } + + private fun invokeCompletion(context: InsertionContext) { + ApplicationManager.getApplication().invokeLater { + CodeCompletionHandlerBase(CompletionType.BASIC, true, false, true) + .invokeCompletion(context.project, context.editor) + } + } + + private fun addDot(context: InsertionContext) { + context.document.insertString(context.tailOffset, ".") + context.commitDocument() + context.editor.caretModel.moveToOffset(context.tailOffset) + } +} + +internal interface SuperCallLookupObject { + val replaceTo: String? + val shortenReferencesInReplaced: Boolean +} \ No newline at end of file diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/keywords/SuperKeywordHandler.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/keywords/SuperKeywordHandler.kt new file mode 100644 index 00000000000..b67e12ce9fd --- /dev/null +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/keywords/SuperKeywordHandler.kt @@ -0,0 +1,62 @@ +/* + * 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.completion.contributors.keywords + +import com.intellij.codeInsight.completion.* +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.idea.completion.KeywordLookupObject +import org.jetbrains.kotlin.idea.completion.contributors.helpers.FirSuperEntriesProvider.getSuperClassesAvailableForSuperCall +import org.jetbrains.kotlin.idea.completion.contributors.helpers.SuperCallLookupObject +import org.jetbrains.kotlin.idea.completion.contributors.helpers.SuperCallInsertionHandler +import org.jetbrains.kotlin.idea.completion.createKeywordElement +import org.jetbrains.kotlin.idea.completion.keywords.CompletionKeywordHandler +import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtExpression + +internal object SuperKeywordHandler : CompletionKeywordHandler(KtTokens.SUPER_KEYWORD) { + @OptIn(ExperimentalStdlibApi::class) + override fun KtAnalysisSession.createLookups( + parameters: CompletionParameters, + expression: KtExpression?, + lookup: LookupElement, + project: Project + ): Collection { + val superClasses = getSuperClassesAvailableForSuperCall(parameters.position) + + if (superClasses.isEmpty()) { + return emptyList() + } + + if (expression == null) { + // for completion in secondary constructor delegation call + return listOf(lookup) + } + + return when { + superClasses.size <= 1 -> listOf(lookup) + else -> buildList { + add(lookup) + superClasses.mapTo(this) { symbol -> + createKeywordElement("super", "<${symbol.name}>", SuperKeywordLookupObject(symbol.name, symbol.classIdIfNonLocal)) + .withInsertHandler(SuperCallInsertionHandler) + } + } + } + } +} + +private class SuperKeywordLookupObject(val className: Name, val classId: ClassId?) : KeywordLookupObject(), SuperCallLookupObject { + override val replaceTo: String? + get() = classId?.let { "super<${it.asSingleFqName().asString()}>" } + + override val shortenReferencesInReplaced: Boolean + get() = classId != null +} +