Title in docs in IDE (#1904)

This commit is contained in:
Maciej Mikołajek 2021-08-17 01:40:58 +02:00 committed by GitHub
parent 98eab2873e
commit 6652a00241
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 144 additions and 132 deletions

View File

@ -17,6 +17,8 @@
propagate error information propagate error information
([#1941](https://github.com/enso-org/enso/pull/1941)). ([#1941](https://github.com/enso-org/enso/pull/1941)).
- Fixed inaproppriate parsing of code blocks in documentation. - Fixed inaproppriate parsing of code blocks in documentation.
- Documentation in IDE now shows names of suggestions
([#1904](https://github.com/enso-org/enso/pull/1904)).
## Tooling ## Tooling

View File

@ -162,7 +162,7 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) {
selfType = selfType.toString, selfType = selfType.toString,
returnType = buildReturnType(returnTypeDef), returnType = buildReturnType(returnTypeDef),
documentation = doc, documentation = doc,
documentationHtml = doc.map(DocParserWrapper.runOnPureDoc), documentationHtml = doc.map(DocParserWrapper.runOnPureDoc(_, name.name)),
reexport = None reexport = None
) )
} }
@ -216,10 +216,11 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) {
doc: Option[String] doc: Option[String]
): Suggestion = ): Suggestion =
Suggestion.Module( Suggestion.Module(
module = module.toString, module = module.toString,
documentation = doc, documentation = doc,
documentationHtml = doc.map(DocParserWrapper.runOnPureDoc), documentationHtml =
reexport = None doc.map(DocParserWrapper.runOnPureDoc(_, module.toString)),
reexport = None
) )
/** Build suggestions for an atom definition. */ /** Build suggestions for an atom definition. */
@ -247,7 +248,7 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) {
arguments = arguments.map(buildArgument), arguments = arguments.map(buildArgument),
returnType = module.createChild(name).toString, returnType = module.createChild(name).toString,
documentation = doc, documentation = doc,
documentationHtml = doc.map(DocParserWrapper.runOnPureDoc), documentationHtml = doc.map(DocParserWrapper.runOnPureDoc(_, name)),
reexport = None reexport = None
) )

View File

