Skip to content

Commit e929407

Browse files
committed
fix(completions): support backticked imports in completions
This will ensure that when you try to backtick an import whether it's needed or not that you correctly capture the prefix and offer a backticked completion. For example: ```scala import scala.util.chaining.`s<TAB> ``` Results in an `Ident(<error>)` which we now check to see if a backtick is causing it and correctly offer the completion. Fixes scala#12514
1 parent 90766d4 commit e929407

File tree

3 files changed

+42
-9
lines changed

3 files changed

+42
-9
lines changed

compiler/src/dotty/tools/dotc/interactive/Completion.scala

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,22 @@ object Completion {
7878
Mode.None
7979
}
8080

81+
/** When dealing with <errors> in varios palces we check to see if they are
82+
* due to incomplete backticks. If so, we ensure we get the full prefix
83+
* including the backtick.
84+
*
85+
* @param content The source content that we'll check the positions for the prefix
86+
* @param start The start position we'll start to look for the prefix at
87+
* @param end The end position we'll look for the prefix at
88+
* @return Either the full prefix including the ` or an empty string
89+
*/
90+
private def checkBacktickPrefix(content: Array[Char], start: Int, end: Int): String =
91+
content.lift(start) match
92+
case Some(char) if char == '`' =>
93+
content.slice(start, end).mkString
94+
case _ =>
95+
""
96+
8197
/**
8298
* Inspect `path` to determine the completion prefix. Only symbols whose name start with the
8399
* returned prefix should be considered.
@@ -92,15 +108,14 @@ object Completion {
92108
completionPrefix(selector :: Nil, pos)
93109
}.getOrElse("")
94110

95-
// We special case Select here because we want to determine if the name
96-
// is an error due to an unclosed backtick.
97-
case (select: untpd.Select) :: _ if (select.name == nme.ERROR) =>
98-
val content = select.source.content()
99-
content.lift(select.nameSpan.start) match
100-
case Some(char) if char == '`' =>
101-
content.slice(select.nameSpan.start, select.span.end).mkString
102-
case _ =>
103-
""
111+
// Foo.`se<TAB> will result in Select(Ident(Foo), <error>)
112+
case (select: untpd.Select) :: _ if select.name == nme.ERROR =>
113+
checkBacktickPrefix(select.source.content(), select.nameSpan.start, select.span.end)
114+
115+
// import scala.util.chaining.`s<TAB> will result in a Ident(<error>)
116+
case (ident: untpd.Ident) :: _ if ident.name == nme.ERROR =>
117+
checkBacktickPrefix(ident.source.content(), ident.span.start, ident.span.end)
118+
104119
case (ref: untpd.RefTree) :: _ =>
105120
if (ref.name == nme.ERROR) ""
106121
else ref.name.toString.take(pos.span.point - ref.span.point)

compiler/test/dotty/tools/repl/TabcompleteTests.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,13 @@ class TabcompleteTests extends ReplTest {
195195
|Foo.`bac"""stripMargin))
196196
}
197197

198+
@Test def backtickedImport = initially {
199+
assertEquals(
200+
List(
201+
"`scalaUtilChainingOps`",
202+
"`synchronized`"
203+
),
204+
tabComplete("import scala.util.chaining.`s"))
205+
}
206+
198207
}

language-server/test/dotty/tools/languageserver/CompletionTest.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,4 +1121,13 @@ class CompletionTest {
11211121
| val x = Bar.`fo${m1}"""
11221122
.withSource.completion(m1, expected)
11231123
}
1124+
1125+
@Test def backticksImported: Unit = {
1126+
val expected = Set(
1127+
("`scalaUtilChainingOps`", Method, "[A](a: A): scala.util.ChainingOps[A]"),
1128+
("`synchronized`", Method, "[X0](x$0: X0): X0")
1129+
)
1130+
code"""import scala.util.chaining.`s${m1}"""
1131+
.withSource.completion(m1, expected)
1132+
}
11241133
}

0 commit comments

Comments
 (0)