Handle escape codes in text for centering

This commit is contained in:
Alexander Gehrke 2024-05-19 02:13:11 +02:00
parent 2dc7e0a0e4
commit 8111309560
4 changed files with 32 additions and 29 deletions

View file

@ -12,37 +12,38 @@ case class Theme(styles: Map[String, fansi.Attrs], figletFonts: Map[String, Stri
figletFonts.getOrElse(key, default) figletFonts.getOrElse(key, default)
def extend(newStyles: Map[String, fansi.Attrs]) = copy(styles = styles ++ newStyles) def extend(newStyles: Map[String, fansi.Attrs]) = copy(styles = styles ++ newStyles)
def ++(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 extend(newFonts: Map[String, String])(implicit d: DummyImplicit) = copy(figletFonts = figletFonts ++ newFonts)
def ++(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: object Theme:
given default: Theme = Theme(Map( given default: Theme = Theme(
"titleLine" -> (fansi.Bold.On ++ fansi.Color.DarkGray), Map(
"code" -> fansi.Color.Yellow "titleLine" -> (fansi.Bold.On ++ fansi.Color.DarkGray),
"code" -> fansi.Color.Yellow,
), ),
Map("titleLine" -> "smbraille") Map("titleLine" -> "smbraille"),
) )
object Format: object Format:
def alignRight(str: String, padding: Int = 2) =" " * (columns - str.length - padding) + str + " " * padding def alignRight(str: String, padding: Int = 2) = " " * (columns - str.length - padding) + str + " " * padding
def center(str: String) = " " * ((columns - str.length) / 2) + str def center(str: String) = " " * ((columns - str.length) / 2) + str
def figlet(str: String, font: String):String = new String(%%("figlet", "-t", "-f", font, str)().out.bytes) def figlet(str: String, font: String): String = new String(%%("figlet", "-t", "-f", font, str)().out.bytes)
def centerLines(str: String) = str.split("\n").map(center).mkString("\n") 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 lines = str.split("\n")
val maxLen = lines.map(_.length).max val maxLen = lines.map(s => Terminal.stripEscapes(s).length).max
val pad = " " * ((columns - maxLen) / 2) val pad = " " * ((columns - maxLen) / 2)
lines.map(pad + _).mkString("\n") lines.map(pad + _).mkString("\n")
def distribute(texts: String*) = def distribute(texts: String*) =
val totalPad = columns - texts.map(_.length).sum val totalPad = columns - texts.map(s => Terminal.stripEscapes(s).length).sum
val numPads = texts.size - 1 val numPads = texts.size - 1
val pad = " " * (totalPad / numPads) val pad = " " * (totalPad / numPads)
texts.init.mkString(pad) + pad + " " * (totalPad % numPads) + texts.last texts.init.mkString(pad) + pad + " " * (totalPad % numPads) + texts.last
private[copret] val ticks = raw"`([^`]*)`".r private[copret] val ticks = raw"`([^`]*)`".r

View file

@ -210,7 +210,7 @@ object TypedCommand:
c => runProcess(Vector(shell, "-c", c.mkString(" "))) c => runProcess(Vector(shell, "-c", c.mkString(" ")))
def runInteractive(using Path): Vector[String] => String = def runInteractive(using Path): Vector[String] => String =
c => { os.proc(c).call(); "" } c => { os.proc(c).call(stdin = os.Inherit, stdout = os.Inherit, stderr = os.Inherit); "" }
def apply(cmd: String*)(using Path): TypedCommand[Vector[String]] = def apply(cmd: String*)(using Path): TypedCommand[Vector[String]] =
TypedCommand(run, cmd.mkString(" "), cmd.toVector) TypedCommand(run, cmd.mkString(" "), cmd.toVector)

View file

@ -4,24 +4,21 @@ import os.Path
trait Templates: trait Templates:
def titleLine(title: String)(using theme: Theme) = Paragraph( def titleLine(title: String)(using theme: Theme) = Paragraph(
"\n" + Format "\n" + Format.figlet(title, theme.font("titleLine", "pagga")).block.blue + "\n",
.figlet(title, theme.font("titleLine", "pagga"))
.block
.blue + "\n"
) )
def header(using theme: Theme) = Meta((p, pos) => { def header(using theme: Theme) = Meta((p, pos) => {
val left = p.meta.getOrElse("author", "") val left = p.meta.getOrElse("author", "")
val center = p.meta.getOrElse("title", "") val center = p.meta.getOrElse("title", "")
val right = s"${pos} / ${p.slides.size - 1}" val right = s"${pos + 1} / ${p.slides.size}"
theme.style("titleLine")(Format.distribute(left, center, right)).text theme.style("titleLine")(Format.distribute(left, center, right)).text
}) })
def slide(title: String)(slides: Slide*)(using Theme) = Group( def slide(title: String)(slides: Slide*)(using Theme) = Group(
Clear :: header :: titleLine(title) :: slides.toList Clear :: header :: titleLine(title) :: slides.toList,
) )
def slide(slides: Slide*)(using Theme) = Group( def slide(slides: Slide*)(using Theme) = Group(
Clear :: header :: slides.toList Clear :: header :: slides.toList,
) )
def markdown(title: String, content: Path)(using Theme) = slide(title)( def markdown(title: String, content: Path)(using Theme) = slide(title)(
@ -30,11 +27,12 @@ trait Templates:
"/usr/bin/mdcat", "/usr/bin/mdcat",
"--columns", "--columns",
(columns * 0.8).toInt.toString, (columns * 0.8).toInt.toString,
content.toString content.toString,
)(using os.pwd).block )(using os.pwd).block,
) ),
) )
lazy val --- = Paragraph(("═" * columns).yellow.toString) lazy val --- = Paragraph(("═" * columns).yellow.toString)
end Templates
/* vim:set tw=120: */ /* vim:set tw=120: */

View file

@ -64,7 +64,11 @@ object Terminal:
def csi = if (isTmux) "\u001bPtmux\u001b\u001b[" else "\u001b[" def csi = if (isTmux) "\u001bPtmux\u001b\u001b[" else "\u001b["
def osc = if (isTmux) "\u001bPtmux\u001b\u001b]" else "\u001b]" def osc = if (isTmux) "\u001bPtmux\u001b\u001b]" else "\u001b]"
def apc = if (isTmux) "\u001bPtmux;\u001b\u001b_" else "\u001b_" def apc = if (isTmux) "\u001bPtmux;\u001b\u001b_" else "\u001b_"
def st = if (isTmux) "\u0007\u001b\\" else "\u0007" def st = if (isTmux) "\u0007\u001b\\" else "\u001b\\"
def stripEscapes(str: String) =
// match CSI until letter or OSC/APC until ST
str.replaceAll("\u001b(\\[[^a-zA-Z]*[a-zA-Z]|(\\]|_).*?(\u0007|\u001b\\\\))", "")
def hideCursor() = print(s"${csi}?25l") def hideCursor() = print(s"${csi}?25l")
def showCursor() = print(s"${csi}?25h") def showCursor() = print(s"${csi}?25h")