Skip to content

Commit ff9d7ed

Browse files
authored
Merge pull request #5320 from dotty-staging/worksheet
Remove Worksheet dependency on JLine
2 parents cedef0d + 3865ba6 commit ff9d7ed

File tree

10 files changed

+81
-109
lines changed

10 files changed

+81
-109
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ import org.jline.reader.impl.history.DefaultHistory
1313
import org.jline.terminal.TerminalBuilder
1414
import org.jline.utils.AttributedString
1515

16-
final class JLineTerminal(needsTerminal: Boolean) extends java.io.Closeable {
16+
final class JLineTerminal extends java.io.Closeable {
1717
// import java.util.logging.{Logger, Level}
1818
// Logger.getLogger("org.jline").setLevel(Level.FINEST)
1919

2020
private val terminal =
2121
TerminalBuilder.builder()
22-
.dumb(!needsTerminal) // fail early if we need a terminal and can't create one
22+
.dumb(false) // fail early if not able to create a terminal
2323
.build()
2424
private val history = new DefaultHistory
2525

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,5 @@ package dotty.tools.repl
33
/** Main entry point to the REPL */
44
object Main {
55
def main(args: Array[String]): Unit =
6-
new ReplDriver(args).runUntilQuit(needsTerminal = true)
7-
}
8-
9-
object WorksheetMain {
10-
def main(args: Array[String]): Unit =
11-
new ReplDriver(args).runUntilQuit(needsTerminal = false)
6+
new ReplDriver(args).runUntilQuit()
127
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ class ReplDriver(settings: Array[String],
101101
* observable outside of the CLI, for this reason, most helper methods are
102102
* `protected final` to facilitate testing.
103103
*/
104-
final def runUntilQuit(needsTerminal: Boolean, initialState: State = initialState): State = {
105-
val terminal = new JLineTerminal(needsTerminal)
104+
final def runUntilQuit(initialState: State = initialState): State = {
105+
val terminal = new JLineTerminal
106106

107107
/** Blockingly read a line, getting back a parse result */
108108
def readLine(state: State): ParseResult = {

language-server/src/dotty/tools/languageserver/worksheet/CancellationThread.scala

Lines changed: 0 additions & 29 deletions
This file was deleted.

language-server/src/dotty/tools/languageserver/worksheet/Evaluator.scala

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package dotty.tools.languageserver.worksheet
22

33
import dotty.tools.dotc.core.Contexts.Context
44

5-
import java.io.{File, PrintStream}
5+
import java.io.{File, PrintStream, IOException}
6+
import java.lang.ProcessBuilder.Redirect
7+
import java.util.concurrent.CancellationException
8+
import java.util.{Timer, TimerTask}
69

710
import org.eclipse.lsp4j.jsonrpc.CancelChecker
811

@@ -31,7 +34,7 @@ private object Evaluator {
3134
def get(cancelChecker: CancelChecker)(implicit ctx: Context): Option[Evaluator] = synchronized {
3235
val classpath = ctx.settings.classpath.value
3336
previousEvaluator match {
34-
case Some(cp, evaluator) if evaluator.isAlive() && cp == classpath =>
37+
case Some(cp, evaluator) if evaluator.isAlive && cp == classpath =>
3538
evaluator.reset(cancelChecker)
3639
Some(evaluator)
3740
case _ =>
@@ -52,12 +55,12 @@ private object Evaluator {
5255
*/
5356
private class Evaluator private (javaExec: String,
5457
userClasspath: String,
55-
cancelChecker: CancelChecker) {
58+
private var cancelChecker: CancelChecker) {
5659
private val process =
5760
new ProcessBuilder(
5861
javaExec,
5962
"-classpath", scala.util.Properties.javaClassPath,
60-
dotty.tools.repl.WorksheetMain.getClass.getName.stripSuffix("$"),
63+
ReplProcess.getClass.getName.stripSuffix("$"),
6164
"-classpath", userClasspath,
6265
"-color:never")
6366
.redirectErrorStream(true)
@@ -67,46 +70,54 @@ private class Evaluator private (javaExec: String,
6770
private val processInput = new PrintStream(process.getOutputStream())
6871

6972
// Messages coming out of the REPL
70-
private val processOutput = new ReplReader(process.getInputStream())
71-
processOutput.start()
72-
73-
// The thread that monitors cancellation
74-
private val cancellationThread = new CancellationThread(cancelChecker, this)
75-
cancellationThread.start()
73+
private val processOutput = new InputStreamConsumer(process.getInputStream())
74+
75+
// A timer that monitors cancellation
76+
private val cancellationTimer = new Timer()
77+
locally {
78+
val task = new TimerTask {
79+
def run(): Unit =
80+
if (!isAlive)
81+
cancellationTimer.cancel()
82+
else
83+
try cancelChecker.checkCanceled()
84+
catch { case _: CancellationException => exit() }
85+
}
86+
val checkCancelledDelayMs = 50L
87+
cancellationTimer.schedule(task, checkCancelledDelayMs, checkCancelledDelayMs)
88+
}
7689

77-
// Wait for the REPL to be ready
78-
processOutput.next()
7990

8091
/** Is the process that runs the REPL still alive? */
81-
def isAlive(): Boolean = process.isAlive()
92+
def isAlive: Boolean = process.isAlive
8293

8394
/**
8495
* Submit `command` to the REPL, wait for the result.
8596
*
8697
* @param command The command to evaluate.
8798
* @return The result from the REPL.
8899
*/
89-
def eval(command: String): Option[String] = {
90-
processInput.println(command)
100+
def eval(command: String): String = {
101+
processInput.print(command)
102+
processInput.print(InputStreamConsumer.delimiter)
91103
processInput.flush()
92-
processOutput.next().map(_.trim)
104+
105+
try processOutput.next().trim
106+
catch { case _: IOException => "" }
93107
}
94108

95109
/**
96110
* Reset the REPL to its initial state, update the cancel checker.
97111
*/
98112
def reset(cancelChecker: CancelChecker): Unit = {
99-
cancellationThread.setCancelChecker(cancelChecker)
113+
this.cancelChecker = cancelChecker
100114
eval(":reset")
101115
}
102116

103117
/** Terminate this JVM. */
104118
def exit(): Unit = {
105-
processOutput.interrupt()
106119
process.destroyForcibly()
107120
Evaluator.previousEvaluator = None
108-
cancellationThread.interrupt()
121+
cancellationTimer.cancel()
109122
}
110-
111123
}
112-
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dotty.tools.languageserver.worksheet
2+
3+
import java.io.{InputStream, IOException}
4+
import java.util.Scanner
5+
6+
class InputStreamConsumer(in: InputStream) {
7+
private[this] val scanner =
8+
new Scanner(in).useDelimiter(InputStreamConsumer.delimiter)
9+
10+
/** Finds and returns the next complete token from this input stream.
11+
*
12+
* A complete token is preceded and followed by input that matches the delimiter pattern.
13+
* This method may block while waiting for input
14+
*/
15+
def next(): String =
16+
try scanner.next()
17+
catch {
18+
case _: NoSuchElementException =>
19+
throw new IOException("InputStream closed")
20+
}
21+
}
22+
23+
object InputStreamConsumer {
24+
def delimiter = "\uE000" // withing private use area
25+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package dotty.tools.languageserver.worksheet
2+
3+
import dotty.tools.repl.ReplDriver
4+
5+
object ReplProcess {
6+
def main(args: Array[String]): Unit = {
7+
val driver = new ReplDriver(args)
8+
val in = new InputStreamConsumer(System.in)
9+
var state = driver.initialState
10+
11+
while (true) {
12+
val code = in.next() // blocking
13+
state = driver.run(code)(state)
14+
print(InputStreamConsumer.delimiter) // needed to mark the end of REPL output
15+
}
16+
}
17+
}

language-server/src/dotty/tools/languageserver/worksheet/ReplReader.scala

Lines changed: 0 additions & 47 deletions
This file was deleted.

language-server/src/dotty/tools/languageserver/worksheet/Worksheet.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ object Worksheet {
4949
}
5050
queries.foreach { (line, code) =>
5151
cancelChecker.checkCanceled()
52-
val res = evaluator.eval(code).getOrElse("")
52+
val res = evaluator.eval(code)
5353
cancelChecker.checkCanceled()
5454
if (res.nonEmpty)
5555
sendMessage(line, res)

sbt-bridge/src/xsbt/ConsoleInterface.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class ConsoleInterface {
4141

4242
val s1 = driver.run(initialCommands)(s0)
4343
// TODO handle failure during initialisation
44-
val s2 = driver.runUntilQuit(needsTerminal = true, s1)
44+
val s2 = driver.runUntilQuit(s1)
4545
driver.run(cleanupCommands)(s2)
4646
}
4747
}

0 commit comments

Comments
 (0)