Refactor keymap to allow showing help

This commit is contained in:
Alexander Gehrke 2022-05-23 23:17:34 +02:00
parent 0acbca2e62
commit 18c0f6ea7a
2 changed files with 79 additions and 34 deletions

View file

@ -10,19 +10,37 @@ enum SlideAction:
case Next case Next
case QuickNext case QuickNext
case Quit case Quit
case Help
case Interactive(cmd: Vector[String], wd: Path) case Interactive(cmd: Vector[String], wd: Path)
case Other(code: List[Int]) case Other(code: List[Int])
def show: String = this match
case Start => "go to first slide"
case Goto(slideIndex: Int) => s"jump directly to slide $slideIndex"
case GotoSelect => "jump to slide"
case Prev => "previous slide (skip animations)"
case Next => "next slide"
case QuickNext => "next slide (skip animations)"
case Quit => "quit"
case Help => "show help"
case Interactive(cmd: Vector[String], wd: Path) => s"execute command \"${cmd.mkString(" ")}\""
case Other(code: List[Int]) => s"Unknown key sequence: $code"
object SlideAction: object SlideAction:
def runForeground(cmd: String*)(implicit wd: Path) = Interactive(cmd.toVector, wd) def runForeground(cmd: String*)(implicit wd: Path) = Interactive(cmd.toVector, wd)
import SlideAction.* import SlideAction.*
case class Keymap(bindings: Map[List[Int], SlideAction]): case class Keymap(bindings: Map[Key, SlideAction]):
def apply(keycode: List[Int]): SlideAction = bindings.getOrElse(keycode, Other(keycode)) private val lookup = bindings.map((k,v) => k.codes -> v)
def apply(keycode: List[Int]): SlideAction = lookup.getOrElse(keycode, Other(keycode))
def extend(newBindings: Map[Key, SlideAction]) = Keymap(bindings ++ newBindings)
def ++(newBindings: Map[Key, SlideAction]) = extend(newBindings)
def help: String =
bindings.toSeq.sortBy(_._2.toString).map((k,v) => k.show.padTo(8, ' ') + " " + v.show).mkString("\n")
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 empty = Keymap(Map())
val default = Keymap(Map( val default = Keymap(Map(
@ -30,53 +48,71 @@ object Keymap:
Key.Left -> Prev, Key.Left -> Prev,
Key.PageUp -> Prev, Key.PageUp -> Prev,
Key('k') -> Prev, Key('k') -> Prev,
Key(' ') -> Next, Key.Space -> Next,
Key('j') -> Next, Key('j') -> Next,
Key.Down -> QuickNext, Key.Down -> QuickNext,
Key.Right -> QuickNext, Key.Right -> QuickNext,
Key.PageDown -> QuickNext, Key.PageDown -> QuickNext,
Key('q') -> Quit, Key('q') -> Quit,
Key('g') -> Start, Key('g') -> Start,
Key.Enter -> Start,
Key('s') -> GotoSelect, Key('s') -> GotoSelect,
Key('?') -> Help,
)) ))
enum Key:
case Code(name: String, codepoints: List[Int])
case Printable(char: Char)
def codes: List[Int] =
this match
case Code(_, cp) => cp
case Printable(char) => List(char.toInt)
def show: String =
this match
case Code(name, _) => name
case Printable(c) => c.toString
object Key: object Key:
def apply(char: Char): Key = Printable(char)
def apply(name: String, codepoints: Int*): Key = Code(name, codepoints.toList)
object codes: object codes:
val Esc = 27 val esc = 27
val Backspace = 127 val backspace = 127
val Esc = List[Int](codes.Esc)
val Backspace = List[Int](codes.Backspace)
val Delete = List[Int](codes.Esc, '[', '3', '~')
val PageUp = List[Int](codes.Esc, '[', '5', '~') val Esc = Key("Escape", codes.esc)
val PageDown = List[Int](codes.Esc, '[', '6', '~') val Backspace = Key("Backspace", codes.backspace)
val Delete = Key("Delete", codes.esc, '[', '3', '~')
val Home = List[Int](codes.Esc, '[', 'H') val PageUp = Key("PageUp", codes.esc, '[', '5', '~')
val End = List[Int](codes.Esc, '[', 'F') val PageDown = Key("PageDown", codes.esc, '[', '6', '~')
val F1 = List[Int](codes.Esc, 'P') val Home = Key("Home", codes.esc, '[', 'H')
val F2 = List[Int](codes.Esc, 'Q') val End = Key("End", codes.esc, '[', 'F')
val F3 = List[Int](codes.Esc, 'R')
val F4 = List[Int](codes.Esc, 'S')
val F5 = List[Int](codes.Esc, '1', '5', '~') val F1 = Key("F1", codes.esc, 'P')
val F6 = List[Int](codes.Esc, '1', '7', '~') val F2 = Key("F2", codes.esc, 'Q')
val F7 = List[Int](codes.Esc, '1', '8', '~') val F3 = Key("F3", codes.esc, 'R')
val F8 = List[Int](codes.Esc, '1', '9', '~') val F4 = Key("F4", codes.esc, 'S')
val F9 = List[Int](codes.Esc, '2', '0', '~') val F5 = Key("F5", codes.esc, '1', '5', '~')
val F10 = List[Int](codes.Esc, '2', '1', '~') val F6 = Key("F6", codes.esc, '1', '7', '~')
val F11 = List[Int](codes.Esc, '2', '3', '~') val F7 = Key("F7", codes.esc, '1', '8', '~')
val F12 = List[Int](codes.Esc, '2', '4', '~') val F8 = Key("F8", codes.esc, '1', '9', '~')
val Tab = List[Int]('\t') val F9 = Key("F9", codes.esc, '2', '0', '~')
val F10 = Key("F10", codes.esc, '2', '1', '~')
val F11 = Key("F11", codes.esc, '2', '3', '~')
val F12 = Key("F12", codes.esc, '2', '4', '~')
val Up = List[Int](codes.Esc, '[', 'A') val Space = Key("<space>", ' ')
val Down = List[Int](codes.Esc, '[', 'B') val Tab = Key("<tab>", '\t')
val Right = List[Int](codes.Esc, '[', 'C') val Enter = Key("<enter>", '\n')
val Left = List[Int](codes.Esc, '[', 'D')
def apply(char: Char): List[Int] = List(char.toInt) val Up = Key("Up", codes.esc, '[', 'A')
val Down = Key("Down", codes.esc, '[', 'B')
val Right = Key("Right", codes.esc, '[', 'C')
val Left = Key("Left", codes.esc, '[', 'D')
/* vim:set tw=120: */ /* vim:set tw=120: */

