diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt index 4f72868147e..8dfb013d20a 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt @@ -95,8 +95,8 @@ public class KotlinCompletionContributor : CompletionContributor() { // search expression to be replaced - go up while we are the first child of parent expression var expression = parent as JetExpression parent = expression.getParent() - while (parent is JetExpression && parent!!.getFirstChild() == expression) { - expression = parent as JetExpression + while (parent is JetExpression && parent.getFirstChild() == expression) { + expression = parent parent = expression.getParent() } @@ -180,7 +180,7 @@ public class KotlinCompletionContributor : CompletionContributor() { val text = builder.toString() val file = JetPsiFactory(tokenBefore.getProject()).createFile(text) val declaration = file.getDeclarations().singleOrNull() ?: return null - if (declaration.getTextLength() != text.length) return null + if (declaration.getTextLength() != text.length()) return null val containsErrorElement = !PsiTreeUtil.processElements(file, PsiElementProcessor{ it !is PsiErrorElement }) return if (containsErrorElement) null else tail + "$" } @@ -384,7 +384,7 @@ public class KotlinCompletionContributor : CompletionContributor() { JetTokens.LPAR -> balance++ JetTokens.RPAR -> balance-- } - current = current!!.prevLeaf() + current = current.prevLeaf() } return balance } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinCallableInsertHandler.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinCallableInsertHandler.kt index fcb8721f67f..627f1e3b4d8 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinCallableInsertHandler.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinCallableInsertHandler.kt @@ -20,7 +20,6 @@ import com.intellij.codeInsight.AutoPopupController import com.intellij.codeInsight.completion.InsertionContext import com.intellij.codeInsight.lookup.Lookup import com.intellij.codeInsight.lookup.LookupElement -import com.intellij.openapi.editor.Document import com.intellij.openapi.project.Project import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiDocumentManager @@ -52,11 +51,6 @@ public abstract class KotlinCallableInsertHandler : BaseDeclarationInsertHandler private fun addImport(context : InsertionContext, item : LookupElement) { PsiDocumentManager.getInstance(context.getProject()).commitAllDocuments() - val startOffset = context.getStartOffset() - val element = context.getFile().findElementAt(startOffset) - - if (element == null) return - val file = context.getFile() val o = item.getObject() if (file is JetFile && o is DeclarationDescriptorLookupObject) { @@ -77,7 +71,30 @@ public abstract class KotlinCallableInsertHandler : BaseDeclarationInsertHandler } } -public object KotlinPropertyInsertHandler : KotlinCallableInsertHandler() +public object KotlinPropertyInsertHandler : KotlinCallableInsertHandler() { + public override fun handleInsert(context: InsertionContext, item: LookupElement) { + super.handleInsert(context, item) + + if (context.getCompletionChar() == Lookup.REPLACE_SELECT_CHAR) { + deleteEmptyParenthesis(context) + } + } + + private fun deleteEmptyParenthesis(context: InsertionContext) { + val psiDocumentManager = PsiDocumentManager.getInstance(context.getProject()) + psiDocumentManager.commitAllDocuments() + psiDocumentManager.doPostponedOperationsAndUnblockDocument(context.getDocument()) + + val offset = context.getTailOffset() + val document = context.getDocument() + val chars = document.getCharsSequence() + + val lParenOffset = chars.indexOfSkippingSpace('(', offset) ?: return + val rParenOffset = chars.indexOfSkippingSpace(')', lParenOffset + 1) ?: return + + document.deleteString(offset, rParenOffset + 1) + } +} public enum class CaretPosition { IN_BRACKETS @@ -143,8 +160,8 @@ public class KotlinFunctionInsertHandler(val caretPosition : CaretPosition, val val closingBracket = if (braces) '}' else ')' if (completionChar == Lookup.REPLACE_SELECT_CHAR) { - val offset1 = skipSpaces(chars, offset) - if (offset1 < document.getTextLength()) { + val offset1 = chars.skipSpaces(offset) + if (offset1 < chars.length()) { if (chars[offset1] == '<') { PsiDocumentManager.getInstance(context.getProject()).commitDocument(document) val token = context.getFile().findElementAt(offset1)!! @@ -158,9 +175,9 @@ public class KotlinFunctionInsertHandler(val caretPosition : CaretPosition, val } } - var openingBracketOffset = indexOfSkippingSpace(document, openingBracket, offset) + var openingBracketOffset = chars.indexOfSkippingSpace(openingBracket, offset) var inBracketsShift = 0 - if (openingBracketOffset == -1) { + if (openingBracketOffset == null) { if (braces) { if (completionChar == ' ' || completionChar == '{') { context.setAddCompletionChar(false) @@ -180,13 +197,12 @@ public class KotlinFunctionInsertHandler(val caretPosition : CaretPosition, val PsiDocumentManager.getInstance(context.getProject()).commitDocument(document) } - openingBracketOffset = indexOfSkippingSpace(document, openingBracket, offset) - assert(openingBracketOffset != -1, "If there wasn't open bracket it should already have been inserted") + openingBracketOffset = chars.indexOfSkippingSpace(openingBracket, offset)!! + + val closeBracketOffset = chars.indexOfSkippingSpace(closingBracket, openingBracketOffset + 1) - val closeBracketOffset = indexOfSkippingSpace(document, closingBracket, openingBracketOffset + 1) val editor = context.getEditor() - - if (shouldPlaceCaretInBrackets(completionChar) || closeBracketOffset == -1) { + if (shouldPlaceCaretInBrackets(completionChar) || closeBracketOffset == null) { editor.getCaretModel().moveToOffset(openingBracketOffset + 1 + inBracketsShift) AutoPopupController.getInstance(context.getProject())?.autoPopupParameterInfo(editor, offsetElement) } @@ -197,7 +213,7 @@ public class KotlinFunctionInsertHandler(val caretPosition : CaretPosition, val PsiDocumentManager.getInstance(context.getProject()).commitDocument(document) if (lambdaInfo != null && lambdaInfo.explicitParameters) { - insertLambdaTemplate(context, TextRange(openingBracketOffset, closeBracketOffset + 1), lambdaInfo.lambdaType) + insertLambdaTemplate(context, TextRange(openingBracketOffset, closeBracketOffset!! + 1), lambdaInfo.lambdaType) } } @@ -211,22 +227,8 @@ public class KotlinFunctionInsertHandler(val caretPosition : CaretPosition, val public val NO_PARAMETERS_HANDLER: KotlinFunctionInsertHandler = KotlinFunctionInsertHandler(CaretPosition.AFTER_BRACKETS, null) public val WITH_PARAMETERS_HANDLER: KotlinFunctionInsertHandler = KotlinFunctionInsertHandler(CaretPosition.IN_BRACKETS, null) - private fun indexOfSkippingSpace(document: Document, ch : Char, startIndex : Int) : Int { - val text = document.getCharsSequence() - for (i in startIndex..text.length() - 1) { - val currentChar = text[i] - if (ch == currentChar) return i - if (currentChar != ' ' && currentChar != '\t') return -1 - } - return -1 - } - - private fun skipSpaces(chars: CharSequence, index : Int) : Int - = (index..chars.length() - 1).firstOrNull { val c = chars[it]; c != ' ' && c != '\t' } ?: chars.length() - - private fun isInsertSpacesInOneLineFunctionEnabled(project : Project) - = CodeStyleSettingsManager.getSettings(project) - .getCustomSettings(javaClass())!!.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD + private fun isInsertSpacesInOneLineFunctionEnabled(project: Project) + = CodeStyleSettingsManager.getSettings(project).getCustomSettings(javaClass())!!.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/handlerUtils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/handlerUtils.kt new file mode 100644 index 00000000000..6c61225f2b0 --- /dev/null +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/handlerUtils.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2015 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.completion.handlers + +fun CharSequence.indexOfSkippingSpace(c: Char, startIndex: Int): Int? { + for (i in startIndex..this.length() - 1) { + val currentChar = this[i] + if (c == currentChar) return i + if (currentChar != ' ' && currentChar != '\t') return null + } + return null +} + +fun CharSequence.skipSpaces(index: Int): Int + = (index..length() - 1).firstOrNull { val c = this[it]; c != ' ' && c != '\t' } ?: this.length() + diff --git a/idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByProperty.kt b/idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByProperty.kt new file mode 100644 index 00000000000..65642f61de2 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByProperty.kt @@ -0,0 +1,10 @@ +class C { + val bar: Int +} + +fun foo(c: C) { + val v = c.getBar() +} + +// ELEMENT: bar +// CHAR: '\t' diff --git a/idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByProperty.kt.after b/idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByProperty.kt.after new file mode 100644 index 00000000000..557c72ef7c8 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByProperty.kt.after @@ -0,0 +1,10 @@ +class C { + val bar: Int +} + +fun foo(c: C) { + val v = c.bar +} + +// ELEMENT: bar +// CHAR: '\t' diff --git a/idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByPropertyArgs.kt b/idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByPropertyArgs.kt new file mode 100644 index 00000000000..b3a831fea8e --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByPropertyArgs.kt @@ -0,0 +1,10 @@ +class C { + val bar: Int +} + +fun foo(c: C) { + val v = c.getBar(0) +} + +// ELEMENT: bar +// CHAR: '\t' diff --git a/idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByPropertyArgs.kt.after b/idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByPropertyArgs.kt.after new file mode 100644 index 00000000000..d91c8379574 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByPropertyArgs.kt.after @@ -0,0 +1,10 @@ +class C { + val bar: Int +} + +fun foo(c: C) { + val v = c.bar(0) +} + +// ELEMENT: bar +// CHAR: '\t' diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java index 417e4b4c482..d605f9b52f2 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java @@ -101,6 +101,18 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion doTest(fileName); } + @TestMetadata("ReplaceFunctionCallByProperty.kt") + public void testReplaceFunctionCallByProperty() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByProperty.kt"); + doTest(fileName); + } + + @TestMetadata("ReplaceFunctionCallByPropertyArgs.kt") + public void testReplaceFunctionCallByPropertyArgs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/ReplaceFunctionCallByPropertyArgs.kt"); + doTest(fileName); + } + @TestMetadata("SecondTypeArg.kt") public void testSecondTypeArg() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/SecondTypeArg.kt");