No "{...}" in presentation for functions in completion of imports and callable references

This commit is contained in:
Valentin Kipyatkov
2015-10-01 21:14:04 +03:00
parent 08335a2ac9
commit e562c019f9
9 changed files with 192 additions and 166 deletions
@@ -151,7 +151,7 @@ class BasicLookupElementFactory(
val returnType = descriptor.getReturnType()
element = element.withTypeText(if (returnType != null) DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(returnType) else "")
val insertsLambda = (insertHandler as KotlinFunctionInsertHandler).lambdaInfo != null
val insertsLambda = (insertHandler as? KotlinFunctionInsertHandler.Normal)?.lambdaInfo != null
if (insertsLambda) {
element = element.appendTailText(" {...} ", false)
}
@@ -211,7 +211,7 @@ class BasicLookupElementFactory(
element = element.withStrikeoutness(true)
}
if (insertHandler is KotlinFunctionInsertHandler && insertHandler.lambdaInfo != null) {
if ((insertHandler as? KotlinFunctionInsertHandler.Normal)?.lambdaInfo != null) {
element.putUserData(KotlinCompletionCharFilter.ACCEPT_OPENING_BRACE, Unit)
}
@@ -39,25 +39,34 @@ class InsertHandlerProvider(
return when (descriptor) {
is FunctionDescriptor -> {
val needTypeArguments = needTypeArguments(descriptor)
val parameters = descriptor.valueParameters
when (parameters.size()) {
0 -> KotlinFunctionInsertHandler(callType, needTypeArguments, inputValueArguments = false)
when (callType) {
is CallType.DEFAULT, is CallType.DOT, is CallType.SAFE -> {
val needTypeArguments = needTypeArguments(descriptor)
val parameters = descriptor.valueParameters
when (parameters.size()) {
0 -> KotlinFunctionInsertHandler.Normal(needTypeArguments, inputValueArguments = false)
1 -> {
val parameterType = parameters.single().getType()
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) {
val parameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(parameterType).size()
if (parameterCount <= 1) {
// otherwise additional item with lambda template is to be added
return KotlinFunctionInsertHandler(callType, needTypeArguments, inputValueArguments = false, lambdaInfo = GenerateLambdaInfo(parameterType, false))
1 -> {
val parameterType = parameters.single().getType()
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) {
val parameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(parameterType).size()
if (parameterCount <= 1) {
// otherwise additional item with lambda template is to be added
return KotlinFunctionInsertHandler.Normal(needTypeArguments, inputValueArguments = false, lambdaInfo = GenerateLambdaInfo(parameterType, false))
}
}
KotlinFunctionInsertHandler.Normal(needTypeArguments, inputValueArguments = true)
}
else -> KotlinFunctionInsertHandler.Normal(needTypeArguments, inputValueArguments = true)
}
KotlinFunctionInsertHandler(callType, needTypeArguments, inputValueArguments = true)
}
else -> KotlinFunctionInsertHandler(callType, needTypeArguments, inputValueArguments = true)
is CallType.INFIX -> KotlinFunctionInsertHandler.Infix
else -> KotlinFunctionInsertHandler.OnlyName
}
}
is PropertyDescriptor -> KotlinPropertyInsertHandler
@@ -29,7 +29,6 @@ import com.intellij.psi.filters.position.PositionElementFilter
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler
import org.jetbrains.kotlin.idea.completion.handlers.KotlinKeywordInsertHandler
import org.jetbrains.kotlin.idea.util.CallType
import org.jetbrains.kotlin.lexer.JetKeywordToken
import org.jetbrains.kotlin.lexer.JetTokens.*
import org.jetbrains.kotlin.psi.*
@@ -77,7 +76,7 @@ object KeywordCompletion {
.withInsertHandler(if (keywordToken !in FUNCTION_KEYWORDS)
KotlinKeywordInsertHandler
else
KotlinFunctionInsertHandler(CallType.DEFAULT, inputTypeArguments = false, inputValueArguments = false))
KotlinFunctionInsertHandler.Normal(inputTypeArguments = false, inputValueArguments = false))
consumer(element)
}
}
@@ -104,7 +104,7 @@ class LookupElementFactory(
private fun createFunctionCallElementWithLambda(descriptor: FunctionDescriptor, parameterType: JetType, explicitLambdaParameters: Boolean, useReceiverTypes: Boolean): LookupElement {
var lookupElement = createLookupElement(descriptor, useReceiverTypes)
val inputTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler).inputTypeArguments
val inputTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler.Normal).inputTypeArguments
val lambdaInfo = GenerateLambdaInfo(parameterType, explicitLambdaParameters)
val lambdaPresentation = lambdaPresentation(if (explicitLambdaParameters) parameterType else null)
@@ -132,7 +132,7 @@ class LookupElementFactory(
}
override fun handleInsert(context: InsertionContext) {
KotlinFunctionInsertHandler(callType!!, inputTypeArguments, inputValueArguments = false, lambdaInfo = lambdaInfo).handleInsert(context, this)
KotlinFunctionInsertHandler.Normal(inputTypeArguments, inputValueArguments = false, lambdaInfo = lambdaInfo).handleInsert(context, this)
}
}
@@ -146,7 +146,7 @@ class LookupElementFactory(
private fun createFunctionCallElementWithArgument(descriptor: FunctionDescriptor, argumentText: String, useReceiverTypes: Boolean): LookupElement {
var lookupElement = createLookupElement(descriptor, useReceiverTypes)
val needTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler).inputTypeArguments
val needTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler.Normal).inputTypeArguments
lookupElement = FunctionCallWithArgumentLookupElement(lookupElement, descriptor, argumentText, needTypeArguments)
if (isInStringTemplateAfterDollar) {
@@ -175,7 +175,7 @@ class LookupElementFactory(
}
override fun handleInsert(context: InsertionContext) {
KotlinFunctionInsertHandler(callType!!, inputTypeArguments = needTypeArguments, inputValueArguments = false, argumentText = argumentText).handleInsert(context, this)
KotlinFunctionInsertHandler.Normal(inputTypeArguments = needTypeArguments, inputValueArguments = false, argumentText = argumentText).handleInsert(context, this)
}
}
@@ -26,7 +26,6 @@ import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
import org.jetbrains.kotlin.idea.core.formatter.JetCodeStyleSettings
import org.jetbrains.kotlin.idea.util.CallType
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.JetTypeArgumentList
import org.jetbrains.kotlin.psi.psiUtil.endOffset
@@ -34,153 +33,166 @@ import org.jetbrains.kotlin.types.JetType
class GenerateLambdaInfo(val lambdaType: JetType, val explicitParameters: Boolean)
class KotlinFunctionInsertHandler(
val callType: CallType<*>,
val inputTypeArguments: Boolean,
val inputValueArguments: Boolean,
val argumentText: String = "",
val lambdaInfo: GenerateLambdaInfo? = null
sealed class KotlinFunctionInsertHandler(
) : KotlinCallableInsertHandler() {
init {
if (lambdaInfo != null) {
assert(argumentText == "")
class Normal(
val inputTypeArguments: Boolean,
val inputValueArguments: Boolean,
val argumentText: String = "",
val lambdaInfo: GenerateLambdaInfo? = null
) : KotlinFunctionInsertHandler() {
init {
if (lambdaInfo != null) {
assert(argumentText == "")
}
}
public override fun handleInsert(context: InsertionContext, item: LookupElement) {
super.handleInsert(context, item)
val psiDocumentManager = PsiDocumentManager.getInstance(context.project)
psiDocumentManager.commitAllDocuments()
psiDocumentManager.doPostponedOperationsAndUnblockDocument(context.document)
val startOffset = context.getStartOffset()
val element = context.getFile().findElementAt(startOffset) ?: return
addArguments(context, element)
}
private fun addArguments(context : InsertionContext, offsetElement : PsiElement) {
val completionChar = context.getCompletionChar()
if (completionChar == '(') { //TODO: more correct behavior related to braces type
context.setAddCompletionChar(false)
}
var offset = context.tailOffset
val document = context.document
val editor = context.editor
val project = context.project
val chars = document.charsSequence
val insertLambda = lambdaInfo != null && completionChar != '(' && !(completionChar == '\t' && chars.isCharAt(offset, '('))
val openingBracket = if (insertLambda) '{' else '('
val closingBracket = if (insertLambda) '}' else ')'
var insertTypeArguments = inputTypeArguments && (completionChar == '\n' || completionChar == '\r' || completionChar == Lookup.REPLACE_SELECT_CHAR)
if (completionChar == Lookup.REPLACE_SELECT_CHAR) {
val offset1 = chars.skipSpaces(offset)
if (offset1 < chars.length()) {
if (chars[offset1] == '<') {
PsiDocumentManager.getInstance(project).commitDocument(document)
val token = context.getFile().findElementAt(offset1)!!
if (token.getNode().getElementType() == JetTokens.LT) {
val parent = token.getParent()
if (parent is JetTypeArgumentList && parent.getText().indexOf('\n') < 0/* if type argument list is on multiple lines this is more likely wrong parsing*/) {
offset = parent.endOffset
insertTypeArguments = false
}
}
}
}
}
if (insertLambda && lambdaInfo!!.explicitParameters) {
insertTypeArguments = false
}
if (insertTypeArguments) {
document.insertString(offset, "<>")
editor.caretModel.moveToOffset(offset + 1)
offset += 2
}
var openingBracketOffset = chars.indexOfSkippingSpace(openingBracket, offset)
var closeBracketOffset = openingBracketOffset?.let { chars.indexOfSkippingSpace(closingBracket, it + 1) }
var inBracketsShift = 0
if (insertLambda && lambdaInfo!!.explicitParameters && closeBracketOffset == null) {
openingBracketOffset = null
}
if (openingBracketOffset == null) {
if (insertLambda) {
if (completionChar == ' ' || completionChar == '{') {
context.setAddCompletionChar(false)
}
if (isInsertSpacesInOneLineFunctionEnabled(project)) {
document.insertString(offset, " { }")
inBracketsShift = 1
}
else {
document.insertString(offset, " {}")
}
}
else {
document.insertString(offset, "()")
}
PsiDocumentManager.getInstance(project).commitDocument(document)
openingBracketOffset = chars.indexOfSkippingSpace(openingBracket, offset)!!
closeBracketOffset = chars.indexOfSkippingSpace(closingBracket, openingBracketOffset + 1)!!
}
if (insertLambda && lambdaInfo!!.explicitParameters) {
insertLambdaTemplate(context, TextRange(openingBracketOffset, closeBracketOffset!! + 1), lambdaInfo!!.lambdaType)
return
}
document.insertString(openingBracketOffset + 1, argumentText)
if (closeBracketOffset != null) {
closeBracketOffset += argumentText.length()
}
if (!insertTypeArguments) {
if (shouldPlaceCaretInBrackets(completionChar) || closeBracketOffset == null) {
editor.caretModel.moveToOffset(openingBracketOffset + 1 + inBracketsShift)
if (!insertLambda) {
AutoPopupController.getInstance(project)?.autoPopupParameterInfo(editor, offsetElement)
}
}
else {
editor.caretModel.moveToOffset(closeBracketOffset + 1)
}
}
}
private fun shouldPlaceCaretInBrackets(completionChar: Char): Boolean {
if (completionChar == ',' || completionChar == '.' || completionChar == '=') return false
if (completionChar == '(') return true
return inputValueArguments || lambdaInfo != null
}
private fun isInsertSpacesInOneLineFunctionEnabled(project: Project)
= CodeStyleSettingsManager.getSettings(project).getCustomSettings(javaClass<JetCodeStyleSettings>())!!.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD
}
object Infix : KotlinFunctionInsertHandler() {
public override fun handleInsert(context: InsertionContext, item: LookupElement) {
super.handleInsert(context, item)
if (context.completionChar == ' ') {
context.setAddCompletionChar(false)
}
val tailOffset = context.tailOffset
context.document.insertString(tailOffset, " ")
context.editor.caretModel.moveToOffset(tailOffset + 1)
}
}
object OnlyName : KotlinFunctionInsertHandler()
public override fun handleInsert(context: InsertionContext, item: LookupElement) {
super.handleInsert(context, item)
val psiDocumentManager = PsiDocumentManager.getInstance(context.project)
psiDocumentManager.commitAllDocuments()
psiDocumentManager.doPostponedOperationsAndUnblockDocument(context.getDocument())
val startOffset = context.getStartOffset()
val element = context.getFile().findElementAt(startOffset) ?: return
when (callType) {
CallType.PACKAGE_DIRECTIVE, CallType.IMPORT_DIRECTIVE, CallType.CALLABLE_REFERENCE -> return
CallType.INFIX -> {
if (context.getCompletionChar() == ' ') {
context.setAddCompletionChar(false)
}
val tailOffset = context.getTailOffset()
context.getDocument().insertString(tailOffset, " ")
context.getEditor().getCaretModel().moveToOffset(tailOffset + 1)
}
else -> addArguments(context, element)
}
psiDocumentManager.doPostponedOperationsAndUnblockDocument(context.document)
}
private fun addArguments(context : InsertionContext, offsetElement : PsiElement) {
val completionChar = context.getCompletionChar()
if (completionChar == '(') { //TODO: more correct behavior related to braces type
context.setAddCompletionChar(false)
}
var offset = context.tailOffset
val document = context.document
val editor = context.editor
val project = context.project
val chars = document.charsSequence
val insertLambda = lambdaInfo != null && completionChar != '(' && !(completionChar == '\t' && chars.isCharAt(offset, '('))
val openingBracket = if (insertLambda) '{' else '('
val closingBracket = if (insertLambda) '}' else ')'
var insertTypeArguments = inputTypeArguments && (completionChar == '\n' || completionChar == '\r' || completionChar == Lookup.REPLACE_SELECT_CHAR)
if (completionChar == Lookup.REPLACE_SELECT_CHAR) {
val offset1 = chars.skipSpaces(offset)
if (offset1 < chars.length()) {
if (chars[offset1] == '<') {
PsiDocumentManager.getInstance(project).commitDocument(document)
val token = context.getFile().findElementAt(offset1)!!
if (token.getNode().getElementType() == JetTokens.LT) {
val parent = token.getParent()
if (parent is JetTypeArgumentList && parent.getText().indexOf('\n') < 0/* if type argument list is on multiple lines this is more likely wrong parsing*/) {
offset = parent.endOffset
insertTypeArguments = false
}
}
}
}
}
if (insertLambda && lambdaInfo!!.explicitParameters) {
insertTypeArguments = false
}
if (insertTypeArguments) {
document.insertString(offset, "<>")
editor.caretModel.moveToOffset(offset + 1)
offset += 2
}
var openingBracketOffset = chars.indexOfSkippingSpace(openingBracket, offset)
var closeBracketOffset = openingBracketOffset?.let { chars.indexOfSkippingSpace(closingBracket, it + 1) }
var inBracketsShift = 0
if (insertLambda && lambdaInfo!!.explicitParameters && closeBracketOffset == null) {
openingBracketOffset = null
}
if (openingBracketOffset == null) {
if (insertLambda) {
if (completionChar == ' ' || completionChar == '{') {
context.setAddCompletionChar(false)
}
if (isInsertSpacesInOneLineFunctionEnabled(project)) {
document.insertString(offset, " { }")
inBracketsShift = 1
}
else {
document.insertString(offset, " {}")
}
}
else {
document.insertString(offset, "()")
}
PsiDocumentManager.getInstance(project).commitDocument(document)
openingBracketOffset = chars.indexOfSkippingSpace(openingBracket, offset)!!
closeBracketOffset = chars.indexOfSkippingSpace(closingBracket, openingBracketOffset + 1)!!
}
if (insertLambda && lambdaInfo!!.explicitParameters) {
insertLambdaTemplate(context, TextRange(openingBracketOffset, closeBracketOffset!! + 1), lambdaInfo!!.lambdaType)
return
}
document.insertString(openingBracketOffset + 1, argumentText)
if (closeBracketOffset != null) {
closeBracketOffset += argumentText.length()
}
if (!insertTypeArguments) {
if (shouldPlaceCaretInBrackets(completionChar) || closeBracketOffset == null) {
editor.caretModel.moveToOffset(openingBracketOffset + 1 + inBracketsShift)
if (!insertLambda) {
AutoPopupController.getInstance(project)?.autoPopupParameterInfo(editor, offsetElement)
}
}
else {
editor.caretModel.moveToOffset(closeBracketOffset + 1)
}
}
}
private fun shouldPlaceCaretInBrackets(completionChar: Char): Boolean {
if (completionChar == ',' || completionChar == '.' || completionChar == '=') return false
if (completionChar == '(') return true
return inputValueArguments || lambdaInfo != null
}
private fun isInsertSpacesInOneLineFunctionEnabled(project: Project)
= CodeStyleSettingsManager.getSettings(project).getCustomSettings(javaClass<JetCodeStyleSettings>())!!.INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD
}
@@ -34,7 +34,6 @@ import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler
import org.jetbrains.kotlin.idea.core.psiClassToDescriptor
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.CallType
import org.jetbrains.kotlin.idea.util.FuzzyType
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.makeNotNullable
@@ -204,9 +203,9 @@ class TypeInstantiationItems(
}
val baseInsertHandler = when (visibleConstructors.size()) {
0 -> KotlinFunctionInsertHandler(CallType.DEFAULT, inputTypeArguments = false, inputValueArguments = false)
1 -> lookupElementFactory.insertHandlerProvider.insertHandler(visibleConstructors.single()) as KotlinFunctionInsertHandler
else -> KotlinFunctionInsertHandler(CallType.DEFAULT, inputTypeArguments = false, inputValueArguments = true)
0 -> KotlinFunctionInsertHandler.Normal(inputTypeArguments = false, inputValueArguments = false)
1 -> lookupElementFactory.insertHandlerProvider.insertHandler(visibleConstructors.single()) as KotlinFunctionInsertHandler.Normal
else -> KotlinFunctionInsertHandler.Normal(inputTypeArguments = false, inputValueArguments = true)
}
insertHandler = object : InsertHandler<LookupElement> {
@@ -12,6 +12,8 @@ class C {
val memberVal = 1
fun funWithFunctionParameter(p: () -> Unit) {}
class NestedClass
inner class InnerClass
@@ -48,3 +50,4 @@ abstract class AbstractClass
// ABSENT: AbstractClass
// ABSENT: class
// ABSENT: class.java
// EXIST: { itemText: "funWithFunctionParameter", tailText: "(p: () -> Unit)", attributes: "bold" }
@@ -3,5 +3,7 @@ package second
fun String.extensionFun(){}
val Int.extensionVal: Int get() = 1
fun topLevelFun(p: (String, Int) -> Unit){}
fun topLevelFun1(p: (String, Int) -> Unit){}
fun topLevelFun2(p: () -> Unit){}
val topLevelVal: Int = 1
@@ -4,5 +4,7 @@ import second.<caret>
// EXIST: { itemText: "extensionFun", tailText: "() for String in second", attributes: "" }
// EXIST: { itemText: "extensionVal", tailText: " for Int in second", attributes: "" }
// EXIST: { itemText: "topLevelFun", tailText: "(p: (String, Int) -> Unit) (second)", attributes: "" }
// EXIST: { itemText: "topLevelFun1", tailText: "(p: (String, Int) -> Unit) (second)", attributes: "" }
// EXIST: { itemText: "topLevelFun2", tailText: "(p: () -> Unit) (second)", attributes: "" }
// EXIST: { itemText: "topLevelVal", tailText: " (second)", attributes: "" }
// NOTHING_ELSE