fix: presentation navigation now works correctly again
This commit is contained in:
parent
3ad18addb0
commit
add8e9a714
125
copret/src/Presentation.scala
Normal file
125
copret/src/Presentation.scala
Normal file
|
@ -0,0 +1,125 @@
|
|||
package de.qwertyuiop.copret
|
||||
|
||||
import os.Path
|
||||
import Terminal.*
|
||||
import syntax.*
|
||||
|
||||
case class Presentation(
|
||||
slides: Vector[Slide],
|
||||
meta: Map[String, String] = Map.empty,
|
||||
):
|
||||
def start(using keymap: Keymap = Keymap.default) =
|
||||
Terminal.enterRawMode()
|
||||
Terminal.hideCursor()
|
||||
Thread.sleep(1000)
|
||||
run()
|
||||
|
||||
import Presentation._
|
||||
def run()(using keymap: Keymap) =
|
||||
logger.info("Starting presentation")
|
||||
import SlideAction.*
|
||||
@annotation.tailrec
|
||||
def rec(currentPos: Int, action: SlideAction): Unit =
|
||||
logger.info(s"Executing slide $currentPos with action $action")
|
||||
|
||||
inline def redraw() =
|
||||
logger.info(s"redrawing ${currentPos}")
|
||||
// rec(currentPos - 1, QuickNext)
|
||||
navigate(currentPos, true, 0)(executeQuick)
|
||||
|
||||
inline def navigate(pos: Int, condition: Boolean, direction: Int)(
|
||||
executor: Int => Slide => Unit,
|
||||
) =
|
||||
logger.info(s"Navigating from ${pos}, condition is $condition, direction is ${direction}")
|
||||
if condition then
|
||||
executor(pos + direction)(slides(pos + direction))
|
||||
rec(pos + direction, waitkey)
|
||||
else rec(pos, waitkey)
|
||||
|
||||
inline def runInteractive(cmd: Vector[String], path: Path) =
|
||||
logger.info(s"Running interactive command ${cmd} with working directory ${path}")
|
||||
Terminal.showCursor()
|
||||
try os.proc(cmd).call(cwd = path)
|
||||
catch case _ => ()
|
||||
Terminal.hideCursor()
|
||||
Terminal.clear()
|
||||
redraw()
|
||||
|
||||
action match
|
||||
case Start => navigate(0, true, 0)(executeSlide)
|
||||
case Next => navigate(currentPos, currentPos + 1 < slides.size, 1)(executeSlide)
|
||||
case QuickNext => navigate(currentPos, currentPos + 1 < slides.size, 1)(executeQuick)
|
||||
case Prev => navigate(currentPos, currentPos > 0, -1)(executeQuick)
|
||||
case Interactive(cmd, path) => runInteractive(cmd, path)
|
||||
|
||||
case Goto(target) =>
|
||||
for i <- 0 until target
|
||||
do executeSilent(i)()
|
||||
rec(target - 1, QuickNext)
|
||||
|
||||
case GotoSelect =>
|
||||
promptSlide() match
|
||||
case Some(i) => rec(currentPos, Goto(i))
|
||||
case None => redraw()
|
||||
|
||||
case Help =>
|
||||
Terminal.clear()
|
||||
println(keymap.help)
|
||||
waitkey
|
||||
redraw()
|
||||
|
||||
case Other(codes) =>
|
||||
Terminal.printStatus(action.show)
|
||||
rec(currentPos, waitkey)
|
||||
|
||||
case Quit =>
|
||||
Terminal.showCursor()
|
||||
end match
|
||||
end rec
|
||||
rec(0, Start)
|
||||
end run
|
||||
|
||||
def promptSlide() =
|
||||
val maxSlide = slides.size
|
||||
prompt(s"Go to slide (1 - $maxSlide):", _.toIntOption)(
|
||||
(res, input) => res.filter((1 to maxSlide).contains).isEmpty && input.nonEmpty,
|
||||
in => s"No such slide: $in (empty input to abort)",
|
||||
).map(_ - 1)
|
||||
|
||||
def executeSlide(pos: Int)(
|
||||
slide: Slide = slides(pos),
|
||||
): Unit = slide match
|
||||
case Paragraph(contents) => println(contents)
|
||||
case Clear => Terminal.clear()
|
||||
case PauseKey => waitkey(using Keymap.empty)
|
||||
case Pause(msec) => Thread.sleep(msec)
|
||||
case incMd @ IncludeMarkdown(_) => println(incMd.markdownBlock())
|
||||
case Image(file, None) => println(KittyGraphicsProtocol.showImage(file))
|
||||
case Image(file, Some(ImageSize(w, h, aspect))) =>
|
||||
println(KittyGraphicsProtocol.showImage(file)) // TODO
|
||||
case cmd: TypedCommand[_] => cmd.show()
|
||||
case Silent(actions) => actions()
|
||||
case Group(slides) => slides.foreach(executeSlide(pos))
|
||||
case lios @ LazyIOSlide(_, display) => executeSlide(pos)(lios.genSlide())
|
||||
case Meta(genSlide) => executeSlide(pos)(genSlide(this, pos))
|
||||
|
||||
def executeQuick(pos: Int)(
|
||||
slide: Slide = slides(pos),
|
||||
): Unit = slide match
|
||||
case Pause(msec) => ()
|
||||
case PauseKey => ()
|
||||
case cmd: TypedCommand[_] => cmd.quickShow()
|
||||
case Group(slides) => slides.foreach(executeQuick(pos))
|
||||
case lios @ LazyIOSlide(_, display) => executeQuick(pos)(lios.genSlide())
|
||||
case _ => executeSlide(pos)(slide)
|
||||
|
||||
def executeSilent(pos: Int)(
|
||||
slide: Slide = slides(pos),
|
||||
): Unit = slide match
|
||||
case cmd: TypedCommand[_] => cmd.force()
|
||||
case Group(slides) => slides.foreach(executeSilent(pos))
|
||||
case lios @ LazyIOSlide(_, display) =>
|
||||
executeSilent(pos)(lios.genSlide())
|
||||
case Paragraph(_) | Image(_, _) | Clear | IncludeMarkdown(_) | Meta(_) => ()
|
||||
case _ => executeQuick(pos)(slide)
|
||||
end Presentation
|
16
copret/src/logger.scala
Normal file
16
copret/src/logger.scala
Normal file
|
@ -0,0 +1,16 @@
|
|||
package de.qwertyuiop.copret
|
||||
|
||||
object logger:
|
||||
import java.util.logging.*
|
||||
val log = Logger.getLogger("copret")
|
||||
val fh = FileHandler("/tmp/copret.log")
|
||||
log.addHandler(fh)
|
||||
log.setUseParentHandlers(false)
|
||||
System.setProperty(
|
||||
"java.util.logging.SimpleFormatter.format",
|
||||
"%1$tY-%1$tm-%1$tdT%1$tH:%1$tM:%1$tS.%1$tN %1$Tp %2$s: %4$s: %5$s%6$s%n",
|
||||
)
|
||||
fh.setFormatter(new SimpleFormatter())
|
||||
fh.setLevel(Level.ALL)
|
||||
|
||||
export log.*
|
|
@ -4,119 +4,6 @@ import os.Path
|
|||
import Terminal.*
|
||||
import syntax.*
|
||||
|
||||
case class Presentation(
|
||||
slides: Vector[Slide],
|
||||
meta: Map[String, String] = Map.empty,
|
||||
):
|
||||
def start(using keymap: Keymap = Keymap.default) =
|
||||
Terminal.enterRawMode()
|
||||
Terminal.hideCursor()
|
||||
Thread.sleep(1000)
|
||||
run()
|
||||
|
||||
import Presentation._
|
||||
def run()(using keymap: Keymap) =
|
||||
import SlideAction.*
|
||||
@annotation.tailrec
|
||||
def rec(pos: Int, action: SlideAction): Unit =
|
||||
|
||||
inline def redraw() = rec(pos - 1, QuickNext)
|
||||
|
||||
inline def navigate(pos: Int, condition: Boolean, direction: Int)(
|
||||
executor: Int => Slide => Unit,
|
||||
) =
|
||||
if condition then
|
||||
executor(pos + direction)(slides(pos))
|
||||
rec(pos + direction, waitkey)
|
||||
else rec(pos, waitkey)
|
||||
|
||||
inline def runInteractive(cmd: Vector[String], path: Path) =
|
||||
Terminal.showCursor()
|
||||
try os.proc(cmd).call(cwd = path)
|
||||
catch case _ => ()
|
||||
Terminal.hideCursor()
|
||||
Terminal.clear()
|
||||
redraw()
|
||||
|
||||
action match
|
||||
case Start => navigate(0, true, 0)(executeSlide)
|
||||
case Next => navigate(pos, pos + 1 < slides.size, 1)(executeSlide)
|
||||
case QuickNext => navigate(pos, pos + 1 < slides.size, 1)(executeQuick)
|
||||
case Prev => navigate(pos, pos > 0, -1)(executeQuick)
|
||||
case Interactive(cmd, path) => runInteractive(cmd, path)
|
||||
|
||||
case Goto(target) =>
|
||||
for i <- 0 until target
|
||||
do executeSilent(i)()
|
||||
rec(target - 1, QuickNext)
|
||||
|
||||
case GotoSelect =>
|
||||
promptSlide() match
|
||||
case Some(i) => rec(pos, Goto(i))
|
||||
case None => redraw()
|
||||
|
||||
case Help =>
|
||||
Terminal.clear()
|
||||
println(keymap.help)
|
||||
waitkey
|
||||
redraw()
|
||||
|
||||
case Other(codes) =>
|
||||
Terminal.printStatus(action.show)
|
||||
rec(pos, waitkey)
|
||||
|
||||
case Quit =>
|
||||
Terminal.showCursor()
|
||||
end match
|
||||
end rec
|
||||
rec(0, Start)
|
||||
end run
|
||||
|
||||
def promptSlide() =
|
||||
val maxSlide = slides.size - 1
|
||||
prompt(s"Go to slide (0 - $maxSlide):", _.toIntOption)(
|
||||
(res, input) => res.filter((0 to maxSlide).contains).isEmpty && input.nonEmpty,
|
||||
in => s"No such slide: $in (empty input to abort)",
|
||||
)
|
||||
|
||||
def executeSlide(pos: Int)(
|
||||
slide: Slide = slides(pos),
|
||||
): Unit = slide match
|
||||
case Paragraph(contents) => println(contents)
|
||||
case Clear => Terminal.clear()
|
||||
case PauseKey => waitkey(using Keymap.empty)
|
||||
case Pause(msec) => Thread.sleep(msec)
|
||||
case incMd @ IncludeMarkdown(_) => println(incMd.markdownBlock())
|
||||
case Image(file, None) => println(KittyGraphicsProtocol.showImage(file))
|
||||
case Image(file, Some(ImageSize(w, h, aspect))) =>
|
||||
println(KittyGraphicsProtocol.showImage(file)) // TODO
|
||||
case cmd: TypedCommand[_] => cmd.show()
|
||||
case Silent(actions) => actions()
|
||||
case Group(slides) => slides.foreach(executeSlide(pos))
|
||||
case lios @ LazyIOSlide(_, display) => executeSlide(pos)(lios.genSlide())
|
||||
case Meta(genSlide) => executeSlide(pos)(genSlide(this, pos))
|
||||
|
||||
def executeQuick(pos: Int)(
|
||||
slide: Slide = slides(pos),
|
||||
): Unit = slide match
|
||||
case Pause(msec) => ()
|
||||
case PauseKey => ()
|
||||
case cmd: TypedCommand[_] => cmd.quickShow()
|
||||
case Group(slides) => slides.foreach(executeQuick(pos))
|
||||
case lios @ LazyIOSlide(_, display) => executeQuick(pos)(lios.genSlide())
|
||||
case _ => executeSlide(pos)(slide)
|
||||
|
||||
def executeSilent(pos: Int)(
|
||||
slide: Slide = slides(pos),
|
||||
): Unit = slide match
|
||||
case cmd: TypedCommand[_] => cmd.force()
|
||||
case Group(slides) => slides.foreach(executeSilent(pos))
|
||||
case lios @ LazyIOSlide(_, display) =>
|
||||
executeSilent(pos)(lios.genSlide())
|
||||
case Paragraph(_) | Image(_, _) | Clear | IncludeMarkdown(_) | Meta(_) => ()
|
||||
case _ => executeQuick(pos)(slide)
|
||||
end Presentation
|
||||
|
||||
case class ImageSize(width: Double, height: Double, keepAspect: Boolean)
|
||||
|
||||
sealed trait Slide
|
||||
|
@ -209,14 +96,14 @@ object TypedCommand:
|
|||
def runShell(using Path): Vector[String] => String =
|
||||
c => runProcess(Vector(shell, "-c", c.mkString(" ")))
|
||||
|
||||
def runInteractive(using Path): Vector[String] => String =
|
||||
c => { os.proc(c).call(stdin = os.Inherit, stdout = os.Inherit, stderr = os.Inherit); "" }
|
||||
def runInteractive(using cwd: Path): Vector[String] => String =
|
||||
c => { os.proc(c).call(stdin = os.Inherit, stdout = os.Inherit, stderr = os.Inherit, cwd = cwd); "" }
|
||||
|
||||
def apply(cmd: String*)(using Path): TypedCommand[Vector[String]] =
|
||||
TypedCommand(run, cmd.mkString(" "), cmd.toVector)
|
||||
|
||||
def apply[T](exec: T => String, display: String, cmd: T) =
|
||||
new TypedCommand(exec, display, cmd, false, false)
|
||||
def apply[T](exec: T => String, display: String, cmd: T): TypedCommand[T] =
|
||||
TypedCommand(exec, display, cmd, false, false)
|
||||
|
||||
def shell(cmd: String*)(using Path): TypedCommand[Vector[String]] =
|
||||
TypedCommand(runShell, cmd.mkString(" "), cmd.toVector)
|
||||
|
|
Loading…
Reference in a new issue