KT-9661 Code completion of "get" and "set" in property accessors

#KT-9661 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-10-21 19:15:28 +03:00
parent 798223b0e4
commit 6eba45febd
20 changed files with 122 additions and 17 deletions
@@ -35,15 +35,14 @@ import org.jetbrains.kotlin.idea.completion.smart.SMART_COMPLETION_ITEM_PRIORITY
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletion import org.jetbrains.kotlin.idea.completion.smart.SmartCompletion
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletionItemPriority import org.jetbrains.kotlin.idea.completion.smart.SmartCompletionItemPriority
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
import org.jetbrains.kotlin.idea.quickfix.moveCaret
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
@@ -266,6 +265,7 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
KeywordValues.process(keywordValueConsumer, callTypeAndReceiver, bindingContext, resolutionFacade, moduleDescriptor, isJvmModule) KeywordValues.process(keywordValueConsumer, callTypeAndReceiver, bindingContext, resolutionFacade, moduleDescriptor, isJvmModule)
val keywordsPrefix = prefix.substringBefore('@') // if there is '@' in the prefix - use shorter prefix to not loose 'this' etc val keywordsPrefix = prefix.substringBefore('@') // if there is '@' in the prefix - use shorter prefix to not loose 'this' etc
val isUseSiteAnnotationTarget = position.prevLeaf()?.node?.elementType == KtTokens.AT
KeywordCompletion.complete(expression ?: parameters.getPosition(), keywordsPrefix, isJvmModule) { lookupElement -> KeywordCompletion.complete(expression ?: parameters.getPosition(), keywordsPrefix, isJvmModule) { lookupElement ->
val keyword = lookupElement.lookupString val keyword = lookupElement.lookupString
if (keyword in keywordsToSkip) return@complete if (keyword in keywordsToSkip) return@complete
@@ -309,6 +309,20 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
"get" -> { "get" -> {
collector.addElement(lookupElement) collector.addElement(lookupElement)
if (!isUseSiteAnnotationTarget) { //TODO: use code style settings
collector.addElement(createKeywordWithSuffixLookupElement(keyword, "() = ", ""))
collector.addElement(createKeywordWithSuffixLookupElement(keyword, "() {", "}"))
}
}
"set" -> {
collector.addElement(lookupElement)
if (!isUseSiteAnnotationTarget) { //TODO: use code style settings
collector.addElement(createKeywordWithSuffixLookupElement(keyword, "(value) {", "}"))
collector.addElement(createKeywordWithSuffixLookupElement(keyword, "(value) = ", ""))
}
} }
else -> collector.addElement(lookupElement) else -> collector.addElement(lookupElement)
@@ -316,6 +330,16 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
} }
} }
private fun createKeywordWithSuffixLookupElement(keyword: String, textBeforeCaret: String, textAfterCaret: String): LookupElementBuilder {
return LookupElementBuilder.create(KeywordLookupObject(), keyword + textBeforeCaret + textAfterCaret)
.withPresentableText(keyword)
.bold()
.withTailText(textBeforeCaret + textAfterCaret)
.withInsertHandler { insertionContext, lookupElement ->
insertionContext.editor.moveCaret(insertionContext.editor.caretModel.offset - textAfterCaret.length)
}
}
private fun completeNonImported() { private fun completeNonImported() {
if (completionKind == CompletionKind.ALL) { if (completionKind == CompletionKind.ALL) {
collector.addDescriptorElements(getTopLevelExtensions(), notImported = true) collector.addDescriptorElements(getTopLevelExtensions(), notImported = true)
@@ -39,7 +39,9 @@ object KotlinKeywordInsertHandler : InsertHandler<LookupElement> {
FILE_KEYWORD, FILE_KEYWORD,
CATCH_KEYWORD, CATCH_KEYWORD,
FINALLY_KEYWORD, FINALLY_KEYWORD,
DYNAMIC_KEYWORD).map { it.getValue() } + "companion object" DYNAMIC_KEYWORD,
GET_KEYWORD,
SET_KEYWORD).map { it.value } + "companion object"
override fun handleInsert(context: InsertionContext, item: LookupElement) { override fun handleInsert(context: InsertionContext, item: LookupElement) {
val keyword = item.lookupString val keyword = item.lookupString
@@ -1,4 +1,4 @@
package propertiesSetter package propertiesGetter
class TestClass { class TestClass {
val a : Int val a : Int
@@ -0,0 +1,6 @@
package propertiesGetter
class TestClass {
val a : Int
get<caret>
}
@@ -1,6 +0,0 @@
package propertiesSetter
class TestClass {
val a : Int
get()<caret>
}
@@ -0,0 +1,4 @@
val v: Int
g<caret>
// ELEMENT: "get() = "
@@ -0,0 +1,4 @@
val v: Int
get() = <caret>
// ELEMENT: "get() = "
@@ -0,0 +1,4 @@
val v: Int
g<caret>
// ELEMENT: "get() {}"
@@ -0,0 +1,4 @@
val v: Int
get() {<caret>}
// ELEMENT: "get() {}"
@@ -0,0 +1,5 @@
var v: Int
get() = 0
s<caret>
// ELEMENT: "set(value) = "
@@ -0,0 +1,5 @@
var v: Int
get() = 0
set(value) = <caret>
// ELEMENT: "set(value) = "
@@ -0,0 +1,5 @@
var v: Int
get() = 0
s<caret>
// ELEMENT: "set(value) {}"
@@ -0,0 +1,5 @@
var v: Int
get() = 0
set(value) {<caret>}
// ELEMENT: "set(value) {}"
@@ -11,6 +11,8 @@ class MouseMovedEventArgs
// EXIST: final // EXIST: final
// EXIST: fun // EXIST: fun
// EXIST: get // EXIST: get
// EXIST: "get() = "
// EXIST: "get() {}"
// EXIST: inner // EXIST: inner
// EXIST: internal // EXIST: internal
// EXIST: object // EXIST: object
@@ -20,6 +22,8 @@ class MouseMovedEventArgs
// EXIST: protected // EXIST: protected
// EXIST: public // EXIST: public
// EXIST: set // EXIST: set
// EXIST: "set(value) = "
// EXIST: "set(value) {}"
// EXIST: interface // EXIST: interface
// EXIST: val // EXIST: val
// EXIST: var // EXIST: var
@@ -16,12 +16,16 @@ var a : Int
// EXIST: final // EXIST: final
// EXIST: fun // EXIST: fun
// EXIST: get // EXIST: get
// EXIST: "get() = "
// EXIST: "get() {}"
// EXIST: internal // EXIST: internal
// EXIST: object // EXIST: object
// EXIST: open // EXIST: open
// EXIST: private // EXIST: private
// EXIST: public // EXIST: public
// EXIST: set // EXIST: set
// EXIST: "set(value) = "
// EXIST: "set(value) {}"
// EXIST: interface // EXIST: interface
// EXIST: val // EXIST: val
// EXIST: var // EXIST: var
@@ -10,6 +10,8 @@ class Some {
// EXIST: final // EXIST: final
// EXIST: fun // EXIST: fun
// EXIST: get // EXIST: get
// EXIST: "get() = "
// EXIST: "get() {}"
// EXIST: inner // EXIST: inner
// EXIST: internal // EXIST: internal
// EXIST: object // EXIST: object
@@ -19,6 +21,8 @@ class Some {
// EXIST: protected // EXIST: protected
// EXIST: public // EXIST: public
// EXIST: set // EXIST: set
// EXIST: "set(value) = "
// EXIST: "set(value) {}"
// EXIST: interface // EXIST: interface
// EXIST: val // EXIST: val
// EXIST: var // EXIST: var
@@ -10,6 +10,8 @@ class Some {
// EXIST: final // EXIST: final
// EXIST: fun // EXIST: fun
// EXIST: get // EXIST: get
// EXIST: "get() = "
// EXIST: "get() {}"
// EXIST: inner // EXIST: inner
// EXIST: internal // EXIST: internal
// EXIST: object // EXIST: object
@@ -19,6 +21,8 @@ class Some {
// EXIST: protected // EXIST: protected
// EXIST: public // EXIST: public
// EXIST: set // EXIST: set
// EXIST: "set(value) = "
// EXIST: "set(value) {}"
// EXIST: interface // EXIST: interface
// EXIST: val // EXIST: val
// EXIST: var // EXIST: var
+7 -1
View File
@@ -10,8 +10,12 @@ class Some {
// EXIST: enum class // EXIST: enum class
// EXIST: final // EXIST: final
// EXIST: fun // EXIST: fun
// EXIST: get
/*TODO*/ /*TODO*/
// EXIST: get
// EXIST: "get() = "
// EXIST: "get() {}"
// EXIST: inner // EXIST: inner
// EXIST: internal // EXIST: internal
// EXIST: object // EXIST: object
@@ -21,6 +25,8 @@ class Some {
// EXIST: protected // EXIST: protected
// EXIST: public // EXIST: public
// EXIST: set // EXIST: set
// EXIST: "set(value) = "
// EXIST: "set(value) {}"
// EXIST: interface // EXIST: interface
// EXIST: val // EXIST: val
// EXIST: var // EXIST: var
@@ -17,11 +17,8 @@
package org.jetbrains.kotlin.completion.handlers package org.jetbrains.kotlin.completion.handlers
import com.intellij.codeInsight.completion.CompletionType import com.intellij.codeInsight.completion.CompletionType
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
import org.jetbrains.kotlin.idea.completion.test.COMPLETION_TEST_DATA_BASE_PATH import org.jetbrains.kotlin.idea.completion.test.COMPLETION_TEST_DATA_BASE_PATH
import org.jetbrains.kotlin.idea.completion.test.handlers.CompletionHandlerTestBase import org.jetbrains.kotlin.idea.completion.test.handlers.CompletionHandlerTestBase
import org.jetbrains.kotlin.idea.core.formatter.JetCodeStyleSettings
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import java.io.File import java.io.File
@Deprecated("All tests from here to be moved to the generated test") @Deprecated("All tests from here to be moved to the generated test")
@@ -77,7 +74,7 @@ public class BasicCompletionHandlerTest : CompletionHandlerTestBase(){
fun testInsertVoidJavaMethod() = doTest() fun testInsertVoidJavaMethod() = doTest()
fun testPropertiesSetter() = doTest() fun testPropertiesGetter() = doTest()
fun testExistingSingleBrackets() = doTest() fun testExistingSingleBrackets() = doTest()
@@ -59,6 +59,18 @@ public class KeywordCompletionHandlerTestGenerated extends AbstractKeywordComple
doTest(fileName); doTest(fileName);
} }
@TestMetadata("Getter1.kt")
public void testGetter1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/Getter1.kt");
doTest(fileName);
}
@TestMetadata("Getter2.kt")
public void testGetter2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/Getter2.kt");
doTest(fileName);
}
@TestMetadata("NoSpaceAfterNull.kt") @TestMetadata("NoSpaceAfterNull.kt")
public void testNoSpaceAfterNull() throws Exception { public void testNoSpaceAfterNull() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/NoSpaceAfterNull.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/NoSpaceAfterNull.kt");
@@ -119,6 +131,18 @@ public class KeywordCompletionHandlerTestGenerated extends AbstractKeywordComple
doTest(fileName); doTest(fileName);
} }
@TestMetadata("Setter1.kt")
public void testSetter1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/Setter1.kt");
doTest(fileName);
}
@TestMetadata("Setter2.kt")
public void testSetter2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/Setter2.kt");
doTest(fileName);
}
@TestMetadata("SpaceAfterImport.kt") @TestMetadata("SpaceAfterImport.kt")
public void testSpaceAfterImport() throws Exception { public void testSpaceAfterImport() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/SpaceAfterImport.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/keywords/SpaceAfterImport.kt");