From 547db918e5d82dc658dcc5d4ce5773e11b00a31a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Miko=C5=82ajek?= Date: Wed, 31 Mar 2021 21:01:52 +0200 Subject: [PATCH] Doc Parser Hotfix: Correctly displays multiple lines in lists, and it won't panic on unknown character anymore. (#1636) --- .../enso/syntax/text/spec/DocParserDef.scala | 13 ++++ .../org/enso/syntax/text/DocParser.scala | 70 ++++++++++++++----- .../scala/org/enso/syntax/text/Parser.scala | 3 +- .../org/enso/syntax/text/DocParserTests.scala | 62 ++++++++++++++-- 4 files changed, 125 insertions(+), 23 deletions(-) diff --git a/lib/scala/syntax/definition/src/main/scala/org/enso/syntax/text/spec/DocParserDef.scala b/lib/scala/syntax/definition/src/main/scala/org/enso/syntax/text/spec/DocParserDef.scala index f27b766a5e..d47d7c7b48 100644 --- a/lib/scala/syntax/definition/src/main/scala/org/enso/syntax/text/spec/DocParserDef.scala +++ b/lib/scala/syntax/definition/src/main/scala/org/enso/syntax/text/spec/DocParserDef.scala @@ -205,6 +205,18 @@ 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() } @@ -878,4 +890,5 @@ case class DocParserDef() extends Parser[Doc] { } ROOT || eof || documentation.onEOF() + ROOT || any || text.push(currentMatch) } diff --git a/lib/scala/syntax/specialization/shared/src/main/scala/org/enso/syntax/text/DocParser.scala b/lib/scala/syntax/specialization/shared/src/main/scala/org/enso/syntax/text/DocParser.scala index 4e37158271..50a0fd7071 100644 --- a/lib/scala/syntax/specialization/shared/src/main/scala/org/enso/syntax/text/DocParser.scala +++ b/lib/scala/syntax/specialization/shared/src/main/scala/org/enso/syntax/text/DocParser.scala @@ -148,7 +148,12 @@ object DocParserRunner { case line2 :: rest => line2 match { case Line(Some(AST.App.Infix.any(ast)), _) => - commentWithInfixForDocumented(com, off, ast, rest) + commentWithInfixForDocumented( + com, + off, + ast, + rest + ) case Line(Some(AST.Def.any(ast)), _) => commentWithDefForDocumented(com, off, ast, rest) case Line(None, _) => @@ -163,9 +168,21 @@ object DocParserRunner { val rHead = restTrav.head rHead match { case Line(Some(AST.App.Infix.any(ast)), _) => - commentWithInfixForDocumented(com, off, ast, rTail, emp) + commentWithInfixForDocumented( + com, + off, + ast, + rTail, + emp + ) case Line(Some(AST.Def.any(ast)), _) => - commentWithDefForDocumented(com, off, ast, rTail, emp) + commentWithDefForDocumented( + com, + off, + ast, + rTail, + emp + ) case _ => line1 :: line2 :: attachDocToSubsequentAST(rest) } @@ -181,8 +198,8 @@ object DocParserRunner { /** Creates Docs from comments found in parsed data * - * @param comment - comment found in AST - * @return - Documentation + * @param comment - Comment found in AST. + * @return - Documentation. */ def createDocFromComment(comment: AST.Comment): Doc = { val in = comment.lines.mkString("\n") @@ -207,7 +224,8 @@ object DocParserRunner { emptyLines: Int = 0 ): List[AST.Block.OptLine] = { val docFromAst = createDocs(ast) - val docLine = createDocumentedLine(com, emptyLines, docFromAst, off) + val docLine = + createDocumentedLine(com, emptyLines, docFromAst, off) docLine :: attachDocToSubsequentAST(rest) } @@ -270,19 +288,25 @@ object DocParserHTMLGenerator { * @param ast - parsed AST.Module and reformatted using Doc Parser */ def generateHTMLForEveryDocumented(ast: AST): String = { + var allDocs = new String ast.map { elem => elem match { case AST.Documented.any(documented) => val file = onHTMLRendering(documented) - return file.code.toString() + generateHTMLForEveryDocumented( + allDocs += file.code.toString() + generateHTMLForEveryDocumented( documented - ) + ) + HTML.hr + HTML.br + case AST.Def.any(tp) => + tp.body match { + case Some(body) => allDocs += generateHTMLForEveryDocumented(body) + case None => () + } case _ => - generateHTMLForEveryDocumented(elem) + allDocs += generateHTMLForEveryDocumented(elem) } elem } - new String + allDocs } /** Function to generate HTML File from pure doc comment w/o connection to AST @@ -339,7 +363,11 @@ object DocParserHTMLGenerator { * @return - HTML Code from Doc and contents of [[AST.Def]] or * [[AST.App.Infix]] */ - def DocumentedToHtml(ast: AST, doc: Doc): TypedTag[String] = { + def DocumentedToHtml( + ast: AST, + doc: Doc, + isInOtherDoc: Boolean = false + ): TypedTag[String] = { val docClass = HTML.`class` := "Documentation" val astHeadCls = HTML.`class` := "ASTHead" val astHTML = createHTMLFromAST(ast) @@ -365,11 +393,19 @@ object DocParserHTMLGenerator { if (doc.tags.html.mkString.contains("DEPRECATED")) { content = HTML.div(strikeoutStyle)(doc.htmlWoTags) } - HTML.div(docClass)( - astName, - content, - doc.tags.html - ) + if (isInOtherDoc) { + HTML.div(docClass)( + astName, + content, + doc.tags.html + ) + } else { + HTML.div(docClass)( + doc.tags.html, + astName, + content + ) + } } } @@ -507,7 +543,7 @@ object DocParserHTMLGenerator { lines match { case Line(Some(AST.Documented.any(doc)), _) :: rest => val cls = HTML.`class` := "DefDoc" - val docHtml = DocumentedToHtml(doc.ast, doc.doc) + val docHtml = DocumentedToHtml(doc.ast, doc.doc, true) HTML.div(cls)(docHtml) :: renderHTMLOnLine(rest) case x :: rest => x match { diff --git a/lib/scala/syntax/specialization/shared/src/main/scala/org/enso/syntax/text/Parser.scala b/lib/scala/syntax/specialization/shared/src/main/scala/org/enso/syntax/text/Parser.scala index 4f67a5b7f2..5ec126a8db 100644 --- a/lib/scala/syntax/specialization/shared/src/main/scala/org/enso/syntax/text/Parser.scala +++ b/lib/scala/syntax/specialization/shared/src/main/scala/org/enso/syntax/text/Parser.scala @@ -520,7 +520,8 @@ object Main extends scala.App { | 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 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. diff --git a/lib/scala/syntax/specialization/shared/src/test/scala/org/enso/syntax/text/DocParserTests.scala b/lib/scala/syntax/specialization/shared/src/test/scala/org/enso/syntax/text/DocParserTests.scala index cf035ba76a..cbd31bf87f 100644 --- a/lib/scala/syntax/specialization/shared/src/test/scala/org/enso/syntax/text/DocParserTests.scala +++ b/lib/scala/syntax/specialization/shared/src/test/scala/org/enso/syntax/text/DocParserTests.scala @@ -40,8 +40,10 @@ class DocParserTests extends AnyFlatSpec with Matchers { //// Formatters ////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// - "*Foo*" ?= Doc(Synopsis(Section.Raw(Formatter(Formatter.Bold, "Foo")))) - "_Foo_" ?= Doc(Synopsis(Section.Raw(Formatter(Formatter.Italic, "Foo")))) + "Foo" ?= Doc(Synopsis(Section.Raw("Foo"))) + "Foo\uD83D\uDC98" ?= Doc(Synopsis(Section.Raw("Foo", "\uD83D\uDC98"))) + "*Foo*" ?= Doc(Synopsis(Section.Raw(Formatter(Formatter.Bold, "Foo")))) + "_Foo_" ?= Doc(Synopsis(Section.Raw(Formatter(Formatter.Italic, "Foo")))) "~Foo~" ?= Doc( Synopsis(Section.Raw(Formatter(Formatter.Strikeout, "Foo"))) ) @@ -752,14 +754,64 @@ class DocParserTests extends AnyFlatSpec with Matchers { """ | - bar - | baz + | baz |""".stripMargin.replaceAll(System.lineSeparator(), "\n") ?= Doc( Synopsis( Section.Raw( Newline, - List(1, List.Unordered, " bar"), + List(1, List.Unordered, " bar\n baz"), + Newline + ) + ) + ) + """ + | - bar + | baz + | - bar + | baz + |""".stripMargin.replaceAll(System.lineSeparator(), "\n") ?= Doc( + Synopsis( + Section.Raw( Newline, - CodeBlock(CodeBlock.Line(1, "baz")), + List(1, List.Unordered, " bar\n baz", " bar\n baz"), + 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, " bar\n baz"), + Newline, + "Another raw text.", + Newline + ) + ) + ) + + """ > Example + | This does foo: + | - bar + | baz + |""".stripMargin.replaceAll(System.lineSeparator(), "\n") ?= Doc( + Synopsis( + Section.Marked( + 1, + 1, + Section.Marked.Example, + Section.Header("Example"), + Newline, + "This does foo:", + Newline, + List(4, List.Unordered, " bar\n baz"), Newline ) )