Skip to content

Commit 79614a4

Browse files
pikinier20romanowski
authored andcommitted
Fix inproper rendering of HTML documentation elements. Fix Assertion Errors on non-existent positions
1 parent bbd092b commit 79614a4

File tree

7 files changed

+141
-12
lines changed

7 files changed

+141
-12
lines changed

scala3doc-testcases/src/tests/htmlTests.scala

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,114 @@ package htmlTests
2626
* </code></pre>
2727
*
2828
*/
29-
class HtmlTest
29+
class HtmlTest
30+
31+
/** Implements functionality for printing Scala values on the terminal. For reading values
32+
* use [[scala.io.StdIn$ StdIn]].
33+
* Also defines constants for marking up text on ANSI terminals.
34+
*
35+
* == Console Output ==
36+
*
37+
* Use the print methods to output text.
38+
* {{{
39+
* scala> Console.printf(
40+
* "Today the outside temperature is a balmy %.1f°C. %<.1f°C beats the previous record of %.1f°C.\n",
41+
* -137.0,
42+
* -135.05)
43+
* Today the outside temperature is a balmy -137.0°C. -137.0°C beats the previous record of -135.1°C.
44+
* }}}
45+
*
46+
* == ANSI escape codes ==
47+
* Use the ANSI escape codes for colorizing console output either to STDOUT or STDERR.
48+
* {{{
49+
* import Console.{GREEN, RED, RESET, YELLOW_B, UNDERLINED}
50+
*
51+
* object PrimeTest {
52+
*
53+
* def isPrime(): Unit = {
54+
*
55+
* val candidate = io.StdIn.readInt().ensuring(_ > 1)
56+
*
57+
* val prime = (2 to candidate - 1).forall(candidate % _ != 0)
58+
*
59+
* if (prime)
60+
* Console.println(s"\${RESET}\${GREEN}yes\${RESET}")
61+
* else
62+
* Console.err.println(s"\${RESET}\${YELLOW_B}\${RED}\${UNDERLINED}NO!\${RESET}")
63+
* }
64+
*
65+
* def main(args: Array[String]): Unit = isPrime()
66+
*
67+
* }
68+
* }}}
69+
*
70+
* <table style="border: 10px solid #000;width:100%">
71+
* <tr><td style="background-color:#000;color:#fff">\$ scala PrimeTest</td></tr>
72+
* <tr><td style="background-color:#000;color:#fff">1234567891</td></tr>
73+
* <tr><td style="background-color:#000;color:#0f0">yes</td></tr>
74+
* <tr><td style="background-color:#000;color:#fff">\$ scala PrimeTest</td></tr>
75+
* <tr><td style="background-color:#000;color:#fff">56474</td></tr>
76+
* <tr><td style="background-color:#000;color:#fff"><span style="background-color:#ff0;color:#f00;text-decoration:underline">NO!</span></td></tr>
77+
* </table>
78+
*
79+
* == IO redefinition ==
80+
*
81+
* Use IO redefinition to temporarily swap in a different set of input and/or output streams. In this example the stream based
82+
* method above is wrapped into a function.
83+
*
84+
* {{{
85+
* import java.io.{ByteArrayOutputStream, StringReader}
86+
*
87+
* object FunctionalPrimeTest {
88+
*
89+
* def isPrime(candidate: Int): Boolean = {
90+
*
91+
* val input = new StringReader(s"\$candidate\n")
92+
* val outCapture = new ByteArrayOutputStream
93+
* val errCapture = new ByteArrayOutputStream
94+
*
95+
* Console.withIn(input) {
96+
* Console.withOut(outCapture) {
97+
* Console.withErr(errCapture) {
98+
* PrimeTest.isPrime()
99+
* }
100+
* }
101+
* }
102+
*
103+
* if (outCapture.toByteArray.nonEmpty) // "yes"
104+
* true
105+
* else if (errCapture.toByteArray.nonEmpty) // "NO!"
106+
* false
107+
* else throw new IllegalArgumentException(candidate.toString)
108+
* }
109+
*
110+
* def main(args: Array[String]): Unit = {
111+
* val primes = (2 to 50) filter (isPrime)
112+
* println(s"First primes: \$primes")
113+
* }
114+
*
115+
* }
116+
* }}}
117+
*
118+
*
119+
* <table style="border: 10px solid #000;width:100%">
120+
* <tr><td style="background-color:#000;color:#fff">\$ scala FunctionalPrimeTest</td></tr>
121+
* <tr><td style="background-color:#000;color:#fff">First primes: Vector(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47)</td></tr>
122+
* </table>
123+
*
124+
* @syntax wiki
125+
* @groupname console-output Console Output
126+
* @groupprio console-output 30
127+
* @groupdesc console-output These methods provide output via the console.
128+
*
129+
* @groupname io-default IO Defaults
130+
* @groupprio io-default 50
131+
* @groupdesc io-default These values provide direct access to the standard IO channels
132+
*
133+
* @groupname io-redefinition IO Redefinition
134+
* @groupprio io-redefinition 60
135+
* @groupdesc io-redefinition These methods allow substituting alternative streams for the duration of
136+
* a body of code. Threadsafe by virtue of [[scala.util.DynamicVariable]].
137+
*
138+
*/
139+
object Console

scala3doc/src/dotty/dokka/tasty/BasicSupport.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import collection.JavaConverters._
66
import dotty.dokka._
77
import dotty.dokka.model.api.Annotation
88
import dotty.dokka.model.api.TastyDocumentableSource
9+
import scala.quoted._
910

