Skip to content

Commit 4e41a4c

Browse files
authored
Merge pull request #14657 from jchyb/scaladoc/jekyll-escapes
Scaladoc: Warn about special characters in filenames according to the default Jekyll rules
2 parents 0761c50 + d3ce129 commit 4e41a4c

File tree

11 files changed

+58
-15
lines changed

11 files changed

+58
-15
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package _docs.tests
1+
package docs.tests
22

33
class Adoc:
44
def foo = 123
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package example
2+
3+
package jekyllIncompat:
4+
object #~
5+
object ~>~

scaladoc/src/dotty/tools/scaladoc/DocContext.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import java.io.PrintStream
1717
import scala.io.Codec
1818
import java.net.URL
1919
import scala.util.Try
20+
import scala.collection.mutable
21+
import dotty.tools.scaladoc.util.Check.checkJekyllIncompatPath
2022

2123
type CompilerContext = dotty.tools.dotc.core.Contexts.Context
2224

@@ -90,3 +92,18 @@ case class DocContext(args: Scaladoc.Args, compilerContext: CompilerContext):
9092

9193

9294
val externalDocumentationLinks = args.externalMappings
95+
96+
97+
// We collect and report any generated files incompatible with Jekyll
98+
private lazy val jekyllIncompatLinks = mutable.HashSet[String]()
99+
100+
def checkPathCompat(path: Seq[String]): Unit =
101+
if checkJekyllIncompatPath(path) then jekyllIncompatLinks.add(path.mkString("/"))
102+
103+
def reportPathCompatIssues(): Unit =
104+
if !jekyllIncompatLinks.isEmpty then
105+
report.warning(
106+
s"""Following generated file paths might not be compatible with Jekyll:
107+
|${jekyllIncompatLinks.mkString("\n")}
108+
|If using GitHub Pages consider adding a \".nojekyll\" file.
109+
""".stripMargin)(using compilerContext)

scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,5 +229,6 @@ object Scaladoc:
229229
given docContext: DocContext = new DocContext(args, ctx)
230230
val module = ScalaModuleProvider.mkModule()
231231
new dotty.tools.scaladoc.renderers.HtmlRenderer(module.rootPackage, module.members).render()
232+
docContext.reportPathCompatIssues()
232233
report.inform("generation completed successfully")
233234
docContext

scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ trait Locations(using ctx: DocContext):
4747
case "<empty>" :: tail => "_empty_" :: tail
4848
case other => other
4949
if ctx.args.apiSubdirectory then "api" :: fqn else fqn
50+
ctx.checkPathCompat(path)
5051
cache.put(dri, path)
5152
path
5253
case cached => cached

scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ abstract class Renderer(rootPackage: Member, val members: Map[DRI, Member], prot
120120
val all = navigablePage +: redirectPages
121121
// We need to check for conflicts only if we have top-level member called docs
122122
val hasPotentialConflict =
123-
rootPackage.members.exists(m => m.name.startsWith("_docs"))
123+
rootPackage.members.exists(m => m.name.startsWith("docs"))
124124

125125
if hasPotentialConflict then
126126
def walk(page: Page): Unit =

scaladoc/src/dotty/tools/scaladoc/site/StaticSiteContext.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ class StaticSiteContext(
2323
val docsPath = root.toPath.resolve("_docs")
2424
val blogPath = root.toPath.resolve("_blog")
2525

26-
val relativizeFrom = if args.apiSubdirectory then docsPath else root.toPath
26+
def relativize(path: Path): Path =
27+
if args.apiSubdirectory then
28+
docsPath.relativize(path)
29+
else
30+
val relativised = docsPath.relativize(path)
31+
Paths.get("docs").resolve(relativised)
2732

2833
val siteExtensions = Set(".html", ".md")
2934

@@ -47,7 +52,7 @@ class StaticSiteContext(
4752
val redirectFrom = loadedTemplate.templateFile.settings.getOrElse("page", Map.empty).asInstanceOf[Map[String, Object]].get("redirectFrom")
4853
def redirectToTemplate(redirectFrom: String) =
4954
val path = if redirectFrom.startsWith("/")
50-
then relativizeFrom.resolve(redirectFrom.drop(1))
55+
then docsPath.resolve(redirectFrom.drop(1))
5156
else loadedTemplate.file.toPath.resolveSibling(redirectFrom)
5257
val driFrom = driFor(path)
5358
val driTo = driFor(loadedTemplate.file.toPath)
@@ -93,7 +98,7 @@ class StaticSiteContext(
9398
}
9499

95100
def driFor(dest: Path): DRI =
96-
val rawFilePath = relativizeFrom.relativize(dest)
101+
val rawFilePath = relativize(dest)
97102
val pageName = dest.getFileName.toString
98103
val dotIndex = pageName.lastIndexOf('.')
99104

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package dotty.tools.scaladoc.util
2+
3+
object Check:
4+
/**
5+
* Jekyll (also used by GitHub Pages) by default makes a couple characters
6+
* illegal to use in file name beginnings.
7+
*/
8+
def checkJekyllIncompatPath(path: Seq[String]): Boolean =
9+
path.find( filename =>
10+
filename.matches("^~.*")
11+
|| filename.matches("^\\_.*")
12+
|| filename.matches("^\\..*")
13+
|| filename.matches("^\\#.*")
14+
).isDefined

scaladoc/test/dotty/tools/scaladoc/ReportingTest.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,11 @@ class ReportingTest:
7676
val docsRoot = testDocPath.resolve("conflicts-pages").toString
7777
checkReportedDiagnostics(_.copy(
7878
docsRoot = Some(docsRoot),
79-
tastyFiles = tastyFiles("tests", rootPck = "_docs")
79+
tastyFiles = tastyFiles("tests", rootPck = "docs")
8080
)){ diag =>
8181
assertNoWarning(diag)
8282
val Seq(msg) = diag.errorMsgs.map(_.toLowerCase)
83-
Seq("conflict","api", "static", "page", "_docs/tests/adoc.html")
83+
Seq("conflict","api", "static", "page", "docs/tests/adoc.html")
8484
.foreach( word =>
8585
Assert.assertTrue(s"Error message: $msg should contains $word", msg.contains(word))
8686
)

scaladoc/test/dotty/tools/scaladoc/site/NavigationTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,5 @@ class NavigationTest extends BaseHtmlTest:
4444
)),
4545
))
4646

