Fix source links in pkldoc (#362)

Fixes an issue where source links are incorrectly URI encoded; i.e. `https%3A//github.com` instead of `https://github.com`.

This was causing the browser to resolve these as relative to the enclosing page.
This commit is contained in:
Daniel Chao
2024-03-26 07:48:55 -07:00
committed by GitHub
parent 11a2343a65
commit c9ed183891
54 changed files with 77 additions and 69 deletions

View File

@@ -179,7 +179,7 @@ internal class DocPackage(val docPackageInfo: DocPackageInfo, val modules: List<
mod,
docPackageInfo.version,
docPackageInfo.getModuleImportUri(mod.moduleName),
docPackageInfo.getModuleSourceCode(mod.moduleName)?.toEncodedUri(),
docPackageInfo.getModuleSourceCode(mod.moduleName),
exampleModulesBySubject[mod.moduleName] ?: listOf()
)
}

View File

@@ -160,19 +160,15 @@ data class DocPackageInfo(
when (importUri) {
"pkl:/" -> "pkl:${moduleName.substring(4)}".toUri()
else -> {
val path =
getModulePath(moduleName, moduleNamePrefix)
.split("/")
.map { it.uriEncoded }
.joinToString("/") { it } + ".pkl"
val path = getModulePath(moduleName, moduleNamePrefix).uriEncoded + ".pkl"
URI(importUri).resolve(path)
}
}
internal fun getModuleSourceCode(moduleName: String): String? {
val path = "/" + getModulePath(moduleName, moduleNamePrefix) + ".pkl"
internal fun getModuleSourceCode(moduleName: String): URI? {
val path = "/" + getModulePath(moduleName, moduleNamePrefix).uriEncoded + ".pkl"
// assumption: the fragment is only used for line numbers
return sourceCodeUrlScheme?.replace("%{path}", path)?.substringBefore('#')
return sourceCodeUrlScheme?.replace("%{path}", path)?.substringBefore('#')?.let(URI::create)
}
/** Information about a depended-on package. */

View File

@@ -253,7 +253,7 @@ internal class PackageScope(
private val moduleScopes: Map<String, ModuleScope> by lazy {
modules.associate { module ->
val docUrl =
url.resolve(getModulePath(module.moduleName, modulePrefix).uriEncodedPath + "/index.html")
url.resolve(getModulePath(module.moduleName, modulePrefix).uriEncoded + "/index.html")
module.moduleName to ModuleScope(module, docUrl, this)
}
}
@@ -326,7 +326,7 @@ internal class ModuleScope(
get() = module.moduleName
val path: String by lazy {
getModulePath(module.moduleName, parent!!.docPackageInfo.moduleNamePrefix).uriEncodedPath
getModulePath(module.moduleName, parent!!.docPackageInfo.moduleNamePrefix).uriEncoded
}
override val dataUrl: URI by lazy { parent!!.dataUrl.resolve("./$path/index.js") }
@@ -386,11 +386,12 @@ internal class ClassScope(
) : PageScope() {
override val url: URI by lazy {
// `isModuleClass` distinction is relevant when this scope is a link target
if (clazz.isModuleClass) parentUrl else parentUrl.resolve("${clazz.simpleName.uriEncoded}.html")
if (clazz.isModuleClass) parentUrl
else parentUrl.resolve("${clazz.simpleName.uriEncodedComponent}.html")
}
override val dataUrl: URI by lazy {
parent!!.dataUrl.resolve("${clazz.simpleName.uriEncoded}.js")
parent!!.dataUrl.resolve("${clazz.simpleName.uriEncodedComponent}.js")
}
override fun getMethod(name: String): MethodScope? =

View File

@@ -426,7 +426,7 @@ internal abstract class PageGenerator<out S>(
// anchors, and requires no JS
protected fun HtmlBlockTag.renderAnchor(anchorId: String, cssClass: String = "anchor") {
div {
id = anchorId.uriEncoded
id = anchorId.uriEncodedComponent
classes = setOf(cssClass)
+" " // needs some content to be considered a valid anchor by browsers
}
@@ -457,7 +457,7 @@ internal abstract class PageGenerator<out S>(
protected fun HtmlBlockTag.renderSelfLink(memberName: String) {
a {
classes = setOf("member-selflink", "material-icons")
href = "#${memberName.uriEncoded}"
href = "#${memberName.uriEncodedComponent}"
+"link"
}
}
@@ -600,7 +600,8 @@ internal abstract class PageGenerator<out S>(
for (example in examples) {
if (first) first = false else +", "
a {
href = docModule.parent.docPackageInfo.getModuleSourceCode(example.moduleName)!!
href =
docModule.parent.docPackageInfo.getModuleSourceCode(example.moduleName)!!.toString()
+example.shortModuleName
}
}

View File

@@ -119,7 +119,12 @@ internal fun String.replaceSourceCodePlaceholders(
.replace("%{endLine}", sourceLocation.endLine.toString())
}
internal val String.uriEncoded
/**
* Encodes a URI string, encoding characters that are part of URI syntax.
*
* Follows `encodeURIComponent` from ECMAScript.
*/
internal val String.uriEncodedComponent
get(): String {
val ret = URLEncoder.encode(this, StandardCharsets.UTF_8)
// Replace `+` with `%20` to be safe
@@ -128,13 +133,18 @@ internal val String.uriEncoded
return ret.replace("+", "%20")
}
internal val String.uriEncodedPath
get(): String = split("/").map { it.uriEncoded }.joinToString("/") { it }
/**
* Encodes a URI string, preserving characters that are part of URI syntax.
*
* Follows `encodeURI` from ECMAScript.
*/
internal val String.uriEncoded
get(): String = replace(Regex("([^;/?:@&=+\$,#]+)")) { it.value.uriEncodedComponent }
fun getModulePath(moduleName: String, packagePrefix: String): String =
moduleName.substring(packagePrefix.length).replace('.', '/')
internal fun String.toEncodedUri(): URI = URI(uriEncodedPath)
internal fun String.toEncodedUri(): URI = URI(uriEncoded)
/**
* Turns `"foo.bar.baz-biz"` into ``"foo.bar.`baz-biz`"``.