diff --git a/pkl-core/src/main/java/org/pkl/core/util/IoUtils.java b/pkl-core/src/main/java/org/pkl/core/util/IoUtils.java index bea5ad23..0f1a867f 100644 --- a/pkl-core/src/main/java/org/pkl/core/util/IoUtils.java +++ b/pkl-core/src/main/java/org/pkl/core/util/IoUtils.java @@ -365,13 +365,13 @@ public final class IoUtils { return uri; } - var basePath = Path.of(base.getRawPath()); + var basePath = Path.of(base.getPath()); if (!base.getRawPath().endsWith("/")) basePath = basePath.getParent(); - var resultPath = basePath.relativize(Path.of(uri.getRawPath())); + var resultPath = basePath.relativize(Path.of(uri.getPath())); try { return new URI( - null, null, null, -1, resultPath.toString(), uri.getRawQuery(), uri.getRawFragment()); + null, null, null, -1, resultPath.toString(), uri.getQuery(), uri.getFragment()); } catch (URISyntaxException e) { throw new IllegalArgumentException(e); } diff --git a/pkl-core/src/test/kotlin/org/pkl/core/runtime/DefaultModuleResolverTest.kt b/pkl-core/src/test/kotlin/org/pkl/core/runtime/DefaultModuleResolverTest.kt index 8268a19f..8e0bb875 100644 --- a/pkl-core/src/test/kotlin/org/pkl/core/runtime/DefaultModuleResolverTest.kt +++ b/pkl-core/src/test/kotlin/org/pkl/core/runtime/DefaultModuleResolverTest.kt @@ -163,7 +163,7 @@ // Arrays.asList("file:///pkl/.*", "https://.*\\.apple\\.com")); // // resolver.resolve("file:///pkl/foo.pkl", fileUrlModule, sourceSection); -// resolver.resolve("https://pkl.io/foo.pkl", fileUrlModule, sourceSection); +// resolver.resolve("https://example.com/foo.pkl", fileUrlModule, sourceSection); // } // // @Test(expected = EvalException.class) diff --git a/pkl-doc/src/main/kotlin/org/pkl/doc/ClassPageGenerator.kt b/pkl-doc/src/main/kotlin/org/pkl/doc/ClassPageGenerator.kt index 5c7e68b6..5c9e0013 100644 --- a/pkl-doc/src/main/kotlin/org/pkl/doc/ClassPageGenerator.kt +++ b/pkl-doc/src/main/kotlin/org/pkl/doc/ClassPageGenerator.kt @@ -41,7 +41,7 @@ internal class ClassPageGenerator( h1 { id = "declaration-title" - +clazz.simpleName + +clazz.simpleName.asIdentifier span { id = "declaration-version" @@ -98,7 +98,7 @@ internal class ClassPageGenerator( span { classes = setOf("name-decl") - +clazz.simpleName + +clazz.simpleName.asIdentifier } renderTypeParameters(clazz.typeParameters) diff --git a/pkl-doc/src/main/kotlin/org/pkl/doc/DocGenerator.kt b/pkl-doc/src/main/kotlin/org/pkl/doc/DocGenerator.kt index 89d6fecb..09523291 100644 --- a/pkl-doc/src/main/kotlin/org/pkl/doc/DocGenerator.kt +++ b/pkl-doc/src/main/kotlin/org/pkl/doc/DocGenerator.kt @@ -23,7 +23,6 @@ import kotlin.io.path.deleteIfExists import kotlin.io.path.exists import kotlin.io.path.isSameFileAs import org.pkl.commons.deleteRecursively -import org.pkl.commons.toUri import org.pkl.core.ModuleSchema import org.pkl.core.PClassInfo import org.pkl.core.Version @@ -180,7 +179,7 @@ internal class DocPackage(val docPackageInfo: DocPackageInfo, val modules: List< mod, docPackageInfo.version, docPackageInfo.getModuleImportUri(mod.moduleName), - docPackageInfo.getModuleSourceCode(mod.moduleName)?.toUri(), + docPackageInfo.getModuleSourceCode(mod.moduleName)?.toEncodedUri(), exampleModulesBySubject[mod.moduleName] ?: listOf() ) } diff --git a/pkl-doc/src/main/kotlin/org/pkl/doc/DocPackageInfo.kt b/pkl-doc/src/main/kotlin/org/pkl/doc/DocPackageInfo.kt index e41ca608..33d843fe 100644 --- a/pkl-doc/src/main/kotlin/org/pkl/doc/DocPackageInfo.kt +++ b/pkl-doc/src/main/kotlin/org/pkl/doc/DocPackageInfo.kt @@ -159,7 +159,14 @@ data class DocPackageInfo( internal fun getModuleImportUri(moduleName: String): URI = when (importUri) { "pkl:/" -> "pkl:${moduleName.substring(4)}".toUri() - else -> "$importUri${getModulePath(moduleName, moduleNamePrefix)}.pkl".toUri() + else -> { + val path = + getModulePath(moduleName, moduleNamePrefix) + .split("/") + .map { it.uriEncoded } + .joinToString("/") { it } + ".pkl" + URI(importUri).resolve(path) + } } internal fun getModuleSourceCode(moduleName: String): String? { @@ -168,9 +175,6 @@ data class DocPackageInfo( return sourceCodeUrlScheme?.replace("%{path}", path)?.substringBefore('#') } - private fun getModulePath(moduleName: String, packagePrefix: String): String = - moduleName.substring(packagePrefix.length).replace('.', '/') - /** Information about a depended-on package. */ data class PackageDependency( /** The name of the depended-on package. */ diff --git a/pkl-doc/src/main/kotlin/org/pkl/doc/DocScope.kt b/pkl-doc/src/main/kotlin/org/pkl/doc/DocScope.kt index 9028ed7f..80156cfe 100644 --- a/pkl-doc/src/main/kotlin/org/pkl/doc/DocScope.kt +++ b/pkl-doc/src/main/kotlin/org/pkl/doc/DocScope.kt @@ -253,9 +253,7 @@ internal class PackageScope( private val moduleScopes: Map by lazy { modules.associate { module -> val docUrl = - url.resolve( - "${module.moduleName.substring(modulePrefix.length).replace('.', '/')}/index.html" - ) + url.resolve(getModulePath(module.moduleName, modulePrefix).uriEncodedPath + "/index.html") module.moduleName to ModuleScope(module, docUrl, this) } } @@ -328,7 +326,7 @@ internal class ModuleScope( get() = module.moduleName val path: String by lazy { - name.substring(parent!!.docPackageInfo.moduleNamePrefix.length).replace('.', '/') + getModulePath(module.moduleName, parent!!.docPackageInfo.moduleNamePrefix).uriEncodedPath } override val dataUrl: URI by lazy { parent!!.dataUrl.resolve("./$path/index.js") } @@ -388,10 +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}.html") + if (clazz.isModuleClass) parentUrl else parentUrl.resolve("${clazz.simpleName.uriEncoded}.html") } - override val dataUrl: URI by lazy { parent!!.dataUrl.resolve("${clazz.simpleName}.js") } + override val dataUrl: URI by lazy { + parent!!.dataUrl.resolve("${clazz.simpleName.uriEncoded}.js") + } override fun getMethod(name: String): MethodScope? = clazz.allMethods[name]?.let { MethodScope(it, this) } diff --git a/pkl-doc/src/main/kotlin/org/pkl/doc/MainOrPackagePageGenerator.kt b/pkl-doc/src/main/kotlin/org/pkl/doc/MainOrPackagePageGenerator.kt index 1367c306..1ac40499 100644 --- a/pkl-doc/src/main/kotlin/org/pkl/doc/MainOrPackagePageGenerator.kt +++ b/pkl-doc/src/main/kotlin/org/pkl/doc/MainOrPackagePageGenerator.kt @@ -70,7 +70,11 @@ internal abstract class MainOrPackagePageGenerator( } else { link } - +name + if (moduleOrPackageScope is ModuleScope) { + +name.asModuleName + } else { + +name + } } } diff --git a/pkl-doc/src/main/kotlin/org/pkl/doc/ModuleOrClassPageGenerator.kt b/pkl-doc/src/main/kotlin/org/pkl/doc/ModuleOrClassPageGenerator.kt index 04ee05b5..2b8159d9 100644 --- a/pkl-doc/src/main/kotlin/org/pkl/doc/ModuleOrClassPageGenerator.kt +++ b/pkl-doc/src/main/kotlin/org/pkl/doc/ModuleOrClassPageGenerator.kt @@ -99,7 +99,7 @@ internal abstract class ModuleOrClassPageGenerator( span { classes = setOf("name-decl") - +propertyName + +propertyName.asIdentifier } +": " @@ -210,7 +210,7 @@ internal abstract class ModuleOrClassPageGenerator( span { classes = setOf("name-decl") - +method.simpleName + +method.simpleName.asIdentifier } renderTypeParameters(method.typeParameters) diff --git a/pkl-doc/src/main/kotlin/org/pkl/doc/ModulePageGenerator.kt b/pkl-doc/src/main/kotlin/org/pkl/doc/ModulePageGenerator.kt index b77a338a..caed649f 100644 --- a/pkl-doc/src/main/kotlin/org/pkl/doc/ModulePageGenerator.kt +++ b/pkl-doc/src/main/kotlin/org/pkl/doc/ModulePageGenerator.kt @@ -47,7 +47,7 @@ internal class ModulePageGenerator( h1 { id = "declaration-title" - +docModule.name + +docModule.name.asModuleName span { id = "declaration-version" @@ -87,7 +87,7 @@ internal class ModulePageGenerator( span { classes = setOf("name-decl") - +docModule.name + +docModule.name.asModuleName } renderModuleAmendsOrExtendsClause(module) diff --git a/pkl-doc/src/main/kotlin/org/pkl/doc/PageGenerator.kt b/pkl-doc/src/main/kotlin/org/pkl/doc/PageGenerator.kt index 27f93aa9..7aaf8480 100644 --- a/pkl-doc/src/main/kotlin/org/pkl/doc/PageGenerator.kt +++ b/pkl-doc/src/main/kotlin/org/pkl/doc/PageGenerator.kt @@ -268,12 +268,12 @@ internal abstract class PageGenerator( a { href = targetScope.urlRelativeTo(pageScope).toString() classes = setOf(cssClass) - +clazz.simpleDisplayName + +clazz.simpleDisplayName.asIdentifier } } else { span { classes = setOf(cssClass) - +clazz.simpleDisplayName + +clazz.simpleDisplayName.asIdentifier } } } @@ -289,12 +289,12 @@ internal abstract class PageGenerator( a { href = targetScope.urlRelativeTo(pageScope).toString() classes = setOf(cssClass) - +typeAlias.simpleName + +typeAlias.simpleName.asIdentifier } } else { span { classes = setOf(cssClass) - +typeAlias.simpleName + +typeAlias.simpleName.asIdentifier } } } @@ -426,7 +426,7 @@ internal abstract class PageGenerator( // anchors, and requires no JS protected fun HtmlBlockTag.renderAnchor(anchorId: String, cssClass: String = "anchor") { div { - id = anchorId + id = anchorId.uriEncoded classes = setOf(cssClass) +" " // needs some content to be considered a valid anchor by browsers } @@ -457,7 +457,7 @@ internal abstract class PageGenerator( protected fun HtmlBlockTag.renderSelfLink(memberName: String) { a { classes = setOf("member-selflink", "material-icons") - href = "#$memberName" + href = "#${memberName.uriEncoded}" +"link" } } diff --git a/pkl-doc/src/main/kotlin/org/pkl/doc/Util.kt b/pkl-doc/src/main/kotlin/org/pkl/doc/Util.kt index 55c8a57e..f85e204d 100644 --- a/pkl-doc/src/main/kotlin/org/pkl/doc/Util.kt +++ b/pkl-doc/src/main/kotlin/org/pkl/doc/Util.kt @@ -16,11 +16,15 @@ package org.pkl.doc import java.io.InputStream +import java.net.URI +import java.net.URLEncoder +import java.nio.charset.StandardCharsets import java.nio.file.Path import kotlin.io.path.bufferedWriter import kotlin.io.path.outputStream import org.pkl.commons.createParentDirectories import org.pkl.core.* +import org.pkl.core.parser.Lexer import org.pkl.core.util.json.JsonWriter // overwrites any existing file @@ -114,3 +118,32 @@ internal fun String.replaceSourceCodePlaceholders( .replace("%{line}", sourceLocation.startLine.toString()) .replace("%{endLine}", sourceLocation.endLine.toString()) } + +internal val String.uriEncoded + get(): String { + val ret = URLEncoder.encode(this, StandardCharsets.UTF_8) + // Replace `+` with `%20` to be safe + // (see + // https://stackoverflow.com/questions/2678551/when-should-space-be-encoded-to-plus-or-20#:~:text=%20%20is%20a%20valid%20way,encodeURIComponent()%20does%20in%20JavaScript.) + return ret.replace("+", "%20") + } + +internal val String.uriEncodedPath + get(): String = split("/").map { it.uriEncoded }.joinToString("/") { it } + +fun getModulePath(moduleName: String, packagePrefix: String): String = + moduleName.substring(packagePrefix.length).replace('.', '/') + +internal fun String.toEncodedUri(): URI = URI(uriEncodedPath) + +/** + * Turns `"foo.bar.baz-biz"` into ``"foo.bar.`baz-biz`"``. + * + * There's a chance that this is wrong; a module might look like: ``"module foo.`bar.baz`.biz"``. + * However, we don't keep around enough information to render this faithfully. + */ +internal val String.asModuleName: String + get() = split(".").map(Lexer::maybeQuoteIdentifier).joinToString(".") { it } + +internal val String.asIdentifier: String + get() = Lexer.maybeQuoteIdentifier(this) diff --git a/pkl-doc/src/test/files/DocGeneratorTest/input/com.externalpackage/doc-package-info.pkl b/pkl-doc/src/test/files/DocGeneratorTest/input/com.externalpackage/doc-package-info.pkl index 15c4b722..f3db5096 100644 --- a/pkl-doc/src/test/files/DocGeneratorTest/input/com.externalpackage/doc-package-info.pkl +++ b/pkl-doc/src/test/files/DocGeneratorTest/input/com.externalpackage/doc-package-info.pkl @@ -2,7 +2,7 @@ amends "pkl:DocPackageInfo" name = "com.externalpackage" version = "1.2.3" -importUri = "https://pkl.io/" +importUri = "https://example.com/" authors { "publisher-externalpackage@group.apple.com" } diff --git a/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/Module Containing Spaces.pkl b/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/Module Containing Spaces.pkl new file mode 100644 index 00000000..32cc34e5 --- /dev/null +++ b/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/Module Containing Spaces.pkl @@ -0,0 +1,2 @@ +/// This is a module that has spaces in its name. +module com.package1.`Module Containing Spaces` diff --git a/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/doc-package-info.pkl b/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/doc-package-info.pkl index 60537767..d892c79d 100644 --- a/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/doc-package-info.pkl +++ b/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/doc-package-info.pkl @@ -17,7 +17,7 @@ import "../com.externalpackage/external1.pkl" name = "com.package1" version = "1.2.3" -importUri = "https://pkl.io/" +importUri = "https://example.com/" authors { "package1-publisher@group.apple.com" } diff --git a/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/referenceToExternalPackage.pkl b/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/referenceToExternalPackage.pkl new file mode 100644 index 00000000..958ab6a8 --- /dev/null +++ b/pkl-doc/src/test/files/DocGeneratorTest/input/com.package1/referenceToExternalPackage.pkl @@ -0,0 +1,4 @@ +import "../com.package2/Module3.pkl" + +/// Something something [Module3.`property {} four`]. +prop: Module3.`Class Two {}` diff --git a/pkl-doc/src/test/files/DocGeneratorTest/input/com.package2/Module3.pkl b/pkl-doc/src/test/files/DocGeneratorTest/input/com.package2/Module3.pkl index 10914f33..3ab0432d 100644 --- a/pkl-doc/src/test/files/DocGeneratorTest/input/com.package2/Module3.pkl +++ b/pkl-doc/src/test/files/DocGeneratorTest/input/com.package2/Module3.pkl @@ -1,7 +1,10 @@ module com.package2.Module3 +/// Something something [`property {} <> four`] property3: String +`property {} <> four`: String + function function3(n: Int): String = n.toString() class Class3 { @@ -9,3 +12,7 @@ class Class3 { function function3(n: Int): String = n.toString() } + +class `Class Two {}` { + prop: String +} diff --git a/pkl-doc/src/test/files/DocGeneratorTest/output/com.package1/1.2.3/Module Containing Spaces/index.html b/pkl-doc/src/test/files/DocGeneratorTest/output/com.package1/1.2.3/Module Containing Spaces/index.html new file mode 100644 index 00000000..ea719b6f --- /dev/null +++ b/pkl-doc/src/test/files/DocGeneratorTest/output/com.package1/1.2.3/Module Containing Spaces/index.html @@ -0,0 +1,196 @@ + + + + Module Containing Spaces (com.package1:1.2.3) • Docsite Title + + + + + + + + + + + +
+ + +
+
Docsite Title > com.package1 +

