Skip to content

Commit 765e26f

Browse files
author
builduser
committed
Merged branch idea251.release into idea251.x
2 parents 3fdbc6e + a61d2ad commit 765e26f

File tree

13 files changed

+436
-37
lines changed

13 files changed

+436
-37
lines changed

scala/scala-impl/resources/messages/ScalaBundle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ export.qualifier.not.parameterless.companion.method=Export qualifier {0} is not
238238

239239
### org/jetbrains/plugins/scala/annotator/element/ScExpressionAnnotator.scala
240240
else.expected='else' expected
241+
expression.expected=Expression expected
241242

242243
### org/jetbrains/plugins/scala/annotator/element/ScForAnnotator.scala
243244
for.pattern.bindings.require.scala3='case' syntax in 'for' pattern bindings requires Scala 3.0
@@ -1357,7 +1358,6 @@ auxiliary.constructor.may.not.have.a.type.annotation=Auxiliary constructor may n
13571358
auxiliary.constructor.definition.expected=Auxiliary constructor definition expected
13581359

13591360
### org/jetbrains/plugins/scala/lang/parser/parsing/statements/PatDef.scala
1360-
expression.expected=Expression expected
13611361
expected.another.pattern=Expected another pattern
13621362

13631363
### org/jetbrains/plugins/scala/lang/parser/parsing/statements/ValDcl.scala

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/completion/ScalaNamedTupleCompletionContributor.scala

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package 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}
44
import com.intellij.codeInsight.lookup.{LookupElement, LookupElementBuilder}
55
import com.intellij.codeInsight.template.TemplateBuilderImpl
66
import com.intellij.openapi.util.TextRange
@@ -11,14 +11,16 @@ import com.intellij.util.ProcessingContext
1111
import org.jetbrains.plugins.scala.extensions.{PsiElementExt, ToNullSafe}
1212
import org.jetbrains.plugins.scala.lang.completion.ScalaNamedTupleCompletionContributor.ScalaNamedTupleCompletionProvider
1313
import org.jetbrains.plugins.scala.lang.psi.api.expr.{ScExpression, ScNamedTuple, ScNamedTupleExprComponent, ScParenthesisedExpr, ScReferenceExpression}
14+
import org.jetbrains.plugins.scala.lang.psi.types.ScType
1415
import org.jetbrains.plugins.scala.lang.psi.types.api.NamedTupleType
1516

1617
import scala.annotation.tailrec
18+
import scala.collection.mutable
1719

