KT-7731 Completion: replacement of call with no parameters with property by Tab

#KT-7731 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-05-14 19:26:34 +03:00
parent 8ea0441348
commit 858adfb2db
8 changed files with 121 additions and 37 deletions
@@ -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<PsiElement>{ 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
}
@@ -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<JetCodeStyleSettings>())!!.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD
private fun isInsertSpacesInOneLineFunctionEnabled(project: Project)
= CodeStyleSettingsManager.getSettings(project).getCustomSettings(javaClass<JetCodeStyleSettings>())!!.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD
}
}
@@ -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()
@@ -0,0 +1,10 @@
class C {
val bar: Int
}
fun foo(c: C) {
val v = c.<caret>getBar()
}
// ELEMENT: bar
// CHAR: '\t'
@@ -0,0 +1,10 @@
class C {
val bar: Int
}
fun foo(c: C) {
val v = c.bar<caret>
}
// ELEMENT: bar
// CHAR: '\t'
@@ -0,0 +1,10 @@
class C {
val bar: Int
}
fun foo(c: C) {
val v = c.<caret>getBar(0)
}
// ELEMENT: bar
// CHAR: '\t'
@@ -0,0 +1,10 @@
class C {
val bar: Int
}
fun foo(c: C) {
val v = c.bar<caret>(0)
}
// ELEMENT: bar
// CHAR: '\t'
@@ -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");