aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/repl/ammonite/Utils.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/dotty/tools/dotc/repl/ammonite/Utils.scala')
-rw-r--r--src/dotty/tools/dotc/repl/ammonite/Utils.scala169
1 files changed, 169 insertions, 0 deletions
diff --git a/src/dotty/tools/dotc/repl/ammonite/Utils.scala b/src/dotty/tools/dotc/repl/ammonite/Utils.scala
new file mode 100644
index 000000000..64a2c1476
--- /dev/null
+++ b/src/dotty/tools/dotc/repl/ammonite/Utils.scala
@@ -0,0 +1,169 @@
+package dotty.tools
+package dotc
+package repl
+package ammonite.terminal
+
+import java.io.{FileOutputStream, Writer, File => JFile}
+import scala.annotation.tailrec
+
+/**
+ * Prints stuff to an ad-hoc logging file when running the repl or terminal in
+ * development mode
+ *
+ * Very handy for the common case where you're debugging terminal interactions
+ * and cannot use `println` because it will stomp all over your already messed
+ * up terminal state and block debugging. With [[Debug]], you can have a
+ * separate terminal open tailing the log file and log as verbosely as you
+ * want without affecting the primary terminal you're using to interact with
+ * Ammonite.
+ */
+object Debug {
+ lazy val debugOutput =
+ new FileOutputStream(new JFile("terminal/target/log"))
+
+ def apply(s: Any) =
+ if (System.getProperty("ammonite-sbt-build") == "true")
+ debugOutput.write((System.currentTimeMillis() + "\t\t" + s + "\n").getBytes)
+}
+
+class AnsiNav(output: Writer) {
+ def control(n: Int, c: Char) = output.write(s"\033[" + n + c)
+
+ /**
+ * Move up `n` squares
+ */
+ def up(n: Int) = if (n == 0) "" else control(n, 'A')
+ /**
+ * Move down `n` squares
+ */
+ def down(n: Int) = if (n == 0) "" else control(n, 'B')
+ /**
+ * Move right `n` squares
+ */
+ def right(n: Int) = if (n == 0) "" else control(n, 'C')
+ /**
+ * Move left `n` squares
+ */
+ def left(n: Int) = if (n == 0) "" else control(n, 'D')
+
+ /**
+ * Clear the screen
+ *
+ * n=0: clear from cursor to end of screen
+ * n=1: clear from cursor to start of screen
+ * n=2: clear entire screen
+ */
+ def clearScreen(n: Int) = control(n, 'J')
+ /**
+ * Clear the current line
+ *
+ * n=0: clear from cursor to end of line
+ * n=1: clear from cursor to start of line
+ * n=2: clear entire line
+ */
+ def clearLine(n: Int) = control(n, 'K')
+}
+
+object AnsiNav {
+ val resetUnderline = "\u001b[24m"
+ val resetForegroundColor = "\u001b[39m"
+ val resetBackgroundColor = "\u001b[49m"
+}
+
+object TTY {
+
+ // Prefer standard tools. Not sure why we need to do this, but for some
+ // reason the version installed by gnu-coreutils blows up sometimes giving
+ // "unable to perform all requested operations"
+ val pathedTput = if (new java.io.File("/usr/bin/tput").exists()) "/usr/bin/tput" else "tput"
+ val pathedStty = if (new java.io.File("/bin/stty").exists()) "/bin/stty" else "stty"
+
+ def consoleDim(s: String) = {
+ import sys.process._
+ Seq("bash", "-c", s"$pathedTput $s 2> /dev/tty").!!.trim.toInt
+ }
+ def init() = {
+ stty("-a")
+
+ val width = consoleDim("cols")
+ val height = consoleDim("lines")
+// Debug("Initializing, Width " + width)
+// Debug("Initializing, Height " + height)
+ val initialConfig = stty("-g").trim
+ stty("-icanon min 1 -icrnl -inlcr -ixon")
+ sttyFailTolerant("dsusp undef")
+ stty("-echo")
+ stty("intr undef")
+// Debug("")
+ (width, height, initialConfig)
+ }
+
+ private def sttyCmd(s: String) = {
+ import sys.process._
+ Seq("bash", "-c", s"$pathedStty $s < /dev/tty"): ProcessBuilder
+ }
+
+ def stty(s: String) =
+ sttyCmd(s).!!
+ /*
+ * Executes a stty command for which failure is expected, hence the return
+ * status can be non-null and errors are ignored.
+ * This is appropriate for `stty dsusp undef`, since it's unsupported on Linux
+ * (http://man7.org/linux/man-pages/man3/termios.3.html).
+ */
+ def sttyFailTolerant(s: String) =
+ sttyCmd(s ++ " 2> /dev/null").!
+
+ def restore(initialConfig: String) = {
+ stty(initialConfig)
+ }
+}
+
+/**
+ * A truly-lazy implementation of scala.Stream
+ */
+case class LazyList[T](headThunk: () => T, tailThunk: () => LazyList[T]) {
+ var rendered = false
+ lazy val head = {
+ rendered = true
+ headThunk()
+ }
+
+ lazy val tail = tailThunk()
+
+ def dropPrefix(prefix: Seq[T]) = {
+ @tailrec def rec(n: Int, l: LazyList[T]): Option[LazyList[T]] = {
+ if (n >= prefix.length) Some(l)
+ else if (prefix(n) == l.head) rec(n + 1, l.tail)
+ else None
+ }
+ rec(0, this)
+ }
+ override def toString = {
+
+ @tailrec def rec(l: LazyList[T], res: List[T]): List[T] = {
+ if (l.rendered) rec(l.tailThunk(), l.head :: res)
+ else res
+ }
+ s"LazyList(${(rec(this, Nil).reverse ++ Seq("...")).mkString(",")})"
+ }
+
+ def ~:(other: => T) = LazyList(() => other, () => this)
+}
+
+object LazyList {
+ object ~: {
+ def unapply[T](x: LazyList[T]) = Some((x.head, x.tail))
+ }
+
+ def continually[T](t: => T): LazyList[T] = LazyList(() => t, () =>continually(t))
+
+ implicit class CS(ctx: StringContext) {
+ val base = ctx.parts.mkString
+ object p {
+ def unapply(s: LazyList[Int]): Option[LazyList[Int]] = {
+ s.dropPrefix(base.map(_.toInt))
+ }
+ }
+ }
+}