Skip to content

Commit

Permalink
Alert as alias type of String
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
halotukozak committed Jun 2, 2024
1 parent 4588821 commit f7a15ce
Show file tree
Hide file tree
Showing 30 changed files with 499 additions and 264 deletions.
2 changes: 1 addition & 1 deletion .idea/scala_compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 22 additions & 1 deletion src/main/scala/Markdown.scala
Original file line number Diff line number Diff line change
@@ -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)
85 changes: 85 additions & 0 deletions src/main/scala/api.scala
Original file line number Diff line number Diff line change
@@ -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
46 changes: 15 additions & 31 deletions src/main/scala/tables/Table.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
12 changes: 6 additions & 6 deletions src/main/scala/typography/Alert.scala
Original file line number Diff line number Diff line change
@@ -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"
5 changes: 2 additions & 3 deletions src/main/scala/typography/Code.scala
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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]

Expand Down
9 changes: 9 additions & 0 deletions src/main/scala/typography/Comment.scala
Original file line number Diff line number Diff line change
@@ -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"<!-- $inner -->"
2 changes: 1 addition & 1 deletion src/main/scala/typography/Emoji.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 5 additions & 2 deletions src/main/scala/typography/Headings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion src/main/scala/typography/List.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 8 additions & 0 deletions src/main/scala/typography/Paragraph.scala
Original file line number Diff line number Diff line change
@@ -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
15 changes: 9 additions & 6 deletions src/main/scala/typography/Quote.scala
Original file line number Diff line number Diff line change
@@ -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
9 changes: 3 additions & 6 deletions src/main/scala/typography/Reference.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)}"
15 changes: 2 additions & 13 deletions src/main/scala/typography/Text.scala
Original file line number Diff line number Diff line change
@@ -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}

Expand All @@ -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
4 changes: 2 additions & 2 deletions src/main/scala/typography/macros/List.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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") }
Expand All @@ -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"
Expand Down
8 changes: 0 additions & 8 deletions src/main/scala/typography/macros/Quote.scala

This file was deleted.

6 changes: 3 additions & 3 deletions src/main/scala/typography/macros/Text.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 + "***" }
Expand All @@ -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 }
}
}
7 changes: 0 additions & 7 deletions src/main/scala/typography/other.scala

This file was deleted.

Loading

0 comments on commit f7a15ce

Please sign in to comment.