Skip to content

Commit dc1e35a

Browse files
authored
Completions should prepend, not replace as it is for Scala 2 (#18803)
By default, in Scala 2 completion prepend and only replace if there is exact match. This PR unifies the experience between the Scala versions. It seems more intuitive to work that way. The change will be as follows (the order is: new logic, old logic and scala 2 logic): https://github.com/lampepfl/dotty/assets/48657087/c037c322-5613-4b95-a6e5-090b4e8827b6 In the future, it should be improved by changing implementation to use `InsertReplaceEdit` instead of `TextEdit`. This will allow users to decide which behaviour they prefer, but this has to be first implemented in metals to properly handle the new logic, but for now the key is to unify behaviours. https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#insertReplaceEdit
1 parent e399c43 commit dc1e35a

File tree

3 files changed

+57
-19
lines changed

3 files changed

+57
-19
lines changed

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

+2-3
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,9 @@ case class CompletionPos(
2929
):
3030

3131
def sourcePos: SourcePosition = cursorPos.withSpan(Spans.Span(start, end))
32+
def stripSuffixEditRange: l.Range = new l.Range(cursorPos.offsetToPos(start), cursorPos.offsetToPos(end))
33+
def toEditRange: l.Range = cursorPos.withStart(start).withEnd(cursorPos.point).toLsp
3234

33-
def toEditRange: l.Range =
34-
new l.Range(cursorPos.offsetToPos(start), cursorPos.offsetToPos(end))
35-
end toEditRange
3635
end CompletionPos
3736

3837
object CompletionPos:

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

+11-16
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,7 @@ class CompletionProvider(
151151
indexedContext: IndexedContext
152152
)(using ctx: Context): CompletionItem =
153153
val printer =
154-
ShortenedTypePrinter(search, IncludeDefaultParam.ResolveLater)(using
155-
indexedContext
156-
)
157-
val editRange = completionPos.toEditRange
154+
ShortenedTypePrinter(search, IncludeDefaultParam.ResolveLater)(using indexedContext)
158155

159156
// For overloaded signatures we get multiple symbols, so we need
160157
// to recalculate the description
@@ -165,24 +162,22 @@ class CompletionProvider(
165162
val ident = completion.insertText.getOrElse(completion.label)
166163

167164
def mkItem(
168-
insertText: String,
165+
newText: String,
169166
additionalEdits: List[TextEdit] = Nil,
170167
range: Option[LspRange] = None
171168
): CompletionItem =
172-
val nameEdit = new TextEdit(
173-
range.getOrElse(editRange),
174-
insertText
175-
)
169+
val oldText = params.text.substring(completionPos.start, completionPos.end)
170+
val editRange = if newText.startsWith(oldText) then completionPos.stripSuffixEditRange
171+
else completionPos.toEditRange
172+
173+
val textEdit = new TextEdit(range.getOrElse(editRange), newText)
174+
176175
val item = new CompletionItem(label)
177176
item.setSortText(f"${idx}%05d")
178177
item.setDetail(description)
179-
item.setFilterText(
180-
completion.filterText.getOrElse(completion.label)
181-
)
182-
item.setTextEdit(nameEdit)
183-
item.setAdditionalTextEdits(
184-
(completion.additionalEdits ++ additionalEdits).asJava
185-
)
178+
item.setFilterText(completion.filterText.getOrElse(completion.label))
179+
item.setTextEdit(textEdit)
180+
item.setAdditionalTextEdits((completion.additionalEdits ++ additionalEdits).asJava)
186181
completion.insertMode.foreach(item.setInsertTextMode)
187182

188183
completion

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

+44
Original file line numberDiff line numberDiff line change
@@ -1500,3 +1500,47 @@ class CompletionSuite extends BaseCompletionSuite:
15001500
|""".stripMargin,
15011501
)
15021502

1503+
@Test def `prepend-instead-of-replace` =
1504+
checkEdit(
1505+
"""|object O:
1506+
| printl@@println()
1507+
|""".stripMargin,
1508+
"""|object O:
1509+
| printlnprintln()
1510+
|""".stripMargin,
1511+
assertSingleItem = false
1512+
)
1513+
1514+
@Test def `prepend-instead-of-replace-duplicate-word` =
1515+
checkEdit(
1516+
"""|object O:
1517+
| println@@println()
1518+
|""".stripMargin,
1519+
"""|object O:
1520+
| printlnprintln()
1521+
|""".stripMargin,
1522+
assertSingleItem = false
1523+
)
1524+
1525+
@Test def `replace-when-inside` =
1526+
checkEdit(
1527+
"""|object O:
1528+
| print@@ln()
1529+
|""".stripMargin,
1530+
"""|object O:
1531+
| println()
1532+
|""".stripMargin,
1533+
assertSingleItem = false
1534+
)
1535+
1536+
@Test def `replace-exact-same` =
1537+
checkEdit(
1538+
"""|object O:
1539+
| println@@()
1540+
|""".stripMargin,
1541+
"""|object O:
1542+
| println()
1543+
|""".stripMargin,
1544+
assertSingleItem = false
1545+
)
1546+

0 commit comments

Comments
 (0)