Documentation Parser: Extended HTML Files Generator (#237)

* Update CSS

* Add some code - WIP

* recursive Documented HTML generation

* WIP - Better documentation, better code, better test

* documentation wip

* code complexity changes

* update  docs

* update docs

* move saving out of class

* move all html generator code to separate object

* Fix documentation

* Change example text

* 80 chars/ln

* Merge remote-tracking branch 'origin/master' into wip/mm/doc-parser-html-output

# Conflicts:
#	Syntax/specialization/src/main/scala/org/enso/syntax/text/Parser.scala

* Merge remote-tracking branch 'origin/master' into wip/mm/doc-parser-html-output

# Conflicts:
#	Syntax/specialization/src/main/scala/org/enso/syntax/text/Parser.scala

* just add example

* add sass

* minor fix

* cleanup

* rm unused func
This commit is contained in:
Maciej Mikołajek 2019-11-25 10:36:31 +01:00 committed by GitHub
parent 85272edcb0
commit 05877a7d0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 703 additions and 453 deletions

View File

@ -35,7 +35,7 @@ final case class Doc(
}
object Doc {
def apply(): Doc = Doc(None, None, None)
def apply(): Doc = Doc(None, None, None)
def apply(tags: Tags): Doc = Doc(Some(tags), None, None)
def apply(synopsis: Synopsis): Doc =
Doc(None, Some(synopsis), None)
@ -61,18 +61,8 @@ object Doc {
* extending tokens and getting HTML file out of Doc Parser
*/
sealed trait Symbol extends Repr.Provider {
def show() = repr.build()
def show(): String = repr.build()
def html: HTML
def renderHTML(cssLink: String): HTMLTag = {
val metaEquiv = HTML.httpEquiv := "Content-Type"
val metaCont = HTML.content := "text/html"
val metaChar = HTML.charset := "UTF-8"
val meta = HTML.meta(metaEquiv)(metaCont)(metaChar)
val cssRel = HTML.rel := "stylesheet"
val cssHref = HTML.href := cssLink
val css = HTML.link(cssRel)(cssHref)
HTML.html(HTML.head(meta, css), HTML.body(html))
}
def htmlCls(): generic.AttrPair[Builder, String] =
HTML.`class` := getClass.toString.split('$').last.split('.').last
@ -155,7 +145,7 @@ object Doc {
}
object Unclosed {
def apply(typ: Type): Unclosed = Unclosed(typ, Nil)
def apply(typ: Type): Unclosed = Unclosed(typ, Nil)
def apply(typ: Type, elem: Elem): Unclosed = Unclosed(typ, elem :: Nil)
def apply(typ: Type, elems: Elem*): Unclosed =
Unclosed(typ, elems.toList)
@ -340,7 +330,7 @@ object Doc {
*/
sealed trait Section extends Symbol {
def indent: Int
def elems: List[Elem]
def elems: List[Elem]
def reprOfNormalText(elem: Elem, prevElem: Elem): Repr.Builder = {
prevElem match {
@ -363,7 +353,7 @@ object Doc {
val html: HTML = Seq(HTML.div(htmlCls())(elems.map(_.html)))
}
object Header {
def apply(elem: Elem): Header = Header(elem :: Nil)
def apply(elem: Elem): Header = Header(elem :: Nil)
def apply(elems: Elem*): Header = Header(elems.toList)
}
@ -441,13 +431,13 @@ object Doc {
}
object Raw {
def apply(indent: Int): Raw = Raw(indent, Nil)
def apply(indent: Int, elem: Elem): Raw = Raw(indent, elem :: Nil)
def apply(indent: Int): Raw = Raw(indent, Nil)
def apply(indent: Int, elem: Elem): Raw = Raw(indent, elem :: Nil)
def apply(indent: Int, elems: Elem*): Raw = Raw(indent, elems.toList)
val defaultIndent = 0
def apply(): Raw = Raw(defaultIndent, Nil)
def apply(elem: Elem): Raw = Raw(defaultIndent, elem :: Nil)
def apply(elems: Elem*): Raw = Raw(defaultIndent, elems.toList)
val defaultIndent = 0
def apply(): Raw = Raw(defaultIndent, Nil)
def apply(elem: Elem): Raw = Raw(defaultIndent, elem :: Nil)
def apply(elems: Elem*): Raw = Raw(defaultIndent, elems.toList)
}
}
@ -511,7 +501,7 @@ object Doc {
val html: HTML = Seq(HTML.div(htmlCls())(elems.toList.map(_.html)))
}
object Tags {
def apply(elem: Tag): Tags = Tags(List1(elem))
def apply(elem: Tag): Tags = Tags(List1(elem))
def apply(elems: Tag*): Tags = Tags(List1(elems.head, elems.tail.toList))
/** Tag - one single tag for Tags
@ -535,7 +525,7 @@ object Doc {
}
}
object Tag {
val defaultIndent = 0
val defaultIndent = 0
def apply(typ: Type): Tag = Tag(defaultIndent, typ, None)
def apply(typ: Type, details: String): Tag =
Tag(defaultIndent, typ, Some(details))

View File

@ -1,5 +1,6 @@
package org.enso.syntax.text
import java.io.{File, PrintWriter}
import org.enso.flexer
import org.enso.flexer.Reader
import org.enso.syntax.text.ast.Doc
@ -20,6 +21,7 @@ import org.enso.syntax.text.AST.Block.{LineOf => Line}
*
* It is used to create structured documentation from the blocks of commented
* text created by the main Enso parser.
*
* It has been built on the same foundation as Parser, so in order not to
* duplicate information, please refer to Parser documentation.
*/
@ -47,65 +49,6 @@ class DocParser {
* @return - unmatched result possibly containing Doc
*/
def run(input: String): Result[Doc] = engine.run(new Reader(input))
//////////////////////////////////////////////////////////////////////////////
//// HTML Rendering of Documentation /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// TODO remove this functionality from parser
/**
* Used to create HTML files from Doc with or without title after Doc Parser
* Runner finished it's job
*
* @param documented - documented made by Doc Parser Runner from AST and Doc
*/
def onHTMLRendering(documented: AST.Documented): Unit = {
val path = "syntax/specialization/target/"
val cssFileName = "style.css"
val htmlCode = renderHTML(documented.ast, documented.doc, cssFileName)
val astLines = documented.ast.show().split("\n")
val fileName = astLines.head.replaceAll("/", "")
htmlCode
}
/**
* Function invoked by [[onHTMLRendering]] to render HTML File
*
* @param ast - ast from Doc Parser Runner
* @param doc - Doc from Doc Parser
* @param cssLink - string containing CSS file name
* @return - HTML Code from Doc with optional title from AST
*/
def renderHTML(
ast: AST,
doc: Doc,
cssLink: String = "style.css"
): TypedTag[String] = {
val title = ast.show().split("\n").head
val astHtml = Seq(HTML.div(HTML.`class` := "ASTData")(ast.show()))
val docClass = HTML.`class` := "Documentation"
val documentation = Seq(HTML.div(docClass)(doc.html, astHtml))
HTML.html(createHTMLHead(title, cssLink), HTML.body(documentation))
}
/**
* Function invoked by [[renderHTML]] to create HTML.Head part of file
*
* @param title - HTML page title
* @param cssLink - string containing CSS file name
* @return - HTML Head Code
*/
def createHTMLHead(title: String, cssLink: String): TypedTag[String] = {
val metaEquiv = HTML.httpEquiv := "Content-Type"
val metaCont = HTML.content := "text/html"
val metaChar = HTML.charset := "UTF-8"
val meta = HTML.meta(metaEquiv)(metaCont)(metaChar)
val cssRel = HTML.rel := "stylesheet"
val cssHref = HTML.href := cssLink
val css = HTML.link(cssRel)(cssHref)
val fileTitle = scalatags.Text.tags2.title(title)
HTML.head(meta, css)(fileTitle)
}
}
object DocParser {
@ -117,7 +60,6 @@ object DocParser {
*/
def runMatched(input: String): Doc = new DocParser().runMatched(input)
def run(input: String): Result[Doc] = new DocParser().run(input)
}
////////////////////////////////////////////////////////////////////////////////
@ -130,8 +72,9 @@ object DocParser {
* Essentially it binds together Enso Parser with Doc Parser.
* When Parser finishes its job it invokes runner with AST created by it after
* resolving macros. Then Runner does it's job - running Doc Parser on every
* [[AST.Comment]], combined with connecting [[Doc]] with AST in [[AST.Documented]]
* node, which gets AST from [[AST.Def]] and [[AST.App.Infix]]
* [[AST.Comment]], combined with connecting [[Doc]] with AST in
* [[AST.Documented]] node, which gets AST from [[AST.Def]] and
* [[AST.App.Infix]]
*/
object DocParserRunner {
//////////////////////////////////////////////////////////////////////////////
@ -165,8 +108,8 @@ object DocParserRunner {
}
/**
* Helper functions for [[createDocs]]
* to traverse through Module and Def body
* This is a helper function for [[createDocs]] to traverse through
* [[AST.Module]] and create Docs from comments with appropriate [[AST]]
*/
def createDocsFromModule(m: AST.Module): AST.Module = {
val emptyLine = List1(AST.Block.OptLine())
@ -175,6 +118,11 @@ object DocParserRunner {
AST.Module(transformedLines)
}
/**
* This is a helper function for [[createDocs]] to traverse through
* [[AST.Def]] and create Docs from comments inside [[AST.Def]] with
* appropriate [[AST]]
*/
def createDocsFromDefBody(
name: AST.Cons,
args: List[AST],
@ -301,6 +249,7 @@ object DocParserRunner {
* method
*
* @param comment - comment found in AST
* @param emptyLines - Empty lines in between Doc and AST
* @param off - line offset
* @param ast - AST to go with comment into Documented
* @return - [[AST.Documented]]
@ -315,24 +264,263 @@ object DocParserRunner {
val documented = Some(AST.Documented(doc, emptyLines, ast))
Line(documented, off)
}
}
//////////////////////////////////////////////////////////////////////////////
//// Generating HTML for created Doc's ///////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//// Doc Parser HTML Generator /////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/**
* This is Doc Parser HTML Generator.
*
* Essentially it enables Doc Parser to create pretty HTML files from
* documented code.
*
* When Doc Parser finishes its job user can invoke DocParserHTMLGenerator by
* simply passing the output of Doc Parser onto function called
* [[DocParserHTMLGenerator.generateHTMLForEveryDocumented]], and it will
* automatically traverse through AST prepared by Doc Parser and generate
* HTML files in all appropriate places.
*/
object DocParserHTMLGenerator {
/**
* This method is used for generation of HTML files from parsed and
* reformatted [[AST.Documented]]
*
* @param ast - parsed AST.Module and reformatted using Doc Parser
* @param path - path to save file
* @param cssFileName - name of file containing stylesheets for the HTML code
*/
def generateHTMLForEveryDocumented(ast: AST): Unit = {
def generateHTMLForEveryDocumented(
ast: AST,
path: String,
cssFileName: String
): Unit = {
ast.map { elem =>
elem match {
case AST.Documented.any(d) => new DocParser().onHTMLRendering(d)
case _ => generateHTMLForEveryDocumented(elem)
case AST.Documented.any(d) =>
val file = onHTMLRendering(d, cssFileName)
saveHTMLToFile(path, file._2, file._1)
case _ => generateHTMLForEveryDocumented(elem, path, cssFileName)
}
elem
}
}
/**
* Saves HTML code to file
*
* @param path - path to file
* @param name - file name
* @param code - HTML code generated with Doc Parser
*/
def saveHTMLToFile(
path: String,
name: String,
code: TypedTag[String]
): Unit = {
val writer = new PrintWriter(new File(path + name + ".html"))
writer.write(code.toString)
writer.close()
}
//////////////////////////////////////////////////////////////////////////////
//// HTML Rendering of Documentation /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
/**
* Used to create HTML files from Doc with or without title after Doc Parser
* Runner finished it's job
*
* @param documented - documented made by Doc Parser Runner from AST and Doc
* @param cssFileName - name of file containing stylesheets for the HTML code
* @return - tuple containing HTML code with file name
*/
def onHTMLRendering(
documented: AST.Documented,
cssFileName: String
): (TypedTag[String], String) = {
val htmlCode = renderHTML(documented.ast, documented.doc, cssFileName)
val astLines = documented.ast.show().split("\n")
val fileName = astLines.head.replaceAll("/", "")
(htmlCode, fileName)
}
/**
* Function invoked by [[onHTMLRendering]] to render HTML File
*
* @param ast - AST from Parser
* @param doc - Doc from Doc Parser
* @param cssLink - string containing CSS file name
* @return - HTML Code from Doc and contents of [[AST.Def]] or
* [[AST.App.Infix]], with optional title made from AST
*/
def renderHTML(
ast: AST,
doc: Doc,
cssLink: String = "style.css"
): TypedTag[String] = {
val title = ast.show().split("\n").head
val documentation = DocumentedToHtml(ast, doc)
HTML.html(createHTMLHead(title, cssLink), HTML.body(documentation))
}
/**
* This function is used to get HTML content of Doc and try to render AST,
* by finding if it also contains Documented to retrieve Doc and it's AST,
* or simply call show() method on other element of AST.
*
* @param ast - AST from Parser
* @param doc - Doc from Doc Parser
* @return - HTML Code from Doc and contents of [[AST.Def]] or
* [[AST.App.Infix]]
*/
def DocumentedToHtml(
ast: AST,
doc: Doc
): TypedTag[String] = {
val astCls = HTML.`class` := "ASTData"
val astHtml = Seq(HTML.div(astCls)(createHTMLFromAST(ast)))
val docClass = HTML.`class` := "Documentation"
HTML.div(docClass)(doc.html, astHtml)
}
/**
* Function invoked by [[DocumentedToHtml]] to create HTML from AST in
* [[AST.Documented]]
*
* @param ast - AST
* @return - HTML Code
*/
def createHTMLFromAST(ast: AST): TypedTag[String] = {
ast match {
case AST.Def.any(d) =>
d.body match {
case Some(body) =>
body match {
case AST.Block.any(b) => createDefWithBody(d.name, d.args, b)
case _ => createDefWithoutBody(d.name, d.args)
}
case None => createDefWithoutBody(d.name, d.args)
}
case AST.App.Infix.any(i) => createInfixHtmlRepr(i)
case _ => HTML.div()
}
}
/**
* Helper function for [[createHTMLFromAST]] to generate appropriate code
* from [[AST.Def]] with traversing through body and creating HTML code
* on elements in it
*
* @param name - Def Name
* @param args - Def Arguments
* @param body - Def body
* @return - HTML code generated from Def
*/
def createDefWithBody(
name: AST.Cons,
args: List[AST],
body: AST.Block
): TypedTag[String] = {
val firstLine = Line(Option(body.firstLine.elem), body.firstLine.off)
val allLines = firstLine :: body.lines
val generatedCode = renderHTMLOnLine(allLines)
val head = createDefTitle(name, args)
val clsBody = HTML.`class` := "DefBody"
val lines = HTML.div(clsBody)(generatedCode)
val cls = HTML.`class` := "Def"
HTML.div(cls)(head, lines)
}
/**
* Helper function for [[createHTMLFromAST]] to generate appropriate code
* from [[AST.Def]] when it doesn't contain anything in it's body
*
* @param name - Def Name
* @param args - Def Arguments
* @return - HTML code generated from Def
*/
def createDefWithoutBody(
name: AST.Cons,
args: List[AST]
): TypedTag[String] = {
val cls = HTML.`class` := "DefNoBody"
HTML.div(cls)(createDefTitle(name, args))
}
/**
* Helper function for [[createDefWithBody]] or [[createDefWithoutBody]]
* to generate [[AST.Def]] title form it's name and args
*
* @param name - Def Name
* @param args - Def Arguments
* @return - Def title in HTML
*/
def createDefTitle(name: AST.Cons, args: List[AST]): TypedTag[String] = {
val clsTitle = HTML.`class` := "DefTitle"
val clsArgs = HTML.`class` := "DefArgs"
HTML.div(clsTitle)(name.show(), HTML.div(clsArgs)(args.map(_.show())))
}
/**
* Helper function for [[createHTMLFromAST]] to generate appropriate HTML
* code from [[AST.App.Infix]]
*
* @param infix - AST Infix
* @return - HTML code generated from Infix
*/
def createInfixHtmlRepr(infix: AST.App.Infix): TypedTag[String] = {
val cls = HTML.`class` := "Infix"
HTML.div(cls)(infix.larg.show())
}
/**
* Helper function for [[createDefWithBody]] to traverse through body's lines
* and try to generate HTML code from [[AST.Documented]] parts of it. It also
* tries to find nested [[AST.Def]] and [[AST.App.Infix]] inside of body
*
* @param lines - lines inside of Def body
* @return - HTML code generated from contents of lines
*/
def renderHTMLOnLine(lines: List[AST.Block.OptLine]): List[TypedTag[String]] =
lines match {
case Line(Some(AST.Documented.any(doc)), _) :: rest =>
val cls = HTML.`class` := "DefDoc"
val docHtml = DocumentedToHtml(doc.ast, doc.doc)
HTML.div(cls)(docHtml) :: renderHTMLOnLine(rest)
case x :: rest =>
x match {
case Line(Some(d), _) =>
val cls = HTML.`class` := "DefNoDoc"
val astHtml = createHTMLFromAST(d)
HTML.div(cls)(astHtml) :: renderHTMLOnLine(rest)
case _ => renderHTMLOnLine(rest)
}
case other =>
other match {
case Nil => List()
case _ :: rest => renderHTMLOnLine(rest)
}
}
/**
* Function invoked by [[DocumentedToHtml]] to create HTML.Head part of file
*
* @param title - HTML page title
* @param cssLink - string containing CSS file name
* @return - HTML Head Code
*/
def createHTMLHead(title: String, cssLink: String): TypedTag[String] = {
val metaEquiv = HTML.httpEquiv := "Content-Type"
val metaCont = HTML.content := "text/html"
val metaChar = HTML.charset := "UTF-8"
val meta = HTML.meta(metaEquiv)(metaCont)(metaChar)
val cssRel = HTML.rel := "stylesheet"
val cssHref = HTML.href := cssLink
val css = HTML.link(cssRel)(cssHref)
val fileTitle = scalatags.Text.tags2.title(title)
HTML.head(meta, css)(fileTitle)
}
}

View File

@ -1,333 +0,0 @@
/*//////////////////////////////////////////////////////////////////////////////
//// UNDER CONSTRUCTION ////
//////////////////////////////////////////////////////////////////////////////*/
/*///////////
//// DOM ////
///////////*/
body {
-webkit-font-smoothing: antialiased;
font-style: normal;
word-wrap: break-word;
font-size: 17px;
line-height: 1.52947;
font-weight: 400;
letter-spacing: -0.021em;
font-family: "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica",
"Arial", sans-serif;
background-color: white;
color: #333333;
font-style: normal;
}
p {
display: block;
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0px;
margin-inline-end: 0px;
}
a:hover {
color: #35a5ff !important;
text-decoration: none;
}
a {
color: #0070c9;
background-color: transparent;
text-decoration: none;
display: inline-block;
transition: all 0.3s ease;
}
img {
display: block;
}
code {
color: #0070c9;
background-color: transparent;
font-size: inherit;
font-family: monospace;
line-height: inherit;
display: inline-block;
white-space: pre-wrap;
}
button {
display: inline-block;
padding: 8px 30px;
margin: 10px 0;
outline: none;
background-color: #777;
border: none;
color: #fafafa;
border-radius: 5px;
box-shadow: 5px 0 #555;
font-size: 13px;
vertical-align: top;
transition: all 0.3s ease;
}
button:hover {
background-color: #333;
}
b {
font-weight: 600;
}
h1 {
font-size: 34px;
line-height: 1.08824;
font-weight: 500;
letter-spacing: 0.01em;
}
h2 {
font-size: 28px;
line-height: 1.1073;
font-weight: 500;
letter-spacing: 0.012em;
}
.Body h2 {
margin: 0;
margin-top: 0.65rem;
}
/*///////////////////
//// Invalid AST ////
///////////////////*/
.creator .Unclosed,
.creator .invalidIndent,
.creator .invalidLink{
display: inline;
color: orangered;
}
.Unclosed,
.invalidIndent,
.invalidLink {
display: inline;
}
/*//////////////
//// Header ////
//////////////*/
.Header {
font-size: 19px;
font-weight: 500;
}
.Important .Header,
.Info .Header,
.Example .Header {
margin-bottom: 0.7em;
font-weight: 600;
letter-spacing: -0.021em;
line-height: 17px;
font-synthesis: none;
font-family: "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica",
"Arial", sans-serif;
}
/*////////////
//// Tags ////
////////////*/
.Doc .Tags {
margin-left: auto;
margin-right: auto;
margin-bottom: 20px;
padding: 15px 0;
text-align: center;
background-color: #fafafa;
}
.Doc .ExtForTagDetails {
margin: 0 3px;
color: #999999;
}
.Doc .Tags .DEPRECATED,
.Doc .Tags .MODIFIED,
.Doc .Tags .ADDED,
.Doc .Tags .UPCOMING,
.Doc .Tags .REMOVED,
.Doc .Tags .UNRECOGNIZED {
line-height: 1.5;
font-weight: 400;
border-radius: 3px;
font-size: 12px;
letter-spacing: -0.021em;
border: 1px solid;
display: inline-flex;
padding: 3px 15px;
margin: 2px;
white-space: nowrap;
background: transparent;
}
.Doc .Tags .DEPRECATED {
border-color: #c35400;
color: #c35400;
}
.Doc .Tags .MODIFIED {
border-color: #8A82CF;
color: #8A82CF;
}
.Doc .Tags .ADDED {
border-color: #79A129;
color: #79A129;
}
.Doc .Tags .UPCOMING,
.Doc .Tags .REMOVED,
.Doc .Tags .UNRECOGNIZED {
border-color: #888888;
color: #666666;
}
.creator .Doc .Tags .UNRECOGNIZED {
border: 2px solid;
color: orangered;
}
/*////////////////
//// Sections ////
////////////////*/
.Raw,
.Important,
.Info,
.CodeBlock,
.Example {
margin-top: 0;
margin-left: auto;
margin-right: auto;
position: relative;
}
.Body .Raw {
padding: 0.75em 0;
margin-bottom: 0.6rem;
font-size: 17px;
line-height: 1.52947;
font-weight: 400;
letter-spacing: -0.021em;
font-family: "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica",
"Arial", sans-serif;
background-color: white;
color: #333333;
font-style: normal;
}
.Synopsis .Raw {
margin-bottom: 2rem;
}
.Important,
.Info,
.CodeBlock,
.Example {
font-size: 17;
padding-top: 0.94118rem;
padding-bottom: 0.94118rem;
padding-left: 18px;
padding-right: 10px;
border: 1px solid #8a82cf;
border-left: 6px solid #8a82cf;
border-radius: 6px;
margin: 0.7em 0;
}
.Important {
border-right: 0px;
border-top: 0px;
border-bottom: 0px;
border-color: #fee450;
background-color: #fbf8e8;
}
.Info {
border-color: 69c5e8;
}
.Example {
border-color: #8a82cf;
}
.CodeBlock {
border-color: #7cd58b;
margin: 10px 20px;
display: none;
}
/*/////////////////////////
//// Synopsis & Detail ////
/////////////////////////*/
.Synopsis,
.Body {
margin: 0 auto;
padding: 5px;
margin-bottom: 20px;
text-align: left;
}
.Synopsis {
font-size: 20px;
line-height: 1.5;
font-weight: 300;
letter-spacing: 0.017em;
border-bottom: 1px solid #d6d6d6
}
.Doc {
margin: 0;
width: 100%;
background-color: #ffffff;
}
@media (min-width: 300px) {
.Synopsis,
.Body {
width: 380px
}
}
@media (min-width: 500px) {
.Synopsis,
.Body {
width: 440px
}
}
@media (min-width: 600px) {
.Synopsis,
.Body {
width: 490px
}
}
@media (min-width: 900px) {
.Synopsis,
.Body {
width: 670px
}
}
@media (min-width: 1300px) {
.Synopsis,
.Body {
width: 780px
}
}

View File

@ -0,0 +1,359 @@
/*///////////
//// DOM ////
///////////*/
body
-webkit-font-smoothing: antialiased
font-style: normal
word-wrap: break-word
font-size: 17px
line-height: 1.52947
font-weight: 400
letter-spacing: -0.021em
font-family: "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica","Arial", sans-serif
background-color: white
color: #333333
font-style: normal
p
display: block
margin-block-start: 1em
margin-block-end: 1em
margin-inline-start: 0
margin-inline-end: 0
a:hover
color: #35a5ff !important
text-decoration: none
a
color: #0070c9
background-color: transparent
text-decoration: none
display: inline-block
transition: all 0.3s ease
img
display: block
code
color: #0070c9
background-color: transparent
font-size: inherit
font-family: monospace
line-height: inherit
display: inline-block
white-space: pre-wrap
button
display: inline-block
padding: 8px 30px
margin: 10px 0
outline: none
background-color: #777
border: none
color: #fafafa
border-radius: 5px
box-shadow: 5px 0 #555
font-size: 13px
vertical-align: top
transition: all 0.3s ease
button:hover
background-color: #333
b
font-weight: 600
h1
font-size: 34px
line-height: 1.08824
font-weight: 500
letter-spacing: 0.01em
h2
font-size: 28px
line-height: 1.1073
font-weight: 500
letter-spacing: 0.012em
.Body h2
margin: 0
margin-top: 0.65rem
/*///////////////////
//// Invalid AST ////
///////////////////*/
// Creator - a special mode for displaying parsing errors in output
.creator
.Unclosed,
.invalidIndent,
.invalidLink
display: inline
color: orangered
.Tags
.UNRECOGNIZED
border: 2px solid
color: orangered
.Unclosed,
.invalidIndent,
.invalidLink
display: inline
/*//////////////
//// Header ////
//////////////*/
.Header
font-size: 19px
font-weight: 500
.Important .Header,
.Info .Header,
.Example .Header
margin-bottom: 0.7em
font-weight: 600
letter-spacing: -0.021em
line-height: 17px
font-synthesis: none
font-family: "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica","Arial", sans-serif
/*////////////
//// Tags ////
////////////*/
.Tags
margin-left: auto
margin-right: auto
margin-bottom: 20px
padding: 15px 10px
text-align: center
background-color: #fafafa
border-radius: 8px
.DEPRECATED,
.MODIFIED,
.ADDED,
.UPCOMING,
.REMOVED,
.UNRECOGNIZED
line-height: 1.5
font-weight: 400
border-radius: 3px
font-size: 12px
letter-spacing: -0.021em
border: 1px solid
display: inline-flex
padding: 3px 15px
margin: 2px
white-space: nowrap
background: transparent
.DEPRECATED
border-color: #c35400
color: #c35400
.MODIFIED
border-color: #8A82CF
color: #8A82CF
.ADDED
border-color: #79A129
color: #79A129
.UPCOMING,
.REMOVED,
.UNRECOGNIZED
border-color: #888888
color: #666666
.ExtForTagDetails
margin: 0 3px
color: #999999
/*////////////////
//// Sections ////
////////////////*/
.Raw,
.Important,
.Info,
.CodeBlock,
.Example
margin-top: 0
margin-left: auto
margin-right: auto
position: relative
.Body .Raw
padding: 0.75em 0
margin-bottom: 0.6rem
font-size: 17px
line-height: 1.52947
font-weight: 400
letter-spacing: -0.021em
font-family: "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica","Arial", sans-serif
background-color: white
color: #333333
font-style: normal
.Synopsis .Raw
margin-bottom: 2rem
.Important,
.Info,
.CodeBlock,
.Example
font-size: 17px
padding-top: 0.94118rem
padding-bottom: 0.94118rem
padding-left: 18px
padding-right: 10px
border: 1px solid #8a82cf
border-left: 6px solid #8a82cf
border-radius: 6px
margin: 0.7em 0
.Important
border-right: 0
border-top: 0
border-bottom: 0
border-color: #fee450
background-color: #fbf8e8
.Info
border-color: #69c5e8
.Example
border-color: #8a82cf
.CodeBlock
border-color: #7cd58b
margin: 10px 20px
display: none
/*///////////////////////////////////
//// HTML generated - Def, Infix ////
///////////////////////////////////*/
.Def
padding: 10px 20px
.Synopsis,
.Body,
.Tags,
.ASTData
width: 90% !important
.DefTitle
display: inline-flex
font-size: x-large
font-weight: 600
margin-bottom: 20px
.DefArgs
margin-left: 5px
font-weight: 400
color: #0070c9
.Infix
font-size: large
font-weight: 500
margin-bottom: 20px
/*/////////////////////////
//// Synopsis & Detail ////
/////////////////////////*/
.Synopsis,
.Body
margin: 0 auto
padding: 5px
margin-bottom: 20px
text-align: left
.Synopsis
font-size: 20px
line-height: 1.5
font-weight: 300
letter-spacing: 0.017em
border-bottom: 1px solid #d6d6d6
.Documentation
.ASTData
margin: 20px auto
text-align: left
line-height: 1.05
letter-spacing: 0.008em
background-color: #fafafa
border-radius: 6px
.Documented
margin: 0
width: 100%
background-color: #ffffff
@media (min-width: 300px)
.Synopsis,
.Body,
.Tags,
.Documentation .ASTData
width: 380px
@media (min-width: 500px)
.Synopsis,
.Body,
.Tags,
.Documentation .ASTData
width: 440px
@media (min-width: 600px)
.Synopsis,
.Body,
.Tags,
.Documentation .ASTData
width: 490px
@media (min-width: 900px)
.Synopsis,
.Body,
.Tags,
.Documentation .ASTData
width: 670px
@media (min-width: 1300px)
.Synopsis,
.Body,
.Tags,
.Documentation .ASTData
width: 780px

View File

@ -261,30 +261,72 @@ object Main extends App {
val in2 = "(a) b = c]"
val inp2 = "a (b (c)) x"
val inp = """## This function adds `x` to `y`
|add x y = x + y
|mul x y = x * y
|
|## This function divides `x` by `y`
|div x y = x / y
|
|## Just a comment
|
|## Doc for infix with empty lines between
|
|sub x y = x - y
|
|## Foo bar baz
| bax
|def Maybe a
| ## test attached to Just
| def Just val:a
| def Nothing
|""".stripMargin
val inp =
"""
|##
| DEPRECATED
| REMOVED - replaced by Foo Bar
| ADDED
| MODIFIED
| UPCOMING
| ALAMAKOTA a kot ma Ale
| This is a test of Enso Documentation Parser. This is a short synopsis.
|
| Here you can write the body of documentation. On top you can see tags
| added to this piece of code. You can customise your text with _Italic_
| ~Strikethrough~ or *Bold*. ~_*Combined*_~ is funny
|
|
| There are 3 kinds of sections
| - Important
| - Info
| - Example
| * You can use example to add multiline code to your documentation
|
| ! Important
| Here is a small test of Important Section
|
| ? Info
| Here is a small test of Info Section
|
| > Example
| Here is a small test of Example Section
| Import Foo
| def Bar a
|type Maybe a
| ## test attached to Just
| type Just val:a
| type Nothing
|""".stripMargin
val inC =
"""
|## Optional values.
| Type `Option` represents an optional value: every `Option` is either `Some`
| and contains a value, or `None`, and does not. Option types are very common
| in Enso code, as they have a number of uses:
| - Initial values.
| - Return values for functions that are not defined over their entire input range (partial functions).
| - Return value for otherwise reporting simple errors, where `None` is returned on error.
| - Optional struct fields.
| - Optional function arguments.
| `Option`s are commonly paired with pattern matching to query the presence of
| a value and take action, always accounting for the None case.
|
|type Option a
| ## The `None` type indicates a presence of a value.
| type Some a
|
| ##
| The `None` type indicates a lack of a value.
| It is a very common type and is used by such types as `Maybe` or `List`.
| Also, `None` is the return value of functions which do not return an
| explicit value.
| type None
|""".stripMargin
println("--- PARSING ---")
val mod = parser.run(new Reader(inp))
val mod = parser.run(new Reader(inC))
println(Debug.pretty(mod.toString))
@ -297,18 +339,22 @@ object Main extends App {
}
println("------")
println(mod.show() == inp)
println(mod.show() == inC)
println("------")
println(mod.show())
println("------")
/** Invoking the Enso Documentation Parser */
println("===== DOCUMENTATION =====")
val isGeneratingHTML = false
val droppedMeta = parser.dropMacroMeta(mod)
val documentation = DocParserRunner.createDocs(droppedMeta)
val documentationHTML =
DocParserRunner.generateHTMLForEveryDocumented(documentation)
val droppedMeta = parser.dropMacroMeta(mod)
val documentation = DocParserRunner.createDocs(droppedMeta)
val htmlPath = "target/"
val cssFileName = "style.css"
DocParserHTMLGenerator.generateHTMLForEveryDocumented(
documentation,
htmlPath,
cssFileName
)
println(Debug.pretty(documentation.toString))
println("------")
println(documentation.show())