From ccc15acfb0fed852f36a9675ca116ed8e0a8ae8a Mon Sep 17 00:00:00 2001 From: Matt Hicks Date: Sun, 21 Aug 2022 14:16:53 -0400 Subject: [PATCH] Fixes to bugs in BrowserConsoleWriter and RichBrowserOutputFormat (Resolves #327) --- build.sbt | 4 +- .../format/RichBrowserOutputFormat.scala | 101 +++++------------- .../scribe/writer/BrowserConsoleWriter.scala | 31 +++++- 3 files changed, 54 insertions(+), 82 deletions(-) diff --git a/build.sbt b/build.sbt index c1d552099..d211889a8 100644 --- a/build.sbt +++ b/build.sbt @@ -13,11 +13,11 @@ val scalaNativeVersions = scalaNot211Versions name := "scribe" ThisBuild / organization := "com.outr" -ThisBuild / version := "3.10.2" +ThisBuild / version := "3.10.3-SNAPSHOT" ThisBuild / scalaVersion := scala213 ThisBuild / scalacOptions ++= Seq("-unchecked", "-deprecation") ThisBuild / javacOptions ++= Seq("-source", "1.8", "-target", "1.8") -ThisBuild / resolvers += Resolver.sonatypeRepo("releases") +ThisBuild / resolvers ++= Resolver.sonatypeOssRepos("releases") ThisBuild / resolvers += Resolver.JCenterRepository //javaOptions in run += "-agentpath:/opt/YourKit-JavaProfiler-2020.9/bin/linux-x86-64/libyjpagent.so=delay=10000,listen=all" diff --git a/core/js/src/main/scala/scribe/output/format/RichBrowserOutputFormat.scala b/core/js/src/main/scala/scribe/output/format/RichBrowserOutputFormat.scala index b98fe828d..4ff3b38b7 100644 --- a/core/js/src/main/scala/scribe/output/format/RichBrowserOutputFormat.scala +++ b/core/js/src/main/scala/scribe/output/format/RichBrowserOutputFormat.scala @@ -3,92 +3,39 @@ package scribe.output.format import scribe.output._ import scribe.writer.BrowserConsoleWriter -import scala.collection.mutable.ListBuffer - /** * Supports rich output to JavaScript console in the browser */ object RichBrowserOutputFormat extends OutputFormat { - override def apply(output: LogOutput, stream: String => Unit): Unit = recurse( - stream = stream, - args = BrowserConsoleWriter.args, - fg = None, - bg = None, - bold = false, - italic = false, - underline = false, - strikethrough = false, - output = output - ) + import BrowserConsoleWriter.args - private def recurse(stream: String => Unit, - args: ListBuffer[String], - fg: Option[String], - bg: Option[String], - bold: Boolean, - italic: Boolean, - underline: Boolean, - strikethrough: Boolean, - output: LogOutput): Unit = output match { - case o: TextOutput => stream(o.plainText) - case o: CompositeOutput => o.entries.foreach(recurse(stream, args, fg, bg, bold, italic, underline, strikethrough, _)) - case o: ColoredOutput => { - val color = color2CSS(o.color) - stream("%c") - val css = s"color: $color" - args += css - recurse(stream, args, Some(css), bg, bold, italic, underline, strikethrough, o.output) - stream("%c") - args += fg.getOrElse(s"color: ${color2CSS(Color.Black)}") - } - case o: BackgroundColoredOutput => { - val color = color2CSS(o.color) - stream("%c") - val css = s"background-color: $color" - args += css - recurse(stream, args, fg, Some(css), bold, italic, underline, strikethrough, o.output) - stream("%c") - args += bg.getOrElse(s"background-color: ${color2CSS(Color.White)}") - } - case o: URLOutput => { - stream("%o (") - args += o.url - recurse(stream, args, fg, bg, bold, italic, underline, strikethrough, o.output) - stream(")") - } - case o: BoldOutput => if (!bold) { - stream("%c") - val css = "font-weight: bold" - args += css - recurse(stream, args, fg, bg, true, italic, underline, strikethrough, o.output) - stream("%c") - args += "font-weight: normal" - } - case o: ItalicOutput => if (!italic) { - stream("%c") - val css = "font-style: italic" - args += css - recurse(stream, args, fg, bg, bold, true, underline, strikethrough, o.output) - stream("%c") - args += "font-style: normal" - } - case o: UnderlineOutput => if (!underline) { + override def apply(output: LogOutput, stream: String => Unit): Unit = recurse(output, stream) + + private def recurse(output: LogOutput, stream: String => Unit): Unit = { + def withArg(key: String, value: String, output: LogOutput): Unit = { stream("%c") - val css = "text-decoration: underline" - args += css - recurse(stream, args, fg, bg, bold, italic, true, strikethrough, o.output) + args.around(key -> value) { + recurse(output, stream) + } stream("%c") - args += "text-decoration: none" } - case o: StrikethroughOutput => if (!strikethrough) { - stream("%c") - val css = "text-decoration: line-through" - args += css - recurse(stream, args, fg, bg, bold, italic, underline, true, o.output) - stream("%c") - args += "text-decoration: none" + output match { + case o: TextOutput => stream(o.plainText) + case o: CompositeOutput => o.entries.foreach(recurse(_, stream)) + case o: ColoredOutput => withArg("color", color2CSS(o.color), o.output) + case o: BackgroundColoredOutput => withArg("background-color", color2CSS(o.color), o.output) + case o: URLOutput => + stream("%o (") + args.around("::URL" -> o.url) { + recurse(o.output, stream) + } + stream(")") + case o: BoldOutput => withArg("font-weight", "bold", o.output) + case o: ItalicOutput => withArg("font-style", "italic", o.output) + case o: UnderlineOutput => withArg("text-decoration", "underline", o.output) + case o: StrikethroughOutput => withArg("text-decoration", "line-through", o.output) + case _ => stream(output.plainText) } - case _ => stream(output.plainText) } private def color2CSS(color: Color): String = color match { diff --git a/core/js/src/main/scala/scribe/writer/BrowserConsoleWriter.scala b/core/js/src/main/scala/scribe/writer/BrowserConsoleWriter.scala index 757063df1..53de389e0 100644 --- a/core/js/src/main/scala/scribe/writer/BrowserConsoleWriter.scala +++ b/core/js/src/main/scala/scribe/writer/BrowserConsoleWriter.scala @@ -13,16 +13,41 @@ import scala.scalajs.js * Writer specifically to target the JavaScript console in the browser */ object BrowserConsoleWriter extends Writer { - val args: ListBuffer[String] = ListBuffer.empty + private var map = Map.empty[String, String] + private var argsList = List.empty[String] + + object args { + def around[Return](t: (String, String))(f: => Return): Return = { + this += t + try { + f + } finally { + this -= t._1 + } + } + private def append(): Unit = argsList = map.map { + case (key, value) if key.startsWith("::") => value + case (key, value) => s"$key: $value" + }.mkString("; ") :: argsList + def +=(t: (String, String)): Unit = { + map += t + append() + } + def -=(key: String): Unit = { + map -= key + append() + } + } override def write(record: LogRecord, output: LogOutput, outputFormat: OutputFormat): Unit = { val b = new mutable.StringBuilder - args.clear() + map = Map.empty + argsList = Nil outputFormat.begin(b.append(_)) outputFormat(output, b.append(_)) outputFormat.end(b.append(_)) - val jsArgs = args.map(js.Any.fromString).toList + val jsArgs = argsList.map(js.Any.fromString).reverse if (record.level >= Level.Error) { console.error(b.toString(), jsArgs: _*) } else if (record.level >= Level.Warn) {