diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e21548 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.bak +out/ diff --git a/copret/src/Presentation.scala b/copret/src/Presentation.scala index e90c84f..8ea705e 100644 --- a/copret/src/Presentation.scala +++ b/copret/src/Presentation.scala @@ -93,7 +93,6 @@ case class Presentation( 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))) => import KittyGraphicsProtocol.Sizing.* @@ -117,10 +116,10 @@ case class Presentation( 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) => + 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) + case Paragraph(_) | Image(_, _) | Clear | Meta(_) => () + case _ => executeQuick(pos)(slide) end Presentation diff --git a/copret/src/main.scala b/copret/src/main.scala deleted file mode 100644 index 47ae888..0000000 --- a/copret/src/main.scala +++ /dev/null @@ -1,224 +0,0 @@ -package de.qwertyuiop.copret - -import de.qwertyuiop.copret.Terminal.* - -import de.qwertyuiop.copret.* -import de.qwertyuiop.copret.syntax.* -import TypedCommand.{interactive, shell => sh} -import os.Path - -given presentationRoot: Path = os.Path("/home/crater2150/org/opencolloq/termescape") -val imgs = presentationRoot / "img" -given theme: Theme = Theme.default - -def figlet(str: String, font: String): String = String( - os.proc("figlet", "-t", "-f", font, str).call().out.bytes, -) - -def lolcat(str: String): String = String( - os.proc("lolcat", "-b", "-f", "-h", "0.7", "-r").call(stdin = str).out.bytes, -) - -def lollet(str: String, font: String): String = lolcat(figlet(str, font)) - -// debug mode -val noInteractiveCommands = false - -val titleSlide = - Group( - Clear, - TypedCommand(TypedCommand.runInteractive(using presentationRoot), "", Vector("./title.zsh"), true, false), - Clear, - header, - Paragraph( - lollet("Terminal", "mono12").block ++ lollet("basics", "mono12").block - ++ "\n" ++ "Was tut so eine Terminalanwendung eigentlich?".right(10), - ), - ) - -/* copret templates are simply normal methods that return slides */ -def chapter(title: String, subtitle: String): Group = - chapter(title, "", subtitle) -def chapter(title1: String, title2: String, subtitle: String): Group = { - val font = - if ((title1.length max title2.length) < columns / 10) "mono12" else "mono9" - Group( - Clear, - header, // a built in template - Paragraph( - lollet(title1, font).block ++ lollet(title2, font).block ++ "\n" ++ subtitle.right(10), - ), - ) -} - -val parentSHApattern = raw"parent ([a-f0-9]{40})".r -val treeSHApattern = raw"tree ([a-f0-9]{40})".r -val blobSHApattern = raw"blob ([a-f0-9]{40})".r - -val meta = Map( - "author" -> "Alexander Gehrke", - "title" -> "Terminals", -) - -def sgr(code: String): String = s"\u001b[${code}m" - -def sgr(code: Int*): String = sgr(code.mkString(";")) - -val reset = sgr(0) - -extension (s: String) - def style(code: String): String = sgr(code) + s + reset - def style(code: Int*): String = sgr(code*) + s + reset - def italic = s.style(3) - def bold = s.style(1) - - def underline(style: Int = 1, color: Int = 7) = - sgr("4:" + style) + sgr(58, 5, color) + s + reset - -val rainbow = Vector( - "\u001b[38;5;196m", - "\u001b[38;5;202m", - "\u001b[38;5;208m", - "\u001b[38;5;214m", - "\u001b[38;5;220m", - "\u001b[38;5;226m", - "\u001b[38;5;190m", - "\u001b[38;5;154m", - "\u001b[38;5;118m", - "\u001b[38;5;82m", - "\u001b[38;5;46m", - "\u001b[38;5;47m", - "\u001b[38;5;48m", - "\u001b[38;5;49m", - "\u001b[38;5;50m", - "\u001b[38;5;51m", - "\u001b[38;5;45m", - "\u001b[38;5;39m", - "\u001b[38;5;33m", -) - -def list(bullet: (Int, Int) => String, continued: (Int, Int) => String)(items: String*)(using Theme): Paragraph = - val randbow = util.Random.shuffle(rainbow) - items.zipWithIndex - .map((str, index) => { - def formatFirst(first: String) = - s" ${randbow(index % randbow.length)}${bullet(index, items.size)}\u001b[0m $first" - - str.split("\n").toList match - case first :: Nil => formatFirst(first) - case first :: cont => - formatFirst(first) - + cont.map(line => s" ${continued(index, items.size)}${line}").mkString("\n", "\n", "") - case Nil => "" - }) - .mkString("\n") - .par - -def ulist(items: String*)(using Theme) = list(bullet = (_, _) => "✦ ", continued = (_, _) => " ")(items*) - -def digits(i: Int): Int = math.ceil(math.log10(i)).toInt - -def olist(items: String*)(using Theme) = - list( - bullet = (i, max) => (" " * (digits(max + 1) - digits(i + 1) - 1)) + s"${i + 1}.", - continued = (i, max) => " " * digits(max + 1), - )(items*) - -def emph(str: String) = sgr(3) + lolcat(str) + reset -def strong(str: String) = sgr(1) + lolcat(str) + reset - -val slides = Vector( - titleSlide, - slide("Terminal IO")( - "Eine Anwendung in einem Terminal kommuniziert (fast) nur via Standard-IO mit diesem:".par, - ulist( - "stdin, für Benutzereingaben und Nachrichten vom Terminal", - "stdout, für Textausgabe und Nachrichten an das Terminal", - "stderr, wird wie stdout behandelt", - ), - s"=> Alle Steuersignale vom Programm ans Terminal oder umgekehrt sind ${emph("inband")}".par, - ), - slide("Steuersignale")( - s"""|Wie unterscheiden Terminal und Programm normalen IO von Steuersignalen? - |Durch ${emph("Steuerzeichen")}, aufgeteilt in zwei Bereiche:""".par, - s"""|${strong("C0")}: alle Zeichen im Bereich von 0x00 bis 0x1F - |Größtenteils veraltet. Noch relevant sind u.a.:""".par, - ulist( - "Whitespaces (\\n, \\r, \\t, ...) und Backspace (0x08)", - "Bell (\\a), führt bei den meisten Desktops zu blinkendem Icon o.ä. (Urgent Flag)", - "XON/XOFF (0x11/0x13), schaltet Ausgabe an/aus (Flow Control)", - "Escape (\\e, 0x1B), das wichtigste für diesen Talk, startet Escapesequenzen", - ), - s"""|${strong("C1")}: Zeichen im Bereich von 0x80 bis 0x9F - |Wegen Kollisionen mit Unicode inkompatibel. - |Heute üblicherweise durch Escapesequenzen ersetzt""".par, - ), - chapter("Escape-", "sequenzen", "die verschiedenen Typen"), - slide("CSI")( - PauseKey, - Image(imgs / "csi.png"), - ), - slide("CSI")( - """|Control Sequence Introducer (CSI) ist der vermutlich häufigste Typ. - |Wird benutzt für:""".par, - ulist( - "Cursor-Steuerung", - "Löschen von Zeichen oder Zeilen", - s"${lolcat("Farben")} ${"und".italic} ${"andere".style(42)} ${"Formatierung".underline(style = 3, color = 1)}", - ), - "|CSI-Sequenzen haben folgendes Format:\n".par, - s"${"ESC [".style(94)} ${"arg1 ; arg2 ; ... ; argn".style(93)} ${"c".style(92)}".block.text, - s"""| - |${"✦ ESC [".style(94)} ist der Teil, der CSI genannt wird - |${"✦ c".style(92)} gibt an, was gemacht werden soll. Immer ein Zeichen zwischen 0x40 (@) und 0x7F (~) - |${"✦ arg1 ; arg2 ; ... ; argn".style(93)} sind Parameter, meistens Zahlen. - | Zeichen, die für ${"c".style(92)} erlaubt sind, sind nicht als Parameter erlaubt. - |\n""".stripMargin.block.text, - s"""\nBeispiel: ${"\\u001b[31m".style(31)} schaltet auf rote Schrift um""".par, - ), - slide("OSC")( - """|Operating System Commands (OSC) haben verschiedenste Anwendungszwecke, - |z.B. Setzen des Fenstertitels, Zugriff aufs Clipboard, etc. - |Im Gegensatz zu CSI Escapes können sie die meisten Zeichen enthalten - | => komplexere Befehle möglich.""".par, - s"${"ESC ]".style(94)} ${"".style(92)} ; ${"args...".style(93)} ${"ST".style(96)}".block.text, - s"""| - |${"✦ ESC ]".style(94)} startet ein OSC - |${"✦ ".style(92)} gibt an, was gemacht werden soll, ein- bis vierstellige Zahl - |${"✦ args...".style(93)} sind je nach Befehl die Argumente. Hier ist alles außer ST erlaubt - |${"✦ ST".style(96)} ist das String Terminator Zeichen. Entweder BEL (\\a) oder ESC \\ - |""".stripMargin.block.text, - s"""\nBeispiel: TODO irgendwas mit clipboard""".par, - ), - slide("APC")( - "Application Program Commands (APC) sind ähnlich zu OSC, aber größtenteils\nspezifisch für einzelne Terminals".par, - ulist( - s"fangen mit ${"ESC _".style(3, 94)} an statt mit ${"ESC ]".style(3, 94)}", - s"Format bis zum ${"ST".style(3, 96)} ist uneinheitlich", - "Benutzt z.B. bei screen und tmux zum Setzen von Statusleisten", - "Beispiel für mittlerweile verbreitetere Anwendung: Kitty Graphics Protocol", - ), - ), - chapter("Ende", "Noch Fragen?", ""), - slide("Sonstige")( - "Ein paar weitere Typen von Escapesequenzen, die wir uns nicht genauer anschauen".par, - ulist( - "DCS (Device Control String): ähnlich wie OSC, kaum noch genutzt.\nU.a. für ältere Grafikprotokolle", - "SCS, DOCS: für Zeichensatzwechsel. Wir haben jetzt Unicode.", - "DEC: Für Dinge wie doppelte Zeilenhöhe. In manchen Terminals noch supported.", - "SM, RM: Setzen von Terminalmodi, war für Hardwareterminals relevant", - "DECSET, DECRST: Noch mehr Terminalmodi, vendorspezifisch.\nWerden noch genutzt, z.B. für Maus-Support", - ), - ), -) - -val presentation = Presentation(slides, meta = meta) - -@main def main = - // enterRawMode() - // println(Terminal.getSize()) - presentation.start(using - Keymap.default ++ Map( - Key('i') -> SlideAction.runForeground("tmux"), - ), - ) diff --git a/copret/src/slides.scala b/copret/src/slides.scala index 1c21715..4ef5dee 100644 --- a/copret/src/slides.scala +++ b/copret/src/slides.scala @@ -16,15 +16,6 @@ case class Paragraph(contents: String) extends Slide: object Paragraph: def apply(str: fansi.Str): Paragraph = Paragraph(str.toString) -case class IncludeMarkdown(path: Path) extends Slide: - def markdownBlock() = - %%%( - "/usr/bin/mdcat", - "--columns", - (columns * 0.8).toInt.toString, - path.toString, - )(using os.pwd).block - case class Image(path: Path, sizing: Option[ImageSize]) extends Slide object Image: def apply(path: Path) = new Image(path, None) diff --git a/copret/src/templates.scala b/copret/src/templates.scala index 39a5b0a..bebd73f 100644 --- a/copret/src/templates.scala +++ b/copret/src/templates.scala @@ -32,6 +32,31 @@ trait Templates: ), ) + def list(bullet: (Int, Int) => String, continued: (Int, Int) => String)(items: String*)(using Theme): Paragraph = + items.zipWithIndex + .map((str, index) => { + def formatFirst(first: String) = + s" ${bullet(index, items.size)} $first" + + str.split("\n").toList match + case first :: Nil => formatFirst(first) + case first :: cont => + formatFirst(first) + + cont.map(line => s" ${continued(index, items.size)}${line}").mkString("\n", "\n", "") + case Nil => "" + }) + .mkString("\n") + .par + + def ulist(items: String*)(using Theme) = list(bullet = (_, _) => "✦ ", continued = (_, _) => " ")(items*) + + def olist(items: String*)(using Theme) = + def digits(i: Int): Int = math.ceil(math.log10(i)).toInt + list( + bullet = (i, max) => (" " * (digits(max + 1) - digits(i + 1) - 1)) + s"${i + 1}.", + continued = (i, max) => " " * digits(max + 1), + )(items*) + lazy val --- = Paragraph("\n" + ("-" * columns).yellow.toString + "\n") lazy val === = Paragraph("\n" + ("═" * columns).yellow.toString + "\n") end Templates