Skip to content

Scala3doc: Fix inproper rendering of HTML documentation elements. #10790

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 111 additions & 1 deletion scala3doc-testcases/src/tests/htmlTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,114 @@ package htmlTests
* </code></pre>
*
*/
class HtmlTest
class HtmlTest

/** Implements functionality for printing Scala values on the terminal. For reading values
* use [[scala.io.StdIn$ StdIn]].
* Also defines constants for marking up text on ANSI terminals.
*
* == Console Output ==
*
* Use the print methods to output text.
* {{{
* scala> Console.printf(
* "Today the outside temperature is a balmy %.1f°C. %<.1f°C beats the previous record of %.1f°C.\n",
* -137.0,
* -135.05)
* Today the outside temperature is a balmy -137.0°C. -137.0°C beats the previous record of -135.1°C.
* }}}
*
* == ANSI escape codes ==
* Use the ANSI escape codes for colorizing console output either to STDOUT or STDERR.
* {{{
* import Console.{GREEN, RED, RESET, YELLOW_B, UNDERLINED}
*
* object PrimeTest {
*
* def isPrime(): Unit = {
*
* val candidate = io.StdIn.readInt().ensuring(_ > 1)
*
* val prime = (2 to candidate - 1).forall(candidate % _ != 0)
*
* if (prime)
* Console.println(s"\${RESET}\${GREEN}yes\${RESET}")
* else
* Console.err.println(s"\${RESET}\${YELLOW_B}\${RED}\${UNDERLINED}NO!\${RESET}")
* }
*
* def main(args: Array[String]): Unit = isPrime()
*
* }
* }}}
*
* <table style="border: 10px solid #000;width:100%">
* <tr><td style="background-color:#000;color:#fff">\$ scala PrimeTest</td></tr>
* <tr><td style="background-color:#000;color:#fff">1234567891</td></tr>
* <tr><td style="background-color:#000;color:#0f0">yes</td></tr>
* <tr><td style="background-color:#000;color:#fff">\$ scala PrimeTest</td></tr>
* <tr><td style="background-color:#000;color:#fff">56474</td></tr>
* <tr><td style="background-color:#000;color:#fff"><span style="background-color:#ff0;color:#f00;text-decoration:underline">NO!</span></td></tr>
* </table>
*
* == IO redefinition ==
*
* Use IO redefinition to temporarily swap in a different set of input and/or output streams. In this example the stream based
* method above is wrapped into a function.
*
* {{{
* import java.io.{ByteArrayOutputStream, StringReader}
*
* object FunctionalPrimeTest {
*
* def isPrime(candidate: Int): Boolean = {
*
* val input = new StringReader(s"\$candidate\n")
* val outCapture = new ByteArrayOutputStream
* val errCapture = new ByteArrayOutputStream
*
* Console.withIn(input) {
* Console.withOut(outCapture) {
* Console.withErr(errCapture) {
* PrimeTest.isPrime()
* }
* }
* }
*
* if (outCapture.toByteArray.nonEmpty) // "yes"
* true
* else if (errCapture.toByteArray.nonEmpty) // "NO!"
* false
* else throw new IllegalArgumentException(candidate.toString)
* }
*
* def main(args: Array[String]): Unit = {
* val primes = (2 to 50) filter (isPrime)
* println(s"First primes: \$primes")
* }
*
* }
* }}}
*
*
* <table style="border: 10px solid #000;width:100%">
* <tr><td style="background-color:#000;color:#fff">\$ scala FunctionalPrimeTest</td></tr>
* <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>
* </table>
*
* @syntax wiki
* @groupname console-output Console Output
* @groupprio console-output 30
* @groupdesc console-output These methods provide output via the console.
*
* @groupname io-default IO Defaults
* @groupprio io-default 50
* @groupdesc io-default These values provide direct access to the standard IO channels
*
* @groupname io-redefinition IO Redefinition
* @groupprio io-redefinition 60
* @groupdesc io-redefinition These methods allow substituting alternative streams for the duration of
* a body of code. Threadsafe by virtue of [[scala.util.DynamicVariable]].
*
*/
object Console
5 changes: 3 additions & 2 deletions scala3doc/src/dotty/dokka/tasty/BasicSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import collection.JavaConverters._
import dotty.dokka._
import dotty.dokka.model.api.Annotation
import dotty.dokka.model.api.TastyDocumentableSource
import scala.quoted._

