Skip to content

Commit 55df3f3

Browse files
committed
bugfix: Auto imports in worksheets in Scala 3
1 parent 929714c commit 55df3f3

File tree

3 files changed

+108
-12
lines changed

3 files changed

+108
-12
lines changed

presentation-compiler/src/main/dotty/tools/pc/AutoImports.scala

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,24 @@ object AutoImports:
302302
}.headOption
303303
case _ => None
304304

305-
def skipUsingDirectivesOffset =
305+
def firstMemberDefinitionStart(tree: Tree)(using Context): Option[Int] =
306+
tree match
307+
case PackageDef(_, stats) =>
308+
stats.flatMap {
309+
case s: PackageDef => firstMemberDefinitionStart(s)
310+
case stat if stat.span.exists => Some(stat.span.start)
311+
case _ => None
312+
}.headOption
313+
case _ => None
314+
315+
316+
def skipUsingDirectivesOffset(
317+
firstObjectPos: Int = firstMemberDefinitionStart(tree).getOrElse(0)
318+
): Int =
319+
val firstObjectLine = pos.source.offsetToLine(firstObjectPos)
306320
comments
307321
.takeWhile(comment =>
308-
!comment.isDocComment && comment.span.end < firstObjectBody(tree)
309-
.fold(0)(_.span.start)
322+
!comment.isDocComment && pos.source.offsetToLine(comment.span.end) + 1 < firstObjectLine
310323
)
311324
.lastOption
312325
.fold(0)(_.span.end + 1)
@@ -318,7 +331,7 @@ object AutoImports:
318331
val (lineNumber, padTop) = lastImportStatement match
319332
case Some(stm) => (stm.endPos.line + 1, false)
320333
case None if pkg.pid.symbol.isEmptyPackage =>
321-
(pos.source.offsetToLine(skipUsingDirectivesOffset), false)
334+
(pos.source.offsetToLine(skipUsingDirectivesOffset()), false)
322335
case None =>
323336
val pos = pkg.pid.endPos
324337
val line =
@@ -330,7 +343,7 @@ object AutoImports:
330343
new AutoImportPosition(offset, text, padTop)
331344
}
332345

333-
def forScript(isAmmonite: Boolean): Option[AutoImportPosition] =
346+
def forScript(path: String): Option[AutoImportPosition] =
334347
firstObjectBody(tree).map { tmpl =>
335348
val lastImportStatement =
336349
tmpl.body.takeWhile(_.isInstanceOf[Import]).lastOption
@@ -340,10 +353,11 @@ object AutoImports:
340353
offset
341354
case None =>
342355
val scriptOffset =
343-
if isAmmonite then
344-
ScriptFirstImportPosition.ammoniteScStartOffset(text, comments)
345-
else
346-
ScriptFirstImportPosition.scalaCliScStartOffset(text, comments)
356+
if path.isAmmoniteGeneratedFile
357+
then ScriptFirstImportPosition.ammoniteScStartOffset(text, comments)
358+
else if path.isScalaCLIGeneratedFile
359+
then ScriptFirstImportPosition.scalaCliScStartOffset(text, comments)
360+
else Some(skipUsingDirectivesOffset(tmpl.span.start))
347361

348362
scriptOffset.getOrElse {
349363
val tmplPoint = tmpl.self.srcPos.span.point
@@ -359,14 +373,16 @@ object AutoImports:
359373

360374
def fileStart =
361375
AutoImportPosition(
362-
skipUsingDirectivesOffset,
376+
skipUsingDirectivesOffset(),
363377
0,
364378
padTop = false
365379
)
366380

367381
val scriptPos =
368-
if path.isAmmoniteGeneratedFile then forScript(isAmmonite = true)
369-
else if path.isScalaCLIGeneratedFile then forScript(isAmmonite = false)
382+
if path.isAmmoniteGeneratedFile ||
383+
path.isScalaCLIGeneratedFile ||
384+
path.isWorksheet
385+
then forScript(path)
370386
else None
371387

372388
scriptPos

presentation-compiler/test/dotty/tools/pc/base/BaseAutoImportsSuite.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ trait BaseAutoImportsSuite extends BaseCodeActionSuite:
4141
selection
4242
)
4343

44+
def checkWorksheetEdit(
45+
original: String,
46+
expected: String,
47+
selection: Int = 0
48+
): Unit =
49+
checkEditSelection(
50+
"example.worksheet.sc",
51+
original,
52+
expected,
53+
selection
54+
)
55+
4456
def checkEditSelection(
4557
filename: String,
4658
original: String,

presentation-compiler/test/dotty/tools/pc/tests/edit/AutoImportsSuite.scala

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,69 @@ class AutoImportsSuite extends BaseAutoImportsSuite:
342342
)
343343
)
344344

345+
@Test def `worksheet-import` =
346+
checkWorksheetEdit(
347+
worksheetPcWrapper(
348+
"""|//> using scala 3.3.0
349+
|
350+
|// Some comment
351+
|
352+
|// Object comment
353+
|object A {
354+
| val p: <<Path>> = ???
355+
|}
356+
|""".stripMargin
357+
),
358+
worksheetPcWrapper(
359+
"""|//> using scala 3.3.0
360+
|
361+
|// Some comment
362+
|import java.nio.file.Path
363+
|
364+
|// Object comment
365+
|object A {
366+
| val p: Path = ???
367+
|}
368+
|""".stripMargin
369+
)
370+
)
371+
372+
@Test def `object-import` =
373+
checkEdit(
374+
"""|object A {
375+
| //some comment
376+
| val p: <<Path>> = ???
377+
|}
378+
|""".stripMargin,
379+
"""|import java.nio.file.Path
380+
|object A {
381+
| //some comment
382+
| val p: Path = ???
383+
|}
384+
|""".stripMargin,
385+
)
386+
387+
@Test def `toplevels-import` =
388+
checkEdit(
389+
"""|//some comment
390+
|
391+
|val p: <<Path>> = ???
392+
|
393+
|//some other comment
394+
|
395+
|val v = 1
396+
|""".stripMargin,
397+
"""|//some comment
398+
|import java.nio.file.Path
399+
|
400+
|val p: Path = ???
401+
|
402+
|//some other comment
403+
|
404+
|val v = 1
405+
|""".stripMargin,
406+
)
407+
345408
private def ammoniteWrapper(code: String): String =
346409
// Vaguely looks like a scala file that Ammonite generates
347410
// from a sc file.
@@ -359,6 +422,11 @@ class AutoImportsSuite extends BaseAutoImportsSuite:
359422
|}
360423
|""".stripMargin
361424

425+
private def worksheetPcWrapper(code: String): String =
426+
s"""|object worksheet{
427+
|$code
428+
|}""".stripMargin
429+
362430
// https://dotty.epfl.ch/docs/internals/syntax.html#soft-keywords
363431
@Test
364432
def `soft-keyword-check-test` =

0 commit comments

Comments
 (0)