Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply Mdoc to inline code #554

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
10 changes: 8 additions & 2 deletions cli/src/main/scala/mdoc/Variable.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package mdoc

import mdoc.internal.markdown.GenModifier
import mdoc.internal.markdown.Modifier
import mdoc.internal.markdown.ModifierInline
import mdoc.internal.markdown.ReplVariablePrinter
import scala.meta.inputs.Position

Expand Down Expand Up @@ -58,9 +60,13 @@ final class Variable private[mdoc] (
val totalVariablesInStatement: Int,
val indexOfStatementInCodeFence: Int,
val totalStatementsInCodeFence: Int,
private[mdoc] val mods: Modifier
private[mdoc] val mods: GenModifier
) {
def isToString: Boolean = mods.isToString
def isToString: Boolean =
mods match {
case fenceModifier: Modifier => fenceModifier.isToString
case modifierInline: ModifierInline => false
}
def isUnit: Boolean = staticType.endsWith("Unit")
override def toString: String = {
ReplVariablePrinter(this)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package mdoc.internal.markdown
20 changes: 20 additions & 0 deletions cli/src/main/scala/mdoc/internal/markdown/ModInline.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package mdoc.internal.markdown

import scala.util.Try

sealed abstract class ModInline extends Product with Serializable
object ModInline {
// The default behavior will be CompileOnly, so we don't need that Mod
case object Fail extends ModInline
case object Warn extends ModInline

def static: List[ModInline] =
List(
Fail,
Warn,
)

def unapply(string: String): Option[ModInline] = {
static.find(_.toString.equalsIgnoreCase(string))
}
}
51 changes: 46 additions & 5 deletions cli/src/main/scala/mdoc/internal/markdown/Modifier.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ package mdoc.internal.markdown
import mdoc.StringModifier
import mdoc.internal.markdown.Mod._


sealed trait GenModifier {
val mods: Set[Mod]
def isDefault: Boolean = mods.isEmpty
def isFailOrWarn: Boolean = isFail || isWarn
def isFail: Boolean = mods(Fail)
def isWarn: Boolean = mods(Warn)
}

/** A mdoc code fence modifier.
*
* Modifiers are parsed from code blocks like here
Expand All @@ -13,11 +22,7 @@ import mdoc.internal.markdown.Mod._
*
* Currently, only supports parsing one modifier per code block.
*/
sealed abstract class Modifier(val mods: Set[Mod]) {
def isDefault: Boolean = mods.isEmpty
def isFailOrWarn: Boolean = isFail || isWarn
def isFail: Boolean = mods(Fail)
def isWarn: Boolean = mods(Warn)
sealed abstract class Modifier(val mods: Set[Mod]) extends GenModifier {
def isPassthrough: Boolean = mods(Passthrough)
def isString: Boolean = this.isInstanceOf[Modifier.Str]
def isPre: Boolean = this.isInstanceOf[Modifier.Pre]
Expand Down Expand Up @@ -82,3 +87,39 @@ object Modifier {
case class Pre(mod: mdoc.PreModifier, info: String) extends Modifier(Set.empty)

}

/** An mdoc inline code modifier.
*
* Modifiers are parsed from inline code blocks like here
*
* `scala mdoc:passthrough println("# Header")`
*
* Currently, only supports parsing one modifier per code block.
*/
case class ModifierInline(val mods: Set[Mod]) extends GenModifier
object ModifierInline {
object Default {
def apply(): ModifierInline = ModifierInline(Set.empty)
}
object Fail {
def unapply(m: ModifierInline): Boolean =
m.isFailOrWarn
}
object Warn {
def unapply(m: ModifierInline): Boolean =
m.isWarn
}

def apply(string: String): Option[ModifierInline] = {
val mods = string.split(":").map {
case Mod(m) => Some(m)
case _ => None
}
if (mods.forall(_.isDefined)) {
Some(ModifierInline(mods.iterator.map(_.get).toSet))
} else {
None
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package mdoc.internal.markdown

import mdoc.StringModifier
import mdoc.internal.markdown.Mod._
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,13 @@ class ReplVariablePrinter(
if (binder.isToString) {
appendMultiline(sb, binder.runtimeValue.toString)
} else {
val heightOverride = binder.mods.heightOverride
val widthOverride = binder.mods.widthOverride
val (heightOverride, widthOverride) =
binder.mods match {
case fenceModifier: Modifier =>
(fenceModifier.heightOverride, fenceModifier.widthOverride)
case modifierInline: ModifierInline =>
(None, None)
}

val lines = pprint.PPrinter.BlackWhite.tokenize(
binder.runtimeValue,
Expand Down
74 changes: 53 additions & 21 deletions mdoc/src/main/scala-2/mdoc/internal/markdown/FailInstrumenter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,63 @@ final class FailInstrumenter(sections: List[SectionInput], i: Int) {
sections.zipWithIndex.foreach { case (section, j) =>
if (j > i) ()
else {
if (section.mod.isReset) {
nest.unnest()
sb.print(Instrumenter.reset(section.mod, gensym.fresh("App")))
} else if (section.mod.isNest) {
nest.nest()
}
if (j == i || !section.mod.isFailOrWarn) {
section.source.stats.foreach { stat =>
stat match {
case i: Import =>
i.importers.foreach {
case Importer(
Term.Name(name),
List(Importee.Name(_: Name.Indeterminate))
section.mod match {
case fenceModifier: Modifier =>
if (fenceModifier.isReset) {
nest.unnest()
sb.print(Instrumenter.reset(fenceModifier, gensym.fresh("App")))
} else if (fenceModifier.isNest) {
nest.nest()
}
if (j == i || !fenceModifier.isFailOrWarn) {
println("Should proceed for fence: " + section.source)
section.source.stats.foreach { stat =>
stat match {
case i: Import =>
i.importers.foreach {
case Importer(
Term.Name(name),
List(Importee.Name(_: Name.Indeterminate))
) if Instrumenter.magicImports(name) =>
case importer =>
sb.print("import ")
sb.print(importer.pos.text)
sb.print(";")
}
case _ =>
sb.println(stat.pos.text)
}
}
}
case modifierInline: ModifierInline =>
println("FailInstrumenter.modifierInline branch")
nest.nest()
sb.println(section.input)
if (j == i || !modifierInline.isFailOrWarn) {
println("Should proceed: " + section.source)
section.source.stats.foreach { stat =>
println("stat: " + stat)
stat match {
case i: Import =>
println("Uh? Import?")
i.importers.foreach {
case Importer(
Term.Name(name),
List(Importee.Name(_: Name.Indeterminate))
) if Instrumenter.magicImports(name) =>
case importer =>
sb.print("import ")
sb.print(importer.pos.text)
sb.print(";")
case importer =>
sb.print("import ")
sb.print(importer.pos.text)
sb.print(";")
}
case _ =>
println("stat.pos.text: " + stat.pos.text)
sb.println(stat.pos.text)
}
case _ =>
sb.println(stat.pos.text)
}
}
}
}

}
}
sb.println("\n }\n}")
Expand Down
139 changes: 87 additions & 52 deletions mdoc/src/main/scala-2/mdoc/internal/markdown/Instrumenter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,67 +37,101 @@ class Instrumenter(
private val sb = new PrintStream(out)
val gensym = new Gensym()
val nest = new Nesting(sb)
private def printAsScript(): Unit = {
sections.zipWithIndex.foreach { case (section, i) =>
if (section.mod.isReset) {
nest.unnest()
sb.print(Instrumenter.reset(section.mod, gensym.fresh("App")))
} else if (section.mod.isNest) {
nest.nest()
}
sb.println("\n$doc.startSection();")
if (section.mod.isFailOrWarn) {
sb.println(s"$$doc.startStatement(${position(section.source.pos)});")
val out = new FailInstrumenter(sections, i).instrument()
val literal = Instrumenter.stringLiteral(out)
val binder = gensym.fresh("res")
sb.append("val ")
.append(binder)
.append(" = _root_.mdoc.internal.document.FailSection(")
.append(literal)
.append(", ")
.append(position(section.source.pos))
.append(");")
printBinder(binder, section.source.pos)
sb.println("\n$doc.endStatement();")
} else if (section.mod.isCompileOnly) {
section.source.stats.foreach { stat =>
sb.println(s"$$doc.startStatement(${position(stat.pos)});")
sb.println("\n$doc.endStatement();")
}
sb.println(s"""object ${gensym.fresh("compile")} {""")
sb.println(section.source.pos.text)
sb.println("\n}")
} else if (section.mod.isCrash) {
section.source.stats match {
case head :: _ =>
sb.println(s"$$doc.startStatement(${position(head.pos)});")

sb.append("$doc.crash(")
.append(position(head.pos))
.append(") {\n")
private def printAsScript(): Unit =
sections.zipWithIndex.foreach { case (section: SectionInput, i) =>

section.mod match {
case fenceModifier: Modifier => {
if (fenceModifier.isReset) {
nest.unnest()
sb.print(Instrumenter.reset(fenceModifier, gensym.fresh("App")))
} else if (fenceModifier.isNest) {
nest.nest()
}
sb.println("\n$doc.startSection();")
if (fenceModifier.isFailOrWarn) {
sb.println(s"$$doc.startStatement(${position(section.source.pos)});")
val out = new FailInstrumenter(sections, i).instrument()
val literal = Instrumenter.stringLiteral(out)
val binder = gensym.fresh("res")
sb.append("val ")
.append(binder)
.append(" = _root_.mdoc.internal.document.FailSection(")
.append(literal)
.append(", ")
.append(position(section.source.pos))
.append(");")
printBinder(binder, section.source.pos)
sb.println("\n$doc.endStatement();")
} else if (fenceModifier.isCompileOnly) {
section.source.stats.foreach { stat =>
sb.append(stat.pos.text).append(";\n")
sb.println(s"$$doc.startStatement(${position(stat.pos)});")
sb.println("\n$doc.endStatement();")
}
// closing the $doc.crash {... block
sb.append("\n}\n")
sb.println(s"""object ${gensym.fresh("compile")} {""")
sb.println(section.source.pos.text)
sb.println("\n}")
} else if (fenceModifier.isCrash) {
section.source.stats match {
case head :: _ =>
sb.println(s"$$doc.startStatement(${position(head.pos)});")

sb.println("\n$doc.endStatement();")
sb.append("$doc.crash(")
.append(position(head.pos))
.append(") {\n")

section.source.stats.foreach { stat =>
sb.append(stat.pos.text).append(";\n")
}
// closing the $doc.crash {... block
sb.append("\n}\n")

sb.println("\n$doc.endStatement();")

case Nil =>
case Nil =>
}
} else {
section.source.stats.foreach { stat =>
sb.println(s"$$doc.startStatement(${position(stat.pos)});")
printStatement(stat, fenceModifier, sb)
sb.println("\n$doc.endStatement();")
}
}
sb.println("$doc.endSection();")
}
} else {
section.source.stats.foreach { stat =>
sb.println(s"$$doc.startStatement(${position(stat.pos)});")
printStatement(stat, section.mod, sb)
sb.println("\n$doc.endStatement();")
nest.unnest()
case modifierInline: ModifierInline => {
nest.nest()
sb.println("\n$doc.startSection();")
if (modifierInline.isFailOrWarn) {
sb.println(s"$$doc.startStatement(${position(section.source.pos)});")
val out = new FailInstrumenter(sections, i).instrument()
val literal = Instrumenter.stringLiteral(out)
val binder = gensym.fresh("res")
sb.append("val ")
.append(binder)
.append(" = _root_.mdoc.internal.document.FailSection(")
.append(literal)
.append(", ")
.append(position(section.source.pos))
.append(");")
printBinder(binder, section.source.pos)
sb.println("\n$doc.endStatement();")
} else {
section.source.stats.foreach { stat =>
sb.println(s"$$doc.startStatement(${position(stat.pos)});")
sb.println("\n$doc.endStatement();")
}
sb.println(s"""object ${gensym.fresh("compile")} {""")
sb.println(section.source.pos.text)
sb.println("\n}")
}
nest.unnest()
}
}
sb.println("$doc.endSection();")

}
nest.unnest()
}


private def printBinder(name: String, pos: Position): Unit = {
sb.print(s"; $$doc.binder($name, ${position(pos)})")
Expand Down Expand Up @@ -134,6 +168,7 @@ class Instrumenter(
}
}
}

}
object Instrumenter {
val magicImports = Set(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import scala.meta.Source
import scala.meta.inputs.Input
import mdoc.document.Section

case class EvaluatedSection(section: Section, input: Input, source: ParsedSource, mod: Modifier) {
case class EvaluatedSection(section: Section, input: Input, source: ParsedSource, mod: GenModifier) {
def out: String = section.statements.map(_.out).mkString
}
Loading