View file

@ -10,14 +10,14 @@ case class Presentation(slides: Vector[Slide], meta: Map[String, String] = Map.e
run() run()
import Presentation._ import Presentation._
def run()(using Keymap) = def run()(using keymap: Keymap) =
import SlideAction.* import SlideAction.*
@annotation.tailrec def rec(p: Presentation, pos: Int, action: SlideAction): Unit = @annotation.tailrec def rec(p: Presentation, pos: Int, action: SlideAction): Unit =
action match action match
case Start => case Start =>
executeSlide(p, pos)() executeSlide(p, pos)()
rec(p, 0, waitkey) rec(p, 0, waitkey)
case Next | Other(_) => case Next =>
if pos + 1 < p.slides.size then if pos + 1 < p.slides.size then
executeSlide(p, pos + 1)() executeSlide(p, pos + 1)()
rec(p, pos + 1, waitkey) rec(p, pos + 1, waitkey)
@ -55,6 +55,15 @@ case class Presentation(slides: Vector[Slide], meta: Map[String, String] = Map.e
target match target match
case Some(i) => rec(p, pos, Goto(i)) case Some(i) => rec(p, pos, Goto(i))
case None => rec(p, pos - 1, QuickNext) case None => rec(p, pos - 1, QuickNext)
case Help =>
Terminal.clear()
println(keymap.help)
waitkey
rec(p, pos - 1, QuickNext)
case Other(codes) =>
Terminal.cursorTo(Terminal.height, 1)
println(action.show)
rec(p, pos, waitkey)
case Quit => case Quit =>
() ()
rec(this, 0, Start) rec(this, 0, Start)