diff --git a/build.sc b/build.sc index d214c67..e9610b8 100644 --- a/build.sc +++ b/build.sc @@ -1,9 +1,8 @@ import mill._, scalalib._, publish._ import $ivy.`com.lihaoyi::mill-contrib-bloop:0.9.5` - object copret extends ScalaModule with PublishModule { - def scalaVersion = "2.13.3" + def scalaVersion = "3.1.0" def publishVersion = "0.0.1" def pomSettings = PomSettings( @@ -14,13 +13,13 @@ object copret extends ScalaModule with PublishModule { licenses = Seq(License.MIT), developers = Seq( Developer("crater2150", "Alexander Gehrke", "https://github.com/crater2150") - ) - ) + ) + ) def ivyDeps = Agg( ivy"org.jline:jline:3.19.0", - ivy"com.lihaoyi::ammonite-ops:2.3.8", - ivy"com.lihaoyi::fansi:0.2.10", - ) + ivy"com.lihaoyi::ammonite-ops:2.3.8".withDottyCompat(scalaVersion()), + ivy"com.lihaoyi::fansi:0.2.14", + ) } diff --git a/copret/src/Theme.scala b/copret/src/Theme.scala index cc6f404..bc260bf 100644 --- a/copret/src/Theme.scala +++ b/copret/src/Theme.scala @@ -2,29 +2,27 @@ package de.qwertyuiop.copret import de.qwertyuiop.copret.syntax._ import ammonite.ops.{%%, pwd} -case class Theme(styles: Map[String, fansi.Attrs], figletFonts: Map[String, String]) { +case class Theme(styles: Map[String, fansi.Attrs], figletFonts: Map[String, String]): def style(key: String, default: fansi.Attrs = fansi.Attrs()) = styles.getOrElse(key, default) def font(key: String, default: String) = figletFonts.getOrElse(key, default) - + def extend(newStyles: Map[String, fansi.Attrs]) = copy(styles = styles ++ newStyles) def ++(newStyles: Map[String, fansi.Attrs]) = copy(styles = styles ++ newStyles) def extend(newFonts: Map[String, String])(implicit d: DummyImplicit) = copy(figletFonts = figletFonts ++ newFonts) def ++(newFonts: Map[String, String])(implicit d: DummyImplicit) = copy(figletFonts = figletFonts ++ newFonts) -} -object Theme { - implicit val default = Theme(Map( + +given default: Theme = Theme(Map( "titleLine" -> (fansi.Bold.On ++ fansi.Color.DarkGray), "code" -> fansi.Color.Yellow ), Map("titleLine" -> "pagga") ) -} -object Format { +object Format: def alignRight(str: String, padding: Int = 2) =" " * (columns - str.length - padding) + str + " " * padding def center(str: String) = " " * ((columns - str.length) / 2) + str @@ -32,21 +30,18 @@ object Format { def figlet(str: String, font: String) = %%("figlet", "-t", "-f", font, str)(pwd).out.string def centerLines(str: String) = str.split("\n").map(center).mkString("\n") - def centerBlock(str: String) = { + def centerBlock(str: String) = val lines = str.split("\n") val maxLen = lines.map(_.length).max val pad = " " * ((columns - maxLen) / 2) lines.map(pad + _).mkString("\n") - } - def distribute(texts: String*) = { + def distribute(texts: String*) = val totalPad = columns - texts.map(_.length).sum val numPads = texts.size - 1 val pad = " " * (totalPad / numPads) texts.init.mkString(pad) + pad + " " * (totalPad % numPads) + texts.last - } private[copret] val ticks = raw"`([^`]*)`".r -} /* vim:set tw=120: */ diff --git a/copret/src/keys.scala b/copret/src/keys.scala index 61bb8e0..f51e018 100644 --- a/copret/src/keys.scala +++ b/copret/src/keys.scala @@ -2,29 +2,28 @@ package de.qwertyuiop.copret import ammonite.ops.Path -sealed trait SlideAction -case object Start extends SlideAction -case class Goto(slideIndex: Int) extends SlideAction -case object GotoSelect extends SlideAction -case object Prev extends SlideAction -case object Next extends SlideAction -case object QuickNext extends SlideAction -case object Quit extends SlideAction -case class Interactive(cmd: Vector[String], wd: Path) extends SlideAction -case class Other(code: List[Int]) extends SlideAction +enum SlideAction: + case Start + case Goto(slideIndex: Int) + case GotoSelect + case Prev + case Next + case QuickNext + case Quit + case Interactive(cmd: Vector[String], wd: Path) + case Other(code: List[Int]) -object SlideAction { +object SlideAction: def runForeground(cmd: String*)(implicit wd: Path) = Interactive(cmd.toVector, wd) -} +import SlideAction.* -case class Keymap(bindings: Map[List[Int], SlideAction]) { +case class Keymap(bindings: Map[List[Int], SlideAction]): def apply(keycode: List[Int]): SlideAction = bindings.getOrElse(keycode, Other(keycode)) def extend(newBindings: Map[List[Int], SlideAction]) = Keymap(bindings ++ newBindings) def ++(newBindings: Map[List[Int], SlideAction]) = Keymap(bindings ++ newBindings) -} -object Keymap { +object Keymap: val empty = Keymap(Map()) val default = Keymap(Map( Key.Up -> Prev, @@ -41,46 +40,43 @@ object Keymap { Key('s') -> GotoSelect, )) -} -object Key { - object codes { +object Key: + object codes: val Esc = 27 val Backspace = 127 - } - val Esc = List(codes.Esc) - val Backspace = List(codes.Backspace) - val Delete = List(codes.Esc, '[', '3', '~') + val Esc = List[Int](codes.Esc) + val Backspace = List[Int](codes.Backspace) + val Delete = List[Int](codes.Esc, '[', '3', '~') - val PageUp = List(codes.Esc, '[', '5', '~') - val PageDown = List(codes.Esc, '[', '6', '~') + val PageUp = List[Int](codes.Esc, '[', '5', '~') + val PageDown = List[Int](codes.Esc, '[', '6', '~') - val Home = List(codes.Esc, '[', 'H') - val End = List(codes.Esc, '[', 'F') + val Home = List[Int](codes.Esc, '[', 'H') + val End = List[Int](codes.Esc, '[', 'F') - val F1 = List(codes.Esc, 'P') - val F2 = List(codes.Esc, 'Q') - val F3 = List(codes.Esc, 'R') - val F4 = List(codes.Esc, 'S') + val F1 = List[Int](codes.Esc, 'P') + val F2 = List[Int](codes.Esc, 'Q') + val F3 = List[Int](codes.Esc, 'R') + val F4 = List[Int](codes.Esc, 'S') - val F5 = List(codes.Esc, '1', '5', '~') - val F6 = List(codes.Esc, '1', '7', '~') - val F7 = List(codes.Esc, '1', '8', '~') - val F8 = List(codes.Esc, '1', '9', '~') + val F5 = List[Int](codes.Esc, '1', '5', '~') + val F6 = List[Int](codes.Esc, '1', '7', '~') + val F7 = List[Int](codes.Esc, '1', '8', '~') + val F8 = List[Int](codes.Esc, '1', '9', '~') - val F9 = List(codes.Esc, '2', '0', '~') - val F10 = List(codes.Esc, '2', '1', '~') - val F11 = List(codes.Esc, '2', '3', '~') - val F12 = List(codes.Esc, '2', '4', '~') + val F9 = List[Int](codes.Esc, '2', '0', '~') + val F10 = List[Int](codes.Esc, '2', '1', '~') + val F11 = List[Int](codes.Esc, '2', '3', '~') + val F12 = List[Int](codes.Esc, '2', '4', '~') - val Tab = List('\t') + val Tab = List[Int]('\t') - val Up = List(codes.Esc, '[', 'A') - val Down = List(codes.Esc, '[', 'B') - val Right = List(codes.Esc, '[', 'C') - val Left = List(codes.Esc, '[', 'D') + val Up = List[Int](codes.Esc, '[', 'A') + val Down = List[Int](codes.Esc, '[', 'B') + val Right = List[Int](codes.Esc, '[', 'C') + val Left = List[Int](codes.Esc, '[', 'D') def apply(char: Char): List[Int] = List(char.toInt) -} /* vim:set tw=120: */ diff --git a/copret/src/slides.scala b/copret/src/slides.scala index 95e6a31..9fcc9d7 100644 --- a/copret/src/slides.scala +++ b/copret/src/slides.scala @@ -3,39 +3,40 @@ import ammonite.ops._ import Terminal._ import syntax._ -case class Presentation(slides: Vector[Slide], meta: Map[String, String] = Map.empty) { - def start(keymap: Keymap = Keymap.default) = { +case class Presentation(slides: Vector[Slide], meta: Map[String, String] = Map.empty): + def start(using keymap: Keymap = Keymap.default) = Terminal.enterRawMode() - run(keymap) - } + run() import Presentation._ - def run(implicit k: Keymap) = { - @annotation.tailrec def rec(p: Presentation, pos: Int, action: SlideAction): Unit = { - action match { + def run()(using Keymap) = + import SlideAction.* + @annotation.tailrec def rec(p: Presentation, pos: Int, action: SlideAction): Unit = + action match case Start => executeSlide(p, pos)() rec(p, 1, waitkey) case Next | Other(_) => - if(pos + 1 < p.slides.size) { + if pos + 1 < p.slides.size then executeSlide(p, pos + 1)() rec(p, pos + 1, waitkey) - } else rec(p, pos, waitkey) + else rec(p, pos, waitkey) case QuickNext => - if(pos + 1 < p.slides.size) { + if pos + 1 < p.slides.size then executeQuick(p, pos + 1)() rec(p, pos + 1, waitkey) - } else rec(p, pos, waitkey) + else rec(p, pos, waitkey) case Prev => - if(pos > 0) { + if pos > 0 then executeQuick(p, pos - 1)() rec(p, pos - 1, waitkey) - } else rec(p, pos, waitkey) + else rec(p, pos, waitkey) case Interactive(cmd, path) => %(cmd)(path) rec(p, pos - 1, QuickNext) case Goto(target) => - for (i <- 0 until target) executeSilent(p, i)() + for i <- 0 until target + do executeSilent(p, i)() rec(p, target - 1, QuickNext) case GotoSelect => val maxSlide = p.slides.size - 1 @@ -43,21 +44,18 @@ case class Presentation(slides: Vector[Slide], meta: Map[String, String] = Map.e (res, input) => res.filter((0 to maxSlide).contains).isEmpty && input.nonEmpty, in => s"No such slide: $in (empty input to abort)" ) - target match { + target match case Some(i) => rec(p, pos, Goto(i)) case None => rec(p, pos - 1, QuickNext) - } case Quit => () - } - } rec(this, 0, Start) - } -} -object Presentation { - def executeSlide(p: Presentation, pos: Int)(slide: Slide = p.slides(pos)): Unit = slide match { + + +object Presentation: + def executeSlide(p: Presentation, pos: Int)(slide: Slide = p.slides(pos)): Unit = slide match case Paragraph(contents) => println(contents) case Clear => print("\u001b[2J\u001b[;H") - case PauseKey => waitkey(Keymap.empty) + case PauseKey => waitkey(using Keymap.empty) case Pause(msec) => Thread.sleep(msec) case incMd @ IncludeMarkdown(_) => println(incMd.markdownBlock()) case Image(file, width, height, keepAspect) => print(Terminal.showImage(file, width, height, keepAspect)) @@ -66,125 +64,114 @@ object Presentation { case Group(slides) => slides.foreach(executeSlide(p, pos)) case lios @ LazyIOSlide(_, display) => executeSlide(p, pos)(lios.genSlide()) case Meta(genSlide) => executeSlide(p, pos)(genSlide(p, pos)) - case other => println("Error: Unknown slide type:"); println(other) - } - def executeQuick(p: Presentation, pos: Int)(slide: Slide = p.slides(pos)): Unit = slide match { + def executeQuick(p: Presentation, pos: Int)(slide: Slide = p.slides(pos)): Unit = slide match case Pause(msec) => () case PauseKey => () case cmd: TypedCommand[_] => cmd.quickShow() case Group(slides) => slides.foreach(executeQuick(p, pos)) case lios @ LazyIOSlide(_, display) => executeQuick(p, pos)(lios.genSlide()) case _ => executeSlide(p, pos)(slide) - } - def executeSilent(p: Presentation, pos: Int)(slide: Slide = p.slides(pos)): Unit = slide match { + def executeSilent(p: Presentation, pos: Int)(slide: Slide = p.slides(pos)): Unit = slide match case cmd: TypedCommand[_] => cmd.force() case Group(slides) => slides.foreach(executeSilent(p, pos)) case lios @ LazyIOSlide(_, display) => executeSilent(p, pos)(lios.genSlide()) case Paragraph(_) | Image(_,_,_,_) | Clear | IncludeMarkdown(_) | Meta(_) => () case _ => executeQuick(p, pos)(slide) - } -} sealed trait Slide case class Paragraph(contents: fansi.Str) extends Slide -case class IncludeMarkdown(path: Path) extends Slide { - def markdownBlock() = %%%("/usr/bin/mdcat", "--columns", (columns * 0.8).toInt.toString, path.toString)(pwd).block -} +case class IncludeMarkdown(path: Path) extends Slide: + def markdownBlock() = + %%%("/usr/bin/mdcat", "--columns", (columns * 0.8).toInt.toString, path.toString)(using ImplicitWd.implicitCwd).block case class Image(path: Path, width: String = "100%", height: String = "100%", keepAspect: Boolean = true) extends Slide case object Clear extends Slide case class Pause(millisec: Long) extends Slide case object PauseKey extends Slide case class Meta(contents: (Presentation, Int) => Slide) extends Slide - -case class TypedCommand[T](exec: T => String, display: String, cmd: T) extends Slide { +case class TypedCommand[T](exec: T => String, display: String, cmd: T) extends Slide: private lazy val _output = exec(cmd) def output = _output - def display(s: String): TypedCommand[T] = TypedCommand(exec, s, cmd) - def show() = { + infix def showing (s: String): TypedCommand[T] = TypedCommand(exec, s, cmd) + + def show() = prompt() typeCmd() print(output) - } - def quickShow() = { + def quickShow() = prompt() println(display) print(output) - } def prompt() = print(fansi.Color.LightGreen("user@host % ")) def force() = _output - private def typeCmd() = { - for (char <- display) { - print(char) - Thread.sleep(50 + scala.util.Random.nextInt(80)) - } + private def typeCmd() = + for char <- display + do print(char) + Thread.sleep(50 + scala.util.Random.nextInt(80)) println() - } /* Conditionally disable execution. Useful for e.g. a debug mode, or a non-interactive mode */ def disable(altDisplay: String = display, condition: Boolean = true) = - if(condition) copy(display = altDisplay, exec = (_:T) => "") + if condition then copy(display = altDisplay, exec = (_:T) => "") else this /* Conditionally replace the executed command (but still displaying the same). Useful for e.g. a non-interactive mode, * where a call to an editor is replaced with a file operation */ def replaceIf(condition: Boolean)(tc: TypedCommand[_]): TypedCommand[_] = - if(condition) tc.display(display) + if condition then tc.showing(display) else this -} -object TypedCommand { +object TypedCommand: val shell = sys.env.getOrElse("SHELL", "sh") - def run(implicit wd: Path): Vector[String] => String = + def run(using Path): Vector[String] => String = c => safe_%%(c) - def runShell(implicit wd: Path): Vector[String] => String = + def runShell(using Path): Vector[String] => String = c => safe_%%(Vector(shell, "-c", c.mkString(" "))) - def runInteractive(implicit wd: Path): Vector[String] => String = + def runInteractive(using Path): Vector[String] => String = c => { %(c); ""} - def apply(cmd: String*)(implicit wd: Path): TypedCommand[Vector[String]] = + def apply(cmd: String*)(using Path): TypedCommand[Vector[String]] = TypedCommand(run, cmd.mkString(" "), cmd.toVector) - def shell(cmd: String*)(implicit wd: Path): TypedCommand[Vector[String]] = + def shell(cmd: String*)(using Path): TypedCommand[Vector[String]] = TypedCommand(runShell, cmd.mkString(" "), cmd.toVector) def fake(cmd: String): TypedCommand[String] = TypedCommand(_ => "", cmd, cmd) - def interactive(cmd: String*)(implicit wd: Path): TypedCommand[Vector[String]] = + def interactive(cmd: String*)(using Path): TypedCommand[Vector[String]] = TypedCommand(runInteractive, cmd.mkString(" "), cmd.toVector) -} + sealed abstract case class Silent[T] private (doStuff: () => T) extends Slide -object Silent { def apply[T](doStuff: => T) = new Silent(() => doStuff){} } +object Silent: + def apply[T](doStuff: => T) = new Silent(() => doStuff){} case class Group(slides: List[Slide]) extends Slide -object Group { def apply(slides: Slide*): Group = Group(slides.toList) } +object Group: + def apply(slides: Slide*): Group = Group(slides.toList) -case class LazyIOSlide[T](runOnce: () => T, display: T => Slide) extends Slide { +case class LazyIOSlide[T](runOnce: () => T, display: T => Slide) extends Slide: private lazy val data = runOnce() def genSlide(): Slide = display(data) -} -trait SlideSyntax { - private[copret] class LazyIOSlideBuilder[T](runOnce: => T) { +trait SlideSyntax: + private[copret] class LazyIOSlideBuilder[T](runOnce: => T): def useIn(display: T => Slide) = LazyIOSlide(() => runOnce, display) - } - def prepare[T](runOnce: => T): LazyIOSlideBuilder[T] = new LazyIOSlideBuilder(runOnce) -} + def prepare[T](runOnce: => T): LazyIOSlideBuilder[T] = LazyIOSlideBuilder(runOnce) /* vim:set tw=120: */ diff --git a/copret/src/syntax.scala b/copret/src/syntax.scala index 56fd6af..0850a41 100644 --- a/copret/src/syntax.scala +++ b/copret/src/syntax.scala @@ -1,38 +1,33 @@ package de.qwertyuiop.copret -object syntax extends Templates with TerminalSyntax with SlideSyntax { - implicit class PresenterStringExtensions(val str: String) { - import Format._ - def code(implicit theme: Theme) = Format.ticks.replaceAllIn(str, m => theme.style("code")("$1").render) - def text(implicit theme: Theme) = Paragraph(str) - def par(implicit theme: Theme) = Paragraph(str.stripMargin.code.padLeft(2)) - def style(key: String, default: fansi.Attrs = fansi.Attrs())(implicit theme: Theme) = theme.style(key, default)(str) +object syntax extends Templates with TerminalSyntax with SlideSyntax: + import Format._ + extension (str: String) + def code(using theme: Theme) = Format.ticks.replaceAllIn(str, m => theme.style("code")("$1").render) + def text(using Theme) = Paragraph(str) + def par(using Theme) = Paragraph(str.stripMargin.code.padLeft(2)) + def style(key: String, default: fansi.Attrs)(using theme: Theme) = theme.style(key, default)(str) def centered = center(str) def block = centerBlock(str) def right = alignRight(str) def right(padding: Int) = alignRight(str, padding) - def padLeft(padding: Int) = { + def padLeft(padding: Int) = val pad = " " * padding str.linesIterator.map(pad + _).mkString("\n") - } def blue = fansi.Color.Blue(str) def green = fansi.Color.Green(str) def yellow = fansi.Color.Yellow(str) def red = fansi.Color.Red(str) - } - implicit class PresenterFansiStringExtensions(val str: fansi.Str) { - import Format._ - def text(implicit theme: Theme) = Paragraph(str) - def style(key: String, default: fansi.Attrs = fansi.Attrs())(implicit theme: Theme) = theme.style(key, default)(str) + extension (str: fansi.Str) + def text(using Theme) = Paragraph(str) + def style(key: String, default: fansi.Attrs)(using theme: Theme) = theme.style(key, default)(str) def blue = fansi.Color.Blue(str) def green = fansi.Color.Green(str) def yellow = fansi.Color.Yellow(str) def red = fansi.Color.Red(str) - } -} /* vim:set tw=120: */ diff --git a/copret/src/templates.scala b/copret/src/templates.scala index 10c9318..29f4d9b 100644 --- a/copret/src/templates.scala +++ b/copret/src/templates.scala @@ -2,7 +2,7 @@ package de.qwertyuiop.copret import syntax._ import ammonite.ops.{Path, %, %%, pwd} -trait Templates { +trait Templates: def titleLine(title: String)(implicit theme: Theme) = Paragraph( "\n" + Format.figlet(title, theme.font("titleLine", "pagga")).block.blue + "\n" ) @@ -20,6 +20,5 @@ trait Templates { def markdown(title: String, content: Path) = slide(title)(IncludeMarkdown(content)) lazy val --- = Paragraph(("═" * columns).yellow) -} /* vim:set tw=120: */ diff --git a/copret/src/terminal.scala b/copret/src/terminal.scala index 7214b25..2e75cb7 100644 --- a/copret/src/terminal.scala +++ b/copret/src/terminal.scala @@ -3,70 +3,69 @@ import ammonite.ops.{Path, ShelloutException, pwd, read, %, %%} import org.jline.terminal.TerminalBuilder import org.jline.reader.LineReaderBuilder -object Terminal { - def safe_%%(cmd: Vector[String])(implicit wd: Path): String = - try { +object Terminal: + def safe_%%(cmd: Vector[String])(using Path): String = + try %%(cmd).out.string - } catch { + catch case e: ShelloutException => e.result.err.string - } - - def tryCmd[T](cmd: => T, default: => T) = - try { cmd } catch { case e: ShelloutException => default } - - def enterRawMode(): Unit = { + def enterRawMode(): Unit = %%("sh", "-c", "stty -icanon min 1 < /dev/tty")(pwd) %%("sh", "-c", "stty -echo < /dev/tty")(pwd) - } private[copret] lazy val jterm = org.jline.terminal.TerminalBuilder.terminal() private[copret] lazy val lineReader = LineReaderBuilder.builder().terminal(jterm).build() - def waitkey(implicit keymap: Keymap): SlideAction = { + def waitkey(using keymap: Keymap): SlideAction = // ignore keypresses done during slide animations - while(Console.in.ready()) Console.in.read + while Console.in.ready() do Console.in.read var key = scala.collection.mutable.ArrayBuffer[Int]() key += Console.in.read - while(Console.in.ready) + while Console.in.ready do key += Console.in.read keymap(key.toList) - } def prompt[T](prefix: String, parse: String => T)( retry: (T, String) => Boolean = (t: T, s: String) => false, error: String => String = in => s"Invalid input: $in" - ): T = { + ): T = val input = lineReader.readLine(prefix + " ") val result = parse(input) - if(retry(result, input)) { + if retry(result, input) then println(error(input)) prompt(prefix, parse)(retry, error) - } else result - } - def isTmux = sys.env.contains("TMUX") || sys.env("TERM").startsWith("screen") + val term = sys.env("TERM") - def osc = if (isTmux) "\u001bPtmux\u001b\u001b]" else "\u001b]" - def st = if (isTmux) "\u0007\u001b\\" else "\u0007" + def isTmux = sys.env.contains("TMUX") || term.startsWith("screen") - def showImage(img: Path, width: String = "100%", height: String = "100%", keepAspect: Boolean = true) = { + def osc = if isTmux then "\u001bPtmux\u001b\u001b]" else "\u001b]" + def st = if isTmux then "\u0007\u001b\\" else "\u0007" + + def showImage(img: Path, width: String = "100%", height: String = "100%", keepAspect: Boolean = true) = + if term == "xterm-kitty" then showImageKitty(img) + else showImageIterm(img, width, height, keepAspect) + + def showImageIterm(img: Path, width: String = "100%", height: String = "100%", keepAspect: Boolean = true) = import java.util.Base64 val image = Base64.getEncoder.encodeToString(read.bytes(img)) - val aspect = if(keepAspect) 1 else 0 + val aspect = if keepAspect then 1 else 0 s"${osc}1337;File=inline=1;width=$width;height=$height;preserveAspectRatio=$aspect:$image$st" - } -} -private[copret] trait TerminalSyntax { + def showImageKitty(img: Path, width: String = "100%", height: String = "100%", keepAspect: Boolean = true) = + import java.util.Base64 + s"\u001b_Gf=100,t=f,a=T;${Base64.getEncoder.encodeToString(img.toString.toCharArray.map(_.toByte))}\u001b\\" + +private[copret] trait TerminalSyntax: import Terminal._ - def %%%(cmd: String*)(implicit wd: Path) = safe_%%(cmd.toVector) + def %%%(cmd: String*)(using Path) = safe_%%(cmd.toVector) def columns = jterm.getSize.getColumns def rows = jterm.getSize.getRows -} + /* vim:set tw=120: */ diff --git a/examples/present.sc b/examples/present.sc index 6123ff5..94768e7 100755 --- a/examples/present.sc +++ b/examples/present.sc @@ -4,7 +4,8 @@ import de.qwertyuiop.copret._ import de.qwertyuiop.copret.syntax._ import TypedCommand.{interactive, shell => sh}, Format.figlet -import ammonite.ops._ +import ammonite.ops.{given, *} + /* Configuration */ @@ -12,8 +13,8 @@ import ammonite.ops._ * most of the commands in this presentation will be git commands in a demo repository, so the path to that repo is set * as implicit (will be the working directory of executed commands) */ val imgs = pwd/"img" -implicit val repoDir = pwd/"demoRepo" -implicit val theme = Theme.default +given repoDir: Path= pwd/"demoRepo" +import de.qwertyuiop.copret.{given Theme} /* You can define any variables and use then in your presentation. * The presentation is pure Scala code, you can use anything that Scala offers . */ @@ -57,7 +58,7 @@ def chapter(title1: String, title2: String, subtitle: String): Group = { Group(Clear, header, // a built in template Paragraph( - figlet(title1, font).block.green ++ figlet(title2, font).block.green + "\n" + subtitle.right(10).green + figlet(title1, font).block.green ++ figlet(title2, font).block.green ++ "\n" ++ subtitle.right(10).green ) ) } @@ -91,7 +92,7 @@ def gitCatFile(ref: String) = TypedCommand("git", "cat-file", "-p", ref) def gitLogGraph(cutOff: Int = 0) = { val shownCmd = "git log --graph --oneline --all" val execCmd = shownCmd + " --decorate=short --color=always " + (if(cutOff > 0) "| head -n -" + cutOff else "") - val typer = sh(execCmd) display shownCmd + val typer = sh(execCmd) showing shownCmd if(cutOff > 0) Group(typer, "...".text) else @@ -133,10 +134,10 @@ val presentation = Presentation(Vector( Pause(500), /* `TypedCommand.shell` (here aliased to `sh`) displays a typing animation of that command and then executes it, * showing its output in the presentation */ - sh("git init demoRepo")(pwd), + sh("git init demoRepo")(using pwd), /* sometimes it's useful to display something else than is actually executed, e.g. to add comments, or to hide * options only required because we don't call from an interactive shell (stuff like --color) */ - sh("ls -1a --color=always demoRepo")(pwd) display "ls -1a demoRepo", + sh("ls -1a --color=always demoRepo")(using pwd) showing "ls -1a demoRepo", /* If you need to run commands that should not display anything in the presentation, use `Silent`. * Here I use it to prepare the repository, but it could also be used e.g. for playing a sound or opening a video.*/ Silent { @@ -307,13 +308,13 @@ val presentation = Presentation(Vector( slide("Branches")( "Machen wir ein paar Änderungen und committen sie:\n".par, ---, - sh("echo 'a new line' >> hello.txt") display "echo 'a new line' >> hello.txt # appends to hello.txt", + sh("echo 'a new line' >> hello.txt") showing "echo 'a new line' >> hello.txt # appends to hello.txt", sh("git add hello.txt"), sh("git commit -m \"Added line to hello\""), ---, "Da der Branch `feature/foo` aktiv war, zeigt dieser auf den neuen Commit. `master` wird nicht geändert:".code.text, PauseKey, - sh("git log --graph --oneline --all --decorate=short --color=always") display "git log --graph --oneline --all", + sh("git log --graph --oneline --all --decorate=short --color=always") showing "git log --graph --oneline --all", ), slide("Branches")( "Jetzt wechseln wir zurück zu `master`:\n".par, @@ -357,7 +358,7 @@ val presentation = Presentation(Vector( """Git speichert Objekte unter `.git/objects//` |Objekte sind komprimiert. |""".par, - sh("tree -C .git/objects/ | head -n 11") display "tree .git/objects/", + sh("tree -C .git/objects/ | head -n 11") showing "tree .git/objects/", s"...".par, ), ), meta=meta) @@ -366,7 +367,7 @@ val presentation = Presentation(Vector( * The `runForeground` action lets you run any command, interrupting the presentation until it exits. * Here I bind the 'i' key to open tmux in the demo repo, for having interactive access, so I can call additional git * commands for questions */ -presentation.start(Keymap.default ++ Map( +presentation.start(using Keymap.default ++ Map( Key('i') -> SlideAction.runForeground("tmux"), ))