1011
trait BasicSupport:
1112
self: TastyParser =>
@@ -36,8 +37,8 @@ trait BasicSupport:
3637

3738
def documentation2 = sym.docstring.map(preparseComment(_, sym.tree))
3839

39-
def source =
40-
val path = Some(sym.pos.get.sourceFile.jpath).filter(_ != null).map(_.toAbsolutePath).map(_.toString)
40+
def source(using Quotes) =
41+
val path = sym.pos.filter(isValidPos(_)).map(_.sourceFile.jpath).filter(_ != null).map(_.toAbsolutePath).map(_.toString)
4142
path.map(TastyDocumentableSource(_, sym.pos.get.startLine))
4243

4344
def getAnnotations(): List[Annotation] =

scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ trait ClassLikeSupport:
103103
kindForClasslike(classDef),
104104
classDef.symbol.getAnnotations(),
105105
selfSiangture,
106-
classDef.symbol.source,
106+
classDef.symbol.source(using qctx),
107107
graph = graph
108108
),
109109
compositeExt
@@ -366,7 +366,7 @@ trait ClassLikeSupport:
366366
methodKind,
367367
methodSymbol.getAnnotations(),
368368
method.returnTpt.dokkaType.asSignature,
369-
methodSymbol.source,
369+
methodSymbol.source(using qctx),
370370
origin
371371
)
372372
)
@@ -420,7 +420,7 @@ trait ClassLikeSupport:
420420
Kind.Type(!isTreeAbstract(typeDef.rhs), typeDef.symbol.isOpaque, generics),
421421
typeDef.symbol.getAnnotations(),
422422
tpeTree.dokkaType.asSignature,
423-
typeDef.symbol.source
423+
typeDef.symbol.source(using qctx)
424424
)
425425
)
426426

@@ -438,7 +438,7 @@ trait ClassLikeSupport:
438438
kind,
439439
valDef.symbol.getAnnotations(),
440440
valDef.tpt.tpe.dokkaType.asSignature,
441-
valDef.symbol.source
441+
valDef.symbol.source(using qctx)
442442
)
443443
)
444444

scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ trait SyntheticsSupport:
3737

3838
def getAllMembers: List[Symbol] = hackGetAllMembers(using qctx)(s)
3939

40+
def isValidPos(pos: Position) =
41+
if hackExists(using qctx)(pos) then pos.start != pos.end else false
42+
4043
def isSyntheticField(c: Symbol) =
4144
c.flags.is(Flags.CaseAccessor) || (c.flags.is(Flags.Module) && !c.flags.is(Flags.Given))
4245

43-
def isValidPos(pos: Position) =
44-
pos.start != pos.end
45-
4646
def constructorWithoutParamLists(c: ClassDef): Boolean =
4747
!isValidPos(c.constructor.pos) || {
4848
val end = c.constructor.pos.end
@@ -88,6 +88,15 @@ trait SyntheticsSupport:
8888
baseTypes.asInstanceOf[List[(Symbol, TypeRepr)]]
8989
}
9090

91+
def hackExists(using Quotes)(rpos: qctx.reflect.Position) = {
92+
import qctx.reflect._
93+
import dotty.tools.dotc
94+
import dotty.tools.dotc.util.Spans._
95+
given dotc.core.Contexts.Context = qctx.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
96+
val pos = rpos.asInstanceOf[dotc.util.SourcePosition]
97+
pos.exists
98+
}
99+
91100
def getSupertypes(using Quotes)(c: ClassDef) = hackGetSupertypes(c).tail
92101

93102
def typeForClass(c: ClassDef): TypeRepr =

scala3doc/src/dotty/dokka/tasty/comments/wiki/Parser.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ final class Parser(
180180
stack.length > 0 && char != endOfText
181181
}) do {}
182182

183-
list mkString ""
183+
list mkString
184184
}
185185

186186
def getInline(isInlineEnd: => Boolean): Inline = {

scala3doc/src/dotty/dokka/transformers/ScalaCommentToContentConverter.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ object ScalaCommentToContentConverter extends DocTagToContentConverter {
1717
styles: JSet[? <: Style],
1818
extra: PropertyContainer[ContentNode]
1919
): JList[ContentNode] = docTag match {
20+
case p: P =>
21+
val group = super.buildContent(p, dci, sourceSets, styles, extra).get(0).asInstanceOf[ContentGroup]
22+
List(group.copy(
23+
group.getChildren,
24+
group.getDci,
25+
group.getSourceSets,
26+
Set(TextStyle.Block).asJava,
27+
group.getExtra
28+
)).asJava
2029
case docTag: A =>
2130
val superRes = super.buildContent(docTag, dci, sourceSets, styles, extra).get(0)
2231
val res = superRes.withNewExtras(superRes.getExtra plus ExtraLinkAttributes(

scala3doc/src/dotty/renderers/MemberRenderer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import dotty.dokka.translators.FilterAttributes
1111

1212
class MemberRenderer(signatureRenderer: SignatureRenderer, buildNode: ContentNode => String)(using DocContext):
1313

14-
private val converter = new DocTagToContentConverter()
1514
import signatureRenderer._
15+
private val converter = ScalaCommentToContentConverter
1616

1717
def renderDocPart(d: DocPart): AppliedTag =
1818
val sb = StringBuilder()

0 commit comments

Comments
 (0)