Fixing code completion in function literal parameters

#KT-4047 Fixed
 #KT-5877 Fixed
This commit is contained in:
Valentin Kipyatkov
2014-10-21 16:52:47 +04:00
parent 59b66e7579
commit 4fd417d419
14 changed files with 120 additions and 12 deletions
@@ -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
}
@@ -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
@@ -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()
@@ -0,0 +1,6 @@
fun bar() {
val handler = { <caret> }
}
// INVOCATION_COUNT: 0
// NUMBER: 0
@@ -0,0 +1,6 @@
fun bar() {
val handler = { (<caret>) }
}
// INVOCATION_COUNT: 0
// NUMBER: 0
@@ -0,0 +1,5 @@
fun bar() {
val handler = { (i: Int, <caret>) }
}
// NUMBER: 0
@@ -0,0 +1,5 @@
fun bar() {
val handler = { (i, <caret>) }
}
// NUMBER: 0
@@ -0,0 +1,5 @@
fun bar() {
val handler = { i, <caret> }
}
// NUMBER: 0
@@ -0,0 +1,8 @@
fun bar() {
val handler = { <caret>
foo()
}
}
// INVOCATION_COUNT: 0
// NUMBER: 0
@@ -0,0 +1,8 @@
fun bar() {
val handler = { (i: <caret>) }
}
// EXIST: Int
// EXIST: String
// ABSENT: bar
// ABSENT: handler
@@ -0,0 +1,8 @@
fun bar() {
val handler = { (i: <caret> }
}
// EXIST: Int
// EXIST: String
// ABSENT: bar
// ABSENT: handler
@@ -0,0 +1,8 @@
fun bar() {
val handler = { (i: Int, s: <caret> }
}
// EXIST: Int
// EXIST: String
// ABSENT: bar
// ABSENT: handler
@@ -0,0 +1,8 @@
fun bar() {
val handler = { (i: List<<caret>>) }
}
// EXIST: Int
// EXIST: String
// ABSENT: bar
// ABSENT: handler
@@ -0,0 +1,8 @@
fun bar() {
val handler = { (i: Map<String, <caret> }
}
// EXIST: Int
// EXIST: String
// ABSENT: bar
// ABSENT: handler