From 6bced245c52cd1c895e19a7063d6205e8170829c Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Fri, 18 Feb 2022 14:57:13 +0100 Subject: [PATCH 1/3] Fix escaping special chars in searchbar links in Scaladoc Unit tests for links with "#" character were also added. --- .../tools/scaladoc/renderers/Locations.scala | 4 +++- .../tools/scaladoc/renderers/Resources.scala | 2 +- .../tools/scaladoc/tasty/InkuireSupport.scala | 4 ++-- .../tools/scaladoc/renderers/LocationTests.scala | 15 +++++++++++++++ 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala index 43602a23af59..dd07d2f7bb10 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala @@ -81,7 +81,9 @@ trait Locations(using ctx: DocContext): def resolveRoot(dri: DRI, path: String): String = resolveRoot(rawLocation(dri), path) def absolutePath(dri: DRI, extension: String = "html"): String = rawLocation(dri).mkString("", "/", s".$extension") - def absolutePathWithAnchor(dri: DRI, extension: String = "html"): String = s"${absolutePath(dri, extension)}#${dri.anchor}" + + def escapedAbsolutePathWithAnchor(dri: DRI, extension: String = "html"): String = + s"${escapeUrl(absolutePath(dri, extension))}#${dri.anchor}" def resolveLink(dri: DRI, url: String): String = if URI(url).isAbsolute then url else resolveRoot(dri, url) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala index 5a5e60f08879..01de29256bf8 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala @@ -133,7 +133,7 @@ trait Resources(using ctx: DocContext) extends Locations, Writer: }.mkString def mkEntry(dri: DRI, name: String, text: String, descr: String, kind: String) = jsonObject( - "l" -> jsonString(absolutePathWithAnchor(dri)), + "l" -> jsonString(escapedAbsolutePathWithAnchor(dri)), "n" -> jsonString(name), "t" -> jsonString(text), "d" -> jsonString(descr), diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala index 36b29ff05eee..3d65094d249a 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala @@ -114,7 +114,7 @@ trait InkuireSupport(using DocContext) extends Resources: ), name = name, packageName = ownerName, - uri = methodSymbol.dri.externalLink.getOrElse(absolutePathWithAnchor(methodSymbol.dri)), + uri = methodSymbol.dri.externalLink.getOrElse(escapedAbsolutePathWithAnchor(methodSymbol.dri)), entryType = "def" ) val curriedSgn = sgn.copy(signature = Inkuire.curry(sgn.signature)) @@ -142,7 +142,7 @@ trait InkuireSupport(using DocContext) extends Resources: ), name = name, packageName = ownerName, - uri = valSymbol.dri.externalLink.getOrElse(absolutePathWithAnchor(valSymbol.dri)), + uri = valSymbol.dri.externalLink.getOrElse(escapedAbsolutePathWithAnchor(valSymbol.dri)), entryType = "val" ) val curriedSgn = sgn.copy(signature = Inkuire.curry(sgn.signature)) diff --git a/scaladoc/test/dotty/tools/scaladoc/renderers/LocationTests.scala b/scaladoc/test/dotty/tools/scaladoc/renderers/LocationTests.scala index ffcf1a69106e..41f84b5629be 100644 --- a/scaladoc/test/dotty/tools/scaladoc/renderers/LocationTests.scala +++ b/scaladoc/test/dotty/tools/scaladoc/renderers/LocationTests.scala @@ -37,3 +37,18 @@ class LocationTests: "../../annotation", path("api/scala/annotation/meta/beanGetter", "api/scala/annotation"), ) + + @Test + def testAnchorLinks() = + def pathWithAnchor(location: String, anchor: String) = + locations.escapedAbsolutePathWithAnchor(new DRI(location, anchor)) + + assertEquals( + "scala/%23::.html#abcde", + pathWithAnchor("scala.#::", "abcde") + ) + + assertEquals( + "scala/collection/immutable/LazyList$$%23$.html#abcde", + pathWithAnchor("scala.collection.immutable.LazyList$$#$", "abcde") + ) From ca35a822604776307bd0ae7ebb7c9824344f658a Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Fri, 18 Feb 2022 16:34:14 +0100 Subject: [PATCH 2/3] Fix handling of the external links in Scaladoc This includes both fixing links found in searchbar, and ones accessible from method names. In searchbar, links are resolved based on whether they are absolute (pointing to an external website, like java docs) or relative (pointing to another sub-website, with root being appended on runtime). This is achieved by serializing another boolean field in PageEntry. --- scaladoc-js/main/src/searchbar/PageEntry.scala | 13 ++++++++----- .../main/src/searchbar/SearchbarComponent.scala | 7 ++++++- .../test/dotty/tools/scaladoc/MatchersTest.scala | 1 + .../dotty/tools/scaladoc/renderers/Locations.scala | 3 +++ .../dotty/tools/scaladoc/renderers/Renderer.scala | 4 +++- .../dotty/tools/scaladoc/renderers/Resources.scala | 3 ++- 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/scaladoc-js/main/src/searchbar/PageEntry.scala b/scaladoc-js/main/src/searchbar/PageEntry.scala index 7b0837e87a95..c042798c7b35 100644 --- a/scaladoc-js/main/src/searchbar/PageEntry.scala +++ b/scaladoc-js/main/src/searchbar/PageEntry.scala @@ -4,17 +4,19 @@ import scala.scalajs.js @js.native trait PageEntryJS extends js.Object { - val n: String = js.native - val t: String = js.native - val d: String = js.native - val l: String = js.native - val k: String = js.native + val n: String = js.native + val t: String = js.native + val d: String = js.native + val l: String = js.native + val e: Boolean = js.native + val k: String = js.native } case class PageEntry( fullName: String, description: String, location: String, + isLocationExternal: Boolean, shortName: String, kind: String, tokens: List[String] @@ -34,6 +36,7 @@ object PageEntry { jsObj.t, jsObj.d, jsObj.l, + jsObj.e, jsObj.n.toLowerCase, jsObj.k, StringUtils.createCamelCaseTokens(jsObj.n) diff --git a/scaladoc-js/main/src/searchbar/SearchbarComponent.scala b/scaladoc-js/main/src/searchbar/SearchbarComponent.scala index 33f38e690a1e..c33667f04e73 100644 --- a/scaladoc-js/main/src/searchbar/SearchbarComponent.scala +++ b/scaladoc-js/main/src/searchbar/SearchbarComponent.scala @@ -22,7 +22,12 @@ class SearchbarComponent(engine: SearchbarEngine, inkuireEngine: InkuireJSSearch icon.classList.add(p.kind.take(2)) val resultA = document.createElement("a").asInstanceOf[html.Anchor] - resultA.href = Globals.pathToRoot + p.location + resultA.href = + if (p.isLocationExternal) { + p.location + } else { + Globals.pathToRoot + p.location + } resultA.text = s"${p.fullName}" resultA.onclick = (event: Event) => if (document.body.contains(rootDiv)) { diff --git a/scaladoc-js/main/test/dotty/tools/scaladoc/MatchersTest.scala b/scaladoc-js/main/test/dotty/tools/scaladoc/MatchersTest.scala index b10ad501bae0..f6cc653ee479 100644 --- a/scaladoc-js/main/test/dotty/tools/scaladoc/MatchersTest.scala +++ b/scaladoc-js/main/test/dotty/tools/scaladoc/MatchersTest.scala @@ -34,6 +34,7 @@ class MatchersTest: s"$kind $name", "", "", + false, s"$name", kind, StringUtils.createCamelCaseTokens(name) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala index dd07d2f7bb10..9490693fb759 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala @@ -85,6 +85,9 @@ trait Locations(using ctx: DocContext): def escapedAbsolutePathWithAnchor(dri: DRI, extension: String = "html"): String = s"${escapeUrl(absolutePath(dri, extension))}#${dri.anchor}" + def relativeInternalOrAbsoluteExternalPath(dri: DRI): String = + dri.externalLink.getOrElse(escapedAbsolutePathWithAnchor(dri)) + def resolveLink(dri: DRI, url: String): String = if URI(url).isAbsolute then url else resolveRoot(dri, url) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala index 45a080d4828a..4e313be5eb1c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala @@ -118,7 +118,9 @@ abstract class Renderer(rootPackage: Member, val members: Map[DRI, Member], prot val signatureRenderer = new SignatureRenderer: def currentDri: DRI = page.link.dri def link(dri: DRI): Option[String] = - Some(pathToPage(currentDri, dri)).filter(_ != UnresolvedLocationLink) + dri.externalLink.orElse( + Some(pathToPage(currentDri, dri)).filter(_ != UnresolvedLocationLink) + ) MemberRenderer(signatureRenderer).fullMember(m) case t: ResolvedTemplate => siteContent(page.link.dri, t) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala index 01de29256bf8..b6e6c5adf1d0 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala @@ -133,7 +133,8 @@ trait Resources(using ctx: DocContext) extends Locations, Writer: }.mkString def mkEntry(dri: DRI, name: String, text: String, descr: String, kind: String) = jsonObject( - "l" -> jsonString(escapedAbsolutePathWithAnchor(dri)), + "l" -> jsonString(relativeInternalOrAbsoluteExternalPath(dri)), + "e" -> (if dri.externalLink.isDefined then rawJSON("true") else rawJSON("false")), "n" -> jsonString(name), "t" -> jsonString(text), "d" -> jsonString(descr), From 213e2759e0fb670ecab8c0dd93ac6431290a05cd Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Tue, 22 Feb 2022 09:44:26 +0100 Subject: [PATCH 3/3] Fix malformed URI issues in Inkuire in Scaladoc --- scaladoc-js/main/src/searchbar/SearchbarComponent.scala | 9 ++++++--- scaladoc/src/dotty/tools/scaladoc/Inkuire.scala | 3 ++- .../src/dotty/tools/scaladoc/tasty/InkuireSupport.scala | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/scaladoc-js/main/src/searchbar/SearchbarComponent.scala b/scaladoc-js/main/src/searchbar/SearchbarComponent.scala index c33667f04e73..9741c6681024 100644 --- a/scaladoc-js/main/src/searchbar/SearchbarComponent.scala +++ b/scaladoc-js/main/src/searchbar/SearchbarComponent.scala @@ -62,11 +62,14 @@ class SearchbarComponent(engine: SearchbarEngine, inkuireEngine: InkuireJSSearch icon.classList.add(m.entryType.take(2)) val resultA = document.createElement("a").asInstanceOf[html.Anchor] + // Inkuire pageLocation should start with e (external) + // or i (internal). The rest of the string is an absolute + // or relative URL resultA.href = - if(new URI(m.pageLocation).isAbsolute()) { - m.pageLocation + if (m.pageLocation(0) == 'e') { + m.pageLocation.substring(1) } else { - Globals.pathToRoot + m.pageLocation + Globals.pathToRoot + m.pageLocation.substring(1) } resultA.text = m.functionName resultA.onclick = (event: Event) => diff --git a/scaladoc/src/dotty/tools/scaladoc/Inkuire.scala b/scaladoc/src/dotty/tools/scaladoc/Inkuire.scala index f1b9414c9835..52b677c30e6a 100644 --- a/scaladoc/src/dotty/tools/scaladoc/Inkuire.scala +++ b/scaladoc/src/dotty/tools/scaladoc/Inkuire.scala @@ -114,6 +114,7 @@ object Inkuire { name: String, packageName: String, uri: String, + isLocationExternal: Boolean, entryType: String ) @@ -333,7 +334,7 @@ object Inkuire { ("signature", serialize(e.signature)), ("name", serialize(e.name)), ("packageName", serialize(e.packageName)), - ("uri", serialize(e.uri)), + ("uri", serialize((if e.isLocationExternal then "e" else "i") + e.uri)), ("entryType", serialize(e.entryType)) ) } diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala index 3d65094d249a..1f3b353eb045 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala @@ -115,6 +115,7 @@ trait InkuireSupport(using DocContext) extends Resources: name = name, packageName = ownerName, uri = methodSymbol.dri.externalLink.getOrElse(escapedAbsolutePathWithAnchor(methodSymbol.dri)), + isLocationExternal = methodSymbol.dri.externalLink.isDefined, entryType = "def" ) val curriedSgn = sgn.copy(signature = Inkuire.curry(sgn.signature)) @@ -143,6 +144,7 @@ trait InkuireSupport(using DocContext) extends Resources: name = name, packageName = ownerName, uri = valSymbol.dri.externalLink.getOrElse(escapedAbsolutePathWithAnchor(valSymbol.dri)), + isLocationExternal = valSymbol.dri.externalLink.isDefined, entryType = "val" ) val curriedSgn = sgn.copy(signature = Inkuire.curry(sgn.signature))