Merge remote-tracking branch 'upstream/java-layout-html-format' into devsite-with-java-layout-html
diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt
index 126a017..4b75f31 100644
--- a/core/src/main/kotlin/DokkaBootstrapImpl.kt
+++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt
@@ -69,7 +69,8 @@
                         languageVersion,
                         apiVersion,
                         cacheRoot,
-                        suppressedFiles.map { File(it) }
+                        suppressedFiles.map { File(it) },
+                        collectInheritedExtensionsFromLibraries
                 )
         )
     }
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt
index 5148097..494e05f 100644
--- a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt
@@ -63,7 +63,7 @@
 
 interface JavaLayoutHtmlTemplateService {
     fun composePage(
-            nodes: List<DocumentationNode>,
+            page: JavaLayoutHtmlFormatOutputBuilder.Page,
             tagConsumer: TagConsumer<Appendable>,
             headContent: HEAD.() -> Unit,
             bodyContent: BODY.() -> Unit
@@ -71,7 +71,7 @@
 
     class Default : JavaLayoutHtmlTemplateService {
         override fun composePage(
-                nodes: List<DocumentationNode>,
+                page: JavaLayoutHtmlFormatOutputBuilder.Page,
                 tagConsumer: TagConsumer<Appendable>,
                 headContent: HEAD.() -> Unit,
                 bodyContent: BODY.() -> Unit
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt
index 0c95a41..a8b754f 100644
--- a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt
@@ -12,19 +12,117 @@
 
 
 open class JavaLayoutHtmlFormatOutputBuilder(
-        val output: Appendable,
-        val languageService: LanguageService,
-        val uriProvider: JavaLayoutHtmlUriProvider,
-        val templateService: JavaLayoutHtmlTemplateService,
-        val logger: DokkaLogger,
-        val uri: URI
+    val output: Appendable,
+    val languageService: LanguageService,
+    val uriProvider: JavaLayoutHtmlUriProvider,
+    val templateService: JavaLayoutHtmlTemplateService,
+    val logger: DokkaLogger,
+    val uri: URI
 ) {
 
     val htmlConsumer = output.appendHTML()
 
-    val contentToHtmlBuilder = ContentToHtmlBuilder(uriProvider, uri)
 
-    open fun <T> FlowContent.summaryNodeGroup(nodes: Iterable<T>, header: String, headerAsRow: Boolean = false, row: TBODY.(T) -> Unit) {
+    private fun FlowContent.hN(
+        level: Int,
+        classes: String? = null,
+        block: CommonAttributeGroupFacadeFlowHeadingPhrasingContent.() -> Unit
+    ) {
+        when (level) {
+            1 -> h1(classes, block)
+            2 -> h2(classes, block)
+            3 -> h3(classes, block)
+            4 -> h4(classes, block)
+            5 -> h5(classes, block)
+            6 -> h6(classes, block)
+        }
+    }
+
+    protected open fun FlowContent.metaMarkup(content: List<ContentNode>) = contentNodesToMarkup(content)
+    protected open fun FlowContent.metaMarkup(content: ContentNode) = contentNodeToMarkup(content)
+
+    private fun FlowContent.contentNodesToMarkup(content: List<ContentNode>): Unit = content.forEach { contentNodeToMarkup(it) }
+    private fun FlowContent.contentNodeToMarkup(content: ContentNode) {
+        when (content) {
+            is ContentText -> +content.text
+            is ContentSymbol -> span("symbol") { +content.text }
+            is ContentKeyword -> span("keyword") { +content.text }
+            is ContentIdentifier -> span("identifier") {
+                content.signature?.let { id = it }
+                +content.text
+            }
+
+            is ContentHeading -> hN(level = content.level) { contentNodesToMarkup(content.children) }
+
+            is ContentEntity -> +content.text
+
+            is ContentStrong -> strong { contentNodesToMarkup(content.children) }
+            is ContentStrikethrough -> del { contentNodesToMarkup(content.children) }
+            is ContentEmphasis -> em { contentNodesToMarkup(content.children) }
+
+            is ContentOrderedList -> ol { contentNodesToMarkup(content.children) }
+            is ContentUnorderedList -> ul { contentNodesToMarkup(content.children) }
+            is ContentListItem -> consumer.li {
+                (content.children.singleOrNull() as? ContentParagraph)
+                    ?.let { paragraph -> contentNodesToMarkup(paragraph.children) }
+                        ?: contentNodesToMarkup(content.children)
+            }
+
+
+            is ContentCode -> contentInlineCode(content)
+            is ContentBlockSampleCode -> contentBlockSampleCode(content)
+            is ContentBlockCode -> contentBlockCode(content)
+
+
+            ContentNonBreakingSpace -> +nbsp
+            ContentSoftLineBreak, ContentIndentedSoftLineBreak -> {}
+            ContentHardLineBreak -> br
+
+            is ContentParagraph -> p { contentNodesToMarkup(content.children) }
+
+            is ContentNodeLink -> {
+                a(href = content.node) { contentNodesToMarkup(content.children) }
+            }
+            is ContentExternalLink -> contentExternalLink(content)
+            is ContentSection -> {}
+            is ContentBlock -> contentNodesToMarkup(content.children)
+        }
+    }
+
+    protected open fun FlowContent.contentInlineCode(content: ContentCode) {
+        code { contentNodesToMarkup(content.children) }
+    }
+
+    protected open fun FlowContent.contentBlockSampleCode(content: ContentBlockSampleCode) {
+        pre {
+            code {
+                attributes["data-language"] = content.language
+                contentNodesToMarkup(content.importsBlock.children)
+                +"\n\n"
+                contentNodesToMarkup(content.children)
+            }
+        }
+    }
+
+    protected open fun FlowContent.contentBlockCode(content: ContentBlockCode) {
+        pre {
+            code {
+                attributes["data-language"] = content.language
+                contentNodesToMarkup(content.children)
+            }
+        }
+    }
+
+    protected open fun FlowContent.contentExternalLink(content: ContentExternalLink) {
+        a(href = content.href) { contentNodesToMarkup(content.children) }
+    }
+
+    protected open fun <T> FlowContent.summaryNodeGroup(
+        nodes: Iterable<T>,
+        header: String,
+        headerAsRow: Boolean = true,
+        row: TBODY.(T) -> Unit
+    ) {
         if (nodes.none()) return
         if (!headerAsRow) {
             h2 { +header }
@@ -39,38 +137,31 @@
         }
     }
 
-    fun FlowContent.metaMarkup(content: ContentNode) = with(contentToHtmlBuilder) {
-        appendContent(content)
-    }
 
-    fun FlowContent.metaMarkup(content: List<ContentNode>) = with(contentToHtmlBuilder) {
-        appendContent(content)
-    }
-
-    open fun TBODY.classLikeRow(node: DocumentationNode) = tr {
+    protected open fun TBODY.classLikeRow(node: DocumentationNode) = tr {
         td { a(href = uriProvider.linkTo(node, uri)) { +node.simpleName() } }
-        td { metaMarkup(node.summary) }
+        td { contentNodeToMarkup(node.summary) }
     }
 
-    fun FlowContent.modifiers(node: DocumentationNode) {
+    protected fun FlowContent.modifiers(node: DocumentationNode) {
         for (modifier in node.details(NodeKind.Modifier)) {
             renderedSignature(modifier, SUMMARY)
         }
     }
 
-    fun FlowContent.shortFunctionParametersList(func: DocumentationNode) {
+    protected fun FlowContent.shortFunctionParametersList(func: DocumentationNode) {
         val params = func.details(NodeKind.Parameter)
-                .map { languageService.render(it, FULL) }
-                .run {
-                    drop(1).fold(listOfNotNull(firstOrNull())) { acc, node ->
-                        acc + ContentText(", ") + node
-                    }
+            .map { languageService.render(it, FULL) }
+            .run {
+                drop(1).fold(listOfNotNull(firstOrNull())) { acc, node ->
+                    acc + ContentText(", ") + node
                 }
+            }
         metaMarkup(listOf(ContentText("(")) + params + listOf(ContentText(")")))
     }
 
 
-    open fun TBODY.functionLikeSummaryRow(node: DocumentationNode) = tr {
+    protected open fun TBODY.functionLikeSummaryRow(node: DocumentationNode) = tr {
         if (node.kind != NodeKind.Constructor) {
             td {
                 modifiers(node)
@@ -85,16 +176,16 @@
                         renderedSignature(receiver.detail(NodeKind.Type), SUMMARY)
                         +"."
                     }
-                    a(href = uriProvider.linkTo(node, uri)) { +node.name }
+                    a(href = node) { +node.name }
                     shortFunctionParametersList(node)
                 }
             }
 
-            metaMarkup(node.summary)
+            contentNodeToMarkup(node.summary)
         }
     }
 
-    open fun TBODY.propertyLikeSummaryRow(node: DocumentationNode) = tr {
+    protected open fun TBODY.propertyLikeSummaryRow(node: DocumentationNode) = tr {
         td {
             modifiers(node)
             renderedSignature(node.detail(NodeKind.Type), SUMMARY)
@@ -102,34 +193,37 @@
         td {
             div {
                 code {
-                    a(href = uriProvider.linkTo(node, uri)) { +node.name }
+                    a(href = node) { +node.name }
                 }
             }
 
-            metaMarkup(node.summary)
+            contentNodeToMarkup(node.summary)
         }
     }
 
-    open fun TBODY.nestedClassSummaryRow(node: DocumentationNode) = tr {
+    protected open fun TBODY.nestedClassSummaryRow(node: DocumentationNode) = tr {
         td {
             modifiers(node)
         }
         td {
             div {
                 code {
-                    a(href = uriProvider.linkTo(node, uri)) { +node.name }
+                    a(href = node) { +node.name }
                 }
             }
 
-            metaMarkup(node.summary)
+            contentNodeToMarkup(node.summary)
         }
     }
 
-    open fun TBODY.inheritRow(entry: Map.Entry<DocumentationNode, List<DocumentationNode>>, summaryRow: TBODY.(DocumentationNode) -> Unit) = tr {
+    protected open fun TBODY.inheritRow(
+        entry: Map.Entry<DocumentationNode, List<DocumentationNode>>,
+        summaryRow: TBODY.(DocumentationNode) -> Unit
+    ) = tr {
         td {
             val (from, nodes) = entry
             +"From class "
-            a(href = uriProvider.linkTo(from.owner!!, uri)) { +from.qualifiedName() }
+            a(href = from.owner!!) { +from.qualifiedName() }
             table {
                 tbody {
                     for (node in nodes) {
@@ -140,11 +234,14 @@
         }
     }
 
-    open fun TBODY.extensionRow(entry: Map.Entry<DocumentationNode, List<DocumentationNode>>, summaryRow: TBODY.(DocumentationNode) -> Unit) = tr {
+    protected open fun TBODY.extensionRow(
+        entry: Map.Entry<DocumentationNode, List<DocumentationNode>>,
+        summaryRow: TBODY.(DocumentationNode) -> Unit
+    ) = tr {
         td {
             val (from, nodes) = entry
             +"From "
-            a(href = uriProvider.linkTo(from, uri)) { +from.qualifiedName() }
+            a(href = from) { +from.qualifiedName() }
             table {
                 tbody {
                     for (node in nodes) {
@@ -155,16 +252,375 @@
         }
     }
 
-    private fun FlowContent.renderedSignature(node: DocumentationNode, mode: LanguageService.RenderMode = SUMMARY) {
-        metaMarkup(languageService.render(node, mode))
+    protected open fun FlowContent.a(href: DocumentationNode?, classes: String? = null, block: A.() -> Unit) {
+        if (href == null) {
+            return a(href = "#", classes = classes, block = block)
+        }
+
+        val hrefText =
+            href.name.takeIf { href.kind == NodeKind.ExternalLink }
+                    ?: href.links.firstOrNull { it.kind == NodeKind.ExternalLink }?.name
+                    ?: "#".takeIf { href.kind == NodeKind.ExternalClass } // When external class unresolved
+                    ?: uriProvider.linkTo(href, uri)
+
+        a(href = hrefText, classes = classes, block = block)
     }
 
-    open fun FlowContent.memberDocs(node: DocumentationNode) {
+    protected open fun FlowContent.renderedSignature(node: DocumentationNode, mode: LanguageService.RenderMode = SUMMARY) {
+        contentNodeToMarkup(languageService.render(node, mode))
+    }
+
+    protected open fun generatePackage(page: Page.PackagePage) = templateService.composePage(
+        page,
+        htmlConsumer,
+        headContent = {
+
+        },
+        bodyContent = {
+            h1 { +page.node.name }
+            contentNodeToMarkup(page.node.content)
+            summaryNodeGroup(page.classes, "Classes", headerAsRow = false) { classLikeRow(it) }
+            summaryNodeGroup(page.exceptions, "Exceptions", headerAsRow = false) { classLikeRow(it) }
+            summaryNodeGroup(page.typeAliases, "Type-aliases", headerAsRow = false) { classLikeRow(it) }
+            summaryNodeGroup(page.annotations, "Annotations", headerAsRow = false) { classLikeRow(it) }
+            summaryNodeGroup(page.enums, "Enums", headerAsRow = false) { classLikeRow(it) }
+
+            summaryNodeGroup(
+                page.constants,
+                "Top-level constants summary",
+                headerAsRow = false
+            ) {
+                propertyLikeSummaryRow(it)
+            }
+
+            summaryNodeGroup(
+                page.functions,
+                "Top-level functions summary",
+                headerAsRow = false
+            ) {
+                functionLikeSummaryRow(it)
+            }
+
+            summaryNodeGroup(
+                page.properties,
+                "Top-level properties summary",
+                headerAsRow = false
+            ) {
+                propertyLikeSummaryRow(it)
+            }
+
+
+            fullMemberDocs(page.constants, "Top-level constants")
+            fullMemberDocs(page.functions, "Top-level functions")
+            fullMemberDocs(page.properties, "Top-level properties")
+        }
+    )
+
+    protected fun FlowContent.qualifiedTypeReference(node: DocumentationNode) {
+        if (node.kind in classLike) {
+            a(href = node) { +node.qualifiedName() }
+            return
+        }
+
+        val targetLink = node.links.singleOrNull()
+
+        if (targetLink?.kind == NodeKind.TypeParameter) {
+            +node.name
+            return
+        }
+
+        a(href = targetLink) {
+            +node.qualifiedNameFromType()
+        }
+        val typeParameters = node.details(NodeKind.Type)
+        if (typeParameters.isNotEmpty()) {
+            +"<"
+            typeParameters.forEach {
+                if (it != typeParameters.first()) {
+                    +", "
+                }
+                qualifiedTypeReference(it)
+            }
+            +">"
+        }
+    }
+
+    protected open fun FlowContent.classHierarchy(superclasses: List<DocumentationNode>) {
+        table {
+            superclasses.forEach {
+                tr {
+                    if (it != superclasses.first()) {
+                        td {
+                            +"   ↳"
+                        }
+                    }
+                    td {
+                        qualifiedTypeReference(it)
+                    }
+                }
+            }
+        }
+    }
+
+    protected open fun FlowContent.subclasses(inheritors: List<DocumentationNode>, direct: Boolean) {
+        if (inheritors.isEmpty()) return
+        div {
+            table {
+                thead {
+                    tr {
+                        td {
+                            if (direct)
+                                +"Known Direct Subclasses"
+                            else
+                                +"Known Indirect Subclasses"
+                        }
+                    }
+                }
+                tbody {
+                    inheritors.forEach { inheritor ->
+                        tr {
+                            td {
+                                a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() }
+                            }
+                            td {
+                                contentNodeToMarkup(inheritor.summary)
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    protected open fun FlowContent.classLikeSummaries(page: Page.ClassPage) = with(page) {
+        summaryNodeGroup(
+            nestedClasses,
+            "Nested classes",
+            headerAsRow = true
+        ) {
+            nestedClassSummaryRow(it)
+        }
+
+        summaryNodeGroup(constants, "Constants") { propertyLikeSummaryRow(it) }
+
+        summaryNodeGroup(
+            constructors,
+            "Constructors",
+            headerAsRow = true
+        ) {
+            functionLikeSummaryRow(it)
+        }
+
+        summaryNodeGroup(functions, "Functions", headerAsRow = true) { functionLikeSummaryRow(it) }
+        summaryNodeGroup(
+            companionFunctions,
+            "Companion functions",
+            headerAsRow = true
+        ) {
+            functionLikeSummaryRow(it)
+        }
+        summaryNodeGroup(
+            inheritedFunctionsByReceiver.entries,
+            "Inherited functions",
+            headerAsRow = true
+        ) {
+            inheritRow(it) {
+                functionLikeSummaryRow(it)
+            }
+        }
+        summaryNodeGroup(
+            extensionFunctions.entries,
+            "Extension functions",
+            headerAsRow = true
+        ) {
+            extensionRow(it) {
+                functionLikeSummaryRow(it)
+            }
+        }
+        summaryNodeGroup(
+            inheritedExtensionFunctions.entries,
+            "Inherited extension functions",
+            headerAsRow = true
+        ) {
+            extensionRow(it) {
+                functionLikeSummaryRow(it)
+            }
+        }
+
+
+        summaryNodeGroup(properties, "Properties", headerAsRow = true) { propertyLikeSummaryRow(it) }
+        summaryNodeGroup(
+            companionProperties,
+            "Companion properties",
+            headerAsRow = true
+        ) {
+            propertyLikeSummaryRow(it)
+        }
+
+        summaryNodeGroup(
+            inheritedPropertiesByReceiver.entries,
+            "Inherited properties",
+            headerAsRow = true
+        ) {
+            inheritRow(it) {
+                propertyLikeSummaryRow(it)
+            }
+        }
+        summaryNodeGroup(
+            extensionProperties.entries,
+            "Extension properties",
+            headerAsRow = true
+        ) {
+            extensionRow(it) {
+                propertyLikeSummaryRow(it)
+            }
+        }
+        summaryNodeGroup(
+            inheritedExtensionProperties.entries,
+            "Inherited extension properties",
+            headerAsRow = true
+        ) {
+            extensionRow(it) {
+                propertyLikeSummaryRow(it)
+            }
+        }
+    }
+
+    protected open fun FlowContent.classLikeFullMemberDocs(page: Page.ClassPage) = with(page) {
+        fullMemberDocs(constants, "Constants")
+        fullMemberDocs(constructors, "Constructors")
+        fullMemberDocs(functions, "Functions")
+        fullMemberDocs(properties, "Properties")
+        if (!hasMeaningfulCompanion) {
+            fullMemberDocs(companionFunctions, "Companion functions")
+            fullMemberDocs(companionProperties, "Companion properties")
+        }
+    }
+
+    protected open fun generateClassLike(page: Page.ClassPage) = templateService.composePage(
+        page,
+        htmlConsumer,
+        headContent = {
+
+        },
+        bodyContent = {
+            val node = page.node
+            with(page) {
+                h1 { +node.name }
+                pre { renderedSignature(node, FULL) }
+                classHierarchy(page.superclasses)
+
+                subclasses(page.directInheritors, true)
+                subclasses(page.indirectInheritors, false)
+
+                contentNodeToMarkup(node.content)
+
+                h2 { +"Summary" }
+                classLikeSummaries(page)
+
+                classLikeFullMemberDocs(page)
+            }
+        }
+    )
+
+    protected open fun generateClassIndex(page: Page.ClassIndex) = templateService.composePage(
+        page,
+        htmlConsumer,
+        headContent = {
+
+        },
+        bodyContent = {
+            h1 { +"Class Index" }
+
+
+            ul {
+                page.classesByFirstLetter.forEach { (letter) ->
+                    li { a(href = "#letter_$letter") { +letter } }
+                }
+            }
+
+            page.classesByFirstLetter.forEach { (letter, classes) ->
+                h2 {
+                    id = "letter_$letter"
+                    +letter
+                }
+                table {
+                    tbody {
+                        for (node in classes) {
+                            tr {
+                                td {
+                                    a(href = uriProvider.linkTo(node, uri)) { +node.classNodeNameWithOuterClass() }
+                                }
+                                td {
+                                    contentNodeToMarkup(node.content)
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    )
+
+    protected open fun generatePackageIndex(page: Page.PackageIndex) = templateService.composePage(
+        page,
+        htmlConsumer,
+        headContent = {
+
+        },
+        bodyContent = {
+            h1 { +"Package Index" }
+            table {
+                tbody {
+                    for (node in page.packages) {
+                        tr {
+                            td {
+                                a(href = uriProvider.linkTo(node, uri)) { +node.name }
+                            }
+                            td {
+                                contentNodeToMarkup(node.content)
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    )
+
+    fun generatePage(page: Page) {
+        when (page) {
+            is Page.PackageIndex -> generatePackageIndex(page)
+            is Page.ClassIndex -> generateClassIndex(page)
+            is Page.ClassPage -> generateClassLike(page)
+            is Page.PackagePage -> generatePackage(page)
+        }
+    }
+
+    protected fun FlowContent.fullMemberDocs(
+        nodes: List<DocumentationNode>,
+        header: String
+    ) {
+        if (nodes.none()) return
+        h2 {
+            +header
+        }
+        for (node in nodes) {
+            fullMemberDocs(node)
+        }
+    }
+
+    protected open fun FlowContent.fullMemberDocs(node: DocumentationNode) {
         div {
             id = node.signatureForAnchor(logger)
             h3 { +node.name }
             pre { renderedSignature(node, FULL) }
-            metaMarkup(node.content)
+            contentNodeToMarkup(node.content)
+            node.constantValue()?.let { value ->
+                pre {
+                    +"Value: "
+                    code { +value }
+                }
+            }
             for ((name, sections) in node.content.sections.groupBy { it.tag }) {
                 table {
                     thead { tr { td { h3 { +name } } } }
@@ -183,357 +639,144 @@
         }
     }
 
-    fun appendPackage(node: DocumentationNode) = templateService.composePage(
-            listOf(node),
-            htmlConsumer,
-            headContent = {
-
-            },
-            bodyContent = {
-                h1 { +node.name }
-                metaMarkup(node.content)
-                summaryNodeGroup(node.members(NodeKind.Class), "Classes") { classLikeRow(it) }
-                summaryNodeGroup(node.members(NodeKind.Exception), "Exceptions") { classLikeRow(it) }
-                summaryNodeGroup(node.members(NodeKind.TypeAlias), "Type-aliases") { classLikeRow(it) }
-                summaryNodeGroup(node.members(NodeKind.AnnotationClass), "Annotations") { classLikeRow(it) }
-                summaryNodeGroup(node.members(NodeKind.Enum), "Enums") { classLikeRow(it) }
-
-                summaryNodeGroup(node.members(NodeKind.Function), "Top-level functions summary") { functionLikeSummaryRow(it) }
-                summaryNodeGroup(node.members(NodeKind.Property), "Top-level properties summary") { propertyLikeSummaryRow(it) }
-
-
-                fullDocs(node.members(NodeKind.Function), "Top-level functions") { memberDocs(it) }
-                fullDocs(node.members(NodeKind.Property), "Top-level properties") { memberDocs(it) }
+    sealed class Page {
+        class PackageIndex(packages: List<DocumentationNode>) : Page() {
+            init {
+                assert(packages.all { it.kind == NodeKind.Package })
             }
-    )
 
-    fun FlowContent.qualifiedTypeReference(node: DocumentationNode) {
-        if (node.kind in classLike) {
-            a(href = uriProvider.linkTo(node, uri)) {
-                +node.qualifiedName()
+            val packages = packages.sortedBy { it.name }
+        }
+
+        class ClassIndex(allTypesNode: DocumentationNode) : Page() {
+            init {
+                assert(allTypesNode.kind == NodeKind.AllTypes)
             }
-            return
-        }
 
-        val targetLink = node.links.single()
+            // Wide-collect all nested classes
+            val classes: List<DocumentationNode> =
+                generateSequence(listOf(allTypesNode)) { nodes ->
+                    nodes
+                        .flatMap { it.members.filter { it.kind in NodeKind.classLike } }
+                        .takeUnless { it.isEmpty() }
+                }.drop(1)
+                    .flatten()
+                    .sortedBy { it.classNodeNameWithOuterClass() }
+                    .toList()
 
-        if (targetLink.kind == NodeKind.TypeParameter) {
-            +node.name
-            return
-        }
 
-        val href = if (targetLink.kind == NodeKind.ExternalLink)
-            targetLink.name
-        else
-            uriProvider.linkTo(targetLink, uri)
-
-        a(href = href) {
-            +node.qualifiedNameFromType()
-        }
-        val typeParameters = node.details(NodeKind.Type)
-        if (typeParameters.isNotEmpty()) {
-            +"<"
-            typeParameters.forEach {
-                if (it != typeParameters.first()) {
-                    +", "
-                }
-                qualifiedTypeReference(it)
-            }
-            +">"
-        }
-    }
-
-    open fun FlowContent.classHierarchy(node: DocumentationNode) {
-
-        val superclasses = generateSequence(node.superclass) { it.links.single().superclass }.toList().asReversed() + node
-        table {
-            superclasses.forEach {
-                tr {
-                    if (it != superclasses.first()) {
-                        td {
-                            +"   ↳"
-                        }
+            // Group all classes by it's first letter and sort
+            val classesByFirstLetter =
+                classes
+                    .groupBy {
+                        it.classNodeNameWithOuterClass().first().toString()
                     }
-                    td {
-                        qualifiedTypeReference(it)
-                    }
-                }
-            }
+                    .entries
+                    .sortedBy { (letter) -> letter }
         }
-    }
 
-    open fun FlowContent.subclasses(inheritors: List<DocumentationNode>, direct: Boolean) {
-        if (inheritors.isEmpty()) return
-        div {
-            table {
-                thead {
-                    tr {
-                        td {
-                            if (direct)
-                                +"Known Direct Subclasses"
-                            else
-                                +"Known Indirect Subclasses"
-                        }
-                    }
-                }
-                tbody {
-                    inheritors.forEach {
-                        tr {
-                            td {
-                                a(href = uriProvider.linkTo(it, uri)) { +it.classNodeNameWithOuterClass() }
-                            }
-                            td {
-                                metaMarkup(it.summary)
-                            }
-                        }
-                    }
-                }
+        class ClassPage(val node: DocumentationNode) : Page() {
+
+            init {
+                assert(node.kind in NodeKind.classLike)
             }
-        }
-    }
 
-    fun appendClassLike(node: DocumentationNode) = templateService.composePage(
-            listOf(node),
-            htmlConsumer,
-            headContent = {
+            val superclasses = (sequenceOf(node) + node.superclassTypeSequence).toList().asReversed()
 
-            },
-            bodyContent = {
-                h1 { +node.name }
-                pre { renderedSignature(node, FULL) }
-                classHierarchy(node)
 
+            val directInheritors: List<DocumentationNode>
+            val indirectInheritors: List<DocumentationNode>
+
+            init {
+                // Wide-collect all inheritors
                 val inheritors = generateSequence(node.inheritors) { inheritors ->
                     inheritors
-                            .flatMap { it.inheritors }
-                            .takeUnless { it.isEmpty() }
+                        .flatMap { it.inheritors }
+                        .takeUnless { it.isEmpty() }
                 }
-                subclasses(inheritors.first(), true)
-                subclasses(inheritors.drop(1).flatten().toList(), false)
+                directInheritors = inheritors.first()
+                indirectInheritors = inheritors.drop(1).flatten().toList()
+            }
+
+            val isCompanion = node.details(NodeKind.Modifier).any { it.name == "companion" }
+            val hasMeaningfulCompanion = !isCompanion && node.companion != null
+
+            private fun DocumentationNode.thisTypeExtension() =
+                detail(NodeKind.Receiver).detail(NodeKind.Type).links.any { it == node }
+
+            val functionKind = if (!isCompanion) NodeKind.Function else NodeKind.CompanionObjectFunction
+            val propertyKind = if (!isCompanion) NodeKind.Property else NodeKind.CompanionObjectProperty
+
+            private fun DocumentationNode.isFunction() = kind == functionKind
+            private fun DocumentationNode.isProperty() = kind == propertyKind
 
 
-                metaMarkup(node.content)
+            val nestedClasses = node.members.filter { it.kind in NodeKind.classLike }
 
-                h2 { +"Summary" }
+            val constants = node.members.filter { it.constantValue() != null }
 
-                val isCompanion = node.details(NodeKind.Modifier).any { it.name == "companion" }
-                val hasMeaningfulCompanion = !isCompanion && node.companion != null
+            val constructors = node.members(NodeKind.Constructor)
+            val functions = node.members(functionKind)
+            val properties = node.members(propertyKind) - constants
+            val inheritedFunctionsByReceiver = node.inheritedMembers(functionKind).groupBy { it.owner!! }
+            val inheritedPropertiesByReceiver = node.inheritedMembers(propertyKind).groupBy { it.owner!! }
 
-                fun DocumentationNode.thisTypeExtension() = detail(NodeKind.Receiver).detail(NodeKind.Type).links.any { it == node }
+            val originalExtensions = if (!isCompanion) node.extensions else node.owner!!.extensions
 
-                val functionKind = if (!isCompanion) NodeKind.Function else NodeKind.CompanionObjectFunction
-                val propertyKind = if (!isCompanion) NodeKind.Property else NodeKind.CompanionObjectProperty
+            val extensionFunctions: Map<DocumentationNode, List<DocumentationNode>>
+            val extensionProperties: Map<DocumentationNode, List<DocumentationNode>>
+            val inheritedExtensionFunctions: Map<DocumentationNode, List<DocumentationNode>>
+            val inheritedExtensionProperties: Map<DocumentationNode, List<DocumentationNode>>
 
-                fun DocumentationNode.isFunction() = kind == functionKind
-                fun DocumentationNode.isProperty() = kind == propertyKind
-
-                val functions = node.members(functionKind)
-                val properties = node.members(propertyKind)
-                val inheritedFunctionsByReceiver = node.inheritedMembers(functionKind).groupBy { it.owner!! }
-                val inheritedPropertiesByReceiver = node.inheritedMembers(propertyKind).groupBy { it.owner!! }
-
-
-                val originalExtensions = if (!isCompanion) node.extensions else node.owner!!.extensions
+            init {
                 val (extensions, inheritedExtensions) = originalExtensions.partition { it.thisTypeExtension() }
-                val extensionFunctions = extensions.filter(DocumentationNode::isFunction).groupBy { it.owner!! }
-                val extensionProperties = extensions.filter(DocumentationNode::isProperty).groupBy { it.owner!! }
-                val inheritedExtensionFunctions = inheritedExtensions.filter(DocumentationNode::isFunction).groupBy { it.owner!! }
-                val inheritedExtensionProperties = inheritedExtensions.filter(DocumentationNode::isProperty).groupBy { it.owner!! }
-
-                val companionFunctions = node.members(NodeKind.CompanionObjectFunction)
-                val companionProperties = node.members(NodeKind.CompanionObjectProperty)
-
-                summaryNodeGroup(node.members.filter { it.kind in NodeKind.classLike }, "Nested classes", headerAsRow = true) { nestedClassSummaryRow(it) }
-
-                summaryNodeGroup(node.members(NodeKind.Constructor), "Constructors", headerAsRow = true) { functionLikeSummaryRow(it) }
-
-                summaryNodeGroup(functions, "Functions", headerAsRow = true) { functionLikeSummaryRow(it) }
-                if (!isCompanion) {
-                    summaryNodeGroup(companionFunctions, "Companion functions", headerAsRow = true) { functionLikeSummaryRow(it) }
-                }
-                summaryNodeGroup(inheritedFunctionsByReceiver.entries, "Inherited functions", headerAsRow = true) { inheritRow(it) { functionLikeSummaryRow(it) } }
-                summaryNodeGroup(extensionFunctions.entries, "Extension functions", headerAsRow = true) { extensionRow(it) { functionLikeSummaryRow(it) } }
-                summaryNodeGroup(inheritedExtensionFunctions.entries, "Inherited extension functions", headerAsRow = true) { extensionRow(it) { functionLikeSummaryRow(it) } }
-
-
-                summaryNodeGroup(properties, "Properties", headerAsRow = true) { propertyLikeSummaryRow(it) }
-                if (!isCompanion) {
-                    summaryNodeGroup(companionProperties, "Companion properties", headerAsRow = true) { propertyLikeSummaryRow(it) }
-                }
-                summaryNodeGroup(inheritedPropertiesByReceiver.entries, "Inherited properties", headerAsRow = true) { inheritRow(it) { propertyLikeSummaryRow(it) } }
-                summaryNodeGroup(extensionProperties.entries, "Extension properties", headerAsRow = true) { extensionRow(it) { propertyLikeSummaryRow(it) } }
-                summaryNodeGroup(inheritedExtensionProperties.entries, "Inherited extension properties", headerAsRow = true) { extensionRow(it) { propertyLikeSummaryRow(it) } }
-
-
-                fullDocs(node.members(NodeKind.Constructor), "Constructors") { memberDocs(it) }
-                fullDocs(functions, "Functions") { memberDocs(it) }
-                fullDocs(properties, "Properties") { memberDocs(it) }
-                if (!isCompanion && !hasMeaningfulCompanion) {
-                    fullDocs(companionFunctions, "Companion functions") { memberDocs(it) }
-                    fullDocs(companionProperties, "Companion properties") { memberDocs(it) }
-                }
+                extensionFunctions = extensions.filter { it.isFunction() }.groupBy { it.owner!! }
+                extensionProperties = extensions.filter { it.isProperty() }.groupBy { it.owner!! }
+                inheritedExtensionFunctions =
+                        inheritedExtensions.filter { it.isFunction() }.groupBy { it.owner!! }
+                inheritedExtensionProperties =
+                        inheritedExtensions.filter { it.isProperty() }.groupBy { it.owner!! }
             }
-    )
 
-    fun generateClassesIndex(allTypesNode: DocumentationNode) = templateService.composePage(
-            listOf(allTypesNode),
-            htmlConsumer,
-            headContent = {
+            val companionFunctions = node.members(NodeKind.CompanionObjectFunction).takeUnless { isCompanion }.orEmpty()
+            val companionProperties =
+                node.members(NodeKind.CompanionObjectProperty).takeUnless { isCompanion }.orEmpty() - constants
 
-            },
-            bodyContent = {
-                h1 { +"Class Index" }
 
-                fun DocumentationNode.classWithNestedClasses(): List<DocumentationNode> =
-                        members.filter { it.kind in classLike }.flatMap(DocumentationNode::classWithNestedClasses) + this
-
-                val classesByFirstLetter = allTypesNode.members
-                        .filterNot { it.kind == NodeKind.ExternalClass }
-                        .flatMap(DocumentationNode::classWithNestedClasses)
-                        .groupBy {
-                            it.classNodeNameWithOuterClass().first().toString()
-                        }
-                        .entries
-                        .sortedBy { (letter) -> letter }
-
-                ul {
-                    classesByFirstLetter.forEach { (letter) ->
-                        li { a(href = "#letter_$letter") { +letter } }
-                    }
-                }
-
-                classesByFirstLetter.forEach { (letter, nodes) ->
-                    h2 {
-                        id = "letter_$letter"
-                        +letter
-                    }
-                    table {
-                        tbody {
-                            for (node in nodes.sortedBy { it.classNodeNameWithOuterClass() }) {
-                                tr {
-                                    td {
-                                        a(href = uriProvider.linkTo(node, uri)) { +node.classNodeNameWithOuterClass() }
-                                    }
-                                    td {
-                                        metaMarkup(node.content)
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-    )
-
-    fun generatePackageIndex(nodes: List<DocumentationNode>) = templateService.composePage(nodes,
-            htmlConsumer,
-            headContent = {
-
-            },
-            bodyContent = {
-                h1 { +"Package Index" }
-                table {
-                    tbody {
-                        for (node in nodes.sortedBy { it.name }) {
-                            tr {
-                                td {
-                                    a(href = uriProvider.linkTo(node, uri)) { +node.name }
-                                }
-                                td {
-                                    metaMarkup(node.content)
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-    )
-
-    private fun FlowContent.fullDocs(
-            nodes: List<DocumentationNode>,
-            header: String,
-            renderNode: FlowContent.(DocumentationNode) -> Unit
-    ) {
-        if (nodes.none()) return
-        h2 {
-            +header
         }
-        for (node in nodes) {
-            renderNode(node)
-        }
-    }
-}
 
-class ContentToHtmlBuilder(val uriProvider: JavaLayoutHtmlUriProvider, val uri: URI) {
-    fun FlowContent.appendContent(content: List<ContentNode>): Unit = content.forEach { appendContent(it) }
+        class PackagePage(val node: DocumentationNode) : Page() {
 
-    private fun FlowContent.hN(level: Int, classes: String? = null, block: CommonAttributeGroupFacadeFlowHeadingPhrasingContent.() -> Unit) {
-        when (level) {
-            1 -> h1(classes, block)
-            2 -> h2(classes, block)
-            3 -> h3(classes, block)
-            4 -> h4(classes, block)
-            5 -> h5(classes, block)
-            6 -> h6(classes, block)
-        }
-    }
-
-    fun FlowContent.appendContent(content: ContentNode) {
-        when (content) {
-            is ContentText -> +content.text
-            is ContentSymbol -> span("symbol") { +content.text }
-            is ContentKeyword -> span("keyword") { +content.text }
-            is ContentIdentifier -> span("identifier") {
-                content.signature?.let { id = it }
-                +content.text
+            init {
+                assert(node.kind == NodeKind.Package)
             }
 
-            is ContentHeading -> hN(level = content.level) { appendContent(content.children) }
+            val classes = node.members(NodeKind.Class)
+            val exceptions = node.members(NodeKind.Exception)
+            val typeAliases = node.members(NodeKind.TypeAlias)
+            val annotations = node.members(NodeKind.AnnotationClass)
+            val enums = node.members(NodeKind.Enum)
 
-            is ContentEntity -> +content.text
+            private val externalClassExtensionFunctions =
+                node.members(NodeKind.ExternalClass).flatMap { it.members(NodeKind.Function) }
+            private val externalClassExtensionProperties =
+                node.members(NodeKind.ExternalClass).flatMap { it.members(NodeKind.Property) }
 
-            is ContentStrong -> strong { appendContent(content.children) }
-            is ContentStrikethrough -> del { appendContent(content.children) }
-            is ContentEmphasis -> em { appendContent(content.children) }
+            val constants = node.members(NodeKind.Property).filter { it.constantValue() != null }
 
-            is ContentOrderedList -> ol { appendContent(content.children) }
-            is ContentUnorderedList -> ul { appendContent(content.children) }
-            is ContentListItem -> consumer.li {
-                (content.children.singleOrNull() as? ContentParagraph)
-                        ?.let { paragraph -> appendContent(paragraph.children) }
-                        ?: appendContent(content.children)
-            }
+            val functions = node.members(NodeKind.Function) + externalClassExtensionFunctions
 
+            val properties = node.members(NodeKind.Property) - constants + externalClassExtensionProperties
 
-            is ContentCode -> pre { code { appendContent(content.children) } }
-            is ContentBlockSampleCode -> pre { code {} }
-            is ContentBlockCode -> pre { code {} }
-
-
-            is ContentNonBreakingSpace -> +nbsp
-            is ContentSoftLineBreak, is ContentIndentedSoftLineBreak -> {
-            }
-
-            is ContentParagraph -> p { appendContent(content.children) }
-
-            is ContentNodeLink -> {
-                a(href = content.node?.let { uriProvider.linkTo(it, uri) }
-                        ?: "#unresolved") { appendContent(content.children) }
-            }
-            is ContentExternalLink -> {
-                a(href = content.href) { appendContent(content.children) }
-            }
-
-            is ContentBlock -> appendContent(content.children)
         }
     }
 }
 
 class JavaLayoutHtmlFormatOutputBuilderFactoryImpl @Inject constructor(
-        val uriProvider: JavaLayoutHtmlUriProvider,
-        val languageService: LanguageService,
-        val templateService: JavaLayoutHtmlTemplateService,
-        val logger: DokkaLogger
+    val uriProvider: JavaLayoutHtmlUriProvider,
+    val languageService: LanguageService,
+    val templateService: JavaLayoutHtmlTemplateService,
+    val logger: DokkaLogger
 ) : JavaLayoutHtmlFormatOutputBuilderFactory {
     override fun createOutputBuilder(output: Appendable, node: DocumentationNode): JavaLayoutHtmlFormatOutputBuilder {
         return createOutputBuilder(output, uriProvider.mainUri(node))
@@ -542,4 +785,9 @@
     override fun createOutputBuilder(output: Appendable, uri: URI): JavaLayoutHtmlFormatOutputBuilder {
         return JavaLayoutHtmlFormatOutputBuilder(output, languageService, uriProvider, templateService, logger, uri)
     }
-}
\ No newline at end of file
+}
+
+fun DocumentationNode.constantValue(): String? =
+    detailOrNull(NodeKind.Value)?.name.takeIf {
+        kind == NodeKind.Property || kind == NodeKind.CompanionObjectProperty
+    }
\ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt
index 63668e7..270c912 100644
--- a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt
@@ -3,6 +3,7 @@
 import com.google.inject.Inject
 import com.google.inject.name.Named
 import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatOutputBuilder.Page
 import org.jetbrains.dokka.NodeKind.Companion.classLike
 import org.jetbrains.kotlin.preprocessor.mkdirsOrFail
 import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
@@ -13,10 +14,9 @@
 
 class JavaLayoutHtmlFormatGenerator @Inject constructor(
         @Named("outputDir") val root: File,
-        val languageService: LanguageService,
-        val templateService: JavaLayoutHtmlTemplateService,
         val packageListService: PackageListService,
         val outputBuilderFactoryService: JavaLayoutHtmlFormatOutputBuilderFactory,
+        private val options: DocumentationOptions,
         val logger: DokkaLogger
 ) : Generator, JavaLayoutHtmlUriProvider {
 
@@ -63,7 +63,7 @@
     fun buildClass(node: DocumentationNode, parentDir: File) {
         val fileForClass = parentDir.resolve(node.classNodeNameWithOuterClass() + ".html")
         fileForClass.bufferedWriter().use {
-            createOutputBuilderForNode(node, it).appendClassLike(node)
+            createOutputBuilderForNode(node, it).generatePage(Page.ClassPage(node))
         }
         for (memberClass in node.members.filter { it.kind in NodeKind.classLike }) {
             buildClass(memberClass, parentDir)
@@ -77,7 +77,7 @@
         directoryForPackage.mkdirsOrFail()
 
         directoryForPackage.resolve("package-summary.html").bufferedWriter().use {
-            createOutputBuilderForNode(node, it).appendPackage(node)
+            createOutputBuilderForNode(node, it).generatePage(Page.PackagePage(node))
         }
 
         members.filter { it.kind in NodeKind.classLike }.forEach {
@@ -88,15 +88,16 @@
     fun buildClassIndex(node: DocumentationNode, parentDir: File) {
         val file = parentDir.resolve("classes.html")
         file.bufferedWriter().use {
-            createOutputBuilderForNode(node, it).generateClassesIndex(node)
+            createOutputBuilderForNode(node, it).generatePage(Page.ClassIndex(node))
         }
     }
 
-    fun buildPackageIndex(nodes: List<DocumentationNode>, parentDir: File) {
+    fun buildPackageIndex(module: DocumentationNode, nodes: List<DocumentationNode>, parentDir: File) {
         val file = parentDir.resolve("packages.html")
         file.bufferedWriter().use {
-            outputBuilderFactoryService.createOutputBuilder(it, containerUri(nodes.first().owner!!).resolve("packages.html"))
-                    .generatePackageIndex(nodes)
+            val uri = containerUri(module).resolve("packages.html")
+            outputBuilderFactoryService.createOutputBuilder(it, uri)
+                .generatePage(Page.PackageIndex(nodes))
         }
     }
 
@@ -107,8 +108,10 @@
         val packages = module.members.filter { it.kind == NodeKind.Package }
         packages.forEach { buildPackage(it, moduleRoot) }
 
-        buildClassIndex(module.members.single { it.kind == NodeKind.AllTypes }, moduleRoot)
-        buildPackageIndex(packages, moduleRoot)
+        if (options.generateIndexPages) {
+            buildClassIndex(module.members.single { it.kind == NodeKind.AllTypes }, moduleRoot)
+            buildPackageIndex(module, packages, moduleRoot)
+        }
     }
 
     override fun buildOutlines(nodes: Iterable<DocumentationNode>) {
diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt
index 09e5ced..33170f3 100644
--- a/core/src/main/kotlin/Generation/DokkaGenerator.kt
+++ b/core/src/main/kotlin/Generation/DokkaGenerator.kt
@@ -160,6 +160,8 @@
     with(injector.getInstance(DocumentationBuilder::class.java)) {
         documentationModule.appendFragments(fragments, packageDocs.packageContent,
                 injector.getInstance(PackageDocumentationBuilder::class.java))
+
+        propagateExtensionFunctionsToSubclasses(fragments, resolutionFacade)
     }
 
     val javaFiles = coreEnvironment.getJavaSourceFiles().filter(filesToDocumentFilter)
diff --git a/core/src/main/kotlin/Generation/configurationImpl.kt b/core/src/main/kotlin/Generation/configurationImpl.kt
index 34d4154..52e8446 100644
--- a/core/src/main/kotlin/Generation/configurationImpl.kt
+++ b/core/src/main/kotlin/Generation/configurationImpl.kt
@@ -36,27 +36,28 @@
                               override val suppress: Boolean = false) : DokkaConfiguration.PackageOptions
 
 data class DokkaConfigurationImpl(
-        override val moduleName: String,
-        override val classpath: List<String>,
-        override val sourceRoots: List<SourceRootImpl>,
-        override val samples: List<String>,
-        override val includes: List<String>,
-        override val outputDir: String,
-        override val format: String,
-        override val includeNonPublic: Boolean,
-        override val includeRootPackage: Boolean,
-        override val reportUndocumented: Boolean,
-        override val skipEmptyPackages: Boolean,
-        override val skipDeprecated: Boolean,
-        override val jdkVersion: Int,
-        override val generateIndexPages: Boolean,
-        override val sourceLinks: List<SourceLinkDefinitionImpl>,
-        override val impliedPlatforms: List<String>,
-        override val perPackageOptions: List<PackageOptionsImpl>,
-        override val externalDocumentationLinks: List<ExternalDocumentationLinkImpl>,
-        override val noStdlibLink: Boolean,
-        override val cacheRoot: String?,
-        override val suppressedFiles: List<String>,
-        override val languageVersion: String?,
-        override val apiVersion: String?
+    override val moduleName: String,
+    override val classpath: List<String>,
+    override val sourceRoots: List<SourceRootImpl>,
+    override val samples: List<String>,
+    override val includes: List<String>,
+    override val outputDir: String,
+    override val format: String,
+    override val includeNonPublic: Boolean,
+    override val includeRootPackage: Boolean,
+    override val reportUndocumented: Boolean,
+    override val skipEmptyPackages: Boolean,
+    override val skipDeprecated: Boolean,
+    override val jdkVersion: Int,
+    override val generateIndexPages: Boolean,
+    override val sourceLinks: List<SourceLinkDefinitionImpl>,
+    override val impliedPlatforms: List<String>,
+    override val perPackageOptions: List<PackageOptionsImpl>,
+    override val externalDocumentationLinks: List<ExternalDocumentationLinkImpl>,
+    override val noStdlibLink: Boolean,
+    override val cacheRoot: String?,
+    override val suppressedFiles: List<String>,
+    override val languageVersion: String?,
+    override val apiVersion: String?,
+    override val collectInheritedExtensionsFromLibraries: Boolean
 ) : DokkaConfiguration
\ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
index dd29cfc..88f517b 100644
--- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
@@ -2,6 +2,7 @@
 
 import com.google.inject.Inject
 import com.intellij.openapi.util.text.StringUtil
+import com.intellij.psi.PsiField
 import com.intellij.psi.PsiJavaFile
 import org.jetbrains.dokka.DokkaConfiguration.*
 import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser
@@ -11,6 +12,9 @@
 import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
 import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
 import org.jetbrains.kotlin.idea.kdoc.findKDoc
+import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
+import org.jetbrains.kotlin.idea.util.makeNotNullable
+import org.jetbrains.kotlin.idea.util.toFuzzyType
 import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
 import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
 import org.jetbrains.kotlin.lexer.KtTokens
@@ -18,15 +22,17 @@
 import org.jetbrains.kotlin.name.FqName
 import org.jetbrains.kotlin.psi.KtModifierListOwner
 import org.jetbrains.kotlin.psi.KtParameter
+import org.jetbrains.kotlin.psi.KtVariableDeclaration
 import org.jetbrains.kotlin.resolve.DescriptorUtils
 import org.jetbrains.kotlin.resolve.constants.ConstantValue
 import org.jetbrains.kotlin.resolve.descriptorUtil.*
 import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors
 import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
+import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
+import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
 import org.jetbrains.kotlin.resolve.source.PsiSourceElement
 import org.jetbrains.kotlin.resolve.source.getPsi
 import org.jetbrains.kotlin.types.*
-import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
 import org.jetbrains.kotlin.types.typeUtil.supertypes
 import java.io.File
 import java.nio.file.Path
@@ -51,7 +57,8 @@
                            val languageVersion: String?,
                            val apiVersion: String?,
                            cacheRoot: String? = null,
-                           val suppressedFiles: List<File> = emptyList()) {
+                           val suppressedFiles: List<File> = emptyList(),
+                           val collectInheritedExtensionsFromLibraries: Boolean = false) {
     init {
         if (perPackageOptions.any { it.prefix == "" })
             throw IllegalArgumentException("Please do not register packageOptions with all match pattern, use global settings instead")
@@ -101,6 +108,10 @@
     fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String>
 }
 
+val ignoredSupertypes = setOf(
+    "kotlin.Annotation", "kotlin.Enum", "kotlin.Any"
+)
+
 class DocumentationBuilder
 @Inject constructor(val resolutionFacade: DokkaResolutionFacade,
                     val descriptorDocumentationParser: DescriptorDocumentationParser,
@@ -132,8 +143,20 @@
         refGraph.register(descriptor.signature(), node)
     }
 
-    fun <T> nodeForDescriptor(descriptor: T, kind: NodeKind): DocumentationNode where T : DeclarationDescriptor, T : Named {
-        val (doc, callback) = descriptorDocumentationParser.parseDocumentationAndDetails(descriptor, kind == NodeKind.Parameter)
+    fun <T> nodeForDescriptor(
+        descriptor: T,
+        kind: NodeKind,
+        external: Boolean = false
+    ): DocumentationNode where T : DeclarationDescriptor, T : Named {
+        val (doc, callback) =
+                if (external) {
+                    Content.Empty to { node -> }
+                } else {
+                    descriptorDocumentationParser.parseDocumentationAndDetails(
+                        descriptor,
+                        kind == NodeKind.Parameter
+                    )
+                }
         val node = DocumentationNode(descriptor.name.asString(), doc, kind).withModifiers(descriptor)
         node.appendSignature(descriptor)
         callback(node)
@@ -171,7 +194,7 @@
         val unwrappedType = superType.unwrap()
         if (unwrappedType is AbbreviatedType) {
             appendSupertype(descriptor, unwrappedType.abbreviation)
-        } else if (!ignoreSupertype(unwrappedType)) {
+        } else {
             appendType(unwrappedType, NodeKind.Supertype)
             val superclass = unwrappedType.constructor.declarationDescriptor
             link(superclass, descriptor, RefKind.Inheritor)
@@ -179,15 +202,6 @@
         }
     }
 
-    private fun ignoreSupertype(superType: KotlinType): Boolean {
-        val superClass = superType.constructor.declarationDescriptor as? ClassDescriptor
-        if (superClass != null) {
-            val fqName = DescriptorUtils.getFqNameSafe(superClass).asString()
-            return fqName == "kotlin.Annotation" || fqName == "kotlin.Enum" || fqName == "kotlin.Any"
-        }
-        return false
-    }
-
     fun DocumentationNode.appendProjection(projection: TypeProjection, kind: NodeKind = NodeKind.Type) {
         if (projection.isStarProjection) {
             appendTextNode("*", NodeKind.Type)
@@ -229,16 +243,33 @@
             node.appendTextNode("?", NodeKind.NullabilityModifier)
         }
         if (classifierDescriptor != null) {
-            val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
+            val externalLink =
+                linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
             if (externalLink != null) {
-                node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
-                node.append(DocumentationNode(classifierDescriptor.fqNameUnsafe.asString(), Content.Empty, NodeKind.QualifiedName), RefKind.Detail)
+                if (classifierDescriptor !is TypeParameterDescriptor) {
+                    val targetNode =
+                        refGraph.lookup(classifierDescriptor.signature()) ?: classifierDescriptor.build(true)
+                    node.append(targetNode, RefKind.ExternalType)
+                    node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+                }
             } else {
-                link(node, classifierDescriptor,
-                        if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link)
+                link(
+                    node, classifierDescriptor,
+                    if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link
+                )
+            }
+            if (classifierDescriptor !is TypeParameterDescriptor) {
+                node.append(
+                    DocumentationNode(
+                        classifierDescriptor.fqNameUnsafe.asString(),
+                        Content.Empty,
+                        NodeKind.QualifiedName
+                    ), RefKind.Detail
+                )
             }
         }
 
+
         append(node, RefKind.Detail)
         node.appendAnnotations(kotlinType)
         for (typeArgument in kotlinType.arguments) {
@@ -272,6 +303,17 @@
         }
     }
 
+    fun DocumentationNode.appendExternalLink(externalLink: String) {
+        append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+    }
+
+    fun DocumentationNode.appendExternalLink(descriptor: DeclarationDescriptor) {
+        val target = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(descriptor)
+        if (target != null) {
+            appendExternalLink(target)
+        }
+    }
+
     fun DocumentationNode.appendSinceKotlin(annotation: DocumentationNode) {
         val kotlinVersion = annotation
                 .detail(NodeKind.Parameter)
@@ -418,15 +460,44 @@
                     declarations, allFqNames)
         }
 
-        propagateExtensionFunctionsToSubclasses(fragments)
     }
 
-    private fun propagateExtensionFunctionsToSubclasses(fragments: Collection<PackageFragmentDescriptor>) {
-        val allDescriptors = fragments.flatMap { it.getMemberScope().getContributedDescriptors() }
-        val allClasses = allDescriptors.filterIsInstance<ClassDescriptor>()
-        val classHierarchy = buildClassHierarchy(allClasses)
+    fun propagateExtensionFunctionsToSubclasses(
+        fragments: Collection<PackageFragmentDescriptor>,
+        resolutionFacade: DokkaResolutionFacade
+    ) {
 
-        val allExtensionFunctions = allDescriptors
+        val moduleDescriptor = resolutionFacade.moduleDescriptor
+
+        // Wide-collect all view descriptors
+        val allPackageViewDescriptors = generateSequence(listOf(moduleDescriptor.getPackage(FqName.ROOT))) { packages ->
+            packages
+                .flatMap { pkg ->
+                    moduleDescriptor.getSubPackagesOf(pkg.fqName) { true }
+                }.map { fqName ->
+                    moduleDescriptor.getPackage(fqName)
+                }.takeUnless { it.isEmpty() }
+        }.flatten()
+
+        val allDescriptors =
+            if (options.collectInheritedExtensionsFromLibraries) {
+                allPackageViewDescriptors.map { it.memberScope }
+            } else {
+                fragments.asSequence().map { it.getMemberScope() }
+            }.flatMap {
+                it.getDescriptorsFiltered(
+                    DescriptorKindFilter.CALLABLES
+                ).asSequence()
+            }
+
+
+        val documentingDescriptors = fragments.flatMap { it.getMemberScope().getContributedDescriptors() }
+        val documentingClasses = documentingDescriptors.filterIsInstance<ClassDescriptor>()
+
+        val classHierarchy = buildClassHierarchy(documentingClasses)
+
+        val allExtensionFunctions =
+            allDescriptors
                 .filterIsInstance<CallableMemberDescriptor>()
                 .filter { it.extensionReceiverParameter != null }
         val extensionFunctionsByName = allExtensionFunctions.groupBy { it.name }
@@ -434,15 +505,31 @@
         for (extensionFunction in allExtensionFunctions) {
             if (extensionFunction.dispatchReceiverParameter != null) continue
             val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name]
-                    ?.filter { fn -> fn.canShadow(extensionFunction) }
+                ?.filter { fn -> fn.canShadow(extensionFunction) }
                     ?: emptyList()
 
             if (extensionFunction.extensionReceiverParameter?.type?.isDynamic() == true) continue
-            val classDescriptor = extensionFunction.getExtensionClassDescriptor() ?: continue
-            val subclasses = classHierarchy[classDescriptor] ?: continue
-            subclasses.forEach { subclass ->
+            val subclasses =
+                classHierarchy.filter { (key) -> key.isExtensionApplicable(extensionFunction) }
+            if (subclasses.isEmpty()) continue
+            subclasses.values.flatten().forEach { subclass ->
                 if (subclass.isExtensionApplicable(extensionFunction) &&
-                        possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) {
+                    possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) {
+
+                    val hasExternalLink =
+                        linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(
+                            extensionFunction
+                        ) != null
+                    if (hasExternalLink) {
+                        val containerDesc =
+                            extensionFunction.containingDeclaration as? PackageFragmentDescriptor
+                        if (containerDesc != null) {
+                            val container = refGraph.lookup(containerDesc.signature())
+                                    ?: containerDesc.buildExternal()
+                            container.append(extensionFunction.buildExternal(), RefKind.Member)
+                        }
+                    }
+
                     refGraph.link(subclass.signature(), extensionFunction.signature(), RefKind.Extension)
                 }
             }
@@ -450,12 +537,9 @@
     }
 
     private fun ClassDescriptor.isExtensionApplicable(extensionFunction: CallableMemberDescriptor): Boolean {
-        val receiverType = extensionFunction.extensionReceiverParameter!!.type
-        if (receiverType.arguments.any { it.type.constructor.declarationDescriptor is TypeParameterDescriptor }) {
-            val receiverClass = receiverType.constructor.declarationDescriptor
-            return receiverClass is ClassDescriptor && DescriptorUtils.isSubclass(this, receiverClass)
-        }
-        return defaultType.isSubtypeOf(receiverType)
+        val receiverType = extensionFunction.fuzzyExtensionReceiverType()?.makeNotNullable()
+        val classType = defaultType.toFuzzyType(declaredTypeParameters)
+        return receiverType != null && classType.checkIsSubtypeOf(receiverType) != null
     }
 
     private fun buildClassHierarchy(classes: List<ClassDescriptor>): Map<ClassDescriptor, List<ClassDescriptor>> {
@@ -494,34 +578,60 @@
     }
 
     fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
-        is ClassDescriptor -> build()
+        is ClassifierDescriptor -> build()
         is ConstructorDescriptor -> build()
         is PropertyDescriptor -> build()
         is FunctionDescriptor -> build()
-        is TypeParameterDescriptor -> build()
         is ValueParameterDescriptor -> build()
         is ReceiverParameterDescriptor -> build()
-        is TypeAliasDescriptor -> build()
         else -> throw IllegalStateException("Descriptor $this is not known")
     }
 
-    fun TypeAliasDescriptor.build(): DocumentationNode {
+    fun PackageFragmentDescriptor.buildExternal(): DocumentationNode {
+        val node = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.Package)
+
+        val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(this)
+        if (externalLink != null) {
+            node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+        }
+        register(this, node)
+        return node
+    }
+
+    fun CallableDescriptor.buildExternal(): DocumentationNode = when(this) {
+        is FunctionDescriptor -> build(true)
+        is PropertyDescriptor -> build(true)
+        else -> throw IllegalStateException("Descriptor $this is not known")
+    }
+
+
+    fun ClassifierDescriptor.build(external: Boolean = false): DocumentationNode = when (this) {
+        is ClassDescriptor -> build(external)
+        is TypeAliasDescriptor -> build(external)
+        is TypeParameterDescriptor -> build()
+        else -> throw IllegalStateException("Descriptor $this is not known")
+    }
+
+    fun TypeAliasDescriptor.build(external: Boolean = false): DocumentationNode {
         val node = nodeForDescriptor(this, NodeKind.TypeAlias)
 
-        node.appendAnnotations(this)
+        if (!external) {
+            node.appendAnnotations(this)
+        }
         node.appendModifiers(this)
         node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail)
 
         node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType)
 
-        node.appendSourceLink(source)
-        node.appendDefaultPlatforms(this)
-
+        if (!external) {
+            node.appendSourceLink(source)
+            node.appendDefaultPlatforms(this)
+        }
         register(this, node)
         return node
     }
 
-    fun ClassDescriptor.build(): DocumentationNode {
+    fun ClassDescriptor.build(external: Boolean = false): DocumentationNode {
         val kind = when {
             kind == ClassKind.OBJECT -> NodeKind.Object
             kind == ClassKind.INTERFACE -> NodeKind.Interface
@@ -531,21 +641,25 @@
             isSubclassOfThrowable() -> NodeKind.Exception
             else -> NodeKind.Class
         }
-        val node = nodeForDescriptor(this, kind)
+        val node = nodeForDescriptor(this, kind, external)
+        register(this, node)
         typeConstructor.supertypes.forEach {
             node.appendSupertype(this, it)
         }
         if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) {
             node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail)
         }
-        for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) {
-            node.appendClassMember(descriptor, inheritedLinkKind, extraModifier)
+        if (!external) {
+            for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) {
+                node.appendClassMember(descriptor, inheritedLinkKind, extraModifier)
+            }
+            node.appendAnnotations(this)
         }
-        node.appendAnnotations(this)
         node.appendModifiers(this)
-        node.appendSourceLink(source)
-        node.appendDefaultPlatforms(this)
-        register(this, node)
+        if (!external) {
+            node.appendSourceLink(source)
+            node.appendDefaultPlatforms(this)
+        }
         return node
     }
 
@@ -612,12 +726,12 @@
         return (receiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor)?.isCompanionObject ?: false
     }
 
-    fun FunctionDescriptor.build(): DocumentationNode {
+    fun FunctionDescriptor.build(external: Boolean = false): DocumentationNode {
         if (ErrorUtils.containsErrorType(this)) {
             logger.warn("Found an unresolved type in ${signatureWithSourceLocation()}")
         }
 
-        val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function)
+        val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function, external)
 
         node.appendInPageChildren(typeParameters, RefKind.Detail)
         extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
@@ -625,8 +739,12 @@
         node.appendType(returnType)
         node.appendAnnotations(this)
         node.appendModifiers(this)
-        node.appendSourceLink(source)
-        node.appendDefaultPlatforms(this)
+        if (!external) {
+            node.appendSourceLink(source)
+            node.appendDefaultPlatforms(this)
+        } else {
+            node.appendExternalLink(this)
+        }
 
         overriddenDescriptors.forEach {
             addOverrideLink(it, this)
@@ -647,32 +765,53 @@
         }
     }
 
-    fun PropertyDescriptor.build(): DocumentationNode {
-        val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property)
+    fun PropertyDescriptor.build(external: Boolean = false): DocumentationNode {
+        val node = nodeForDescriptor(
+            this,
+            if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property,
+            external
+        )
         node.appendInPageChildren(typeParameters, RefKind.Detail)
         extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
         node.appendType(returnType)
         node.appendAnnotations(this)
         node.appendModifiers(this)
-        node.appendSourceLink(source)
-        if (isVar) {
-            node.appendTextNode("var", NodeKind.Modifier)
-        }
-        getter?.let {
-            if (!it.isDefault) {
-                node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter")
+        if (!external) {
+            node.appendSourceLink(source)
+            if (isVar) {
+                node.appendTextNode("var", NodeKind.Modifier)
             }
-        }
-        setter?.let {
-            if (!it.isDefault) {
-                node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter")
+
+            if (isConst) {
+                val psi = sourcePsi()
+                val valueText = when (psi) {
+                    is KtVariableDeclaration -> psi.initializer?.text
+                    is PsiField -> psi.initializer?.text
+                    else -> null
+                }
+                valueText?.let { node.appendTextNode(it, NodeKind.Value) }
             }
+
+
+            getter?.let {
+                if (!it.isDefault) {
+                    node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter")
+                }
+            }
+            setter?.let {
+                if (!it.isDefault) {
+                    node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter")
+                }
+            }
+            node.appendDefaultPlatforms(this)
+        }
+        if (external) {
+            node.appendExternalLink(this)
         }
 
         overriddenDescriptors.forEach {
             addOverrideLink(it, this)
         }
-        node.appendDefaultPlatforms(this)
 
         register(this, node)
         return node
diff --git a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
index 108cee7..330e76a 100644
--- a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
+++ b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
@@ -154,8 +154,8 @@
     fun buildExternalDocumentationLink(symbol: DeclarationDescriptor): String? {
         val packageFqName: FqName =
                 when (symbol) {
-                    is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
                     is PackageFragmentDescriptor -> symbol.fqName
+                    is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
                     else -> return null
                 }
 
diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
index b6f4471..aa185de 100644
--- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
+++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
@@ -291,7 +291,7 @@
     }
 
     private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) {
-        val supertypes = node.details(NodeKind.Supertype)
+        val supertypes = node.details(NodeKind.Supertype).filterNot { it.qualifiedNameFromType() in ignoredSupertypes }
         if (supertypes.any()) {
             nbsp()
             symbol(":")
diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt
index a145ae8..85ecdf8 100644
--- a/core/src/main/kotlin/Model/DocumentationNode.kt
+++ b/core/src/main/kotlin/Model/DocumentationNode.kt
@@ -104,12 +104,27 @@
         get() = references(RefKind.Deprecation).singleOrNull()?.to
     val platforms: List<String>
         get() = references(RefKind.Platform).map { it.to.name }
+    val externalType: DocumentationNode?
+        get() = references(RefKind.ExternalType).map { it.to }.firstOrNull()
 
     val supertypes: List<DocumentationNode>
         get() = details(NodeKind.Supertype)
 
-    val superclass: DocumentationNode?
-        get() = supertypes.firstOrNull { it.links.any { it.kind in NodeKind.classLike } }
+    val superclassType: DocumentationNode?
+        get() = when (kind) {
+            NodeKind.Supertype -> (links.firstOrNull { it.kind in NodeKind.classLike } ?: externalType)?.superclassType
+            NodeKind.Interface -> null
+            in NodeKind.classLike -> supertypes.firstOrNull {
+                it.links.any { it.kind in NodeKind.classLike } ||
+                        it.externalType != null
+            }
+            else -> null
+        }
+
+    val superclassTypeSequence: Sequence<DocumentationNode>
+        get() = generateSequence(superclassType) {
+            it.superclassType
+        }
 
     // TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice
     fun addReferenceTo(to: DocumentationNode, kind: RefKind) {
@@ -194,6 +209,8 @@
 fun DocumentationNode.qualifiedName(): String {
     if (kind == NodeKind.Type) {
         return qualifiedNameFromType()
+    } else if (kind == NodeKind.Package) {
+        return name
     }
     return path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".")
 }
diff --git a/core/src/main/kotlin/Model/DocumentationReference.kt b/core/src/main/kotlin/Model/DocumentationReference.kt
index a968f40..b0f011b 100644
--- a/core/src/main/kotlin/Model/DocumentationReference.kt
+++ b/core/src/main/kotlin/Model/DocumentationReference.kt
@@ -18,7 +18,8 @@
     HiddenAnnotation,
     Deprecation,
     TopLevelPage,
-    Platform
+    Platform,
+    ExternalType
 }
 
 data class DocumentationReference(val from: DocumentationNode, val to: DocumentationNode, val kind: RefKind) {
diff --git a/core/src/test/kotlin/TestAPI.kt b/core/src/test/kotlin/TestAPI.kt
index aa3eff4..aeff928 100644
--- a/core/src/test/kotlin/TestAPI.kt
+++ b/core/src/test/kotlin/TestAPI.kt
@@ -6,7 +6,6 @@
 import com.intellij.openapi.util.io.FileUtil
 import com.intellij.rt.execution.junit.FileComparisonFailure
 import org.jetbrains.dokka.*
-import org.jetbrains.dokka.DokkaConfiguration.SourceLinkDefinition
 import org.jetbrains.dokka.Utilities.DokkaAnalysisModule
 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
@@ -25,22 +24,25 @@
                 format: String = "html",
                 includeNonPublic: Boolean = true,
                 perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(),
+                noStdlibLink: Boolean = true,
+                collectInheritedExtensionsFromLibraries: Boolean = false,
                 verifier: (DocumentationModule) -> Unit) {
     val documentation = DocumentationModule("test")
 
     val options = DocumentationOptions(
-            "",
-            format,
-            includeNonPublic = includeNonPublic,
-            skipEmptyPackages = false,
-            includeRootPackage = true,
-            sourceLinks = listOf(),
-            perPackageOptions = perPackageOptions,
-            generateIndexPages = false,
-            noStdlibLink = true,
-            cacheRoot = "default",
-            languageVersion = null,
-            apiVersion = null
+        "",
+        format,
+        includeNonPublic = includeNonPublic,
+        skipEmptyPackages = false,
+        includeRootPackage = true,
+        sourceLinks = listOf(),
+        perPackageOptions = perPackageOptions,
+        generateIndexPages = false,
+        noStdlibLink = noStdlibLink,
+        cacheRoot = "default",
+        languageVersion = null,
+        apiVersion = null,
+        collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
     )
 
     appendDocumentation(documentation, *roots,
@@ -161,13 +163,17 @@
                  withKotlinRuntime: Boolean = false,
                  format: String = "html",
                  includeNonPublic: Boolean = true,
+                 noStdlibLink: Boolean = true,
+                 collectInheritedExtensionsFromLibraries: Boolean = false,
                  outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
     verifyModel(
-            *roots,
-            withJdk = withJdk,
-            withKotlinRuntime = withKotlinRuntime,
-            format = format,
-            includeNonPublic = includeNonPublic
+        *roots,
+        withJdk = withJdk,
+        withKotlinRuntime = withKotlinRuntime,
+        format = format,
+        includeNonPublic = includeNonPublic,
+        noStdlibLink = noStdlibLink,
+        collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
     ) {
         verifyModelOutput(it, outputExtension, roots.first().path, outputGenerator)
     }
@@ -184,21 +190,27 @@
     assertEqualsIgnoringSeparators(expectedFile, output.toString())
 }
 
-fun verifyOutput(path: String,
-                 outputExtension: String,
-                 withJdk: Boolean = false,
-                 withKotlinRuntime: Boolean = false,
-                 format: String = "html",
-                 includeNonPublic: Boolean = true,
-                 outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
+fun verifyOutput(
+    path: String,
+    outputExtension: String,
+    withJdk: Boolean = false,
+    withKotlinRuntime: Boolean = false,
+    format: String = "html",
+    includeNonPublic: Boolean = true,
+    noStdlibLink: Boolean = true,
+    collectInheritedExtensionsFromLibraries: Boolean = false,
+    outputGenerator: (DocumentationModule, StringBuilder) -> Unit
+) {
     verifyOutput(
-            arrayOf(contentRootFromPath(path)),
-            outputExtension,
-            withJdk,
-            withKotlinRuntime,
-            format,
-            includeNonPublic,
-            outputGenerator
+        arrayOf(contentRootFromPath(path)),
+        outputExtension,
+        withJdk,
+        withKotlinRuntime,
+        format,
+        includeNonPublic,
+        noStdlibLink,
+        collectInheritedExtensionsFromLibraries,
+        outputGenerator
     )
 }
 
diff --git a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt
new file mode 100644
index 0000000..160b3cb
--- /dev/null
+++ b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt
@@ -0,0 +1,58 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptor
+import org.jetbrains.dokka.NodeKind
+import org.junit.Test
+
+class JavaLayoutHtmlFormatTest : JavaLayoutHtmlFormatTestCase() {
+    override val formatDescriptor = JavaLayoutHtmlFormatDescriptor()
+
+    @Test
+    fun simple() {
+        verifyNode("simple.kt")
+    }
+
+    @Test
+    fun topLevel() {
+        verifyPackageNode("topLevel.kt")
+    }
+
+    @Test
+    fun codeBlocks() {
+        verifyNode("codeBlocks.kt") { model ->
+            listOf(model.members.single().members.single { it.name == "foo" })
+        }
+    }
+
+    @Test
+    fun const() {
+        verifyPackageNode("const.kt", noStdlibLink = true)
+        verifyNode("const.kt", noStdlibLink = true) { model ->
+            model.members.single().members.filter { it.kind in NodeKind.classLike }
+        }
+    }
+
+    @Test
+    fun externalClassExtension() {
+        verifyPackageNode("externalClassExtension.kt")
+    }
+
+    @Test
+    fun unresolvedExternalClass() {
+        verifyNode("unresolvedExternalClass.kt", noStdlibLink = true) { model ->
+            listOf(model.members.single().members.single { it.name == "MyException" })
+        }
+    }
+
+    @Test
+    fun genericExtension() {
+        verifyNode("genericExtension.kt", noStdlibLink = true) { model ->
+            model.members.single().members(NodeKind.Class)
+        }
+    }
+
+    @Test
+    fun constJava() {
+        verifyNode("ConstJava.java", noStdlibLink = true)
+    }
+}
\ No newline at end of file
diff --git a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt
new file mode 100644
index 0000000..50af8f2
--- /dev/null
+++ b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt
@@ -0,0 +1,115 @@
+package org.jetbrains.dokka.tests
+
+import com.google.inject.Guice
+import com.google.inject.Injector
+import com.google.inject.Module
+import com.google.inject.name.Names
+import org.jetbrains.dokka.DocumentationNode
+import org.jetbrains.dokka.DocumentationOptions
+import org.jetbrains.dokka.DokkaLogger
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptorBase
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatGenerator
+import org.jetbrains.dokka.Generator
+import org.jetbrains.dokka.Utilities.bind
+import org.junit.Rule
+import org.junit.rules.TemporaryFolder
+import java.io.File
+import java.net.URI
+
+abstract class JavaLayoutHtmlFormatTestCase {
+
+    abstract val formatDescriptor: JavaLayoutHtmlFormatDescriptorBase
+
+    @get:Rule
+    var folder = TemporaryFolder()
+
+    var options =
+        DocumentationOptions(
+            "",
+            "java-layout-html",
+            apiVersion = null,
+            languageVersion = null,
+            generateIndexPages = false,
+            noStdlibLink = false,
+            collectInheritedExtensionsFromLibraries = true
+        )
+
+    val injector: Injector by lazy {
+        Guice.createInjector(Module { binder ->
+            binder.bind<File>().annotatedWith(Names.named("outputDir")).toInstance(folder.apply { create() }.root)
+
+            binder.bind<DocumentationOptions>().toProvider { options }
+            binder.bind<DokkaLogger>().toInstance(object : DokkaLogger {
+                override fun info(message: String) {
+                    println(message)
+                }
+
+                override fun warn(message: String) {
+                    println("WARN: $message")
+                }
+
+                override fun error(message: String) {
+                    println("ERROR: $message")
+                }
+
+            })
+
+            formatDescriptor.configureOutput(binder)
+        })
+    }
+
+
+    protected fun buildPagesAndReadInto(model: DocumentationNode, nodes: List<DocumentationNode>, sb: StringBuilder) =
+        with(injector.getInstance(Generator::class.java)) {
+            this as JavaLayoutHtmlFormatGenerator
+            buildPages(listOf(model))
+            val byLocations = nodes.groupBy { mainUri(it) }
+            byLocations.forEach { (loc, _) ->
+                sb.appendln("<!-- File: $loc -->")
+                sb.append(folder.root.toURI().resolve(URI("/").relativize(loc)).toURL().readText())
+            }
+        }
+
+
+    protected fun verifyNode(
+        fileName: String,
+        noStdlibLink: Boolean = false,
+        fileExtension: String = ".html",
+        select: (model: DocumentationNode) -> List<DocumentationNode>
+    ) {
+        verifyOutput(
+            "testdata/format/java-layout-html/$fileName",
+            fileExtension,
+            format = "java-layout-html",
+            withKotlinRuntime = true,
+            noStdlibLink = noStdlibLink,
+            collectInheritedExtensionsFromLibraries = true
+        ) { model, output ->
+            buildPagesAndReadInto(
+                model,
+                select(model),
+                output
+            )
+        }
+    }
+
+    protected fun verifyNode(fileName: String, noStdlibLink: Boolean = false) {
+        verifyNode(fileName, noStdlibLink) { model -> listOf(model.members.single().members.single()) }
+    }
+
+    protected fun verifyPackageNode(fileName: String, noStdlibLink: Boolean = false) {
+        verifyOutput(
+            "testdata/format/java-layout-html/$fileName",
+            ".package-summary.html",
+            format = "java-layout-html",
+            withKotlinRuntime = true,
+            noStdlibLink = noStdlibLink
+        ) { model, output ->
+            buildPagesAndReadInto(
+                model,
+                listOf(model.members.single()),
+                output
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/src/test/kotlin/model/JavaTest.kt b/core/src/test/kotlin/model/JavaTest.kt
index e6c22ee..c2ede8f 100644
--- a/core/src/test/kotlin/model/JavaTest.kt
+++ b/core/src/test/kotlin/model/JavaTest.kt
@@ -193,7 +193,7 @@
     @Test fun enumValues() {
         verifyJavaPackageMember("testdata/java/enumValues.java") { cls ->
             val superTypes = cls.details(NodeKind.Supertype)
-            assertEquals(0, superTypes.size)
+            assertEquals(1, superTypes.size)
             assertEquals(1, cls.members(NodeKind.EnumItem).size)
         }
     }
diff --git a/core/testdata/format/java-layout-html/ConstJava.html b/core/testdata/format/java-layout-html/ConstJava.html
new file mode 100644
index 0000000..26c9aa2
--- /dev/null
+++ b/core/testdata/format/java-layout-html/ConstJava.html
@@ -0,0 +1,93 @@
+<!-- File: /test/p/ConstJava.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>ConstJava</h1>
+    <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">ConstJava</span></pre>
+    <table>
+      <tr>
+        <td><a href="#">p.ConstJava</a></td>
+      </tr>
+    </table>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constants</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><span class="keyword">static</span> <span class="identifier">String</span></td>
+          <td>
+            <div><code><a href="#myStringConst%3Akotlin.String">myStringConst</a></code></div>
+          </td>
+        </tr>
+        <tr>
+          <td><span class="keyword">static</span> <span class="identifier">Int</span></td>
+          <td>
+            <div><code><a href="#myIntConst%3Akotlin.Int">myIntConst</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constructors</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+            <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Properties</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><span class="keyword">static</span> <a href="#"><span class="identifier">ConstJava</span></a></td>
+          <td>
+            <div><code><a href="#myConstObjConst%3Ap.ConstJava">myConstObjConst</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constants</h2>
+    <div id="myStringConst:kotlin.String">
+      <h3>myStringConst</h3>
+      <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myStringConst</span><span class="symbol">: </span><span class="identifier">String</span></pre>
+      <pre>Value: <code>&quot;&quot;</code></pre>
+    </div>
+    <div id="myIntConst:kotlin.Int">
+      <h3>myIntConst</h3>
+      <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myIntConst</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+      <pre>Value: <code>0</code></pre>
+    </div>
+    <h2>Constructors</h2>
+    <div id="&lt;init&gt;()">
+      <h3>&lt;init&gt;</h3>
+      <pre><span class="identifier">ConstJava</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+    </div>
+    <h2>Properties</h2>
+    <div id="myConstObjConst:p.ConstJava">
+      <h3>myConstObjConst</h3>
+      <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myConstObjConst</span><span class="symbol">: </span><a href="#"><span class="identifier">ConstJava</span></a></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/ConstJava.java b/core/testdata/format/java-layout-html/ConstJava.java
new file mode 100644
index 0000000..eb5bb2b
--- /dev/null
+++ b/core/testdata/format/java-layout-html/ConstJava.java
@@ -0,0 +1,10 @@
+package p;
+
+
+public class ConstJava {
+
+    public static final String myStringConst = "";
+    public static final int myIntConst = 0;
+
+    public static final ConstJava myConstObjConst = new ConstJava(); // Not a constant, as it have not primitive type
+}
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/codeBlocks.html b/core/testdata/format/java-layout-html/codeBlocks.html
new file mode 100644
index 0000000..1d6bc6c
--- /dev/null
+++ b/core/testdata/format/java-layout-html/codeBlocks.html
@@ -0,0 +1,49 @@
+<!-- File: /test/p/package-summary.html#foo%28%29 -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>p</h1>
+    <h2>Top-level functions summary</h2>
+    <table>
+      <tbody>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html"><span class="identifier">Int</span></a></td>
+          <td>
+            <div><code><a href="#foo%28%29">foo</a>()</code></div>
+            <p>See that <code>inline code</code> here</p>
+          </td>
+        </tr>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+          <td>
+            <div><code><a href="#sample%28%29">sample</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Top-level functions</h2>
+    <div id="foo()">
+      <h3>foo</h3>
+      <pre><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html"><span class="identifier">Int</span></a></pre>
+      <p>See that <code>inline code</code> here</p>
+      <p>Some full code-block</p>
+      <pre><code data-language="Kotlin">
+    println(foo()) // Prints 42
+    println(foo() - 10) // Prints 32
+</code></pre>
+      <p>Some indented code-block
+    fun ref() = foo()
+    val a = 2</p>
+      <pre><code data-language="kotlin">
+
+println(foo()) // Answer unlimate question of all
+println(foo() * 2) // 84!</code></pre>
+    </div>
+    <div id="sample()">
+      <h3>sample</h3>
+      <pre><span class="keyword">fun </span><span class="identifier">sample</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/codeBlocks.kt b/core/testdata/format/java-layout-html/codeBlocks.kt
new file mode 100644
index 0000000..de0c794
--- /dev/null
+++ b/core/testdata/format/java-layout-html/codeBlocks.kt
@@ -0,0 +1,26 @@
+package p
+
+/**
+ * See that `inline code` here
+ *
+ * Some full code-block
+ * ```Kotlin
+ *    println(foo()) // Prints 42
+ *    println(foo() - 10) // Prints 32
+ * ```
+ *
+ * Some indented code-block
+ *     fun ref() = foo()
+ *     val a = 2
+ * 
+ * @sample p.sample
+ */
+fun foo(): Int {
+    return 42
+}
+
+
+fun sample() {
+    println(foo()) // Answer unlimate question of all
+    println(foo() * 2) // 84!
+}
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/const.html b/core/testdata/format/java-layout-html/const.html
new file mode 100644
index 0000000..89cb572
--- /dev/null
+++ b/core/testdata/format/java-layout-html/const.html
@@ -0,0 +1,99 @@
+<!-- File: /test/p/G.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>G</h1>
+    <pre><span class="keyword">object </span><span class="identifier">G</span></pre>
+    <table>
+      <tr>
+        <td><a href="#">p.G</a></td>
+      </tr>
+    </table>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constants</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><span class="keyword">const</span> <span class="identifier">Int</span></td>
+          <td>
+            <div><code><a href="#y%3Akotlin.Int">y</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constants</h2>
+    <div id="y:kotlin.Int">
+      <h3>y</h3>
+      <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">y</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+      <pre>Value: <code>0</code></pre>
+    </div>
+  </body>
+</html>
+<!-- File: /test/p/D.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>D</h1>
+    <pre><span class="keyword">class </span><span class="identifier">D</span></pre>
+    <table>
+      <tr>
+        <td><a href="#">p.D</a></td>
+      </tr>
+    </table>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constants</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><span class="keyword">const</span> <span class="identifier">Int</span></td>
+          <td>
+            <div><code><a href="#Companion.z%3Akotlin.Int">z</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constructors</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+            <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constants</h2>
+    <div id="Companion.z:kotlin.Int">
+      <h3>z</h3>
+      <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">z</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+      <pre>Value: <code>0</code></pre>
+    </div>
+    <h2>Constructors</h2>
+    <div id="&lt;init&gt;()">
+      <h3>&lt;init&gt;</h3>
+      <pre><span class="identifier">D</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/const.kt b/core/testdata/format/java-layout-html/const.kt
new file mode 100644
index 0000000..a6a0884
--- /dev/null
+++ b/core/testdata/format/java-layout-html/const.kt
@@ -0,0 +1,14 @@
+package p
+
+const val x = 0
+
+
+object G {
+    const val y = 0
+}
+
+class D {
+    companion object {
+        const val z = 0
+    }
+}
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/const.package-summary.html b/core/testdata/format/java-layout-html/const.package-summary.html
new file mode 100644
index 0000000..50f0653
--- /dev/null
+++ b/core/testdata/format/java-layout-html/const.package-summary.html
@@ -0,0 +1,35 @@
+<!-- File: /test/p/package-summary.html -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>p</h1>
+    <h2>Classes</h2>
+    <table>
+      <tbody>
+        <tr>
+          <td><a href="D.html#">D</a></td>
+          <td></td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Top-level constants summary</h2>
+    <table>
+      <tbody>
+        <tr>
+          <td><span class="keyword">const</span> <span class="identifier">Int</span></td>
+          <td>
+            <div><code><a href="#x%3Akotlin.Int">x</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Top-level constants</h2>
+    <div id="x:kotlin.Int">
+      <h3>x</h3>
+      <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">x</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+      <pre>Value: <code>0</code></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/externalClassExtension.kt b/core/testdata/format/java-layout-html/externalClassExtension.kt
new file mode 100644
index 0000000..04415cc
--- /dev/null
+++ b/core/testdata/format/java-layout-html/externalClassExtension.kt
@@ -0,0 +1,5 @@
+package p
+
+fun String.ext() {
+    println(this)
+}
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/externalClassExtension.package-summary.html b/core/testdata/format/java-layout-html/externalClassExtension.package-summary.html
new file mode 100644
index 0000000..39db6c8
--- /dev/null
+++ b/core/testdata/format/java-layout-html/externalClassExtension.package-summary.html
@@ -0,0 +1,25 @@
+<!-- File: /test/p/package-summary.html -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>p</h1>
+    <h2>Top-level functions summary</h2>
+    <table>
+      <tbody>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+          <td>
+            <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a>.<a href="#%28kotlin.String%29.ext%28%29">ext</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Top-level functions</h2>
+    <div id="(kotlin.String).ext()">
+      <h3>ext</h3>
+      <pre><span class="keyword">fun </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a><span class="symbol">.</span><span class="identifier">ext</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/genericExtension.html b/core/testdata/format/java-layout-html/genericExtension.html
new file mode 100644
index 0000000..91b318e
--- /dev/null
+++ b/core/testdata/format/java-layout-html/genericExtension.html
@@ -0,0 +1,99 @@
+<!-- File: /test/p/Some.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>Some</h1>
+    <pre><span class="keyword">class </span><span class="identifier">Some</span></pre>
+    <table>
+      <tr>
+        <td><a href="#">p.Some</a></td>
+      </tr>
+    </table>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constructors</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+            <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Inherited extension functions</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>From <a href="package-summary.html">p</a>
+            <table>
+              <tbody>
+                <tr>
+                  <td><span class="identifier">String</span></td>
+                  <td>
+                    <div><code><a href="package-summary.html#%28p.extFun.T%29.extFun%28%29%2FT"><span class="identifier">T</span></a>.<a href="package-summary.html#%28p.extFun.T%29.extFun%28%29">extFun</a>()</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">String</span></td>
+                  <td>
+                    <div><code><a href="package-summary.html#%28p.nullableExtFun.T%29.nullableExtFun%28%29%2FT"><span class="identifier">T</span></a>.<a href="package-summary.html#%28p.nullableExtFun.T%29.nullableExtFun%28%29">nullableExtFun</a>()</code></div>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Inherited extension properties</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>From <a href="package-summary.html">p</a>
+            <table>
+              <tbody>
+                <tr>
+                  <td><span class="identifier">String</span></td>
+                  <td>
+                    <div><code><a href="package-summary.html#%28p.extVal.T%29.extVal%3Akotlin.String">extVal</a></code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">String</span></td>
+                  <td>
+                    <div><code><a href="package-summary.html#%28p.nullableExtVal.T%29.nullableExtVal%3Akotlin.String">nullableExtVal</a></code></div>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constructors</h2>
+    <div id="&lt;init&gt;()">
+      <h3>&lt;init&gt;</h3>
+      <pre><span class="identifier">Some</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/genericExtension.kt b/core/testdata/format/java-layout-html/genericExtension.kt
new file mode 100644
index 0000000..0a9d74e
--- /dev/null
+++ b/core/testdata/format/java-layout-html/genericExtension.kt
@@ -0,0 +1,10 @@
+package p
+
+class Some
+
+
+fun <T : Some> T.extFun() = ""
+val <T : Some> T.extVal get() = ""
+
+fun <T : Some?> T.nullableExtFun() = ""
+val <T : Some?> T.nullableExtVal get() = ""
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/simple.html b/core/testdata/format/java-layout-html/simple.html
new file mode 100644
index 0000000..0b82c49
--- /dev/null
+++ b/core/testdata/format/java-layout-html/simple.html
@@ -0,0 +1,177 @@
+<!-- File: /test/p/Foo.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>Foo</h1>
+    <pre><span class="keyword">class </span><span class="identifier">Foo</span></pre>
+    <table>
+      <tr>
+        <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html">kotlin.Any</a></td>
+      </tr>
+      <tr>
+        <td>   ↳</td>
+        <td><a href="#">p.Foo</a></td>
+      </tr>
+    </table>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constructors</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+            <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Functions</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+          <td>
+            <div><code><a href="#s%28%29">s</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Inherited extension functions</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>From <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/index.html">kotlin</a>
+            <table>
+              <tbody>
+                <tr>
+                  <td><span class="identifier">T</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/also.html">also</a>(<span class="identifier" id="kotlin$also(kotlin.also.T, kotlin.Function1((kotlin.also.T, kotlin.Unit)))/block">block</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">T</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/apply.html">apply</a>(<span class="identifier" id="kotlin$apply(kotlin.apply.T, kotlin.Function1((kotlin.apply.T, kotlin.Unit)))/block">block</span><span class="symbol">:</span>&nbsp;<span class="identifier">T</span><span class="symbol">.</span><span class="symbol">(</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">R</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html">let</a>(<span class="identifier" id="kotlin$let(kotlin.let.T, kotlin.Function1((kotlin.let.T, kotlin.let.R)))/block">block</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<span class="identifier">R</span>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">R</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/run.html">run</a>(<span class="identifier" id="kotlin$run(kotlin.run.T, kotlin.Function1((kotlin.run.T, kotlin.run.R)))/block">block</span><span class="symbol">:</span>&nbsp;<span class="identifier">T</span><span class="symbol">.</span><span class="symbol">(</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<span class="identifier">R</span>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">T</span><span class="symbol">?</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/take-if.html">takeIf</a>(<span class="identifier" id="kotlin$takeIf(kotlin.takeIf.T, kotlin.Function1((kotlin.takeIf.T, kotlin.Boolean)))/predicate">predicate</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html"><span class="identifier">Boolean</span></a>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><span class="identifier">T</span><span class="symbol">?</span></td>
+                  <td>
+                    <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/take-unless.html">takeUnless</a>(<span class="identifier" id="kotlin$takeUnless(kotlin.takeUnless.T, kotlin.Function1((kotlin.takeUnless.T, kotlin.Boolean)))/predicate">predicate</span><span class="symbol">:</span>&nbsp;<span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span>&nbsp;<span class="symbol">-&gt;</span>&nbsp;<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html"><span class="identifier">Boolean</span></a>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-pair/index.html"><span class="identifier">Pair</span></a><span class="symbol">&lt;</span><span class="identifier">A</span><span class="symbol">,</span>&nbsp;<span class="identifier">B</span><span class="symbol">&gt;</span></td>
+                  <td>
+                    <div><code><span class="identifier">A</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/to.html">to</a>(<span class="identifier" id="kotlin$to(kotlin.to.A, kotlin.to.B)/that">that</span><span class="symbol">:</span>&nbsp;<span class="identifier">B</span>)</code></div>
+                  </td>
+                </tr>
+                <tr>
+                  <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+                  <td>
+                    <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html"><span class="identifier">Any</span></a><span class="symbol">?</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/to-string.html">toString</a>()</code></div>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Properties</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+          <td>
+            <div><code><a href="#g%3Akotlin.String">g</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Inherited extension properties</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>From <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/index.html">kotlin.jvm</a>
+            <table>
+              <tbody>
+                <tr>
+                  <td><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html"><span class="identifier">Class</span></a><span class="symbol">&lt;</span><span class="identifier">T</span><span class="symbol">&gt;</span></td>
+                  <td>
+                    <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/java-class.html">javaClass</a></code></div>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constructors</h2>
+    <div id="&lt;init&gt;()">
+      <h3>&lt;init&gt;</h3>
+      <pre><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+    </div>
+    <h2>Functions</h2>
+    <div id="s()">
+      <h3>s</h3>
+      <pre><span class="keyword">fun </span><span class="identifier">s</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+    </div>
+    <h2>Properties</h2>
+    <div id="g:kotlin.String">
+      <h3>g</h3>
+      <pre><span class="keyword">val </span><span class="identifier">g</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/simple.kt b/core/testdata/format/java-layout-html/simple.kt
new file mode 100644
index 0000000..89789c4
--- /dev/null
+++ b/core/testdata/format/java-layout-html/simple.kt
@@ -0,0 +1,8 @@
+package p
+
+
+class Foo {
+    fun s(): Unit {}
+
+    val g = ""
+}
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/topLevel.kt b/core/testdata/format/java-layout-html/topLevel.kt
new file mode 100644
index 0000000..85b1a43
--- /dev/null
+++ b/core/testdata/format/java-layout-html/topLevel.kt
@@ -0,0 +1,15 @@
+package p
+
+class Some
+
+fun topLevelFun() {}
+
+val topLevelVal = ""
+
+const val topLevelConst = ""
+
+val topLevelGetVal get() = ""
+
+val Some.topLevelExtVal get() = ""
+
+fun Some.topLevelExtFun() {}
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/topLevel.package-summary.html b/core/testdata/format/java-layout-html/topLevel.package-summary.html
new file mode 100644
index 0000000..791c371
--- /dev/null
+++ b/core/testdata/format/java-layout-html/topLevel.package-summary.html
@@ -0,0 +1,90 @@
+<!-- File: /test/p/package-summary.html -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>p</h1>
+    <h2>Classes</h2>
+    <table>
+      <tbody>
+        <tr>
+          <td><a href="Some.html#">Some</a></td>
+          <td></td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Top-level functions summary</h2>
+    <table>
+      <tbody>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+          <td>
+            <div><code><a href="#topLevelFun%28%29">topLevelFun</a>()</code></div>
+          </td>
+        </tr>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+          <td>
+            <div><code><a href="Some.html#"><span class="identifier">Some</span></a>.<a href="#%28p.Some%29.topLevelExtFun%28%29">topLevelExtFun</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Top-level properties summary</h2>
+    <table>
+      <tbody>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+          <td>
+            <div><code><a href="#topLevelVal%3Akotlin.String">topLevelVal</a></code></div>
+          </td>
+        </tr>
+        <tr>
+          <td><span class="keyword">const</span> <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+          <td>
+            <div><code><a href="#topLevelConst%3Akotlin.String">topLevelConst</a></code></div>
+          </td>
+        </tr>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+          <td>
+            <div><code><a href="#topLevelGetVal%3Akotlin.String">topLevelGetVal</a></code></div>
+          </td>
+        </tr>
+        <tr>
+          <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+          <td>
+            <div><code><a href="#%28p.Some%29.topLevelExtVal%3Akotlin.String">topLevelExtVal</a></code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Top-level functions</h2>
+    <div id="topLevelFun()">
+      <h3>topLevelFun</h3>
+      <pre><span class="keyword">fun </span><span class="identifier">topLevelFun</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+    </div>
+    <div id="(p.Some).topLevelExtFun()">
+      <h3>topLevelExtFun</h3>
+      <pre><span class="keyword">fun </span><a href="Some.html#"><span class="identifier">Some</span></a><span class="symbol">.</span><span class="identifier">topLevelExtFun</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+    </div>
+    <h2>Top-level properties</h2>
+    <div id="topLevelVal:kotlin.String">
+      <h3>topLevelVal</h3>
+      <pre><span class="keyword">val </span><span class="identifier">topLevelVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+    </div>
+    <div id="topLevelConst:kotlin.String">
+      <h3>topLevelConst</h3>
+      <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">topLevelConst</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+    </div>
+    <div id="topLevelGetVal:kotlin.String">
+      <h3>topLevelGetVal</h3>
+      <pre><span class="keyword">val </span><span class="identifier">topLevelGetVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+    </div>
+    <div id="(p.Some).topLevelExtVal:kotlin.String">
+      <h3>topLevelExtVal</h3>
+      <pre><span class="keyword">val </span><a href="Some.html#"><span class="identifier">Some</span></a><span class="symbol">.</span><span class="identifier">topLevelExtVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/unresolvedExternalClass.html b/core/testdata/format/java-layout-html/unresolvedExternalClass.html
new file mode 100644
index 0000000..eca242c
--- /dev/null
+++ b/core/testdata/format/java-layout-html/unresolvedExternalClass.html
@@ -0,0 +1,63 @@
+<!-- File: /test/p/MyException.html# -->
+<html>
+  <head>
+    <meta charset="UTF-8">
+  </head>
+  <body>
+    <h1>MyException</h1>
+    <pre><span class="keyword">class </span><span class="identifier">MyException</span>&nbsp;<span class="symbol">:</span>&nbsp;<span class="identifier">Exception</span></pre>
+    <table>
+      <tr>
+        <td><a href="#">p.MyException</a></td>
+      </tr>
+    </table>
+    <h2>Summary</h2>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Constructors</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>
+            <div><code><a href="#%3Cinit%3E%28%29">&lt;init&gt;</a>()</code></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <table>
+      <thead>
+        <tr>
+          <td>
+            <h3>Inherited extension functions</h3>
+          </td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>From <a href="#">p.java.lang.Exception</a>
+            <table>
+              <tbody>
+                <tr>
+                  <td><span class="identifier">Unit</span></td>
+                  <td>
+                    <div><code><span class="identifier">Exception</span>.<a href="package-summary.html#%28kotlin.Exception%29.ext%28%29">ext</a>()</code></div>
+                    <p>Some docs...</p>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <h2>Constructors</h2>
+    <div id="&lt;init&gt;()">
+      <h3>&lt;init&gt;</h3>
+      <pre><span class="identifier">MyException</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+    </div>
+  </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/unresolvedExternalClass.kt b/core/testdata/format/java-layout-html/unresolvedExternalClass.kt
new file mode 100644
index 0000000..0831dac
--- /dev/null
+++ b/core/testdata/format/java-layout-html/unresolvedExternalClass.kt
@@ -0,0 +1,13 @@
+package p
+
+// noStdlibLink set to true for that test
+
+/**
+ * Some docs...
+ */
+fun Exception.ext() {
+
+}
+
+
+class MyException: Exception()
\ No newline at end of file
diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt b/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
index 90e5b5f..46a5727 100644
--- a/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
+++ b/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
@@ -41,6 +41,7 @@
     val noStdlibLink: Boolean
     val cacheRoot: String?
     val suppressedFiles: List<String>
+    val collectInheritedExtensionsFromLibraries: Boolean
 
     interface SourceRoot {
         val path: String
@@ -82,29 +83,30 @@
 }
 
 data class SerializeOnlyDokkaConfiguration(
-        override val moduleName: String,
-        override val classpath: List<String>,
-        override val sourceRoots: List<DokkaConfiguration.SourceRoot>,
-        override val samples: List<String>,
-        override val includes: List<String>,
-        override val outputDir: String,
-        override val format: String,
-        override val includeNonPublic: Boolean,
-        override val includeRootPackage: Boolean,
-        override val reportUndocumented: Boolean,
-        override val skipEmptyPackages: Boolean,
-        override val skipDeprecated: Boolean,
-        override val jdkVersion: Int,
-        override val generateIndexPages: Boolean,
-        override val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition>,
-        override val impliedPlatforms: List<String>,
-        override val perPackageOptions: List<DokkaConfiguration.PackageOptions>,
-        override val externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink>,
-        override val noStdlibLink: Boolean,
-        override val cacheRoot: String?,
-        override val suppressedFiles: List<String>,
-        override val languageVersion: String?,
-        override val apiVersion: String?
+    override val moduleName: String,
+    override val classpath: List<String>,
+    override val sourceRoots: List<DokkaConfiguration.SourceRoot>,
+    override val samples: List<String>,
+    override val includes: List<String>,
+    override val outputDir: String,
+    override val format: String,
+    override val includeNonPublic: Boolean,
+    override val includeRootPackage: Boolean,
+    override val reportUndocumented: Boolean,
+    override val skipEmptyPackages: Boolean,
+    override val skipDeprecated: Boolean,
+    override val jdkVersion: Int,
+    override val generateIndexPages: Boolean,
+    override val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition>,
+    override val impliedPlatforms: List<String>,
+    override val perPackageOptions: List<DokkaConfiguration.PackageOptions>,
+    override val externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink>,
+    override val noStdlibLink: Boolean,
+    override val cacheRoot: String?,
+    override val suppressedFiles: List<String>,
+    override val languageVersion: String?,
+    override val apiVersion: String?,
+    override val collectInheritedExtensionsFromLibraries: Boolean
 ) : DokkaConfiguration
 
 
diff --git a/runners/cli/src/main/kotlin/cli/main.kt b/runners/cli/src/main/kotlin/cli/main.kt
index fe945ed..111e142 100644
--- a/runners/cli/src/main/kotlin/cli/main.kt
+++ b/runners/cli/src/main/kotlin/cli/main.kt
@@ -62,6 +62,9 @@
     @set:Argument(value = "apiVersion", description = "Kotlin Api Version to pass to Kotlin Analysis")
     var apiVersion: String? = null
 
+    @set:Argument(value = "collectInheritedExtensionsFromLibraries", description = "Search for applicable extensions in libraries")
+    var collectInheritedExtensionsFromLibraries: Boolean = false
+
 }
 
 
@@ -106,18 +109,19 @@
         val classPath = arguments.classpath.split(File.pathSeparatorChar).toList()
 
         val documentationOptions = DocumentationOptions(
-                arguments.outputDir.let { if (it.endsWith('/')) it else it + '/' },
-                arguments.outputFormat,
-                skipDeprecated = arguments.nodeprecated,
-                sourceLinks = sourceLinks,
-                impliedPlatforms = arguments.impliedPlatforms.split(','),
-                perPackageOptions = parsePerPackageOptions(arguments.packageOptions),
-                jdkVersion = arguments.jdkVersion,
-                externalDocumentationLinks = parseLinks(arguments.links),
-                noStdlibLink = arguments.noStdlibLink,
-                cacheRoot = arguments.cacheRoot,
-                languageVersion = arguments.languageVersion,
-                apiVersion = arguments.apiVersion
+            arguments.outputDir.let { if (it.endsWith('/')) it else it + '/' },
+            arguments.outputFormat,
+            skipDeprecated = arguments.nodeprecated,
+            sourceLinks = sourceLinks,
+            impliedPlatforms = arguments.impliedPlatforms.split(','),
+            perPackageOptions = parsePerPackageOptions(arguments.packageOptions),
+            jdkVersion = arguments.jdkVersion,
+            externalDocumentationLinks = parseLinks(arguments.links),
+            noStdlibLink = arguments.noStdlibLink,
+            cacheRoot = arguments.cacheRoot,
+            languageVersion = arguments.languageVersion,
+            apiVersion = arguments.apiVersion,
+            collectInheritedExtensionsFromLibraries = arguments.collectInheritedExtensionsFromLibraries
         )
 
         val generator = DokkaGenerator(
diff --git a/runners/gradle-plugin/src/main/kotlin/main.kt b/runners/gradle-plugin/src/main/kotlin/main.kt
index bdecc3f..5138fce 100644
--- a/runners/gradle-plugin/src/main/kotlin/main.kt
+++ b/runners/gradle-plugin/src/main/kotlin/main.kt
@@ -122,6 +122,9 @@
     @Optional @Input
     var apiVersion: String? = null
 
+    @Input
+    var collectInheritedExtensionsFromLibraries: Boolean = false
+
     @get:Input
     internal val kotlinCompileBasedClasspathAndSourceRoots: ClasspathAndSourceRoots by lazy { extractClasspathAndSourceRootsFromKotlinTasks() }
 
@@ -269,29 +272,31 @@
             val bootstrapProxy: DokkaBootstrap = automagicTypedProxy(javaClass.classLoader, bootstrapInstance)
 
             val configuration = SerializeOnlyDokkaConfiguration(
-                    moduleName,
-                    fullClasspath.map { it.absolutePath },
-                    sourceRoots,
-                    samples.filterNotNull().map { project.file(it).absolutePath },
-                    includes.filterNotNull().map { project.file(it).absolutePath },
-                    outputDirectory,
-                    outputFormat,
-                    includeNonPublic,
-                    false,
-                    reportNotDocumented,
-                    skipEmptyPackages,
-                    skipDeprecated,
-                    jdkVersion,
-                    true,
-                    linkMappings,
-                    impliedPlatforms,
-                    perPackageOptions,
-                    externalDocumentationLinks,
-                    noStdlibLink,
-                    cacheRoot,
-                    collectSuppressedFiles(sourceRoots),
-                    languageVersion,
-                    apiVersion)
+                moduleName,
+                fullClasspath.map { it.absolutePath },
+                sourceRoots,
+                samples.filterNotNull().map { project.file(it).absolutePath },
+                includes.filterNotNull().map { project.file(it).absolutePath },
+                outputDirectory,
+                outputFormat,
+                includeNonPublic,
+                false,
+                reportNotDocumented,
+                skipEmptyPackages,
+                skipDeprecated,
+                jdkVersion,
+                true,
+                linkMappings,
+                impliedPlatforms,
+                perPackageOptions,
+                externalDocumentationLinks,
+                noStdlibLink,
+                cacheRoot,
+                collectSuppressedFiles(sourceRoots),
+                languageVersion,
+                apiVersion,
+                collectInheritedExtensionsFromLibraries
+            )
 
 
             bootstrapProxy.configure(