Fixing code completion in function literal parameters
#KT-4047 Fixed #KT-5877 Fixed
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user