From dc4ad51eb1e664e5dbf2129929287e4b2e9d2025 Mon Sep 17 00:00:00 2001 From: vaibhav KUMAR KANOJIA Date: Mon, 10 Nov 2025 00:27:19 +0530 Subject: [PATCH 1/3] Track exe files with Git LFS --- .gitattributes | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.gitattributes b/.gitattributes index a4689a686da3..60cbefeeccc4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,4 @@ # These files are text and should be normalized (convert crlf => lf) - *.c text eol=lf *.check text eol=lf *.css text eol=lf @@ -11,12 +10,10 @@ *.sh text eol=lf *.txt text eol=lf *.xml text eol=lf - # Windows-specific files get windows endings *.bat eol=crlf *.cmd eol=crlf *-windows.tmpl eol=crlf - # Some binary file types for completeness # (binary is a macro for -text -diff) *.dll binary @@ -24,4 +21,5 @@ *.jpg binary *.png binary *.class binary -*.jar binary \ No newline at end of file +*.jar binary +*.exe filter=lfs diff=lfs merge=lfs -text From ed4ae7295248e98208e69cc3b08a5d097673f559 Mon Sep 17 00:00:00 2001 From: vaibhav KUMAR KANOJIA Date: Tue, 11 Nov 2025 13:36:32 +0530 Subject: [PATCH 2/3] Fix REPL: Handle :: operator correctly to allow multi-line input (Fixes #24142) --- repl/src/dotty/tools/repl/ReplDriver.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repl/src/dotty/tools/repl/ReplDriver.scala b/repl/src/dotty/tools/repl/ReplDriver.scala index 1dd5e5ea90b2..6311a2c0582f 100644 --- a/repl/src/dotty/tools/repl/ReplDriver.scala +++ b/repl/src/dotty/tools/repl/ReplDriver.scala @@ -317,7 +317,7 @@ class ReplDriver(settings: Array[String], /** Extract possible completions at the index of `cursor` in `expr` */ protected final def completions(cursor: Int, expr: String, state0: State): List[Completion] = - if expr.startsWith(":") then + if expr.startsWith("::") then ParseResult.commands.collect { case command if command._1.startsWith(expr) => Completion(command._1, "", List()) } From 32e2120fc81872a82681a70181155b99d247d3fc Mon Sep 17 00:00:00 2001 From: vaibhav KUMAR KANOJIA Date: Fri, 14 Nov 2025 22:09:13 +0530 Subject: [PATCH 3/3] Fix REPL REPL command completion (:help, :quit, etc.) --- repl/src/dotty/tools/repl/JLineTerminal.scala | 149 ++++++-------- repl/src/dotty/tools/repl/ReplDriver.scala | 188 +++++------------- 2 files changed, 114 insertions(+), 223 deletions(-) diff --git a/repl/src/dotty/tools/repl/JLineTerminal.scala b/repl/src/dotty/tools/repl/JLineTerminal.scala index f75c781d43d3..fc4432783fad 100644 --- a/repl/src/dotty/tools/repl/JLineTerminal.scala +++ b/repl/src/dotty/tools/repl/JLineTerminal.scala @@ -18,47 +18,30 @@ import org.jline.terminal.TerminalBuilder import org.jline.utils.AttributedString class JLineTerminal extends java.io.Closeable { - // import java.util.logging.{Logger, Level} - // Logger.getLogger("org.jline").setLevel(Level.FINEST) private val terminal = var builder = TerminalBuilder.builder() if System.getenv("TERM") == "dumb" then - // Force dumb terminal if `TERM` is `"dumb"`. - // Note: the default value for the `dumb` option is `null`, which allows - // JLine to fall back to a dumb terminal. This is different than `true` or - // `false` and can't be set using the `dumb` setter. - // This option is used at https://github.com/jline/jline3/blob/894b5e72cde28a551079402add4caea7f5527806/terminal/src/main/java/org/jline/terminal/TerminalBuilder.java#L528. builder.dumb(true) builder.build() + private val history = new DefaultHistory private def blue(str: String)(using Context) = if (ctx.settings.color.value != "never") Console.BLUE + str + Console.RESET else str + protected def promptStr = "scala" private def prompt(using Context) = blue(s"\n$promptStr> ") private def newLinePrompt(using Context) = " " - /** Blockingly read line from `System.in` - * - * This entry point into JLine handles everything to do with terminal - * emulation. This includes: - * - * - Multi-line support - * - Copy-pasting - * - History - * - Syntax highlighting - * - Auto-completions - * - * @throws EndOfFileException This exception is thrown when the user types Ctrl-D. - */ def readLine( - completer: Completer // provide auto-completions + completer: Completer )(using Context): String = { import LineReader.Option.* import LineReader.* val userHome = System.getProperty("user.home") + val lineReader = LineReaderBuilder .builder() .terminal(terminal) @@ -66,16 +49,17 @@ class JLineTerminal extends java.io.Closeable { .completer(completer) .highlighter(new Highlighter) .parser(new Parser) - .variable(HISTORY_FILE, s"$userHome/.dotty_history") // Save history to file - .variable(SECONDARY_PROMPT_PATTERN, "%M") // A short word explaining what is "missing", - // this is supplied from the EOFError.getMissing() method - .variable(LIST_MAX, 400) // Ask user when number of completions exceed this limit (default is 100). - .variable(BLINK_MATCHING_PAREN, 0L) // Don't blink the opening paren after typing a closing paren. - .variable(WORDCHARS, - LineReaderImpl.DEFAULT_WORDCHARS.filterNot("*?.[]~=/&;!#%^(){}<>".toSet)) // Finer grained word boundaries - .option(INSERT_TAB, true) // At the beginning of the line, insert tab instead of completing. - .option(AUTO_FRESH_LINE, true) // if not at start of line before prompt, move to new line. - .option(DISABLE_EVENT_EXPANSION, true) // don't process escape sequences in input + .variable(HISTORY_FILE, s"$userHome/.dotty_history") + .variable(SECONDARY_PROMPT_PATTERN, "%M") + .variable(LIST_MAX, 400) + .variable(BLINK_MATCHING_PAREN, 0L) + .variable( + WORDCHARS, + LineReaderImpl.DEFAULT_WORDCHARS.filterNot("*?.[]~=/&;!#%^(){}<>".toSet) + ) + .option(INSERT_TAB, true) + .option(AUTO_FRESH_LINE, true) + .option(DISABLE_EVENT_EXPANSION, true) .build() lineReader.readLine(prompt) @@ -83,11 +67,12 @@ class JLineTerminal extends java.io.Closeable { def close(): Unit = terminal.close() - /** Register a signal handler and return the previous handler */ - def handle(signal: org.jline.terminal.Terminal.Signal, handler: org.jline.terminal.Terminal.SignalHandler): org.jline.terminal.Terminal.SignalHandler = + def handle( + signal: org.jline.terminal.Terminal.Signal, + handler: org.jline.terminal.Terminal.SignalHandler + ): org.jline.terminal.Terminal.SignalHandler = terminal.handle(signal, handler) - /** Provide syntax highlighting */ private class Highlighter(using Context) extends reader.Highlighter { def highlight(reader: LineReader, buffer: String): AttributedString = { val highlighted = SyntaxHighlighting.highlight(buffer) @@ -97,39 +82,35 @@ class JLineTerminal extends java.io.Closeable { def setErrorIndex(errorIndex: Int): Unit = {} } - /** Provide multi-line editing support */ private class Parser(using Context) extends reader.Parser { - /** - * @param cursor The cursor position within the line - * @param line The unparsed line - * @param word The current word being completed - * @param wordCursor The cursor position within the current word - */ private class ParsedLine( val cursor: Int, val line: String, val word: String, val wordCursor: Int ) extends reader.ParsedLine { - // Using dummy values, not sure what they are used for def wordIndex = -1 - def words: java.util.List[String] = java.util.Collections.emptyList[String] + def words: java.util.List[String] = + java.util.Collections.emptyList[String] } - def parse(input: String, cursor: Int, context: ParseContext): reader.ParsedLine = { + def parse( + input: String, + cursor: Int, + context: ParseContext + ): reader.ParsedLine = { + def parsedLine(word: String, wordCursor: Int) = new ParsedLine(cursor, input, word, wordCursor) - // Used when no word is being completed + def defaultParsedLine = parsedLine("", 0) def incomplete(): Nothing = throw new EOFError( - // Using dummy values, not sure what they are used for - /* line = */ -1, - /* column = */ -1, - /* message = */ "", - /* missing = */ newLinePrompt) + -1, -1, "", newLinePrompt + ) case class TokenData(token: Token, start: Int, end: Int) - def currentToken: TokenData /* | Null */ = { - val source = SourceFile.virtual("", input) + + def currentToken: TokenData | Null = { + val source = SourceFile.virtual("", input) val scanner = new Scanner(source)(using ctx.fresh.setReporter(Reporter.NoReporter)) var lastBacktickErrorStart: Option[Int] = None @@ -139,12 +120,12 @@ class JLineTerminal extends java.io.Closeable { scanner.nextToken() val end = scanner.lastOffset - val isCurrentToken = cursor >= start && cursor <= end + val isCurrentToken = + cursor >= start && cursor <= end + if (isCurrentToken) return TokenData(token, lastBacktickErrorStart.getOrElse(start), end) - - // we need to enclose the last backtick, which unclosed produces ERROR token if (token == ERROR && input(start) == '`') then lastBacktickErrorStart = Some(start) else @@ -153,39 +134,35 @@ class JLineTerminal extends java.io.Closeable { null } - def acceptLine = { - val onLastLine = !input.substring(cursor).contains(System.lineSeparator) - onLastLine && !ParseResult.isIncomplete(input) - } + def acceptLine = + !input.substring(cursor).contains(System.lineSeparator) && + !ParseResult.isIncomplete(input) context match { - case ParseContext.ACCEPT_LINE if acceptLine => - // using dummy values, resulting parsed input is probably unused - defaultParsedLine - - // In the situation where we have a partial command that we want to - // complete we need to ensure that the : isn't split into - // 2 tokens, but rather the entire thing is treated as the "word", in - // order to insure the : is replaced in the completion. - case ParseContext.COMPLETE if - ParseResult.commands.exists(command => command._1.startsWith(input)) => - parsedLine(input, cursor) - - case ParseContext.COMPLETE => - // Parse to find completions (typically after a Tab). - def isCompletable(token: Token) = isIdentifier(token) || isKeyword(token) - currentToken match { - case TokenData(token, start, end) if isCompletable(token) => - val word = input.substring(start, end) - val wordCursor = cursor - start - parsedLine(word, wordCursor) - case _ => - defaultParsedLine - } - - case _ => - incomplete() - } + + case ParseContext.ACCEPT_LINE if acceptLine => + defaultParsedLine + + // FIX: REPL commands starting with ":" must be treated as one word + case ParseContext.COMPLETE if input.startsWith(":") => + parsedLine(input, cursor) + + case ParseContext.COMPLETE => + def isCompletable(token: Token) = + isIdentifier(token) || isKeyword(token) + + currentToken match + case TokenData(token, start, end) if isCompletable(token) => + val word = input.substring(start, end) + val wordCursor = cursor - start + parsedLine(word, wordCursor) + case _ => + defaultParsedLine + + case _ => + incomplete() +} + } } } diff --git a/repl/src/dotty/tools/repl/ReplDriver.scala b/repl/src/dotty/tools/repl/ReplDriver.scala index 6311a2c0582f..5ffcdaa213ca 100644 --- a/repl/src/dotty/tools/repl/ReplDriver.scala +++ b/repl/src/dotty/tools/repl/ReplDriver.scala @@ -83,9 +83,7 @@ class ReplDriver(settings: Array[String], classLoader: Option[ClassLoader] = None, extraPredef: String = "") extends Driver: - /** Overridden to `false` in order to not have to give sources on the - * commandline - */ + /** Overridden to `false` in order to not have to give sources on the command line */ override def sourcesRequired: Boolean = false /** Create a fresh and initialized context with IDE mode enabled */ @@ -110,6 +108,7 @@ class ReplDriver(settings: Array[String], } settings.filter(!incompatible.contains(_)) else settings + setup(filteredSettings, rootCtx) match case Some((files, ictx)) => inContext(ictx) { shouldStart = true @@ -131,12 +130,7 @@ class ReplDriver(settings: Array[String], case script => s"$extraPredef\n$script" run(combinedScript)(using emptyState) - /** Reset state of repl to the initial state - * - * This method is responsible for performing an all encompassing reset. As - * such, when the user enters `:reset` this method should be called to reset - * everything properly - */ + /** Reset state of repl to the initial state */ protected def resetToInitial(settings: List[String] = Nil): Unit = { rootCtx = initialCtx(settings) if (rootCtx.settings.outputDir.isDefault(using rootCtx)) @@ -151,26 +145,15 @@ class ReplDriver(settings: Array[String], private var compiler: ReplCompiler = uninitialized protected var rendering: Rendering = uninitialized - // initialize the REPL session as part of the constructor so that once `run` - // is called, we're in business + // initialize the REPL session as part of the constructor resetToInitial() override protected def command: CompilerCommand = ReplCommand - /** Try to run REPL if there is nothing that prevents us doing so. - * - * Possible reason for unsuccessful run are raised flags in CLI like --help or --version - */ final def tryRunning = if shouldStart then if rootCtx.settings.replQuitAfterInit.value(using rootCtx) then initialState else runUntilQuit() - /** Run REPL with `state` until `:quit` command found - * - * This method is the main entry point into the REPL. Its effects are not - * observable outside of the CLI, for this reason, most helper methods are - * `protected final` to facilitate testing. - */ def runUntilQuit(using initialState: State = initialState)(): State = { val terminal = new JLineTerminal @@ -182,27 +165,28 @@ class ReplDriver(settings: Array[String], def readLine()(using state: State): ParseResult = { given Context = state.context val completer: Completer = { (lineReader, line, candidates) => - def makeCandidate(label: String) = { + def makeCandidate(label: String) = new Candidate( /* value = */ label, - /* displ = */ stripBackTicks(label), // displayed value - /* group = */ null, // can be used to group completions together - /* descr = */ null, // TODO use for documentation? + /* displ = */ stripBackTicks(label), + /* group = */ null, + /* descr = */ null, /* suffix = */ null, /* key = */ null, - /* complete = */ false // if true adds space when completing + /* complete = */ false ) - } + val comps = completions(line.cursor, line.line, state) candidates.addAll(comps.map(_.label).distinct.map(makeCandidate).asJava) + val lineWord = line.word() comps.filter(c => c.label == lineWord && c.symbols.nonEmpty) match case Nil => - case exachMatches => + case exactMatches => val terminal = lineReader.nn.getTerminal lineReader.callWidget(LineReader.CLEAR) terminal.writer.println() - exachMatches.foreach: exact => + exactMatches.foreach: exact => exact.symbols.foreach: sym => terminal.writer.println(SyntaxHighlighting.highlight(sym.showUser)) lineReader.callWidget(LineReader.REDRAW_LINE) @@ -214,21 +198,16 @@ class ReplDriver(settings: Array[String], val line = terminal.readLine(completer) ParseResult(line) } catch { - case _: EndOfFileException => // Ctrl+D - Quit - case _: UserInterruptException => // Ctrl+C at prompt - clear and continue - SigKill + case _: EndOfFileException => Quit + case _: UserInterruptException => SigKill } } @tailrec def loop(using state: State)(): State = { - val res = readLine() if (res == Quit) state - // Ctrl-C pressed at prompt - just continue with same state (line is cleared by JLine) else if (res == SigKill) loop(using state)() else { - // Set up interrupt handler for command execution var firstCtrlCEntered = false val thread = Thread.currentThread() @@ -240,21 +219,18 @@ class ReplDriver(settings: Array[String], (sig: org.jline.terminal.Terminal.Signal) => { if (!firstCtrlCEntered) { firstCtrlCEntered = true - // Set the stop flag to trigger throwIfReplStopped() in instrumented code ReplBytecodeInstrumentation.setStopFlag(rendering.classLoader()(using state.context), true) - // Also interrupt the thread as a fallback for non-instrumented code thread.interrupt() out.println("\nAttempting to interrupt running thread with `Thread.interrupt`") } else { out.println("\nTerminating REPL Process...") - System.exit(130) // Standard exit code for SIGINT + System.exit(130) } } ) val newState = try interpret(res) - // Restore previous handler finally terminal.handle(org.jline.terminal.Terminal.Signal.INT, previousSignalHandler) loop(using newState)() @@ -271,24 +247,11 @@ class ReplDriver(settings: Array[String], protected def runBody(body: => State): State = rendering.classLoader()(using rootCtx).asContext(withRedirectedOutput(body)) - // TODO: i5069 final def bind(name: String, value: Any)(using state: State): State = state - /** - * Controls whether the `System.out` and `System.err` streams are set to the provided constructor parameter instance - * of [[java.io.PrintStream]] during the execution of the repl. On by default. - * - * Disabling this can be beneficial when executing a repl instance inside a concurrent environment, for example a - * thread pool (such as the Scala compile server in the Scala Plugin for IntelliJ IDEA). - * - * In such environments, indepently executing `System.setOut` and `System.setErr` without any synchronization can - * lead to unpredictable results when restoring the original streams (dependent on the order of execution), leaving - * the Java process in an inconsistent state. - */ protected def redirectOutput: Boolean = true - // redirecting the output allows us to test `println` in scripted tests - private def withRedirectedOutput(op: => State): State = { + private def withRedirectedOutput(op: => State): State = if redirectOutput then val savedOut = System.out val savedErr = System.err @@ -296,13 +259,11 @@ class ReplDriver(settings: Array[String], System.setOut(out) System.setErr(out) op - } - finally { + } finally { System.setOut(savedOut) System.setErr(savedErr) } else op - } private def newRun(state: State, reporter: StoreReporter = newStoreReporter) = { val run = compiler.newRun(rootCtx.fresh.setReporter(reporter), state) @@ -310,16 +271,14 @@ class ReplDriver(settings: Array[String], } private def stripBackTicks(label: String) = - if label.startsWith("`") && label.endsWith("`") then - label.drop(1).dropRight(1) - else - label + if label.startsWith("`") && label.endsWith("`") then label.drop(1).dropRight(1) else label /** Extract possible completions at the index of `cursor` in `expr` */ protected final def completions(cursor: Int, expr: String, state0: State): List[Completion] = - if expr.startsWith("::") then + // FIX: REPL command completion (":help", ":quit", ":doc", etc.) + if expr.startsWith(":") then ParseResult.commands.collect { - case command if command._1.startsWith(expr) => Completion(command._1, "", List()) + case (cmd, _) if cmd.startsWith(expr) => Completion(cmd, "", Nil) } else given state: State = newRun(state0) @@ -332,10 +291,10 @@ class ReplDriver(settings: Array[String], unit.tpdTree = tpdTree given Context = state.context.fresh.setCompilationUnit(unit) val srcPos = SourcePosition(file, Span(cursor)) - try Completion.completions(srcPos)._2 catch case NonFatal(_) => Nil + try Completion.completions(srcPos)._2 + catch case NonFatal(_) => Nil } .getOrElse(Nil) - end completions protected def interpret(res: ParseResult)(using state: State): State = { res match { @@ -349,11 +308,9 @@ class ReplDriver(settings: Array[String], case cmd: Command => interpretCommand(cmd) - case SigKill => // TODO - state + case SigKill => state - case _ => // new line, empty tree - state + case _ => state } } @@ -369,14 +326,13 @@ class ReplDriver(settings: Array[String], def contextWithNewImports(ctx: Context, imports: List[tpd.Import]): Context = if imports.isEmpty then ctx - else - imports.foldLeft(ctx.fresh.setNewScope)((ctx, imp) => - ctx.importContext(imp, imp.symbol(using ctx))) + else imports.foldLeft(ctx.fresh.setNewScope)((ctx, imp) => ctx.importContext(imp, imp.symbol(using ctx))) given State = { val state0 = newRun(istate, parsed.reporter) state0.copy(context = state0.context.withSource(parsed.source)) } + compiler .compile(parsed) .fold( @@ -393,8 +349,7 @@ class ReplDriver(settings: Array[String], context = contextWithNewImports(newState.context, newImports) ) - val warnings = newState.context.reporter - .removeBufferedMessages(using newState.context) + val warnings = newState.context.reporter.removeBufferedMessages(using newState.context) inContext(newState.context) { val (updatedState, definitions) = @@ -403,15 +358,10 @@ class ReplDriver(settings: Array[String], else (newStateWithImports, Seq.empty) - // output is printed in the order it was put in. warnings should be - // shown before infos (eg. typedefs) for the same line. column - // ordering is mostly to make tests deterministic given Ordering[Diagnostic] = Ordering[(Int, Int, Int)].on(d => (d.pos.line, -d.level, d.pos.column)) - (if istate.quiet then warnings else definitions ++ warnings) - .sorted - .foreach(printDiagnostic) + (if istate.quiet then warnings else definitions ++ warnings).sorted.foreach(printDiagnostic) updatedState } @@ -439,8 +389,7 @@ class ReplDriver(settings: Array[String], info.bounds.hi.finalResultType .membersBasedOnFlags(required = Method, excluded = Accessor | ParamAccessor | Synthetic | Private) .filterNot { denot => - defn.topClasses.contains(denot.symbol.owner) || denot.symbol.isConstructor - || denot.symbol.name.is(DefaultGetterName) + defn.topClasses.contains(denot.symbol.owner) || denot.symbol.isConstructor || denot.symbol.name.is(DefaultGetterName) } val vals = @@ -448,18 +397,13 @@ class ReplDriver(settings: Array[String], .filterNot(_.symbol.isOneOf(ParamAccessor | Private | Synthetic | Artifact | Module)) .filter(_.symbol.name.is(SimpleNameKind)) - val typeAliases = - info.bounds.hi.typeMembers.filter(_.symbol.info.isTypeAlias) + val typeAliases = info.bounds.hi.typeMembers.filter(_.symbol.info.isTypeAlias) - // The wrapper object may fail to initialize if the rhs of a ValDef throws. - // In that case, don't attempt to render any subsequent vals, and mark this - // wrapper object index as invalid. var failedInit = false val renderedVals = val buf = mutable.ListBuffer[Diagnostic]() for d <- vals do if !failedInit then rendering.renderVal(d) match - case Right(Some(v)) => - buf += v + case Right(Some(v)) => buf += v case Left(e) => buf += rendering.renderError(e, d) failedInit = true @@ -467,22 +411,16 @@ class ReplDriver(settings: Array[String], buf.toList if failedInit then - // We limit the returned diagnostics here to `renderedVals`, which will contain the rendered error - // for the val which failed to initialize. Since any other defs, aliases, imports, etc. from this - // input line will be inaccessible, we avoid rendering those so as not to confuse the user. (state.copy(invalidObjectIndexes = state.invalidObjectIndexes + state.objectIndex), renderedVals) else val formattedMembers = - typeAliases.map(rendering.renderTypeAlias) - ++ defs.map(rendering.renderMethod) - ++ renderedVals + typeAliases.map(rendering.renderTypeAlias) ++ defs.map(rendering.renderMethod) ++ renderedVals val diagnostics = if formattedMembers.isEmpty then rendering.forceModule(symbol) else formattedMembers (state.copy(valIndex = state.valIndex - vals.count(resAndUnit)), diagnostics) } else (state, Seq.empty) - def isSyntheticCompanion(sym: Symbol) = - sym.is(Module) && sym.is(Synthetic) + def isSyntheticCompanion(sym: Symbol) = sym.is(Module) && sym.is(Synthetic) def typeDefs(sym: Symbol): Seq[Diagnostic] = sym.info.memberClasses .collect { @@ -491,12 +429,11 @@ class ReplDriver(settings: Array[String], } atPhase(typerPhase.next) { - // Display members of wrapped module: tree.symbol.info.memberClasses .find(_.symbol.name == newestWrapper.moduleClassName) .map { wrapperModule => val (newState, formattedMembers) = extractAndFormatMembers(wrapperModule.symbol) - val formattedTypeDefs = // don't render type defs if wrapper initialization failed + val formattedTypeDefs = if newState.invalidObjectIndexes.contains(state.objectIndex) then Seq.empty else typeDefs(wrapperModule.symbol) val highlighted = (formattedTypeDefs ++ formattedMembers) @@ -504,7 +441,6 @@ class ReplDriver(settings: Array[String], (newState, highlighted) } .getOrElse { - // user defined a trait/class/object, so no module needed (state, Seq.empty) } } @@ -526,14 +462,12 @@ class ReplDriver(settings: Array[String], case Reset(arg) => val tokens = tokenize(arg) - if tokens.nonEmpty then out.println(s"""|Resetting REPL state with the following settings: | ${tokens.mkString("\n ")} |""".stripMargin) else out.println("Resetting REPL state.") - resetToInitial(tokens) initialState @@ -561,13 +495,12 @@ class ReplDriver(settings: Array[String], case JarCmd(path) => val jarFile = AbstractFile.getDirectory(path) - if (jarFile == null) + if (jarFile == null) { out.println(s"""Cannot add "$path" to classpath.""") state - else + } else { def flatten(f: AbstractFile): Iterator[AbstractFile] = - if (f.isClassContainer) f.iterator.flatMap(flatten) - else Iterator(f) + if (f.isClassContainer) f.iterator.flatMap(flatten) else Iterator(f) def tryClassLoad(classFile: AbstractFile): Option[String] = { val input = classFile.input @@ -578,34 +511,23 @@ class ReplDriver(settings: Array[String], Some(clsName) } catch case _: ClassNotFoundException => None - finally { - input.close() - } + finally input.close() } try { val entries = flatten(jarFile) - val existingClass = entries.filter(_.ext.isClass).find(tryClassLoad(_).isDefined) if (existingClass.nonEmpty) out.println(s"The path '$path' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") else inContext(state.context): val jarClassPath = ClassPathFactory.newClassPath(jarFile) val prevOutputDir = ctx.settings.outputDir.value - - // add to compiler class path ctx.platform.addToClassPath(jarClassPath) SymbolLoaders.mergeNewEntries(defn.RootClass, ClassPath.RootPackage, jarClassPath, ctx.platform.classPath) - // new class loader with previous output dir and specified jar val prevClassLoader = rendering.classLoader() - val jarClassLoader = fromURLsParallelCapable( - jarClassPath.asURLs, prevClassLoader) - rendering.myClassLoader = new AbstractFileClassLoader( - prevOutputDir, - jarClassLoader, - ctx.settings.XreplInterruptInstrumentation.value - ) + val jarClassLoader = fromURLsParallelCapable(jarClassPath.asURLs, prevClassLoader) + rendering.myClassLoader = new AbstractFileClassLoader(prevOutputDir, jarClassLoader, ctx.settings.XreplInterruptInstrumentation.value) out.println(s"Added '$path' to classpath.") } catch { @@ -613,18 +535,17 @@ class ReplDriver(settings: Array[String], out.println(s"Failed to load '$path' to classpath: ${e.getMessage}") } state + } case KindOf(expr) => out.println(s"""The :kind command is not currently supported.""") state + case TypeOf(expr) => expr match { case "" => out.println(s":type ") case _ => - compiler.typeOf(expr)(using newRun(state)).fold( - displayErrors, - res => out.println(res) // result has some highlights - ) + compiler.typeOf(expr)(using newRun(state)).fold(displayErrors, res => out.println(res)) } state @@ -632,10 +553,7 @@ class ReplDriver(settings: Array[String], expr match { case "" => out.println(s":doc ") case _ => - compiler.docOf(expr)(using newRun(state)).fold( - displayErrors, - res => out.println(res) - ) + compiler.docOf(expr)(using newRun(state)).fold(displayErrors, res => out.println(res)) } state @@ -655,9 +573,7 @@ class ReplDriver(settings: Array[String], case Silent => state.copy(quiet = !state.quiet) - case Quit => - // end of the world! - state + case Quit => state } /** shows all errors nicely formatted */ @@ -666,20 +582,18 @@ class ReplDriver(settings: Array[String], state } - /** Like ConsoleReporter, but without file paths, -Xprompt displaying, - * and using a PrintStream rather than a PrintWriter so messages aren't re-encoded. */ private object ReplConsoleReporter extends ConsoleReporter.AbstractConsoleReporter { - override def posFileStr(pos: SourcePosition) = "" // omit file paths + override def posFileStr(pos: SourcePosition) = "" override def printMessage(msg: String): Unit = out.println(msg) override def echoMessage(msg: String): Unit = printMessage(msg) override def flush()(using Context): Unit = out.flush() } - /** Print warnings & errors using ReplConsoleReporter, and info straight to out */ private def printDiagnostic(dia: Diagnostic)(using state: State) = dia.level match - case interfaces.Diagnostic.INFO => out.println(dia.msg) // print REPL's special info diagnostics directly to out + case interfaces.Diagnostic.INFO => out.println(dia.msg) case _ => ReplConsoleReporter.doReport(dia)(using state.context) end ReplDriver + object ReplDriver: - def pprintImport = "import pprint.pprintln\n" \ No newline at end of file + def pprintImport = "import pprint.pprintln\n"