From 864137e56823a2121120b112bc4240832609a251 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 21 Apr 2020 15:36:13 +0200 Subject: [PATCH 1/2] Ignore.metals files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a734c76e11ed..68aa7d385c31 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ node_modules # VS Code .vscode/ +.metals/ # Scala-IDE specific .scala_dependencies From f5b10b6f36250f1a5585ad7e6662f094899c5e5c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 Apr 2020 11:01:57 +0200 Subject: [PATCH 2/2] 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". --- .../dotty/tools/dotc/parsing/Parsers.scala | 2 +- .../dotty/tools/dotc/parsing/Scanners.scala | 19 +++++++++++-------- .../dotty/tools/dotc/util/SourceFile.scala | 9 ++++++++- .../src/dotty/tools/repl/ParseResult.scala | 4 ++-- .../src/dotty/tools/repl/ReplDriver.scala | 2 +- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 9dbb2a7efbb9..b757513b5fe1 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1288,7 +1288,7 @@ object Parsers { if in.token == COLONEOL then in.nextToken() if in.token != INDENT then - syntaxError(i"indented definitions expected") + syntaxErrorOrIncomplete(i"indented definitions expected") else newLineOptWhenFollowedBy(LBRACE) diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 4182c8795b2a..95d95683839b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -581,7 +581,7 @@ object Scanners { def observeColonEOL(): Unit = if token == COLON then lookahead() - val atEOL = isAfterLineEnd + val atEOL = isAfterLineEnd || token == EOF reset() if atEOL then token = COLONEOL @@ -618,6 +618,12 @@ object Scanners { this.copyFrom(prev) } + def closeIndented() = currentRegion match + case r: Indented if !r.isOutermost => + insert(OUTDENT, offset) + currentRegion = r.outer + case _ => + /** - Join CASE + CLASS => CASECLASS, CASE + OBJECT => CASEOBJECT, SEMI + ELSE => ELSE, COLON + => COLONEOL * - Insert missing OUTDENTs at EOF */ @@ -658,13 +664,10 @@ object Scanners { reset() case COLON => if colonSyntax then observeColonEOL() - case EOF | RBRACE | RPAREN | RBRACKET => - currentRegion match { - case r: Indented if !r.isOutermost => - insert(OUTDENT, offset) - currentRegion = r.outer - case _ => - } + case RBRACE | RPAREN | RBRACKET => + closeIndented() + case EOF if !source.maybeIncomplete => + closeIndented() case _ => } } diff --git a/compiler/src/dotty/tools/dotc/util/SourceFile.scala b/compiler/src/dotty/tools/dotc/util/SourceFile.scala index a8a6089d10b0..c4aff62cf707 100644 --- a/compiler/src/dotty/tools/dotc/util/SourceFile.scala +++ b/compiler/src/dotty/tools/dotc/util/SourceFile.scala @@ -48,6 +48,10 @@ class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends myContent } + private var _maybeInComplete: Boolean = false + + def maybeIncomplete: Boolean = _maybeInComplete + def this(file: AbstractFile, codec: Codec) = this(file, new String(file.toByteArray, codec.charSet).toCharArray) /** Tab increment; can be overridden */ @@ -200,7 +204,10 @@ object SourceFile { def fromId(id: Int): SourceFile = sourceOfChunk(id >> ChunkSizeLog) - def virtual(name: String, content: String) = new SourceFile(new VirtualFile(name, content.getBytes), scala.io.Codec.UTF8) + def virtual(name: String, content: String, maybeIncomplete: Boolean = false) = + val src = new SourceFile(new VirtualFile(name, content.getBytes), scala.io.Codec.UTF8) + src._maybeInComplete = maybeIncomplete + src private final val ChunkSizeLog = 10 private final val ChunkSize = 1 << ChunkSizeLog diff --git a/compiler/src/dotty/tools/repl/ParseResult.scala b/compiler/src/dotty/tools/repl/ParseResult.scala index 12cf55354740..541e965e9df9 100644 --- a/compiler/src/dotty/tools/repl/ParseResult.scala +++ b/compiler/src/dotty/tools/repl/ParseResult.scala @@ -147,7 +147,7 @@ object ParseResult { } def apply(sourceCode: String)(implicit state: State): ParseResult = - apply(SourceFile.virtual(str.REPL_SESSION_LINE + (state.objectIndex + 1), sourceCode)) + apply(SourceFile.virtual(str.REPL_SESSION_LINE + (state.objectIndex + 1), sourceCode, maybeIncomplete = true)) /** Check if the input is incomplete. * @@ -159,7 +159,7 @@ object ParseResult { case CommandExtract(_) | "" => false case _ => { val reporter = newStoreReporter - val source = SourceFile.virtual("", sourceCode) + val source = SourceFile.virtual("", sourceCode, maybeIncomplete = true) val unit = CompilationUnit(source, mustExist = false) val localCtx = ctx.fresh .setCompilationUnit(unit) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 2e15177477d8..3be769b02cb1 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -176,7 +176,7 @@ class ReplDriver(settings: Array[String], compiler .typeCheck(expr, errorsAllowed = true) .map { tree => - val file = SourceFile.virtual("", expr) + val file = SourceFile.virtual("", expr, maybeIncomplete = true) val unit = CompilationUnit(file)(state.context) unit.tpdTree = tree implicit val ctx = state.context.fresh.setCompilationUnit(unit)