com.package1.`Module Containing Spaces`1.2.3

+ +
+
+
module com.package1.`Module Containing Spaces`
+

This is a module that has spaces in its name.

+
+
Module URI:
+
https://example.com/Module%20Containing%20Spaces.pklcontent_copy
+
Source code:
+
Module Containing Spaces.pkl
+ + + + + + +
+
+
+
+

Properties(show inherited)

+
    +
  • +
    + +
  • +
+
+
+
+

Methods(show inherited)

+
    +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    +
    +
    + +
  • +
  • +
    +
    + +
  • +
  • +
    +
    + +
  • +
  • +
    +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    +
    + +
  • +
+
+
+ + diff --git a/pkl-doc/src/test/files/DocGeneratorTest/output/com.package1/1.2.3/baseModule/BaseClass.html b/pkl-doc/src/test/files/DocGeneratorTest/output/com.package1/1.2.3/baseModule/BaseClass.html index 427a1d9a..9ccff287 100644 --- a/pkl-doc/src/test/files/DocGeneratorTest/output/com.package1/1.2.3/baseModule/BaseClass.html +++ b/pkl-doc/src/test/files/DocGeneratorTest/output/com.package1/1.2.3/baseModule/BaseClass.html @@ -39,8 +39,8 @@

Methods(show inherited)

  • -
    -
  • -
    -
  • -
    -
    -
    -
  • -
    -
    -
  • -
    -
    -
  • -
    -
    -
  • -
    -
  • -
    -