47-
testNavMenu("_docs/Adoc.html", topLevelNav)
47+
testNavMenu("docs/Adoc.html", topLevelNav)
4848
}

scaladoc/test/dotty/tools/scaladoc/site/SiteGeneratationTest.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ class SiteGeneratationTest extends BaseHtmlTest:
3232
}
3333

3434
def testDocPages()(using ProjectContext) =
35-
checkFile("_docs/Adoc.html")(title = "Adoc", header = "Header in Adoc", parents = Seq(projectName))
36-
checkFile("_docs/dir/index.html")(title = "A directory", header = "A directory", parents = Seq(projectName))
37-
checkFile("_docs/dir/nested.html")(
35+
checkFile("docs/Adoc.html")(title = "Adoc", header = "Header in Adoc", parents = Seq(projectName))
36+
checkFile("docs/dir/index.html")(title = "A directory", header = "A directory", parents = Seq(projectName))
37+
checkFile("docs/dir/nested.html")(
3838
title = "Nested in a directory", header = "Nested in a directory", parents = Seq(projectName, "A directory"))
3939

4040
def testDocIndexPage()(using ProjectContext) =
41-
checkFile("_docs/index.html")(title = projectName, header = s"$projectName in header")
41+
checkFile("docs/index.html")(title = projectName, header = s"$projectName in header")
4242

4343
def testApiPages(
4444
mainTitle: String = "API",
@@ -66,13 +66,13 @@ class SiteGeneratationTest extends BaseHtmlTest:
6666
testDocIndexPage()
6767
testApiPages()
6868

69-
withHtmlFile("_docs/Adoc.html"){ content =>
69+
withHtmlFile("docs/Adoc.html"){ content =>
7070
content.assertAttr("p a","href", "../tests/site/SomeClass.html")
7171
}
7272

7373
withHtmlFile("tests/site/SomeClass.html"){ content =>
7474
content.assertAttr(".breadcrumbs a","href",
75-
"../../_docs/index.html", "../../index.html", "../site.html", "SomeClass.html"
75+
"../../docs/index.html", "../../index.html", "../site.html", "SomeClass.html"
7676
)
7777
}
7878
}
@@ -98,7 +98,7 @@ class SiteGeneratationTest extends BaseHtmlTest:
9898
@Test
9999
def staticLinking() = withGeneratedSite(testDocPath.resolve("static-links")){
100100

101-
withHtmlFile("_docs/Adoc.html"){ content =>
101+
withHtmlFile("docs/Adoc.html"){ content =>
102102
content.assertAttr("p a","href",
103103
"dir/html.html",
104104
"dir/name...with..dots..html",

0 commit comments

Comments
 (0)