From 632b100e90f29d5230c5e44afd9a9b6c4d5f385d Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Tue, 24 Nov 2020 01:49:40 +0100 Subject: [PATCH 01/19] Migrate scala3doc to compiler's parser --- .../dotty/tools/dotc/config/Settings.scala | 6 +- scala3doc/src/dotty/dokka/Main.scala | 156 +++++++++++------- scala3doc/src/dotty/tools/dottydoc/Main.scala | 47 +----- 3 files changed, 104 insertions(+), 105 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/Settings.scala b/compiler/src/dotty/tools/dotc/config/Settings.scala index a351921e8e64..367a403ebad9 100644 --- a/compiler/src/dotty/tools/dotc/config/Settings.scala +++ b/compiler/src/dotty/tools/dotc/config/Settings.scala @@ -234,7 +234,7 @@ object Settings { * * to get their arguments. */ - protected[config] def processArguments(state: ArgsSummary, processAll: Boolean, skipped: List[String]): ArgsSummary = { + def processArguments(state: ArgsSummary, processAll: Boolean, skipped: List[String]): ArgsSummary = { def stateWithArgs(args: List[String]) = ArgsSummary(state.sstate, args, state.errors, state.warnings) state.arguments match { case Nil => @@ -269,8 +269,8 @@ object Settings { def BooleanSetting(name: String, descr: String, initialValue: Boolean = false): Setting[Boolean] = publish(Setting(name, descr, initialValue)) - def StringSetting(name: String, helpArg: String, descr: String, default: String): Setting[String] = - publish(Setting(name, descr, default, helpArg)) + def StringSetting(name: String, helpArg: String, descr: String, default: String, aliases: List[String] = Nil): Setting[String] = + publish(Setting(name, descr, default, helpArg, aliases = aliases)) def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String): Setting[String] = publish(Setting(name, descr, default, helpArg, choices)) diff --git a/scala3doc/src/dotty/dokka/Main.scala b/scala3doc/src/dotty/dokka/Main.scala index dfee60225ef7..7794021e2af2 100644 --- a/scala3doc/src/dotty/dokka/Main.scala +++ b/scala3doc/src/dotty/dokka/Main.scala @@ -12,66 +12,93 @@ import collection.immutable.ArraySeq import scala.tasty.inspector.TastyInspector import java.nio.file.Files -import org.kohsuke.args4j.{CmdLineParser, Option => COption} -import org.kohsuke.args4j.spi.StringArrayOptionHandler - -class RawArgs: - @COption(name="--tastyRoots", required = true, aliases = Array("-t"), usage="Roots where tools should look for tasty files") - protected var tastyRoots: String = null - - @COption(name="--dest",required = true, aliases = Array("-d"), usage="Output to generate documentation to") - protected var output: String = "output" - - @COption(name="--classpath", aliases = Array("--cp", "-c"), usage="Classpath to load dependencies from") - protected var classpath: String = System.getProperty("java.class.path") - - @COption(name="--name", required = true, aliases = Array("-n"), usage="Name of module in generated documentation") - protected var name: String = "main" - - @COption(name="--docs", aliases = Array("-p"), usage="Root of project docs") - private var docsRoot: String = null - - @COption(name="--sources", handler = classOf[StringArrayOptionHandler], aliases = Array("-s"), usage = "Links to source files provided in convention: local_directory=remote_directory#line_suffix") - private var sourceLinks: JList[String] = null - - @COption(name="--projectTitle") - protected var projectTitle: String = null - - @COption(name="--projectVersion") - protected var projectVersion: String = null - - @COption(name="--projectLogo") - protected var projectLogo: String = null - - @COption(name="--syntax") - protected var syntax: String = null - - @COption(name="--revision") - protected var revision: String = null - - def toArgs = - val parsedSyntax = syntax match - case null => None - case other => - Args.CommentSyntax.fromString(other) match - case None => - sys.error(s"unrecognized value for --syntax option: $other") - case some => some - - Args( - name, - tastyRoots.split(File.pathSeparatorChar).toList.map(new File(_)), - classpath, - new File(output), - Option(docsRoot), - Option(projectVersion), - Option(projectTitle), - Option(projectLogo), - parsedSyntax, - Option(sourceLinks).map(_.asScala.toList).getOrElse(List.empty), - Option(revision) - ) - +import dotty.tools.dotc.config.Settings._ + +abstract class Scala3Args extends SettingGroup: + val tastyRoots: Setting[String] = + StringSetting("--tastyRoots", "tastyRoots", "Roots where tools should look for tasty files", "", aliases = List("-t")) + val tastyRootsAlias: Setting[String] = + StringSetting("-t", "tastyRoots", "Roots where tools should look for tasty files", "", aliases = List("-t")) + + val dest: Setting[String] = + StringSetting("--dest", "dest", "Output to generate documentation to", "", aliases = List("-d")) + val destAlias: Setting[String] = + StringSetting("-d", "dest", "Output to generate documentation to", "") + + val classpath: Setting[String] = + StringSetting("--classpath", "classpath", "Classpath to load dependencies from", "", aliases = List("--cp", "-c")) + val classpathAlias1: Setting[String] = + StringSetting("--cp", "classpath", "Classpath to load dependencies from", "", aliases = List("--cp", "-c")) + val classpathAlias2: Setting[String] = + StringSetting("-c", "classpath", "Classpath to load dependencies from", "", aliases = List("--cp", "-c")) + + val name: Setting[String] = + StringSetting("--name", "name", "Name of module in generated documentation", "", aliases = List("-n")) + val nameAlias: Setting[String] = + StringSetting("-n", "name", "Name of module in generated documentation", "") + + val docsRoot: Setting[String] = + StringSetting("--docs", "docs", "Root of project docs", "", aliases = List("-p")) + val docsRootAlias: Setting[String] = + StringSetting("-p", "docs", "Root of project docs", "", aliases = List("-p")) + + + val sourceLinks: Setting[String] = + StringSetting("--sources", "sources", "Links to source files provided in convention: local_directory=remote_directory#line_suffix", "") + val sourceLinksAlias: Setting[String] = + StringSetting("-s", "sources", "Links to source files provided in convention: local_directory=remote_directory#line_suffix", "") + + val projectTitle: Setting[String] = + StringSetting("--projectTitle", "projectTitle", "Title of the project used in documentation", "") + + val projectVersion: Setting[String] = + StringSetting("--projectVersion", "projectVersion", "Version of the project used in documentation", "") + + val projectLogo: Setting[String] = + StringSetting("--projectLogo", "projectLogo", "Relative path to logo of the project", "") + + val revision: Setting[String] = + StringSetting("--revision", "revision", "Revision (branch or ref) used to build project project", "") + + val syntax: Setting[String] = + StringSetting("--syntax", "syntax", "Syntax of the comment used", "") + + protected def defaultName(): String + protected def defaultTastFiles(): List[File] + protected def defaultDest(): File + + def extract(args: List[String]) = + val initialSummary = ArgsSummary(defaultState, args, errors = Nil, warnings = Nil) + val res = processArguments(initialSummary, processAll = true, skipped = Nil) + // TODO! + if res.errors.nonEmpty then sys.error(s"Unable to parse arguments:\n ${res.errors.mkString("\n")}") + + val parsedSyntax = syntax.valueIn(res.sstate) match + case "" => None + case other => + Args.CommentSyntax.fromString(other) match + case None => + sys.error(s"unrecognized value for --syntax option: $other") + case some => some + + def parseOptionalArg(args: Setting[String]*) = + args.map(_.valueIn(res.sstate)).find(_ != "") + + def parseTastyRoots(roots: String) = roots.split(File.pathSeparatorChar).toList.map(new File(_)) + + Args( + parseOptionalArg(name, nameAlias).getOrElse(defaultName()), + parseOptionalArg(tastyRoots, tastyRootsAlias).fold(defaultTastFiles())(parseTastyRoots), + parseOptionalArg(classpath, classpathAlias1, classpathAlias2).getOrElse(System.getProperty("java.class.path")), + parseOptionalArg(dest, destAlias).fold(defaultDest())(new File(_)), + parseOptionalArg(docsRoot, docsRootAlias), + parseOptionalArg(projectVersion), + parseOptionalArg(projectTitle), + parseOptionalArg(projectLogo), + parsedSyntax, + parseOptionalArg(sourceLinks, sourceLinksAlias).fold(Nil)(_.split(",").toList), // TODO! + parseOptionalArg(revision) + ) case class Args( name: String, @@ -150,9 +177,12 @@ object Main: sys.exit(1) def main(args: Array[String]): Unit = - val rawArgs = new RawArgs - new CmdLineParser(rawArgs).parseArgument(args:_*) - main(rawArgs.toArgs) + val argDefinition = new Scala3Args { + protected def defaultName(): String = sys.error(s"Argument '${name.name}' is required") + protected def defaultTastFiles(): List[File] = sys.error(s"Argument '${tastyRoots.name}' is required") + protected def defaultDest(): File = sys.error(s"Argument '${dest.name}' is required") + } + main(argDefinition.extract(args.toList)) // Sometimes jvm is hanging, so we want to be sure that we force shout down the jvm sys.exit(0) diff --git a/scala3doc/src/dotty/tools/dottydoc/Main.scala b/scala3doc/src/dotty/tools/dottydoc/Main.scala index 0b5fc6e82a3a..da367bcc7a80 100644 --- a/scala3doc/src/dotty/tools/dottydoc/Main.scala +++ b/scala3doc/src/dotty/tools/dottydoc/Main.scala @@ -1,7 +1,7 @@ package dotty.tools package dottydoc -import dotty.dokka.{Args, RawArgs, DocConfiguration, DottyDokkaConfig} +import dotty.dokka.{Args, DocConfiguration, DottyDokkaConfig, Scala3Args} import org.jetbrains.dokka._ import org.jetbrains.dokka.utilities._ @@ -32,49 +32,18 @@ object Main extends Driver { * how they're split). */ override def process(args: Array[String], rootCtx: Context): Reporter = { - // split args into ours and Dotty's - val (dokkaStrArgs, compilerArgs) = { - args.partitionMap { arg => - // our options start with this magic prefix, inserted by the SBT plugin - val magicPrefix = "--+DOC+" - if arg startsWith magicPrefix then - Left(arg stripPrefix magicPrefix) - else - Right(arg) - } - } - val (filesToCompile, ctx) = setup(compilerArgs, rootCtx) - given Context = ctx - // parse Dokka args - // note: all required args should be set with SBT settings, - // to make it easier to set and override them - val dokkaArgs = { - val dokkaRawArgs = new RawArgs - val requiredArgs = Seq( - "--tastyRoots", "", // hack, value is not used in SBT but required in CLI - // we extract some settings from Dotty options since that's how SBT passes them - "--name", ctx.settings.projectName.value, - "--projectTitle", ctx.settings.projectName.value, - "--dest", ctx.settings.outputDir.value.toString, - ) + val (filesToCompile, ctx) = setup(args, rootCtx) + given Context = ctx - val allArgs = requiredArgs ++ dokkaStrArgs - println(s"Running scala3doc with arguments: $allArgs") - val parser = org.kohsuke.args4j.CmdLineParser(dokkaRawArgs) - try { - parser.parseArgument(allArgs : _*) - } catch { - case ex: org.kohsuke.args4j.CmdLineException => - // compiler errors are reported in SBT - dotc.report.error(s"Error when parsing Scala3doc options: ${ex.getMessage}") - throw ex - } - dokkaRawArgs.toArgs + val argDefinition = new Scala3Args() { + protected def defaultName(): String = ctx.settings.projectName.value + protected def defaultTastFiles(): List[File] = Nil + protected def defaultDest(): File = File(ctx.settings.outputDir.value.toString) } - val config = DocConfiguration.Sbt(dokkaArgs, filesToCompile, ctx) + val config = DocConfiguration.Sbt(argDefinition.extract(args.toList), filesToCompile, ctx) val dokkaCfg = new DottyDokkaConfig(config) new DokkaGenerator(dokkaCfg, DokkaConsoleLogger.INSTANCE).generate() From a77c2fede35b7cbbae435dec9f0187ef7203cf37 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Tue, 24 Nov 2020 02:46:02 +0100 Subject: [PATCH 02/19] fix todos, add missing arguments and descriptions --- scala3doc/src/dotty/dokka/Main.scala | 38 +++++++++++-------- scala3doc/src/dotty/dokka/SourceLinks.scala | 3 +- scala3doc/src/dotty/tools/dottydoc/Main.scala | 3 +- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/scala3doc/src/dotty/dokka/Main.scala b/scala3doc/src/dotty/dokka/Main.scala index 7794021e2af2..0f1ab4083d91 100644 --- a/scala3doc/src/dotty/dokka/Main.scala +++ b/scala3doc/src/dotty/dokka/Main.scala @@ -14,11 +14,12 @@ import java.nio.file.Files import dotty.tools.dotc.config.Settings._ -abstract class Scala3Args extends SettingGroup: +abstract class Scala3Args(reportError: String => Unit) extends SettingGroup: + private val tastyRootsUsage = "Roots where tools should look for tasty files. Directories and jars are accepted" val tastyRoots: Setting[String] = - StringSetting("--tastyRoots", "tastyRoots", "Roots where tools should look for tasty files", "", aliases = List("-t")) + StringSetting("--tastyRoots", "tastyRoots", tastyRootsUsage, "", aliases = List("-t")) val tastyRootsAlias: Setting[String] = - StringSetting("-t", "tastyRoots", "Roots where tools should look for tasty files", "", aliases = List("-t")) + StringSetting("-t", "tastyRoots", tastyRootsUsage, "", aliases = List("-t")) val dest: Setting[String] = StringSetting("--dest", "dest", "Output to generate documentation to", "", aliases = List("-d")) @@ -42,17 +43,24 @@ abstract class Scala3Args extends SettingGroup: val docsRootAlias: Setting[String] = StringSetting("-p", "docs", "Root of project docs", "", aliases = List("-p")) - val sourceLinks: Setting[String] = - StringSetting("--sources", "sources", "Links to source files provided in convention: local_directory=remote_directory#line_suffix", "") - val sourceLinksAlias: Setting[String] = - StringSetting("-s", "sources", "Links to source files provided in convention: local_directory=remote_directory#line_suffix", "") + StringSetting("--sources", "sources", SourceLinks.usage, "") + val sourceLinksAlias1: Setting[String] = + StringSetting("-s", "sources", SourceLinks.usage, "") + val sourceLinksAlias2: Setting[String] = + StringSetting("-doc-source-url", "sources", SourceLinks.usage, "") val projectTitle: Setting[String] = StringSetting("--projectTitle", "projectTitle", "Title of the project used in documentation", "") + val projectTitleAlias: Setting[String] = + StringSetting("-doc-title", "projectTitle", "Title of the project used in documentation", "") + val projectVersion: Setting[String] = StringSetting("--projectVersion", "projectVersion", "Version of the project used in documentation", "") + val projectVersionAlias: Setting[String] = + StringSetting("-doc-version", "projectVersion", "Version of the project used in documentation", "") + val projectLogo: Setting[String] = StringSetting("--projectLogo", "projectLogo", "Relative path to logo of the project", "") @@ -70,15 +78,16 @@ abstract class Scala3Args extends SettingGroup: def extract(args: List[String]) = val initialSummary = ArgsSummary(defaultState, args, errors = Nil, warnings = Nil) val res = processArguments(initialSummary, processAll = true, skipped = Nil) - // TODO! - if res.errors.nonEmpty then sys.error(s"Unable to parse arguments:\n ${res.errors.mkString("\n")}") + + if res.errors.nonEmpty then reportError(s"Unable to parse arguments:\n ${res.errors.mkString("\n")}") val parsedSyntax = syntax.valueIn(res.sstate) match case "" => None case other => Args.CommentSyntax.fromString(other) match case None => - sys.error(s"unrecognized value for --syntax option: $other") + reportError(s"unrecognized value for --syntax option: $other") + None case some => some def parseOptionalArg(args: Setting[String]*) = @@ -92,11 +101,11 @@ abstract class Scala3Args extends SettingGroup: parseOptionalArg(classpath, classpathAlias1, classpathAlias2).getOrElse(System.getProperty("java.class.path")), parseOptionalArg(dest, destAlias).fold(defaultDest())(new File(_)), parseOptionalArg(docsRoot, docsRootAlias), - parseOptionalArg(projectVersion), - parseOptionalArg(projectTitle), + parseOptionalArg(projectVersion, projectVersionAlias), + parseOptionalArg(projectTitle, projectTitleAlias), parseOptionalArg(projectLogo), parsedSyntax, - parseOptionalArg(sourceLinks, sourceLinksAlias).fold(Nil)(_.split(",").toList), // TODO! + parseOptionalArg(sourceLinks, sourceLinksAlias1, sourceLinksAlias2).fold(Nil)(_.split(",").toList), parseOptionalArg(revision) ) @@ -166,7 +175,6 @@ object Main: if (parsedArgs.output.exists()) IO.delete(parsedArgs.output) - // TODO #20 pass options, classpath etc. new DokkaGenerator(new DottyDokkaConfig(config), DokkaConsoleLogger.INSTANCE).generate() println("Done") @@ -177,7 +185,7 @@ object Main: sys.exit(1) def main(args: Array[String]): Unit = - val argDefinition = new Scala3Args { + val argDefinition = new Scala3Args(sys.error) { protected def defaultName(): String = sys.error(s"Argument '${name.name}' is required") protected def defaultTastFiles(): List[File] = sys.error(s"Argument '${tastyRoots.name}' is required") protected def defaultDest(): File = sys.error(s"Argument '${dest.name}' is required") diff --git a/scala3doc/src/dotty/dokka/SourceLinks.scala b/scala3doc/src/dotty/dokka/SourceLinks.scala index be66d794a7c1..156da4913987 100644 --- a/scala3doc/src/dotty/dokka/SourceLinks.scala +++ b/scala3doc/src/dotty/dokka/SourceLinks.scala @@ -90,7 +90,8 @@ case class SourceLinks(links: Seq[SourceLink], projectRoot: Path): object SourceLinks: val usage = - """Source links provide a mapping between file in documentation and code repositry. + """Source links provide a mapping between file in documentation and code repository. + | |Accepted formats: |= | diff --git a/scala3doc/src/dotty/tools/dottydoc/Main.scala b/scala3doc/src/dotty/tools/dottydoc/Main.scala index da367bcc7a80..2b8e9c2359ed 100644 --- a/scala3doc/src/dotty/tools/dottydoc/Main.scala +++ b/scala3doc/src/dotty/tools/dottydoc/Main.scala @@ -11,6 +11,7 @@ import dotc.core.Contexts._ import dotc.reporting.Reporter import dotc.{ Compiler, Driver } import dotc.config._ +import dotty.tools.dotc.report.error import dotty.tools.dotc.config.Settings.Setting.value @@ -37,7 +38,7 @@ object Main extends Driver { val (filesToCompile, ctx) = setup(args, rootCtx) given Context = ctx - val argDefinition = new Scala3Args() { + val argDefinition = new Scala3Args(error(_)) { protected def defaultName(): String = ctx.settings.projectName.value protected def defaultTastFiles(): List[File] = Nil protected def defaultDest(): File = File(ctx.settings.outputDir.value.toString) From fcb9b7ba42c32dc68a16fcc33108d6259369da51 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Tue, 24 Nov 2020 20:43:56 +0100 Subject: [PATCH 03/19] scala3doc has similar api like compiler use arguments extends of --tasty-root parameter --- project/Build.scala | 4 +- .../dotty/tools/sbtplugin/DottyPlugin.scala | 4 +- scala3doc/src/dotty/dokka/Main.scala | 64 ++++++++----------- scala3doc/src/dotty/tools/dottydoc/Main.scala | 15 ++--- 4 files changed, 34 insertions(+), 53 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 031a496a4b90..293196e0c60c 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1485,7 +1485,7 @@ object Build { def generateDocumentation(targets: String, name: String, outDir: String, ref: String, params: String = "") = Def.taskDyn { val projectVersion = version.value val sourcesAndRevision = s"-s github://lampepfl/dotty --revision $ref --projectVersion $projectVersion" - val cmd = s""" -d $outDir -t $targets -n "$name" $sourcesAndRevision $params""" + val cmd = s""" -d $outDir -n "$name" $sourcesAndRevision $params $targets""" run.in(Compile).toTask(cmd) } @@ -1538,7 +1538,7 @@ object Build { (`scala3-library-bootstrapped`/Compile/products).value, ).flatten - val roots = joinProducts(dottyJars) + val roots = dottyJars.mkString(" ") if (dottyJars.isEmpty) Def.task { streams.value.log.error("Dotty lib wasn't found") } else Def.task{ diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala index 368560276a26..77cca34ca3b0 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala @@ -362,11 +362,9 @@ object DottyPlugin extends AutoPlugin { useScala3doc := false, scala3docOptions := Nil, Compile / doc / scalacOptions := { - // We are passing scala3doc argument list as single argument to scala instance starting with magic prefix "--+DOC+" - val s3dOpts = scala3docOptions.value.map("--+DOC+" + _) val s3cOpts = (Compile / doc / scalacOptions).value if (isDotty.value && useScala3doc.value) { - s3dOpts ++ s3cOpts + scala3docOptions.value ++ s3cOpts } else { s3cOpts } diff --git a/scala3doc/src/dotty/dokka/Main.scala b/scala3doc/src/dotty/dokka/Main.scala index 0f1ab4083d91..79795bb12ed3 100644 --- a/scala3doc/src/dotty/dokka/Main.scala +++ b/scala3doc/src/dotty/dokka/Main.scala @@ -14,12 +14,7 @@ import java.nio.file.Files import dotty.tools.dotc.config.Settings._ -abstract class Scala3Args(reportError: String => Unit) extends SettingGroup: - private val tastyRootsUsage = "Roots where tools should look for tasty files. Directories and jars are accepted" - val tastyRoots: Setting[String] = - StringSetting("--tastyRoots", "tastyRoots", tastyRootsUsage, "", aliases = List("-t")) - val tastyRootsAlias: Setting[String] = - StringSetting("-t", "tastyRoots", tastyRootsUsage, "", aliases = List("-t")) +class Scala3Args(reportError: String => Unit) extends SettingGroup: val dest: Setting[String] = StringSetting("--dest", "dest", "Output to generate documentation to", "", aliases = List("-d")) @@ -29,7 +24,7 @@ abstract class Scala3Args(reportError: String => Unit) extends SettingGroup: val classpath: Setting[String] = StringSetting("--classpath", "classpath", "Classpath to load dependencies from", "", aliases = List("--cp", "-c")) val classpathAlias1: Setting[String] = - StringSetting("--cp", "classpath", "Classpath to load dependencies from", "", aliases = List("--cp", "-c")) + StringSetting("-classpath", "classpath", "Classpath to load dependencies from", "", aliases = List("--cp", "-c")) val classpathAlias2: Setting[String] = StringSetting("-c", "classpath", "Classpath to load dependencies from", "", aliases = List("--cp", "-c")) @@ -71,9 +66,7 @@ abstract class Scala3Args(reportError: String => Unit) extends SettingGroup: val syntax: Setting[String] = StringSetting("--syntax", "syntax", "Syntax of the comment used", "") - protected def defaultName(): String - protected def defaultTastFiles(): List[File] - protected def defaultDest(): File + protected def defaultDest(): File = sys.error(s"Argument '${dest.name}' is required") def extract(args: List[String]) = val initialSummary = ArgsSummary(defaultState, args, errors = Nil, warnings = Nil) @@ -95,11 +88,13 @@ abstract class Scala3Args(reportError: String => Unit) extends SettingGroup: def parseTastyRoots(roots: String) = roots.split(File.pathSeparatorChar).toList.map(new File(_)) + val tastyRoots = res.arguments.map(new File(_)) // check provided files + Args( - parseOptionalArg(name, nameAlias).getOrElse(defaultName()), - parseOptionalArg(tastyRoots, tastyRootsAlias).fold(defaultTastFiles())(parseTastyRoots), + parseOptionalArg(name, nameAlias).getOrElse("root"), + tastyRoots, parseOptionalArg(classpath, classpathAlias1, classpathAlias2).getOrElse(System.getProperty("java.class.path")), - parseOptionalArg(dest, destAlias).fold(defaultDest())(new File(_)), + parseOptionalArg(dest, destAlias).fold(defaultDest())(File(_)), parseOptionalArg(docsRoot, docsRootAlias), parseOptionalArg(projectVersion, projectVersionAlias), parseOptionalArg(projectTitle, projectTitleAlias), @@ -158,40 +153,35 @@ enum DocConfiguration extends BaseDocConfiguration: */ object Main: def main(parsedArgs: Args): Unit = - try - val (files, dirs) = parsedArgs.tastyRoots.partition(_.isFile) - val (providedTastyFiles, jars) = files.toList.map(_.getAbsolutePath).partition(_.endsWith(".tasty")) - jars.foreach(j => if(!j.endsWith(".jar")) sys.error(s"Provided file $j is not jar not tasty file") ) + val (files, dirs) = parsedArgs.tastyRoots.partition(_.isFile) + val (providedTastyFiles, jars) = files.toList.map(_.getAbsolutePath).partition(_.endsWith(".tasty")) + jars.foreach(j => if(!j.endsWith(".jar")) sys.error(s"Provided file $j is not jar not tasty file") ) + def listTastyFiles(f: File): Seq[String] = + val (files, dirs) = Option(f.listFiles()).toArray.flatten.partition(_.isFile) + ArraySeq.unsafeWrapArray( + files.filter(_.getName.endsWith(".tasty")).map(_.toString) ++ dirs.flatMap(listTastyFiles) + ) + val tastyFiles = providedTastyFiles ++ dirs.flatMap(listTastyFiles) - def listTastyFiles(f: File): Seq[String] = - val (files, dirs) = f.listFiles().partition(_.isFile) - ArraySeq.unsafeWrapArray( - files.filter(_.getName.endsWith(".tasty")).map(_.toString) ++ dirs.flatMap(listTastyFiles) - ) - val tastyFiles = providedTastyFiles ++ dirs.flatMap(listTastyFiles) + val config = DocConfiguration.Standalone(parsedArgs, tastyFiles, jars) - val config = DocConfiguration.Standalone(parsedArgs, tastyFiles, jars) + if (parsedArgs.output.exists()) IO.delete(parsedArgs.output) - if (parsedArgs.output.exists()) IO.delete(parsedArgs.output) + new DokkaGenerator(new DottyDokkaConfig(config), DokkaConsoleLogger.INSTANCE).generate() + println("Done") - new DokkaGenerator(new DottyDokkaConfig(config), DokkaConsoleLogger.INSTANCE).generate() - println("Done") + def main(args: Array[String]): Unit = + try + val argDefinition = new Scala3Args(sys.error) + main(argDefinition.extract(args.toList)) + // Sometimes jvm is hanging, so we want to be sure that we force shout down the jvm + sys.exit(0) catch case a: Exception => a.printStackTrace() // Sometimes jvm is hanging, so we want to be sure that we force shout down the jvm sys.exit(1) - def main(args: Array[String]): Unit = - val argDefinition = new Scala3Args(sys.error) { - protected def defaultName(): String = sys.error(s"Argument '${name.name}' is required") - protected def defaultTastFiles(): List[File] = sys.error(s"Argument '${tastyRoots.name}' is required") - protected def defaultDest(): File = sys.error(s"Argument '${dest.name}' is required") - } - main(argDefinition.extract(args.toList)) - // Sometimes jvm is hanging, so we want to be sure that we force shout down the jvm - sys.exit(0) - diff --git a/scala3doc/src/dotty/tools/dottydoc/Main.scala b/scala3doc/src/dotty/tools/dottydoc/Main.scala index 2b8e9c2359ed..13299dfb1231 100644 --- a/scala3doc/src/dotty/tools/dottydoc/Main.scala +++ b/scala3doc/src/dotty/tools/dottydoc/Main.scala @@ -33,20 +33,13 @@ object Main extends Driver { * how they're split). */ override def process(args: Array[String], rootCtx: Context): Reporter = { + given Context = rootCtx + val argDefinition = new Scala3Args(error(_)) + val parsedArgs = argDefinition.extract(args.toList) - val (filesToCompile, ctx) = setup(args, rootCtx) - given Context = ctx + dotty.dokka.Main.main(parsedArgs) - val argDefinition = new Scala3Args(error(_)) { - protected def defaultName(): String = ctx.settings.projectName.value - protected def defaultTastFiles(): List[File] = Nil - protected def defaultDest(): File = File(ctx.settings.outputDir.value.toString) - } - - val config = DocConfiguration.Sbt(argDefinition.extract(args.toList), filesToCompile, ctx) - val dokkaCfg = new DottyDokkaConfig(config) - new DokkaGenerator(dokkaCfg, DokkaConsoleLogger.INSTANCE).generate() rootCtx.reporter } From d7121170bf6eaa6109865bf8c667f27cbab53d03 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Wed, 25 Nov 2020 21:58:44 +0100 Subject: [PATCH 04/19] Handle arguments properly Use compiler reporter and provide our reporter for dokka Refactor how we handle arguments Unify sbt and cmd way to run documentation --- .../tools/dotc/config/ScalaSettings.scala | 107 ++++++------ project/Build.scala | 10 +- .../dotty/tools/sbtplugin/DottyPlugin.scala | 9 +- .../src/dotty/dokka/DottyDokkaConfig.scala | 14 +- .../src/dotty/dokka/DottyDokkaPlugin.scala | 4 +- scala3doc/src/dotty/dokka/Main.scala | 155 +----------------- scala3doc/src/dotty/dokka/Scala3doc.scala | 93 +++++++++++ scala3doc/src/dotty/dokka/Scala3docArgs.scala | 109 ++++++++++++ .../src/dotty/dokka/ScalaModuleCreator.scala | 27 +-- scala3doc/src/dotty/dokka/SourceLinks.scala | 6 +- .../ScalaResourceInstaller.scala | 2 +- .../dotty/dokka/site/StaticSiteContext.scala | 8 +- .../src/dotty/dokka/site/processors.scala | 2 +- .../dotty/dokka/tasty/ScalaDocSupport.scala | 11 +- .../src/dotty/dokka/tasty/TastyParser.scala | 76 +-------- .../tasty/comments/MarkdownConverter.scala | 4 + .../dotty/renderers/ScalaHtmlRenderer.scala | 2 +- scala3doc/src/dotty/tools/dottydoc/Main.scala | 32 +--- scala3doc/test/dotty/dokka/ScaladocTest.scala | 15 +- .../dokka/site/SiteGeneratationTest.scala | 18 +- 20 files changed, 328 insertions(+), 376 deletions(-) create mode 100644 scala3doc/src/dotty/dokka/Scala3doc.scala create mode 100644 scala3doc/src/dotty/dokka/Scala3docArgs.scala diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index fbce5ad1f02b..8b531a42f753 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -7,8 +7,8 @@ import PathResolver.Defaults import rewrites.Rewrites import Settings.Setting -class ScalaSettings extends Settings.SettingGroup { - +/** Settings shared by compiler and scala3doc */ +trait CommonScalaSettings { self: Settings.SettingGroup => protected def defaultClasspath: String = sys.env.getOrElse("CLASSPATH", ".") /** Path related settings */ @@ -18,33 +18,76 @@ class ScalaSettings extends Settings.SettingGroup { val javaextdirs: Setting[String] = PathSetting("-javaextdirs", "Override java extdirs classpath.", Defaults.javaExtDirs) withAbbreviation "--java-extension-directories" val sourcepath: Setting[String] = PathSetting("-sourcepath", "Specify location(s) of source files.", Defaults.scalaSourcePath) withAbbreviation "--source-path" val sourceroot: Setting[String] = PathSetting("-sourceroot", "Specify workspace root directory.", ".") - val semanticdbTarget: Setting[String] = PathSetting("-semanticdb-target", "Specify an alternative output directory for SemanticDB files.", "") val classpath: Setting[String] = PathSetting("-classpath", "Specify where to find user class files.", defaultClasspath) withAbbreviation "-cp" withAbbreviation "--class-path" val outputDir: Setting[AbstractFile] = OutputSetting("-d", "directory|jar", "Destination for generated classfiles.", new PlainDirectory(Directory("."))) val priorityclasspath: Setting[String] = PathSetting("-priorityclasspath", "Class path that takes precedence over all other paths (or testing only).", "") withAbbreviation "--priority-class-path" + val color: Setting[String] = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/) withAbbreviation "--color" + val verbose: Setting[Boolean] = BooleanSetting("-verbose", "Output messages about what the compiler is doing.") withAbbreviation "--verbose" + val version: Setting[Boolean] = BooleanSetting("-version", "Print product version and exit.") withAbbreviation "--version" + val pageWidth: Setting[Int] = IntSetting("-pagewidth", "Set page width", 80) withAbbreviation "--page-width" + val silentWarnings: Setting[Boolean] = BooleanSetting("-nowarn", "Silence all warnings.") withAbbreviation "--no-warnings" /** Other settings */ - val deprecation: Setting[Boolean] = BooleanSetting("-deprecation", "Emit warning and location for usages of deprecated APIs.") withAbbreviation "--deprecation" val encoding: Setting[String] = StringSetting("-encoding", "encoding", "Specify character encoding used by source files.", Properties.sourceEncoding) withAbbreviation "--encoding" + val usejavacp: Setting[Boolean] = BooleanSetting("-usejavacp", "Utilize the java.class.path in classpath resolution.") withAbbreviation "--use-java-class-path" + + /** Plugin-related setting */ + val plugin: Setting[List[String]] = MultiStringSetting ("-Xplugin", "paths", "Load a plugin from each classpath.") + val disable: Setting[List[String]] = MultiStringSetting ("-Xplugin-disable", "plugin", "Disable plugins by name.") + val require: Setting[List[String]] = MultiStringSetting ("-Xplugin-require", "plugin", "Abort if a named plugin is not loaded.") + val showPlugins: Setting[Boolean] = BooleanSetting ("-Xplugin-list", "Print a synopsis of loaded plugins.") + val pluginsDir: Setting[String] = StringSetting ("-Xpluginsdir", "path", "Path to search for plugin archives.", Defaults.scalaPluginPath) + val pluginOptions: Setting[List[String]] = MultiStringSetting ("-P", "plugin:opt", "Pass an option to a plugin, e.g. -P::") + + /** Doctool specific settings */ + val siteRoot: Setting[String] = StringSetting( + "-siteroot", + "site root", + "A directory containing static files from which to generate documentation.", + "./docs" + ) + + + val projectName: Setting[String] = StringSetting ( + "-project", + "project title", + "The name of the project.", + "" + ) + + val projectVersion: Setting[String] = StringSetting ( + "-project-version", + "project version", + "The current version of your project.", + "" + ) + + val projectLogo: Setting[String] = StringSetting( + "-project-logo", + "project logo filename", + "The file that contains the project's logo (in /images).", + "" + ) +} + +class ScalaSettings extends Settings.SettingGroup with CommonScalaSettings { + /** Path related settings */ + val semanticdbTarget: Setting[String] = PathSetting("-semanticdb-target", "Specify an alternative output directory for SemanticDB files.", "") + + val deprecation: Setting[Boolean] = BooleanSetting("-deprecation", "Emit warning and location for usages of deprecated APIs.") withAbbreviation "--deprecation" val explainTypes: Setting[Boolean] = BooleanSetting("-explain-types", "Explain type errors in more detail.") withAbbreviation "--explain-types" val explain: Setting[Boolean] = BooleanSetting("-explain", "Explain errors in more detail.") withAbbreviation "--explain" val feature: Setting[Boolean] = BooleanSetting("-feature", "Emit warning and location for usages of features that should be imported explicitly.") withAbbreviation "--feature" val help: Setting[Boolean] = BooleanSetting("-help", "Print a synopsis of standard options.") withAbbreviation "--help" - val color: Setting[String] = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/) withAbbreviation "--color" val source: Setting[String] = ChoiceSetting("-source", "source version", "source version", List("3.0", "3.1", "3.0-migration", "3.1-migration"), "3.0").withAbbreviation("--source") val target: Setting[String] = ChoiceSetting("-target", "target", "Target platform for object files.", List("jvm-1.8", "jvm-9"), "jvm-1.8") withAbbreviation "--target" val scalajs: Setting[Boolean] = BooleanSetting("-scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).") withAbbreviation "--scalajs" val unchecked: Setting[Boolean] = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.") withAbbreviation "--unchecked" val uniqid: Setting[Boolean] = BooleanSetting("-uniqid", "Uniquely tag all identifiers in debugging output.") withAbbreviation "--unique-id" - val usejavacp: Setting[Boolean] = BooleanSetting("-usejavacp", "Utilize the java.class.path in classpath resolution.") withAbbreviation "--use-java-class-path" - val verbose: Setting[Boolean] = BooleanSetting("-verbose", "Output messages about what the compiler is doing.") withAbbreviation "--verbose" - val version: Setting[Boolean] = BooleanSetting("-version", "Print product version and exit.") withAbbreviation "--version" - val pageWidth: Setting[Int] = IntSetting("-pagewidth", "Set page width", 80) withAbbreviation "--page-width" val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.") withAbbreviation "--language" val rewrite: Setting[Option[Rewrites]] = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with a `...-migration` source version, rewrites sources to migrate to new version.") withAbbreviation "--rewrite" - val silentWarnings: Setting[Boolean] = BooleanSetting("-nowarn", "Silence all warnings.") withAbbreviation "--no-warnings" val fromTasty: Setting[Boolean] = BooleanSetting("-from-tasty", "Compile classes from tasty files. The arguments are .tasty or .jar files.") withAbbreviation "--from-tasty" val newSyntax: Setting[Boolean] = BooleanSetting("-new-syntax", "Require `then` and `do` in control expressions.") @@ -57,20 +100,11 @@ class ScalaSettings extends Settings.SettingGroup { val printTasty: Setting[Boolean] = BooleanSetting("-print-tasty", "Prints the raw tasty.") withAbbreviation "--print-tasty" val printLines: Setting[Boolean] = BooleanSetting("-print-lines", "Show source code line numbers.") withAbbreviation "--print-lines" - /** Plugin-related setting */ - val plugin: Setting[List[String]] = MultiStringSetting ("-Xplugin", "paths", "Load a plugin from each classpath.") - val disable: Setting[List[String]] = MultiStringSetting ("-Xplugin-disable", "plugin", "Disable plugins by name.") - val require: Setting[List[String]] = MultiStringSetting ("-Xplugin-require", "plugin", "Abort if a named plugin is not loaded.") - val showPlugins: Setting[Boolean] = BooleanSetting ("-Xplugin-list", "Print a synopsis of loaded plugins.") - val pluginsDir: Setting[String] = StringSetting ("-Xpluginsdir", "path", "Path to search for plugin archives.", Defaults.scalaPluginPath) - val pluginOptions: Setting[List[String]] = MultiStringSetting ("-P", "plugin:opt", "Pass an option to a plugin, e.g. -P::") - /** Scala.js-related settings */ val scalajsGenStaticForwardersForNonTopLevelObjects: Setting[Boolean] = BooleanSetting("-scalajs-genStaticForwardersForNonTopLevelObjects", "Generate static forwarders even for non-top-level objects (Scala.js only)") val scalajsMapSourceURI: Setting[List[String]] = MultiStringSetting("-scalajs-mapSourceURI", "uri1[->uri2]", "rebases source URIs from uri1 to uri2 (or to a relative URI) for source maps (Scala.js only)") - /** -X "Advanced" settings - */ + /** -X "Advanced" settings */ val Xhelp: Setting[Boolean] = BooleanSetting("-X", "Print a synopsis of advanced options.") val XnoForwarders: Setting[Boolean] = BooleanSetting("-Xno-forwarders", "Do not generate static forwarders in mirror classes.") val XmaxInlines: Setting[Int] = IntSetting("-Xmax-inlines", "Maximal number of successive inlines.", 32) @@ -186,28 +220,8 @@ class ScalaSettings extends Settings.SettingGroup { val Yinstrument: Setting[Boolean] = BooleanSetting("-Yinstrument", "Add instrumentation code that counts allocations and closure creations.") val YinstrumentDefs: Setting[Boolean] = BooleanSetting("-Yinstrument-defs", "Add instrumentation code that counts method calls; needs -Yinstrument to be set, too.") - /** Dottydoc specific settings */ - val siteRoot: Setting[String] = StringSetting( - "-siteroot", - "site root", - "A directory containing static files from which to generate documentation.", - "./docs" - ) - - - val projectName: Setting[String] = StringSetting ( - "-project", - "project title", - "The name of the project.", - "" - ) - - val projectVersion: Setting[String] = StringSetting ( - "-project-version", - "project version", - "The current version of your project.", - "" - ) + /** Dottydoc specific settings that are not used in scala3doc */ + val docSnapshot: Setting[Boolean] = BooleanSetting("-doc-snapshot", "Generate a documentation snapshot for the current Dotty version") val projectUrl: Setting[String] = StringSetting ( "-project-url", @@ -216,14 +230,5 @@ class ScalaSettings extends Settings.SettingGroup { "" ) - val projectLogo: Setting[String] = StringSetting( - "-project-logo", - "project logo filename", - "The file that contains the project's logo (in /images).", - "" - ) - - val docSnapshot: Setting[Boolean] = BooleanSetting("-doc-snapshot", "Generate a documentation snapshot for the current Dotty version") - val wikiSyntax: Setting[Boolean] = BooleanSetting("-Xwiki-syntax", "Retains the Scala2 behavior of using Wiki Syntax in Scaladoc.") } diff --git a/project/Build.scala b/project/Build.scala index 293196e0c60c..781a64dd73a8 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1484,8 +1484,8 @@ object Build { def asScala3doc: Project = { def generateDocumentation(targets: String, name: String, outDir: String, ref: String, params: String = "") = Def.taskDyn { val projectVersion = version.value - val sourcesAndRevision = s"-s github://lampepfl/dotty --revision $ref --projectVersion $projectVersion" - val cmd = s""" -d $outDir -n "$name" $sourcesAndRevision $params $targets""" + val sourcesAndRevision = s"-source-links github://lampepfl/dotty -revision $ref -project-version $projectVersion" + val cmd = s""" -d $outDir -project "$name" $sourcesAndRevision $params $targets""" run.in(Compile).toTask(cmd) } @@ -1524,7 +1524,7 @@ object Build { generateDocumentation( classDirectory.in(Compile).value.getAbsolutePath, "scala3doc", "scala3doc/output/self", VersionUtil.gitHash, - "-p scala3doc/documentation --projectLogo scala3doc/documentation/logo.svg") + "-siteroot scala3doc/documentation -project-logo scala3doc/documentation/logo.svg") }.value, generateScala3Documentation := Def.inputTaskDyn { @@ -1548,7 +1548,7 @@ object Build { IO.write(dest / "CNAME", "dotty.epfl.ch") }.dependsOn(generateDocumentation( roots, "Scala 3", dest.getAbsolutePath, "master", - "-p scala3doc/scala3-docs --projectLogo scala3doc/scala3-docs/logo.svg")) + "-siteroot scala3doc/scala3-docs -project-logo scala3doc/scala3-docs/logo.svg")) }.evaluated, @@ -1562,7 +1562,7 @@ object Build { if (dottyJars.isEmpty) Def.task { streams.value.log.error("Dotty lib wasn't found") } else generateDocumentation( roots, "Scala 3", "scala3doc/output/scala3-stdlib", "maser", - "-p scala3doc/scala3-docs --syntax wiki --projectLogo scala3doc/scala3-docs/logo.svg " + "-siteroot scala3doc/scala3-docs -comment-syntax wiki -projec-logo scala3doc/scala3-docs/logo.svg " ) }.value, diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala index 77cca34ca3b0..d6821dd779b4 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala @@ -19,6 +19,7 @@ object DottyPlugin extends AutoPlugin { val isDottyJS = settingKey[Boolean]("Is this project compiled with Dotty and Scala.js?") val useScala3doc = settingKey[Boolean]("Use Scala3doc as the documentation tool") + val tastyFiles = taskKey[Seq[File]]("List all testy files") val scala3docOptions = settingKey[Seq[String]]("Options for Scala3doc") // NOTE: @@ -441,13 +442,15 @@ object DottyPlugin extends AutoPlugin { } private val docSettings = inTask(doc)(Seq( + tastyFiles := { + val _ = compile.value // Ensure that everything is compiled, so TASTy is available. + (classDirectory.value ** "*.tasty").get.map(_.getAbsoluteFile) + }, sources := Def.taskDyn { val old = sources.value if (isDotty.value) Def.task { - val _ = compile.value // Ensure that everything is compiled, so TASTy is available. - val tastyFiles = (classDirectory.value ** "*.tasty").get.map(_.getAbsoluteFile) - old ++ tastyFiles + old ++ tastyFiles.value } else Def.task { old } diff --git a/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala b/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala index d97d0fc475da..c143cedc13b7 100644 --- a/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala +++ b/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala @@ -6,8 +6,8 @@ import java.io.File import collection.JavaConverters._ import dotty.dokka.site.StaticSiteContext -case class DottyDokkaConfig(docConfiguration: DocConfiguration) extends DokkaConfiguration: - override def getOutputDir: File = docConfiguration.args.output +case class DottyDokkaConfig(args: Scala3doc.Args) extends DokkaConfiguration: + override def getOutputDir: File = args.output override def getCacheRoot: File = null override def getOfflineMode: Boolean = false override def getFailOnWarning: Boolean = false @@ -17,12 +17,12 @@ case class DottyDokkaConfig(docConfiguration: DocConfiguration) extends DokkaCon override def getModuleName(): String = "ModuleName" override def getModuleVersion(): String = "" - lazy val sourceLinks: SourceLinks = SourceLinks.load(docConfiguration) + lazy val sourceLinks: SourceLinks = SourceLinks.load(args) - lazy val staticSiteContext = docConfiguration.args.docsRoot.map(path => StaticSiteContext( + lazy val staticSiteContext = args.docsRoot.map(path => StaticSiteContext( File(path).getAbsoluteFile(), Set(mkSourceSet.asInstanceOf[SourceSetWrapper]), - docConfiguration.args, + args, sourceLinks )) @@ -30,8 +30,8 @@ case class DottyDokkaConfig(docConfiguration: DocConfiguration) extends DokkaCon lazy val mkSourceSet: DokkaSourceSet = new DokkaSourceSetImpl( - /*displayName=*/ docConfiguration.args.name, - /*sourceSetID=*/ new DokkaSourceSetID(docConfiguration.args.name, "main"), + /*displayName=*/ args.name, + /*sourceSetID=*/ new DokkaSourceSetID(args.name, "main"), /*classpath=*/ JList(), /*sourceRoots=*/ JSet(), /*dependentSourceSets=*/ JSet(), diff --git a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala index d4c1c2823072..830911d56271 100644 --- a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala +++ b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala @@ -14,7 +14,7 @@ import org.jetbrains.dokka.base.parsers._ import org.jetbrains.dokka.plugability.DokkaContext import collection.JavaConverters._ import org.jetbrains.dokka.model.properties.PropertyContainer -import dotty.dokka.tasty.{DokkaTastyInspector, SbtDokkaTastyInspector} +import dotty.dokka.tasty.DokkaTastyInspector import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter import org.jetbrains.dokka.utilities.DokkaLogger import org.jetbrains.dokka.base.signatures.SignatureProvider @@ -170,7 +170,7 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: extension (ctx: DokkaContext): def siteContext: Option[StaticSiteContext] = ctx.getConfiguration.asInstanceOf[DottyDokkaConfig].staticSiteContext - def args: Args = ctx.getConfiguration.asInstanceOf[DottyDokkaConfig].docConfiguration.args + def args: Scala3doc.Args = ctx.getConfiguration.asInstanceOf[DottyDokkaConfig].args // TODO (https://github.com/lampepfl/scala3doc/issues/232): remove once problem is fixed in Dokka extension [T] (builder: ExtensionBuilder[T]): diff --git a/scala3doc/src/dotty/dokka/Main.scala b/scala3doc/src/dotty/dokka/Main.scala index 79795bb12ed3..7145d2e4452e 100644 --- a/scala3doc/src/dotty/dokka/Main.scala +++ b/scala3doc/src/dotty/dokka/Main.scala @@ -13,132 +13,9 @@ import scala.tasty.inspector.TastyInspector import java.nio.file.Files import dotty.tools.dotc.config.Settings._ - -class Scala3Args(reportError: String => Unit) extends SettingGroup: - - val dest: Setting[String] = - StringSetting("--dest", "dest", "Output to generate documentation to", "", aliases = List("-d")) - val destAlias: Setting[String] = - StringSetting("-d", "dest", "Output to generate documentation to", "") - - val classpath: Setting[String] = - StringSetting("--classpath", "classpath", "Classpath to load dependencies from", "", aliases = List("--cp", "-c")) - val classpathAlias1: Setting[String] = - StringSetting("-classpath", "classpath", "Classpath to load dependencies from", "", aliases = List("--cp", "-c")) - val classpathAlias2: Setting[String] = - StringSetting("-c", "classpath", "Classpath to load dependencies from", "", aliases = List("--cp", "-c")) - - val name: Setting[String] = - StringSetting("--name", "name", "Name of module in generated documentation", "", aliases = List("-n")) - val nameAlias: Setting[String] = - StringSetting("-n", "name", "Name of module in generated documentation", "") - - val docsRoot: Setting[String] = - StringSetting("--docs", "docs", "Root of project docs", "", aliases = List("-p")) - val docsRootAlias: Setting[String] = - StringSetting("-p", "docs", "Root of project docs", "", aliases = List("-p")) - - val sourceLinks: Setting[String] = - StringSetting("--sources", "sources", SourceLinks.usage, "") - val sourceLinksAlias1: Setting[String] = - StringSetting("-s", "sources", SourceLinks.usage, "") - val sourceLinksAlias2: Setting[String] = - StringSetting("-doc-source-url", "sources", SourceLinks.usage, "") - - val projectTitle: Setting[String] = - StringSetting("--projectTitle", "projectTitle", "Title of the project used in documentation", "") - val projectTitleAlias: Setting[String] = - StringSetting("-doc-title", "projectTitle", "Title of the project used in documentation", "") - - - val projectVersion: Setting[String] = - StringSetting("--projectVersion", "projectVersion", "Version of the project used in documentation", "") - val projectVersionAlias: Setting[String] = - StringSetting("-doc-version", "projectVersion", "Version of the project used in documentation", "") - - - val projectLogo: Setting[String] = - StringSetting("--projectLogo", "projectLogo", "Relative path to logo of the project", "") - - val revision: Setting[String] = - StringSetting("--revision", "revision", "Revision (branch or ref) used to build project project", "") - - val syntax: Setting[String] = - StringSetting("--syntax", "syntax", "Syntax of the comment used", "") - - protected def defaultDest(): File = sys.error(s"Argument '${dest.name}' is required") - - def extract(args: List[String]) = - val initialSummary = ArgsSummary(defaultState, args, errors = Nil, warnings = Nil) - val res = processArguments(initialSummary, processAll = true, skipped = Nil) - - if res.errors.nonEmpty then reportError(s"Unable to parse arguments:\n ${res.errors.mkString("\n")}") - - val parsedSyntax = syntax.valueIn(res.sstate) match - case "" => None - case other => - Args.CommentSyntax.fromString(other) match - case None => - reportError(s"unrecognized value for --syntax option: $other") - None - case some => some - - def parseOptionalArg(args: Setting[String]*) = - args.map(_.valueIn(res.sstate)).find(_ != "") - - def parseTastyRoots(roots: String) = roots.split(File.pathSeparatorChar).toList.map(new File(_)) - - val tastyRoots = res.arguments.map(new File(_)) // check provided files - - Args( - parseOptionalArg(name, nameAlias).getOrElse("root"), - tastyRoots, - parseOptionalArg(classpath, classpathAlias1, classpathAlias2).getOrElse(System.getProperty("java.class.path")), - parseOptionalArg(dest, destAlias).fold(defaultDest())(File(_)), - parseOptionalArg(docsRoot, docsRootAlias), - parseOptionalArg(projectVersion, projectVersionAlias), - parseOptionalArg(projectTitle, projectTitleAlias), - parseOptionalArg(projectLogo), - parsedSyntax, - parseOptionalArg(sourceLinks, sourceLinksAlias1, sourceLinksAlias2).fold(Nil)(_.split(",").toList), - parseOptionalArg(revision) - ) - -case class Args( - name: String, - tastyRoots: Seq[File], - classpath: String, - output: File, - docsRoot: Option[String] = None, - projectVersion: Option[String] = None, - projectTitle: Option[String] = None, - projectLogo: Option[String] = None, - defaultSyntax: Option[Args.CommentSyntax] = None, - sourceLinks: List[String] = Nil, - revision: Option[String] = None -) - -object Args: - enum CommentSyntax: - case Wiki - case Markdown - - object CommentSyntax: - def fromString(str: String): Option[CommentSyntax] = - str match - case "wiki" => Some(Wiki) - case "markdown" => Some(Markdown) - case _ => None -end Args - -import dotty.tools.dotc.core.Contexts.{Context => DottyContext} -trait BaseDocConfiguration: - val args: Args - val tastyFiles: List[String] - -enum DocConfiguration extends BaseDocConfiguration: - case Standalone(args: Args, tastyFiles: List[String], tastyJars: List[String]) - case Sbt(args: Args, tastyFiles: List[String], rootCtx: DottyContext) +import dotty.tools.dotc.config.CommonScalaSettings +import dotty.tools.dotc.report +import dotty.tools.dotc.core.Contexts._ /** Main class for the doctool. * @@ -152,32 +29,12 @@ enum DocConfiguration extends BaseDocConfiguration: * - [](package.DottyDokkaConfig) is our config for Dokka. */ object Main: - def main(parsedArgs: Args): Unit = - val (files, dirs) = parsedArgs.tastyRoots.partition(_.isFile) - val (providedTastyFiles, jars) = files.toList.map(_.getAbsolutePath).partition(_.endsWith(".tasty")) - jars.foreach(j => if(!j.endsWith(".jar")) sys.error(s"Provided file $j is not jar not tasty file") ) - - def listTastyFiles(f: File): Seq[String] = - val (files, dirs) = Option(f.listFiles()).toArray.flatten.partition(_.isFile) - ArraySeq.unsafeWrapArray( - files.filter(_.getName.endsWith(".tasty")).map(_.toString) ++ dirs.flatMap(listTastyFiles) - ) - val tastyFiles = providedTastyFiles ++ dirs.flatMap(listTastyFiles) - - val config = DocConfiguration.Standalone(parsedArgs, tastyFiles, jars) - - if (parsedArgs.output.exists()) IO.delete(parsedArgs.output) - - new DokkaGenerator(new DottyDokkaConfig(config), DokkaConsoleLogger.INSTANCE).generate() - println("Done") - - def main(args: Array[String]): Unit = try - val argDefinition = new Scala3Args(sys.error) - main(argDefinition.extract(args.toList)) + // We should create our own context here... + val reporter = Scala3doc.run(args)(using (new ContextBase).initialCtx) // Sometimes jvm is hanging, so we want to be sure that we force shout down the jvm - sys.exit(0) + sys.exit(if reporter.hasErrors then 1 else 0) catch case a: Exception => a.printStackTrace() diff --git a/scala3doc/src/dotty/dokka/Scala3doc.scala b/scala3doc/src/dotty/dokka/Scala3doc.scala new file mode 100644 index 000000000000..8044c5798216 --- /dev/null +++ b/scala3doc/src/dotty/dokka/Scala3doc.scala @@ -0,0 +1,93 @@ +package dotty.dokka + +import org.jetbrains.dokka._ +import org.jetbrains.dokka.utilities._ +import org.jetbrains.dokka.plugability._ +import java.util.ServiceLoader +import java.io.File +import java.util.jar._ +import collection.JavaConverters._ +import collection.immutable.ArraySeq + +import scala.tasty.inspector.TastyInspector +import java.nio.file.Files + +import dotty.tools.dotc.config.Settings._ +import dotty.tools.dotc.config.CommonScalaSettings +import dotty.tools.dotc.report +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.reporting.Reporter + +class Scala3DocDokkaLogger(using Context) extends DokkaLogger: + def debug(msg: String): Unit = report.debuglog(msg) + + // We do not want errors from dokka (that are) not critical to fail our runs + def error(msg: String): Unit = + errors += 1 + report.warning(msg) + + def info(msg: String): Unit = report.inform(msg) + def progress(msg: String): Unit = report.informProgress(msg) + def warn(msg: String): Unit = + warnings += 1 + report.warning(msg) + + private var errors = 0 + private var warnings = 0 + def getErrorsCount(): Int = errors + def getWarningsCount(): Int = warnings + def setErrorsCount(count: Int): Unit = errors = count + def setWarningsCount(count: Int): Unit = warnings = count + +object Scala3doc: + enum CommentSyntax: + case Wiki + case Markdown + + object CommentSyntax: + def parse(str: String) = str match + case "wiki" => Some(CommentSyntax.Wiki) + case "markdown" => Some(CommentSyntax.Markdown) + case _ => None + + val default = CommentSyntax.Markdown + + case class Args( + name: String, + tastyDirs: Seq[File] = Nil, + tastyFiles: Seq[File] = Nil, + classpath: String = "", + output: File, + docsRoot: Option[String] = None, + projectVersion: Option[String] = None, + projectLogo: Option[String] = None, + defaultSyntax: CommentSyntax = CommentSyntax.Markdown, + sourceLinks: List[String] = Nil, + revision: Option[String] = None + ) + + def run(args: Array[String])(using Context): Reporter = + val parsedArgs = Scala3docArgs.extract(args.toList) + + def listTastyFiles(f: File): Seq[File] = + val (files, dirs) = Option(f.listFiles()).toArray.flatten.partition(_.isFile) + ArraySeq.unsafeWrapArray( + files.filter(_.getName.endsWith(".tasty")) ++ dirs.flatMap(listTastyFiles) + ) + val tastyFiles = parsedArgs.tastyFiles ++ parsedArgs.tastyDirs.flatMap(listTastyFiles) + + val reporter = summon[Context].reporter + if !reporter.hasErrors then + val updatedArgs = parsedArgs.copy(tastyDirs = Nil, tastyFiles = tastyFiles) + + if (parsedArgs.output.exists()) IO.delete(parsedArgs.output) + + run(updatedArgs, new Scala3DocDokkaLogger) + report.inform("Done") + else report.error("Failure") + reporter + + private [dokka] def run(args: Args, logger: DokkaLogger = DokkaConsoleLogger.INSTANCE) = + new DokkaGenerator(new DottyDokkaConfig(args), logger).generate() + + diff --git a/scala3doc/src/dotty/dokka/Scala3docArgs.scala b/scala3doc/src/dotty/dokka/Scala3docArgs.scala new file mode 100644 index 000000000000..18806b93c65f --- /dev/null +++ b/scala3doc/src/dotty/dokka/Scala3docArgs.scala @@ -0,0 +1,109 @@ +package dotty.dokka + +import org.jetbrains.dokka._ +import org.jetbrains.dokka.utilities._ +import org.jetbrains.dokka.plugability._ +import java.util.ServiceLoader +import java.io.File +import java.util.jar._ +import collection.JavaConverters._ +import collection.immutable.ArraySeq + +import scala.tasty.inspector.TastyInspector +import java.nio.file.Files + +import dotty.tools.dotc.config.Settings._ +import dotty.tools.dotc.config.CommonScalaSettings +import dotty.tools.dotc.report +import dotty.tools.dotc.core.Contexts._ +import dotty.dokka.Scala3doc._ + +class Scala3docArgs extends SettingGroup with CommonScalaSettings: + val unsupportedSettings = Seq( + // Options that we like to support + bootclasspath, extdirs, javabootclasspath, encoding, usejavacp, + // Needed for plugin architecture + plugin,disable,require, pluginsDir, pluginOptions, + // we need support for sourcepath and sourceroot + sourcepath, sourceroot + ) + + val sourceLinks: Setting[String] = + StringSetting("-source-links", "sources", SourceLinks.usage, "") + + val syntax: Setting[String] = + StringSetting("-comment-syntax", "syntax", "Syntax of the comment used", "") + + val revision: Setting[String] = + StringSetting("-revision", "revision", "Revision (branch or ref) used to build project project", "") + +object Scala3docArgs: + def extract(args: List[String])(using Context) = + val inst = new Scala3docArgs + import inst._ + val initialSummary = + ArgsSummary(defaultState, args, errors = Nil, warnings = Nil) + val summary = + processArguments(initialSummary, processAll = true, skipped = Nil) + + summary.warnings.foreach(report.warning(_)) + summary.errors.foreach(report.error(_)) + + extension[T](arg: Setting[T]): + def get = arg.valueIn(summary.sstate) + def withDefault(default: => T) = + if arg.get == arg.default then default else arg.get + def nonDefault = + if arg.get == arg.default then None else Some(arg.get) + + def parseTastyRoots(roots: String) = + roots.split(File.pathSeparatorChar).toList.map(new File(_)) + + val (existing, nonExisting) = + summary.arguments.map(File(_)).partition(_.exists) + + if nonExisting.nonEmpty then report.warning( + s"Scala3doc will ignore following nonexisiten paths: ${nonExisting.mkString(", ")}" + ) + + val (dirs, files) = existing.partition(_.isDirectory) + val (validFiles, other) = files.partition(f => + f.getName.endsWith(".tasty") || f.getName.endsWith(".jar") + ) + + if other.nonEmpty then report.warning( + s"Scala3doc suports only .tasty and .jar files, following files will be ignored: ${other.mkString(", ")}" + ) + + def defaultDest(): File = + report.error("Destenation is missing, please provide '-d' parameter poitning to director here docs should be created") + File("output") + + val parseSyntax = syntax.nonDefault.fold(CommentSyntax.default){ str => + CommentSyntax.parse(str).getOrElse{ + report.error(s"unrecognized value for -syntax option: $str") + CommentSyntax.default + } + } + + unsupportedSettings.filter(s => s.get != s.default).foreach { s => + report.warning(s"Setting ${s.name} is currently not supported.") + } + val destFile = outputDir.nonDefault.fold(defaultDest())(_.file) + val printableProjectName = projectName.nonDefault.fold("")("for " + _ ) + report.inform( + s"Generating documenation $printableProjectName in $destFile") + + Args( + projectName.withDefault("root"), + dirs, + validFiles, + classpath.get, + destFile, + siteRoot.nonDefault, + projectVersion.nonDefault, + projectLogo.nonDefault, + parseSyntax, + sourceLinks.nonDefault.fold(Nil)(_.split(",").toList), + revision.nonDefault + ) \ No newline at end of file diff --git a/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala b/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala index 30e217fc23ad..0040633ceb01 100644 --- a/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala +++ b/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala @@ -6,7 +6,7 @@ import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator -import dotty.dokka.tasty.{DokkaTastyInspector, SbtDokkaTastyInspector} +import dotty.dokka.tasty.DokkaTastyInspector import org.jetbrains.dokka.pages._ import dotty.dokka.model.api._ import org.jetbrains.dokka.model._ @@ -18,23 +18,14 @@ object ScalaModuleProvider extends SourceToDocumentableTranslator: override def invoke(sourceSet: DokkaSourceSet, cxt: DokkaContext, unused: Continuation[? >: DModule]) = cxt.getConfiguration match case dottyConfig: DottyDokkaConfig => - val result = dottyConfig.docConfiguration match { - case DocConfiguration.Standalone(args, tastyFiles, jars) => - // TODO use it to resolve link logic - val inspector = DokkaTastyInspector(sourceSet, new MarkdownParser(_ => null), dottyConfig) - inspector.inspectAllTastyFiles(tastyFiles, jars, args.classpath.split(java.io.File.pathSeparator).toList) - inspector.result() - case DocConfiguration.Sbt(args, tastyFiles, rootCtx) => - val inspector = - SbtDokkaTastyInspector( - sourceSet, - // new MarkdownParser(null, null, cxt.getLogger), - dottyConfig, - tastyFiles, - rootCtx, - ) - inspector.run() - } + val parser = new MarkdownParser(_ => null) + val inspector = DokkaTastyInspector(sourceSet, parser, dottyConfig) + val filePaths = dottyConfig.args.tastyFiles.map(_.getAbsolutePath) + val (tastyFiles, jars) = filePaths.toList.partition(_.endsWith(".tasty")) + val classpath = dottyConfig.args.classpath.split(java.io.File.pathSeparator).toList + + inspector.inspectAllTastyFiles(tastyFiles, jars, classpath) + val result = inspector.result() def flattenMember(m: Member): Seq[(DRI, Member)] = (m.dri -> m) +: m.allMembers.flatMap(flattenMember) diff --git a/scala3doc/src/dotty/dokka/SourceLinks.scala b/scala3doc/src/dotty/dokka/SourceLinks.scala index 156da4913987..d64395737ac4 100644 --- a/scala3doc/src/dotty/dokka/SourceLinks.scala +++ b/scala3doc/src/dotty/dokka/SourceLinks.scala @@ -132,10 +132,10 @@ object SourceLinks: SourceLinks(mappings.collect {case (_, Right(link)) => link}, projectRoot) - def load(config: DocConfiguration): SourceLinks = + def load(args: Scala3doc.Args): SourceLinks = load( - config.args.sourceLinks, - config.args.revision, + args.sourceLinks, + args.revision, // TODO (https://github.com/lampepfl/scala3doc/issues/240): configure source root Paths.get("").toAbsolutePath ) \ No newline at end of file diff --git a/scala3doc/src/dotty/dokka/preprocessors/ScalaResourceInstaller.scala b/scala3doc/src/dotty/dokka/preprocessors/ScalaResourceInstaller.scala index 151094880d01..8f4fdd6d5af8 100644 --- a/scala3doc/src/dotty/dokka/preprocessors/ScalaResourceInstaller.scala +++ b/scala3doc/src/dotty/dokka/preprocessors/ScalaResourceInstaller.scala @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import dotty.dokka.translators.FilterAttributes import java.nio.file.Paths -class ScalaResourceInstaller(args: Args) extends PageTransformer: +class ScalaResourceInstaller(args: Scala3doc.Args) extends PageTransformer: private def dottyRes(resourceName: String) = new RendererSpecificResourcePage(resourceName, java.util.ArrayList(), RenderingStrategy$Copy(s"/dotty_res/$resourceName")) diff --git a/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala b/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala index 12493020bc2f..cd42f0a80c7d 100644 --- a/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala +++ b/scala3doc/src/dotty/dokka/site/StaticSiteContext.scala @@ -20,7 +20,7 @@ import util.Try import scala.collection.JavaConverters._ -class StaticSiteContext(val root: File, sourceSets: Set[SourceSetWrapper], val args: Args, val sourceLinks: SourceLinks): +class StaticSiteContext(val root: File, sourceSets: Set[SourceSetWrapper], val args: Scala3doc.Args, val sourceLinks: SourceLinks): var memberLinkResolver: String => Option[DRI] = _ => None @@ -104,8 +104,7 @@ class StaticSiteContext(val root: File, sourceSets: Set[SourceSetWrapper], val a if from.getParentFile.toPath == docsPath && templateFile.isIndexPage() then // TODO (https://github.com/lampepfl/scala3doc/issues/238): provide proper error handling if templateFile.title != "index" then println(s"[WARN] title in $from will be overriden") - val projectTitle = args.projectTitle.getOrElse(args.name) - templateFile.copy(title = projectTitle) + templateFile.copy(title = args.name) else templateFile Some(LoadedTemplate(processedTemplate, processedChildren.toList, from)) @@ -182,5 +181,4 @@ class StaticSiteContext(val root: File, sourceSets: Set[SourceSetWrapper], val a val projectWideProperties = Seq("projectName" -> args.name) ++ - args.projectVersion.map("projectVersion" -> _) ++ - args.projectTitle.map("projectTitle" -> _) + args.projectVersion.map("projectVersion" -> _) diff --git a/scala3doc/src/dotty/dokka/site/processors.scala b/scala3doc/src/dotty/dokka/site/processors.scala index a65a1ec53e91..19c1720b6b09 100644 --- a/scala3doc/src/dotty/dokka/site/processors.scala +++ b/scala3doc/src/dotty/dokka/site/processors.scala @@ -103,7 +103,7 @@ class SitePagesCreator(ctx: Option[StaticSiteContext]) extends BaseStaticSitePro val rootContent = indexes.headOption.fold(ctx.asContent(Text(), mkDRI(extra = "root_content")).get(0))(_.getContent) val root = AContentPage( - ctx.args.projectTitle.getOrElse(ctx.args.name), + ctx.args.name, (List(modifiedModuleRoot.modified("API", modifiedModuleRoot.getChildren)) ++ children).asJava, rootContent, JSet(docsDRI), diff --git a/scala3doc/src/dotty/dokka/tasty/ScalaDocSupport.scala b/scala3doc/src/dotty/dokka/tasty/ScalaDocSupport.scala index 773ee6e61e9c..377c471c2ff1 100644 --- a/scala3doc/src/dotty/dokka/tasty/ScalaDocSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/ScalaDocSupport.scala @@ -4,7 +4,7 @@ import scala.jdk.CollectionConverters._ import org.jetbrains.dokka.model.{doc => dkkd} -import dotty.dokka.Args.CommentSyntax +import dotty.dokka.Scala3doc.CommentSyntax import dotty.dokka.ScalaTagWrapper import comments.{kt, dkk} @@ -34,12 +34,12 @@ trait ScaladocSupport { self: TastyParser => val commentSyntax = preparsed.syntax.headOption match { case Some(commentSetting) => - CommentSyntax.fromString(commentSetting).getOrElse { + CommentSyntax.parse(commentSetting).getOrElse { println(s"WARN: not a valid comment syntax: $commentSetting") println(s"WARN: Defaulting to Markdown syntax.") - CommentSyntax.Markdown + CommentSyntax.default } - case None => defaultCommentSyntax + case None => self.config.args.defaultSyntax } val parser = commentSyntax match { @@ -98,7 +98,4 @@ trait ScaladocSupport { self: TastyParser => new dkkd.DocumentationNode(bld.build()) } - - private val defaultCommentSyntax = - self.config.docConfiguration.args.defaultSyntax getOrElse CommentSyntax.Markdown } diff --git a/scala3doc/src/dotty/dokka/tasty/TastyParser.scala b/scala3doc/src/dotty/dokka/tasty/TastyParser.scala index f660a7222884..4287f0f048db 100644 --- a/scala3doc/src/dotty/dokka/tasty/TastyParser.scala +++ b/scala3doc/src/dotty/dokka/tasty/TastyParser.scala @@ -25,79 +25,7 @@ import scala.util.Try * * Delegates most of the work to [[TastyParser]] [[dotty.dokka.tasty.TastyParser]]. */ -case class DokkaTastyInspector(sourceSet: SourceSetWrapper, parser: Parser, config: DottyDokkaConfig) extends DokkaBaseTastyInspector with TastyInspector - -import dotty.tools.dotc.core.Contexts.{Context => DottyContext} -case class SbtDokkaTastyInspector( - sourceSet: SourceSetWrapper, - config: DottyDokkaConfig, - filesToDocument: List[String], - rootCtx: DottyContext, -) extends DokkaBaseTastyInspector: - self => - - import dotty.tools.dotc.Compiler - import dotty.tools.dotc.Driver - import dotty.tools.dotc.Run - import dotty.tools.dotc.core.Contexts.Context - import dotty.tools.dotc.core.Mode - import dotty.tools.dotc.core.Phases.Phase - import dotty.tools.dotc.fromtasty._ - import scala.quoted.runtime.impl.QuotesImpl - - - val parser: Parser = null - - def run(): List[DPackage] = { - val driver = new InspectorDriver - driver.run(filesToDocument)(rootCtx) - result() - } - - class InspectorDriver extends Driver: - override protected def newCompiler(implicit ctx: Context): Compiler = new TastyFromClass - - def run(filesToDocument: List[String])(implicit ctx: Context): Unit = - doCompile(newCompiler, filesToDocument) - - end InspectorDriver - - class TastyFromClass extends TASTYCompiler: - - override protected def frontendPhases: List[List[Phase]] = - List(new ReadTasty) :: // Load classes from tasty - Nil - - override protected def picklerPhases: List[List[Phase]] = Nil - - override protected def transformPhases: List[List[Phase]] = Nil - - override protected def backendPhases: List[List[Phase]] = - List(new TastyInspectorPhase) :: // Print all loaded classes - Nil - - override def newRun(implicit ctx: Context): Run = - reset() - new TASTYRun(this, ctx.fresh.addMode(Mode.ReadPositions).addMode(Mode.ReadComments)) - - end TastyFromClass - - class TastyInspectorPhase extends Phase: - - override def phaseName: String = "tastyInspector" - - override def run(implicit ctx: Context): Unit = - val qctx = QuotesImpl() - self.processCompilationUnit(using qctx)(ctx.compilationUnit.tpdTree.asInstanceOf[qctx.reflect.Tree]) - - end TastyInspectorPhase - -end SbtDokkaTastyInspector - -trait DokkaBaseTastyInspector: - val sourceSet: SourceSetWrapper - val parser: Parser - val config: DottyDokkaConfig +case class DokkaTastyInspector(sourceSet: SourceSetWrapper, parser: Parser, config: DottyDokkaConfig) extends TastyInspector: private val topLevels = Seq.newBuilder[Documentable] @@ -167,7 +95,7 @@ trait DokkaBaseTastyInspector: ) /** Parses a single Tasty compilation unit. */ -case class TastyParser(qctx: Quotes, inspector: DokkaBaseTastyInspector, config: DottyDokkaConfig) +case class TastyParser(qctx: Quotes, inspector: DokkaTastyInspector, config: DottyDokkaConfig) extends ScaladocSupport with BasicSupport with TypesSupport with ClassLikeSupport with SyntheticsSupport with PackageSupport with NameNormalizer: import qctx.reflect._ diff --git a/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala b/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala index b97b621b2827..d1536351adf8 100644 --- a/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala +++ b/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala @@ -161,6 +161,10 @@ class MarkdownConverter(val repr: Repr) extends BaseConverter { case _: mda.SoftLineBreak => emit(dkkd.Br.INSTANCE) + // TODO (https://github.com/lampepfl/scala3doc/issues/205): for now just silent the warnigs + case _:mda.HtmlInline | _: mda.LinkRef | _: mda.HtmlEntity => + emit(dkk.text(MarkdownParser.renderToText(n))) + case _ => println(s"WARN: Encountered unrecognised Markdown node `${n.getNodeName}`, please open an issue.") emit(dkk.text(MarkdownParser.renderToText(n))) diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index 5dff012f4afe..45415d448ae8 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -42,7 +42,7 @@ class SignatureRenderer(pageContext: ContentPage, sourceSetRestriciton: JSet[Dis def renderElement(e: String | (String, DRI) | Link) = renderElementWith(e) -class ScalaHtmlRenderer(ctx: DokkaContext, args: Args) extends HtmlRenderer(ctx) { +class ScalaHtmlRenderer(ctx: DokkaContext, args: Scala3doc.Args) extends HtmlRenderer(ctx) { // TODO #239 val hackScalaSearchbarDataInstaller: SearchbarDataInstaller = { diff --git a/scala3doc/src/dotty/tools/dottydoc/Main.scala b/scala3doc/src/dotty/tools/dottydoc/Main.scala index 13299dfb1231..069ca05f08a7 100644 --- a/scala3doc/src/dotty/tools/dottydoc/Main.scala +++ b/scala3doc/src/dotty/tools/dottydoc/Main.scala @@ -1,27 +1,16 @@ package dotty.tools package dottydoc -import dotty.dokka.{Args, DocConfiguration, DottyDokkaConfig, Scala3Args} -import org.jetbrains.dokka._ -import org.jetbrains.dokka.utilities._ -import org.jetbrains.dokka.plugability._ - -import dotc.core.Contexts._ import dotc.reporting.Reporter -import dotc.{ Compiler, Driver } -import dotc.config._ -import dotty.tools.dotc.report.error - -import dotty.tools.dotc.config.Settings.Setting.value - -import java.io.File +import dotc.Driver +import dotc.core.Contexts.Context /** Main object for SBT. * * See [[this.process]]. */ -object Main extends Driver { +object Main extends Driver: /** Actual entrypoint from SBT. * @@ -32,16 +21,5 @@ object Main extends Driver { * `args` contains arguments both for us and for the compiler (see code on * how they're split). */ - override def process(args: Array[String], rootCtx: Context): Reporter = { - given Context = rootCtx - - val argDefinition = new Scala3Args(error(_)) - val parsedArgs = argDefinition.extract(args.toList) - - dotty.dokka.Main.main(parsedArgs) - - - rootCtx.reporter - } - -} + override def process(args: Array[String], rootCtx: Context): Reporter = + dotty.dokka.Scala3doc.run(args)(using rootCtx) diff --git a/scala3doc/test/dotty/dokka/ScaladocTest.scala b/scala3doc/test/dotty/dokka/ScaladocTest.scala index d54c3c132d44..a95bc132161f 100644 --- a/scala3doc/test/dotty/dokka/ScaladocTest.scala +++ b/scala3doc/test/dotty/dokka/ScaladocTest.scala @@ -20,18 +20,11 @@ abstract class ScaladocTest(val name: String): folder.create() folder - private def args = Args( + private def args = Scala3doc.Args( name = "test", - tastyRoots = Nil , - classpath = System.getProperty("java.class.path"), - None, + tastyFiles = tastyFiles.map(new File(_)), output = getTempDir().getRoot, - projectVersion = Some("1.0"), - projectTitle = None, - projectLogo = None, - defaultSyntax = None, - sourceLinks = Nil, - revision = None + projectVersion = Some("1.0") ) private def tastyFiles = @@ -53,7 +46,7 @@ abstract class ScaladocTest(val name: String): @Test def executeTest = DokkaTestGenerator( - DottyDokkaConfig(DocConfiguration.Standalone(args, tastyFiles, Nil)), + DottyDokkaConfig(args), TestLogger(DokkaConsoleLogger.INSTANCE), assertions.asTestMethods, Nil.asJava diff --git a/scala3doc/test/dotty/dokka/site/SiteGeneratationTest.scala b/scala3doc/test/dotty/dokka/site/SiteGeneratationTest.scala index a1c125a37bb8..687b2a304b96 100644 --- a/scala3doc/test/dotty/dokka/site/SiteGeneratationTest.scala +++ b/scala3doc/test/dotty/dokka/site/SiteGeneratationTest.scala @@ -13,22 +13,18 @@ import java.nio.charset.Charset class SiteGeneratationTest: val projectName = "Test Project Name" - val projectTitle = "Test Project Title" val projectVersion = "1.0.1-M1" private def withGeneratedSite(base: Path)(op: Path => Unit) = val dest = Files.createTempDirectory("test-doc") try - val args = Args( + val args = Scala3doc.Args( name = projectName, - tastyRoots = Nil , - classpath = System.getProperty("java.class.path"), output = dest.toFile, docsRoot = Some(base.toAbsolutePath.toString), projectVersion = Some(projectVersion), - projectTitle = Some(projectTitle) ) - Main.main(args) + Scala3doc.run(args) op(dest) finally println(dest.toFile) // IO.delete(dest.toFile) @@ -65,11 +61,11 @@ class SiteGeneratationTest: } checkFile("index.html")(title = "Basic test", header = "Header") - checkFile("docs/Adoc.html")(title = "Adoc", header = "Header in Adoc", parents = Seq(projectTitle)) - checkFile("docs/Adoc.html")(title = "Adoc", header = "Header in Adoc", parents = Seq(projectTitle)) - checkFile("docs/dir/index.html")(title = "A directory", header = "A directory", parents = Seq(projectTitle)) + checkFile("docs/Adoc.html")(title = "Adoc", header = "Header in Adoc", parents = Seq(projectName)) + checkFile("docs/Adoc.html")(title = "Adoc", header = "Header in Adoc", parents = Seq(projectName)) + checkFile("docs/dir/index.html")(title = "A directory", header = "A directory", parents = Seq(projectName)) checkFile("docs/dir/nested.html")( - title = "Nested in a directory", header = "Nested in a directory", parents = Seq(projectTitle, "A directory")) + title = "Nested in a directory", header = "Nested in a directory", parents = Seq(projectName, "A directory")) - checkFile("docs/index.html")(title = projectTitle, header = s"$projectTitle in header") + checkFile("docs/index.html")(title = projectName, header = s"$projectName in header") } \ No newline at end of file From b2311cfeb77286ff6987f4b12ad1f3ef04bbd971 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Wed, 25 Nov 2020 22:30:16 +0100 Subject: [PATCH 05/19] Problem with package objects --- .../src/example/typeAndObjects/package.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 scala3doc-testcases/src/example/typeAndObjects/package.scala diff --git a/scala3doc-testcases/src/example/typeAndObjects/package.scala b/scala3doc-testcases/src/example/typeAndObjects/package.scala new file mode 100644 index 000000000000..d1f68cae1d5a --- /dev/null +++ b/scala3doc-testcases/src/example/typeAndObjects/package.scala @@ -0,0 +1,12 @@ +package example + +// Ala fails Ola does not +package object typeAndObjects: + type Ala + +package typeAndObjects { + object Ala +} + +type Ola +object Ola \ No newline at end of file From 05d8d1188512c8b61726194025fe769ccbe83307 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Thu, 26 Nov 2020 20:12:31 +0100 Subject: [PATCH 06/19] Fix generate* tasks in scala3doc --- project/Build.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/project/Build.scala b/project/Build.scala index 781a64dd73a8..3e5aa1d72fd7 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1484,6 +1484,7 @@ object Build { def asScala3doc: Project = { def generateDocumentation(targets: String, name: String, outDir: String, ref: String, params: String = "") = Def.taskDyn { val projectVersion = version.value + IO.createDirectory(file(outDir)) val sourcesAndRevision = s"-source-links github://lampepfl/dotty -revision $ref -project-version $projectVersion" val cmd = s""" -d $outDir -project "$name" $sourcesAndRevision $params $targets""" run.in(Compile).toTask(cmd) From 36e300df8d02cd2b12ce78667db21207d49eadcd Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Thu, 26 Nov 2020 20:14:07 +0100 Subject: [PATCH 07/19] Simply sources config for doc task --- .../src/dotty/tools/sbtplugin/DottyPlugin.scala | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala index d6821dd779b4..d374125c5280 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala @@ -446,16 +446,10 @@ object DottyPlugin extends AutoPlugin { val _ = compile.value // Ensure that everything is compiled, so TASTy is available. (classDirectory.value ** "*.tasty").get.map(_.getAbsoluteFile) }, - sources := Def.taskDyn { - val old = sources.value - - if (isDotty.value) Def.task { - old ++ tastyFiles.value - } else Def.task { - old - } + sources ++= Def.taskDyn[Seq[File]] { + if (isDotty.value) Def.task { tastyFiles.value } + else Def.task { Nil } }.value, - scalacOptions ++= { if (isDotty.value) { val projectName = From fb8e7f63e9857e67bdb4fe6385c372e2db92a330 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Thu, 26 Nov 2020 21:10:14 +0100 Subject: [PATCH 08/19] Sinlience out Kotlin analysis in scala3doc --- scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala index 830911d56271..4b37b45ae364 100644 --- a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala +++ b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala @@ -58,6 +58,7 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: val cleanUpEmptyModules = extend( _.extensionPoint(CoreExtensions.INSTANCE.getPreMergeDocumentableTransformer) .fromInstance(_.asScala.filterNot(_.getName.isEmpty).asJava) + .overrideExtension(dokkaBase.getModulesAndPackagesDocumentation) ) val ourSignatureProvider = extend( @@ -99,6 +100,7 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: _.extensionPoint(CoreExtensions.INSTANCE.getPageTransformer) .fromRecipe(PackageHierarchyTransformer(_)) .before(dokkaBase.getRootCreator) + .overrideExtension(dokkaBase.getDefaultSamplesTransformer) ) val inheritanceTransformer = extend( From 30ce3c20a0b7a7eabebebb83e7be76af9cb0a039 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Thu, 26 Nov 2020 21:12:06 +0100 Subject: [PATCH 09/19] Make sure that project does not hang in case of errors --- scala3doc/src/dotty/dokka/Scala3docArgs.scala | 2 +- .../dotty/dokka/site/StaticSiteLocationProvider.scala | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/scala3doc/src/dotty/dokka/Scala3docArgs.scala b/scala3doc/src/dotty/dokka/Scala3docArgs.scala index 18806b93c65f..05ab6ab4f084 100644 --- a/scala3doc/src/dotty/dokka/Scala3docArgs.scala +++ b/scala3doc/src/dotty/dokka/Scala3docArgs.scala @@ -76,7 +76,7 @@ object Scala3docArgs: ) def defaultDest(): File = - report.error("Destenation is missing, please provide '-d' parameter poitning to director here docs should be created") + report.error("Destenation is missing, please provide '-d' parameter pointing to directory here docs should be created") File("output") val parseSyntax = syntax.nonDefault.fold(CommentSyntax.default){ str => diff --git a/scala3doc/src/dotty/dokka/site/StaticSiteLocationProvider.scala b/scala3doc/src/dotty/dokka/site/StaticSiteLocationProvider.scala index b82e173523b6..cfa1b9c6861e 100644 --- a/scala3doc/src/dotty/dokka/site/StaticSiteLocationProvider.scala +++ b/scala3doc/src/dotty/dokka/site/StaticSiteLocationProvider.scala @@ -15,7 +15,15 @@ import java.nio.file.Path class StaticSiteLocationProviderFactory(private val ctx: DokkaContext) extends LocationProviderFactory: override def getLocationProvider(pageNode: RootPageNode): LocationProvider = - new StaticSiteLocationProvider(ctx, pageNode) + try new StaticSiteLocationProvider(ctx, pageNode) + catch + case e: Error => + // TODO (https://github.com/lampepfl/scala3doc/issues/238) error handling + e.printStackTrace() + // We encounter bug in Kotlin coroutines (race) when this method throws exception + // In such case we want to return null to trigger NPE in other piece of code to fail properly coroutine context + // Making generated DRIs not-unique will reproduce this behavior + null class StaticSiteLocationProvider(ctx: DokkaContext, pageNode: RootPageNode) extends DokkaLocationProvider(pageNode, ctx, ".html"): From 60931264472bee1124dd2a40913839a40ee24363 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Thu, 26 Nov 2020 21:14:05 +0100 Subject: [PATCH 10/19] Make sure that our DRIs are unique --- scala3doc/src/dotty/dokka/tasty/BasicSupport.scala | 2 +- scala3doc/src/dotty/dokka/tasty/SymOps.scala | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala b/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala index 2b59f5ff13f5..24a27e15f18a 100644 --- a/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/BasicSupport.scala @@ -45,6 +45,6 @@ trait BasicSupport: path.map(TastyDocumentableSource(_, sym.pos.startLine)) def getAnnotations(): List[Annotation] = - sym.annots.filterNot(_.symbol.packageName.startsWith("scala.annotation.internal")).map(parseAnnotation).reverse + sym.annots.filterNot(_.symbol.packageName.startsWith("scala.annotation.internal")).map(parseAnnotation).reverse diff --git a/scala3doc/src/dotty/dokka/tasty/SymOps.scala b/scala3doc/src/dotty/dokka/tasty/SymOps.scala index 97664bfe1e59..a15992fa63b3 100644 --- a/scala3doc/src/dotty/dokka/tasty/SymOps.scala +++ b/scala3doc/src/dotty/dokka/tasty/SymOps.scala @@ -94,7 +94,9 @@ class SymOps[Q <: Quotes](val q: Q): // TODO #22 make sure that DRIs are unique plus probably reuse semantic db code? def dri: DRI = - if sym == Symbol.noSymbol then emptyDRI else if sym.isValDef && sym.moduleClass.exists then sym.moduleClass.dri else + if sym == Symbol.noSymbol then emptyDRI + else if sym.isValDef && sym.moduleClass.exists then sym.moduleClass.dri + else val pointsTo = if (!sym.isTypeDef) PointingToDeclaration.INSTANCE else PointingToGenericParameters(sym.owner.typeMembers.indexOf(sym)) @@ -108,8 +110,10 @@ class SymOps[Q <: Quotes](val q: Q): sym.packageName, sym.topLevelEntryName.orNull, // TODO do we need any of this fields? method.map(s => new org.jetbrains.dokka.links.Callable(s.name, null, JList())).orNull, - pointsTo, // TODO different targets? - s"${sym.show}/${sym.signature.resultSig}/[${sym.signature.paramSigs.mkString("/")}]" + pointsTo, + // sym.show returns the same signature for def << = 1 and def >> = 2. + // For some reason it contains `$$$` instrad of symbol name + s"${sym.name}${sym.show}/${sym.signature.resultSig}/[${sym.signature.paramSigs.mkString("/")}]" ) private val emptyDRI = DRI.Companion.getTopLevel From 565d8d97281e1f4a31cab2168f0803d7dd8f3d17 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Thu, 26 Nov 2020 21:16:26 +0100 Subject: [PATCH 11/19] Add support for another shape of Lambda types --- .../src/example/typeAndObjects/binaryops.scala | 14 ++++++++++++++ scala3doc-testcases/src/tests/typeLambdas.scala | 4 +++- scala3doc/src/dotty/dokka/tasty/TypesSupport.scala | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 scala3doc-testcases/src/example/typeAndObjects/binaryops.scala diff --git a/scala3doc-testcases/src/example/typeAndObjects/binaryops.scala b/scala3doc-testcases/src/example/typeAndObjects/binaryops.scala new file mode 100644 index 000000000000..890d39732ca8 --- /dev/null +++ b/scala3doc-testcases/src/example/typeAndObjects/binaryops.scala @@ -0,0 +1,14 @@ +package example.typeAndObjects + + +sealed trait Expr + +object Expr{ + case class BinaryOp(offset: Int, lhs: Expr, op: BinaryOp.Op, rhs: Expr) extends Expr + + object BinaryOp{ + sealed trait Op + case object `<<` extends Op + case object `>>` extends Op + } +} \ No newline at end of file diff --git a/scala3doc-testcases/src/tests/typeLambdas.scala b/scala3doc-testcases/src/tests/typeLambdas.scala index 78e270aae76f..3aa0f95f442b 100644 --- a/scala3doc-testcases/src/tests/typeLambdas.scala +++ b/scala3doc-testcases/src/tests/typeLambdas.scala @@ -10,4 +10,6 @@ type Id[T <: AnyKind] type TL1 = Id[[X, Y] =>> Map[X,Y]] -type TL2 = Id[[X >: Int] =>> [Y <: String] =>> Map[X, Y]] \ No newline at end of file +type TL2 = Id[[X >: Int] =>> [Y <: String] =>> Map[X, Y]] + +type LabdaA = List[(x: String) => List[x.type]] diff --git a/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala b/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala index 6d19a78c1d96..e1e06e15c376 100644 --- a/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/TypesSupport.scala @@ -243,6 +243,8 @@ trait TypesSupport: inner(sc) ++ texts(" match {\n") ++ casesTexts ++ texts("}") case ParamRef(TypeLambda(names, _, _), i) => texts(names.apply(i)) + + case ParamRef(m: MethodType, i) => texts(m.paramNames(i)) case RecursiveType(tp) => inner(tp) From 2612aca840c4724a6963c7082ac25f5e8641183c Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Thu, 26 Nov 2020 21:16:57 +0100 Subject: [PATCH 12/19] Removing failing file and silence down a lot of errors --- .../src/example/typeAndObjects/package.scala | 12 ------------ .../dokka/tasty/comments/MarkdownConverter.scala | 4 ++-- 2 files changed, 2 insertions(+), 14 deletions(-) delete mode 100644 scala3doc-testcases/src/example/typeAndObjects/package.scala diff --git a/scala3doc-testcases/src/example/typeAndObjects/package.scala b/scala3doc-testcases/src/example/typeAndObjects/package.scala deleted file mode 100644 index d1f68cae1d5a..000000000000 --- a/scala3doc-testcases/src/example/typeAndObjects/package.scala +++ /dev/null @@ -1,12 +0,0 @@ -package example - -// Ala fails Ola does not -package object typeAndObjects: - type Ala - -package typeAndObjects { - object Ala -} - -type Ola -object Ola \ No newline at end of file diff --git a/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala b/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala index d1536351adf8..e9b4014de324 100644 --- a/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala +++ b/scala3doc/src/dotty/dokka/tasty/comments/MarkdownConverter.scala @@ -162,7 +162,7 @@ class MarkdownConverter(val repr: Repr) extends BaseConverter { case _: mda.SoftLineBreak => emit(dkkd.Br.INSTANCE) // TODO (https://github.com/lampepfl/scala3doc/issues/205): for now just silent the warnigs - case _:mda.HtmlInline | _: mda.LinkRef | _: mda.HtmlEntity => + case _:mda.HtmlInline | _: mda.LinkRef | _: mda.HtmlEntity | _: mda.HtmlBlock | _: com.vladsch.flexmark.ext.emoji.Emoji => emit(dkk.text(MarkdownParser.renderToText(n))) case _ => @@ -185,7 +185,7 @@ class MarkdownConverter(val repr: Repr) extends BaseConverter { case Some((sym, targetText)) => dkkd.DocumentationLink(sym.dri, resolveBody(default = targetText), kt.emptyMap) case None => - println(s"WARN: Definition lookup for following query failed: $queryStr") + // println(s"WARN: Definition lookup for following query failed: $queryStr") dkkd.A(resolveBody(default = query.join), Map("title" -> s"Definition was not found: $queryStr", "href" -> "#").asJava) } } From 57c36af264b6a7f6dc1fd7f3d4d5799cd7076fbd Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Fri, 27 Nov 2020 15:06:38 +0100 Subject: [PATCH 13/19] Fix typo and remove unused setting --- project/Build.scala | 2 +- .../src/dotty/tools/sbtplugin/DottyPlugin.scala | 12 +----------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 3e5aa1d72fd7..614719e8cfa6 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1563,7 +1563,7 @@ object Build { if (dottyJars.isEmpty) Def.task { streams.value.log.error("Dotty lib wasn't found") } else generateDocumentation( roots, "Scala 3", "scala3doc/output/scala3-stdlib", "maser", - "-siteroot scala3doc/scala3-docs -comment-syntax wiki -projec-logo scala3doc/scala3-docs/logo.svg " + "-siteroot scala3doc/scala3-docs -comment-syntax wiki -project-logo scala3doc/scala3-docs/logo.svg " ) }.value, diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala index d374125c5280..fae1ee09d160 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala @@ -20,7 +20,6 @@ object DottyPlugin extends AutoPlugin { val useScala3doc = settingKey[Boolean]("Use Scala3doc as the documentation tool") val tastyFiles = taskKey[Seq[File]]("List all testy files") - val scala3docOptions = settingKey[Seq[String]]("Options for Scala3doc") // NOTE: // - this is a def to support `scalaVersion := dottyLatestNightlyBuild` @@ -361,15 +360,6 @@ object DottyPlugin extends AutoPlugin { // Configuration for the doctool resolvers ++= (if(!useScala3doc.value) Nil else Seq(Resolver.jcenterRepo)), useScala3doc := false, - scala3docOptions := Nil, - Compile / doc / scalacOptions := { - val s3cOpts = (Compile / doc / scalacOptions).value - if (isDotty.value && useScala3doc.value) { - scala3docOptions.value ++ s3cOpts - } else { - s3cOpts - } - }, // We need to add doctool classes to the classpath so they can be called scalaInstance in doc := Def.taskDyn { if (isDotty.value) @@ -447,7 +437,7 @@ object DottyPlugin extends AutoPlugin { (classDirectory.value ** "*.tasty").get.map(_.getAbsoluteFile) }, sources ++= Def.taskDyn[Seq[File]] { - if (isDotty.value) Def.task { tastyFiles.value } + if (isDotty.value) Def.task { tastyFiles.value } else Def.task { Nil } }.value, scalacOptions ++= { From af92734cd923fcff9e714feed4d06afbf6460809 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Fri, 27 Nov 2020 19:57:56 +0100 Subject: [PATCH 14/19] Clean up imports --- scala3doc/src/dotty/dokka/Main.scala | 1 - scala3doc/src/dotty/dokka/Scala3docArgs.scala | 1 - 2 files changed, 2 deletions(-) diff --git a/scala3doc/src/dotty/dokka/Main.scala b/scala3doc/src/dotty/dokka/Main.scala index 7145d2e4452e..491b05b677f9 100644 --- a/scala3doc/src/dotty/dokka/Main.scala +++ b/scala3doc/src/dotty/dokka/Main.scala @@ -9,7 +9,6 @@ import java.util.jar._ import collection.JavaConverters._ import collection.immutable.ArraySeq -import scala.tasty.inspector.TastyInspector import java.nio.file.Files import dotty.tools.dotc.config.Settings._ diff --git a/scala3doc/src/dotty/dokka/Scala3docArgs.scala b/scala3doc/src/dotty/dokka/Scala3docArgs.scala index 05ab6ab4f084..2d5043d264c8 100644 --- a/scala3doc/src/dotty/dokka/Scala3docArgs.scala +++ b/scala3doc/src/dotty/dokka/Scala3docArgs.scala @@ -9,7 +9,6 @@ import java.util.jar._ import collection.JavaConverters._ import collection.immutable.ArraySeq -import scala.tasty.inspector.TastyInspector import java.nio.file.Files import dotty.tools.dotc.config.Settings._ From e226aa8994f881fe57c61a5511b1d7601a8411a9 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Fri, 27 Nov 2020 19:58:25 +0100 Subject: [PATCH 15/19] Tasty inspector is able to run using provided context --- .../tasty/inspector/TastyInspector.scala | 67 +++++++++++-------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala b/tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala index aaca40307e81..af4c55518a3b 100644 --- a/tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala +++ b/tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala @@ -13,6 +13,7 @@ import dotty.tools.dotc.fromtasty._ import dotty.tools.dotc.util.ClasspathFromClassloader import dotty.tools.dotc.CompilationUnit import dotty.tools.unsupported +import dotty.tools.dotc.report import java.io.File.pathSeparator @@ -57,13 +58,39 @@ trait TastyInspector: val files = tastyFiles ::: jars files.nonEmpty && inspectFiles(dependenciesClasspath, files) - private def inspectFiles(classpath: List[String], classes: List[String]): Boolean = - if (classes.isEmpty) - throw new IllegalArgumentException("Parameter classes should no be empty") + /** Load and process TASTy files using TASTy reflect and provided context + * + * Used in doctool to reuse reporter and setup provided by sbt + * + * @param classes List of paths of `.tasty` and `.jar` files (no validation is performed) + * @param classpath Classpath with extra dependencies needed to load class in the `.tasty` files + */ + protected def inspectFilesInContext(classpath: List[String], classes: List[String])(using Context): Unit = + if (classes.isEmpty) report.error("Parameter classes should no be empty") + inspectorDriver().process(inspectorArgs(classpath, classes), summon[Context]) + + private def inspectorDriver() = class InspectorDriver extends Driver: override protected def newCompiler(implicit ctx: Context): Compiler = new TastyFromClass + class TastyInspectorPhase extends Phase: + override def phaseName: String = "tastyInspector" + + override def run(implicit ctx: Context): Unit = + val qctx = QuotesImpl() + self.processCompilationUnit(using qctx)(ctx.compilationUnit.tpdTree.asInstanceOf[qctx.reflect.Tree]) + + class TastyInspectorFinishPhase extends Phase: + override def phaseName: String = "tastyInspectorFinish" + + override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] = + val qctx = QuotesImpl() + self.postProcess(using qctx) + units + + override def run(implicit ctx: Context): Unit = unsupported("run") + class TastyFromClass extends TASTYCompiler: override protected def frontendPhases: List[List[Phase]] = @@ -83,35 +110,19 @@ trait TastyInspector: reset() new TASTYRun(this, ctx.fresh.addMode(Mode.ReadPositions).addMode(Mode.ReadComments)) - end TastyFromClass + new InspectorDriver - class TastyInspectorPhase extends Phase: - - override def phaseName: String = "tastyInspector" - - override def run(implicit ctx: Context): Unit = - val qctx = QuotesImpl() - self.processCompilationUnit(using qctx)(ctx.compilationUnit.tpdTree.asInstanceOf[qctx.reflect.Tree]) - - end TastyInspectorPhase - - class TastyInspectorFinishPhase extends Phase: - - override def phaseName: String = "tastyInspectorFinish" - - override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] = - val qctx = QuotesImpl() - self.postProcess(using qctx) - units + private def inspectorArgs(classpath: List[String], classes: List[String]): Array[String] = + val currentClasspath = ClasspathFromClassloader(getClass.getClassLoader) + val fullClasspath = (classpath :+ currentClasspath).mkString(pathSeparator) + ("-from-tasty" :: "-Yretain-trees" :: "-classpath" :: fullClasspath :: classes).toArray - override def run(implicit ctx: Context): Unit = unsupported("run") - end TastyInspectorFinishPhase + private def inspectFiles(classpath: List[String], classes: List[String]): Boolean = + if (classes.isEmpty) + throw new IllegalArgumentException("Parameter classes should no be empty") - val currentClasspath = ClasspathFromClassloader(getClass.getClassLoader) - val fullClasspath = (classpath :+ currentClasspath).mkString(pathSeparator) - val args = "-from-tasty" :: "-Yretain-trees" :: "-classpath" :: fullClasspath :: classes - val reporter = (new InspectorDriver).process(args.toArray) + val reporter = inspectorDriver().process(inspectorArgs(classpath, classes)) reporter.hasErrors end inspectFiles From aba505b9ff18a403332243abc40f62628e69006b Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Fri, 27 Nov 2020 19:59:02 +0100 Subject: [PATCH 16/19] Introduce DocContext and use it in TastyInspector --- scala3doc/src/dotty/dokka/DocContext.scala | 5 +++++ scala3doc/src/dotty/dokka/DottyDokkaConfig.scala | 2 +- scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala | 3 ++- scala3doc/src/dotty/dokka/Scala3doc.scala | 12 +++++------- scala3doc/src/dotty/dokka/ScalaModuleCreator.scala | 11 +++-------- scala3doc/src/dotty/dokka/tasty/TastyParser.scala | 6 ++++++ 6 files changed, 22 insertions(+), 17 deletions(-) create mode 100644 scala3doc/src/dotty/dokka/DocContext.scala diff --git a/scala3doc/src/dotty/dokka/DocContext.scala b/scala3doc/src/dotty/dokka/DocContext.scala new file mode 100644 index 000000000000..c043f17ae761 --- /dev/null +++ b/scala3doc/src/dotty/dokka/DocContext.scala @@ -0,0 +1,5 @@ +package dotty.dokka + +import dotty.tools.dotc.core.Contexts._ + +type DocContext = Context \ No newline at end of file diff --git a/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala b/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala index c143cedc13b7..88296f609074 100644 --- a/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala +++ b/scala3doc/src/dotty/dokka/DottyDokkaConfig.scala @@ -6,7 +6,7 @@ import java.io.File import collection.JavaConverters._ import dotty.dokka.site.StaticSiteContext -case class DottyDokkaConfig(args: Scala3doc.Args) extends DokkaConfiguration: +case class DottyDokkaConfig(args: Scala3doc.Args, docContext: DocContext) extends DokkaConfiguration: override def getOutputDir: File = args.output override def getCacheRoot: File = null override def getOfflineMode: Boolean = false diff --git a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala index 4b37b45ae364..5612178b90ab 100644 --- a/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala +++ b/scala3doc/src/dotty/dokka/DottyDokkaPlugin.scala @@ -49,7 +49,7 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: // Just turn off another translator since multiple overrides does not work val disableDescriptorTranslator = extend( _.extensionPoint(CoreExtensions.INSTANCE.getSourceToDocumentableTranslator) - .fromInstance(ScalaModuleProvider) + .fromRecipe(ctx => new ScalaModuleProvider(using ctx.docContext)) .overrideExtension(dokkaBase.getDescriptorToDocumentableTranslator) .name("disableDescriptorTranslator") ) @@ -173,6 +173,7 @@ class DottyDokkaPlugin extends DokkaJavaPlugin: extension (ctx: DokkaContext): def siteContext: Option[StaticSiteContext] = ctx.getConfiguration.asInstanceOf[DottyDokkaConfig].staticSiteContext def args: Scala3doc.Args = ctx.getConfiguration.asInstanceOf[DottyDokkaConfig].args + def docContext = ctx.getConfiguration.asInstanceOf[DottyDokkaConfig].docContext // TODO (https://github.com/lampepfl/scala3doc/issues/232): remove once problem is fixed in Dokka extension [T] (builder: ExtensionBuilder[T]): diff --git a/scala3doc/src/dotty/dokka/Scala3doc.scala b/scala3doc/src/dotty/dokka/Scala3doc.scala index 8044c5798216..a5bd256fbc66 100644 --- a/scala3doc/src/dotty/dokka/Scala3doc.scala +++ b/scala3doc/src/dotty/dokka/Scala3doc.scala @@ -9,16 +9,14 @@ import java.util.jar._ import collection.JavaConverters._ import collection.immutable.ArraySeq -import scala.tasty.inspector.TastyInspector import java.nio.file.Files import dotty.tools.dotc.config.Settings._ import dotty.tools.dotc.config.CommonScalaSettings import dotty.tools.dotc.report -import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.reporting.Reporter -class Scala3DocDokkaLogger(using Context) extends DokkaLogger: +class Scala3DocDokkaLogger(using DocContext) extends DokkaLogger: def debug(msg: String): Unit = report.debuglog(msg) // We do not want errors from dokka (that are) not critical to fail our runs @@ -66,7 +64,7 @@ object Scala3doc: revision: Option[String] = None ) - def run(args: Array[String])(using Context): Reporter = + def run(args: Array[String])(using DocContext): Reporter = val parsedArgs = Scala3docArgs.extract(args.toList) def listTastyFiles(f: File): Seq[File] = @@ -76,7 +74,7 @@ object Scala3doc: ) val tastyFiles = parsedArgs.tastyFiles ++ parsedArgs.tastyDirs.flatMap(listTastyFiles) - val reporter = summon[Context].reporter + val reporter = summon[DocContext].reporter if !reporter.hasErrors then val updatedArgs = parsedArgs.copy(tastyDirs = Nil, tastyFiles = tastyFiles) @@ -87,7 +85,7 @@ object Scala3doc: else report.error("Failure") reporter - private [dokka] def run(args: Args, logger: DokkaLogger = DokkaConsoleLogger.INSTANCE) = - new DokkaGenerator(new DottyDokkaConfig(args), logger).generate() + private [dokka] def run(args: Args, logger: DokkaLogger = DokkaConsoleLogger.INSTANCE)(using DocContext) = + new DokkaGenerator(new DottyDokkaConfig(args, summon[DocContext]), logger).generate() diff --git a/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala b/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala index 0040633ceb01..74e29af1569d 100644 --- a/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala +++ b/scala3doc/src/dotty/dokka/ScalaModuleCreator.scala @@ -13,19 +13,14 @@ import org.jetbrains.dokka.model._ import org.jetbrains.dokka.base.parsers.MarkdownParser import collection.JavaConverters._ import kotlin.coroutines.Continuation +import dotty.tools.dotc.core.Contexts._ -object ScalaModuleProvider extends SourceToDocumentableTranslator: +class ScalaModuleProvider(using DocContext) extends SourceToDocumentableTranslator: override def invoke(sourceSet: DokkaSourceSet, cxt: DokkaContext, unused: Continuation[? >: DModule]) = cxt.getConfiguration match case dottyConfig: DottyDokkaConfig => val parser = new MarkdownParser(_ => null) - val inspector = DokkaTastyInspector(sourceSet, parser, dottyConfig) - val filePaths = dottyConfig.args.tastyFiles.map(_.getAbsolutePath) - val (tastyFiles, jars) = filePaths.toList.partition(_.endsWith(".tasty")) - val classpath = dottyConfig.args.classpath.split(java.io.File.pathSeparator).toList - - inspector.inspectAllTastyFiles(tastyFiles, jars, classpath) - val result = inspector.result() + val result = DokkaTastyInspector(sourceSet, parser, dottyConfig).result() def flattenMember(m: Member): Seq[(DRI, Member)] = (m.dri -> m) +: m.allMembers.flatMap(flattenMember) diff --git a/scala3doc/src/dotty/dokka/tasty/TastyParser.scala b/scala3doc/src/dotty/dokka/tasty/TastyParser.scala index 4287f0f048db..8e8c6653e35d 100644 --- a/scala3doc/src/dotty/dokka/tasty/TastyParser.scala +++ b/scala3doc/src/dotty/dokka/tasty/TastyParser.scala @@ -43,6 +43,12 @@ case class DokkaTastyInspector(sourceSet: SourceSetWrapper, parser: Parser, conf topLevels ++= parser.parseRootTree(root.asInstanceOf[parser.qctx.reflect.Tree]) def result(): List[DPackage] = + topLevels.clear() + val filePaths = config.args.tastyFiles.map(_.getAbsolutePath).toList + val classpath = config.args.classpath.split(java.io.File.pathSeparator).toList + + inspectFilesInContext(classpath, filePaths)(using config.docContext) + val all = topLevels.result() val packages = all .filter(_.isInstanceOf[DPackage]) From 47abb661d4a2e900748447b5a8f01c8e517d4e13 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Sat, 28 Nov 2020 14:12:02 +0100 Subject: [PATCH 17/19] Add test for basic reporting in Scala3doc --- scala3doc/src/dotty/dokka/Scala3doc.scala | 13 ++-- .../test/dotty/dokka/RaportingTest.scala | 64 +++++++++++++++++++ scala3doc/test/dotty/dokka/ScaladocTest.scala | 18 ++---- .../dokka/site/SiteGeneratationTest.scala | 6 +- scala3doc/test/dotty/dokka/testUtils.scala | 62 ++++++++++++++++++ 5 files changed, 141 insertions(+), 22 deletions(-) create mode 100644 scala3doc/test/dotty/dokka/RaportingTest.scala create mode 100644 scala3doc/test/dotty/dokka/testUtils.scala diff --git a/scala3doc/src/dotty/dokka/Scala3doc.scala b/scala3doc/src/dotty/dokka/Scala3doc.scala index a5bd256fbc66..ab6d5d48cfdc 100644 --- a/scala3doc/src/dotty/dokka/Scala3doc.scala +++ b/scala3doc/src/dotty/dokka/Scala3doc.scala @@ -26,9 +26,12 @@ class Scala3DocDokkaLogger(using DocContext) extends DokkaLogger: def info(msg: String): Unit = report.inform(msg) def progress(msg: String): Unit = report.informProgress(msg) + + private val dokkaAlphaWarning = "Dokka 1.4.* is an alpha project" def warn(msg: String): Unit = - warnings += 1 - report.warning(msg) + if msg != dokkaAlphaWarning then + warnings += 1 + report.warning(msg) private var errors = 0 private var warnings = 0 @@ -80,12 +83,12 @@ object Scala3doc: if (parsedArgs.output.exists()) IO.delete(parsedArgs.output) - run(updatedArgs, new Scala3DocDokkaLogger) + run(updatedArgs) report.inform("Done") else report.error("Failure") reporter - private [dokka] def run(args: Args, logger: DokkaLogger = DokkaConsoleLogger.INSTANCE)(using DocContext) = - new DokkaGenerator(new DottyDokkaConfig(args, summon[DocContext]), logger).generate() + private [dokka] def run(args: Args)(using DocContext) = + new DokkaGenerator(new DottyDokkaConfig(args, summon[DocContext]), new Scala3DocDokkaLogger).generate() diff --git a/scala3doc/test/dotty/dokka/RaportingTest.scala b/scala3doc/test/dotty/dokka/RaportingTest.scala new file mode 100644 index 000000000000..99d31f6f5c5b --- /dev/null +++ b/scala3doc/test/dotty/dokka/RaportingTest.scala @@ -0,0 +1,64 @@ +package dotty.dokka + +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import org.junit.Test +import org.junit.Assert +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import java.nio.charset.Charset +import dotty.tools.dotc.core.Contexts._ + +class ReportingTest: + import Scala3doc.Args + + private def checkReportedDiagnostics( + newArgs: Args => Args = identity, + ctx: Context = testContext)( + op: ReportedDiagnostics => Unit): Unit = + + val dest = Files.createTempDirectory("test-doc") + try + val args = Args( + name = "Test Project Name", + output = dest.toFile, + tastyFiles = tastyFiles("nested") // Random package + ) + Scala3doc.run(newArgs(args))(using ctx) + op(ctx.reportedDiagnostics) + + finally IO.delete(dest.toFile) + + @Test + def noMessageForMostCases = checkReportedDiagnostics(){ diag => + assertNoWarning(diag) + assertNoErrors(diag) + assertNoInfos(diag) + } + + @Test + def noClassesErors = checkReportedDiagnostics(_.copy(tastyFiles = Nil)){ diag => + assertMessagesAbout(diag.errorMsgs)("classes should no be empty") + } + + @Test + def errorsInCaseOfIncompletClasspath = + val notTasty = Files.createTempFile("broken", ".notTasty") + try + Files.write(notTasty, "Random file".getBytes) + checkReportedDiagnostics(a => a.copy(tastyFiles = notTasty.toFile +: a.tastyFiles)){ diag => + assertMessagesAbout(diag.errorMsgs)("File extension is not `tasty` or `jar`") + } + finally Files.delete(notTasty) + + @Test + def verbosePrintsDokkaMessage = + val ctx = testContext + ctx.setSetting(ctx.settings.verbose, true) + checkReportedDiagnostics(ctx = ctx){ diag => + assertNoWarning(diag) + assertNoErrors(diag) + + assertMessagesAbout(diag.infoMsgs)("dotty.dokka.DottyDokkaPlugin", "generation completed successfully") + } \ No newline at end of file diff --git a/scala3doc/test/dotty/dokka/ScaladocTest.scala b/scala3doc/test/dotty/dokka/ScaladocTest.scala index a95bc132161f..f9dc29a2b261 100644 --- a/scala3doc/test/dotty/dokka/ScaladocTest.scala +++ b/scala3doc/test/dotty/dokka/ScaladocTest.scala @@ -11,6 +11,7 @@ import scala.jdk.CollectionConverters.{ListHasAsScala, SeqHasAsJava} import org.junit.{Test, Rule} import org.junit.rules.{TemporaryFolder, ErrorCollector} import java.io.File +import dotty.tools.dotc.core.Contexts._ abstract class ScaladocTest(val name: String): def assertions: Seq[Assertion] @@ -22,22 +23,11 @@ abstract class ScaladocTest(val name: String): private def args = Scala3doc.Args( name = "test", - tastyFiles = tastyFiles.map(new File(_)), + tastyFiles = tastyFiles(name), output = getTempDir().getRoot, projectVersion = Some("1.0") ) - private def tastyFiles = - def listFilesSafe(dir: File) = Option(dir.listFiles).getOrElse { - throw AssertionError(s"$dir not found. The test name is incorrect or scala3doc-testcases were not recompiled.") - } - def collectFiles(dir: File): List[String] = listFilesSafe(dir).toList.flatMap { - case f if f.isDirectory => collectFiles(f) - case f if f.getName endsWith ".tasty" => f.getAbsolutePath :: Nil - case _ => Nil - } - collectFiles(File(s"${BuildInfo.test_testcasesOutputDir}/tests/$name")) - @Rule def collector = _collector private val _collector = new ErrorCollector(); @@ -46,8 +36,8 @@ abstract class ScaladocTest(val name: String): @Test def executeTest = DokkaTestGenerator( - DottyDokkaConfig(args), - TestLogger(DokkaConsoleLogger.INSTANCE), + DottyDokkaConfig(args, testContext), + TestLogger(new Scala3DocDokkaLogger(using testContext)), assertions.asTestMethods, Nil.asJava ).generate() diff --git a/scala3doc/test/dotty/dokka/site/SiteGeneratationTest.scala b/scala3doc/test/dotty/dokka/site/SiteGeneratationTest.scala index 687b2a304b96..f1e532d3f75d 100644 --- a/scala3doc/test/dotty/dokka/site/SiteGeneratationTest.scala +++ b/scala3doc/test/dotty/dokka/site/SiteGeneratationTest.scala @@ -9,7 +9,7 @@ import org.junit.Assert._ import org.jsoup.Jsoup import org.jsoup.nodes.Document import java.nio.charset.Charset - +import dotty.tools.dotc.core.Contexts._ class SiteGeneratationTest: val projectName = "Test Project Name" @@ -24,10 +24,10 @@ class SiteGeneratationTest: docsRoot = Some(base.toAbsolutePath.toString), projectVersion = Some(projectVersion), ) - Scala3doc.run(args) + Scala3doc.run(args)(using testContext) op(dest) - finally println(dest.toFile) // IO.delete(dest.toFile) + finally IO.delete(dest.toFile) val testDocPath = Paths.get(BuildInfo.testDocumentationRoot) diff --git a/scala3doc/test/dotty/dokka/testUtils.scala b/scala3doc/test/dotty/dokka/testUtils.scala new file mode 100644 index 000000000000..23396356ade9 --- /dev/null +++ b/scala3doc/test/dotty/dokka/testUtils.scala @@ -0,0 +1,62 @@ +package dotty.dokka + +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.reporting.Diagnostic +import dotty.tools.dotc.reporting.ConsoleReporter +import dotty.tools.dotc.interfaces.Diagnostic.{ERROR, INFO, WARNING} +import org.junit.Assert._ +import java.io.File + +case class ReportedDiagnostics(errors: List[Diagnostic], warnings: List[Diagnostic], infos: List[Diagnostic]): + def errorMsgs = errors.map(_.msg.rawMessage) + def warningMsgs = warnings.map(_.msg.rawMessage) + def infoMsgs = infos.map(_.msg.rawMessage) + + +extension (c: Context) def reportedDiagnostics: ReportedDiagnostics = + val t = c.reporter.asInstanceOf[TestReporter] + ReportedDiagnostics(t.errors.result, t.warnings.result, t.infos.result) + +def assertNoWarning(diag: ReportedDiagnostics) = assertEquals("Warnings should be empty", Nil, diag.warningMsgs) +def assertNoErrors(diag: ReportedDiagnostics) = assertEquals("Erros should be empty", Nil, diag.errorMsgs) +def assertNoInfos(diag: ReportedDiagnostics) = assertEquals("Infos should be empty", Nil, diag.infoMsgs) + +def assertMessagesAbout(messages: Seq[String])(patterns: String*) = + patterns.foldLeft(messages){ (toCheck, pattern) => + val (matching, rest) = toCheck.partition(_.contains(pattern)) + assertTrue( + s"Unable to find messages matching `$pattern`in $toCheck" + + " (not that some methods may be filtered out by previous patterns", + matching.nonEmpty + ) + rest + } + +class TestReporter extends ConsoleReporter: + val errors = List.newBuilder[Diagnostic] + val warnings = List.newBuilder[Diagnostic] + val infos = List.newBuilder[Diagnostic] + + + override def doReport(dia: Diagnostic)(using Context): Unit = dia.level match + case INFO => + infos += dia + case ERROR => + errors += dia + super.doReport(dia) + case WARNING => + warnings += dia + super.doReport(dia) + +def testContext = (new ContextBase).initialCtx.fresh.setReporter(new TestReporter) + +def tastyFiles(name: String) = + def listFilesSafe(dir: File) = Option(dir.listFiles).getOrElse { + throw AssertionError(s"$dir not found. The test name is incorrect or scala3doc-testcases were not recompiled.") + } + def collectFiles(dir: File): List[File] = listFilesSafe(dir).toList.flatMap { + case f if f.isDirectory => collectFiles(f) + case f if f.getName endsWith ".tasty" => f :: Nil + case _ => Nil + } + collectFiles(File(s"${BuildInfo.test_testcasesOutputDir}/tests/$name")) \ No newline at end of file From 35f3e120c682fd44e3c00d719ce1fab9ed2e0c95 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Sat, 28 Nov 2020 14:12:59 +0100 Subject: [PATCH 18/19] Do not add scala sources to scala3doc Only use .tasty files --- sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala index fae1ee09d160..16e8e71f8aaa 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala @@ -436,9 +436,9 @@ object DottyPlugin extends AutoPlugin { val _ = compile.value // Ensure that everything is compiled, so TASTy is available. (classDirectory.value ** "*.tasty").get.map(_.getAbsoluteFile) }, - sources ++= Def.taskDyn[Seq[File]] { + sources := Def.taskDyn[Seq[File]] { if (isDotty.value) Def.task { tastyFiles.value } - else Def.task { Nil } + else Def.task { sources.value } }.value, scalacOptions ++= { if (isDotty.value) { From 2cd20e42c06f62fe4f4e468585cdcd90be22de77 Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Mon, 30 Nov 2020 13:41:12 +0100 Subject: [PATCH 19/19] Make inspectFilesInContext private to inspector --- scala3doc/src/dotty/dokka/tasty/TastyParser.scala | 7 +++++-- .../src/scala/tasty/inspector/DocTastyInspector.scala | 9 +++++++++ .../src/scala/tasty/inspector/TastyInspector.scala | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 scala3doc/src/scala/tasty/inspector/DocTastyInspector.scala diff --git a/scala3doc/src/dotty/dokka/tasty/TastyParser.scala b/scala3doc/src/dotty/dokka/tasty/TastyParser.scala index 8e8c6653e35d..5537240c0417 100644 --- a/scala3doc/src/dotty/dokka/tasty/TastyParser.scala +++ b/scala3doc/src/dotty/dokka/tasty/TastyParser.scala @@ -15,7 +15,7 @@ import org.jetbrains.dokka.model.properties.PropertyContainerKt._ import org.jetbrains.dokka.model.properties.{WithExtraProperties} import quoted.Quotes -import scala.tasty.inspector.TastyInspector +import scala.tasty.inspector.DocTastyInspector import dotty.dokka.model.api.withNewMembers import dotty.dokka.tasty.comments.MemberLookup import dotty.dokka.tasty.comments.QueryParser @@ -25,7 +25,10 @@ import scala.util.Try * * Delegates most of the work to [[TastyParser]] [[dotty.dokka.tasty.TastyParser]]. */ -case class DokkaTastyInspector(sourceSet: SourceSetWrapper, parser: Parser, config: DottyDokkaConfig) extends TastyInspector: +case class DokkaTastyInspector( + sourceSet: SourceSetWrapper, + parser: Parser, + config: DottyDokkaConfig) extends DocTastyInspector: private val topLevels = Seq.newBuilder[Documentable] diff --git a/scala3doc/src/scala/tasty/inspector/DocTastyInspector.scala b/scala3doc/src/scala/tasty/inspector/DocTastyInspector.scala new file mode 100644 index 000000000000..f652551443c5 --- /dev/null +++ b/scala3doc/src/scala/tasty/inspector/DocTastyInspector.scala @@ -0,0 +1,9 @@ +package scala.tasty.inspector + +import dotty.tools.dotc.core.Contexts.Context + +abstract class DocTastyInspector extends TastyInspector: + def inspectFilesInDocContext( + classpath: List[String], + filePaths: List[String])( + using Context): Unit = inspectFilesInContext(classpath, filePaths) \ No newline at end of file diff --git a/tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala b/tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala index af4c55518a3b..c6f11e76d177 100644 --- a/tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala +++ b/tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala @@ -65,7 +65,7 @@ trait TastyInspector: * @param classes List of paths of `.tasty` and `.jar` files (no validation is performed) * @param classpath Classpath with extra dependencies needed to load class in the `.tasty` files */ - protected def inspectFilesInContext(classpath: List[String], classes: List[String])(using Context): Unit = + protected[inspector] def inspectFilesInContext(classpath: List[String], classes: List[String])(using Context): Unit = if (classes.isEmpty) report.error("Parameter classes should no be empty") inspectorDriver().process(inspectorArgs(classpath, classes), summon[Context])