@ -33,18 +33,15 @@ class SuggestionBuilderTest extends CompilerTest {
private val moduleDoc = "Module doc" private val moduleDoc = "Module doc"
private val DoccedModuleNode = Tree.Node( private val DoccedModuleNode = Tree.Node(
Suggestion.Module( Suggestion.Module(
module = Module.toString, module = Module.toString,
documentation = Some(" " + moduleDoc), documentation = Some(" " + moduleDoc),
documentationHtml = Some(DocParserWrapper.runOnPureDoc(moduleDoc)), documentationHtml =
reexport = None Some(DocParserWrapper.runOnPureDoc(moduleDoc, Module.toString)),
reexport = None
), ),
Vector() 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>"
}
"SuggestionBuilder" should { "SuggestionBuilder" should {
"build method without explicit arguments" in { "build method without explicit arguments" in {
@ -96,10 +93,11 @@ class SuggestionBuilderTest extends CompilerTest {
arguments = Seq( arguments = Seq(
Suggestion.Argument("this", "Unnamed.Test", false, false, None) Suggestion.Argument("this", "Unnamed.Test", false, false, None)
), ),
selfType = "Unnamed.Test", selfType = "Unnamed.Test",
returnType = SuggestionBuilder.Any, returnType = SuggestionBuilder.Any,
documentation = Some(" The foo"), documentation = Some(" The foo"),
documentationHtml = Some(htmlDoc("<p>The foo</p>")) documentationHtml =
Some(DocParserWrapper.runOnPureDoc(" The foo", "foo"))
), ),
Vector() Vector()
) )
@ -129,10 +127,11 @@ class SuggestionBuilderTest extends CompilerTest {
arguments = Seq( arguments = Seq(
Suggestion.Argument("this", "Unnamed.Test", false, false, None) Suggestion.Argument("this", "Unnamed.Test", false, false, None)
), ),
selfType = "Unnamed.Test", selfType = "Unnamed.Test",
returnType = "Number", returnType = "Number",
documentation = Some(" The foo"), documentation = Some(" The foo"),
documentationHtml = Some(htmlDoc("<p>The foo</p>")) documentationHtml =
Some(DocParserWrapper.runOnPureDoc(" The foo", "foo"))
), ),
Vector() Vector()
) )
@ -460,10 +459,11 @@ class SuggestionBuilderTest extends CompilerTest {
Suggestion.Argument("a", "Number", false, false, None), Suggestion.Argument("a", "Number", false, false, None),
Suggestion.Argument("b", "Number", false, false, None) Suggestion.Argument("b", "Number", false, false, None)
), ),
selfType = "Unnamed.Test.MyAtom", selfType = "Unnamed.Test.MyAtom",
returnType = "Number", returnType = "Number",
documentation = Some(" My bar"), documentation = Some(" My bar"),
documentationHtml = Some(htmlDoc("<p>My bar</p>")) documentationHtml =
Some(DocParserWrapper.runOnPureDoc(" My bar", "bar"))
), ),
Vector() Vector()
) )
@ -1172,9 +1172,10 @@ class SuggestionBuilderTest extends CompilerTest {
Suggestion Suggestion
.Argument("b", SuggestionBuilder.Any, false, false, None) .Argument("b", SuggestionBuilder.Any, false, false, None)
), ),
returnType = "Unnamed.Test.MyType", returnType = "Unnamed.Test.MyType",
documentation = Some(" My sweet type"), documentation = Some(" My sweet type"),
documentationHtml = Some(htmlDoc("<p>My sweet type</p>")) documentationHtml =
Some(DocParserWrapper.runOnPureDoc(" My sweet type", "MyType"))
), ),
Vector() Vector()
), ),
@ -1292,13 +1293,15 @@ class SuggestionBuilderTest extends CompilerTest {
DoccedModuleNode, DoccedModuleNode,
Tree.Node( Tree.Node(
Suggestion.Atom( Suggestion.Atom(
externalId = None, externalId = None,
module = "Unnamed.Test", module = "Unnamed.Test",
name = "Nothing", name = "Nothing",
arguments = Seq(), arguments = Seq(),
returnType = "Unnamed.Test.Nothing", returnType = "Unnamed.Test.Nothing",
documentation = Some(" Nothing here"), documentation = Some(" Nothing here"),
documentationHtml = Some(htmlDoc("<p>Nothing here</p>")) documentationHtml = Some(
DocParserWrapper.runOnPureDoc(" Nothing here", "Nothing")
)
), ),
Vector() Vector()
), ),
@ -1311,9 +1314,11 @@ class SuggestionBuilderTest extends CompilerTest {
Suggestion Suggestion
.Argument("a", SuggestionBuilder.Any, false, false, None) .Argument("a", SuggestionBuilder.Any, false, false, None)
), ),
returnType = "Unnamed.Test.Just", returnType = "Unnamed.Test.Just",
documentation = Some(" Something there"), documentation = Some(" Something there"),
documentationHtml = Some(htmlDoc("<p>Something there</p>")) documentationHtml = Some(
DocParserWrapper.runOnPureDoc(" Something there", "Just")
)
), ),
Vector() Vector()
), ),
@ -1357,25 +1362,27 @@ class SuggestionBuilderTest extends CompilerTest {
ModuleNode, ModuleNode,
Tree.Node( Tree.Node(
Suggestion.Atom( Suggestion.Atom(
externalId = None, externalId = None,
module = "Unnamed.Test", module = "Unnamed.Test",
name = "Cons", name = "Cons",
arguments = Seq(), arguments = Seq(),
returnType = "Unnamed.Test.Cons", returnType = "Unnamed.Test.Cons",
documentation = Some(" And more"), documentation = Some(" And more"),
documentationHtml = Some(htmlDoc("<p>And more</p>")) documentationHtml =
Some(DocParserWrapper.runOnPureDoc(" And more", "Cons"))
), ),
Vector() Vector()
), ),
Tree.Node( Tree.Node(
Suggestion.Atom( Suggestion.Atom(
externalId = None, externalId = None,
module = "Unnamed.Test", module = "Unnamed.Test",
name = "Nil", name = "Nil",
arguments = Seq(), arguments = Seq(),
returnType = "Unnamed.Test.Nil", returnType = "Unnamed.Test.Nil",
documentation = Some(" End"), documentation = Some(" End"),
documentationHtml = Some(htmlDoc("<p>End</p>")) documentationHtml =
Some(DocParserWrapper.runOnPureDoc(" End", "Nil"))
), ),
Vector() Vector()
), ),
@ -1388,10 +1395,11 @@ class SuggestionBuilderTest extends CompilerTest {
Suggestion Suggestion
.Argument("this", "Unnamed.Test.Cons", false, false, None) .Argument("this", "Unnamed.Test.Cons", false, false, None)
), ),
selfType = "Unnamed.Test.Cons", selfType = "Unnamed.Test.Cons",
returnType = "List", returnType = "List",
documentation = Some(" a method"), documentation = Some(" a method"),
documentationHtml = Some(htmlDoc("<p>a method</p>")) documentationHtml =
Some(DocParserWrapper.runOnPureDoc(" a method", "empty"))
), ),
Vector() Vector()
), ),
@ -1404,10 +1412,11 @@ class SuggestionBuilderTest extends CompilerTest {
Suggestion Suggestion
.Argument("this", "Unnamed.Test.Nil", false, false, None) .Argument("this", "Unnamed.Test.Nil", false, false, None)
), ),
selfType = "Unnamed.Test.Nil", selfType = "Unnamed.Test.Nil",
returnType = "List", returnType = "List",
documentation = Some(" a method"), documentation = Some(" a method"),
documentationHtml = Some(htmlDoc("<p>a method</p>")) documentationHtml =
Some(DocParserWrapper.runOnPureDoc(" a method", "empty"))
), ),
Vector() Vector()
) )
@ -1897,7 +1906,7 @@ class SuggestionBuilderTest extends CompilerTest {
"Unnamed.Test", "Unnamed.Test",
Some(" Module doc"), Some(" Module doc"),
Some( Some(
"<html><body><div class=\"doc\" style=\"font-size: 13px;\"><div><div class=\"\"><p>Module doc</p></div></div></div></body></html>" DocParserWrapper.runOnPureDoc(" Module doc", "Unnamed.Test")
), ),
None None
), ),
@ -1911,10 +1920,11 @@ class SuggestionBuilderTest extends CompilerTest {
arguments = Seq( arguments = Seq(
Suggestion.Argument("this", "Unnamed.Test", false, false, None) Suggestion.Argument("this", "Unnamed.Test", false, false, None)
), ),
selfType = "Unnamed.Test", selfType = "Unnamed.Test",
returnType = SuggestionBuilder.Any, returnType = SuggestionBuilder.Any,
documentation = Some(" The foo"), documentation = Some(" The foo"),
documentationHtml = Some(htmlDoc("<p>The foo</p>")) documentationHtml =
Some(DocParserWrapper.runOnPureDoc(" The foo", "foo"))
), ),
Vector() Vector()
) )

