From 18c0f6ea7a27adeedf6f76a270c8a3c08c7f5edc Mon Sep 17 00:00:00 2001 From: Alexander Gehrke Date: Mon, 23 May 2022 23:17:34 +0200 Subject: [PATCH] Refactor keymap to allow showing help --- copret/src/keys.scala | 100 +++++++++++++++++++++++++++------------- copret/src/slides.scala | 13 +++++- 2 files changed, 79 insertions(+), 34 deletions(-) diff --git a/copret/src/keys.scala b/copret/src/keys.scala index f51e018..1924b57 100644 --- a/copret/src/keys.scala +++ b/copret/src/keys.scala @@ -10,19 +10,37 @@ enum SlideAction: case Next case QuickNext case Quit + case Help case Interactive(cmd: Vector[String], wd: Path) 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: def runForeground(cmd: String*)(implicit wd: Path) = Interactive(cmd.toVector, wd) import SlideAction.* -case class Keymap(bindings: Map[List[Int], SlideAction]): - def apply(keycode: List[Int]): SlideAction = bindings.getOrElse(keycode, Other(keycode)) +case class Keymap(bindings: Map[Key, SlideAction]): + 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: val empty = Keymap(Map()) val default = Keymap(Map( @@ -30,53 +48,71 @@ object Keymap: Key.Left -> Prev, Key.PageUp -> Prev, Key('k') -> Prev, - Key(' ') -> Next, + Key.Space -> Next, Key('j') -> Next, Key.Down -> QuickNext, Key.Right -> QuickNext, Key.PageDown -> QuickNext, Key('q') -> Quit, Key('g') -> Start, + Key.Enter -> Start, 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: + def apply(char: Char): Key = Printable(char) + def apply(name: String, codepoints: Int*): Key = Code(name, codepoints.toList) object codes: - val Esc = 27 - val Backspace = 127 - val Esc = List[Int](codes.Esc) - val Backspace = List[Int](codes.Backspace) - val Delete = List[Int](codes.Esc, '[', '3', '~') + val esc = 27 + val backspace = 127 - val PageUp = List[Int](codes.Esc, '[', '5', '~') - val PageDown = List[Int](codes.Esc, '[', '6', '~') + val Esc = Key("Escape", codes.esc) + val Backspace = Key("Backspace", codes.backspace) + val Delete = Key("Delete", codes.esc, '[', '3', '~') - val Home = List[Int](codes.Esc, '[', 'H') - val End = List[Int](codes.Esc, '[', 'F') + val PageUp = Key("PageUp", codes.esc, '[', '5', '~') + val PageDown = Key("PageDown", codes.esc, '[', '6', '~') - 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 Home = Key("Home", codes.esc, '[', 'H') + val End = Key("End", codes.esc, '[', 'F') - 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 F1 = Key("F1", codes.esc, 'P') + val F2 = Key("F2", codes.esc, 'Q') + val F3 = Key("F3", codes.esc, 'R') + val F4 = Key("F4", codes.esc, 'S') - 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 F5 = Key("F5", codes.esc, '1', '5', '~') + val F6 = Key("F6", codes.esc, '1', '7', '~') + val F7 = Key("F7", codes.esc, '1', '8', '~') + 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 Down = List[Int](codes.Esc, '[', 'B') - val Right = List[Int](codes.Esc, '[', 'C') - val Left = List[Int](codes.Esc, '[', 'D') + val Space = Key("", ' ') + val Tab = Key("", '\t') + val Enter = Key("", '\n') - 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: */ diff --git a/copret/src/slides.scala b/copret/src/slides.scala index 390f117..554151f 100644 --- a/copret/src/slides.scala +++ b/copret/src/slides.scala @@ -10,14 +10,14 @@ case class Presentation(slides: Vector[Slide], meta: Map[String, String] = Map.e run() import Presentation._ - def run()(using Keymap) = + def run()(using keymap: Keymap) = import SlideAction.* @annotation.tailrec def rec(p: Presentation, pos: Int, action: SlideAction): Unit = action match case Start => executeSlide(p, pos)() rec(p, 0, waitkey) - case Next | Other(_) => + case Next => if pos + 1 < p.slides.size then executeSlide(p, pos + 1)() rec(p, pos + 1, waitkey) @@ -55,6 +55,15 @@ case class Presentation(slides: Vector[Slide], meta: Map[String, String] = Map.e target match case Some(i) => rec(p, pos, Goto(i)) 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 => () rec(this, 0, Start)