Skip to content

Commit 5f05011

Browse files
committed
Move snippet compiler logic to parsing phase.
1 parent 1cb7080 commit 5f05011

File tree

8 files changed

+86
-87
lines changed

8 files changed

+86
-87
lines changed

scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ import java.nio.file.Files
1414
import java.nio.file.FileVisitOption
1515
import java.io.File
1616

17-
import dotty.tools.scaladoc.snippets._
18-
1917
case class Page(link: Link, content: Member | ResolvedTemplate | String, children: Seq[Page]):
2018
def withNewChildren(newChildren: Seq[Page]) = copy(children = children ++ newChildren)
2119

@@ -28,7 +26,6 @@ case class Page(link: Link, content: Member | ResolvedTemplate | String, childre
2826
class HtmlRenderer(rootPackage: Member, val members: Map[DRI, Member])(using ctx: DocContext)
2927
extends SiteRenderer, Resources, Locations, Writer:
3028
private val args = summon[DocContext].args
31-
private val snippetChecker = SnippetChecker()
3229
val staticSite = summon[DocContext].staticSiteContext
3330

3431
val effectiveMembers = members
@@ -78,7 +75,7 @@ class HtmlRenderer(rootPackage: Member, val members: Map[DRI, Member])(using ctx
7875
def link(dri: DRI): Option[String] =
7976
Some(pathToPage(currentDri, dri)).filter(_ != UnresolvedLocationLink)
8077

81-
MemberRenderer(signatureRenderer, snippetChecker).fullMember(m)
78+
MemberRenderer(signatureRenderer).fullMember(m)
8279
case t: ResolvedTemplate => siteContent(page.link.dri, t)
8380
case a: String => raw(a)
8481

@@ -125,7 +122,6 @@ class HtmlRenderer(rootPackage: Member, val members: Map[DRI, Member])(using ctx
125122
def render(): Unit =
126123
val renderedResources = renderResources()
127124
val sites = allPages.map(renderPage(_, Vector.empty))
128-
println(snippetChecker.summary)
129125

130126
def mkHead(page: Page): AppliedTag =
131127
val resources = page.content match

scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ import dotty.tools.scaladoc.tasty.comments.markdown.DocFlexmarkRenderer
99
import com.vladsch.flexmark.util.ast.{Node => MdNode}
1010
import dotty.tools.scaladoc.tasty.comments.wiki.UncycloDocElement
1111
import translators._
12-
import dotty.tools.scaladoc.snippets._
1312

14-
class MemberRenderer(signatureRenderer: SignatureRenderer, snippetChecker: SnippetChecker)(using DocContext) extends DocRender(signatureRenderer, snippetChecker):
13+
class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) extends DocRender(signatureRenderer):
1514
import signatureRenderer._
1615

17-
def doc(m: Member): Seq[AppliedTag] = m.docs.fold(Nil)(d => Seq(renderDocPart(d.body)(using m)))
16+
def doc(m: Member): Seq[AppliedTag] = m.docs.fold(Nil)(d => Seq(renderDocPart(d.body)))
1817

1918
def tableRow(name: String, content: AppliedTag) = Seq(dt(name), dd(content))
2019

@@ -38,14 +37,14 @@ class MemberRenderer(signatureRenderer: SignatureRenderer, snippetChecker: Snipp
3837
def nested(name: String, on: SortedMap[String, DocPart]): Seq[AppliedTag] =
3938
if on.isEmpty then Nil else
4039
tableRow(name, dl(cls := "attributes")(
41-
on.map { case (name, value) => tableRow(name, renderDocPart(value)(using m))}.toList:_*
40+
on.map { case (name, value) => tableRow(name, renderDocPart(value))}.toList:_*
4241
))
4342

4443
def list(name: String, on: List[DocPart]): Seq[AppliedTag] =
45-
if on.isEmpty then Nil else tableRow(name, div(on.map(e => div(renderDocPart(e)(using m)))))
44+
if on.isEmpty then Nil else tableRow(name, div(on.map(e => div(renderDocPart(e)))))
4645

4746
def opt(name: String, on: Option[DocPart]): Seq[AppliedTag] =
48-
if on.isEmpty then Nil else tableRow(name, renderDocPart(on.get)(using m))
47+
if on.isEmpty then Nil else tableRow(name, renderDocPart(on.get))
4948

5049
def authors(authors: List[DocPart]) = if summon[DocContext].args.includeAuthors then list("Authors", authors) else Nil
5150

@@ -90,14 +89,14 @@ class MemberRenderer(signatureRenderer: SignatureRenderer, snippetChecker: Snipp
9089
Seq(
9190
since.map(s => code("[Since version ", parameter(s), "] ")),
9291
message.map(m => parameter(m)))
93-
++ m.docs.map(_.deprecated.toSeq.map(renderDocPart(_)(using m)))
92+
++ m.docs.map(_.deprecated.toSeq.map(renderDocPart))
9493
).flatten
9594
Seq(dt("Deprecated"), dd(content:_*))
9695
}
9796

9897
def memberInfo(m: Member, withBrief: Boolean = false): Seq[AppliedTag] =
9998
val comment = m.docs
100-
val bodyContents = m.docs.fold(Nil)(e => renderDocPart(e.body)(using m) :: Nil)
99+
val bodyContents = m.docs.fold(Nil)(e => renderDocPart(e.body) :: Nil)
101100

102101
Seq(
103102
Option.when(withBrief)(div(cls := "documentableBrief doc")(comment.flatMap(_.short).fold("")(renderDocPart))),
@@ -252,8 +251,8 @@ class MemberRenderer(signatureRenderer: SignatureRenderer, snippetChecker: Snipp
252251
val rawGroups = membersInGroups.groupBy(_.docs.flatMap(_.group)).collect {
253252
case (Some(groupName), members) =>
254253
ExpandedGroup(
255-
names.get(groupName).fold(raw(groupName))(renderDocPart(_)(using m)),
256-
descriptions.get(groupName).fold(raw(""))(renderDocPart(_)(using m)),
254+
names.get(groupName).fold(raw(groupName))(renderDocPart),
255+
descriptions.get(groupName).fold(raw(""))(renderDocPart),
257256
prios.getOrElse(groupName, 1000)
258257
) -> members
259258
}

scaladoc/src/dotty/tools/scaladoc/renderers/UncycloDocRenderer.scala

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,21 @@ import util.HTML._
66
import com.vladsch.flexmark.util.ast.{Node => MdNode}
77
import dotty.tools.scaladoc.tasty.comments.wiki.UncycloDocElement
88
import dotty.tools.scaladoc.tasty.comments.markdown.DocFlexmarkRenderer
9-
import dotty.tools.scaladoc.snippets._
109

11-
class DocRender(signatureRenderer: SignatureRenderer, snippetChecker: SnippetChecker)(using ctx: DocContext):
10+
class DocRender(signatureRenderer: SignatureRenderer)(using DocContext):
1211

13-
private def snippetCheckingFuncFromMember: Member => SnippetChecker.SnippetCheckingFunc =
14-
(m: Member) => {
15-
(str: String, lineOffset: SnippetChecker.LineOffset, argOverride: Option[SCFlags]) => {
16-
val pathBasedArg = ctx.snippetCompilerArgs.get(m)
17-
val arg = argOverride.fold(pathBasedArg)(pathBasedArg.overrideFlag(_))
18-
19-
snippetChecker.checkSnippet(str, m.docs.map(_.snippetCompilerData), arg, lineOffset).foreach { _ match {
20-
case r: SnippetCompilationResult if !r.isSuccessful =>
21-
println(s"In member ${m.name} (${m.dri.location}):")
22-
println(r.getSummary)
23-
case _ =>
24-
}
25-
}
26-
}
27-
}
28-
29-
def renderDocPart(doc: DocPart)(using Member): AppliedTag = doc match
12+
def renderDocPart(doc: DocPart): AppliedTag = doc match
3013
case md: MdNode => renderMarkdown(md)
3114
case Nil => raw("")
3215
case Seq(elem: UncycloDocElement) => renderElement(elem)
3316
case list: Seq[UncycloDocElement @unchecked] => div(list.map(renderElement))
3417

35-
private def renderMarkdown(el: MdNode)(using m: Member): AppliedTag =
36-
raw(DocFlexmarkRenderer.render(el)(
37-
(link,name) =>
38-
renderLink(link, default => text(if name.isEmpty then default else name)).toString,
39-
snippetCheckingFuncFromMember(m)
18+
private def renderMarkdown(el: MdNode): AppliedTag =
19+
raw(DocFlexmarkRenderer.render(el)( (link,name) =>
20+
renderLink(link, default => text(if name.isEmpty then default else name)).toString
4021
))
4122

42-
private def listItems(items: Seq[UncycloDocElement])(using m: Member) =
23+
private def listItems(items: Seq[UncycloDocElement]) =
4324
items.map(i => li(renderElement(i)))
4425
private def notSupported(name: String, content: AppliedTag): AppliedTag =
4526
report.warning(s"Uncyclo syntax does not support $name in ${signatureRenderer.currentDri.location}")
@@ -54,7 +35,7 @@ class DocRender(signatureRenderer: SignatureRenderer, snippetChecker: SnippetChe
5435
val tooltip = s"Problem linking $query: $msg"
5536
signatureRenderer.unresolvedLink(linkBody(query), titleAttr := tooltip)
5637

57-
private def renderElement(e: UncycloDocElement)(using m: Member): AppliedTag = e match
38+
private def renderElement(e: UncycloDocElement): AppliedTag = e match
5839
case Title(text, level) =>
5940
val content = renderElement(text)
6041
level match
@@ -65,9 +46,7 @@ class DocRender(signatureRenderer: SignatureRenderer, snippetChecker: SnippetChe
6546
case 5 => h5(content)
6647
case 6 => h6(content)
6748
case Paragraph(text) => p(renderElement(text))
68-
case Code(data: String) =>
69-
snippetCheckingFuncFromMember(m)(data, 0, None)
70-
pre(code(raw(data))) // TODO add classes
49+
case Code(data: String) => pre(code(raw(data))) // TODO add classes
7150
case HorizontalRule => hr
7251

7352
case UnorderedList(items) => ul(listItems(items))

scaladoc/src/dotty/tools/scaladoc/snippets/SnippetChecker.scala

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ class SnippetChecker()(using ctx: DocContext):
1111
ctx.args.tastyDirs.map(_.getAbsolutePath()).mkString(sep)
1212
private val compiler: SnippetCompiler = SnippetCompiler(classpath = cp)
1313

14-
private var warningsCount = 0
15-
private var errorsCount = 0
16-
1714
def checkSnippet(
1815
snippet: String,
1916
data: Option[SnippetCompilerData],
@@ -31,18 +28,10 @@ class SnippetChecker()(using ctx: DocContext):
3128
data.fold(0)(_.position.column)
3229
)
3330
val res = compiler.compile(wrapped, arg)
34-
if !res.isSuccessful && res.messages.exists(_.level == MessageLevel.Error) then errorsCount = errorsCount + 1
35-
if !res.isSuccessful && res.messages.exists(_.level == MessageLevel.Warning) then warningsCount = warningsCount + 1
3631
Some(res)
3732
else None
3833
}
3934

40-
def summary: String = s"""
41-
|Snippet compiler summary:
42-
| Found $warningsCount warnings
43-
| Found $errorsCount errors
44-
|""".stripMargin
45-
4635
object SnippetChecker:
4736
type LineOffset = Int
4837
type SnippetCheckingFunc = (String, LineOffset, Option[SCFlags]) => Unit

scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompilerArgs.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package dotty.tools.scaladoc
22
package snippets
33

4+
import java.nio.file.Path
5+
46
case class SnippetCompilerArg(flag: SCFlags, debug: Boolean):
57
def overrideFlag(f: SCFlags): SnippetCompilerArg = copy(flag = f)
68

@@ -19,6 +21,12 @@ case class SnippetCompilerArgs(scFlags: PathBased[SCFlags], val debug: Boolean,
1921
.flatMap(s => scFlags.get(s.path).map(_.elem))
2022
.fold(SnippetCompilerArg(defaultFlag, debug))(SnippetCompilerArg(_, debug))
2123

24+
def get(path: Option[Path]): SnippetCompilerArg =
25+
path
26+
.flatMap(p => scFlags.get(p).map(_.elem))
27+
.fold(SnippetCompilerArg(defaultFlag, debug))(SnippetCompilerArg(_, debug))
28+
29+
2230
object SnippetCompilerArgs:
2331
val usage =
2432
"""

scaladoc/src/dotty/tools/scaladoc/tasty/ScalaDocSupport.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ object ScaladocSupport:
2929

3030
val parser = commentSyntax match {
3131
case CommentSyntax.Uncyclo =>
32-
comments.UncycloCommentParser(comments.Repr(quotes)(sym))
32+
comments.UncycloCommentParser(comments.Repr(quotes)(sym), snippetChecker)
3333
case CommentSyntax.Markdown =>
34-
comments.MarkdownCommentParser(comments.Repr(quotes)(sym))
34+
comments.MarkdownCommentParser(comments.Repr(quotes)(sym), snippetChecker)
3535
}
3636
parser.parse(preparsed)
3737

scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import dotty.tools.scaladoc.tasty.comments.wiki.Paragraph
1414
import dotty.tools.scaladoc.DocPart
1515
import dotty.tools.scaladoc.tasty.SymOpsWithLinkCache
1616
import collection.JavaConverters._
17-
import collection.JavaConverters._
17+
import dotty.tools.scaladoc.snippets._
1818

1919
class Repr(val qctx: Quotes)(val sym: qctx.reflect.Symbol)
2020

@@ -71,13 +71,14 @@ case class PreparsedComment(
7171

7272
case class DokkaCommentBody(summary: Option[DocPart], body: DocPart)
7373

74-
abstract class MarkupConversion[T](val repr: Repr)(using DocContext) {
74+
abstract class MarkupConversion[T](val repr: Repr, snippetChecker: SnippetChecker)(using dctx: DocContext) {
7575
protected def stringToMarkup(str: String): T
7676
protected def markupToDokka(t: T): DocPart
7777
protected def markupToString(t: T): String
7878
protected def markupToDokkaCommentBody(t: T): DokkaCommentBody
7979
protected def filterEmpty(xs: List[String]): List[T]
8080
protected def filterEmpty(xs: SortedMap[String, String]): SortedMap[String, T]
81+
protected def processSnippets(t: T): T
8182

8283
val qctx: repr.qctx.type = if repr == null then null else repr.qctx // TODO why we do need null?
8384
val owner: qctx.reflect.Symbol =
@@ -175,8 +176,27 @@ abstract class MarkupConversion[T](val repr: Repr)(using DocContext) {
175176
}
176177
}
177178

179+
def snippetCheckingFunc: qctx.reflect.Symbol => SnippetChecker.SnippetCheckingFunc =
180+
(s: qctx.reflect.Symbol) => {
181+
val path = s.source.map(_.path)
182+
val pathBasedArg = dctx.snippetCompilerArgs.get(path)
183+
val data = getSnippetCompilerData(s)
184+
(str: String, lineOffset: SnippetChecker.LineOffset, argOverride: Option[SCFlags]) => {
185+
val arg = argOverride.fold(pathBasedArg)(pathBasedArg.overrideFlag(_))
186+
187+
snippetChecker.checkSnippet(str, Some(data), arg, lineOffset).foreach { _ match {
188+
case r: SnippetCompilationResult if !r.isSuccessful =>
189+
val msg = s"In member ${s.name} (${s.dri.location}):\n${r.getSummary}"
190+
report.error(msg)(using dctx.compilerContext)
191+
case _ =>
192+
}
193+
}
194+
}
195+
}
196+
178197
final def parse(preparsed: PreparsedComment): Comment =
179-
val body = markupToDokkaCommentBody(stringToMarkup(preparsed.body))
198+
val markup = stringToMarkup(preparsed.body)
199+
val body = markupToDokkaCommentBody(processSnippets(markup))
180200
Comment(
181201
body = body.body,
182202
short = body.summary,
@@ -202,8 +222,8 @@ abstract class MarkupConversion[T](val repr: Repr)(using DocContext) {
202222
)
203223
}
204224

205-
class MarkdownCommentParser(repr: Repr)(using DocContext)
206-
extends MarkupConversion[mdu.Node](repr) {
225+
class MarkdownCommentParser(repr: Repr, snippetChecker: SnippetChecker)(using DocContext)
226+
extends MarkupConversion[mdu.Node](repr, snippetChecker) {
207227

208228
def stringToMarkup(str: String) =
209229
MarkdownParser.parseToMarkdown(str, markdown.DocFlexmarkParser(resolveLink))
@@ -228,10 +248,32 @@ class MarkdownCommentParser(repr: Repr)(using DocContext)
228248
xs.view.mapValues(_.trim)
229249
.filterNot { case (_, v) => v.isEmpty }
230250
.mapValues(stringToMarkup).to(SortedMap)
251+
252+
def processSnippets(root: mdu.Node): mdu.Node = {
253+
val nodes = root.getDescendants().asScala.collect {
254+
case fcb: mda.FencedCodeBlock => fcb
255+
}.toList
256+
if !nodes.isEmpty then {
257+
val checkingFunc: SnippetChecker.SnippetCheckingFunc = snippetCheckingFunc(owner)
258+
nodes.foreach { node =>
259+
val snippet = node.getContentChars.toString
260+
val lineOffset = node.getStartLineNumber
261+
val info = node.getInfo.toString
262+
val argOverride =
263+
info.split(" ")
264+
.find(_.startsWith("sc:"))
265+
.map(_.stripPrefix("sc:"))
266+
.map(snippets.SCFlagsParser.parse)
267+
.flatMap(_.toOption)
268+
checkingFunc(snippet, lineOffset, argOverride)
269+
}
270+
}
271+
root
272+
}
231273
}
232274

233-
class UncycloCommentParser(repr: Repr)(using DocContext)
234-
extends MarkupConversion[wiki.Body](repr):
275+
class UncycloCommentParser(repr: Repr, snippetChecker: SnippetChecker)(using DocContext)
276+
extends MarkupConversion[wiki.Body](repr, snippetChecker):
235277

236278
def stringToMarkup(str: String) = wiki.Parser(str, resolverLink).document()
237279

@@ -281,3 +323,7 @@ class UncycloCommentParser(repr: Repr)(using DocContext)
281323
def filterEmpty(xs: SortedMap[String,String]) =
282324
xs.view.mapValues(stringToMarkup).to(SortedMap)
283325
.filterNot { case (_, v) => v.blocks.isEmpty }
326+
327+
def processSnippets(root: wiki.Body): wiki.Body =
328+
// Currently not supported
329+
root

scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/DocFlexmarkExtension.scala

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ import com.vladsch.flexmark.ext.wikilink.internal.UncycloLinkLinkRefProcessor
1010
import com.vladsch.flexmark.util.ast._
1111
import com.vladsch.flexmark.util.options._
1212
import com.vladsch.flexmark.util.sequence.BasedSequence
13-
import com.vladsch.flexmark._
1413

15-
import dotty.tools.scaladoc.snippets.SnippetChecker
1614

1715
class DocLinkNode(
1816
val target: DocLink,
@@ -47,33 +45,17 @@ object DocFlexmarkParser {
4745
}
4846
}
4947

50-
case class DocFlexmarkRenderer(renderLink: (DocLink, String) => String, snippetCheckingFunc: SnippetChecker.SnippetCheckingFunc)
48+
case class DocFlexmarkRenderer(renderLink: (DocLink, String) => String)
5149
extends HtmlRenderer.HtmlRendererExtension:
52-
5350
def rendererOptions(opt: MutableDataHolder): Unit = () // noop
5451

55-
object FencedCodeBlockHandler extends CustomNodeRenderer[ast.FencedCodeBlock]:
56-
override def render(node: ast.FencedCodeBlock, c: NodeRendererContext, html: HtmlWriter): Unit =
57-
val info = node.getInfo.toString
58-
val argOverride =
59-
info.split(" ")
60-
.find(_.startsWith("sc:"))
61-
.map(_.stripPrefix("sc:"))
62-
.map(snippets.SCFlagsParser.parse)
63-
.flatMap(_.toOption)
64-
snippetCheckingFunc(node.getContentChars.toString, node.getStartLineNumber, argOverride)
65-
c.delegateRender()
66-
6752
object Handler extends CustomNodeRenderer[DocLinkNode]:
6853
override def render(node: DocLinkNode, c: NodeRendererContext, html: HtmlWriter): Unit =
6954
html.raw(renderLink(node.target, node.body))
7055

7156
object Render extends NodeRenderer:
7257
override def getNodeRenderingHandlers: JSet[NodeRenderingHandler[_]] =
73-
JSet(
74-
new NodeRenderingHandler(classOf[DocLinkNode], Handler),
75-
new NodeRenderingHandler(classOf[ast.FencedCodeBlock], FencedCodeBlockHandler)
76-
)
58+
JSet(new NodeRenderingHandler(classOf[DocLinkNode], Handler))
7759

7860
object Factory extends NodeRendererFactory:
7961
override def create(options: DataHolder): NodeRenderer = Render
@@ -82,6 +64,6 @@ case class DocFlexmarkRenderer(renderLink: (DocLink, String) => String, snippetC
8264
htmlRendererBuilder.nodeRendererFactory(Factory)
8365

8466
object DocFlexmarkRenderer:
85-
def render(node: Node)(renderLink: (DocLink, String) => String, snippetCheckingFunc: SnippetChecker.SnippetCheckingFunc) =
86-
val opts = MarkdownParser.mkMarkdownOptions(Seq(DocFlexmarkRenderer(renderLink, snippetCheckingFunc)))
87-
HtmlRenderer.builder(opts).build().render(node)
67+
def render(node: Node)(renderLink: (DocLink, String) => String) =
68+
val opts = MarkdownParser.mkMarkdownOptions(Seq(DocFlexmarkRenderer(renderLink)))
69+
HtmlRenderer.builder(opts).build().render(node)

0 commit comments

Comments
 (0)