diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/jet/lang/psi/psiUtil/jetPsiUtil.kt index e3ebb4b7c27..df0aa1bb938 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/psiUtil/jetPsiUtil.kt @@ -35,6 +35,8 @@ import com.intellij.psi.PsiDirectory import org.jetbrains.jet.lang.psi.stubs.PsiJetClassOrObjectStub import org.jetbrains.jet.lang.types.expressions.OperatorConventions import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils +import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.PsiComment public fun JetCallElement.getCallNameExpression(): JetSimpleNameExpression? { val calleeExpression = getCalleeExpression() @@ -357,3 +359,20 @@ public fun PsiElement.prevLeaf(skipEmptyElements: Boolean = false): PsiElement? public fun PsiElement.nextLeaf(skipEmptyElements: Boolean = false): PsiElement? = PsiTreeUtil.nextLeaf(this, skipEmptyElements) + +public fun PsiElement.prevLeafSkipWhitespacesAndComments(): PsiElement? { + var leaf = prevLeaf() + while (leaf is PsiWhiteSpace || leaf is PsiComment) { + leaf = leaf!!.prevLeaf() + } + return leaf +} + +public fun PsiElement.nextLeafSkipWhitespacesAndComments(): PsiElement? { + var leaf = nextLeaf() + while (leaf is PsiWhiteSpace || leaf is PsiComment) { + leaf = leaf!!.nextLeaf() + } + return leaf +} + diff --git a/idea/src/org/jetbrains/jet/plugin/completion/KeywordCompletion.kt b/idea/src/org/jetbrains/jet/plugin/completion/KeywordCompletion.kt index 9029841811a..9c135c50924 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/KeywordCompletion.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/KeywordCompletion.kt @@ -28,14 +28,11 @@ import com.intellij.psi.PsiErrorElement import org.jetbrains.jet.lexer.JetKeywordToken import com.intellij.openapi.project.Project import org.jetbrains.jet.lang.psi.psiUtil.getParentByType -import org.jetbrains.jet.lang.psi.psiUtil.prevLeaf import org.jetbrains.jet.plugin.completion.handlers.KotlinKeywordInsertHandler import org.jetbrains.jet.lang.psi.psiUtil.siblings -import com.intellij.psi.PsiWhiteSpace -import com.intellij.psi.PsiComment import org.jetbrains.jet.lexer.JetTokens.* -import com.intellij.openapi.util.text.StringUtil +import org.jetbrains.jet.lang.psi.psiUtil.prevLeafSkipWhitespacesAndComments class KeywordLookupObject(val keyword: String) @@ -174,14 +171,6 @@ object KeywordCompletion { } } - private fun PsiElement.prevLeafSkipWhitespacesAndComments(): PsiElement? { - var leaf = prevLeaf() - while (leaf is PsiWhiteSpace || leaf is PsiComment) { - leaf = leaf!!.prevLeaf() - } - return leaf - } - // builds text within scope (or from the start of the file) before position element excluding almost all declarations private fun buildReducedContextBefore(builder: StringBuilder, position: PsiElement, scope: PsiElement?) { if (position == scope) return diff --git a/idea/src/org/jetbrains/jet/plugin/completion/KotlinCompletionContributor.kt b/idea/src/org/jetbrains/jet/plugin/completion/KotlinCompletionContributor.kt index 358612fe10a..228245f0336 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/KotlinCompletionContributor.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/KotlinCompletionContributor.kt @@ -43,6 +43,10 @@ import org.jetbrains.jet.lang.psi.JetPsiFactory import com.intellij.psi.PsiErrorElement import com.intellij.psi.search.PsiElementProcessor import com.intellij.psi.PsiComment +import org.jetbrains.jet.lang.psi.psiUtil.parents +import org.jetbrains.jet.lang.psi.JetParameterList +import org.jetbrains.jet.lang.psi.JetFunctionLiteral +import org.jetbrains.jet.lang.psi.psiUtil.prevLeafSkipWhitespacesAndComments import org.jetbrains.jet.lang.psi.JetValueArgument import org.jetbrains.jet.lang.psi.JetValueArgumentList @@ -75,6 +79,8 @@ public class KotlinCompletionContributor : CompletionContributor() { PackageDirectiveCompletion.ACTIVATION_PATTERN.accepts(tokenBefore) -> PackageDirectiveCompletion.DUMMY_IDENTIFIER + isInFunctionLiteralParameterList(tokenBefore) -> CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED + else -> specialExtensionReceiverDummyIdentifier(tokenBefore) ?: DEFAULT_DUMMY_IDENTIFIER } context.setDummyIdentifier(dummyIdentifier) @@ -119,6 +125,12 @@ public class KotlinCompletionContributor : CompletionContributor() { } } + private fun isInFunctionLiteralParameterList(tokenBefore: PsiElement?): Boolean { + val parameterList = tokenBefore?.parents(false)?.firstOrNull { it is JetParameterList } ?: return false + val parent = parameterList.getParent() + return parent is JetFunctionLiteral && parent.getValueParameterList() == parameterList + } + private val declarationKeywords = TokenSet.create(JetTokens.FUN_KEYWORD, JetTokens.VAL_KEYWORD, JetTokens.VAR_KEYWORD) private val declarationTokens = TokenSet.orSet(TokenSet.create(JetTokens.IDENTIFIER, JetTokens.LT, JetTokens.GT, JetTokens.COMMA, JetTokens.DOT, JetTokens.QUEST, JetTokens.COLON, @@ -214,6 +226,9 @@ public class KotlinCompletionContributor : CompletionContributor() { // no auto-popup on typing after "val", "var" and "fun" because it's likely the name of the declaration which is being typed by user if (invocationCount == 0 && isInExtensionReceiver(position)) return true + // no auto-popup on typing in the very beginning of function literal where name of the first parameter can be + if (invocationCount == 0 && isInFunctionLiteralStart(position)) return true + return false } @@ -230,6 +245,16 @@ public class KotlinCompletionContributor : CompletionContributor() { } } + private fun isInFunctionLiteralStart(position: PsiElement): Boolean { + var prev = position.prevLeafSkipWhitespacesAndComments() + if (prev?.getNode()?.getElementType() == JetTokens.LPAR) { + prev = prev?.prevLeafSkipWhitespacesAndComments() + } + if (prev?.getNode()?.getElementType() != JetTokens.LBRACE) return false + val functionLiteral = prev!!.getParent() as? JetFunctionLiteral ?: return false + return functionLiteral.getOpenBraceNode().getPsi() == prev + } + private fun isAtEndOfLine(offset: Int, document: Document): Boolean { var i = offset val chars = document.getCharsSequence() diff --git a/idea/testData/completion/basic/common/FunctionLiteralParameterName0.kt b/idea/testData/completion/basic/common/FunctionLiteralParameterName0.kt new file mode 100644 index 00000000000..b2cb76d672f --- /dev/null +++ b/idea/testData/completion/basic/common/FunctionLiteralParameterName0.kt @@ -0,0 +1,6 @@ +fun bar() { + val handler = { } +} + +// INVOCATION_COUNT: 0 +// NUMBER: 0 diff --git a/idea/testData/completion/basic/common/FunctionLiteralParameterName1.kt b/idea/testData/completion/basic/common/FunctionLiteralParameterName1.kt new file mode 100644 index 00000000000..c1a20dbff97 --- /dev/null +++ b/idea/testData/completion/basic/common/FunctionLiteralParameterName1.kt @@ -0,0 +1,6 @@ +fun bar() { + val handler = { () } +} + +// INVOCATION_COUNT: 0 +// NUMBER: 0 diff --git a/idea/testData/completion/basic/common/FunctionLiteralParameterName2.kt b/idea/testData/completion/basic/common/FunctionLiteralParameterName2.kt new file mode 100644 index 00000000000..55db1b4c8ab --- /dev/null +++ b/idea/testData/completion/basic/common/FunctionLiteralParameterName2.kt @@ -0,0 +1,5 @@ +fun bar() { + val handler = { (i: Int, ) } +} + +// NUMBER: 0 diff --git a/idea/testData/completion/basic/common/FunctionLiteralParameterName3.kt b/idea/testData/completion/basic/common/FunctionLiteralParameterName3.kt new file mode 100644 index 00000000000..8bc5663f4c1 --- /dev/null +++ b/idea/testData/completion/basic/common/FunctionLiteralParameterName3.kt @@ -0,0 +1,5 @@ +fun bar() { + val handler = { (i, ) } +} + +// NUMBER: 0 diff --git a/idea/testData/completion/basic/common/FunctionLiteralParameterName4.kt b/idea/testData/completion/basic/common/FunctionLiteralParameterName4.kt new file mode 100644 index 00000000000..260f160dd77 --- /dev/null +++ b/idea/testData/completion/basic/common/FunctionLiteralParameterName4.kt @@ -0,0 +1,5 @@ +fun bar() { + val handler = { i, } +} + +// NUMBER: 0 diff --git a/idea/testData/completion/basic/common/FunctionLiteralParameterName5.kt b/idea/testData/completion/basic/common/FunctionLiteralParameterName5.kt new file mode 100644 index 00000000000..9dd05b3784f --- /dev/null +++ b/idea/testData/completion/basic/common/FunctionLiteralParameterName5.kt @@ -0,0 +1,8 @@ +fun bar() { + val handler = { + foo() + } +} + +// INVOCATION_COUNT: 0 +// NUMBER: 0 diff --git a/idea/testData/completion/basic/common/FunctionLiteralParameterType1.kt b/idea/testData/completion/basic/common/FunctionLiteralParameterType1.kt new file mode 100644 index 00000000000..de166f1fe4a --- /dev/null +++ b/idea/testData/completion/basic/common/FunctionLiteralParameterType1.kt @@ -0,0 +1,8 @@ +fun bar() { + val handler = { (i: ) } +} + +// EXIST: Int +// EXIST: String +// ABSENT: bar +// ABSENT: handler \ No newline at end of file diff --git a/idea/testData/completion/basic/common/FunctionLiteralParameterType2.kt b/idea/testData/completion/basic/common/FunctionLiteralParameterType2.kt new file mode 100644 index 00000000000..fc207cd39db --- /dev/null +++ b/idea/testData/completion/basic/common/FunctionLiteralParameterType2.kt @@ -0,0 +1,8 @@ +fun bar() { + val handler = { (i: } +} + +// EXIST: Int +// EXIST: String +// ABSENT: bar +// ABSENT: handler \ No newline at end of file diff --git a/idea/testData/completion/basic/common/FunctionLiteralParameterType3.kt b/idea/testData/completion/basic/common/FunctionLiteralParameterType3.kt new file mode 100644 index 00000000000..17af74fc398 --- /dev/null +++ b/idea/testData/completion/basic/common/FunctionLiteralParameterType3.kt @@ -0,0 +1,8 @@ +fun bar() { + val handler = { (i: Int, s: } +} + +// EXIST: Int +// EXIST: String +// ABSENT: bar +// ABSENT: handler \ No newline at end of file diff --git a/idea/testData/completion/basic/common/FunctionLiteralParameterType4.kt b/idea/testData/completion/basic/common/FunctionLiteralParameterType4.kt new file mode 100644 index 00000000000..9829505c48a --- /dev/null +++ b/idea/testData/completion/basic/common/FunctionLiteralParameterType4.kt @@ -0,0 +1,8 @@ +fun bar() { + val handler = { (i: List<>) } +} + +// EXIST: Int +// EXIST: String +// ABSENT: bar +// ABSENT: handler \ No newline at end of file diff --git a/idea/testData/completion/basic/common/FunctionLiteralParameterType5.kt b/idea/testData/completion/basic/common/FunctionLiteralParameterType5.kt new file mode 100644 index 00000000000..d9199af455b --- /dev/null +++ b/idea/testData/completion/basic/common/FunctionLiteralParameterType5.kt @@ -0,0 +1,8 @@ +fun bar() { + val handler = { (i: Map } +} + +// EXIST: Int +// EXIST: String +// ABSENT: bar +// ABSENT: handler \ No newline at end of file