Skip to content

Commit f5b10b6

Browse files
committed
Fix #8762: Make REPL work with indented code
This is great even if we do not use optional braces. For instance, now you can write: ```scala scala> if c == 1 then "yes" else "no" ``` Previously, it would have stopped you after the "yes".
1 parent 864137e commit f5b10b6

File tree

5 files changed

+23
-13
lines changed

5 files changed

+23
-13
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1288,7 +1288,7 @@ object Parsers {
12881288
if in.token == COLONEOL then
12891289
in.nextToken()
12901290
if in.token != INDENT then
1291-
syntaxError(i"indented definitions expected")
1291+
syntaxErrorOrIncomplete(i"indented definitions expected")
12921292
else
12931293
newLineOptWhenFollowedBy(LBRACE)
12941294

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ object Scanners {
581581
def observeColonEOL(): Unit =
582582
if token == COLON then
583583
lookahead()
584-
val atEOL = isAfterLineEnd
584+
val atEOL = isAfterLineEnd || token == EOF
585585
reset()
586586
if atEOL then token = COLONEOL
587587

@@ -618,6 +618,12 @@ object Scanners {
618618
this.copyFrom(prev)
619619
}
620620

621+
def closeIndented() = currentRegion match
622+
case r: Indented if !r.isOutermost =>
623+
insert(OUTDENT, offset)
624+
currentRegion = r.outer
625+
case _ =>
626+
621627
/** - Join CASE + CLASS => CASECLASS, CASE + OBJECT => CASEOBJECT, SEMI + ELSE => ELSE, COLON + <EOL> => COLONEOL
622628
* - Insert missing OUTDENTs at EOF
623629
*/
@@ -658,13 +664,10 @@ object Scanners {
658664
reset()
659665
case COLON =>
660666
if colonSyntax then observeColonEOL()
661-
case EOF | RBRACE | RPAREN | RBRACKET =>
662-
currentRegion match {
663-
case r: Indented if !r.isOutermost =>
664-
insert(OUTDENT, offset)
665-
currentRegion = r.outer
666-
case _ =>
667-
}
667+
case RBRACE | RPAREN | RBRACKET =>
668+
closeIndented()
669+
case EOF if !source.maybeIncomplete =>
670+
closeIndented()
668671
case _ =>
669672
}
670673
}

compiler/src/dotty/tools/dotc/util/SourceFile.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends
4848
myContent
4949
}
5050

51+
private var _maybeInComplete: Boolean = false
52+
53+
def maybeIncomplete: Boolean = _maybeInComplete
54+
5155
def this(file: AbstractFile, codec: Codec) = this(file, new String(file.toByteArray, codec.charSet).toCharArray)
5256

5357
/** Tab increment; can be overridden */
@@ -200,7 +204,10 @@ object SourceFile {
200204

201205
def fromId(id: Int): SourceFile = sourceOfChunk(id >> ChunkSizeLog)
202206

203-
def virtual(name: String, content: String) = new SourceFile(new VirtualFile(name, content.getBytes), scala.io.Codec.UTF8)
207+
def virtual(name: String, content: String, maybeIncomplete: Boolean = false) =
208+
val src = new SourceFile(new VirtualFile(name, content.getBytes), scala.io.Codec.UTF8)
209+
src._maybeInComplete = maybeIncomplete
210+
src
204211

205212
private final val ChunkSizeLog = 10
206213
private final val ChunkSize = 1 << ChunkSizeLog

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ object ParseResult {
147147
}
148148

149149
def apply(sourceCode: String)(implicit state: State): ParseResult =
150-
apply(SourceFile.virtual(str.REPL_SESSION_LINE + (state.objectIndex + 1), sourceCode))
150+
apply(SourceFile.virtual(str.REPL_SESSION_LINE + (state.objectIndex + 1), sourceCode, maybeIncomplete = true))
151151

152152
/** Check if the input is incomplete.
153153
*
@@ -159,7 +159,7 @@ object ParseResult {
159159
case CommandExtract(_) | "" => false
160160
case _ => {
161161
val reporter = newStoreReporter
162-
val source = SourceFile.virtual("<incomplete-handler>", sourceCode)
162+
val source = SourceFile.virtual("<incomplete-handler>", sourceCode, maybeIncomplete = true)
163163
val unit = CompilationUnit(source, mustExist = false)
164164
val localCtx = ctx.fresh
165165
.setCompilationUnit(unit)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ class ReplDriver(settings: Array[String],
176176
compiler
177177
.typeCheck(expr, errorsAllowed = true)
178178
.map { tree =>
179-
val file = SourceFile.virtual("<completions>", expr)
179+
val file = SourceFile.virtual("<completions>", expr, maybeIncomplete = true)
180180
val unit = CompilationUnit(file)(state.context)
181181
unit.tpdTree = tree
182182
implicit val ctx = state.context.fresh.setCompilationUnit(unit)

0 commit comments

Comments
 (0)