View File

@ -6,6 +6,7 @@ import org.enso.compiler.core.IR
import org.enso.compiler.pass.resolve.GenerateDocumentation import org.enso.compiler.pass.resolve.GenerateDocumentation
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager} import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
import org.enso.compiler.test.CompilerTest import org.enso.compiler.test.CompilerTest
import org.enso.docs.generator.DocParserWrapper
import org.scalatest.Inside import org.scalatest.Inside
import pprint.pprintln import pprint.pprintln
@ -79,24 +80,6 @@ class GenerateDocumentationTest extends CompilerTest with Inside {
meta.get.documentation meta.get.documentation
} }
/** Creates full html doc generator output from the `inner` string, the one
* that will only change during the test.
*/
def unfoldedDocumentationForAssertion(inner: String): String =
s"""<html>
| <body>
| <div class="doc" style="font-size: 13px;">
| <div>
| <div class="">
| $inner
| </div>
| </div>
| </div>
| </body>
|</html>""".stripMargin
.replaceAll(System.lineSeparator(), "")
.replaceAll(">[ ]+<", "><")
// === The Tests ============================================================ // === The Tests ============================================================
"Documentation comments in the top scope" should { "Documentation comments in the top scope" should {
@ -117,11 +100,11 @@ class GenerateDocumentationTest extends CompilerTest with Inside {
ir.bindings(0) shouldBe an[IR.Module.Scope.Definition.Atom] ir.bindings(0) shouldBe an[IR.Module.Scope.Definition.Atom]
ir.bindings(1) shouldBe an[IR.Module.Scope.Definition.Method] ir.bindings(1) shouldBe an[IR.Module.Scope.Definition.Method]
getDoc(ir.bindings(0)) shouldEqual unfoldedDocumentationForAssertion( getDoc(ir.bindings(0)) shouldEqual DocParserWrapper.runOnPureDoc(
"<p>This is doc for My<div class=\"Unclosed\"><i>Atom</i></div></p>" " This is doc for My_Atom"
) )
getDoc(ir.bindings(1)) shouldEqual unfoldedDocumentationForAssertion( getDoc(ir.bindings(1)) shouldEqual DocParserWrapper.runOnPureDoc(
"<p>This is doc for my<div class=\"Unclosed\"><i>method</i></div></p>" " This is doc for my_method"
) )
} }
} }
@ -148,11 +131,11 @@ class GenerateDocumentationTest extends CompilerTest with Inside {
pprintln(body) pprintln(body)
body.expressions.length shouldEqual 1 body.expressions.length shouldEqual 1
getDoc(body.expressions(0)) shouldEqual unfoldedDocumentationForAssertion( getDoc(body.expressions(0)) shouldEqual DocParserWrapper.runOnPureDoc(
"<p>Do thing</p>" " Do thing"
) )
getDoc(body.returnValue) shouldEqual unfoldedDocumentationForAssertion( getDoc(body.returnValue) shouldEqual DocParserWrapper.runOnPureDoc(
"<p>Do another thing</p>" " Do another thing"
) )
} }
@ -178,11 +161,11 @@ class GenerateDocumentationTest extends CompilerTest with Inside {
body.expressions.length shouldEqual 2 body.expressions.length shouldEqual 2
body.expressions(0) shouldBe an[IR.Application.Operator.Binary] body.expressions(0) shouldBe an[IR.Application.Operator.Binary]
getDoc(body.expressions(0)) shouldEqual unfoldedDocumentationForAssertion( getDoc(body.expressions(0)) shouldEqual DocParserWrapper.runOnPureDoc(
"<p>Id</p>" " Id"
) )
getDoc(body.returnValue) shouldEqual unfoldedDocumentationForAssertion( getDoc(body.returnValue) shouldEqual DocParserWrapper.runOnPureDoc(
"<p>Return thing</p>" " Return thing"
) )
} }
} }
@ -208,31 +191,30 @@ class GenerateDocumentationTest extends CompilerTest with Inside {
| ## the return | ## the return
| 0 | 0
|""".stripMargin.preprocessModule.resolve |""".stripMargin.preprocessModule.resolve
// pprintln(ir)
val tp = ir.bindings(0).asInstanceOf[IR.Module.Scope.Definition.Type] val tp = ir.bindings(0).asInstanceOf[IR.Module.Scope.Definition.Type]
getDoc(tp) shouldEqual unfoldedDocumentationForAssertion( getDoc(tp) shouldEqual DocParserWrapper.runOnPureDoc(
"<p>the type Foo</p>" " the type Foo"
) )
val t1 = tp.body(0) val t1 = tp.body(0)
getDoc(t1) shouldEqual unfoldedDocumentationForAssertion( getDoc(t1) shouldEqual DocParserWrapper.runOnPureDoc(
"<p>the constructor Bar</p>" " the constructor Bar"
) )
val t2 = tp.body(1) val t2 = tp.body(1)
getDoc(t2) shouldEqual unfoldedDocumentationForAssertion( getDoc(t2) shouldEqual DocParserWrapper.runOnPureDoc(
"<p>the included Unit</p>" " the included Unit"
) )
val method = tp.body(2).asInstanceOf[IR.Function.Binding] val method = tp.body(2).asInstanceOf[IR.Function.Binding]
getDoc(method) shouldEqual unfoldedDocumentationForAssertion( getDoc(method) shouldEqual DocParserWrapper.runOnPureDoc(
"<p>a method</p>" " a method"
) )
val block = method.body.asInstanceOf[IR.Expression.Block] val block = method.body.asInstanceOf[IR.Expression.Block]
getDoc( getDoc(
block.expressions(0) block.expressions(0)
) shouldEqual unfoldedDocumentationForAssertion( ) shouldEqual DocParserWrapper.runOnPureDoc(
"<p>a statement</p>" " a statement"
) )
getDoc(block.returnValue) shouldEqual unfoldedDocumentationForAssertion( getDoc(block.returnValue) shouldEqual DocParserWrapper.runOnPureDoc(
"<p>the return</p>" " the return"
) )
} }
} }

