diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index ad1e0ad7151a..9f346f97e742 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -52,36 +52,6 @@ trait CommonScalaSettings { self: Settings.SettingGroup => 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 { diff --git a/project/Build.scala b/project/Build.scala index 402bc133125f..bcf6d0deff4f 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -291,6 +291,8 @@ object Build { disableDocSetting ) + private lazy val currentYear: String = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR).toString + lazy val scalacOptionsDocSettings = Seq( "-external-mappings:" + ".*scala.*::scaladoc3::http://dotty.epfl.ch/api/," + @@ -309,6 +311,9 @@ object Build { // Reflect doesn't expect to see it as a standalone definition // and therefore it's easier just not to document it "-skip-by-id:scala.runtime.MatchCase", + "-project-footer", s"Copyright (c) 2002-$currentYear, LAMP/EPFL", + "-author", + "-groups" ) // Settings used when compiling dotty with a non-bootstrapped dotty @@ -426,7 +431,7 @@ object Build { s"""version.number=${version.value} |maven.version.number=${version.value} |git.hash=${VersionUtil.gitHash} - |copyright.string=Copyright 2002-${Calendar.getInstance().get(Calendar.YEAR)}, LAMP/EPFL + |copyright.string=Copyright 2002-$currentYear, LAMP/EPFL """.stripMargin if (!(file.exists && IO.read(file) == contents)) { diff --git a/scaladoc-testcases/src/example/Documentation2.scala b/scaladoc-testcases/src/example/Documentation2.scala index 52eb51f348cc..78b23f099bc1 100644 --- a/scaladoc-testcases/src/example/Documentation2.scala +++ b/scaladoc-testcases/src/example/Documentation2.scala @@ -9,4 +9,4 @@ class UserDocLinkingClass { object ReturnObjectWithType { type returnType = Int -} \ No newline at end of file +} diff --git a/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala b/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala index ec7c56881e39..4365e7b06198 100644 --- a/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala +++ b/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala @@ -35,6 +35,7 @@ object Scaladoc: docsRoot: Option[String] = None, projectVersion: Option[String] = None, projectLogo: Option[String] = None, + projectFooter: Option[String] = None, defaultSyntax: CommentSyntax = CommentSyntax.Markdown, sourceLinks: List[String] = Nil, revision: Option[String] = None, @@ -43,6 +44,10 @@ object Scaladoc: identifiersToSkip: List[String] = Nil, regexesToSkip: List[String] = Nil, rootDocPath: Option[String] = None, + includeAuthors: Boolean = false, + includeGroups: Boolean = false, + includePrivateAPI: Boolean = false, + docCanonicalBaseUrl: String = "", documentSyntheticTypes: Boolean = false, ) @@ -94,7 +99,8 @@ object Scaladoc: )(s => newContext.setSetting(s.asInstanceOf[Setting[T]], newValue)) } - allSettings.filterNot(scaladocSpecificSettings.contains).foreach(setInGlobal) + val commonScalaSettings = (new SettingGroup with CommonScalaSettings).allSettings + allSettings.filter(commonScalaSettings.contains).foreach(setInGlobal) def parseTastyRoots(roots: String) = roots.split(File.pathSeparatorChar).toList.map(new File(_)) @@ -162,6 +168,7 @@ object Scaladoc: siteRoot.nonDefault, projectVersion.nonDefault, projectLogo.nonDefault, + projectFooter.nonDefault, parseSyntax, sourceLinks.get, revision.nonDefault, @@ -170,6 +177,10 @@ object Scaladoc: skipById.get ++ deprecatedSkipPackages.get, skipByRegex.get, docRootContent.nonDefault, + author.get, + groups.get, + visibilityPrivate.get, + docCanonicalBaseUrl.get, YdocumentSyntheticTypes.get ) (Some(docArgs), newContext) diff --git a/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala b/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala index 538292929dad..119305a68ad2 100644 --- a/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala +++ b/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala @@ -26,6 +26,17 @@ class ScaladocSettings extends SettingGroup with CommonScalaSettings: sourcepath, sourceroot ) + val projectName: Setting[String] = + StringSetting("-project", "project title", "The name of the project.", "", aliases = List("-doc-title")) + + val projectVersion: Setting[String] = + StringSetting("-project-version", "project version", "The current version of your project.", "", aliases = List("-doc-version")) + + val projectLogo: Setting[String] = + StringSetting("-project-logo", "project logo filename", "The file that contains the project's logo (in /images).", "", aliases = List("-doc-logo")) + + val projectFooter: Setting[String] = StringSetting("-project-footer", "project footer", "A footer on every Scaladoc page.", "", aliases = List("-doc-footer")) + val sourceLinks: Setting[List[String]] = MultiStringSetting("-source-links", "sources", SourceLinks.usage) @@ -57,8 +68,29 @@ class ScaladocSettings extends SettingGroup with CommonScalaSettings: val docRootContent: Setting[String] = StringSetting("-doc-root-content", "path", "The file from which the root package documentation should be imported.", "") + val author: Setting[Boolean] = + BooleanSetting("-author", "Include authors.", false) + + val groups: Setting[Boolean] = + BooleanSetting("-groups", "Group similar functions together (based on the @group annotation)", false) + + val visibilityPrivate: Setting[Boolean] = + BooleanSetting("-private", "Show all types and members. Unless specified, show only public and protected types and members.", false) + + val docCanonicalBaseUrl: Setting[String] = + StringSetting( + "-doc-canonical-base-url", + "url", + s"A base URL to use as prefix and add `canonical` URLs to all pages. The canonical URL may be used by search engines to choose the URL that you want people to see in search results. If unset no canonical URLs are generated.", + "" + ) + + val siteRoot: Setting[String] = StringSetting( + "-siteroot", + "site root", + "A directory containing static files from which to generate documentation.", + "./docs" + ) + val YdocumentSyntheticTypes: Setting[Boolean] = BooleanSetting("-Ydocument-synthetic-types", "Documents intrinsic types e. g. Any, Nothing. Setting is useful only for stdlib", false) - - def scaladocSpecificSettings: Set[Setting[_]] = - Set(sourceLinks, syntax, revision, externalDocumentationMappings, socialLinks, skipById, skipByRegex, deprecatedSkipPackages, docRootContent) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala index aeb1948373b7..b3bcb9b55ac4 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala @@ -131,6 +131,7 @@ class HtmlRenderer(rootPackage: Member, val members: Map[DRI, Member])(using ctx meta(charset := "utf-8"), meta(util.HTML.name := "viewport", content := "width=device-width, initial-scale=1"), title(page.link.name), + canonicalUrl(absolutePath(page.link.dri)), link( rel := "shortcut icon", `type` := "image/x-icon", @@ -170,6 +171,14 @@ class HtmlRenderer(rootPackage: Member, val members: Map[DRI, Member])(using ctx ) renderNested(navigablePage, toplevel = true)._2 + private def canonicalUrl(l: String): AppliedTag | String = + val canon = args.docCanonicalBaseUrl + if !canon.isEmpty then + val canonicalUrl = if canon.endsWith("/") then canon else canon + "/" + link(rel := "canonical", href := canonicalUrl + l) + else + "" // return empty tag + private def hasSocialLinks = !args.socialLinks.isEmpty private def socialLinks(whiteIcon: Boolean = true) = @@ -194,6 +203,13 @@ class HtmlRenderer(rootPackage: Member, val members: Map[DRI, Member])(using ctx )).dropRight(1) div(cls := "breadcrumbs")(innerTags:_*) + def textFooter: String | AppliedTag = + args.projectFooter.fold("") { f => + div(id := "footer-text")( + raw(f) + ) + } + div(id := "container")( div(id := "leftColumn")( div(id := "logo")( @@ -244,7 +260,8 @@ class HtmlRenderer(rootPackage: Member, val members: Map[DRI, Member])(using ctx cls := "scaladoc_logo" ) ) + ), + textFooter ) ) ) - ) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala index e8e88c3f6ef9..a36ceefab328 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala @@ -46,13 +46,15 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext def opt(name: String, on: Option[DocPart]): Seq[AppliedTag] = if on.isEmpty then Nil else tableRow(name, renderDocPart(on.get)) + def authors(authors: List[DocPart]) = if summon[DocContext].args.includeAuthors then list("Authors", authors) else Nil + m.docs.fold(Nil)(d => nested("Type Params", d.typeParams) ++ nested("Value Params", d.valueParams) ++ opt("Returns", d.result) ++ nested("Throws", d.throws) ++ opt("Constructor", d.constructor) ++ - list("Authors", d.authors) ++ + authors(d.authors) ++ list("See also", d.see) ++ opt("Version", d.version) ++ opt("Since", d.since) ++ @@ -263,7 +265,13 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext Tab("Grouped members", "custom_groups", content, "selected") def buildMembers(s: Member): AppliedTag = - val (membersInGroups, rest) = s.members.partition(_.docs.exists(_.group.nonEmpty)) + def partitionIntoGroups(members: Seq[Member]) = + if summon[DocContext].args.includeGroups then + members.partition(_.docs.exists(_.group.nonEmpty)) + else + (Nil, members) + + val (membersInGroups, rest) = partitionIntoGroups(s.members) val extensions = rest.groupBy{ _.kind match diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala index ec1b9c743a05..12738943c1fe 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala @@ -83,15 +83,15 @@ class SymOps[Q <: Quotes](val q: Q) extends JavadocAnchorCreator with Scaladoc2A Flags.Case -> Modifier.Case, ).collect { case (flag, mod) if sym.flags.is(flag) => mod } - def isHiddenByVisibility: Boolean = + def isHiddenByVisibility(using dctx: DocContext): Boolean = import VisibilityScope._ - getVisibility() match + !summon[DocContext].args.includePrivateAPI && getVisibility().match case Visibility.Private(_) => true case Visibility.Protected(ThisScope | ImplicitModuleScope | _: ExplicitModuleScope) => true case _ => false - def shouldDocumentClasslike: Boolean = !isHiddenByVisibility + def shouldDocumentClasslike(using dctx: DocContext): Boolean = !isHiddenByVisibility && !sym.flags.is(Flags.Synthetic) && (!sym.flags.is(Flags.Case) || !sym.flags.is(Flags.Enum)) && !(sym.companionModule.flags.is(Flags.Given))