diff --git a/scala3doc-testcases/src/tests/htmlTests.scala b/scala3doc-testcases/src/tests/htmlTests.scala
index 0132f217c197..72f331add0d9 100644
--- a/scala3doc-testcases/src/tests/htmlTests.scala
+++ b/scala3doc-testcases/src/tests/htmlTests.scala
@@ -26,4 +26,114 @@ package htmlTests
*
*
*/
-class HtmlTest
\ No newline at end of file
+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()
+ *
+ * }
+ * }}}
+ *
+ *
+ * \$ scala PrimeTest |
+ * 1234567891 |
+ * yes |
+ * \$ scala PrimeTest |
+ * 56474 |
+ * NO! |
+ *
+ *
+ * == 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")
+ * }
+ *
+ * }
+ * }}}
+ *
+ *
+ *
+ * \$ scala FunctionalPrimeTest |
+ * First primes: Vector(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47) |
+ *
+ *
+ * @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
\ No newline at end of file
diff --git a/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala b/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala
index e157b9fbc830..d2713bb7e7fb 100644
--- a/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala
+++ b/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala
@@ -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 =>
@@ -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] =
diff --git a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala
index d430b020c068..d6a1417aeb6f 100644
--- a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala
+++ b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala
@@ -103,7 +103,7 @@ trait ClassLikeSupport:
kindForClasslike(classDef),
classDef.symbol.getAnnotations(),
selfSiangture,
- classDef.symbol.source,
+ classDef.symbol.source(using qctx),
graph = graph
),
compositeExt
@@ -366,7 +366,7 @@ trait ClassLikeSupport:
methodKind,
methodSymbol.getAnnotations(),
method.returnTpt.dokkaType.asSignature,
- methodSymbol.source,
+ methodSymbol.source(using qctx),
origin
)
)
@@ -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)
)
)
@@ -438,7 +438,7 @@ trait ClassLikeSupport:
kind,
valDef.symbol.getAnnotations(),
valDef.tpt.tpe.dokkaType.asSignature,
- valDef.symbol.source
+ valDef.symbol.source(using qctx)
)
)
diff --git a/scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala b/scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala
index d2a0dc6c58e2..7e923b5007a9 100644
--- a/scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala
+++ b/scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala
@@ -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
@@ -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 =
diff --git a/scala3doc/src/dotty/dokka/tasty/comments/wiki/Parser.scala b/scala3doc/src/dotty/dokka/tasty/comments/wiki/Parser.scala
index caf51e1e69d9..94cda29c05a7 100644
--- a/scala3doc/src/dotty/dokka/tasty/comments/wiki/Parser.scala
+++ b/scala3doc/src/dotty/dokka/tasty/comments/wiki/Parser.scala
@@ -180,7 +180,7 @@ final class Parser(
stack.length > 0 && char != endOfText
}) do {}
- list mkString ""
+ list mkString
}
def getInline(isInlineEnd: => Boolean): Inline = {
diff --git a/scala3doc/src/dotty/dokka/transformers/ScalaCommentToContentConverter.scala b/scala3doc/src/dotty/dokka/transformers/ScalaCommentToContentConverter.scala
index ce2db16b5c7c..33b873fa8732 100644
--- a/scala3doc/src/dotty/dokka/transformers/ScalaCommentToContentConverter.scala
+++ b/scala3doc/src/dotty/dokka/transformers/ScalaCommentToContentConverter.scala
@@ -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(
diff --git a/scala3doc/src/dotty/renderers/MemberRenderer.scala b/scala3doc/src/dotty/renderers/MemberRenderer.scala
index 75fab3b7a182..95f7eef82350 100644
--- a/scala3doc/src/dotty/renderers/MemberRenderer.scala
+++ b/scala3doc/src/dotty/renderers/MemberRenderer.scala
@@ -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()