From 8c23762a55d811791bff3629865e6a808fa6eb07 Mon Sep 17 00:00:00 2001 From: halotukozak Date: Sun, 7 Apr 2024 00:24:01 +0200 Subject: [PATCH] add list, task list, link, img support --- src/main/scala/Markdown.scala | 3 + src/main/scala/typography/Code.scala | 5 ++ src/main/scala/typography/List.scala | 11 ++++ src/main/scala/typography/Reference.scala | 13 +++++ src/main/scala/typography/macros/List.scala | 31 ++++++++++ src/main/scala/typography/macros/Text.scala | 6 +- src/main/scala/typography/other.scala | 5 ++ .../utils/{types.scala => NameOfType.scala} | 0 src/test/scala/typography/CodeTest.scala | 6 ++ src/test/scala/typography/ListTest.scala | 58 +++++++++++++++++++ src/test/scala/typography/ReferenceTest.scala | 37 ++++++++++++ 11 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 src/main/scala/Markdown.scala create mode 100644 src/main/scala/typography/List.scala create mode 100644 src/main/scala/typography/Reference.scala create mode 100644 src/main/scala/typography/macros/List.scala create mode 100644 src/main/scala/typography/other.scala rename src/main/scala/utils/{types.scala => NameOfType.scala} (100%) create mode 100644 src/test/scala/typography/ListTest.scala create mode 100644 src/test/scala/typography/ReferenceTest.scala diff --git a/src/main/scala/Markdown.scala b/src/main/scala/Markdown.scala new file mode 100644 index 0000000..f86cab2 --- /dev/null +++ b/src/main/scala/Markdown.scala @@ -0,0 +1,3 @@ +package halotukozak.smark + +def markdown(input: String*): String = input.mkString("\n") diff --git a/src/main/scala/typography/Code.scala b/src/main/scala/typography/Code.scala index a91ca5d..e945ef8 100644 --- a/src/main/scala/typography/Code.scala +++ b/src/main/scala/typography/Code.scala @@ -23,6 +23,11 @@ implicit class ScalaHelper(private val sc: StringContext) extends AnyVal { def java(args: Any*): java = sc.s(args *).asInstanceOf[java] def kotlin(args: Any*): kotlin = sc.s(args *).asInstanceOf[kotlin] + def sql(args: Any*): sql = sc.s(args *).asInstanceOf[sql] + def bash(args: Any*): bash = sc.s(args *).asInstanceOf[bash] + + + def html(args: Any*): String = "\n" + sc.s(args *) + "\n" } diff --git a/src/main/scala/typography/List.scala b/src/main/scala/typography/List.scala new file mode 100644 index 0000000..3d5736c --- /dev/null +++ b/src/main/scala/typography/List.scala @@ -0,0 +1,11 @@ +package halotukozak.smark +package typography + +type Asterisk +type Plus +type Hyphen +type Unordered = Asterisk | Plus | Hyphen +type Ordered + +private[typography] type ListStyle = Unordered | Ordered + diff --git a/src/main/scala/typography/Reference.scala b/src/main/scala/typography/Reference.scala new file mode 100644 index 0000000..50ef1a6 --- /dev/null +++ b/src/main/scala/typography/Reference.scala @@ -0,0 +1,13 @@ +package halotukozak.smark +package typography + +import utils.nameOf + +private inline def titleFrom[Title] = nameOf[Title].filterNot(_ == '"') match + case "scala.Predef.String" => "" + case t => t +inline def link[Title <: String](inline url: String): String = "[" + titleFrom[Title] + "](" + url + ")" +inline def link(inline url: String, inline title: String): String = "[" + title + "](" + url + ")" + +inline def image[title <: String](inline url: String): String = "![" + titleFrom[title] + "](" + url + ")" +inline def image(inline url: String, inline title: String): String = "![" + title + "](" + url + ")" \ No newline at end of file diff --git a/src/main/scala/typography/macros/List.scala b/src/main/scala/typography/macros/List.scala new file mode 100644 index 0000000..142a695 --- /dev/null +++ b/src/main/scala/typography/macros/List.scala @@ -0,0 +1,31 @@ +package halotukozak.smark +package typography.macros + +import typography.* + +import _root_.scala.quoted.{Expr, Quotes, Type} + +inline def list[Style <: ListStyle](inline inner: String*): String = ${ listImpl[Style]('{ inner }) } +private def listImpl[Style <: ListStyle : Type](inner: Expr[Seq[String]])(using Quotes): Expr[String] = { + Type.of[Style] match { + case '[Ordered] => '{ $inner.zipWithIndex.map((s, i) => s"${i + 1}. $s").mkString("\n") } + case '[Unordered] => + '{ + val marker = ${ Expr(Type.of[Style] match + case '[Plus] => "+" + case '[Hyphen] => "-" + case _ => "*" + ) } + $inner.map(s => s"$marker $s").mkString("\n") + } + } +} + +inline def taskList(inline points: ((Boolean, String) | String)*): String = ${ taskListImpl('{ points }) } +private def taskListImpl(points: Expr[Seq[(Boolean, String) | String]])(using Quotes): Expr[String] = { + '{ $points.map { + case (true, s) => s"- [x] $s" + case (false, s) => s"- [ ] $s" + case s: String => s"- [ ] $s" + }.mkString("\n") } +} \ No newline at end of file diff --git a/src/main/scala/typography/macros/Text.scala b/src/main/scala/typography/macros/Text.scala index b6c5c17..660a976 100644 --- a/src/main/scala/typography/macros/Text.scala +++ b/src/main/scala/typography/macros/Text.scala @@ -5,9 +5,9 @@ import typography.* import _root_.scala.quoted.{Expr, Quotes, Type} -inline def text[Styles <: TextStyle](inline inner: String): String = ${ textImpl[Styles]('{ inner }) } -private def textImpl[Styles <: TextStyle : Type](inner: Expr[String])(using Quotes): Expr[String] = { - Type.of[Styles] match { +inline def text[Style <: TextStyle](inline inner: String): String = ${ textImpl[Style]('{ inner }) } +private def textImpl[Style <: TextStyle : Type](inner: Expr[String])(using Quotes): Expr[String] = { + Type.of[Style] match { case '[Normal] => inner case '[Bold & Italic] => '{ "***" + $inner + "***" } case '[Bold] => '{ "**" + $inner + "**" } diff --git a/src/main/scala/typography/other.scala b/src/main/scala/typography/other.scala new file mode 100644 index 0000000..c231d64 --- /dev/null +++ b/src/main/scala/typography/other.scala @@ -0,0 +1,5 @@ +package halotukozak.smark +package typography + + +inline def hr: String = "***" \ No newline at end of file diff --git a/src/main/scala/utils/types.scala b/src/main/scala/utils/NameOfType.scala similarity index 100% rename from src/main/scala/utils/types.scala rename to src/main/scala/utils/NameOfType.scala diff --git a/src/test/scala/typography/CodeTest.scala b/src/test/scala/typography/CodeTest.scala index a660241..e11b01a 100644 --- a/src/test/scala/typography/CodeTest.scala +++ b/src/test/scala/typography/CodeTest.scala @@ -77,6 +77,12 @@ final class CodeTest extends AnyWordSpec with Matchers { |echo "HelloWorld" |```""".stripMargin } + "html" in { + html"""

HelloWorld

""" shouldBe + """ + |

HelloWorld

+ |""".stripMargin + } "Custom" in { codeUnsafe( "custom", diff --git a/src/test/scala/typography/ListTest.scala b/src/test/scala/typography/ListTest.scala new file mode 100644 index 0000000..66ca1da --- /dev/null +++ b/src/test/scala/typography/ListTest.scala @@ -0,0 +1,58 @@ +package halotukozak.smark +package typography + +import typography.macros.{list, taskList} + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +final class ListTest extends AnyWordSpec with Matchers { + + "List" should { + "be evaluated" when { + "asterisk" in { + list[Asterisk]("a", "b", "c") shouldBe "* a\n* b\n* c" + } + "plus" in { + list[Plus]("a", "b", "c") shouldBe "+ a\n+ b\n+ c" + } + "hyphen" in { + list[Hyphen]("a", "b", "c") shouldBe "- a\n- b\n- c" + } + "unordered" in { + list[Unordered]("a", "b", "c") shouldBe "* a\n* b\n* c" + } + "ordered" in { + list[Ordered]("a", "b", "c") shouldBe "1. a\n2. b\n3. c" + } + "default" ignore { + // list("a", "b", "c") shouldBe "* a\n* b\n* c" + } + + + } + } + + "task list" should { + "be evaluated" when { + "only task" in { + taskList("a", "b", "c") shouldBe "- [ ] a\n- [ ] b\n- [ ] c" + } + "with completion status" in { + taskList( + true -> "a", + false -> "b", + true -> "c", + ) shouldBe "- [x] a\n- [ ] b\n- [x] c" + } + "mixed" in { + taskList( + true -> "a", + "b", + false -> "c", + ) shouldBe "- [x] a\n- [ ] b\n- [ ] c" + } + } + } + +} diff --git a/src/test/scala/typography/ReferenceTest.scala b/src/test/scala/typography/ReferenceTest.scala new file mode 100644 index 0000000..b5fdf1d --- /dev/null +++ b/src/test/scala/typography/ReferenceTest.scala @@ -0,0 +1,37 @@ +package halotukozak.smark +package typography + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +final class ReferenceTest extends AnyWordSpec with Matchers { + + "link" should { + "be evaluated correctly" in { + link["Google"]("https://www.google.com") shouldBe "[Google](https://www.google.com)" + link[""]("https://www.google.com") shouldBe "[](https://www.google.com)" + link("https://www.google.com") shouldBe "[](https://www.google.com)" + + for name <- Seq("google", "facebook", "twitter") do + link("https://www." + name + ".com") shouldBe s"[](https://www.$name.com)" + + for name <- Seq("google", "facebook", "twitter") do + link("https://www." + name + ".com", name) shouldBe s"[$name](https://www.$name.com)" + + } + } + + "image" should { + "be evaluated correctly" in { + image["img"]("https://www.google.com") shouldBe "![img](https://www.google.com)" + image[""]("https://www.google.com") shouldBe "![](https://www.google.com)" + image("https://www.google.com") shouldBe "![](https://www.google.com)" + + for name <- Seq("google", "facebook", "twitter") do + image("https://www." + name + ".com") shouldBe s"![](https://www.$name.com)" + + for name <- Seq("google", "facebook", "twitter") do + image("https://www." + name + ".com", name) shouldBe s"![$name](https://www.$name.com)" + } + } +}