Skip to content

Commit

Permalink
Auto-update lazy val-s too
Browse files Browse the repository at this point in the history
  • Loading branch information
alexarchambault committed Apr 11, 2019
1 parent 11b8ef9 commit 24f29b6
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,21 +92,45 @@ object AmmInterpreter {
new CompilerLifecycleManager(storage, headFrame) {
import scala.reflect.internal.Flags
import scala.tools.nsc.{Global => G}
def customPprintSignature(ident: String, customMsg: Option[String], modOpt: Option[String]) = {
def customPprintSignature(ident: String, customMsg: Option[String], modOpt: Option[String], modErrOpt: Option[String]) = {
val customCode = customMsg.fold("_root_.scala.None")(x => s"""_root_.scala.Some("$x")""")
val modOptCode = modOpt.fold("_root_.scala.None")(x => s"""_root_.scala.Some($x)""")
val modErrOptCode = modErrOpt.fold("_root_.scala.None")(x => s"""_root_.scala.Some($x)""")
s"""_root_.almond
| .api
| .JupyterAPIHolder
| .value
| .Internal
| .printOnChange($ident, ${fastparse.internal.Util.literalize(ident)}, $customCode, $modOptCode)""".stripMargin
| .printOnChange($ident, ${fastparse.internal.Util.literalize(ident)}, $customCode, $modOptCode, $modErrOptCode)""".stripMargin
}
override def preprocess(fileName: String): Preprocessor =
synchronized {
if (compiler == null) init(force = true)
new DefaultPreprocessor(compiler.parse(fileName, _)) {
val CustomPatVarDef = Processor {

val CustomLazyDef = Processor {
case (_, code, t: G#ValDef)
if !DefaultPreprocessor.isPrivate(t) &&
!t.name.decoded.contains("$") &&
t.mods.hasFlag(Flags.LAZY) =>
val (code0, modOpt) = fastparse.parse(code, Parsers.PatVarSplitter(_)) match {
case Parsed.Success((lhs, rhs), _) if lhs.startsWith("lazy val ") =>
val mod = Name.backtickWrap(t.name.decoded + "$value")
val c = s"""val $mod = new _root_.almond.api.internal.Lazy(() => $rhs)
|import $mod.{value => ${Name.backtickWrap(t.name.decoded)}}
|""".stripMargin
(c, Some(mod + ".onChange"))
case _ =>
(code, None)
}
DefaultPreprocessor.Expanded(
code0,
Seq(customPprintSignature(Name.backtickWrap(t.name.decoded), Some("[lazy]"), None, modOpt))
)
}

val CustomVarDef = Processor {

case (_, code, t: G#ValDef)
if !DefaultPreprocessor.isPrivate(t) &&
!t.name.decoded.contains("$") &&
Expand All @@ -123,11 +147,12 @@ object AmmInterpreter {
}
DefaultPreprocessor.Expanded(
code0,
Seq(customPprintSignature(Name.backtickWrap(t.name.decoded), None, modOpt))
Seq(customPprintSignature(Name.backtickWrap(t.name.decoded), None, modOpt, None))
)

}
override val decls = Seq[(String, String, G#Tree) => Option[DefaultPreprocessor.Expanded]](
CustomPatVarDef,
CustomLazyDef, CustomVarDef,
// same as super.decls
ObjectDef, ClassDef, TraitDef, DefDef, TypeDef, PatVarDef, Import, Expr
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ object Execute {
fansi.Str(s" ") ++ method ++ "(" ++ src ++ ")"
}

private def showException(ex: Throwable,
def showException(ex: Throwable,
error: fansi.Attrs,
highlightError: fansi.Attrs,
source: fansi.Attrs) = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ final class JupyterApiImpl(
value: => T,
ident: String,
custom: Option[String],
onChange: Option[(T => Unit) => Unit]
onChange: Option[(T => Unit) => Unit],
onChangeOrError: Option[(Either[Throwable, T] => Unit) => Unit]
)(implicit
tprint: TPrint[T],
tcolors: TPrintColors,
classTagT: ClassTag[T]
): Iterator[String] =
replApi.printSpecial(value, ident, custom, onChange, replApi.pprinter, Some(updatableResults))(tprint, tcolors, classTagT).getOrElse {
replApi.printSpecial(value, ident, custom, onChange, onChangeOrError, replApi.pprinter, Some(updatableResults))(tprint, tcolors, classTagT).getOrElse {
replApi.Internal.print(value, ident, custom)(tprint, tcolors, classTagT)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ammonite.repl.{FrontEnd, FullReplAPI, ReplLoad, SessionApiImpl}
import ammonite.runtime.{History, Storage}
import ammonite.util.{Bind, Colors, CompilationError, Ref, Res}
import ammonite.util.Util.normalizeNewlines
import fansi.Attr
import jupyter.{Displayer, Displayers}
import pprint.{TPrint, TPrintColors}

Expand All @@ -31,6 +32,7 @@ final class ReplApiImpl(
ident: String,
custom: Option[String],
onChange: Option[(T => Unit) => Unit],
onChangeOrError: Option[(Either[Throwable, T] => Unit) => Unit],
pprinter: Ref[pprint.PPrinter],
updatableResultsOpt: Option[JupyterApi.UpdatableResults]
)(implicit tprint: TPrint[T], tcolors: TPrintColors, classTagT: ClassTag[T]): Option[Iterator[String]] =
Expand Down Expand Up @@ -63,7 +65,7 @@ final class ReplApiImpl(
p.display(DisplayData(m))
Some(Iterator())
} else
for (updatableResults <- updatableResultsOpt; onChange0 <- onChange) yield {
for (updatableResults <- updatableResultsOpt if (onChange.nonEmpty && custom.isEmpty) || (onChangeOrError.nonEmpty && custom.nonEmpty)) yield {

// Pre-compute how many lines and how many columns the prefix of the
// printed output takes, so we can feed that information into the
Expand Down Expand Up @@ -94,7 +96,7 @@ final class ReplApiImpl(
).map(_.render).mkString
}

onChange0 { value0 =>
onChange.foreach(_ { value0 =>
if (value0 != currentValue) {
val s = pprinter().tokenize(
value0,
Expand All @@ -104,12 +106,31 @@ final class ReplApiImpl(
updatableResults.update(id, s.map(_.render).mkString, last = false)
currentValue = value0
}
}
})

id

case Some(s) =>
pprinter().colorLiteral(s).render

val id = updatableResults.updatable {
fansi.Color.LightGray(s).render
}

onChangeOrError.foreach(_ {
case Left(ex) =>
val err = Execute.showException(ex, colors0().error(), Attr.Reset, colors0().literal())
val s = fansi.Color.LightGray("[last attempt failed]").render + "\n" + err
updatableResults.update(id, s, last = false)
case Right(value0) =>
val s = pprinter().tokenize(
value0,
height = pprinter().defaultHeight - prefix.completedLineCount,
initialOffset = prefix.lastLineLength
)
updatableResults.update(id, s.map(_.render).mkString, last = true)
})

id
}

output.iterator.map(_.render) ++ Iterator(rhs)
Expand Down Expand Up @@ -165,7 +186,7 @@ final class ReplApiImpl(
ident: String,
custom: Option[String]
)(implicit tprint: TPrint[T], tcolors: TPrintColors, classTagT: ClassTag[T]): Iterator[String] =
printSpecial(value, ident, custom, None, pprinter, None)(tprint, tcolors, classTagT).getOrElse {
printSpecial(value, ident, custom, None, None, pprinter, None)(tprint, tcolors, classTagT).getOrElse {
super.print(value, ident, custom)(tprint, tcolors, classTagT)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ trait FullJupyterApi extends JupyterApi { self =>
protected def printOnChange[T](value: => T,
ident: String,
custom: Option[String],
onChange: Option[(T => Unit) => Unit])
onChange: Option[(T => Unit) => Unit],
onChangeOrError: Option[(Either[Throwable, T] => Unit) => Unit])
(implicit tprint: pprint.TPrint[T],
tcolors: pprint.TPrintColors,
classTagT: ClassTag[T] = null): Iterator[String]
Expand All @@ -18,11 +19,12 @@ trait FullJupyterApi extends JupyterApi { self =>
def printOnChange[T](value: => T,
ident: String,
custom: Option[String],
onChange: Option[(T => Unit) => Unit])
onChange: Option[(T => Unit) => Unit],
onChangeOrError: Option[(Either[Throwable, T] => Unit) => Unit])
(implicit tprint: pprint.TPrint[T],
tcolors: pprint.TPrintColors,
classTagT: ClassTag[T] = null): Iterator[String] =
self.printOnChange(value, ident, custom, onChange)(tprint, tcolors, classTagT)
self.printOnChange(value, ident, custom, onChange, onChangeOrError)(tprint, tcolors, classTagT)
def ansiTextToHtml(text: String): String =
self.ansiTextToHtml(text)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package almond.api.internal

final class Lazy[T](private var compute: () => T) {
private var listeners = List.empty[Either[Throwable, T] => Unit]
def onChange: (Either[Throwable, T] => Unit) => Unit = { f =>
listeners = f :: listeners
}
lazy val value: T = {
val e =
try Right(compute())
catch {
case ex: Throwable => // catch less things here?
Left(ex)
}
listeners.foreach(_(e))
e match {
case Right(t) =>
compute = null
t
case Left(ex) =>
throw ex
}
}
}

0 comments on commit 24f29b6

Please sign in to comment.