Skip to content

Commit 64a02c1

Browse files
committed
Fix missing braces in interpolator, apply last review
1 parent b74ca93 commit 64a02c1

File tree

5 files changed

+71
-52
lines changed

5 files changed

+71
-52
lines changed

presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ case class CompletionAffix(
5353
endPos <- ranges.map(_.getEnd).maxOption
5454
yield Range(startPos, endPos)
5555

56-
private def loopPrefix(prefixes: List[PrefixKind]) =
56+
private def loopPrefix(prefixes: List[PrefixKind]): String =
5757
prefixes match
58-
case PrefixKind.New :: tail => "new "
58+
case PrefixKind.New :: tail => "new " + loopPrefix(tail)
5959
case _ => ""
6060

6161
/**

presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,25 @@ class CompletionProvider(
161161
val label = completion.labelWithDescription(printer)
162162
val ident = completion.insertText.getOrElse(completion.label)
163163

164+
lazy val isInStringInterpolation =
165+
path match
166+
// s"My name is $name"
167+
case (_: Ident) :: (_: SeqLiteral) :: (_: Typed) :: Apply(
168+
Select(Apply(Select(Select(_, name), _), _), _),
169+
_
170+
) :: _ =>
171+
name == StdNames.nme.StringContext
172+
// "My name is $name"
173+
case Literal(Constant(_: String)) :: _ =>
174+
true
175+
case _ =>
176+
false
177+
178+
def wrapInBracketsIfRequired(newText: String): String =
179+
if completion.snippetAffix.nonEmpty && isInStringInterpolation then
180+
"{" + newText + "}"
181+
else newText
182+
164183
def mkItem(
165184
newText: String,
166185
additionalEdits: List[TextEdit] = Nil,
@@ -170,7 +189,7 @@ class CompletionProvider(
170189
val editRange = if newText.startsWith(oldText) then completionPos.stripSuffixEditRange
171190
else completionPos.toEditRange
172191

173-
val textEdit = new TextEdit(range.getOrElse(editRange), newText)
192+
val textEdit = new TextEdit(range.getOrElse(editRange), wrapInBracketsIfRequired(newText))
174193

175194
val item = new CompletionItem(label)
176195
item.setSortText(f"${idx}%05d")
@@ -199,20 +218,6 @@ class CompletionProvider(
199218
val completionTextSuffix = completion.snippetAffix.toSuffix
200219
val completionTextPrefix = completion.snippetAffix.toInsertPrefix
201220

202-
lazy val isInStringInterpolation =
203-
path match
204-
// s"My name is $name"
205-
case (_: Ident) :: (_: SeqLiteral) :: (_: Typed) :: Apply(
206-
Select(Apply(Select(Select(_, name), _), _), _),
207-
_
208-
) :: _ =>
209-
name == StdNames.nme.StringContext
210-
// "My name is $name"
211-
case Literal(Constant(_: String)) :: _ =>
212-
true
213-
case _ =>
214-
false
215-
216221
lazy val backtickSoftKeyword = path match
217222
case (_: Select) :: _ => false
218223
case _ => true
@@ -243,13 +248,12 @@ class CompletionProvider(
243248
case IndexedContext.Result.InScope =>
244249
mkItem(
245250
v.insertText.getOrElse(
246-
completionTextPrefix +
247-
ident.backticked(
248-
backtickSoftKeyword
249-
) + completionTextSuffix
251+
completionTextPrefix + ident.backticked(backtickSoftKeyword) + completionTextSuffix
250252
),
251253
range = v.range,
252254
)
255+
// Special case when symbol is out of scope, and there is no auto import.
256+
// It means that it will use fully qualified path
253257
case _ if isInStringInterpolation =>
254258
mkItem(
255259
"{" + completionTextPrefix + sym.fullNameBackticked + completionTextSuffix + "}",
@@ -280,10 +284,7 @@ class CompletionProvider(
280284
case _ =>
281285
val nameText = completion.insertText.getOrElse(ident.backticked(backtickSoftKeyword))
282286
val nameWithAffixes = completionTextPrefix + nameText + completionTextSuffix
283-
val insertText = if completion.snippetAffix.nonEmpty && isInStringInterpolation then
284-
"{" + nameWithAffixes + "}"
285-
else nameWithAffixes
286-
mkItem(insertText, range = completion.range)
287+
mkItem(nameWithAffixes, range = completion.range)
287288

288289
end match
289290
end completionItems

presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -262,10 +262,6 @@ class Completions(
262262

263263
methodDenots.map { methodDenot =>
264264
val suffix = findSuffix(methodDenot.symbol)
265-
val currentPrefix = adjustedPath match
266-
case Select(qual, _) :: _ => Some(qual.show + ".", qual.span.start)
267-
case _ => None
268-
269265
val affix = if methodDenot.symbol.isConstructor && existsApply then
270266
adjustedPath match
271267
case (select @ Select(qual, _)) :: _ =>
@@ -606,29 +602,26 @@ class Completions(
606602
/** If we try to complete TypeName, we should favor types over terms with same name value and without suffix.
607603
*/
608604
def deduplicateCompletions(completions: List[CompletionValue]): List[CompletionValue] =
609-
val typeResultMappings = mutable.Map.empty[Name, Seq[CompletionValue]]
610605
val (symbolicCompletions, rest) = completions.partition:
611606
_.isInstanceOf[CompletionValue.Symbolic]
612607

613608
val symbolicCompletionsMap = symbolicCompletions
614609
.collect { case symbolic: CompletionValue.Symbolic => symbolic }
615610
.groupBy(_.symbol.fullName) // we somehow have to ignore proxy type
616611

617-
symbolicCompletionsMap.foreach: (name, denots) =>
612+
val filteredSymbolicCompletions = symbolicCompletionsMap.filter: (name, denots) =>
618613
lazy val existsTypeWithoutSuffix: Boolean = !symbolicCompletionsMap
619614
.get(name.toTypeName)
620615
.forall(_.forall(sym => sym.snippetAffix.suffixes.nonEmpty))
621616

622-
if completionMode.is(Mode.Term) && !completionMode.is(Mode.ImportOrExport) then
623-
typeResultMappings += name -> denots
617+
(completionMode.is(Mode.Term) && !completionMode.is(Mode.ImportOrExport)) ||
624618
// show non synthetic symbols
625619
// companion test should not result TrieMap[K, V]
626-
else if name.isTermName && !existsTypeWithoutSuffix then
627-
typeResultMappings += name -> denots
628-
else if name.isTypeName then
629-
typeResultMappings += name -> denots
620+
(name.isTermName && !existsTypeWithoutSuffix) ||
621+
name.isTypeName
622+
.toList.unzip._2.flatten
630623

631-
typeResultMappings.toList.unzip._2.flatten ++ rest
624+
filteredSymbolicCompletions ++ rest
632625

633626
extension (l: List[CompletionValue])
634627
def filterInteresting(

presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,17 @@ object InterpolatorCompletions:
110110
buildTargetIdentifier: String
111111
)(using Context, ReportContext): List[CompletionValue] =
112112
def newText(
113-
name: String,
114-
suffix: Option[String],
113+
label: String,
114+
affix: CompletionAffix ,
115115
identOrSelect: Ident | Select
116116
): String =
117-
val snippetCursor = suffixEnding(suffix, areSnippetsSupported)
117+
val snippetCursor = suffixEnding(affix.toSuffixOpt, areSnippetsSupported)
118118
new StringBuilder()
119119
.append('{')
120-
.append(
121-
text.substring(identOrSelect.span.start, identOrSelect.span.end)
122-
)
120+
.append(affix.toPrefix) // we use toPrefix here, because previous prefix is added in the next step
121+
.append(text.substring(identOrSelect.span.start, identOrSelect.span.end))
123122
.append('.')
124-
.append(name.backticked)
123+
.append(label.backticked)
125124
.append(snippetCursor)
126125
.append('}')
127126
.toString
@@ -156,11 +155,11 @@ object InterpolatorCompletions:
156155
completions.completionsWithAffix(
157156
sym,
158157
label,
159-
(name, denot, suffix) =>
158+
(name, denot, affix) =>
160159
CompletionValue.Interpolator(
161160
denot.symbol,
162161
label,
163-
Some(newText(name, suffix.toSuffixOpt, identOrSelect)),
162+
Some(newText(name, affix, identOrSelect)),
164163
Nil,
165164
Some(completionPos.originalCursorPosition.withStart(identOrSelect.span.start).toLsp),
166165
// Needed for VS Code which will not show the completion otherwise
@@ -250,16 +249,18 @@ object InterpolatorCompletions:
250249
interpolatorEdit ++ dollarEdits
251250
end additionalEdits
252251

253-
def newText(symbolName: String, suffix: Option[String]): String =
252+
def newText(symbolName: String, affix: CompletionAffix): String =
254253
val out = new StringBuilder()
255254
val identifier = symbolName.backticked
256255
val symbolNeedsBraces =
257256
interpolator.needsBraces ||
258257
identifier.startsWith("`") ||
259-
suffix.isDefined
258+
affix.toSuffixOpt.isDefined ||
259+
affix.toPrefix.nonEmpty
260260
if symbolNeedsBraces && !hasOpeningBrace then out.append('{')
261+
out.append(affix.toInsertPrefix)
261262
out.append(identifier)
262-
out.append(suffixEnding(suffix, areSnippetsSupported))
263+
out.append(suffixEnding(affix.toSuffixOpt, areSnippetsSupported))
263264
if symbolNeedsBraces && !hasClosingBrace then out.append('}')
264265
out.toString
265266
end newText
@@ -287,11 +288,11 @@ object InterpolatorCompletions:
287288
completions.completionsWithAffix(
288289
sym,
289290
label,
290-
(name, denot, suffix) =>
291+
(name, denot, affix) =>
291292
CompletionValue.Interpolator(
292293
denot.symbol,
293294
label,
294-
Some(newText(name, suffix.toSuffixOpt)),
295+
Some(newText(name, affix)),
295296
additionalEdits(),
296297
Some(nameRange),
297298
None,

presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,3 +779,27 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite:
779779
|""".stripMargin,
780780
"host: String"
781781
)
782+
783+
@Test def `prepend-new-missing-interpolator` =
784+
checkSnippet(
785+
"""|object Wrapper:
786+
| "$Try@@"
787+
|
788+
|""".stripMargin,
789+
"""|Try$0
790+
|{Try($0)}
791+
|{new Try$0}
792+
|""".stripMargin,
793+
)
794+
795+
@Test def `prepend-new-interpolator` =
796+
checkSnippet(
797+
"""|object Wrapper:
798+
| s"$Try@@"
799+
|
800+
|""".stripMargin,
801+
"""|Try
802+
|{Try($0)}
803+
|{new Try}
804+
|""".stripMargin,
805+
)

0 commit comments

Comments
 (0)