mirror of
https://github.com/enso-org/enso.git
synced 2024-11-23 08:08:34 +03:00
Split documentation comment into sections (#3347)
This commit is contained in:
parent
cc7333812d
commit
9d402bd599
@ -610,8 +610,10 @@ lazy val `docs-generator` = (project in file("lib/scala/docs-generator"))
|
||||
inConfig(Benchmark)(Defaults.testSettings),
|
||||
Benchmark / unmanagedSourceDirectories +=
|
||||
baseDirectory.value.getParentFile / "bench" / "scala",
|
||||
libraryDependencies +=
|
||||
libraryDependencies ++= Seq(
|
||||
"com.storm-enroute" %% "scalameter" % scalameterVersion % "bench",
|
||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
||||
),
|
||||
testFrameworks := List(
|
||||
new TestFramework("org.scalatest.tools.Framework"),
|
||||
new TestFramework("org.scalameter.ScalaMeterFramework")
|
||||
|
@ -1,9 +1,10 @@
|
||||
package org.enso.docs.generator
|
||||
|
||||
import java.io.File
|
||||
import org.enso.syntax.text.{DocParser, Parser}
|
||||
import org.enso.syntax.text.docparser._
|
||||
|
||||
import java.io.File
|
||||
|
||||
/** Defines useful wrappers for Doc Parser.
|
||||
*/
|
||||
object DocParserWrapper {
|
||||
|
@ -0,0 +1,151 @@
|
||||
package org.enso.docs.sections
|
||||
|
||||
import cats.kernel.Monoid
|
||||
import cats.syntax.compose._
|
||||
import org.enso.syntax.text.ast.Doc
|
||||
|
||||
/** Combine the documentation into a list of [[ParsedSection]]s. */
|
||||
final class ParsedSectionsBuilder {
|
||||
|
||||
import ParsedSectionsBuilder._
|
||||
|
||||
/** Build the parsed sections from the provided documentation comment.
|
||||
*
|
||||
* @param doc the parsed documentation comment.
|
||||
* @return the list of parsed sections.
|
||||
*/
|
||||
def build(doc: Doc): List[ParsedSection] = {
|
||||
val tagSections = doc.tags.map(buildTags)
|
||||
val synopsisSections = doc.synopsis.map(buildSynopsis)
|
||||
val bodySections = doc.body.map(buildBody)
|
||||
Monoid.combineAll(tagSections ++ synopsisSections ++ bodySections)
|
||||
}
|
||||
|
||||
/** Process the tags section of the documentation comment.
|
||||
*
|
||||
* @param tags the tags section
|
||||
* @return the list of parsed sections
|
||||
*/
|
||||
private def buildTags(tags: Doc.Tags): List[ParsedSection] =
|
||||
tags.elems.toList.map { tag =>
|
||||
Section.Tag(tag.name, tag.details.map(_.trim).map(Doc.Elem.Text))
|
||||
}
|
||||
|
||||
/** Process the synopsis section of the documentation comment.
|
||||
*
|
||||
* @param synopsis the synopsis section
|
||||
* @return the list of parsed sections
|
||||
*/
|
||||
private def buildSynopsis(synopsis: Doc.Synopsis): List[ParsedSection] =
|
||||
(joinSections _ >>> buildSections)(synopsis.elems.toList)
|
||||
|
||||
/** Process the body section of the documentation comment.
|
||||
*
|
||||
* @param body the body section
|
||||
* @return the list of parsed sections
|
||||
*/
|
||||
private def buildBody(body: Doc.Body): List[ParsedSection] =
|
||||
(joinSections _ >>> buildSections)(body.elems.toList)
|
||||
|
||||
/** Process the list of [[Doc.Section]] documentation sections.
|
||||
*
|
||||
* @param sections the list of parsed documentation sections
|
||||
* @return the list of parsed sections
|
||||
*/
|
||||
private def buildSections(
|
||||
sections: List[Doc.Section]
|
||||
): List[ParsedSection] =
|
||||
sections.map {
|
||||
case Doc.Section.Raw(_, elems) =>
|
||||
elems match {
|
||||
case Doc.Elem.Text(text) :: t =>
|
||||
val (key, value) = text.span(_ != const.COLON)
|
||||
if (value.nonEmpty) {
|
||||
val line = value.drop(1).stripPrefix(const.SPACE)
|
||||
val body = if (line.isEmpty) t else Doc.Elem.Text(line) :: t
|
||||
Section.Keyed(key, body)
|
||||
} else {
|
||||
Section.Paragraph(elems)
|
||||
}
|
||||
case _ =>
|
||||
Section.Paragraph(elems)
|
||||
}
|
||||
case Doc.Section.Marked(_, _, typ, elems) =>
|
||||
elems match {
|
||||
case head :: tail =>
|
||||
val header = head match {
|
||||
case header: Doc.Section.Header =>
|
||||
Some(header.repr.build())
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
Section.Marked(buildMark(typ), header, tail)
|
||||
case Nil =>
|
||||
Section.Marked(buildMark(typ), None, elems)
|
||||
}
|
||||
}
|
||||
|
||||
/** Create the [[Section.Mark]] from the [[Doc.Section.Marked.Type]]
|
||||
* section type.
|
||||
*
|
||||
* @param typ the type of documentation section
|
||||
* @return the corresponding section mark
|
||||
*/
|
||||
private def buildMark(typ: Doc.Section.Marked.Type): Section.Mark =
|
||||
typ match {
|
||||
case Doc.Section.Marked.Important => Section.Mark.Important
|
||||
case Doc.Section.Marked.Info => Section.Mark.Info
|
||||
case Doc.Section.Marked.Example => Section.Mark.Example
|
||||
}
|
||||
|
||||
/** Preprocess the list of documentation sections and join the paragraphs of
|
||||
* the same offset with the marked section.
|
||||
*
|
||||
* ==Example==
|
||||
* In the parsed [[Doc.Section]], the "Some paragraph" is a separate section,
|
||||
* while having the same indentation. This pass joins them into a single
|
||||
* section.
|
||||
*
|
||||
* {{{
|
||||
* ? Info
|
||||
* Some info.
|
||||
*
|
||||
* Some paragraph.
|
||||
* }}}
|
||||
*
|
||||
* @param sections the list of documentation sections
|
||||
* @return preprocessed list of documentation sections with the
|
||||
* paragraphs joined into the corresponding marked sections.
|
||||
*/
|
||||
private def joinSections(sections: List[Doc.Section]): List[Doc.Section] = {
|
||||
val init: Option[Doc.Section.Marked] = None
|
||||
val stack: List[Doc.Section] = Nil
|
||||
|
||||
val (result, acc) = sections.foldLeft((stack, init)) {
|
||||
case ((stack, acc), section) =>
|
||||
(section, acc) match {
|
||||
case (marked: Doc.Section.Marked, _) =>
|
||||
(acc.toList ::: stack, Some(marked))
|
||||
case (raw: Doc.Section.Raw, None) =>
|
||||
(raw :: stack, acc)
|
||||
case (raw: Doc.Section.Raw, Some(marked)) =>
|
||||
if (raw.indent == marked.indent) {
|
||||
val newElems = marked.elems ::: Doc.Elem.Newline :: raw.elems
|
||||
(stack, Some(marked.copy(elems = newElems)))
|
||||
} else {
|
||||
(raw :: marked :: stack, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(acc.toList ::: result).reverse
|
||||
}
|
||||
}
|
||||
|
||||
object ParsedSectionsBuilder {
|
||||
|
||||
object const {
|
||||
final val COLON = ':'
|
||||
final val SPACE = " "
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package org.enso.docs.sections
|
||||
|
||||
/** The base trait for the section. */
|
||||
sealed trait Section[A]
|
||||
object Section {
|
||||
|
||||
/** The documentation tag.
|
||||
*
|
||||
* {{{
|
||||
* name text
|
||||
* }}}
|
||||
*
|
||||
* ==Example==
|
||||
*
|
||||
* {{{
|
||||
* UNSTABLE
|
||||
* DEPRECATED
|
||||
* ALIAS Length
|
||||
* }}}
|
||||
*
|
||||
* @param name the tag name
|
||||
* @param text the tag text
|
||||
*/
|
||||
case class Tag[A](name: String, text: Option[A]) extends Section[A]
|
||||
|
||||
/** The paragraph of the text.
|
||||
*
|
||||
* ==Example==
|
||||
*
|
||||
* {{{
|
||||
* Arbitrary text in the documentation comment.
|
||||
*
|
||||
* This is another paragraph.
|
||||
* }}}
|
||||
*
|
||||
* @param body the elements that make up this paragraph
|
||||
*/
|
||||
case class Paragraph[A](body: List[A]) extends Section[A]
|
||||
|
||||
/** The section that starts with the key followed by the colon and the body.
|
||||
*
|
||||
* {{{
|
||||
* key: body
|
||||
* }}}
|
||||
*
|
||||
* ==Example==
|
||||
*
|
||||
* {{{
|
||||
* Arguments:
|
||||
* - one: the first
|
||||
* - two: the second
|
||||
* }}}
|
||||
*
|
||||
* {{{
|
||||
* Icon: table-from-rows
|
||||
* }}}
|
||||
*
|
||||
* @param key the section key
|
||||
* @param body the elements the make up the body of the section
|
||||
*/
|
||||
case class Keyed[A](key: String, body: List[A]) extends Section[A]
|
||||
|
||||
/** The section that starts with the mark followed by the header and the body.
|
||||
*
|
||||
* {{{
|
||||
* mark header
|
||||
* body
|
||||
* }}}
|
||||
*
|
||||
* ==Example==
|
||||
*
|
||||
* {{{
|
||||
* > Example
|
||||
* This is how it's done.
|
||||
* foo = bar baz
|
||||
* }}}
|
||||
*
|
||||
* {{{
|
||||
* ! Notice
|
||||
* This is important.
|
||||
* }}}
|
||||
*/
|
||||
case class Marked[A](mark: Mark, header: Option[String], body: List[A])
|
||||
extends Section[A]
|
||||
|
||||
/** The base trait for the section marks. */
|
||||
sealed trait Mark
|
||||
object Mark {
|
||||
case object Important extends Mark
|
||||
case object Info extends Mark
|
||||
case object Example extends Mark
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package org.enso.docs
|
||||
|
||||
import org.enso.syntax.text.ast.Doc
|
||||
|
||||
package object sections {
|
||||
|
||||
type ParsedSection = Section[Doc.Elem]
|
||||
type RenderedSection = Section[String]
|
||||
}
|
@ -0,0 +1,480 @@
|
||||
package org.enso.docs.sections
|
||||
|
||||
import org.enso.syntax.text.DocParser
|
||||
import org.enso.syntax.text.ast.Doc
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpec
|
||||
|
||||
class ParsedSectionsBuilderTest extends AnyWordSpec with Matchers {
|
||||
|
||||
import ParsedSectionsBuilderTest._
|
||||
|
||||
"DocSectionsGenerator" should {
|
||||
|
||||
"generate single tag" in {
|
||||
val comment =
|
||||
""" UNSTABLE
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Tag("UNSTABLE", None)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate multiple tags" in {
|
||||
val comment =
|
||||
""" UNSTABLE
|
||||
| DEPRECATED
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Tag("UNSTABLE", None),
|
||||
Section.Tag("DEPRECATED", None)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate tag with description" in {
|
||||
val comment =
|
||||
""" ALIAS Check Matches
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Tag("ALIAS", Some(Doc.Elem.Text("Check Matches")))
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate description single line" in {
|
||||
val comment =
|
||||
""" hello world
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Paragraph(List(Doc.Elem.Text("hello world")))
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate description multiline" in {
|
||||
val comment =
|
||||
""" hello world
|
||||
| second line
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Paragraph(
|
||||
List(
|
||||
Doc.Elem.Text("hello world"),
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("second line")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate description multiple paragraphs" in {
|
||||
val comment =
|
||||
""" Hello world
|
||||
| second line
|
||||
|
|
||||
| Second paragraph
|
||||
| multiline
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Paragraph(
|
||||
List(
|
||||
Doc.Elem.Text("Hello world"),
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("second line"),
|
||||
Doc.Elem.Newline
|
||||
)
|
||||
),
|
||||
Section.Paragraph(
|
||||
List(
|
||||
Doc.Elem.Text("Second paragraph"),
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("multiline")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate keyed arguments" in {
|
||||
val comment =
|
||||
""" Description
|
||||
|
|
||||
| Arguments:
|
||||
| - one: The first
|
||||
| - two: The second
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Paragraph(
|
||||
List(Doc.Elem.Text("Description"), Doc.Elem.Newline)
|
||||
),
|
||||
Section.Keyed(
|
||||
"Arguments",
|
||||
List(
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.List(
|
||||
1,
|
||||
Doc.Elem.List.Unordered,
|
||||
Doc.Elem.Text("one: The first"),
|
||||
Doc.Elem.Text("two: The second")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate keyed icon" in {
|
||||
val comment =
|
||||
""" Description
|
||||
|
|
||||
| Icon: my-icon
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Paragraph(
|
||||
List(Doc.Elem.Text("Description"), Doc.Elem.Newline)
|
||||
),
|
||||
Section.Keyed(
|
||||
"Icon",
|
||||
List(Doc.Elem.Text("my-icon"))
|
||||
)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate keyed aliases" in {
|
||||
val comment =
|
||||
""" Description
|
||||
|
|
||||
| Aliases: foo, bar baz, redshift®
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Paragraph(
|
||||
List(Doc.Elem.Text("Description"), Doc.Elem.Newline)
|
||||
),
|
||||
Section.Keyed(
|
||||
"Aliases",
|
||||
List(Doc.Elem.Text("foo, bar baz, redshift"), Doc.Elem.Text("®"))
|
||||
)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate marked example" in {
|
||||
val comment =
|
||||
""" Description
|
||||
|
|
||||
| > Example
|
||||
| Simple program
|
||||
| main = 42
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Paragraph(
|
||||
List(Doc.Elem.Text("Description"), Doc.Elem.Newline)
|
||||
),
|
||||
Section.Marked(
|
||||
Section.Mark.Example,
|
||||
Some("Example"),
|
||||
List(
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("Simple program"),
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.CodeBlock(Doc.Elem.CodeBlock.Line(7, "main = 42"))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate marked multiple examples" in {
|
||||
val comment =
|
||||
""" Description
|
||||
|
|
||||
| > Example
|
||||
| Simple program
|
||||
| Multiline
|
||||
| main = 42
|
||||
|
|
||||
| > Example
|
||||
| Another example
|
||||
|
|
||||
| import Foo.Bar
|
||||
|
|
||||
| main =
|
||||
| 42
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Paragraph(
|
||||
List(Doc.Elem.Text("Description"), Doc.Elem.Newline)
|
||||
),
|
||||
Section.Marked(
|
||||
Section.Mark.Example,
|
||||
Some("Example"),
|
||||
List(
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("Simple program"),
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("Multiline"),
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.CodeBlock(Doc.Elem.CodeBlock.Line(7, "main = 42")),
|
||||
Doc.Elem.Newline
|
||||
)
|
||||
),
|
||||
Section.Marked(
|
||||
Section.Mark.Example,
|
||||
Some("Example"),
|
||||
List(
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("Another example"),
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.CodeBlock(
|
||||
Doc.Elem.CodeBlock.Line(7, "import Foo.Bar"),
|
||||
Doc.Elem.CodeBlock.Line(7, "main ="),
|
||||
Doc.Elem.CodeBlock.Line(11, "42")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate marked important" in {
|
||||
val comment =
|
||||
""" Description
|
||||
|
|
||||
| ! This is important
|
||||
| Beware of nulls.
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Paragraph(
|
||||
List(Doc.Elem.Text("Description"), Doc.Elem.Newline)
|
||||
),
|
||||
Section.Marked(
|
||||
Section.Mark.Important,
|
||||
Some("This is important"),
|
||||
List(
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("Beware of nulls.")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate marked info" in {
|
||||
val comment =
|
||||
""" Description
|
||||
|
|
||||
| ? Out of curiosity
|
||||
| FYI.
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Paragraph(
|
||||
List(Doc.Elem.Text("Description"), Doc.Elem.Newline)
|
||||
),
|
||||
Section.Marked(
|
||||
Section.Mark.Info,
|
||||
Some("Out of curiosity"),
|
||||
List(
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("FYI.")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate marked info multiple sections" in {
|
||||
val comment =
|
||||
""" Description
|
||||
|
|
||||
| ? Out of curiosity
|
||||
| FYI.
|
||||
|
|
||||
| Another section.
|
||||
|
|
||||
| And another.
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Paragraph(
|
||||
List(Doc.Elem.Text("Description"), Doc.Elem.Newline)
|
||||
),
|
||||
Section.Marked(
|
||||
Section.Mark.Info,
|
||||
Some("Out of curiosity"),
|
||||
List(
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("FYI."),
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("Another section."),
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("And another.")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate marked info and paragraph sections" in {
|
||||
val comment =
|
||||
""" Description
|
||||
|
|
||||
| ? Out of curiosity
|
||||
| FYI.
|
||||
|
|
||||
| Another section.
|
||||
|
|
||||
| This is paragraph.
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Paragraph(
|
||||
List(Doc.Elem.Text("Description"), Doc.Elem.Newline)
|
||||
),
|
||||
Section.Marked(
|
||||
Section.Mark.Info,
|
||||
Some("Out of curiosity"),
|
||||
List(
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("FYI."),
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("Another section."),
|
||||
Doc.Elem.Newline
|
||||
)
|
||||
),
|
||||
Section.Paragraph(
|
||||
List(Doc.Elem.Text("This is paragraph."))
|
||||
)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate marked info and important sections" in {
|
||||
val comment =
|
||||
""" Description
|
||||
|
|
||||
| ? Out of curiosity
|
||||
| FYI.
|
||||
|
|
||||
| ! Warning
|
||||
| Pretty important.
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Paragraph(
|
||||
List(Doc.Elem.Text("Description"), Doc.Elem.Newline)
|
||||
),
|
||||
Section.Marked(
|
||||
Section.Mark.Info,
|
||||
Some("Out of curiosity"),
|
||||
List(
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("FYI."),
|
||||
Doc.Elem.Newline
|
||||
)
|
||||
),
|
||||
Section.Marked(
|
||||
Section.Mark.Important,
|
||||
Some("Warning"),
|
||||
List(
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("Pretty important.")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
|
||||
"generate multiple sections" in {
|
||||
val comment =
|
||||
""" DEPRECATED
|
||||
|
|
||||
| Some paragraph
|
||||
| Second line
|
||||
|
|
||||
| Arguments:
|
||||
| - one: The first
|
||||
| - two: The second
|
||||
|
|
||||
| ! This is important
|
||||
| Boo.
|
||||
|
|
||||
| ? Out of curiosity
|
||||
| FYI.
|
||||
|""".stripMargin.linesIterator.mkString("\n")
|
||||
val expected = List(
|
||||
Section.Tag("DEPRECATED", None),
|
||||
Section.Paragraph(List(Doc.Elem.Newline)),
|
||||
Section.Paragraph(
|
||||
List(
|
||||
Doc.Elem.Text("Some paragraph"),
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("Second line"),
|
||||
Doc.Elem.Newline
|
||||
)
|
||||
),
|
||||
Section.Keyed(
|
||||
"Arguments",
|
||||
List(
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.List(
|
||||
1,
|
||||
Doc.Elem.List.Unordered,
|
||||
Doc.Elem.Text("one: The first"),
|
||||
Doc.Elem.Text("two: The second")
|
||||
),
|
||||
Doc.Elem.Newline
|
||||
)
|
||||
),
|
||||
Section.Marked(
|
||||
Section.Mark.Important,
|
||||
Some("This is important"),
|
||||
List(
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("Boo."),
|
||||
Doc.Elem.Newline
|
||||
)
|
||||
),
|
||||
Section.Marked(
|
||||
Section.Mark.Info,
|
||||
Some("Out of curiosity"),
|
||||
List(
|
||||
Doc.Elem.Newline,
|
||||
Doc.Elem.Text("FYI.")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
parseSections(comment) shouldEqual expected
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
object ParsedSectionsBuilderTest {
|
||||
|
||||
val parsedSectionsBuilder = new ParsedSectionsBuilder
|
||||
|
||||
def parseSections(comment: String): List[ParsedSection] = {
|
||||
val doc = DocParser.runMatched(comment)
|
||||
parsedSectionsBuilder.build(doc)
|
||||
}
|
||||
}
|
@ -284,57 +284,179 @@ object Doc {
|
||||
//// List - Ordered & Unordered, Invalid Indent ////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** An element of the list.
|
||||
*
|
||||
* The list can contain following elements:
|
||||
* - [[ListItem]] a plain list element
|
||||
* - [[List]] a sublist
|
||||
* - [[MisalignedItem]] a list item that is not aligned correctly with the
|
||||
* previous list item
|
||||
*/
|
||||
sealed trait ListElem extends Elem {
|
||||
|
||||
/** Append elements to this list item.
|
||||
*
|
||||
* @param xs elements to append
|
||||
*/
|
||||
def append(xs: scala.List[Elem]): ListElem
|
||||
}
|
||||
|
||||
/** A list item that can hold a complex element structure.
|
||||
*
|
||||
* @param elems the elements that make up this list item
|
||||
*/
|
||||
final case class ListItem(elems: scala.List[Elem]) extends ListElem {
|
||||
|
||||
override val repr: Repr.Builder = R + elems
|
||||
|
||||
override def html: HTML = elems.map(_.html)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def append(xs: scala.List[Elem]): ListItem =
|
||||
copy(elems = elems :++ xs)
|
||||
}
|
||||
object ListItem {
|
||||
|
||||
/** Create a list item from the provided elemenets
|
||||
*
|
||||
* @param elems the elements that make up the list item
|
||||
* @return the list item
|
||||
*/
|
||||
def apply(elems: Elem*): ListItem =
|
||||
ListItem(elems.toList)
|
||||
}
|
||||
|
||||
/** The list item that is not aligned correctly with the previous list item.
|
||||
*
|
||||
* @param indent the indentation of this list item
|
||||
* @param typ the list type
|
||||
* @param elems the elements that make up this list item
|
||||
*/
|
||||
final case class MisalignedItem(
|
||||
indent: Int,
|
||||
typ: List.Type,
|
||||
elems: scala.List[Elem]
|
||||
) extends ListElem {
|
||||
|
||||
override val repr: Repr.Builder =
|
||||
R + indent + typ.marker + List.ElemIndent + elems
|
||||
|
||||
override def html: HTML =
|
||||
elems.map(_.html)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def append(xs: scala.List[Elem]): MisalignedItem =
|
||||
copy(elems = elems :++ xs)
|
||||
}
|
||||
object MisalignedItem {
|
||||
|
||||
/** Create a misaligned item from the provided elements.
|
||||
*
|
||||
* @param indent the indentation of this list item
|
||||
* @param typ the list type
|
||||
* @param elems the elements that make up the list item
|
||||
* @return the new misaligned item
|
||||
*/
|
||||
def apply(indent: Int, typ: List.Type, elems: Elem*): MisalignedItem =
|
||||
new MisalignedItem(indent, typ, elems.toList)
|
||||
}
|
||||
|
||||
/** List - block used to hold ordered and unordered lists
|
||||
*
|
||||
* @param indent - specifies indentation of list
|
||||
* @param typ - type of list
|
||||
* @param elems - elements which make up list
|
||||
*
|
||||
* Indent.Invalid - holds list element with invalid indent
|
||||
*
|
||||
* @param indent specifies indentation of list
|
||||
* @param typ the list type
|
||||
* @param elems the elements that make up this list
|
||||
*/
|
||||
final case class List(indent: Int, typ: List.Type, elems: List1[Elem])
|
||||
extends Elem {
|
||||
val repr: Repr.Builder = R + indent + typ.marker + elems.head + elems.tail
|
||||
.map {
|
||||
case elem @ (_: Elem.Invalid) => R + Newline + elem
|
||||
case elem @ (_: List) => R + Newline + elem
|
||||
case elem =>
|
||||
R + Newline + indent + typ.marker + elem
|
||||
final case class List(indent: Int, typ: List.Type, elems: List1[ListElem])
|
||||
extends ListElem {
|
||||
|
||||
val repr: Repr.Builder = {
|
||||
val listElems = elems.reverse
|
||||
R + indent + typ.marker + List.ElemIndent + listElems.head +
|
||||
listElems.tail.map {
|
||||
case elem: List =>
|
||||
R + Newline + elem
|
||||
case elem: ListItem =>
|
||||
R + Newline + indent + typ.marker + List.ElemIndent + elem
|
||||
case elem: MisalignedItem =>
|
||||
R + Newline + elem
|
||||
}
|
||||
}
|
||||
|
||||
val html: HTML = {
|
||||
val elemsHTML = elems.toList.map {
|
||||
case elem @ (_: List) => elem.html
|
||||
case elem => Seq(HTML.li(elem.html))
|
||||
val elemsHTML = elems.reverse.toList.map {
|
||||
case elem: List => elem.html
|
||||
case elem: ListElem => Seq(HTML.li(elem.html))
|
||||
}
|
||||
Seq(typ.HTMLMarker(elemsHTML))
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def append(xs: scala.List[Elem]): List = {
|
||||
val newElems = List1(elems.head.append(xs), elems.tail)
|
||||
this.copy(elems = newElems)
|
||||
}
|
||||
|
||||
/** Add a new list item.
|
||||
*
|
||||
* @param item the list item to add
|
||||
* @return the new list with this item added
|
||||
*/
|
||||
def addItem(item: ListElem): List = {
|
||||
val newElems = elems.prepend(item)
|
||||
this.copy(elems = newElems)
|
||||
}
|
||||
|
||||
/** Add an empty list item.
|
||||
*
|
||||
* @return the new list with an empty list item added
|
||||
*/
|
||||
def addItem(): List =
|
||||
this.copy(elems = elems.prepend(ListItem(Nil)))
|
||||
}
|
||||
|
||||
object List {
|
||||
def apply(indent: Int, listType: Type, elem: Elem): List =
|
||||
List(indent, listType, List1(elem))
|
||||
def apply(indent: Int, listType: Type, elems: Elem*): List =
|
||||
List(indent, listType, List1(elems.head, elems.tail.toList))
|
||||
|
||||
val ElemIndent: Int = 1
|
||||
|
||||
/** Create an empty list.
|
||||
*
|
||||
* @param indent the list indentation
|
||||
* @param typ the list type
|
||||
* @return the new list
|
||||
*/
|
||||
def empty(indent: Int, typ: Type): List =
|
||||
new List(indent, typ, List1(ListItem(Nil)))
|
||||
|
||||
/** Create a new list.
|
||||
*
|
||||
* @param indent the list indentation
|
||||
* @param typ the list type
|
||||
* @param elem the first elements of this list
|
||||
* @param elems the rest of the list elements
|
||||
* @return the new list
|
||||
*/
|
||||
def apply(indent: Int, typ: Type, elem: Elem, elems: Elem*): List = {
|
||||
val listItems = (elem :: elems.toList).reverse.map {
|
||||
case list: List => list
|
||||
case elem: ListItem => elem
|
||||
case elem: MisalignedItem => elem
|
||||
case elem => ListItem(elem)
|
||||
}
|
||||
new List(
|
||||
indent,
|
||||
typ,
|
||||
List1.fromListOption(listItems).get
|
||||
)
|
||||
}
|
||||
|
||||
/** The list type. */
|
||||
abstract class Type(val marker: Char, val HTMLMarker: HTMLTag)
|
||||
final case object Unordered extends Type('-', HTML.ul)
|
||||
final case object Ordered extends Type('*', HTML.ol)
|
||||
|
||||
object Indent {
|
||||
final case class Invalid(indent: Int, typ: Type, elem: Elem)
|
||||
extends Elem.Invalid {
|
||||
val repr: Repr.Builder = R + indent + typ.marker + elem
|
||||
val html: HTML = {
|
||||
val className = this.productPrefix
|
||||
val htmlCls = HTML.`class` := className + getObjectName
|
||||
Seq(HTML.div(htmlCls)(elem.html))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getObjectName: String =
|
||||
getClass.toString.split('$').last
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,7 +474,7 @@ object Doc {
|
||||
*/
|
||||
sealed trait Section extends Symbol {
|
||||
def indent: Int
|
||||
var elems: List[Elem]
|
||||
def elems: List[Elem]
|
||||
|
||||
def reprOfNormalText(elem: Elem, prevElem: Elem): Repr.Builder = {
|
||||
prevElem match {
|
||||
@ -387,7 +509,7 @@ object Doc {
|
||||
indentBeforeMarker: Int,
|
||||
indentAfterMarker: Int,
|
||||
typ: Marked.Type,
|
||||
var elems: List[Elem]
|
||||
elems: List[Elem]
|
||||
) extends Section {
|
||||
val marker: String = typ.marker.toString
|
||||
val firstIndentRepr: Repr.Builder =
|
||||
@ -443,7 +565,7 @@ object Doc {
|
||||
case object Example extends Type('>')
|
||||
}
|
||||
|
||||
final case class Raw(indent: Int, var elems: List[Elem]) extends Section {
|
||||
final case class Raw(indent: Int, elems: List[Elem]) extends Section {
|
||||
val dummyElem = Elem.Text("")
|
||||
val newLn: Elem = Elem.Newline
|
||||
val elemsRepr: List[Repr.Builder] = elems.zip(dummyElem :: elems).map {
|
||||
|
@ -56,7 +56,8 @@ case class DocParserDef() extends Parser[Doc] {
|
||||
val lowerChar: Pattern = range('a', 'z')
|
||||
val upperChar: Pattern = range('A', 'Z')
|
||||
val digit: Pattern = range('0', '9')
|
||||
val whitespace: Pattern = ' '.many1
|
||||
val space: Pattern = ' '
|
||||
val whitespace: Pattern = space.many1
|
||||
val newline: Char = '\n'
|
||||
|
||||
val char: Pattern = lowerChar | upperChar
|
||||
@ -205,18 +206,6 @@ case class DocParserDef() extends Parser[Doc] {
|
||||
}
|
||||
case Some(_) | None => result.push()
|
||||
}
|
||||
if (result.stack.tail.head.isInstanceOf[Elem.List]) {
|
||||
val code = result.current.get.asInstanceOf[Elem.CodeBlock]
|
||||
result.pop()
|
||||
result.pop()
|
||||
val list = result.current.get.asInstanceOf[Elem.List]
|
||||
val last = list.elems.toList.last.repr + newline + code.elems.repr
|
||||
val newElems =
|
||||
list.elems.reverse.tail.reverse :+ Elem.stringToText(last.build())
|
||||
val nElems = List1(newElems).get
|
||||
val newList = Elem.List(list.indent, list.typ, nElems)
|
||||
result.current = Some(newList)
|
||||
}
|
||||
result.push()
|
||||
}
|
||||
|
||||
@ -226,7 +215,7 @@ case class DocParserDef() extends Parser[Doc] {
|
||||
}
|
||||
|
||||
val notNewLine: Pattern = not(newline).many1
|
||||
val CODE: State = state.define("Code")
|
||||
lazy val CODE: State = state.define("Code")
|
||||
|
||||
ROOT || code.inlinePattern || code.onPushingInline(currentMatch)
|
||||
CODE || newline || { state.end(); state.begin(NEWLINE) }
|
||||
@ -453,24 +442,31 @@ case class DocParserDef() extends Parser[Doc] {
|
||||
var stack: List[Int] = Nil
|
||||
def current: Int =
|
||||
stack match {
|
||||
case Nil => 0
|
||||
case ::(head, _) => head
|
||||
case h :: _ => h
|
||||
case Nil => 0
|
||||
}
|
||||
|
||||
def onIndent(): Unit =
|
||||
logger.trace {
|
||||
val diff = currentMatch.length - current
|
||||
if (diff < 0 && list.inListFlag) {
|
||||
list.appendInnerToOuter()
|
||||
stack = stack.tail
|
||||
} else if (
|
||||
currentMatch.length > section.currentIndentRaw && result.stack.nonEmpty
|
||||
) {
|
||||
tryToFindCodeInStack()
|
||||
stack +:= currentMatch.length
|
||||
state.begin(CODE)
|
||||
if (list.isInList) {
|
||||
if (diff > list.minIndent) {
|
||||
tryToFindCodeInStack()
|
||||
stack +:= currentMatch.length
|
||||
state.begin(CODE)
|
||||
} else {
|
||||
text.push(currentMatch)
|
||||
}
|
||||
} else {
|
||||
section.currentIndentRaw = currentMatch.length
|
||||
if (
|
||||
currentMatch.length > section.currentIndentRaw && result.stack.nonEmpty
|
||||
) {
|
||||
tryToFindCodeInStack()
|
||||
stack +:= currentMatch.length
|
||||
state.begin(CODE)
|
||||
} else {
|
||||
section.currentIndentRaw = currentMatch.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -487,51 +483,36 @@ case class DocParserDef() extends Parser[Doc] {
|
||||
result.push()
|
||||
}
|
||||
|
||||
def onIndentForListCreation(
|
||||
indent: Int,
|
||||
typ: Elem.List.Type,
|
||||
content: String
|
||||
): Unit =
|
||||
def onIndentForListCreation(indent: Int, typ: Elem.List.Type): Unit =
|
||||
logger.trace {
|
||||
val diff = indent - current
|
||||
if (diff > 0) {
|
||||
/* NOTE
|
||||
* Used to push new line before pushing first list
|
||||
*/
|
||||
if (!list.inListFlag) onPushingNewLine()
|
||||
val diff = indent - list.current
|
||||
|
||||
if (!list.isInList) {
|
||||
onPushingNewLine()
|
||||
|
||||
stack +:= indent
|
||||
list.inListFlag = true
|
||||
list.addNew(indent, typ, content)
|
||||
} else if (diff == 0 && list.inListFlag) {
|
||||
list.addContent(content)
|
||||
} else if (diff < 0 && list.inListFlag) {
|
||||
if (stack.tail.head != indent) {
|
||||
onInvalidIndent(indent, typ, content)
|
||||
} else {
|
||||
list.appendInnerToOuter()
|
||||
list.addContent(content)
|
||||
stack = stack.tail
|
||||
}
|
||||
list.startNewList(indent, typ)
|
||||
} else if (diff == 0) {
|
||||
list.endListItem()
|
||||
list.startListItem()
|
||||
} else if (diff > 0) {
|
||||
stack +:= indent
|
||||
list.endListItem()
|
||||
list.startNewList(indent, typ)
|
||||
} else {
|
||||
onInvalidIndent(indent, typ, content)
|
||||
if (indent > list.prev) {
|
||||
list.endListItem()
|
||||
list.startMisalignedListItem(indent, typ)
|
||||
} else {
|
||||
do {
|
||||
list.endListItem()
|
||||
list.endSublist()
|
||||
} while (indent < list.current)
|
||||
list.startListItem()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def onInvalidIndent(
|
||||
indent: Int,
|
||||
typ: Elem.List.Type,
|
||||
content: String
|
||||
): Unit = {
|
||||
if (!list.inListFlag && typ == Elem.List.Ordered) {
|
||||
onPushingNewLine()
|
||||
formatter.onPushing(Elem.Formatter.Bold)
|
||||
result.current = Some(content)
|
||||
result.push()
|
||||
} else {
|
||||
list.addContent(Elem.List.Indent.Invalid(indent, typ, content))
|
||||
}
|
||||
}
|
||||
|
||||
def onPushingNewLine(): Unit =
|
||||
logger.trace {
|
||||
result.current = Some(Elem.Newline)
|
||||
@ -540,9 +521,8 @@ case class DocParserDef() extends Parser[Doc] {
|
||||
|
||||
def onEmptyLine(): Unit =
|
||||
logger.trace {
|
||||
if (list.inListFlag) {
|
||||
list.appendInnerToOuter()
|
||||
list.inListFlag = false
|
||||
if (list.isInList) {
|
||||
list.endListItem(endList = true)
|
||||
}
|
||||
onPushingNewLine()
|
||||
section.onEOS()
|
||||
@ -569,7 +549,7 @@ case class DocParserDef() extends Parser[Doc] {
|
||||
val EOFPattern: Pattern = indentPattern >> eof
|
||||
}
|
||||
|
||||
val NEWLINE: State = state.define("Newline")
|
||||
lazy val NEWLINE: State = state.define("Newline")
|
||||
|
||||
ROOT || newline || state.begin(NEWLINE)
|
||||
NEWLINE || indent.EOFPattern || indent.onEOFPattern()
|
||||
@ -584,79 +564,180 @@ case class DocParserDef() extends Parser[Doc] {
|
||||
* there are 2 possible types of lists - ordered and unordered.
|
||||
*/
|
||||
final object list {
|
||||
var inListFlag: Boolean = false
|
||||
|
||||
def addNew(indent: Int, listType: Elem.List.Type, content: Elem): Unit =
|
||||
/** The minimum list indentation consisting of a list symbol and a space
|
||||
* character.
|
||||
*/
|
||||
val minIndent: Int = 2
|
||||
|
||||
var stack: List[Int] = Nil
|
||||
def current: Int =
|
||||
stack match {
|
||||
case h :: _ => h
|
||||
case Nil => 0
|
||||
}
|
||||
def prev: Int =
|
||||
stack match {
|
||||
case _ :: p :: _ => p
|
||||
case _ => 0
|
||||
}
|
||||
|
||||
def isInList: Boolean = stack.nonEmpty
|
||||
|
||||
def startNewList(indent: Int, listType: Elem.List.Type): Unit =
|
||||
logger.trace {
|
||||
result.current = Some(Elem.List(indent, listType, content))
|
||||
list.stack +:= indent
|
||||
result.current = Some(Elem.List.empty(indent, listType))
|
||||
result.push()
|
||||
}
|
||||
|
||||
def addContent(content: Elem): Unit =
|
||||
def startListItem(): Unit =
|
||||
logger.trace {
|
||||
result.pop()
|
||||
result.current match {
|
||||
case Some(list @ (_: Elem.List)) =>
|
||||
var currentContent = list.elems
|
||||
currentContent = currentContent.append(content)
|
||||
result.current =
|
||||
Some(Elem.List(list.indent, list.typ, currentContent))
|
||||
case _ =>
|
||||
case Some(l: Elem.List) =>
|
||||
result.current = Some(l.addItem())
|
||||
result.push()
|
||||
case elem =>
|
||||
throw new IllegalStateException(
|
||||
s"Illegal startListItem state [current=$elem]"
|
||||
)
|
||||
}
|
||||
result.push()
|
||||
}
|
||||
|
||||
def appendInnerToOuter(): Unit =
|
||||
def startMisalignedListItem(indent: Int, typ: Elem.List.Type): Unit =
|
||||
logger.trace {
|
||||
result.pop()
|
||||
val innerList = result.current.orNull
|
||||
result.stack.head match {
|
||||
case outerList @ (_: Elem.List) =>
|
||||
var outerContent = outerList.elems
|
||||
innerList match {
|
||||
case Elem.Newline =>
|
||||
case _ => outerContent = outerContent.append(innerList)
|
||||
}
|
||||
result.pop()
|
||||
result.current =
|
||||
Some(Elem.List(outerList.indent, outerList.typ, outerContent))
|
||||
case _ =>
|
||||
}
|
||||
result.push()
|
||||
innerList match {
|
||||
case Elem.Newline => indent.onPushingNewLine()
|
||||
case _ =>
|
||||
result.current match {
|
||||
case Some(l: Elem.List) =>
|
||||
val item = Elem.MisalignedItem(indent, typ, List())
|
||||
result.current = Some(l.addItem(item))
|
||||
result.push()
|
||||
case elem =>
|
||||
throw new IllegalStateException(
|
||||
s"Illegal startMisalignedListItem state [current=$elem]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def endListItem(endList: Boolean = false): Unit =
|
||||
logger.trace {
|
||||
section.checkForUnclosedFormattersOnEOS()
|
||||
val elems = stackUnwind()
|
||||
result.current match {
|
||||
case Some(l: Elem.List) =>
|
||||
if (endList) {
|
||||
list.stack = list.stack.tail
|
||||
}
|
||||
result.current = Some(l.append(elems))
|
||||
result.push()
|
||||
case elem =>
|
||||
throw new IllegalStateException(
|
||||
s"Illegal endListItem state [current=$elem]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def endSublist(): Unit =
|
||||
logger.trace {
|
||||
result.current match {
|
||||
case None =>
|
||||
result.pop()
|
||||
result.current match {
|
||||
case Some(sublist: Elem.List) =>
|
||||
result.pop()
|
||||
result.current match {
|
||||
case Some(l: Elem.List) =>
|
||||
list.stack = list.stack.tail
|
||||
result.current = Some(l.addItem(sublist))
|
||||
result.push()
|
||||
case elem =>
|
||||
throw new IllegalStateException(
|
||||
s"Illegal endSublist stack [List,$elem,...]"
|
||||
)
|
||||
}
|
||||
case elem =>
|
||||
throw new IllegalStateException(
|
||||
s"Illegal endSublist stack [$elem,...]"
|
||||
)
|
||||
}
|
||||
case elem =>
|
||||
throw new IllegalStateException(
|
||||
s"Illegal endSublist state [current=$elem]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def addLastItem(): Unit =
|
||||
logger.trace {
|
||||
val elems = stackUnwind()
|
||||
result.current match {
|
||||
case Some(l: Elem.List) =>
|
||||
list.stack = list.stack.tail
|
||||
if (elems.last == Elem.Newline) {
|
||||
result.current = Some(l.append(elems.init))
|
||||
result.push()
|
||||
result.current = Some(Elem.Newline)
|
||||
result.push()
|
||||
} else {
|
||||
result.current = Some(l.append(elems))
|
||||
result.push()
|
||||
}
|
||||
while (stack.nonEmpty) {
|
||||
list.endListItem()
|
||||
list.endSublist()
|
||||
}
|
||||
case elem =>
|
||||
throw new IllegalStateException(
|
||||
s"Illegal addLastItem state [current=$elem]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Get all elements from the stack that were added after the [[Elem.List]]
|
||||
* node was pushed.
|
||||
*/
|
||||
def stackUnwind(): List[Elem] = {
|
||||
@scala.annotation.tailrec
|
||||
def go(elems: List[Elem]): List[Elem] = {
|
||||
result.pop()
|
||||
result.current match {
|
||||
case Some(_: Elem.List) | None =>
|
||||
elems
|
||||
case Some(elem) =>
|
||||
go(elem :: elems)
|
||||
}
|
||||
}
|
||||
|
||||
val init = result.current.toList
|
||||
go(init)
|
||||
}
|
||||
|
||||
def onOrdered(): Unit =
|
||||
logger.trace {
|
||||
state.end()
|
||||
val matchedContent = currentMatch.split(orderedListTrigger)
|
||||
val listIndent = matchedContent(0).length
|
||||
val listElems = matchedContent(1)
|
||||
indent.onIndentForListCreation(listIndent, Elem.List.Ordered, listElems)
|
||||
val listIndent = currentMatch
|
||||
.takeWhile(_ != orderedListTrigger)
|
||||
.length
|
||||
indent.onIndentForListCreation(listIndent, Elem.List.Ordered)
|
||||
}
|
||||
|
||||
def onUnordered(): Unit =
|
||||
logger.trace {
|
||||
state.end()
|
||||
val matchedContent = currentMatch.split(unorderedListTrigger)
|
||||
val listIndent = matchedContent(0).length
|
||||
val listElems = matchedContent(1)
|
||||
indent.onIndentForListCreation(
|
||||
listIndent,
|
||||
Elem.List.Unordered,
|
||||
listElems
|
||||
)
|
||||
val listIndent = currentMatch
|
||||
.takeWhile(_ != unorderedListTrigger)
|
||||
.length
|
||||
indent.onIndentForListCreation(listIndent, Elem.List.Unordered)
|
||||
}
|
||||
|
||||
val orderedListTrigger: Char = Elem.List.Ordered.marker
|
||||
val unorderedListTrigger: Char = Elem.List.Unordered.marker
|
||||
|
||||
val orderedPattern: Pattern =
|
||||
indent.indentPattern >> orderedListTrigger >> notNewLine
|
||||
indent.indentPattern >> orderedListTrigger >> space
|
||||
val unorderedPattern: Pattern =
|
||||
indent.indentPattern >> unorderedListTrigger >> notNewLine
|
||||
indent.indentPattern >> unorderedListTrigger >> space
|
||||
}
|
||||
|
||||
NEWLINE || list.orderedPattern || list.onOrdered()
|
||||
@ -742,6 +823,11 @@ case class DocParserDef() extends Parser[Doc] {
|
||||
formatter.checkForUnclosed(Elem.Formatter.Strikeout)
|
||||
}
|
||||
|
||||
def checkForUnclosedListsOnEOS(): Unit =
|
||||
if (list.isInList) {
|
||||
list.addLastItem()
|
||||
}
|
||||
|
||||
def reverseStackOnEOS(): Unit =
|
||||
logger.trace {
|
||||
result.stack = result.stack.reverse
|
||||
@ -794,6 +880,7 @@ case class DocParserDef() extends Parser[Doc] {
|
||||
def onEOS(): Unit =
|
||||
logger.trace {
|
||||
checkForUnclosedFormattersOnEOS()
|
||||
checkForUnclosedListsOnEOS()
|
||||
reverseStackOnEOS()
|
||||
header.create()
|
||||
push()
|
||||
@ -850,13 +937,17 @@ case class DocParserDef() extends Parser[Doc] {
|
||||
def transformOverlyIndentedRawIntoCode(
|
||||
baseIndent: Int
|
||||
): Unit = {
|
||||
var newStack = List[Section]()
|
||||
var newStack = List[Section]()
|
||||
var currentIndent = baseIndent
|
||||
while (section.stack.nonEmpty) {
|
||||
var current = section.pop().get
|
||||
if (current.indent > baseIndent && current.isInstanceOf[Section.Raw]) {
|
||||
if (
|
||||
current.indent > currentIndent &&
|
||||
current.isInstanceOf[Section.Raw]
|
||||
) {
|
||||
var stackOfCodeSections: List[Section] = List[Section]()
|
||||
while (
|
||||
section.stack.nonEmpty && current.indent > baseIndent && current
|
||||
section.stack.nonEmpty && current.indent > currentIndent && current
|
||||
.isInstanceOf[Section.Raw] && section.stack.head
|
||||
.isInstanceOf[Section.Raw]
|
||||
) {
|
||||
@ -866,20 +957,30 @@ case class DocParserDef() extends Parser[Doc] {
|
||||
}
|
||||
}
|
||||
stackOfCodeSections = stackOfCodeSections :+ current
|
||||
val codeLines = stackOfCodeSections.flatMap(s => {
|
||||
val inLines = s.repr.build().split("\n").map(_.trim)
|
||||
inLines.map(Doc.Elem.CodeBlock.Line(s.indent, _))
|
||||
})
|
||||
val codeLines = stackOfCodeSections.flatMap {
|
||||
_.repr
|
||||
.build()
|
||||
.split("\n")
|
||||
.map { line =>
|
||||
val (indent, text) = line.span(_ == ' ')
|
||||
Doc.Elem.CodeBlock.Line(indent.length, text)
|
||||
}
|
||||
}
|
||||
if (codeLines.nonEmpty) {
|
||||
val l1CodeLines = List1(codeLines.head, codeLines.tail)
|
||||
val codeBlock = Doc.Elem.CodeBlock(l1CodeLines)
|
||||
val s = newStack.head
|
||||
val sElems = newStack.head.elems :+ codeBlock
|
||||
s.elems = sElems
|
||||
val newElems = newStack.head.elems :+ codeBlock
|
||||
val newSection = newStack.head match {
|
||||
case marked: Doc.Section.Marked =>
|
||||
marked.copy(elems = newElems)
|
||||
case raw: Doc.Section.Raw =>
|
||||
raw.copy(elems = newElems)
|
||||
}
|
||||
newStack = newStack.drop(1)
|
||||
newStack +:= s
|
||||
newStack +:= newSection
|
||||
}
|
||||
} else {
|
||||
currentIndent = current.indent
|
||||
newStack +:= current
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
assertExpr(input, out)
|
||||
}
|
||||
def ?==(out: Doc): Unit = testBase in {
|
||||
assertExpr(input, out, false)
|
||||
assertExpr(input, out, assertShow = false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,6 +258,151 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
.Marked(1, 4, Section.Marked.Important, Section.Header("Important"))
|
||||
)
|
||||
)
|
||||
""" ! Important
|
||||
| This is important.""".stripMargin.replaceAll(
|
||||
System.lineSeparator(),
|
||||
"\n"
|
||||
) ?= Doc(
|
||||
Synopsis(
|
||||
Section.Marked(
|
||||
1,
|
||||
1,
|
||||
Section.Marked.Important,
|
||||
Section.Header("Important"),
|
||||
Doc.Elem.Newline,
|
||||
"This is important."
|
||||
)
|
||||
)
|
||||
)
|
||||
"""! Synopsis
|
||||
| This _is_ important""".stripMargin.replaceAll(
|
||||
System.lineSeparator(),
|
||||
"\n"
|
||||
) ?= Doc(
|
||||
Synopsis(
|
||||
Section.Marked(
|
||||
0,
|
||||
1,
|
||||
Section.Marked.Important,
|
||||
Section.Header("Synopsis"),
|
||||
Doc.Elem.Newline,
|
||||
"This ",
|
||||
Formatter(Formatter.Italic, "is"),
|
||||
" important"
|
||||
)
|
||||
)
|
||||
)
|
||||
"""Synopsis
|
||||
|This _is1_ important""".stripMargin.replaceAll(
|
||||
System.lineSeparator(),
|
||||
"\n"
|
||||
) ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
"Synopsis",
|
||||
Doc.Elem.Newline,
|
||||
"This ",
|
||||
Formatter(Formatter.Italic, "is1"),
|
||||
" important"
|
||||
)
|
||||
)
|
||||
)
|
||||
""" Synopsis
|
||||
|
|
||||
| ! Important
|
||||
| This is important.""".stripMargin.replaceAll(
|
||||
System.lineSeparator(),
|
||||
"\n"
|
||||
) ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(1, "Synopsis", Doc.Elem.Newline)
|
||||
),
|
||||
Body(
|
||||
Section.Marked(
|
||||
1,
|
||||
1,
|
||||
Section.Marked.Important,
|
||||
Section.Header("Important"),
|
||||
Doc.Elem.Newline,
|
||||
"This is important."
|
||||
)
|
||||
)
|
||||
)
|
||||
""" Synopsis
|
||||
|
|
||||
| ! Important
|
||||
| This is important.
|
||||
|
|
||||
| And this""".stripMargin.replaceAll(
|
||||
System.lineSeparator(),
|
||||
"\n"
|
||||
) ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(1, "Synopsis", Doc.Elem.Newline)
|
||||
),
|
||||
Body(
|
||||
Section.Marked(
|
||||
1,
|
||||
1,
|
||||
Section.Marked.Important,
|
||||
Section.Header("Important"),
|
||||
Doc.Elem.Newline,
|
||||
"This is important.",
|
||||
Doc.Elem.Newline
|
||||
),
|
||||
Section.Raw(3, "And this")
|
||||
)
|
||||
)
|
||||
""" Synopsis
|
||||
|
|
||||
| !Important
|
||||
| This is a code
|
||||
|
|
||||
| And this is not""".stripMargin.replaceAll(
|
||||
System.lineSeparator(),
|
||||
"\n"
|
||||
) ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(1, "Synopsis", Doc.Elem.Newline)
|
||||
),
|
||||
Body(
|
||||
Section.Marked(
|
||||
1,
|
||||
0,
|
||||
Section.Marked.Important,
|
||||
Section.Header("Important"),
|
||||
Doc.Elem.Newline,
|
||||
CodeBlock(CodeBlock.Line(4, "This is a code")),
|
||||
Doc.Elem.Newline
|
||||
),
|
||||
Section.Raw(2, "And this is not")
|
||||
)
|
||||
)
|
||||
"""Synopsis
|
||||
|
|
||||
|! Important
|
||||
| This is important
|
||||
|
|
||||
| And this is a code""".stripMargin.replaceAll(
|
||||
System.lineSeparator(),
|
||||
"\n"
|
||||
) ?== Doc(
|
||||
Synopsis(
|
||||
Section.Raw("Synopsis", Doc.Elem.Newline)
|
||||
),
|
||||
Body(
|
||||
Section.Marked(
|
||||
0,
|
||||
1,
|
||||
Section.Marked.Important,
|
||||
Section.Header("Important"),
|
||||
Doc.Elem.Newline,
|
||||
"This is important",
|
||||
Doc.Elem.Newline,
|
||||
CodeBlock(CodeBlock.Line(4, "And this is a code"))
|
||||
)
|
||||
)
|
||||
)
|
||||
"?Info" ?= Doc(
|
||||
Synopsis(Section.Marked(Section.Marked.Info, Section.Header("Info")))
|
||||
)
|
||||
@ -293,6 +438,45 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
Section.Marked(Section.Marked.Example, Section.Header("Example"))
|
||||
)
|
||||
)
|
||||
"""Synopsis
|
||||
|
|
||||
| ! Important
|
||||
| This is important
|
||||
| And this is a code
|
||||
|
|
||||
|> Example
|
||||
| This is example
|
||||
| More code""".stripMargin.replaceAll(
|
||||
System.lineSeparator(),
|
||||
"\n"
|
||||
) ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw("Synopsis", Doc.Elem.Newline)
|
||||
),
|
||||
Body(
|
||||
Section.Marked(
|
||||
1,
|
||||
1,
|
||||
Section.Marked.Important,
|
||||
Section.Header("Important"),
|
||||
Doc.Elem.Newline,
|
||||
"This is important",
|
||||
Doc.Elem.Newline,
|
||||
CodeBlock(CodeBlock.Line(5, "And this is a code")),
|
||||
Doc.Elem.Newline
|
||||
),
|
||||
Section.Marked(
|
||||
0,
|
||||
1,
|
||||
Section.Marked.Example,
|
||||
Section.Header("Example"),
|
||||
Doc.Elem.Newline,
|
||||
"This is example",
|
||||
Doc.Elem.Newline,
|
||||
CodeBlock(CodeBlock.Line(6, "More code"))
|
||||
)
|
||||
)
|
||||
)
|
||||
"""Foo *Foo* ~*Bar~ `foo bar baz bo`
|
||||
|
|
||||
|
|
||||
@ -339,7 +523,7 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
Section.Raw(
|
||||
"ul:",
|
||||
Newline,
|
||||
List(2, List.Unordered, " Foo", " Bar")
|
||||
List(2, List.Unordered, "Foo", "Bar")
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -348,25 +532,25 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
Section.Raw(
|
||||
"ol:",
|
||||
Newline,
|
||||
List(2, List.Ordered, " Foo", " Bar")
|
||||
List(2, List.Ordered, "Foo", "Bar")
|
||||
)
|
||||
)
|
||||
)
|
||||
"""List
|
||||
| - First unordered item
|
||||
| - Second unordered item
|
||||
| - Third unordered item""".stripMargin
|
||||
|- First
|
||||
|- Second
|
||||
|- Third""".stripMargin
|
||||
.replaceAll(System.lineSeparator(), "\n") ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
"List",
|
||||
Newline,
|
||||
List(
|
||||
2,
|
||||
0,
|
||||
List.Unordered,
|
||||
" First unordered item",
|
||||
" Second unordered item",
|
||||
" Third unordered item"
|
||||
"First",
|
||||
"Second",
|
||||
"Third"
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -384,14 +568,234 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
List(
|
||||
2,
|
||||
List.Unordered,
|
||||
" First unordered item",
|
||||
" Second unordered item",
|
||||
" Third unordered item"
|
||||
"First unordered item",
|
||||
"Second unordered item",
|
||||
"Third unordered item"
|
||||
),
|
||||
Newline
|
||||
)
|
||||
)
|
||||
)
|
||||
"""List
|
||||
|- _First_
|
||||
|- *Second*""".stripMargin
|
||||
.replaceAll(System.lineSeparator(), "\n") ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
"List",
|
||||
Newline,
|
||||
List(
|
||||
0,
|
||||
List.Unordered,
|
||||
Formatter(Formatter.Italic, "First"),
|
||||
Formatter(Formatter.Bold, "Second")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
"""List
|
||||
|- _First_ list `item`
|
||||
|- *Second* list ~item""".stripMargin
|
||||
.replaceAll(System.lineSeparator(), "\n") ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
"List",
|
||||
Newline,
|
||||
List(
|
||||
0,
|
||||
List.Unordered,
|
||||
ListItem(
|
||||
Formatter(Formatter.Italic, "First"),
|
||||
" list ",
|
||||
CodeBlock.Inline("item")
|
||||
),
|
||||
ListItem(
|
||||
Formatter(Formatter.Bold, "Second"),
|
||||
" list ",
|
||||
Formatter.Unclosed(Formatter.Strikeout, "item")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
"""List
|
||||
|- _First_ list `item`
|
||||
|- *Second* list ~item
|
||||
|""".stripMargin
|
||||
.replaceAll(System.lineSeparator(), "\n") ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
"List",
|
||||
Newline,
|
||||
List(
|
||||
0,
|
||||
List.Unordered,
|
||||
ListItem(
|
||||
Formatter(Formatter.Italic, "First"),
|
||||
" list ",
|
||||
CodeBlock.Inline("item")
|
||||
),
|
||||
ListItem(
|
||||
Formatter(Formatter.Bold, "Second"),
|
||||
" list ",
|
||||
Formatter.Unclosed(Formatter.Strikeout, "item", Newline)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
""" List
|
||||
| - unclosed_formatter
|
||||
| - second""".stripMargin.stripMargin
|
||||
.replaceAll(System.lineSeparator(), "\n") ?== Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
1,
|
||||
"List",
|
||||
Newline,
|
||||
List(
|
||||
3,
|
||||
List.Unordered,
|
||||
ListItem(
|
||||
"unclosed",
|
||||
Formatter.Unclosed(Formatter.Italic, "formatter")
|
||||
),
|
||||
"second"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
"""List
|
||||
| - First
|
||||
| - Second
|
||||
| * First1
|
||||
| * Second1
|
||||
| - Third""".stripMargin
|
||||
.replaceAll(System.lineSeparator(), "\n") ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
"List",
|
||||
Newline,
|
||||
List(
|
||||
2,
|
||||
List.Unordered,
|
||||
"First",
|
||||
"Second",
|
||||
List(
|
||||
4,
|
||||
List.Ordered,
|
||||
"First1",
|
||||
"Second1"
|
||||
),
|
||||
"Third"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
"""List
|
||||
| - First
|
||||
| - First1
|
||||
| - First2
|
||||
| - First3""".stripMargin
|
||||
.replaceAll(System.lineSeparator(), "\n") ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
"List",
|
||||
Newline,
|
||||
List(
|
||||
2,
|
||||
List.Unordered,
|
||||
"First",
|
||||
List(
|
||||
4,
|
||||
List.Unordered,
|
||||
"First1",
|
||||
List(
|
||||
6,
|
||||
List.Unordered,
|
||||
"First2",
|
||||
List(
|
||||
8,
|
||||
List.Unordered,
|
||||
"First3"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
"""List
|
||||
| - First
|
||||
| - First1
|
||||
| - First2
|
||||
| - First3
|
||||
|""".stripMargin
|
||||
.replaceAll(System.lineSeparator(), "\n") ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
"List",
|
||||
Newline,
|
||||
List(
|
||||
2,
|
||||
List.Unordered,
|
||||
"First",
|
||||
List(
|
||||
4,
|
||||
List.Unordered,
|
||||
"First1",
|
||||
List(
|
||||
6,
|
||||
List.Unordered,
|
||||
"First2",
|
||||
List(
|
||||
8,
|
||||
List.Unordered,
|
||||
ListItem("First3", Newline)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
"""List
|
||||
| - First
|
||||
| - First1
|
||||
| - First2
|
||||
| - First3
|
||||
| - Second""".stripMargin
|
||||
.replaceAll(System.lineSeparator(), "\n") ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
"List",
|
||||
Newline,
|
||||
List(
|
||||
2,
|
||||
List.Unordered,
|
||||
"First",
|
||||
List(
|
||||
4,
|
||||
List.Unordered,
|
||||
"First1",
|
||||
List(
|
||||
6,
|
||||
List.Unordered,
|
||||
"First2",
|
||||
List(
|
||||
8,
|
||||
List.Unordered,
|
||||
"First3"
|
||||
)
|
||||
)
|
||||
),
|
||||
"Second"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
"""List
|
||||
| - First unordered item
|
||||
| - Second unordered item
|
||||
@ -406,15 +810,15 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
List(
|
||||
2,
|
||||
List.Unordered,
|
||||
" First unordered item",
|
||||
" Second unordered item",
|
||||
"First unordered item",
|
||||
"Second unordered item",
|
||||
List(
|
||||
4,
|
||||
List.Ordered,
|
||||
" First ordered sub item",
|
||||
" Second ordered sub item"
|
||||
"First ordered sub item",
|
||||
"Second ordered sub item"
|
||||
),
|
||||
" Third unordered item"
|
||||
"Third unordered item"
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -433,15 +837,15 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
List(
|
||||
2,
|
||||
List.Unordered,
|
||||
" First unordered item",
|
||||
" Second unordered item",
|
||||
"First unordered item",
|
||||
"Second unordered item",
|
||||
List(
|
||||
4,
|
||||
List.Ordered,
|
||||
" First ordered sub item",
|
||||
" Second ordered sub item"
|
||||
"First ordered sub item",
|
||||
" Second ordered sub item"
|
||||
),
|
||||
" Third unordered item"
|
||||
"Third unordered item"
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -466,29 +870,29 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
List(
|
||||
2,
|
||||
List.Unordered,
|
||||
" First unordered item",
|
||||
" Second unordered item",
|
||||
"First unordered item",
|
||||
"Second unordered item",
|
||||
List(
|
||||
4,
|
||||
List.Ordered,
|
||||
" First ordered sub item",
|
||||
" Second ordered sub item"
|
||||
"First ordered sub item",
|
||||
"Second ordered sub item"
|
||||
),
|
||||
" Third unordered item",
|
||||
"Third unordered item",
|
||||
List(
|
||||
4,
|
||||
List.Ordered,
|
||||
" First ordered sub item",
|
||||
" Second ordered sub item",
|
||||
"First ordered sub item",
|
||||
"Second ordered sub item",
|
||||
List(
|
||||
6,
|
||||
List.Unordered,
|
||||
" First unordered sub item",
|
||||
" Second unordered sub item"
|
||||
"First unordered sub item",
|
||||
"Second unordered sub item"
|
||||
),
|
||||
" Third ordered sub item"
|
||||
"Third ordered sub item"
|
||||
),
|
||||
" Fourth unordered item"
|
||||
"Fourth unordered item"
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -499,18 +903,12 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
"""List
|
||||
| - First unordered item
|
||||
| - Second unordered item
|
||||
| * First ordered sub item
|
||||
| * Second ordered sub item
|
||||
| - Third unordered item
|
||||
| * First ordered sub item
|
||||
| * Second ordered sub item
|
||||
| - First unordered sub item
|
||||
| - Second unordered sub item
|
||||
| * Third ordered sub item
|
||||
| * Wrong Indent Item
|
||||
| - Fourth unordered item""".stripMargin
|
||||
| - First
|
||||
| * Aligned
|
||||
| * Misaligned
|
||||
| * Misaligned _styled_
|
||||
| * Correct
|
||||
| - Second""".stripMargin
|
||||
.replaceAll(System.lineSeparator(), "\n") ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
@ -519,30 +917,53 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
List(
|
||||
2,
|
||||
List.Unordered,
|
||||
" First unordered item",
|
||||
" Second unordered item",
|
||||
"First",
|
||||
List(
|
||||
4,
|
||||
5,
|
||||
List.Ordered,
|
||||
" First ordered sub item",
|
||||
" Second ordered sub item"
|
||||
"Aligned",
|
||||
Elem.MisalignedItem(4, List.Ordered, "Misaligned"),
|
||||
Elem.MisalignedItem(
|
||||
3,
|
||||
List.Ordered,
|
||||
"Misaligned ",
|
||||
Formatter(Formatter.Italic, "styled")
|
||||
),
|
||||
"Correct"
|
||||
),
|
||||
" Third unordered item",
|
||||
"Second"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
"""List
|
||||
| - First
|
||||
| - First1
|
||||
| - Second1
|
||||
| - First2
|
||||
| - Second""".stripMargin
|
||||
.replaceAll(System.lineSeparator(), "\n") ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
"List",
|
||||
Newline,
|
||||
List(
|
||||
2,
|
||||
List.Unordered,
|
||||
"First",
|
||||
List(
|
||||
4,
|
||||
List.Ordered,
|
||||
" First ordered sub item",
|
||||
" Second ordered sub item",
|
||||
List.Unordered,
|
||||
"First1",
|
||||
Elem.MisalignedItem(3, List.Unordered, "Second1"),
|
||||
List(
|
||||
6,
|
||||
List.Unordered,
|
||||
" First unordered sub item",
|
||||
" Second unordered sub item"
|
||||
),
|
||||
" Third ordered sub item",
|
||||
List.Indent.Invalid(3, List.Ordered, " Wrong Indent Item")
|
||||
"First2"
|
||||
)
|
||||
),
|
||||
" Fourth unordered item"
|
||||
"Second"
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -770,7 +1191,23 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
Newline,
|
||||
List(1, List.Unordered, " bar\n baz"),
|
||||
List(1, List.Unordered, ListItem("bar", Newline, " ", "baz")),
|
||||
Newline
|
||||
)
|
||||
)
|
||||
)
|
||||
"""
|
||||
| - bar
|
||||
| baz
|
||||
|""".stripMargin.replaceAll(System.lineSeparator(), "\n") ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
Newline,
|
||||
List(
|
||||
1,
|
||||
List.Unordered,
|
||||
ListItem("bar", Newline, CodeBlock(CodeBlock.Line(5, "baz")))
|
||||
),
|
||||
Newline
|
||||
)
|
||||
)
|
||||
@ -784,7 +1221,31 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
Newline,
|
||||
List(1, List.Unordered, " bar\n baz", " bar\n baz"),
|
||||
List(
|
||||
1,
|
||||
List.Unordered,
|
||||
ListItem("bar", Newline, " ", "baz"),
|
||||
ListItem("bar", Newline, " ", "baz")
|
||||
),
|
||||
Newline
|
||||
)
|
||||
)
|
||||
)
|
||||
"""
|
||||
| - bar
|
||||
| baz
|
||||
| - bar
|
||||
| baz
|
||||
|""".stripMargin.replaceAll(System.lineSeparator(), "\n") ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
Newline,
|
||||
List(
|
||||
1,
|
||||
List.Unordered,
|
||||
ListItem("bar", Newline, CodeBlock(CodeBlock.Line(5, "baz"))),
|
||||
ListItem("bar", Newline, " ", "baz")
|
||||
),
|
||||
Newline
|
||||
)
|
||||
)
|
||||
@ -800,8 +1261,41 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
1,
|
||||
"This does foo:",
|
||||
Newline,
|
||||
List(1, List.Unordered, " bar\n baz"),
|
||||
List(
|
||||
1,
|
||||
List.Unordered,
|
||||
ListItem(
|
||||
"bar",
|
||||
Newline,
|
||||
" ",
|
||||
"baz",
|
||||
Newline,
|
||||
" ",
|
||||
"Another raw text."
|
||||
)
|
||||
),
|
||||
Newline
|
||||
)
|
||||
)
|
||||
)
|
||||
""" This does foo:
|
||||
| - bar
|
||||
| baz
|
||||
|
|
||||
| Another raw text.
|
||||
|""".stripMargin.replaceAll(System.lineSeparator(), "\n") ?= Doc(
|
||||
Synopsis(
|
||||
Section.Raw(
|
||||
1,
|
||||
"This does foo:",
|
||||
Newline,
|
||||
List(1, List.Unordered, ListItem("bar", Newline, " ", "baz")),
|
||||
Newline
|
||||
)
|
||||
),
|
||||
Body(
|
||||
Section.Raw(
|
||||
1,
|
||||
"Another raw text.",
|
||||
Newline
|
||||
)
|
||||
@ -822,7 +1316,7 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
Newline,
|
||||
"This does foo:",
|
||||
Newline,
|
||||
List(4, List.Unordered, " bar\n baz"),
|
||||
List(4, List.Unordered, ListItem("bar", Newline, " ", "baz")),
|
||||
Newline
|
||||
)
|
||||
)
|
||||
@ -928,7 +1422,10 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
| import Standard.Base.System.File
|
||||
| import Standard.Examples
|
||||
|
|
||||
| example_new = File.new Examples.csv_path
|
||||
| example_new =
|
||||
| path =
|
||||
| Examples.csv_path
|
||||
| File.new path
|
||||
|""".stripMargin.replaceAll(System.lineSeparator(), "\n") ?== Doc(
|
||||
Tags(Tags.Tag(0, Tags.Tag.Type.Alias, " New File")),
|
||||
Synopsis(Section.Raw(0, Newline)),
|
||||
@ -951,9 +1448,13 @@ class DocParserTests extends AnyFlatSpec with Matchers {
|
||||
CodeBlock(
|
||||
CodeBlock.Line(6, "import Standard.Base.System.File"),
|
||||
CodeBlock.Line(6, "import Standard.Examples"),
|
||||
CodeBlock.Line(6, "example_new = File.new Examples.csv_path")
|
||||
CodeBlock.Line(6, "example_new ="),
|
||||
CodeBlock.Line(10, "path ="),
|
||||
CodeBlock.Line(14, "Examples.csv_path"),
|
||||
CodeBlock.Line(10, "File.new path")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user