Skip to content

Commit a4d38b9

Browse files
authored
Merge pull request #5652 from dotty-staging/fix-5183
Fix #5183: (REPL) Only insert line break when cursor is not on the last line
2 parents 8743454 + d9549fa commit a4d38b9

File tree

7 files changed

+61
-31
lines changed

7 files changed

+61
-31
lines changed

compiler/src/dotty/tools/repl/JLineTerminal.scala

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ final class JLineTerminal extends java.io.Closeable {
9595
def words = java.util.Collections.emptyList[String]
9696
}
9797

98-
def parse(line: String, cursor: Int, context: ParseContext): reader.ParsedLine = {
98+
def parse(input: String, cursor: Int, context: ParseContext): reader.ParsedLine = {
9999
def parsedLine(word: String, wordCursor: Int) =
100-
new ParsedLine(cursor, line, word, wordCursor)
100+
new ParsedLine(cursor, input, word, wordCursor)
101101
// Used when no word is being completed
102102
def defaultParsedLine = parsedLine("", 0)
103103

@@ -110,7 +110,7 @@ final class JLineTerminal extends java.io.Closeable {
110110

111111
case class TokenData(token: Token, start: Int, end: Int)
112112
def currentToken: TokenData /* | Null */ = {
113-
val source = new SourceFile("<completions>", line)
113+
val source = new SourceFile("<completions>", input)
114114
val scanner = new Scanner(source)(ctx.fresh.setReporter(Reporter.NoReporter))
115115
while (scanner.token != EOF) {
116116
val start = scanner.offset
@@ -125,23 +125,22 @@ final class JLineTerminal extends java.io.Closeable {
125125
null
126126
}
127127

128+
def acceptLine = {
129+
val onLastLine = !input.substring(cursor).contains(System.lineSeparator)
130+
onLastLine && !ParseResult.isIncomplete(input)
131+
}
132+
128133
context match {
129-
case ParseContext.ACCEPT_LINE =>
130-
// ENTER means SUBMIT when
131-
// - cursor is at end (discarding whitespaces)
132-
// - and, input line is complete
133-
val cursorIsAtEnd = line.indexWhere(!_.isWhitespace, from = cursor) < 0
134-
if (cursorIsAtEnd && !ParseResult.isIncomplete(line))
135-
defaultParsedLine // using dummy values, resulting parsed line is probably unused
136-
else
137-
incomplete()
134+
case ParseContext.ACCEPT_LINE if acceptLine =>
135+
// using dummy values, resulting parsed input is probably unused
136+
defaultParsedLine
138137

139138
case ParseContext.COMPLETE =>
140139
// Parse to find completions (typically after a Tab).
141140
def isCompletable(token: Token) = isIdentifier(token) || isKeyword(token)
142141
currentToken match {
143142
case TokenData(token, start, end) if isCompletable(token) =>
144-
val word = line.substring(start, end)
143+
val word = input.substring(start, end)
145144
val wordCursor = cursor - start
146145
parsedLine(word, wordCursor)
147146
case _ =>

compiler/src/dotty/tools/repl/ReplCompiler.scala

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,26 +75,36 @@ class ReplCompiler extends Compiler {
7575

7676
implicit val ctx: Context = state.context
7777

78+
// If trees is of the form `{ def1; def2; def3 }` then `List(def1, def2, def3)`
79+
val flattened = trees match {
80+
case List(Block(stats, expr)) =>
81+
if (expr eq EmptyTree) stats // happens when expr is not an expression
82+
else stats :+ expr
83+
case _ =>
84+
trees
85+
}
86+
7887
var valIdx = state.valIndex
88+
val defs = new mutable.ListBuffer[Tree]
7989

80-
val defs = trees.flatMap {
90+
flattened.foreach {
8191
case expr @ Assign(id: Ident, _) =>
8292
// special case simple reassignment (e.g. x = 3)
8393
// in order to print the new value in the REPL
8494
val assignName = (id.name ++ str.REPL_ASSIGN_SUFFIX).toTermName
8595
val assign = ValDef(assignName, TypeTree(), id).withPos(expr.pos)
86-
List(expr, assign)
96+
defs += expr += assign
8797
case expr if expr.isTerm =>
8898
val resName = (str.REPL_RES_PREFIX + valIdx).toTermName
8999
valIdx += 1
90100
val vd = ValDef(resName, TypeTree(), expr).withPos(expr.pos)
91-
vd :: Nil
101+
defs += vd
92102
case other =>
93-
other :: Nil
103+
defs += other
94104
}
95105

96106
Definitions(
97-
defs,
107+
defs.toList,
98108
state.copy(
99109
objectIndex = state.objectIndex + (if (defs.isEmpty) 0 else 1),
100110
valIndex = valIdx

compiler/src/dotty/tools/repl/ReplDriver.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,14 +254,16 @@ class ReplDriver(settings: Array[String],
254254
denot.symbol.owner == defn.ObjectClass ||
255255
denot.symbol.isConstructor
256256
}
257+
.sortBy(_.name)
257258

258259
val vals =
259260
info.fields
260261
.filterNot(_.symbol.is(ParamAccessor | Private | Synthetic | Module))
261262
.filter(_.symbol.name.is(SimpleNameKind))
263+
.sortBy(_.name)
262264

263265
val typeAliases =
264-
info.bounds.hi.typeMembers.filter(_.symbol.info.isTypeAlias)
266+
info.bounds.hi.typeMembers.filter(_.symbol.info.isTypeAlias).sortBy(_.name)
265267

266268
(
267269
typeAliases.map("// defined alias " + _.symbol.showUser) ++

compiler/test-resources/repl/errmsgs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,6 @@ scala> val x: List[Int] = "foo" :: List(1)
5353
| ^^^^^
5454
| Found: String("foo")
5555
| Required: Int
56-
scala> { def f: Int = g; val x: Int = 1; def g: Int = 5; }
57-
1 | { def f: Int = g; val x: Int = 1; def g: Int = 5; }
58-
| ^
59-
| g is a forward reference extending over the definition of x
6056
scala> while ((( foo ))) {}
6157
1 | while ((( foo ))) {}
6258
| ^^^
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
scala> { case class A(); def foo = 1; 2 }
2+
// defined case class A
3+
def foo: Int
4+
val res0: Int = 2
5+
6+
scala> foo
7+
val res1: Int = 1
8+
9+
scala> A()
10+
val res2: A = A()
11+
12+
scala> { def f: Int = g; def g: Int = 5 }
13+
def f: Int
14+
def g: Int
15+
16+
scala> f + g
17+
val res3: Int = 10
18+
19+
scala> { val x = 3; 4; val y = 5 }
20+
val res4: Int = 4
21+
val x: Int = 3
22+
val y: Int = 5

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package dotty.tools.repl
22

3-
import java.lang.System.{lineSeparator => EOL}
4-
53
import org.junit.Assert._
64
import org.junit.{Ignore, Test}
75

86
class ReplCompilerTests extends ReplTest {
97

8+
private def lines() =
9+
storedOutput().trim.linesIterator.toList
10+
1011
@Test def compileSingle = fromInitialState { implicit state =>
1112
run("def foo: 1 = 1")
1213
assertEquals("def foo: Int(1)", storedOutput().trim)
@@ -47,13 +48,13 @@ class ReplCompilerTests extends ReplTest {
4748

4849
val expected = List(
4950
"def foo: Int",
50-
"val x: Int = 10",
5151
"val res0: Int = 2",
52-
"var y: Int = 5",
53-
"val res1: Int = 20"
52+
"val res1: Int = 20",
53+
"val x: Int = 10",
54+
"var y: Int = 5"
5455
)
5556

56-
assertEquals(expected, storedOutput().split(EOL).toList)
57+
assertEquals(expected, lines())
5758
}
5859

5960
@Test def testImportMutable =
@@ -124,6 +125,6 @@ class ReplCompilerTests extends ReplTest {
124125
)
125126

126127
run(source)
127-
assertEquals(expected, storedOutput().split(EOL).toList)
128+
assertEquals(expected, lines())
128129
}
129130
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class WorksheetTest {
8585

8686
@Test def patternMatching1: Unit = {
8787
ws"""${m1}val (foo, bar) = (1, 2)""".withSource
88-
.run(m1, s"1:val foo: Int = 1${nl}val bar: Int = 2")
88+
.run(m1, s"1:val bar: Int = 2${nl}val foo: Int = 1")
8989
}
9090

9191
@Test def evaluationException: Unit = {

0 commit comments

Comments
 (0)