aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcin Aman <marcin.aman@gmail.com>2020-11-25 11:54:20 +0100
committerGitHub <noreply@github.com>2020-11-25 11:54:20 +0100
commit78850b5786b7b2a767db1dbd7132a374b2f4f227 (patch)
treeb2ed4d4ec393a24e4445dc952296106ea30a0b5e
parentb38654fe4f98964a63a4819023332cec03ce6ca1 (diff)
downloaddokka-78850b5786b7b2a767db1dbd7132a374b2f4f227.tar.gz
dokka-78850b5786b7b2a767db1dbd7132a374b2f4f227.tar.bz2
dokka-78850b5786b7b2a767db1dbd7132a374b2f4f227.zip
Make searchbar an extension point (#1615)
* Make searchbar an extension point * Change SearchbarDataInstaller to be a preprocessor * Split navigation and navigation search
-rw-r--r--core/src/main/kotlin/pages/RendererSpecificPage.kt6
-rw-r--r--plugins/base/src/main/kotlin/DokkaBase.kt11
-rw-r--r--plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt7
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt15
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/SearchbarDataInstaller.kt125
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt70
6 files changed, 138 insertions, 96 deletions
diff --git a/core/src/main/kotlin/pages/RendererSpecificPage.kt b/core/src/main/kotlin/pages/RendererSpecificPage.kt
index d7f4491a..a057b95e 100644
--- a/core/src/main/kotlin/pages/RendererSpecificPage.kt
+++ b/core/src/main/kotlin/pages/RendererSpecificPage.kt
@@ -5,7 +5,8 @@ import org.jetbrains.dokka.model.DisplaySourceSet
import org.jetbrains.dokka.renderers.Renderer
import kotlin.reflect.KClass
-typealias LocationResolver = (DRI, Set<DisplaySourceSet>) -> String
+fun interface DriResolver: (DRI, Set<DisplaySourceSet>) -> String?
+fun interface PageResolver: (PageNode, PageNode?) -> String?
interface RendererSpecificPage : PageNode {
val strategy: RenderingStrategy
@@ -33,7 +34,8 @@ sealed class RenderingStrategy {
class Callback(val instructions: Renderer.(PageNode) -> String): RenderingStrategy()
data class Copy(val from: String) : RenderingStrategy()
data class Write(val text: String) : RenderingStrategy()
- data class LocationResolvableWrite(val contentToResolve: (LocationResolver) -> String) : RenderingStrategy()
+ data class DriLocationResolvableWrite(val contentToResolve: (DriResolver) -> String) : RenderingStrategy()
+ data class PageLocationResolvableWrite(val contentToResolve: (PageResolver) -> String) : RenderingStrategy()
object DoNothing : RenderingStrategy()
companion object {
diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt
index a5d45282..e92759ff 100644
--- a/plugins/base/src/main/kotlin/DokkaBase.kt
+++ b/plugins/base/src/main/kotlin/DokkaBase.kt
@@ -39,7 +39,6 @@ class DokkaBase : DokkaPlugin() {
val kotlinAnalysis by extensionPoint<KotlinAnalysis>()
val tabSortingStrategy by extensionPoint<TabSortingStrategy>()
-
val descriptorToDocumentableTranslator by extending {
CoreExtensions.sourceToDocumentableTranslator providing ::DefaultDescriptorToDocumentableTranslator
}
@@ -169,7 +168,11 @@ class DokkaBase : DokkaPlugin() {
}
val navigationPageInstaller by extending {
- htmlPreprocessors with NavigationPageInstaller order { after(rootCreator) }
+ htmlPreprocessors providing ::NavigationPageInstaller order { after(rootCreator) }
+ }
+
+ val navigationSearchInstaller by extending {
+ htmlPreprocessors providing ::NavigationSearchInstaller order { after(rootCreator) }
}
val scriptsInstaller by extending {
@@ -207,4 +210,8 @@ class DokkaBase : DokkaPlugin() {
MultimodulePageCreator(it)
}
}
+
+ val baseSearchbarDataInstaller by extending {
+ htmlPreprocessors providing ::SearchbarDataInstaller order { after(sourceLinksTransformer) }
+ }
}
diff --git a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
index f393c0fa..182ecbc6 100644
--- a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
+++ b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
@@ -169,8 +169,11 @@ abstract class DefaultRenderer<T>(
is RenderingStrategy.Copy -> outputWriter.writeResources(strategy.from, path)
is RenderingStrategy.Write -> outputWriter.write(path, strategy.text, "")
is RenderingStrategy.Callback -> outputWriter.write(path, strategy.instructions(this, page), ".html")
- is RenderingStrategy.LocationResolvableWrite -> outputWriter.write(path, strategy.contentToResolve { dri, sourcesets ->
- locationProvider.resolveOrThrow(dri, sourcesets)
+ is RenderingStrategy.DriLocationResolvableWrite -> outputWriter.write(path, strategy.contentToResolve { dri, sourcesets ->
+ locationProvider.resolve(dri, sourcesets)
+ }, "")
+ is RenderingStrategy.PageLocationResolvableWrite -> outputWriter.write(path, strategy.contentToResolve { pageToLocate, context ->
+ locationProvider.resolve(pageToLocate, context)
}, "")
RenderingStrategy.DoNothing -> Unit
}
diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
index 615bbfc3..d943fefb 100644
--- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
@@ -41,8 +41,6 @@ open class HtmlRenderer(
override val preprocessors = context.plugin<DokkaBase>().query { htmlPreprocessors }
- val searchbarDataInstaller = SearchbarDataInstaller()
-
private val tabSortingStrategy = context.plugin<DokkaBase>().querySingle { tabSortingStrategy }
private fun <T : ContentNode> sortTabs(strategy: TabSortingStrategy, tabs: Collection<T>): List<T> {
@@ -682,14 +680,6 @@ open class HtmlRenderer(
}
}
-
- override suspend fun renderPage(page: PageNode) {
- super.renderPage(page)
- if (page is ContentPage && page !is ModulePageNode && page !is PackagePageNode)
- searchbarDataInstaller.processPage(page, locationProvider.resolve(page)
- ?: run { context.logger.error("Cannot resolve path for ${page.dri}"); "" })
- }
-
override fun FlowContent.buildText(textNode: ContentText) =
when {
textNode.hasStyle(TextStyle.Indented) -> {
@@ -703,11 +693,6 @@ open class HtmlRenderer(
override fun render(root: RootPageNode) {
shouldRenderSourceSetBubbles = shouldRenderSourceSetBubbles(root)
super.render(root)
- runBlocking(Dispatchers.Default) {
- launch {
- outputWriter.write("scripts/pages", "var pages = ${searchbarDataInstaller.generatePagesList()}", ".js")
- }
- }
}
private fun PageNode.root(path: String) = locationProvider.pathToRoot(this) + path
diff --git a/plugins/base/src/main/kotlin/renderers/html/SearchbarDataInstaller.kt b/plugins/base/src/main/kotlin/renderers/html/SearchbarDataInstaller.kt
index 3c562315..6ef6a6ec 100644
--- a/plugins/base/src/main/kotlin/renderers/html/SearchbarDataInstaller.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/SearchbarDataInstaller.kt
@@ -4,74 +4,107 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.jetbrains.dokka.Platform
import org.jetbrains.dokka.model.DisplaySourceSet
import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.model.withDescendants
import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.transformers.pages.PageTransformer
import java.util.concurrent.ConcurrentHashMap
-data class SearchRecord(val name: String, val description: String? = null, val location: String, val searchKeys: List<String> = listOf(name)) {
- companion object { }
+typealias PageId = String
+typealias Json = String
+
+data class SearchRecord(
+ val name: String,
+ val description: String? = null,
+ val location: String,
+ val searchKeys: List<String> = listOf(name)
+) {
+ companion object {}
}
-open class SearchbarDataInstaller {
- private val mapper = jacksonObjectMapper()
+open class SearchbarDataInstaller(val context: DokkaContext) : PageTransformer {
+ data class PageWithId(val id: PageId, val page: ContentPage) {
+ val displayableSignature = getSymbolSignature(page)?.let { flattenToText(it) } ?: page.name
+ }
- private val pageList = ConcurrentHashMap<String, Pair<String, String>>()
+ private val mapper = jacksonObjectMapper()
- open fun generatePagesList(): String {
- val pages = pageList.entries
+ open fun generatePagesList(pages: Map<PageId, PageWithId>, locationResolver: PageResolver): Json =
+ pages.entries
.filter { it.key.isNotEmpty() }
- .sortedWith(compareBy({ it.key }, { it.value.first }, { it.value.second }))
+ .sortedWith(compareBy({ it.key }, { it.value.displayableSignature }))
.groupBy { it.key.substringAfterLast(".") }
.entries
.flatMap { entry ->
entry.value.map { subentry ->
- val name = subentry.value.first
createSearchRecord(
- name = name,
+ name = subentry.value.displayableSignature,
description = subentry.key,
- location = subentry.value.second,
- searchKeys = listOf(entry.key, name)
+ location = resolveLocation(locationResolver, subentry.value.page).orEmpty(),
+ searchKeys = listOf(entry.key, subentry.value.displayableSignature)
)
}
- }
- return mapper.writeValueAsString(pages)
- }
+ }.run { mapper.writeValueAsString(this) }
- open fun createSearchRecord(name: String, description: String?, location: String, searchKeys: List<String>): SearchRecord =
+ open fun createSearchRecord(
+ name: String,
+ description: String?,
+ location: String,
+ searchKeys: List<String>
+ ): SearchRecord =
SearchRecord(name, description, location, searchKeys)
+ open fun processPage(page: PageNode): PageWithId? =
+ when (page) {
+ is ContentPage -> page.takeIf { page !is ModulePageNode && page !is PackagePageNode }?.documentable
+ ?.let { documentable ->
+ listOfNotNull(
+ documentable.dri.packageName,
+ documentable.dri.classNames,
+ documentable.dri.callable?.name
+ ).takeIf { it.isNotEmpty() }?.joinToString(".")
+ }?.let { id ->
+ PageWithId(id, page)
+ }
+ else -> null
+ }
- private fun getSymbolSignature(page: ContentPage) = page.content.dfs { it.dci.kind == ContentKind.Symbol }
+ private fun resolveLocation(locationResolver: PageResolver, page: ContentPage): String? =
+ locationResolver(page, null).also { location ->
+ if (location.isNullOrBlank()) context.logger.warn("Cannot resolve path for ${page.dri}")
+ }
- private fun flattenToText(node: ContentNode): String {
- fun getContentTextNodes(node: ContentNode, sourceSetRestriction: DisplaySourceSet): List<ContentText> =
- when (node) {
- is ContentText -> listOf(node)
- is ContentComposite -> node.children
- .filter { sourceSetRestriction in it.sourceSets }
- .flatMap { getContentTextNodes(it, sourceSetRestriction) }
- .takeIf { node.dci.kind != ContentKind.Annotations }
- .orEmpty()
- else -> emptyList()
- }
+ override fun invoke(input: RootPageNode): RootPageNode {
+ val page = RendererSpecificResourcePage(
+ name = "scripts/pages.js",
+ children = emptyList(),
+ strategy = RenderingStrategy.PageLocationResolvableWrite { resolver ->
+ input.withDescendants().fold(emptyMap<PageId, PageWithId>()) { pageList, page ->
+ processPage(page)?.let { pageList + Pair(it.id, it) } ?: pageList
+ }.run {
+ """var pages = ${generatePagesList(this, resolver)}"""
+ }
+ })
- val sourceSetRestriction =
- node.sourceSets.find { it.platform == Platform.common } ?: node.sourceSets.first()
- return getContentTextNodes(node, sourceSetRestriction).joinToString("") { it.text }
+ return input.modified(children = input.children + page)
}
+}
+
+private fun getSymbolSignature(page: ContentPage) = page.content.dfs { it.dci.kind == ContentKind.Symbol }
- open fun processPage(page: ContentPage, link: String) {
- val signature = getSymbolSignature(page)
- val textNodes = signature?.let { flattenToText(it) }
- val documentable = page.documentable
- if (documentable != null) {
- listOf(
- documentable.dri.packageName,
- documentable.dri.classNames,
- documentable.dri.callable?.name
- ).filter { !it.isNullOrEmpty() }
- .takeIf { it.isNotEmpty() }
- ?.joinToString(".")
- ?.let { id -> pageList.put(id, Pair(textNodes ?: page.name, link)) }
+private fun flattenToText(node: ContentNode): String {
+ fun getContentTextNodes(node: ContentNode, sourceSetRestriction: DisplaySourceSet): List<ContentText> =
+ when (node) {
+ is ContentText -> listOf(node)
+ is ContentComposite -> node.children
+ .filter { sourceSetRestriction in it.sourceSets }
+ .flatMap { getContentTextNodes(it, sourceSetRestriction) }
+ .takeIf { node.dci.kind != ContentKind.Annotations }
+ .orEmpty()
+ else -> emptyList()
}
- }
-}
+
+ val sourceSetRestriction =
+ node.sourceSets.find { it.platform == Platform.common } ?: node.sourceSets.first()
+ return getContentTextNodes(node, sourceSetRestriction).joinToString("") { it.text }
+} \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt
index 8e31fbc6..45159fea 100644
--- a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt
@@ -4,39 +4,18 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.DokkaBaseConfiguration
import org.jetbrains.dokka.base.renderers.sourceSets
-import org.jetbrains.dokka.model.DEnum
-import org.jetbrains.dokka.model.DEnumEntry
-import org.jetbrains.dokka.model.DFunction
-import org.jetbrains.dokka.model.withDescendants
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.configuration
import org.jetbrains.dokka.transformers.pages.PageTransformer
-object NavigationPageInstaller : PageTransformer {
- private val mapper = jacksonObjectMapper()
-
- fun SearchRecord.Companion.from(node: NavigationNode, location: String): SearchRecord =
- SearchRecord(name = node.name, location = location)
-
- override fun invoke(input: RootPageNode): RootPageNode {
- val nodes = input.children.filterIsInstance<ContentPage>().single()
- .let(NavigationPageInstaller::visit)
+abstract class NavigationDataProvider {
+ open fun navigableChildren(input: RootPageNode): NavigationNode =
+ input.children.filterIsInstance<ContentPage>().single().let { visit(it) }
- val page = RendererSpecificResourcePage(
- name = "scripts/navigation-pane.json",
- children = emptyList(),
- strategy = RenderingStrategy.LocationResolvableWrite { resolver ->
- mapper.writeValueAsString(
- nodes.withDescendants().map { SearchRecord.from(it, resolver(it.dri, it.sourceSets)) })
- })
-
- return input.modified(
- children = input.children + page + NavigationPage(nodes)
- )
- }
-
- private fun visit(page: ContentPage): NavigationNode =
+ open fun visit(page: ContentPage): NavigationNode =
NavigationNode(
name = page.displayableName,
dri = page.dri.first(),
@@ -61,6 +40,39 @@ object NavigationPageInstaller : PageTransformer {
}
}
+open class NavigationSearchInstaller(val context: DokkaContext) : NavigationDataProvider(), PageTransformer {
+ private val mapper = jacksonObjectMapper()
+
+ open fun createSearchRecordFromNode(node: NavigationNode, location: String): SearchRecord =
+ SearchRecord(name = node.name, location = location)
+
+ override fun invoke(input: RootPageNode): RootPageNode {
+ val page = RendererSpecificResourcePage(
+ name = "scripts/navigation-pane.json",
+ children = emptyList(),
+ strategy = RenderingStrategy.DriLocationResolvableWrite { resolver ->
+ mapper.writeValueAsString(
+ navigableChildren(input).withDescendants().map {
+ createSearchRecordFromNode(it, resolveLocation(resolver, it.dri, it.sourceSets).orEmpty())
+ })
+ })
+
+ return input.modified(children = input.children + page)
+ }
+
+ private fun resolveLocation(locationResolver: DriResolver, dri: DRI, sourceSets: Set<DisplaySourceSet>): String? =
+ locationResolver(dri, sourceSets).also { location ->
+ if (location.isNullOrBlank()) context.logger.warn("Cannot resolve path for $dri and sourceSets: ${sourceSets.joinToString { it.name }}")
+ }
+
+}
+
+open class NavigationPageInstaller(val context: DokkaContext) : NavigationDataProvider(), PageTransformer {
+
+ override fun invoke(input: RootPageNode): RootPageNode =
+ input.modified(children = input.children + NavigationPage(navigableChildren(input)))
+}
+
class CustomResourceInstaller(val dokkaContext: DokkaContext) : PageTransformer {
private val configuration = configuration<DokkaBase, DokkaBaseConfiguration>(dokkaContext)
@@ -74,7 +86,8 @@ class CustomResourceInstaller(val dokkaContext: DokkaContext) : PageTransformer
override fun invoke(input: RootPageNode): RootPageNode {
val customResourcesPaths = (customAssets + customStylesheets).map { it.name }.toSet()
- val withEmbeddedResources = input.transformContentPagesTree { it.modified(embeddedResources = it.embeddedResources + customResourcesPaths) }
+ val withEmbeddedResources =
+ input.transformContentPagesTree { it.modified(embeddedResources = it.embeddedResources + customResourcesPaths) }
val (currentResources, otherPages) = withEmbeddedResources.children.partition { it is RendererSpecificResourcePage }
return input.modified(children = otherPages + currentResources.filterNot { it.name in customResourcesPaths } + customAssets + customStylesheets)
}
@@ -166,4 +179,3 @@ class SourcesetDependencyAppender(val context: DokkaContext) : PageTransformer {
}
}
-