mirror of
https://github.com/enso-org/enso.git
synced 2024-09-11 13:15:52 +03:00
Add support for documenting modules (#1900)
This commit is contained in:
parent
a7478bc573
commit
bc96f0e05c
@ -1,5 +1,10 @@
|
||||
# Enso Next
|
||||
|
||||
## Interpreter/Runtime
|
||||
|
||||
- Added support for documenting modules directly
|
||||
([#1900](https://github.com/enso-org/enso/pull/1900)).
|
||||
|
||||
# Enso 0.2.16 (2021-07-23)
|
||||
|
||||
## Interpreter/Runtime
|
||||
|
@ -89,6 +89,10 @@ for more information). By way of example:
|
||||
until I unindent again.
|
||||
```
|
||||
|
||||
Documentation blocks are associated with the _next_ entity in the file, except
|
||||
for if they occur as the _very first_ entity in the file. In this case, they are
|
||||
treated as the module's documentation.
|
||||
|
||||
Documentation comments are _not_ allowed inside textual interpolations.
|
||||
|
||||
The tool that generates this documentation aims to be fairly robust, and tries
|
||||
|
@ -9,8 +9,8 @@ object Suggestions {
|
||||
|
||||
val module: Suggestion.Module = Suggestion.Module(
|
||||
module = "Test.Main",
|
||||
documentation = None,
|
||||
documentationHtml = None
|
||||
documentation = Some("Module doc"),
|
||||
documentationHtml = Some("<html></html>")
|
||||
)
|
||||
|
||||
val atom: Suggestion.Atom = Suggestion.Atom(
|
||||
|
@ -127,7 +127,13 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) {
|
||||
}
|
||||
|
||||
val builder: TreeBuilder = Vector.newBuilder
|
||||
builder += Tree.Node(buildModule(module), Vector())
|
||||
builder += Tree.Node(
|
||||
buildModule(
|
||||
module,
|
||||
ir.getMetadata(DocumentationComments).map(_.documentation)
|
||||
),
|
||||
Vector()
|
||||
)
|
||||
|
||||
Tree.Root(
|
||||
go(builder, Scope(ir.children, ir.location))
|
||||
@ -205,11 +211,14 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) {
|
||||
}
|
||||
|
||||
/** Build an atom suggestion representing a module. */
|
||||
private def buildModule(module: QualifiedName): Suggestion =
|
||||
private def buildModule(
|
||||
module: QualifiedName,
|
||||
doc: Option[String]
|
||||
): Suggestion =
|
||||
Suggestion.Module(
|
||||
module = module.toString,
|
||||
documentation = None,
|
||||
documentationHtml = None,
|
||||
documentation = doc,
|
||||
documentationHtml = doc.map(DocParserWrapper.runOnPureDoc),
|
||||
reexport = None
|
||||
)
|
||||
|
||||
|
@ -9,6 +9,9 @@ import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.desugar.{ComplexType, GenerateMethodBodies}
|
||||
|
||||
/** Associates doc comments with the commented entities as metadata.
|
||||
*
|
||||
* If the first module definition is a documentation comment, it is treated as
|
||||
* the module documentation.
|
||||
*
|
||||
* This pass has no configuration.
|
||||
*
|
||||
@ -159,7 +162,12 @@ case object DocumentationComments extends IRPass {
|
||||
* @return `ir`, with any doc comments associated with nodes as metadata
|
||||
*/
|
||||
private def resolveModule(ir: IR.Module): IR.Module = {
|
||||
val newBindings = resolveList(ir.bindings).map(resolveDefinition)
|
||||
val newBindings = (ir.bindings.headOption match {
|
||||
case Some(doc: IR.Comment.Documentation) =>
|
||||
ir.updateMetadata(this -->> Doc(doc.doc))
|
||||
resolveList(ir.bindings.drop(1))
|
||||
case _ => resolveList(ir.bindings)
|
||||
}).map(resolveDefinition)
|
||||
ir.copy(bindings = newBindings)
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
package org.enso.compiler.test.context
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import org.enso.compiler.Passes
|
||||
import org.enso.compiler.context.{
|
||||
FreshNameSupply,
|
||||
@ -11,10 +9,13 @@ import org.enso.compiler.context.{
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.pass.PassManager
|
||||
import org.enso.compiler.test.CompilerTest
|
||||
import org.enso.docs.generator.DocParserWrapper
|
||||
import org.enso.pkg.QualifiedName
|
||||
import org.enso.polyglot.Suggestion
|
||||
import org.enso.polyglot.data.Tree
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
implicit val passManager: PassManager = new Passes(defaultConfig).passManager
|
||||
@ -29,6 +30,16 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
),
|
||||
Vector()
|
||||
)
|
||||
private val moduleDoc = "Module doc"
|
||||
private val DoccedModuleNode = Tree.Node(
|
||||
Suggestion.Module(
|
||||
module = Module.toString,
|
||||
documentation = Some(" " + moduleDoc),
|
||||
documentationHtml = Some(DocParserWrapper.runOnPureDoc(moduleDoc)),
|
||||
reexport = None
|
||||
),
|
||||
Vector()
|
||||
)
|
||||
|
||||
private def htmlDoc(inner: String): String = {
|
||||
"<html><body><div class=\"doc\" style=\"font-size: 13px;\"><div><div class=\"\">" + inner + "</div></div></div></body></html>"
|
||||
@ -68,13 +79,15 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
implicit val moduleContext: ModuleContext = freshModuleContext
|
||||
|
||||
val code =
|
||||
"""## The foo
|
||||
"""## Module doc
|
||||
|
|
||||
|## The foo
|
||||
|foo = 42""".stripMargin
|
||||
val module = code.preprocessModule
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleNode,
|
||||
DoccedModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -98,14 +111,16 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
implicit val moduleContext: ModuleContext = freshModuleContext
|
||||
|
||||
val code =
|
||||
"""## The foo
|
||||
"""## Module doc
|
||||
|
|
||||
|## The foo
|
||||
|foo : Number
|
||||
|foo = 42""".stripMargin
|
||||
val module = code.preprocessModule
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleNode,
|
||||
DoccedModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
@ -1084,13 +1099,15 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
implicit val moduleContext: ModuleContext = freshModuleContext
|
||||
|
||||
val code =
|
||||
"""## My sweet type
|
||||
"""## Module doc
|
||||
|
|
||||
|## My sweet type
|
||||
|type MyType a b""".stripMargin
|
||||
val module = code.preprocessModule
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleNode,
|
||||
DoccedModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -1207,7 +1224,9 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
implicit val moduleContext: ModuleContext = freshModuleContext
|
||||
|
||||
val code =
|
||||
"""## When in doubt
|
||||
"""## Module doc
|
||||
|
|
||||
|## When in doubt
|
||||
|type Maybe
|
||||
| ## Nothing here
|
||||
| type Nothing
|
||||
@ -1217,7 +1236,7 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
ModuleNode,
|
||||
DoccedModuleNode,
|
||||
Tree.Node(
|
||||
Suggestion.Atom(
|
||||
externalId = None,
|
||||
@ -1808,6 +1827,47 @@ class SuggestionBuilderTest extends CompilerTest {
|
||||
)
|
||||
}
|
||||
|
||||
"build module with documentation" in {
|
||||
implicit val moduleContext: ModuleContext = freshModuleContext
|
||||
|
||||
val code =
|
||||
"""## Module doc
|
||||
|
|
||||
|## The foo
|
||||
|foo = 42""".stripMargin
|
||||
val module = code.preprocessModule
|
||||
|
||||
build(code, module) shouldEqual Tree.Root(
|
||||
Vector(
|
||||
Tree.Node(
|
||||
Suggestion.Module(
|
||||
"Unnamed.Test",
|
||||
Some(" Module doc"),
|
||||
Some(
|
||||
"<html><body><div class=\"doc\" style=\"font-size: 13px;\"><div><div class=\"\"><p>Module doc</p></div></div></div></body></html>"
|
||||
),
|
||||
None
|
||||
),
|
||||
Vector()
|
||||
),
|
||||
Tree.Node(
|
||||
Suggestion.Method(
|
||||
externalId = None,
|
||||
module = "Unnamed.Test",
|
||||
name = "foo",
|
||||
arguments = Seq(
|
||||
Suggestion.Argument("this", "Unnamed.Test", false, false, None)
|
||||
),
|
||||
selfType = "Unnamed.Test",
|
||||
returnType = SuggestionBuilder.Any,
|
||||
documentation = Some(" The foo"),
|
||||
documentationHtml = Some(htmlDoc("<p>The foo</p>"))
|
||||
),
|
||||
Vector()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def build(source: String, ir: IR.Module): Tree.Root[Suggestion] =
|
||||
|
@ -175,7 +175,8 @@ class FunctionBindingTest extends CompilerTest {
|
||||
|
||||
"retain documentation comments and annotations associated with them" in {
|
||||
val ir =
|
||||
s"""
|
||||
s"""## Module doc
|
||||
|
|
||||
|## My documentation for this conversion.
|
||||
|@My_Annotation
|
||||
|My_Type.$from (that : Value) = that
|
||||
|
@ -80,18 +80,20 @@ class DocumentationCommentsTest extends CompilerTest with Inside {
|
||||
// === The Tests ============================================================
|
||||
|
||||
"Documentation comments in the top scope" should {
|
||||
"be associated with atoms and methods" in {
|
||||
implicit val moduleContext: ModuleContext = mkModuleContext
|
||||
val ir =
|
||||
"""
|
||||
|## This is doc for My_Atom
|
||||
|type My_Atom a b c
|
||||
|
|
||||
|## This is doc for my_method
|
||||
|MyAtom.my_method x = x + this
|
||||
|
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
implicit val moduleContext: ModuleContext = mkModuleContext
|
||||
val ir =
|
||||
"""
|
||||
|## My module documentation
|
||||
|
|
||||
|## This is doc for My_Atom
|
||||
|type My_Atom a b c
|
||||
|
|
||||
|## This is doc for my_method
|
||||
|MyAtom.my_method x = x + this
|
||||
|
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
"be associated with atoms and methods" in {
|
||||
ir.bindings.length shouldEqual 2
|
||||
ir.bindings(0) shouldBe an[IR.Module.Scope.Definition.Atom]
|
||||
ir.bindings(1) shouldBe an[IR.Module.Scope.Definition.Method]
|
||||
@ -99,6 +101,27 @@ class DocumentationCommentsTest extends CompilerTest with Inside {
|
||||
getDoc(ir.bindings(0)) shouldEqual " This is doc for My_Atom"
|
||||
getDoc(ir.bindings(1)) shouldEqual " This is doc for my_method"
|
||||
}
|
||||
|
||||
"be associated with modules" in {
|
||||
getDoc(ir) shouldEqual " My module documentation"
|
||||
}
|
||||
|
||||
"not be associated with modules when not the first entity" in {
|
||||
implicit val moduleContext: ModuleContext = mkModuleContext
|
||||
val ir =
|
||||
"""from Standard.Base import al
|
||||
|
|
||||
|## My module documentation
|
||||
|
|
||||
|## This is doc for My_Atom
|
||||
|type My_Atom a b c
|
||||
|
|
||||
|## This is doc for my_method
|
||||
|MyAtom.my_method x = x + this
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.getMetadata(DocumentationComments) should not be defined
|
||||
}
|
||||
}
|
||||
|
||||
"Documentation comments in blocks" should {
|
||||
@ -174,7 +197,8 @@ class DocumentationCommentsTest extends CompilerTest with Inside {
|
||||
implicit val moduleContext: ModuleContext = mkModuleContext
|
||||
"assign docs to all entities" in {
|
||||
val ir =
|
||||
"""
|
||||
"""## My Module documentation
|
||||
|
|
||||
|## the type Foo
|
||||
|type Foo
|
||||
| ## the constructor Bar
|
||||
@ -212,7 +236,9 @@ class DocumentationCommentsTest extends CompilerTest with Inside {
|
||||
buildModuleContext(freshNameSupply = Some(new FreshNameSupply))
|
||||
|
||||
val module =
|
||||
"""## The foo
|
||||
"""## Module docs
|
||||
|
|
||||
|## The foo
|
||||
|foo : Integer
|
||||
|foo = 42""".stripMargin.preprocessModule
|
||||
val foo = module.bindings.head
|
||||
@ -227,7 +253,8 @@ class DocumentationCommentsTest extends CompilerTest with Inside {
|
||||
buildModuleContext(freshNameSupply = Some(new FreshNameSupply))
|
||||
|
||||
val ir =
|
||||
"""
|
||||
"""## Module Docs
|
||||
|
|
||||
|## the type Foo
|
||||
|type Foo
|
||||
| ## the constructor Bar
|
||||
|
@ -103,7 +103,8 @@ class GenerateDocumentationTest extends CompilerTest with Inside {
|
||||
"be associated with atoms and methods" in {
|
||||
implicit val moduleContext: ModuleContext = mkModuleContext
|
||||
val ir =
|
||||
"""
|
||||
"""## Module Docs
|
||||
|
|
||||
|## This is doc for My_Atom
|
||||
|type My_Atom a b c
|
||||
|
|
||||
|
@ -124,7 +124,9 @@ class TypeSignaturesTest extends CompilerTest {
|
||||
|
||||
"reattach documentation to method definitions" in {
|
||||
val ir =
|
||||
"""## My bar
|
||||
"""## Module doc
|
||||
|
|
||||
|## My bar
|
||||
|bar : Number -> Number -> Number
|
||||
|bar a b = a + b
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
Loading…
Reference in New Issue
Block a user