Skip to content
This repository was archived by the owner on Sep 1, 2020. It is now read-only.

Commit c3aca10

Browse files
committed
SI-9206 Fix REPL code indentation
To make code in error messages line up with the original line of code, templated code is indented by the width of the prompt. Use the raw prompt (without ANSI escapes or newlines) to determine the indentation. Also, indent only once per line.
1 parent aa98d9a commit c3aca10

27 files changed

+223
-200
lines changed

src/compiler/scala/tools/nsc/Properties.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ object Properties extends scala.util.PropertiesTrait {
1313

1414
// settings based on jar properties, falling back to System prefixed by "scala."
1515
def residentPromptString = scalaPropOrElse("resident.prompt", "\nnsc> ")
16-
def shellPromptString = scalaPropOrElse("shell.prompt", "\nscala> ")
16+
def shellPromptString = scalaPropOrElse("shell.prompt", "%nscala> ")
1717
// message to display at EOF (which by default ends with
1818
// a newline so as not to break the user's terminal)
1919
def shellInterruptedString = scalaPropOrElse("shell.interrupted", f":quit$lineSeparator")

src/repl/scala/tools/nsc/interpreter/Formatting.scala

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,25 @@ package interpreter
88

99
import util.stringFromWriter
1010

11-
trait Formatting {
12-
def prompt: String
11+
class Formatting(indent: Int) {
1312

14-
def spaces(code: String): String = {
13+
private val indentation = " " * indent
14+
15+
private def indenting(code: String): Boolean = {
1516
/** Heuristic to avoid indenting and thereby corrupting """-strings and XML literals. */
1617
val tokens = List("\"\"\"", "</", "/>")
1718
val noIndent = (code contains "\n") && (tokens exists code.contains)
1819

19-
if (noIndent) ""
20-
else prompt drop 1 map (_ => ' ')
20+
!noIndent
2121
}
2222
/** Indent some code by the width of the scala> prompt.
2323
* This way, compiler error messages read better.
2424
*/
25-
def indentCode(code: String) = {
26-
val indent = spaces(code)
27-
stringFromWriter(str =>
28-
for (line <- code.lines) {
29-
str print indent
30-
str print (line + "\n")
31-
str.flush()
32-
}
33-
)
34-
}
25+
def indentCode(code: String) = stringFromWriter(str =>
26+
for (line <- code.lines) {
27+
if (indenting(code)) str print indentation
28+
str println line
29+
str.flush()
30+
}
31+
)
3532
}

src/repl/scala/tools/nsc/interpreter/ILoop.scala

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,10 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
109109
}
110110

111111
class ILoopInterpreter extends IMain(settings, out) {
112-
outer =>
113-
114-
override lazy val formatting = new Formatting {
115-
def prompt = ILoop.this.prompt
116-
}
112+
// the expanded prompt but without color escapes and without leading newline, for purposes of indenting
113+
override lazy val formatting: Formatting = new Formatting(
114+
(replProps.promptString format Properties.versionNumberString).lines.toList.last.length
115+
)
117116
override protected def parentClassLoader =
118117
settings.explicitParentLoader.getOrElse( classOf[ILoop].getClassLoader )
119118
}

src/repl/scala/tools/nsc/interpreter/IMain.scala

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,13 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
112112
def this(factory: ScriptEngineFactory) = this(factory, new Settings())
113113
def this() = this(new Settings())
114114

115-
lazy val formatting: Formatting = new Formatting {
116-
val prompt = Properties.shellPromptString
117-
}
115+
// the expanded prompt but without color escapes and without leading newline, for purposes of indenting
116+
lazy val formatting: Formatting = new Formatting(
117+
(replProps.promptString format Properties.versionNumberString).lines.toList.last.length
118+
)
118119
lazy val reporter: ReplReporter = new ReplReporter(this)
119120

120-
import formatting._
121+
import formatting.indentCode
121122
import reporter.{ printMessage, printUntruncatedMessage }
122123

123124
// This exists mostly because using the reporter too early leads to deadlock.
@@ -468,7 +469,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
468469
}
469470

470471
private def requestFromLine(line: String, synthetic: Boolean): Either[IR.Result, Request] = {
471-
val content = indentCode(line)
472+
val content = line //indentCode(line)
472473
val trees = parse(content) match {
473474
case parse.Incomplete => return Left(IR.Incomplete)
474475
case parse.Error => return Left(IR.Error)
@@ -909,10 +910,10 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
909910
else List("def %s = %s".format("$line", tquoted(originalLine)), "def %s = Nil".format("$trees"))
910911
}
911912
def preamble = s"""
912-
|$preambleHeader
913-
|%s%s%s
914-
""".stripMargin.format(lineRep.readName, envLines.map(" " + _ + ";\n").mkString,
915-
importsPreamble, indentCode(toCompute))
913+
|${preambleHeader format lineRep.readName}
914+
|${envLines mkString (" ", ";\n ", ";\n")}
915+
|$importsPreamble
916+
|${indentCode(toCompute)}""".stripMargin
916917

917918
val generate = (m: MemberHandler) => m extraCodeToEvaluate Request.this
918919

src/repl/scala/tools/nsc/interpreter/ReplProps.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ class ReplProps {
1717
val colorOk = bool("scala.color")
1818

1919
// Handy system prop for shell prompt, or else pick it up from compiler.properties
20+
val promptString = Prop[String]("scala.repl.prompt").option getOrElse Properties.shellPromptString
2021
val prompt = {
2122
import scala.io.AnsiColor.{ MAGENTA, RESET }
22-
val p = Prop[String]("scala.repl.prompt").option getOrElse Properties.shellPromptString
23-
val q = String.format(p, Properties.versionNumberString)
24-
if (colorOk) s"$MAGENTA$q$RESET" else q
23+
val p = promptString format Properties.versionNumberString
24+
if (colorOk) s"$MAGENTA$p$RESET" else p
2525
}
2626

2727
val info = bool("scala.repl.info")

test/files/jvm/interpreter.check

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ scala> val four: anotherint = 4
3232
four: anotherint = 4
3333

3434
scala> val bogus: anotherint = "hello"
35-
<console>:8: error: type mismatch;
35+
<console>:11: error: type mismatch;
3636
found : String("hello")
3737
required: anotherint
3838
(which expands to) Int
@@ -280,13 +280,13 @@ scala> // both of the following should abort immediately:
280280

281281
scala> def x => y => z
282282
<console>:1: error: '=' expected but '=>' found.
283-
def x => y => z
284-
^
283+
def x => y => z
284+
^
285285

286286
scala> [1,2,3]
287287
<console>:1: error: illegal start of definition
288-
[1,2,3]
289-
^
288+
[1,2,3]
289+
^
290290

291291
scala>
292292

@@ -355,7 +355,7 @@ defined class Term
355355
scala> def f(e: Exp) = e match { // non-exhaustive warning here
356356
case _:Fact => 3
357357
}
358-
<console>:18: warning: match may not be exhaustive.
358+
<console>:21: warning: match may not be exhaustive.
359359
It would fail on the following inputs: Exp(), Term()
360360
def f(e: Exp) = e match { // non-exhaustive warning here
361361
^
@@ -365,6 +365,6 @@ scala> :quit
365365
plusOne: (x: Int)Int
366366
res0: Int = 6
367367
res0: String = after reset
368-
<console>:8: error: not found: value plusOne
369-
plusOne(5) // should be undefined now
370-
^
368+
<console>:11: error: not found: value plusOne
369+
plusOne(5) // should be undefined now
370+
^

test/files/run/constrained-types.check

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,16 +135,16 @@ y: String = hello
135135
scala>
136136

137137
scala> val x = 3 : Int @Annot(e+f+g+h) // should have a graceful error message
138-
<console>:8: error: not found: value e
138+
<console>:11: error: not found: value e
139139
val x = 3 : Int @Annot(e+f+g+h) // should have a graceful error message
140140
^
141-
<console>:8: error: not found: value f
141+
<console>:11: error: not found: value f
142142
val x = 3 : Int @Annot(e+f+g+h) // should have a graceful error message
143143
^
144-
<console>:8: error: not found: value g
144+
<console>:11: error: not found: value g
145145
val x = 3 : Int @Annot(e+f+g+h) // should have a graceful error message
146146
^
147-
<console>:8: error: not found: value h
147+
<console>:11: error: not found: value h
148148
val x = 3 : Int @Annot(e+f+g+h) // should have a graceful error message
149149
^
150150

test/files/run/kind-repl-command.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ scala> :k new { def empty = false }
2121
AnyRef{def empty: Boolean}'s kind is A
2222

2323
scala> :k Nonexisting
24-
<console>:8: error: not found: value Nonexisting
25-
Nonexisting
26-
^
24+
<console>:11: error: not found: value Nonexisting
25+
Nonexisting
26+
^
2727

2828
scala> :quit

test/files/run/reify-repl-fail-gracefully.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import scala.reflect.runtime.universe._
1010
scala>
1111

1212
scala> reify
13-
<console>:12: error: too few argument lists for macro invocation
14-
reify
15-
^
13+
<console>:15: error: too few argument lists for macro invocation
14+
reify
15+
^
1616

1717
scala> :quit

test/files/run/reify_newimpl_22.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ scala> {
1717
}
1818
println(code.eval)
1919
}
20-
<console>:15: free term: Ident(TermName("x")) defined by res0 in <console>:14:21
21-
val code = reify {
22-
^
20+
<console>:18: free term: Ident(TermName("x")) defined by res0 in <console>:17:14
21+
val code = reify {
22+
^
2323
2
2424

2525
scala> :quit

test/files/run/reify_newimpl_23.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ scala> def foo[T]{
1616
}
1717
println(code.eval)
1818
}
19-
<console>:13: free type: Ident(TypeName("T")) defined by foo in <console>:12:16
19+
<console>:16: free type: Ident(TypeName("T")) defined by foo in <console>:15:16
2020
val code = reify {
2121
^
2222
foo: [T]=> Unit

test/files/run/reify_newimpl_25.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ scala> {
77
val tt = implicitly[TypeTag[x.type]]
88
println(tt)
99
}
10-
<console>:11: free term: Ident(TermName("x")) defined by res0 in <console>:10:21
11-
val tt = implicitly[TypeTag[x.type]]
12-
^
10+
<console>:14: free term: Ident(TermName("x")) defined by res0 in <console>:13:14
11+
val tt = implicitly[TypeTag[x.type]]
12+
^
1313
TypeTag[x.type]
1414

1515
scala> :quit

test/files/run/reify_newimpl_26.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ scala> def foo[T]{
66
val tt = implicitly[WeakTypeTag[List[T]]]
77
println(tt)
88
}
9-
<console>:9: free type: Ident(TypeName("T")) defined by foo in <console>:7:16
9+
<console>:12: free type: Ident(TypeName("T")) defined by foo in <console>:10:16
1010
val tt = implicitly[WeakTypeTag[List[T]]]
1111
^
1212
foo: [T]=> Unit

test/files/run/repl-bare-expr.check

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,33 @@ Type in expressions to have them evaluated.
22
Type :help for more information.
33

44
scala> 2 ; 3
5-
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
6-
2 ;;
7-
^
5+
<console>:10: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
6+
2 ;;
7+
^
88
res0: Int = 3
99

1010
scala> { 2 ; 3 }
11-
<console>:8: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
12-
{ 2 ; 3 }
13-
^
11+
<console>:11: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
12+
{ 2 ; 3 }
13+
^
1414
res1: Int = 3
1515

1616
scala> 5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
1717
1 +
1818
2 +
1919
3 } ; bippy+88+11
20-
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
21-
5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
22-
^
23-
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
24-
5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
25-
^
26-
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
27-
5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
28-
^
29-
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
30-
5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
31-
^
20+
<console>:10: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
21+
5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
22+
^
23+
<console>:10: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
24+
5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
25+
^
26+
<console>:10: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
27+
5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
28+
^
29+
<console>:10: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
30+
5 ; 10 ; case object Cow ; 20 ; class Moo { override def toString = "Moooooo" } ; 30 ; def bippy = {
31+
^
3232
defined object Cow
3333
defined class Moo
3434
bippy: Int

test/files/run/repl-colon-type.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ Type :help for more information.
33

44
scala> :type List[1, 2, 3]
55
<console>:1: error: identifier expected but integer literal found.
6-
List[1, 2, 3]
7-
^
6+
List[1, 2, 3]
7+
^
88

99
scala> :type List(1, 2, 3)
1010
List[Int]

test/files/run/repl-parens.check

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ scala> ( (2 + 2 ) )
2020
res5: Int = 4
2121

2222
scala> 5 ; ( (2 + 2 ) ) ; ((5))
23-
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
24-
5 ; ( (2 + 2 ) ) ;;
25-
^
26-
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
27-
5 ; ( (2 + 2 ) ) ;;
28-
^
23+
<console>:10: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
24+
5 ; ( (2 + 2 ) ) ;;
25+
^
26+
<console>:10: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
27+
5 ; ( (2 + 2 ) ) ;;
28+
^
2929
res6: Int = 5
3030

3131
scala> (((2 + 2)), ((2 + 2)))
@@ -40,18 +40,18 @@ res9: String = 4423
4040
scala>
4141

4242
scala> 55 ; ((2 + 2)) ; (1, 2, 3)
43-
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
44-
55 ; ((2 + 2)) ;;
45-
^
46-
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
47-
55 ; ((2 + 2)) ;;
48-
^
43+
<console>:10: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
44+
55 ; ((2 + 2)) ;;
45+
^
46+
<console>:10: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
47+
55 ; ((2 + 2)) ;;
48+
^
4949
res10: (Int, Int, Int) = (1,2,3)
5050

5151
scala> 55 ; (x: Int) => x + 1 ; () => ((5))
52-
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
53-
55 ; (x: Int) => x + 1 ;;
54-
^
52+
<console>:10: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
53+
55 ; (x: Int) => x + 1 ;;
54+
^
5555
res11: () => Int = <function0>
5656

5757
scala>
@@ -60,9 +60,9 @@ scala> () => 5
6060
res12: () => Int = <function0>
6161

6262
scala> 55 ; () => 5
63-
<console>:7: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
64-
55 ;;
65-
^
63+
<console>:10: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
64+
55 ;;
65+
^
6666
res13: () => Int = <function0>
6767

6868
scala> () => { class X ; new X }

test/files/run/repl-paste-2.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ scala> res5 + res6
4444
res1: Int = 690
4545

4646
scala> val x = dingus
47-
<console>:7: error: not found: value dingus
47+
<console>:10: error: not found: value dingus
4848
val x = dingus
4949
^
5050

0 commit comments

Comments
 (0)