11package org .jetbrains .plugins .scala .lang .completion
22
3- import com .intellij .codeInsight .completion .{CompletionContributor , CompletionParameters , CompletionType , InsertHandler , InsertionContext }
3+ import com .intellij .codeInsight .completion .{CompletionContributor , CompletionParameters , CompletionType , CompletionUtilCore , InsertHandler , InsertionContext }
44import com .intellij .codeInsight .lookup .{LookupElement , LookupElementBuilder }
55import com .intellij .codeInsight .template .TemplateBuilderImpl
66import com .intellij .openapi .util .TextRange
@@ -11,14 +11,16 @@ import com.intellij.util.ProcessingContext
1111import org .jetbrains .plugins .scala .extensions .{PsiElementExt , ToNullSafe }
1212import org .jetbrains .plugins .scala .lang .completion .ScalaNamedTupleCompletionContributor .ScalaNamedTupleCompletionProvider
1313import org .jetbrains .plugins .scala .lang .psi .api .expr .{ScExpression , ScNamedTuple , ScNamedTupleExprComponent , ScParenthesisedExpr , ScReferenceExpression }
14+ import org .jetbrains .plugins .scala .lang .psi .types .ScType
1415import org .jetbrains .plugins .scala .lang .psi .types .api .NamedTupleType
1516
1617import scala .annotation .tailrec
18+ import scala .collection .mutable
1719
1820class ScalaNamedTupleCompletionContributor extends CompletionContributor {
1921 extend(
2022 CompletionType .BASIC ,
21- identifierWithParentsPattern(classOf [ScReferenceExpression ], classOf [ ScNamedTupleExprComponent ], classOf [ScNamedTuple ]),
23+ identifierWithParentsPattern(classOf [ScNamedTupleExprComponent ], classOf [ScNamedTuple ]),
2224 ScalaNamedTupleCompletionProvider ,
2325 )
2426
@@ -33,42 +35,55 @@ object ScalaNamedTupleCompletionContributor {
3335 object ScalaNamedTupleCompletionProvider extends ScalaCompletionProvider {
3436 override protected def completionsFor (position : PsiElement )
3537 (implicit parameters : CompletionParameters , context : ProcessingContext ): Iterable [LookupElement ] = {
36- val refParent = getContextOfType(position, classOf [ScReferenceExpression ])
37- .nullSafe
38- .map(_.getParent)
39- .orNull
40- refParent match {
41- case expr : ScParenthesisedExpr => createCompletionFor(expr, Map .empty)
42- case comp : ScNamedTupleExprComponent =>
38+ val identifierParent = getContextOfType(position, classOf [ScParenthesisedExpr ], classOf [ScNamedTupleExprComponent ])
39+ identifierParent match {
40+ case expr : ScParenthesisedExpr => createCompletionFor(expr, Seq .empty)
41+ case comp : ScNamedTupleExprComponent if position == comp.nameId =>
4342 val namedTuple = comp.namedTuple
4443 val existingComponents =
4544 namedTuple
4645 .components
4746 .flatMap { comp =>
4847 comp.nameElement.zip(comp.expr)
49- .map { case (name, expr) => name.getText -> expr.getText }
50- }.toMap
48+ .map { case (name, expr) => name.getText.replace( CompletionUtilCore . DUMMY_IDENTIFIER_TRIMMED , " " ) -> expr.getText }
49+ }
5150
5251 createCompletionFor(namedTuple, existingComponents)
5352 case _ =>
5453 Seq .empty
5554 }
5655 }
5756
58- private def createCompletionFor (expr : ScExpression , existingComponents : Map [String , String ]): Seq [LookupElement ] = {
57+ private def createCompletionFor (expr : ScExpression , existingComponents : Seq [(String , String )]): Seq [LookupElement ] = {
58+ val existingComponentsMap = existingComponents.toMap
59+ def hasNewComponent (components : Seq [(ScType , _)]): Boolean =
60+ components.size > existingComponents.size || components.exists {
61+ case (NamedTupleType .NameType (name), _) => ! existingComponentsMap.contains(name)
62+ case _ => false
63+ }
64+
5965 expr.expectedType() match {
60- case Some (NamedTupleType (components)) =>
66+ case Some (NamedTupleType (components)) if hasNewComponent(components) =>
6167 val elements =
6268 components.map {
6369 case (NamedTupleType .NameType (name), _) => name // s"$name = " + existingComponents.getOrElse(name, s"???")
6470 case _ => " ???"
6571 }
6672
6773 val presentation = elements
68- .filterNot(existingComponents .contains)
74+ .filterNot(existingComponentsMap .contains)
6975 .mkString(" " , " , " , " =" )
76+
77+ // the identifier that we currently try to complete is of course not in components
78+ // so just put down unmatched components in order
79+ val unmatchedExpressions = existingComponents
80+ .iterator
81+ .collect { case (name, expr) if ! elements.contains(name) => expr }
82+ .to(mutable.Queue )
83+ def nextUnmatchedExpression =
84+ if (unmatchedExpressions.isEmpty) " ???" else unmatchedExpressions.dequeue()
7085 val text = elements
71- .map(name => s " $name = ${existingComponents .getOrElse(name, " ??? " )}" )
86+ .map(name => s " $name = ${existingComponentsMap .getOrElse(name, nextUnmatchedExpression )}" )
7287 .mkString(" , " )
7388 Seq (
7489 LookupElementBuilder
0 commit comments