trait BasicSupport:
self: TastyParser =>
Expand Down Expand Up @@ -36,8 +37,8 @@ trait BasicSupport:

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

def source =
val path = Some(sym.pos.get.sourceFile.jpath).filter(_ != null).map(_.toAbsolutePath).map(_.toString)
def source(using Quotes) =
val path = sym.pos.filter(isValidPos(_)).map(_.sourceFile.jpath).filter(_ != null).map(_.toAbsolutePath).map(_.toString)
path.map(TastyDocumentableSource(_, sym.pos.get.startLine))

def getAnnotations(): List[Annotation] =
Expand Down
8 changes: 4 additions & 4 deletions scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ trait ClassLikeSupport:
kindForClasslike(classDef),
classDef.symbol.getAnnotations(),
selfSiangture,
classDef.symbol.source,
classDef.symbol.source(using qctx),
graph = graph
),
compositeExt
Expand Down Expand Up @@ -366,7 +366,7 @@ trait ClassLikeSupport:
methodKind,
methodSymbol.getAnnotations(),
method.returnTpt.dokkaType.asSignature,
methodSymbol.source,
methodSymbol.source(using qctx),
origin
)
)
Expand Down Expand Up @@ -420,7 +420,7 @@ trait ClassLikeSupport:
Kind.Type(!isTreeAbstract(typeDef.rhs), typeDef.symbol.isOpaque, generics),
typeDef.symbol.getAnnotations(),
tpeTree.dokkaType.asSignature,
typeDef.symbol.source
typeDef.symbol.source(using qctx)
)
)

Expand All @@ -438,7 +438,7 @@ trait ClassLikeSupport:
kind,
valDef.symbol.getAnnotations(),
valDef.tpt.tpe.dokkaType.asSignature,
valDef.symbol.source
valDef.symbol.source(using qctx)
)
)

Expand Down
15 changes: 12 additions & 3 deletions scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ trait SyntheticsSupport:

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

def isValidPos(pos: Position) =
if hackExists(using qctx)(pos) then pos.start != pos.end else false

def isSyntheticField(c: Symbol) =
c.flags.is(Flags.CaseAccessor) || (c.flags.is(Flags.Module) && !c.flags.is(Flags.Given))

def isValidPos(pos: Position) =
pos.start != pos.end

def constructorWithoutParamLists(c: ClassDef): Boolean =
!isValidPos(c.constructor.pos) || {
val end = c.constructor.pos.end
Expand Down Expand Up @@ -88,6 +88,15 @@ trait SyntheticsSupport:
baseTypes.asInstanceOf[List[(Symbol, TypeRepr)]]
}

def hackExists(using Quotes)(rpos: qctx.reflect.Position) = {
import qctx.reflect._
import dotty.tools.dotc
import dotty.tools.dotc.util.Spans._
given dotc.core.Contexts.Context = qctx.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
val pos = rpos.asInstanceOf[dotc.util.SourcePosition]
pos.exists
}

def getSupertypes(using Quotes)(c: ClassDef) = hackGetSupertypes(c).tail

def typeForClass(c: ClassDef): TypeRepr =
Expand Down
2 changes: 1 addition & 1 deletion scala3doc/src/dotty/dokka/tasty/comments/wiki/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ final class Parser(
stack.length > 0 && char != endOfText
}) do {}

list mkString ""
list mkString
}

def getInline(isInlineEnd: => Boolean): Inline = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ object ScalaCommentToContentConverter extends DocTagToContentConverter {
styles: JSet[? <: Style],
extra: PropertyContainer[ContentNode]
): JList[ContentNode] = docTag match {
case p: P =>
val group = super.buildContent(p, dci, sourceSets, styles, extra).get(0).asInstanceOf[ContentGroup]
List(group.copy(
group.getChildren,
group.getDci,
group.getSourceSets,
Set(TextStyle.Block).asJava,
group.getExtra
)).asJava
case docTag: A =>
val superRes = super.buildContent(docTag, dci, sourceSets, styles, extra).get(0)
val res = superRes.withNewExtras(superRes.getExtra plus ExtraLinkAttributes(
Expand Down
2 changes: 1 addition & 1 deletion scala3doc/src/dotty/renderers/MemberRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import dotty.dokka.translators.FilterAttributes

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

private val converter = new DocTagToContentConverter()
import signatureRenderer._
private val converter = ScalaCommentToContentConverter

def renderDocPart(d: DocPart): AppliedTag =
val sb = StringBuilder()
Expand Down