1820
class 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

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/parser/parsing/builder/ScalaPsiBuilder.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,8 @@ trait ScalaPsiBuilder extends PsiBuilder {
6363
final def wrongExpressionError(): Unit = {
6464
error(ErrMsg("wrong.expression"))
6565
}
66+
67+
final def expressionExpectedError(): Unit = {
68+
error(ErrMsg("expression.expected"))
69+
}
6670
}

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/parser/parsing/expressions/Enumerator.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ object Enumerator extends ParsingRule {
2121
case ScalaTokenTypes.tASSIGN =>
2222
builder.advanceLexer() //Ate =
2323
if (!ExprInIndentationRegion()) {
24-
builder.wrongExpressionError()
24+
builder.expressionExpectedError()
2525
}
2626
enumeratorMarker.done(ScalaElementType.FOR_BINDING)
2727
true

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/parser/parsing/expressions/Generator.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ object Generator extends ParsingRule {
2727
builder.advanceLexer()
2828

2929
if (!ExprInIndentationRegion()) {
30-
builder.wrongExpressionError()
30+
builder.expressionExpectedError()
3131
}
3232
genMarker.done(ScalaElementType.GENERATOR)
3333
builder.getTokenType match {

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/parser/parsing/expressions/SimpleExpr.scala

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -112,20 +112,29 @@ object SimpleExpr extends ParsingRule {
112112
val namedTupleComponentMarker = builder.mark()
113113
var hasNamedTupleContent = false
114114
if (isNamedTuple) {
115-
if (builder.lookAhead(tIDENTIFIER, tASSIGN)) {
116-
hasNamedTupleContent = true
117-
builder.advanceLexer()
118-
builder.advanceLexer()
119-
} else if (builder.getTokenType == tASSIGN) {
120-
hasNamedTupleContent = true
121-
builder.error(ErrMsg("identifier.expected"))
122-
builder.advanceLexer()
123-
} else {
124-
builder.error(ErrMsg("identifier.expected"))
115+
builder.getTokenType match {
116+
case `tIDENTIFIER` =>
117+
hasNamedTupleContent = true
118+
builder.advanceLexer()
119+
120+
if (builder.getTokenType == tASSIGN) {
121+
builder.advanceLexer()
122+
} else {
123+
builder.error(ScalaBundle.message("assignment.expected"))
124+
}
125+
case token =>
126+
builder.error(ErrMsg("identifier.expected"))
127+
128+
if (token == `tASSIGN`) {
129+
builder.advanceLexer()
130+
}
125131
}
126132
}
127133

128134
val parsedExpr = Expr()
135+
if (!parsedExpr) {
136+
builder.expressionExpectedError()
137+
}
129138

130139
if (isNamedTuple && (hasNamedTupleContent || parsedExpr)) {
131140
namedTupleComponentMarker.done(ScalaElementType.NAMED_TUPLE_COMPONENT)

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/parser/parsing/params/Param.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import org.jetbrains.plugins.scala.lang.lexer.{ScalaTokenType, ScalaTokenTypes}
44
import org.jetbrains.plugins.scala.lang.parser.{ErrMsg, ScalaElementType}
55
import org.jetbrains.plugins.scala.lang.parser.parsing.ParsingRule
66
import org.jetbrains.plugins.scala.lang.parser.parsing.builder.ScalaPsiBuilder
7-
import org.jetbrains.plugins.scala.lang.parser.parsing.expressions.{Annotations, Expr}
7+
import org.jetbrains.plugins.scala.lang.parser.parsing.expressions.{Annotations, Expr, ExprInIndentationRegion}
88
import org.jetbrains.plugins.scala.lang.parser.parsing.types.ParamType
99

1010
/**
@@ -49,7 +49,7 @@ object Param extends ParsingRule {
4949
builder.getTokenType match {
5050
case ScalaTokenTypes.tASSIGN =>
5151
builder.advanceLexer() //Ate =
52-
if (!Expr()) builder error ErrMsg("expression.expected")
52+
if (!ExprInIndentationRegion()) builder error ErrMsg("expression.expected")
5353
case _ =>
5454
}
5555
paramMarker.done(ScalaElementType.PARAM)

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/parser/parsing/statements/FunDef.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ object FunDef extends ParsingRule {
4242
true
4343
}
4444
else {
45-
builder.wrongExpressionError()
45+
builder.expressionExpectedError()
4646
faultMarker.drop()
4747
true
4848
}
@@ -59,7 +59,7 @@ object FunDef extends ParsingRule {
5959
true
6060
}
6161
else {
62-
builder.error(ScalaBundle.message("expression.expected"))
62+
builder.expressionExpectedError()
6363
faultMarker.drop()
6464
true
6565
}

scala/scala-impl/src/org/jetbrains/plugins/scala/lang/parser/parsing/statements/PatDef.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ object PatDef extends ParsingRule {
5353
builder.checkedAdvanceLexer()
5454

5555
if (!ExprInIndentationRegion()) {
56-
builder.error(ErrMsg("expression.expected"))
56+
builder.expressionExpectedError()
5757
}
5858

5959
patDefMarker.drop()

scala/scala-impl/test/org/jetbrains/plugins/scala/lang/completion3/ScalaNamedTupleCompletionContributorTest.scala

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,40 @@ class ScalaNamedTupleCompletionContributorTest extends ScalaCompletionTestBase {
4040
|""".stripMargin,
4141
item = """a = ???, b = "test"""",
4242
)
43+
44+
def testCompleteExistingNamedTupleWithReordering2(): Unit = doCompletionTest(
45+
fileText =
46+
s"""val x: (a: Int, bbb: String) = (a = 1, b$CARET = "test")
47+
|""".stripMargin,
48+
resultText =
49+
s"""val x: (a: Int, bbb: String) = (a = 1, bbb = "test")
50+
|""".stripMargin,
51+
item = """a = 1, bbb = "test"""",
52+
)
53+
54+
def testCompleteExistingNamedTupleWithReordering3(): Unit = doCompletionTest(
55+
fileText =
56+
s"""val x: (a: Int, bbb: String, ccc: Boolean) = (b$CARET = "test", a = 1, c = true)
57+
|""".stripMargin,
58+
resultText =
59+
s"""val x: (a: Int, bbb: String, ccc: Boolean) = (a = 1, bbb = "test", ccc = true)
60+
|""".stripMargin,
61+
item = """a = 1, bbb = "test", ccc = true""",
62+
)
63+
64+
def testDontCompleteInExpr(): Unit =
65+
checkNoCompletion(s"val x: (a: Int, b: String) = (a = $CARET)") {
66+
item => item.getLookupString.contains(" = ")
67+
}
68+
69+
// SCL-23823
70+
def testDontCompleteInExpr2(): Unit =
71+
checkNoCompletion(s"val x: (a: Int, b: String) = (a = p.$CARET, b = ???)") {
72+
item => item.getLookupString.contains(" = ")
73+
}
74+
75+
def testDontCompleteIfAllComponentsAreThere(): Unit =
76+
checkNoCompletion(s"val x: (a: Int, b: String) = (a = ???, b$CARET = ???)") {
77+
item => item.getLookupString.contains(" = ")
78+
}
4379
}

0 commit comments

Comments
 (0)