View File

@ -21,9 +21,9 @@ object DocParserWrapper {
/** Generates HTML from Documentation string. /** Generates HTML from Documentation string.
*/ */
def runOnPureDoc(comment: String): String = { def runOnPureDoc(comment: String, title: String = ""): String = {
val doc = DocParser.runMatched(comment) val doc = DocParser.runMatched(comment)
val html = DocParserHTMLGenerator.generateHTMLPureDoc(doc) val html = DocParserHTMLGenerator.generateHTMLPureDoc(doc, title)
html html
} }

View File

@ -19,7 +19,7 @@ object DocsGenerator {
/** Generates list of HTML docs from given doc comments. /** Generates list of HTML docs from given doc comments.
*/ */
def generate(comments: List[String]): List[String] = { def generate(comments: List[String]): List[String] = {
comments.map(runOnPureDoc) comments.map(c => runOnPureDoc(c))
} }
/** Connects HTML documentation with it's AST element. /** Connects HTML documentation with it's AST element.

View File

@ -39,6 +39,19 @@ final case class Doc(
val html: Doc.HTML = Seq( val html: Doc.HTML = Seq(
HTML.div(tags.html)(synopsis.html)(body.html) HTML.div(tags.html)(synopsis.html)(body.html)
) )
def htmlWithTitle(title: String): Doc.HTML = {
if (title != "") {
Seq(
HTML.div(HTML.`class` := "doc-title-container")(
HTML.div(HTML.`class` := "doc-title-name")(title),
tags.html
)(synopsis.html)(body.html)
)
} else {
Seq(HTML.div(tags.html)(synopsis.html)(body.html))
}
}
} }
object Doc { object Doc {
@ -182,7 +195,7 @@ object Doc {
val htmlIdCode = HTML.`id` := uniqueIDCode val htmlIdCode = HTML.`id` := uniqueIDCode
val htmlIdBtn = HTML.`id` := uniqueIDBtn val htmlIdBtn = HTML.`id` := uniqueIDBtn
val firstIndent = elems.head.indent val firstIndent = elems.head.indent
val elemsHTML = elems.toList.map(elem => elem.htmlOffset(firstIndent)) val elemsHTML = elems.toList.map(_.htmlOffset(firstIndent))
val copyClass = HTML.`class` := "doc-copy-btn flex" val copyClass = HTML.`class` := "doc-copy-btn flex"
val codeClass = HTML.`class` := "doc-code-container" val codeClass = HTML.`class` := "doc-code-container"
val copyBtn = HTML.button(htmlIdBtn)(copyClass)("Copy") val copyBtn = HTML.button(htmlIdBtn)(copyClass)("Copy")
@ -441,7 +454,7 @@ object Doc {
val repr: Repr.Builder = R + indent + elemsRepr val repr: Repr.Builder = R + indent + elemsRepr
override def htmlCls(): generic.AttrPair[Builder, String] = { override def htmlCls(): generic.AttrPair[Builder, String] = {
HTML.`class` := "" HTML.`class` := "raw"
} }
override val html: HTML = Seq(HTML.p(elems.map(_.html))) override val html: HTML = Seq(HTML.p(elems.map(_.html)))
@ -471,7 +484,7 @@ object Doc {
val repr: Repr.Builder = R + elems.head + elems.tail.map(R + newLn + _) val repr: Repr.Builder = R + elems.head + elems.tail.map(R + newLn + _)
val html: HTML = { val html: HTML = {
Seq( Seq(
HTML.div(HTML.`class` := "")( HTML.div(HTML.`class` := "synopsis")(
elems.toList.map(_.html) elems.toList.map(_.html)
) )
) )
@ -504,7 +517,7 @@ object Doc {
R + newLn + _ R + newLn + _
) )
val html: HTML = Seq( val html: HTML = Seq(
HTML.div(elems.toList.map(_.html)) HTML.div(HTML.`class` := "body")(elems.toList.map(_.html))
) )
} }
@ -551,16 +564,17 @@ object Doc {
case _ => R + indent + name + details case _ => R + indent + name + details
} }
val html: HTML = { val html: HTML = {
val htmlClass = HTML.`class` := "tag"
typ match { typ match {
case Tag.Unrecognized => case Tag.Unrecognized =>
Seq( Seq(
HTML.p(HTML.`class` := "tag")( HTML.p(htmlClass)(
HTML.span(HTML.`class` := cName)(details.html) HTML.span(HTML.`class` := cName)(details.html)
) )
) )
case Tag.Type.TextOnly => case Tag.Type.TextOnly =>
Seq( Seq(
HTML.p(HTML.`class` := "tag")( HTML.p(htmlClass)(
HTML.span(HTML.`class` := cName)("TEXT ONLY")( HTML.span(HTML.`class` := cName)("TEXT ONLY")(
details.html details.html
) )
@ -568,7 +582,7 @@ object Doc {
) )
case _ => case _ =>
Seq( Seq(
HTML.p(HTML.`class` := "tag")( HTML.p(htmlClass)(
HTML.span(HTML.`class` := cName)(name)(details.html) HTML.span(HTML.`class` := cName)(name)(details.html)
) )
) )

View File

@ -855,14 +855,21 @@ case class DocParserDef() extends Parser[Doc] {
var current = section.pop().get var current = section.pop().get
if (current.indent > baseIndent && current.isInstanceOf[Section.Raw]) { if (current.indent > baseIndent && current.isInstanceOf[Section.Raw]) {
var stackOfCodeSections: List[Section] = List[Section]() var stackOfCodeSections: List[Section] = List[Section]()
while (section.stack.nonEmpty && current.indent > baseIndent) { while (
section.stack.nonEmpty && current.indent > baseIndent && current
.isInstanceOf[Section.Raw] && section.stack.head
.isInstanceOf[Section.Raw]
) {
stackOfCodeSections = stackOfCodeSections :+ current stackOfCodeSections = stackOfCodeSections :+ current
current = section.pop().get if (section.stack.head.isInstanceOf[Section.Raw]) {
current = section.pop().get
}
} }
stackOfCodeSections = stackOfCodeSections :+ current stackOfCodeSections = stackOfCodeSections :+ current
val codeLines = stackOfCodeSections.map(s => val codeLines = stackOfCodeSections.flatMap(s => {
Doc.Elem.CodeBlock.Line(s.indent, s.repr.build().trim) val inLines = s.repr.build().split("\n").map(_.trim)
) inLines.map(Doc.Elem.CodeBlock.Line(s.indent, _))
})
if (codeLines.nonEmpty) { if (codeLines.nonEmpty) {
val l1CodeLines = List1(codeLines.head, codeLines.tail) val l1CodeLines = List1(codeLines.head, codeLines.tail)
val codeBlock = Doc.Elem.CodeBlock(l1CodeLines) val codeBlock = Doc.Elem.CodeBlock(l1CodeLines)

View File

@ -69,13 +69,11 @@ object DocParserHTMLGenerator {
* @param doc - Doc from Doc Parser * @param doc - Doc from Doc Parser
* @return - HTML Code from Doc * @return - HTML Code from Doc
*/ */
def generateHTMLPureDoc(doc: Doc): String = def generateHTMLPureDoc(doc: Doc, title: String = ""): String =
HTML HTML
.html( .html(
HTML.body( HTML.body(
HTML.div(HTML.`class` := "doc")(HTML.style := "font-size: 13px;")( HTML.div(HTML.`class` := "enso docs")(doc.htmlWithTitle(title))
doc.html
)
) )
) )
.toString() .toString()

View File

@ -949,10 +949,8 @@ class DocParserTests extends AnyFlatSpec with Matchers {
" file in the project directory.", " file in the project directory.",
Newline, Newline,
CodeBlock( CodeBlock(
CodeBlock.Line( CodeBlock.Line(6, "import Standard.Base.System.File"),
6, CodeBlock.Line(6, "import Standard.Examples"),
"import Standard.Base.System.File\n import Standard.Examples"
),
CodeBlock.Line(6, "example_new = File.new Examples.csv_path") CodeBlock.Line(6, "example_new = File.new Examples.csv_path")
) )
) )