From f7a15ce98f405951ffcf43aeb0124984a5e84934 Mon Sep 17 00:00:00 2001 From: halotukozak Date: Sun, 2 Jun 2024 23:55:44 +0200 Subject: [PATCH] Alert as alias type of String extract all api introduce string package introduce HasInner make Markdown and others context aware and context required deprecate unsafe methods add implicit conversion (still unstable) --- .idea/scala_compiler.xml | 2 +- src/main/scala/Markdown.scala | 23 ++++- src/main/scala/api.scala | 85 +++++++++++++++++++ src/main/scala/tables/Table.scala | 46 ++++------ src/main/scala/typography/Alert.scala | 12 +-- src/main/scala/typography/Code.scala | 5 +- src/main/scala/typography/Comment.scala | 9 ++ src/main/scala/typography/Emoji.scala | 2 +- src/main/scala/typography/Headings.scala | 7 +- src/main/scala/typography/List.scala | 2 +- src/main/scala/typography/Paragraph.scala | 8 ++ src/main/scala/typography/Quote.scala | 15 ++-- src/main/scala/typography/Reference.scala | 9 +- src/main/scala/typography/Text.scala | 15 +--- src/main/scala/typography/macros/List.scala | 4 +- src/main/scala/typography/macros/Quote.scala | 8 -- src/main/scala/typography/macros/Text.scala | 6 +- src/main/scala/typography/other.scala | 7 -- src/main/scala/utils/HasInner.scala | 8 ++ src/main/scala/utils/NameOfType.scala | 2 +- .../scala/integration/IntegrationTest.scala | 46 +++++----- src/test/scala/tables/TableTest.scala | 72 +++++++++------- src/test/scala/typography/CodeTest.scala | 59 ++++++++----- src/test/scala/typography/EmojiTest.scala | 9 +- src/test/scala/typography/HeadingTest.scala | 47 +++++++--- src/test/scala/typography/ListTest.scala | 57 ++++++++----- src/test/scala/typography/OtherTest.scala | 60 +++++++++++++ src/test/scala/typography/QuoteTest.scala | 44 +++++----- src/test/scala/typography/ReferenceTest.scala | 54 +++++++----- src/test/scala/typography/TextTest.scala | 40 ++++----- 30 files changed, 499 insertions(+), 264 deletions(-) create mode 100644 src/main/scala/api.scala create mode 100644 src/main/scala/typography/Comment.scala create mode 100644 src/main/scala/typography/Paragraph.scala delete mode 100644 src/main/scala/typography/macros/Quote.scala delete mode 100644 src/main/scala/typography/other.scala create mode 100644 src/main/scala/utils/HasInner.scala create mode 100644 src/test/scala/typography/OtherTest.scala diff --git a/.idea/scala_compiler.xml b/.idea/scala_compiler.xml index 3b66412..f6b25bb 100644 --- a/.idea/scala_compiler.xml +++ b/.idea/scala_compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/src/main/scala/Markdown.scala b/src/main/scala/Markdown.scala index 22c7341..1d45c38 100644 --- a/src/main/scala/Markdown.scala +++ b/src/main/scala/Markdown.scala @@ -1,3 +1,24 @@ package halotukozak.smark -def markdown(input: String*): String = input.mkString("\n\n") +import tables.Table +import utils.HasInner + +type MdContent = String | MdElement | Table +opaque type MdUnit = Unit + +private trait MdElement extends HasInner[MdContent]: + private[smark] def eval: String +end MdElement + +private class Markdown extends MdElement: + private[smark] def eval = inner.mkString("\n\n") + +end Markdown + +extension (s: MdContent) + private def eval: String = s match + case s: String => s + case e: MdElement => e.eval + case t: Table => t.eval + +implicit def stringToModifier(s: String)(using m: MdElement): MdUnit = m.add(s) \ No newline at end of file diff --git a/src/main/scala/api.scala b/src/main/scala/api.scala new file mode 100644 index 0000000..40ddd47 --- /dev/null +++ b/src/main/scala/api.scala @@ -0,0 +1,85 @@ +package halotukozak.smark + +import tables.* +import typography.* +import typography.macros.* + + +private inline def initAndAdd[T <: MdElement](inline q: T)(inline init: T ?=> MdUnit)(using m: MdElement): MdUnit = + init(using q) + m.add(q) + +def markdown(init: MdElement ?=> MdUnit): String = + given m: MdElement = new Markdown + + init(using m) + m.eval + +inline def text[Style <: TextStyle](inline inner: String)(using m: MdElement): MdUnit = m.add(textMacro[Style](inner)) + +inline def emoji[E <: Emoji](using m: MdElement): MdUnit = m.add(emojiMacro[E]) + +inline def quote[AlertType <: Alert : ValueOf](inline init: MdElement ?=> MdUnit)(using m: MdElement): MdUnit = initAndAdd(new typography.Quote[AlertType])(init) + +inline def heading[N <: HeadingLevel : ValueOf](inline init: MdElement ?=> MdUnit)(using m: MdElement): MdUnit = initAndAdd(new typography.Heading[N])(init) + +inline def code[L <: code](inline inner: L)(using m: MdElement): MdUnit = m.add(codeMacro[L](inner)) + +inline def list[Style <: ListStyle](inline elements: String*)(using m: MdElement): MdUnit = m.add(listMacro[Style](elements)) + +inline def taskList(inline points: ((Boolean, String) | String)*)(using m: MdElement): MdUnit = m.add(taskListMacro(points *)) + +inline def hr(using m: MdElement): MdUnit = m.add("***") + +inline def paragraph(inline init: MdElement ?=> MdUnit)(using m: MdElement): MdUnit = initAndAdd(new typography.Paragraph)(init) + +inline def comment(inline init: MdElement ?=> MdUnit)(using m: MdElement): MdUnit = initAndAdd(new typography.Comment)(init) + +inline def link[Title <: String](inline url: String)(using m: MdElement): MdUnit = m.add(linkMacro[Title](url)) + +inline def image[title <: String](inline url: String)(using m: MdElement): MdUnit = m.add(imageMacro[title](url)) + +def table(init: Table ?=> MdUnit)(using m: MdElement): MdUnit = + given t: Table = new Table + + init(using t) + m.add(t) + +def row(init: Row ?=> MdUnit)(using t: Table): MdUnit = + given r: Row = new Row + + init(using r) + t.add(r) + +def header(columns: (Column | String)*)(using t: Table): MdUnit = + t.add { + Header(columns.map { + case c: Column => c + case s: String => Column.None(s) + }) + } + +def cell(init: MdElement ?=> MdUnit)(using r: Row): MdUnit = + r.add { + given c: Cell = new Cell + + init(using c) + c + } + + +package string: + inline def text[Style <: TextStyle](inline inner: String): String = textMacro[Style](inner) + + inline def emoji[E <: Emoji]: String = emojiMacro[E] + + inline def link[Title <: String](inline url: String): String = linkMacro[Title](url) + + inline def image[title <: String](inline url: String): String = imageMacro[title](url) +end string + + +import _root_.scala.language.implicitConversions + +given Conversion[Unit, MdUnit] with + def apply(u: Unit): MdUnit = ().asInstanceOf \ No newline at end of file diff --git a/src/main/scala/tables/Table.scala b/src/main/scala/tables/Table.scala index b2311f7..468da85 100644 --- a/src/main/scala/tables/Table.scala +++ b/src/main/scala/tables/Table.scala @@ -3,27 +3,29 @@ package tables import tables.Column.symbol +import utils.HasInner -import scala.collection.mutable.ArrayBuffer +import scala.collection.mutable.ListBuffer -private final class Table(private[tables] val rows: ArrayBuffer[Row | Header] = new ArrayBuffer[Row | Header]) extends AnyVal: + +private[smark] final class Table extends HasInner[Row | Header]: extension (ab: Iterable[String]) private def mkRow = ab.mkString("| ", " | ", " |") - private[tables] def eval = rows.map { - case r: Row => r.cells.map(_.elem).mkRow - case h: Header => h.elements.map(_.name).mkRow + "\n" + h.elements.map(symbol).mkRow + private[smark] def eval: String = inner.map { + case r: Row => r.inner.map(_.eval).mkRow + case h: Header => h.inner.map(_.name).mkRow + "\n" + h.inner.map(symbol).mkRow }.mkString("\n") -private class Row(private[tables] val cells: ArrayBuffer[Cell] = new ArrayBuffer[Cell]) extends AnyVal +private[smark] class Row extends HasInner[Cell] -private class Header(private[tables] val elements: Seq[Column]) extends AnyVal +private[smark] class Header(elements: Seq[Column]) extends HasInner[Column](ListBuffer.from(elements)) -sealed abstract class Column(val name: String) +private[smark] sealed abstract class Column(val name: String) -private object Column: +private[smark] object Column: final case class Left(override val name: String) extends Column(name) final case class Right(override val name: String) extends Column(name) @@ -44,30 +46,12 @@ type Left = Column.Left type Right = Column.Right type Center = Column.Center type None = Column.None + given Conversion[String, Left] = Column.Left(_) given Conversion[String, Right] = Column.Right(_) given Conversion[String, Center] = Column.Center(_) given Conversion[String, None] = Column.None(_) - -private final class Cell(private[tables] val elem: String) extends AnyVal - -def table(init: Table ?=> Unit): String = - given t: Table = Table() - - init(using t) - t.eval - -def row(init: Row ?=> Unit)(using t: Table): Unit = - given r: Row = Row() - - init(using r) - t.rows += r - -def header(columns: (Column | String)*)(using t: Table): Unit = - t.rows += Header(columns.map { - case c: Column => c - case s: String => s: None - }) - -def cell(str: String)(using r: Row): Unit = r.cells += Cell(str) +private[smark] final class Cell extends MdElement: + override private[smark] def eval: String = inner.mkString("\n") +end Cell diff --git a/src/main/scala/typography/Alert.scala b/src/main/scala/typography/Alert.scala index 2b53afb..540fe30 100644 --- a/src/main/scala/typography/Alert.scala +++ b/src/main/scala/typography/Alert.scala @@ -1,9 +1,9 @@ package halotukozak.smark package typography -type Alert -type Note <: Alert -type Tip <: Alert -type Important <: Alert -type Warning <: Alert -type Caution <: Alert +type Alert = Note | Tip | Important | Warning | Caution +type Note = "Note" +type Tip = "Tip" +type Important = "Important" +type Warning = "Warning" +type Caution = "Caution" \ No newline at end of file diff --git a/src/main/scala/typography/Code.scala b/src/main/scala/typography/Code.scala index 0520e51..71fce9e 100644 --- a/src/main/scala/typography/Code.scala +++ b/src/main/scala/typography/Code.scala @@ -1,9 +1,10 @@ package halotukozak.smark package typography -import typography.macros.text import utils.nameOf +inline def codeMacro[L <: code](inline inner: L): String = "```" + nameOf[L] + "\n" + inner + "\n```" + type code <: String type scala <: code type python <: code @@ -13,8 +14,6 @@ type sql <: code type bash <: code type html <: code -inline def code[L <: code](inline inner: L): String = "```" + nameOf[L] + "\n" + inner + "\n```" - implicit class ScalaHelper(private val sc: StringContext) extends AnyVal { def scala(args: Any*): scala = sc.s(args *).asInstanceOf[scala] diff --git a/src/main/scala/typography/Comment.scala b/src/main/scala/typography/Comment.scala new file mode 100644 index 0000000..4b00ce9 --- /dev/null +++ b/src/main/scala/typography/Comment.scala @@ -0,0 +1,9 @@ +package halotukozak.smark +package typography + +final class Comment extends MdElement: + override private[smark] def eval: String = + val lines = inner.map(e => e.eval).toSeq + commentMacro(if lines.length == 1 then lines.mkString("\n") else lines.mkString("\n", "\n", "\n")) + +private[smark] inline def commentMacro(inline inner: String): String = s"" diff --git a/src/main/scala/typography/Emoji.scala b/src/main/scala/typography/Emoji.scala index 2f90bf5..cbf0585 100644 --- a/src/main/scala/typography/Emoji.scala +++ b/src/main/scala/typography/Emoji.scala @@ -3,7 +3,7 @@ package typography import utils.nameOf -inline def emoji[E <: Emoji] = s":${nameOf[E]}:" +inline def emojiMacro[E <: Emoji]: String = s":${nameOf[E]}:" type Emoji diff --git a/src/main/scala/typography/Headings.scala b/src/main/scala/typography/Headings.scala index f79ed4f..0fa6ab7 100644 --- a/src/main/scala/typography/Headings.scala +++ b/src/main/scala/typography/Headings.scala @@ -2,6 +2,9 @@ package halotukozak.smark package typography -private[typography] type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6 +private[smark] type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6 -inline def heading[N <: HeadingLevel : ValueOf](inline inner: String): String = "#" * valueOf[N] + inner +final class Heading[N <: HeadingLevel : ValueOf] extends MdElement: + override private[smark] def eval: String = inner.map(e => headingMacro[N](e.eval)).mkString("\n") + +private[smark] inline def headingMacro[N <: HeadingLevel : ValueOf](inline inner: String): String = "#" * valueOf[N] + inner diff --git a/src/main/scala/typography/List.scala b/src/main/scala/typography/List.scala index d346e73..59e9972 100644 --- a/src/main/scala/typography/List.scala +++ b/src/main/scala/typography/List.scala @@ -7,4 +7,4 @@ type Hyphen type Unordered = Asterisk | Plus | Hyphen type Ordered -private[typography] type ListStyle = Unordered | Ordered +private[smark] type ListStyle = Unordered | Ordered diff --git a/src/main/scala/typography/Paragraph.scala b/src/main/scala/typography/Paragraph.scala new file mode 100644 index 0000000..821f748 --- /dev/null +++ b/src/main/scala/typography/Paragraph.scala @@ -0,0 +1,8 @@ +package halotukozak.smark +package typography + +final class Paragraph extends MdElement: + + override private[smark] def eval: String = paragraphMacro(inner.map(e => e.eval).mkString("\n\n")) + +private[smark] inline def paragraphMacro(inline inner: String): String = "\n" + inner diff --git a/src/main/scala/typography/Quote.scala b/src/main/scala/typography/Quote.scala index 529de71..9b61350 100644 --- a/src/main/scala/typography/Quote.scala +++ b/src/main/scala/typography/Quote.scala @@ -1,10 +1,13 @@ package halotukozak.smark package typography -import typography.macros.quote +import typography.* +import typography.macros.textMacro -inline def note(inline inner: String*): String = quote[Note](inner *) -inline def tip(inline inner: String*): String = quote[Tip](inner *) -inline def important(inline inner: String*): String = quote[Important](inner *) -inline def warning(inline inner: String*): String = quote[Warning](inner *) -inline def caution(inline inner: String*): String = quote[Caution](inner *) +private[smark] final class Quote[AlertType <: Alert : ValueOf] extends MdElement: + override private[smark] def eval: String = + (s"[!${valueOf[AlertType]}]" +: inner) + .map(e => textMacro[Quoted](e.eval)) + .mkString("\n") + +end Quote \ No newline at end of file diff --git a/src/main/scala/typography/Reference.scala b/src/main/scala/typography/Reference.scala index 402c805..c5d020d 100644 --- a/src/main/scala/typography/Reference.scala +++ b/src/main/scala/typography/Reference.scala @@ -3,12 +3,9 @@ package typography import utils.nameOf -private inline def titleFrom[Title] = nameOf[Title].filterNot(_ == '"') match +private[smark] inline def titleFrom[Title] = nameOf[Title].filterNot(_ == '"') match case "scala.Predef.String" => "" case t => t -inline def link[Title <: String](inline url: String): String = s"[${titleFrom[Title]}]($url)" -inline def link(inline url: String, inline title: String): String = s"[$title]($url)" - -inline def image[title <: String](inline url: String): String = s"!${link[title](url)}" -inline def image(inline url: String, inline title: String): String = s"!${link(url, title)}" +private[smark] inline def linkMacro[Title <: String](inline url: String): String = s"[${titleFrom[Title]}]($url)" +private[smark] inline def imageMacro[title <: String](inline url: String): String = s"!${linkMacro[title](url)}" diff --git a/src/main/scala/typography/Text.scala b/src/main/scala/typography/Text.scala index f43f72a..7633084 100644 --- a/src/main/scala/typography/Text.scala +++ b/src/main/scala/typography/Text.scala @@ -1,9 +1,6 @@ package halotukozak.smark package typography -import typography.macros.text - -import scala.annotation.targetName import scala.compiletime.constValue import scala.quoted.{Expr, Quotes, quotes} @@ -16,14 +13,6 @@ type Superscript type InlineCode type BlockCode type Code = InlineCode | BlockCode -type Quote - -private[typography] type TextStyle = Normal | Bold | Italic | Strikethrough | Subscript | Superscript | Code | Quote +type Quoted -inline def bold(inner: String): String = text[Bold](inner) -inline def italic(inner: String): String = text[Italic](inner) -inline def boldAndItalic(inner: String): String = text[Bold & Italic](inner) -inline def strikethrough(inner: String): String = text[Strikethrough](inner) -inline def subscript(inner: String): String = text[Subscript](inner) -inline def superscript(inner: String): String = text[Superscript](inner) -inline def quoted(inner: String): String = text[Quote](inner) +private[smark] type TextStyle = Normal | Bold | Italic | Strikethrough | Subscript | Superscript | Code | Quoted diff --git a/src/main/scala/typography/macros/List.scala b/src/main/scala/typography/macros/List.scala index 142a695..e368f17 100644 --- a/src/main/scala/typography/macros/List.scala +++ b/src/main/scala/typography/macros/List.scala @@ -5,7 +5,7 @@ import typography.* import _root_.scala.quoted.{Expr, Quotes, Type} -inline def list[Style <: ListStyle](inline inner: String*): String = ${ listImpl[Style]('{ inner }) } +private[smark] inline def listMacro[Style <: ListStyle](inline elements: Seq[String]): String = ${ listImpl[Style]('{ elements }) } 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") } @@ -21,7 +21,7 @@ private def listImpl[Style <: ListStyle : Type](inner: Expr[Seq[String]])(using } } -inline def taskList(inline points: ((Boolean, String) | String)*): String = ${ taskListImpl('{ points }) } +private[smark] inline def taskListMacro(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" diff --git a/src/main/scala/typography/macros/Quote.scala b/src/main/scala/typography/macros/Quote.scala deleted file mode 100644 index 26be947..0000000 --- a/src/main/scala/typography/macros/Quote.scala +++ /dev/null @@ -1,8 +0,0 @@ -package halotukozak.smark -package typography.macros - -import typography.* -import utils.nameOf - -inline def quote[AlertType <: Alert](inline inner: String*): String = (s"[!${nameOf[AlertType].capitalize}]" +: inner) - .map(text[Quote]).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 660a976..f72f2b1 100644 --- a/src/main/scala/typography/macros/Text.scala +++ b/src/main/scala/typography/macros/Text.scala @@ -5,8 +5,8 @@ import typography.* import _root_.scala.quoted.{Expr, Quotes, Type} -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] = { +private[smark] inline def textMacro[Style <: TextStyle](inline inner: String): String = ${ textImpl[Style]('{ inner }) } +def textImpl[Style <: TextStyle : Type](inner: Expr[String])(using Quotes): Expr[String] = { Type.of[Style] match { case '[Normal] => inner case '[Bold & Italic] => '{ "***" + $inner + "***" } @@ -21,6 +21,6 @@ private def textImpl[Style <: TextStyle : Type](inner: Expr[String])(using Quote if ($inner.split("\n").length == 1) "`" + $inner + "`" else "```\n" + $inner + "\n```" } - case '[Quote] => '{ "> " + $inner } + case '[Quoted] => '{ "> " + $inner } } } diff --git a/src/main/scala/typography/other.scala b/src/main/scala/typography/other.scala deleted file mode 100644 index 592c0a0..0000000 --- a/src/main/scala/typography/other.scala +++ /dev/null @@ -1,7 +0,0 @@ -package halotukozak.smark -package typography - -inline def hr: String = "***" -inline def paragraph(inline inner: String*): String = "\n" + inner.mkString("\n\n") -inline def comment(inline inner: String*): String = s"" -inline def html(inline args: String*): String = "\n" + args.mkString("\n") + "\n" diff --git a/src/main/scala/utils/HasInner.scala b/src/main/scala/utils/HasInner.scala new file mode 100644 index 0000000..de3e8c4 --- /dev/null +++ b/src/main/scala/utils/HasInner.scala @@ -0,0 +1,8 @@ +package halotukozak.smark +package utils + +import scala.collection.mutable +import scala.collection.mutable.{ListBuffer, Seq} + +private[smark] abstract class HasInner[T](private[smark] val inner: ListBuffer[T] = new ListBuffer[T]): + final def add(e: T): Unit = inner.addOne(e) diff --git a/src/main/scala/utils/NameOfType.scala b/src/main/scala/utils/NameOfType.scala index 92c8928..5d3d219 100644 --- a/src/main/scala/utils/NameOfType.scala +++ b/src/main/scala/utils/NameOfType.scala @@ -5,6 +5,6 @@ import scala.quoted.{Expr, Quotes, Type} inline def nameOf[T]: String = ${ nameOfImpl[T] } -def nameOfImpl[T](using Type[T], Quotes): Expr[String] = Expr { +def nameOfImpl[T: Type](using Quotes): Expr[String] = Expr { """halotukozak\.smark\.typography\.[A-Za-z]+\$package\.""".r.replaceAllIn(Type.show[T], "") } diff --git a/src/test/scala/integration/IntegrationTest.scala b/src/test/scala/integration/IntegrationTest.scala index 1ba1b52..2b5db04 100644 --- a/src/test/scala/integration/IntegrationTest.scala +++ b/src/test/scala/integration/IntegrationTest.scala @@ -8,51 +8,52 @@ import typography.macros.* import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class IntegrationTest extends AnyWordSpec with Matchers { +final class IntegrationTest extends AnyWordSpec with Matchers: "Integration" should { "work" in { - markdown( - text[Normal]("hello"), - text[Bold]("boldedHello"), - text[Normal](text[Italic]("world")), - heading[1]("heading1"), - heading[2](bold("heading2")), + markdown { + text[Normal]("hello") + text[Bold]("boldedHello") + text[Normal](string.text[Italic]("world")) + heading[1]("heading1") + heading[2](string.text[Bold]("heading2")) code { scala"""val x = 1""" - }, - emoji[joy], - quote[Caution]("read this!"), - paragraph( - text[Bold & Italic]("boldedHello"), - heading[4](bold("heading4")), + } + emoji[joy] + quote[Caution]("read this!") + paragraph { + text[Bold & Italic]("boldedHello") + heading[4](string.text[Bold]("heading4")) list[Ordered]( "item1", "item2", ) - ), + } taskList( true -> "task1", false -> "task2", "task3" - ), - image["Google"](url = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png"), - link("https://www.google.com", "Google"), + ) + image["Google"](url = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png") + link["Google"](url = "https://www.google.com") table { row { cell { text[Bold]("a") + text[Italic]("ba") } cell("b") } row { cell { - text[Code](scala"val x = 1") + // string.text[Code](scala"val x = 1") } cell("d") } - }, - ) shouldEqual + } + } shouldEqual """hello | |**boldedHello** @@ -88,9 +89,8 @@ class IntegrationTest extends AnyWordSpec with Matchers { | |[Google](https://www.google.com) | - || **a** | b | + || **a***ba* | b | || `val x = 1` | d |""".stripMargin } } - -} +end IntegrationTest diff --git a/src/test/scala/tables/TableTest.scala b/src/test/scala/tables/TableTest.scala index bfb2894..175545d 100644 --- a/src/test/scala/tables/TableTest.scala +++ b/src/test/scala/tables/TableTest.scala @@ -1,59 +1,69 @@ package halotukozak.smark package tables -import typography.macros.text -import typography.{Bold, Code, ScalaHelper} +import tables.* +import typography.macros.* +import typography.* import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec - -final class TableTest extends AnyWordSpec with Matchers { +final class TableTest extends AnyWordSpec with Matchers: "table" should { "be evaluated" in { - table { - row {} + markdown { + table { + row {} + } } shouldBe "| |" - table { - row { - cell("a") - cell("b") + markdown { + table { + row { + cell("a") + cell("b") + } } } shouldBe "| a | b |" - table { - row { - cell { - text[Bold]("a") + markdown { + table { + row { + cell { + textMacro[Bold]("a") + } + cell("b") } - cell("b") - } - row { - cell { - text[Code](scala"val x = 1") + row { + cell { + textMacro[Code](scala"val x = 1") + } + cell("d") } - cell("d") } } shouldBe "| **a** | b |\n| `val x = 1` | d |" - table { - header("a": Left, "b") - row { - cell("1") - cell("2") + markdown { + table { + header("a": Left, "b") + row { + cell("1") + cell("2") + } } } shouldBe "| a | b |\n| :--- | --- |\n| 1 | 2 |" - table { - header("a": Right, "b": Center) - row { - cell("1") - cell("2") + markdown { + table { + header("a": Right, "b": Center) + row { + cell("1") + cell("2") + } } } shouldBe "| a | b |\n| ---: | :---: |\n| 1 | 2 |" } } -} +end TableTest diff --git a/src/test/scala/typography/CodeTest.scala b/src/test/scala/typography/CodeTest.scala index df239eb..c48ef56 100644 --- a/src/test/scala/typography/CodeTest.scala +++ b/src/test/scala/typography/CodeTest.scala @@ -1,17 +1,20 @@ package halotukozak.smark package typography -import typography.macros.text +import tables.* +import typography.* +import typography.macros.* import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -final class CodeTest extends AnyWordSpec with Matchers { +final class CodeTest extends AnyWordSpec with Matchers: + "code text" should { "be evaluated correctly" when { "not specified" in { - text[Code]("HelloWorld") shouldBe "`HelloWorld`" - text[Code] { + string.text[Code]("HelloWorld") shouldBe "`HelloWorld`" + string.text[Code] { """ |var a = 1 |var b = 2 @@ -25,8 +28,8 @@ final class CodeTest extends AnyWordSpec with Matchers { |```""".stripMargin } - text[Code]("HelloWorld") shouldBe "`HelloWorld`" - text[Code] { + string.text[Code]("HelloWorld") shouldBe "`HelloWorld`" + string.text[Code] { """ |var a = 1 |var b = 2 @@ -42,53 +45,69 @@ final class CodeTest extends AnyWordSpec with Matchers { } "python" in { - code(python"""print("HelloWorld")""") shouldBe + markdown { + code(python"""print("HelloWorld")""") + } shouldBe """```python |print("HelloWorld") |```""".stripMargin } "scala" in { - code(scala"""println("HelloWorld")""") shouldBe + markdown { + code(scala"""println("HelloWorld")""") + } shouldBe """```scala |println("HelloWorld") |```""".stripMargin } "java" in { - code(java"""System.out.println("HelloWorld")""") shouldBe + markdown { + code(java"""System.out.println("HelloWorld");""") + } shouldBe """```java - |System.out.println("HelloWorld") + |System.out.println("HelloWorld"); |```""".stripMargin } "kotlin" in { - code(kotlin"""println("HelloWorld")""") shouldBe + markdown { + code(kotlin"""println("HelloWorld")""") + } shouldBe """```kotlin |println("HelloWorld") |```""".stripMargin } "sql" in { - code(sql"""SELECT * FROM table""") shouldBe + markdown { + code(sql"""SELECT * FROM table""") + } shouldBe """```sql |SELECT * FROM table |```""".stripMargin } "bash" in { - code(bash"""echo "HelloWorld"""") shouldBe + markdown { + code(bash"""echo "HelloWorld"""") + } shouldBe """```bash |echo "HelloWorld" |```""".stripMargin } "html" in { - code(html"""

HelloWorld

""") shouldBe + markdown { + code(html"""

HelloWorld

""") + } shouldBe """```html |

HelloWorld

|```""".stripMargin } "custom" in { type custom <: code - code[custom]( - """ - |println("HelloWorld") - |""".stripMargin.asInstanceOf[custom]) shouldBe + markdown { + code[custom]( + """ + |println("HelloWorld") + |""".stripMargin.asInstanceOf[custom]) + } shouldBe """```custom | |println("HelloWorld") @@ -96,6 +115,6 @@ final class CodeTest extends AnyWordSpec with Matchers { |```""".stripMargin } } - } -} + +end CodeTest diff --git a/src/test/scala/typography/EmojiTest.scala b/src/test/scala/typography/EmojiTest.scala index f2949b2..dc02c1e 100644 --- a/src/test/scala/typography/EmojiTest.scala +++ b/src/test/scala/typography/EmojiTest.scala @@ -4,12 +4,13 @@ package typography import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -class EmojiTest extends AnyWordSpec with Matchers { +final class EmojiTest extends AnyWordSpec with Matchers: "emoji" should { "be evaluated correctly" in { - emoji[smile] shouldBe ":smile:" - emoji[`+1`] shouldBe ":+1:" + emojiMacro[smile] shouldBe ":smile:" + emojiMacro[`+1`] shouldBe ":+1:" } } -} + +end EmojiTest diff --git a/src/test/scala/typography/HeadingTest.scala b/src/test/scala/typography/HeadingTest.scala index 62b408a..a51a7e5 100644 --- a/src/test/scala/typography/HeadingTest.scala +++ b/src/test/scala/typography/HeadingTest.scala @@ -2,41 +2,66 @@ package halotukozak.smark package typography +import tables.* +import typography.* import typography.macros.* import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -final class HeadingTest extends AnyWordSpec with Matchers { +final class HeadingTest extends AnyWordSpec with Matchers: "Heading" should { "be evaluated" when { "level 1" in { - heading[1]("Hello") shouldBe "#Hello" + markdown { + heading[1] { + "Hello" + } + } shouldBe "#Hello" } "level 2" in { - heading[2]("Hello") shouldBe "##Hello" + markdown { + heading[2]("Hello") + } shouldBe "##Hello" } "level 3" in { - heading[3]("Hello") shouldBe "###Hello" + markdown { + heading[3]("Hello") + } shouldBe "###Hello" } "level 4" in { - heading[4]("Hello") shouldBe "####Hello" + markdown { + heading[4]("Hello") + } shouldBe "####Hello" } "level 5" in { - heading[5]("Hello") shouldBe "#####Hello" + markdown { + heading[5]("Hello") + } shouldBe "#####Hello" } "level 6" in { - heading[6]("Hello") shouldBe "######Hello" + markdown { + heading[6]("Hello") + } shouldBe "######Hello" + } + "multiline" in { + markdown { + heading[1] { + text[Normal]("Hello") + text[Bold]("World") + } + } shouldBe "#Hello\n#**World**" } } "not be evaluated" when { "level is out of range" in { - assertDoesNotCompile("""heading[7]("Hello")""") - assertDoesNotCompile("""heading[0]("Hello")""") - assertDoesNotCompile("""heading[-5]("Hello")""") + assertDoesNotCompile("""markdown{ heading[7]("Hello") }""") + assertDoesNotCompile("""markdown{ heading[0]("Hello") }""") + assertDoesNotCompile("""markdown{ heading[-5]("Hello") }""") } } } -} \ No newline at end of file + +end HeadingTest diff --git a/src/test/scala/typography/ListTest.scala b/src/test/scala/typography/ListTest.scala index 66ca1da..480ebd9 100644 --- a/src/test/scala/typography/ListTest.scala +++ b/src/test/scala/typography/ListTest.scala @@ -1,29 +1,41 @@ package halotukozak.smark package typography -import typography.macros.{list, taskList} +import tables.* +import typography.* +import typography.macros.* import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -final class ListTest extends AnyWordSpec with Matchers { +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" + markdown { + list[Asterisk]("a", "b", "c") + } shouldBe "* a\n* b\n* c" } "plus" in { - list[Plus]("a", "b", "c") shouldBe "+ a\n+ b\n+ c" + markdown { + list[Plus]("a", "b", "c") + } shouldBe "+ a\n+ b\n+ c" } "hyphen" in { - list[Hyphen]("a", "b", "c") shouldBe "- a\n- b\n- c" + markdown { + list[Hyphen]("a", "b", "c") + } shouldBe "- a\n- b\n- c" } "unordered" in { - list[Unordered]("a", "b", "c") shouldBe "* a\n* b\n* c" + markdown { + 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" + markdown { + list[Ordered]("a", "b", "c") + } shouldBe "1. a\n2. b\n3. c" } "default" ignore { // list("a", "b", "c") shouldBe "* a\n* b\n* c" @@ -36,23 +48,28 @@ final class ListTest extends AnyWordSpec with Matchers { "task list" should { "be evaluated" when { "only task" in { - taskList("a", "b", "c") shouldBe "- [ ] a\n- [ ] b\n- [ ] c" + markdown { + 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" + markdown { + 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" + markdown { + taskList( + true -> "a", + "b", + false -> "c", + ) + } shouldBe "- [x] a\n- [ ] b\n- [ ] c" } } } - -} +end ListTest diff --git a/src/test/scala/typography/OtherTest.scala b/src/test/scala/typography/OtherTest.scala new file mode 100644 index 0000000..c2f2d07 --- /dev/null +++ b/src/test/scala/typography/OtherTest.scala @@ -0,0 +1,60 @@ +package halotukozak.smark +package typography + +import tables.* +import typography.* +import typography.macros.* + +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +final class OtherTest extends AnyWordSpec with Matchers: + + "paragraph" should { + "be evaluated correctly" in { + markdown { + paragraph { + text[Normal]("hello") + text[Bold]("boldedHello") + text[Normal](string.text[Italic]("world")) + } + } shouldBe + """ + |hello + | + |**boldedHello** + | + |*world*""".stripMargin + } + } + + "comment" should { + "be evaluated correctly" in { + markdown { + comment("hello") + } shouldBe "" + + markdown { + comment { + text[Normal]("hello") + text[Bold]("boldedHello") + text[Normal](string.text[Italic]("world")) + } + } shouldBe + """""".stripMargin + } + } + + "hr" should { + "be evaluated correctly" in { + markdown { + hr + } shouldBe "***" + } + } +end OtherTest + diff --git a/src/test/scala/typography/QuoteTest.scala b/src/test/scala/typography/QuoteTest.scala index 77dccfd..5747a97 100644 --- a/src/test/scala/typography/QuoteTest.scala +++ b/src/test/scala/typography/QuoteTest.scala @@ -2,40 +2,46 @@ package halotukozak.smark package typography import typography.* -import typography.macros.quote +import typography.macros.* import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -final class QuoteTest extends AnyWordSpec with Matchers { + +final class QuoteTest extends AnyWordSpec with Matchers: + "quote" should { "be evaluated correctly" in { - quote[Note]("Hello, World!") shouldBe + markdown { + quote[Note]("Hello, World!") + } shouldBe """|> [!Note] |> Hello, World!""".stripMargin - quote[Tip]("Hello, World!") shouldBe + markdown { + quote[Tip]("Hello, World!") + } shouldBe """|> [!Tip] |> Hello, World!""".stripMargin - quote[Warning]("Hello, World!") shouldBe + markdown { + quote[Warning]("Hello, World!") + } shouldBe """|> [!Warning] |> Hello, World!""".stripMargin - quote[Caution]("Hello, World!") shouldBe + markdown { + quote[Caution]("Hello, World!") + } shouldBe """|> [!Caution] |> Hello, World!""".stripMargin - typography.note("Hello, World!") shouldBe + markdown { + quote[Note] { + text[Normal]("Hello") + text[Bold]("World") + } + } shouldBe """|> [!Note] - |> Hello, World!""".stripMargin - tip("Hello, World!") shouldBe - """|> [!Tip] - |> Hello, World!""".stripMargin - warning("Hello, World!") shouldBe - """|> [!Warning] - |> Hello, World!""".stripMargin - caution("Hello, World!") shouldBe - """|> [!Caution] - |> Hello, World!""".stripMargin - + |> Hello + |> **World**""".stripMargin } } -} +end QuoteTest diff --git a/src/test/scala/typography/ReferenceTest.scala b/src/test/scala/typography/ReferenceTest.scala index b5fdf1d..b59c2d5 100644 --- a/src/test/scala/typography/ReferenceTest.scala +++ b/src/test/scala/typography/ReferenceTest.scala @@ -1,37 +1,49 @@ package halotukozak.smark package typography +import tables.* +import typography.* +import typography.macros.* + import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -final class ReferenceTest extends AnyWordSpec with Matchers { +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)" - + string.link["Google"]("https://www.google.com") shouldBe "[Google](https://www.google.com)" + string.link[""]("https://www.google.com") shouldBe "[](https://www.google.com)" + string.link("https://www.google.com") shouldBe "[](https://www.google.com)" + + markdown { + link["Google"]("https://www.google.com") + } shouldBe "[Google](https://www.google.com)" + markdown { + link[""]("https://www.google.com") + } shouldBe "[](https://www.google.com)" + markdown { + link("https://www.google.com") + } shouldBe "[](https://www.google.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)" + string.image["img"]("https://www.google.com") shouldBe "![img](https://www.google.com)" + string.image[""]("https://www.google.com") shouldBe "![](https://www.google.com)" + string.image("https://www.google.com") shouldBe "![](https://www.google.com)" + + markdown { + image["img"]("https://www.google.com") + } shouldBe "![img](https://www.google.com)" + markdown { + image[""]("https://www.google.com") + } shouldBe "![](https://www.google.com)" + markdown { + image("https://www.google.com") + } shouldBe "![](https://www.google.com)" } } -} + +end ReferenceTest diff --git a/src/test/scala/typography/TextTest.scala b/src/test/scala/typography/TextTest.scala index 89090df..c5c80f3 100644 --- a/src/test/scala/typography/TextTest.scala +++ b/src/test/scala/typography/TextTest.scala @@ -1,61 +1,55 @@ package halotukozak.smark package typography -import typography.macros.text +import tables.* +import typography.* +import typography.macros.* import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -final class TextTest extends AnyWordSpec with Matchers { +final class TextTest extends AnyWordSpec with Matchers: "Text eval" when { "normal text" in { - text[Normal]("HelloWorld") shouldBe "HelloWorld" + string.text[Normal]("HelloWorld") shouldBe "HelloWorld" } "bold text" in { - text[Bold]("HelloWorld") shouldBe "**HelloWorld**" + string.text[Bold]("HelloWorld") shouldBe "**HelloWorld**" } "italic text" in { - text[Italic]("HelloWorld") shouldBe "*HelloWorld*" + string.text[Italic]("HelloWorld") shouldBe "*HelloWorld*" } "bold italic text" in { - text[Bold & Italic]("HelloWorld") shouldBe "***HelloWorld***" + string.text[Bold & Italic]("HelloWorld") shouldBe "***HelloWorld***" - text[Italic & Bold]("HelloWorld") shouldBe "***HelloWorld***" + string.text[Italic & Bold]("HelloWorld") shouldBe "***HelloWorld***" } "strikethrough text" in { - text[Strikethrough]("HelloWorld") shouldBe "~~HelloWorld~~" + string.text[Strikethrough]("HelloWorld") shouldBe "~~HelloWorld~~" } "subscript text" in { - text[Subscript]("HelloWorld") shouldBe "HelloWorld" + string.text[Subscript]("HelloWorld") shouldBe "HelloWorld" } "superscript text" in { - text[Superscript]("HelloWorld") shouldBe "HelloWorld" + string.text[Superscript]("HelloWorld") shouldBe "HelloWorld" } "quoted text" in { - text[Quote]("HelloWorld") shouldBe "> HelloWorld" + string.text[Quoted]("HelloWorld") shouldBe "> HelloWorld" } } "Nested text" should { "be evaluated correctly" in { - text[Bold]("Hello" + text[Italic]("World")) shouldBe "**Hello*World***" - text[Strikethrough]("Hello" + text[Subscript]("World")) shouldBe "~~HelloWorld~~" - text[Bold](text[Bold](text[Bold]("Hello"))) shouldBe "******Hello******" + string.text[Bold]("Hello" + string.text[Italic]("World")) shouldBe "**Hello*World***" + string.text[Strikethrough]("Hello" + string.text[Subscript]("World")) shouldBe "~~HelloWorld~~" + string.text[Bold](string.text[Bold](string.text[Bold]("Hello"))) shouldBe "******Hello******" } } "untyped" should { - "be evaluated correctly" in { - bold("HelloWorld") shouldBe "**HelloWorld**" - italic("HelloWorld") shouldBe "*HelloWorld*" - boldAndItalic("HelloWorld") shouldBe "***HelloWorld***" - strikethrough("HelloWorld") shouldBe "~~HelloWorld~~" - subscript("HelloWorld") shouldBe "HelloWorld" - superscript("HelloWorld") shouldBe "HelloWorld" - } "not be evaluated" in { assertDoesNotCompile("text(\"HelloWorld\")") } } -} +end TextTest