Skip to content

Commit c037996

Browse files
committed
Move snippet compiler logic to parsing phase.
1 parent 06b721d commit c037996

File tree

11 files changed

+94
-92
lines changed

11 files changed

+94
-92
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

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

127123
def mkHead(page: Page): AppliedTag =
128124
val resources = page.content match

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

Lines changed: 10 additions & 11 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
m.docs.fold(Nil)(d =>
5150
nested("Type Params", d.typeParams) ++
@@ -88,17 +87,17 @@ class MemberRenderer(signatureRenderer: SignatureRenderer, snippetChecker: Snipp
8887
Seq(
8988
since.map(s => code("[Since version ", parameter(s), "] ")),
9089
message.map(m => parameter(m)))
91-
++ m.docs.map(_.deprecated.toSeq.map(renderDocPart(_)(using m)))
90+
++ m.docs.map(_.deprecated.toSeq.map(renderDocPart))
9291
).flatten
9392
Seq(dt("Deprecated"), dd(content:_*))
9493
}
9594

9695
def memberInfo(m: Member): Seq[AppliedTag] =
9796
val comment = m.docs
98-
val bodyContents = m.docs.fold(Nil)(e => renderDocPart(e.body)(using m) :: Nil)
97+
val bodyContents = m.docs.fold(Nil)(e => renderDocPart(e.body) :: Nil)
9998

10099
Seq(
101-
div(cls := "documentableBrief doc")(comment.flatMap(_.short).fold("")(renderDocPart(_)(using m))),
100+
div(cls := "documentableBrief doc")(comment.flatMap(_.short).fold("")(renderDocPart)),
102101
div(cls := "cover")(
103102
div(cls := "doc")(bodyContents),
104103
dl(cls := "attributes")(
@@ -248,8 +247,8 @@ class MemberRenderer(signatureRenderer: SignatureRenderer, snippetChecker: Snipp
248247
val rawGroups = membersInGroups.groupBy(_.docs.flatMap(_.group)).collect {
249248
case (Some(groupName), members) =>
250249
ExpandedGroup(
251-
names.get(groupName).fold(raw(groupName))(renderDocPart(_)(using m)),
252-
descriptions.get(groupName).fold(raw(""))(renderDocPart(_)(using m)),
250+
names.get(groupName).fold(raw(groupName))(renderDocPart),
251+
descriptions.get(groupName).fold(raw(""))(renderDocPart),
253252
prios.getOrElse(groupName, 1000)
254253
) -> members
255254
}

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/BasicSupport.scala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ trait BasicSupport:
3535
extension (sym: Symbol)
3636
def documentation = sym.docstring.map(parseComment(_, sym.tree))
3737

38-
def source(using Quotes) =
39-
val path = sym.pos.map(_.sourceFile.jpath).filter(_ != null).map(_.toAbsolutePath)
40-
path.map(TastyMemberSource(_, sym.pos.get.startLine))
41-
4238
def getAnnotations(): List[Annotation] =
4339
sym.annotations.filterNot(_.symbol.packageName.startsWith("scala.annotation.internal")).map(parseAnnotation).reverse
4440

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ trait ClassLikeSupport:
444444
modifiers = modifiers,
445445
annotations = symbol.getAnnotations(),
446446
signature = signature,
447-
sources = symbol.source(using qctx),
447+
sources = symbol.source,
448448
origin = origin,
449449
inheritedFrom = inheritedFrom,
450450
graph = graph,

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import dotty.tools.scaladoc.tasty.comments.Comment
99
trait ScaladocSupport { self: TastyParser =>
1010
import qctx.reflect._
1111

12+
private val snippetChecker: snippets.SnippetChecker = snippets.SnippetChecker()
13+
1214
def parseCommentString(comment: String, sym: Symbol, pos: Option[Position]): Comment =
1315
val preparsed = comments.Preparser.preparse(comments.Cleaner.clean(comment))
1416

@@ -27,9 +29,9 @@ trait ScaladocSupport { self: TastyParser =>
2729

2830
val parser = commentSyntax match {
2931
case CommentSyntax.Uncyclo =>
30-
comments.UncycloCommentParser(comments.Repr(qctx)(sym))
32+
comments.UncycloCommentParser(comments.Repr(qctx)(sym), snippetChecker)
3133
case CommentSyntax.Markdown =>
32-
comments.MarkdownCommentParser(comments.Repr(qctx)(sym))
34+
comments.MarkdownCommentParser(comments.Repr(qctx)(sym), snippetChecker)
3335
}
3436
parser.parse(preparsed)
3537

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ class SymOps[Q <: Quotes](val q: Q) extends JavadocAnchorCreator with Scaladoc2A
1414
private val externalLinkCache: scala.collection.mutable.Map[AbstractFile, Option[ExternalDocLink]] = MMap()
1515

1616
extension (sym: Symbol)
17+
def source =
18+
val path = sym.pos.map(_.sourceFile.jpath).filter(_ != null).map(_.toAbsolutePath)
19+
path.map(TastyMemberSource(_, sym.pos.get.startLine))
20+
1721
def packageName: String = (
1822
if (sym.isPackageDef) sym.fullName
1923
else sym.maybeOwner.packageName

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

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import scala.quoted._
1313
import dotty.tools.scaladoc.tasty.comments.wiki.Paragraph
1414
import dotty.tools.scaladoc.DocPart
1515
import dotty.tools.scaladoc.tasty.SymOps
16+
import dotty.tools.scaladoc.snippets._
1617
import collection.JavaConverters._
1718

1819
class Repr(val qctx: Quotes)(val sym: qctx.reflect.Symbol)
@@ -70,13 +71,14 @@ case class PreparsedComment(
7071

7172
case class DokkaCommentBody(summary: Option[DocPart], body: DocPart)
7273

73-
abstract class MarkupConversion[T](val repr: Repr)(using DocContext) {
74+
abstract class MarkupConversion[T](val repr: Repr, snippetChecker: SnippetChecker)(using dctx: DocContext) {
7475
protected def stringToMarkup(str: String): T
7576
protected def markupToDokka(t: T): DocPart
7677
protected def markupToString(t: T): String
7778
protected def markupToDokkaCommentBody(t: T): DokkaCommentBody
7879
protected def filterEmpty(xs: List[String]): List[T]
7980
protected def filterEmpty(xs: SortedMap[String, String]): SortedMap[String, T]
81+
protected def processSnippets(t: T): T
8082

8183
val qctx: repr.qctx.type = if repr == null then null else repr.qctx // TODO why we do need null?
8284
val owner: qctx.reflect.Symbol =
@@ -173,8 +175,27 @@ abstract class MarkupConversion[T](val repr: Repr)(using DocContext) {
173175
}
174176
}
175177

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

203-
class MarkdownCommentParser(repr: Repr)(using DocContext)
204-
extends MarkupConversion[mdu.Node](repr) {
224+
class MarkdownCommentParser(repr: Repr, snippetChecker: SnippetChecker)(using DocContext)
225+
extends MarkupConversion[mdu.Node](repr, snippetChecker) {
205226

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

231-
class UncycloCommentParser(repr: Repr)(using DocContext)
232-
extends MarkupConversion[wiki.Body](repr):
274+
class UncycloCommentParser(repr: Repr, snippetChecker: SnippetChecker)(using DocContext)
275+
extends MarkupConversion[wiki.Body](repr, snippetChecker):
233276

234277
def stringToMarkup(str: String) = wiki.Parser(str, resolverLink).document()
235278

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

0 commit comments

Comments
 (0)