diff options
author | Paweł Marks <pmarks@virtuslab.com> | 2020-07-17 16:36:09 +0200 |
---|---|---|
committer | Paweł Marks <pmarks@virtuslab.com> | 2020-07-17 16:36:09 +0200 |
commit | 6996b1135f61c7d2cb60b0652c6a2691dda31990 (patch) | |
tree | d568096c25e31c28d14d518a63458b5a7526b896 /core | |
parent | de56cab76f556e5b4af0b8c8cb08d8b482b86d0a (diff) | |
parent | 1c3530dcbb50c347f80bef694829dbefe89eca77 (diff) | |
download | dokka-6996b1135f61c7d2cb60b0652c6a2691dda31990.tar.gz dokka-6996b1135f61c7d2cb60b0652c6a2691dda31990.tar.bz2 dokka-6996b1135f61c7d2cb60b0652c6a2691dda31990.zip |
Merge branch 'dev-0.11.0'
Diffstat (limited to 'core')
150 files changed, 2979 insertions, 14575 deletions
diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 00000000..1e1b410b --- /dev/null +++ b/core/.gitignore @@ -0,0 +1 @@ +src/main/resources/dokka/scripts/*.svg
\ No newline at end of file diff --git a/core/build.gradle b/core/build.gradle deleted file mode 100644 index 9e3026cb..00000000 --- a/core/build.gradle +++ /dev/null @@ -1,53 +0,0 @@ -import javax.tools.ToolProvider - -buildscript { - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -apply plugin: 'kotlin' - -sourceCompatibility = 1.8 - -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { - languageVersion = language_version - apiVersion = language_version - jvmTarget = "1.8" - } -} - -dependencies { - compile "org.jetbrains.kotlin:kotlin-stdlib:$bundled_kotlin_compiler_version" - compile "org.jetbrains.kotlin:kotlin-reflect:$bundled_kotlin_compiler_version" - - compile group: 'com.google.inject', name: 'guice', version: '3.0' - compile "org.jsoup:jsoup:1.8.3" - - compile "org.jetbrains.kotlin:kotlin-compiler:$bundled_kotlin_compiler_version" - compile "org.jetbrains.kotlin:kotlin-script-runtime:$bundled_kotlin_compiler_version" - - compile "org.jetbrains:markdown:0.1.41" - - compile intellijCoreAnalysis() - - compile "org.jetbrains.kotlin:kotlin-plugin-ij193:$kotlin_plugin_version" //TODO: parametrize ij version after 1.3.70 - - compile 'org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.8' - - //tools.jar - def toolsJar = files(((URLClassLoader) ToolProvider.getSystemToolClassLoader()).getURLs().findAll { it.path.endsWith("jar") }) - compileOnly toolsJar - testCompile toolsJar - - compile project(":integration") - - testCompile group: 'junit', name: 'junit', version: '4.12' - testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit', version: kotlin_version - testCompile "com.nhaarman:mockito-kotlin-kt1.1:1.5.0" - implementation "com.google.code.gson:gson:$gson_version" - testCompile ideaRT() - testImplementation "org.jetbrains.kotlin:kotlin-stdlib-js:$bundled_kotlin_compiler_version" - testImplementation "org.jetbrains.kotlin:kotlin-stdlib-common:$bundled_kotlin_compiler_version" -}
\ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts new file mode 100644 index 00000000..168baf48 --- /dev/null +++ b/core/build.gradle.kts @@ -0,0 +1,33 @@ +import org.jetbrains.registerDokkaArtifactPublication + +plugins { + `maven-publish` + id("com.jfrog.bintray") +} + +dependencies { + api("org.jetbrains:markdown:0.1.45") + implementation(kotlin("reflect")) + implementation("com.google.code.gson:gson:2.8.5") + implementation("org.jsoup:jsoup:1.12.1") + + testImplementation(project(":testApi")) + testImplementation(kotlin("test-junit")) +} + +tasks { + processResources { + val dokka_version: String by project + eachFile { + if (name == "dokka-version.properties") { + filter { line -> + line.replace("<dokka-version>", dokka_version) + } + } + } + } +} + +registerDokkaArtifactPublication("dokkaCore") { + artifactId = "dokka-core" +} diff --git a/core/migration_guide.md b/core/migration_guide.md new file mode 100644 index 00000000..82be3768 --- /dev/null +++ b/core/migration_guide.md @@ -0,0 +1,166 @@ +## Changes between 0.10.x and 1.4-M3 + +There are two main changes between dokka 0.10.x and 1.4-M3 + +The first is the introduction of plugability - new documentation creating process is divided into several steps and each step provides extension points to be used. To learn more about new dokka pipeline and possible plugins, please read Developer's guide. + +Second difference comes with the change with the subject of dokka pass. Previously, separate dokka passes where set for every targeted platform, now every source set has its own pass and the name itself changed to `sourceSet`. + +### Gradle + +With changing the approach from platform-based to source-set-based, we replace both `configuration` and `multiplatform` blocks with `dokkaSourceSets`. It's still a collection of dokka passes configuration, so the structure stays as it was. +Format selection is now done using plugins with dokka providing preconfigured tasks for different formats: `dokkaHtml`, `dokkaJavadoc`, `dokkaGfm` and `dokkaJekyll`. + +* `moduleName` has changed to `moduleDisplayName` +* `targets` has been dropped. Declaration merging is now done by the source set mechanism. Name customization can be done using `displayName` property +* `outputFormat` has been dropped. Format can be selected with appropriate plugins, please refer to the README + +#### Groovy +##### Old +```groovy +dokka { + outputFormat = 'html' + outputDirectory = "$buildDir/dokka" + multiplatform { + js { + includes = ["src/jsMain/resources/doc.md"] + samples = ["src/jsMain/resources/Samples.kt"] + sourceLink { + path = "src/jsMain/kotlin" + url = "https:/dokka.documentation.com/jsMain/kotlin" + lineSuffix = "#L" + } + } + jvm { + includes = ["src/jvmMain/resources/doc.md"] + samples = ["src/jsMain/resources/Samples.kt"] + sourceLink { + path = "src/jvmMain/kotlin" + url = "https:/dokka.documentation.com/jvmMain/kotlin" + lineSuffix = "#L" + } + } + } +} +``` +##### New +```groovy +dokkaHtml { // or dokkaGfm, dokkaJekyll, ... + outputDirectory = "$buildDir/dokka" + + dokkaSourceSets { + commonMain {} + jsMain { + includes = ["src/jsMain/resources/doc.md"] + samples = ["src/jsMain/resources/Samples.kt"] + sourceLink { + path = "src/jsMain/kotlin" + url = "https:/dokka.documentation.com/jsMain/kotlin" + lineSuffix = "#L" + } + } + + jvmMain { + includes = ["src/jvmMain/resources/doc.md"] + samples = ["src/jsMain/resources/Samples.kt"] + sourceLink { + path = "src/jvmMain/kotlin" + url = "https:/dokka.documentation.com/jvmMain/kotlin" + lineSuffix = "#L" + } + } + } +} +``` + +#### Kotlin + +##### Old +```kotlin +kotlin { // Kotlin Multiplatform plugin configuration + jvm() + js("customName") +} + +val dokka by getting(DokkaTask::class) { + outputDirectory = "$buildDir/dokka" + outputFormat = "html" + + multiplatform { + val customName by creating { // The same name as in Kotlin Multiplatform plugin, so the sources are fetched automatically + includes = listOf("packages.md", "extra.md") + samples = listOf("samples/basic.kt", "samples/advanced.kt") + } + + register("differentName") { // Different name, so source roots must be passed explicitly + targets = listOf("JVM") + platform = "jvm" + sourceRoot { + path = kotlin.sourceSets.getByName("jvmMain").kotlin.srcDirs.first().toString() + } + sourceRoot { + path = kotlin.sourceSets.getByName("commonMain").kotlin.srcDirs.first().toString() + } + } + } +} +``` + +##### New +```kotlin +kotlin { // Kotlin Multiplatform plugin configuration + jvm() + js("customName") +} + +dokkaHtml { // or dokkaGfm, dokkaJekyll, ... + outputDirectory = "$buildDir/dokka" + + dokkaSourceSets { + val customNameMain by creating { // The same source set name as in Kotlin Multiplatform plugin, so the sources are fetched automatically + includes = listOf("packages.md", "extra.md") + samples = listOf("samples/basic.kt", "samples/advanced.kt") + } + + val commonMain by creating {} // Document commonj source set + + create("differentName") { // Different name, so source roots must be passed explicitly + sourceSetName = listOf("JVM") + platform = "jvm" + sourceRoot { + path = kotlin.sourceSets.getByName("jvmMain").kotlin.srcDirs.first().toString() + } + dependsOn(commonMain) // The jvm source set depends on the common source set + } + } +} +``` + +#### Multimodule page + +There is a new task, `dokkaMultimodule`, that creates an index page for all documented subprojects. It is available only for top-level project. + +```groovy +dokkaMultimodule { + // File to be used as an excerpt for each subprojects + documentationFileName = "README.md" + // output format for rendering multimodule page (accepts the same values as regular dokka task) + outputFormat = "html" + // output directory for page + outputDirectory = "$buildDir/dokka" +} +``` + +### Maven + +There are no changes in maven configuration API for dokka, all previous configurations should work without issues. +The default platform label is "JVM", `sourceSetName` can be used to change it. + +### Ant +Support for the Ant plugin has been dropped, dokka should be used with CLI in Ant instead. + +### Command Line +Dokka fajtar has been dropped, thus the command line interface has changed slightly. +Most importantly, all plugins and their dependencies have to be provided in the `-pluginsClasspath` argument (paths are seperated with ';'). +A build tool like Gradle or Maven is recommended to resolve and download all required artifacts. +Instead of creating a long configuration command, dokka can be configured with a JSON file. Please refer to the README. diff --git a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt deleted file mode 100644 index f5705c89..00000000 --- a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt +++ /dev/null @@ -1,592 +0,0 @@ -package org.jetbrains.dokka - -import com.google.common.collect.ImmutableMap -import com.intellij.core.CoreApplicationEnvironment -import com.intellij.core.CoreModuleManager -import com.intellij.mock.MockApplication -import com.intellij.mock.MockComponentManager -import com.intellij.openapi.Disposable -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.extensions.Extensions -import com.intellij.openapi.module.Module -import com.intellij.openapi.module.ModuleManager -import com.intellij.openapi.project.Project -import com.intellij.openapi.roots.OrderEnumerationHandler -import com.intellij.openapi.roots.ProjectFileIndex -import com.intellij.openapi.roots.ProjectRootManager -import com.intellij.openapi.util.Disposer -import com.intellij.openapi.vfs.StandardFileSystems -import com.intellij.psi.PsiElement -import com.intellij.psi.search.GlobalSearchScope -import org.jetbrains.kotlin.analyzer.* -import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters -import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices -import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory -import org.jetbrains.kotlin.builtins.DefaultBuiltIns -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns -import org.jetbrains.kotlin.caches.resolve.KotlinCacheService -import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys -import org.jetbrains.kotlin.cli.common.config.ContentRoot -import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot -import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot -import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles -import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM -import org.jetbrains.kotlin.cli.jvm.config.* -import org.jetbrains.kotlin.cli.jvm.index.JavaRoot -import org.jetbrains.kotlin.config.* -import org.jetbrains.kotlin.container.getService -import org.jetbrains.kotlin.container.tryGetService -import org.jetbrains.kotlin.context.ProjectContext -import org.jetbrains.kotlin.context.withModule -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl -import org.jetbrains.kotlin.ide.konan.NativeLibraryInfo -import org.jetbrains.kotlin.ide.konan.analyzer.NativeResolverForModuleFactory -import org.jetbrains.kotlin.ide.konan.decompiler.KotlinNativeLoadingMetadataCache -import org.jetbrains.kotlin.idea.resolve.ResolutionFacade -import org.jetbrains.kotlin.js.config.JSConfigurationKeys -import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices -import org.jetbrains.kotlin.js.resolve.JsResolverForModuleFactory -import org.jetbrains.kotlin.library.impl.createKotlinLibrary -import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.platform.CommonPlatforms -import org.jetbrains.kotlin.platform.TargetPlatform -import org.jetbrains.kotlin.platform.js.JsPlatforms -import org.jetbrains.kotlin.platform.jvm.JvmPlatforms -import org.jetbrains.kotlin.platform.jvm.JvmPlatforms.unspecifiedJvmPlatform -import org.jetbrains.kotlin.platform.konan.KonanPlatforms -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.BindingTrace -import org.jetbrains.kotlin.resolve.CompilerEnvironment -import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices -import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics -import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters -import org.jetbrains.kotlin.resolve.jvm.JvmResolverForModuleFactory -import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices -import org.jetbrains.kotlin.resolve.konan.platform.NativePlatformAnalyzerServices -import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -import org.jetbrains.kotlin.resolve.lazy.ResolveSession -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice -import org.jetbrains.kotlin.util.slicedMap.WritableSlice -import java.io.File - -const val JAR_SEPARATOR = "!/" -const val KLIB_EXTENSION = "klib" - -/** - * Kotlin as a service entry point - * - * Configures environment, analyses files and provides facilities to perform code processing without emitting bytecode - * - * $messageCollector: required by compiler infrastructure and will receive all compiler messages - * $body: optional and can be used to configure environment without creating local variable - */ -class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPlatform: Platform) : Disposable { - val configuration = CompilerConfiguration() - - init { - configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) - } - - fun createCoreEnvironment(): KotlinCoreEnvironment { - System.setProperty("idea.io.use.fallback", "true") - - val configFiles = when (analysisPlatform) { - Platform.jvm, Platform.common -> EnvironmentConfigFiles.JVM_CONFIG_FILES - Platform.native -> EnvironmentConfigFiles.NATIVE_CONFIG_FILES - Platform.js -> EnvironmentConfigFiles.JS_CONFIG_FILES - } - - val environment = KotlinCoreEnvironment.createForProduction(this, configuration, configFiles) - val projectComponentManager = environment.project as MockComponentManager - - val projectFileIndex = CoreProjectFileIndex( - environment.project, - environment.configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS) - ) - - val moduleManager = object : CoreModuleManager(environment.project, this) { - override fun getModules(): Array<out Module> = arrayOf(projectFileIndex.module) - } - - CoreApplicationEnvironment.registerComponentInstance( - projectComponentManager.picoContainer, - ModuleManager::class.java, moduleManager - ) - - Extensions.registerAreaClass("IDEA_MODULE", null) - CoreApplicationEnvironment.registerExtensionPoint( - Extensions.getRootArea(), - OrderEnumerationHandler.EP_NAME, OrderEnumerationHandler.Factory::class.java - ) - - projectComponentManager.registerService( - ProjectFileIndex::class.java, - projectFileIndex - ) - - projectComponentManager.registerService( - ProjectRootManager::class.java, - CoreProjectRootManager(projectFileIndex) - ) - - return environment - } - - private fun createSourceModuleSearchScope(project: Project, sourceFiles: List<KtFile>): GlobalSearchScope = - when (analysisPlatform) { - Platform.jvm -> TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, sourceFiles) - Platform.js, Platform.common, Platform.native -> GlobalSearchScope.filesScope( - project, - sourceFiles.map { it.virtualFile }.toSet() - ) - } - - fun createResolutionFacade(environment: KotlinCoreEnvironment): Pair<DokkaResolutionFacade, DokkaResolutionFacade> { - val projectContext = ProjectContext(environment.project, "Dokka") - val sourceFiles = environment.getSourceFiles() - - - val targetPlatform = when (analysisPlatform) { - Platform.js -> JsPlatforms.defaultJsPlatform - Platform.common -> CommonPlatforms.defaultCommonPlatform - Platform.native -> KonanPlatforms.defaultKonanPlatform - Platform.jvm -> JvmPlatforms.defaultJvmPlatform - } - - val nativeLibraries = classpath.filter { it.extension == KLIB_EXTENSION } - .map { createNativeLibraryModuleInfo(it) } - - val library = object : LibraryModuleInfo { - override val analyzerServices: PlatformDependentAnalyzerServices = - analysisPlatform.analyzerServices() - override val name: Name = Name.special("<library>") - override val platform: TargetPlatform = targetPlatform - override fun dependencies(): List<ModuleInfo> = listOf(this) - override fun getLibraryRoots(): Collection<String> = - classpath.filterNot { it.extension == KLIB_EXTENSION }.map { it.absolutePath } - } - - val module = object : ModuleInfo { - override val analyzerServices: PlatformDependentAnalyzerServices = - analysisPlatform.analyzerServices() - override val name: Name = Name.special("<module>") - override val platform: TargetPlatform = targetPlatform - override fun dependencies(): List<ModuleInfo> = listOf(this, library) + nativeLibraries - } - - val sourcesScope = createSourceModuleSearchScope(environment.project, sourceFiles) - val modulesContent: (ModuleInfo) -> ModuleContent<ModuleInfo> = { - when (it) { - library -> ModuleContent(it, emptyList(), GlobalSearchScope.notScope(sourcesScope)) - module -> ModuleContent(it, emptyList(), GlobalSearchScope.allScope(environment.project)) - in nativeLibraries -> ModuleContent(it, emptyList(), GlobalSearchScope.notScope(sourcesScope)) - else -> throw IllegalArgumentException("Unexpected module info") - } - } - - var builtIns: JvmBuiltIns? = null - - val resolverForProject = when (analysisPlatform) { - Platform.jvm -> { - builtIns = JvmBuiltIns( - projectContext.storageManager, - JvmBuiltIns.Kind.FROM_CLASS_LOADER - ) // TODO we should use FROM_DEPENDENCIES - createJvmResolverForProject( - projectContext, - module, - library, - modulesContent, - sourcesScope, - builtIns - ) - } - Platform.common -> createCommonResolverForProject( - projectContext, - module, - library, - modulesContent, - environment - ) - Platform.js -> createJsResolverForProject(projectContext, module, library, modulesContent) - Platform.native -> createNativeResolverForProject(projectContext, module, library, modulesContent) - - } - val libraryModuleDescriptor = resolverForProject.descriptorForModule(library) - val moduleDescriptor = resolverForProject.descriptorForModule(module) - builtIns?.initialize(moduleDescriptor, true) - - val resolverForLibrary = - resolverForProject.resolverForModule(library) // Required before module to initialize library properly - val resolverForModule = resolverForProject.resolverForModule(module) - val libraryResolutionFacade = - DokkaResolutionFacade(environment.project, libraryModuleDescriptor, resolverForLibrary) - val created = DokkaResolutionFacade(environment.project, moduleDescriptor, resolverForModule) - val projectComponentManager = environment.project as MockComponentManager - projectComponentManager.registerService(KotlinCacheService::class.java, CoreKotlinCacheService(created)) - - return created to libraryResolutionFacade - } - - private fun Platform.analyzerServices() = when (this) { - Platform.js -> JsPlatformAnalyzerServices - Platform.common -> CommonPlatformAnalyzerServices - Platform.native -> NativePlatformAnalyzerServices - Platform.jvm -> JvmPlatformAnalyzerServices - } - - private fun createNativeLibraryModuleInfo(libraryFile: File): LibraryModuleInfo { - val kotlinLibrary = createKotlinLibrary(org.jetbrains.kotlin.konan.file.File(libraryFile.absolutePath), false) - return object : LibraryModuleInfo { - override val analyzerServices: PlatformDependentAnalyzerServices = - analysisPlatform.analyzerServices() - override val name: Name = Name.special("<klib>") - override val platform: TargetPlatform = KonanPlatforms.defaultKonanPlatform - override fun dependencies(): List<ModuleInfo> = listOf(this) - override fun getLibraryRoots(): Collection<String> = listOf(libraryFile.absolutePath) - override val capabilities: Map<ModuleDescriptor.Capability<*>, Any?> - get() = super.capabilities + (NativeLibraryInfo.NATIVE_LIBRARY_CAPABILITY to kotlinLibrary) - } - } - - private fun createCommonResolverForProject( - projectContext: ProjectContext, - module: ModuleInfo, - library: LibraryModuleInfo, - modulesContent: (ModuleInfo) -> ModuleContent<ModuleInfo>, - environment: KotlinCoreEnvironment - ): ResolverForProject<ModuleInfo> { - return object : AbstractResolverForProject<ModuleInfo>( - "Dokka", - projectContext, - modules = listOf(module, library) - ) { - override fun sdkDependency(module: ModuleInfo): ModuleInfo? = null - - override fun modulesContent(module: ModuleInfo): ModuleContent<ModuleInfo> = modulesContent(module) - - override fun builtInsForModule(module: ModuleInfo): KotlinBuiltIns = DefaultBuiltIns.Instance - - override fun createResolverForModule( - descriptor: ModuleDescriptor, - moduleInfo: ModuleInfo - ): ResolverForModule = - CommonResolverForModuleFactory( - CommonAnalysisParameters { content -> - environment.createPackagePartProvider(content.moduleContentScope) - }, - CompilerEnvironment, - unspecifiedJvmPlatform, - true - ).createResolverForModule( - descriptor as ModuleDescriptorImpl, - projectContext.withModule(descriptor), - modulesContent(moduleInfo), - this, - LanguageVersionSettingsImpl.DEFAULT - ) - } - } - - private fun createJsResolverForProject( - projectContext: ProjectContext, - module: ModuleInfo, - library: LibraryModuleInfo, - modulesContent: (ModuleInfo) -> ModuleContent<ModuleInfo> - ): ResolverForProject<ModuleInfo> { - return object : AbstractResolverForProject<ModuleInfo>( - "Dokka", - projectContext, - modules = listOf(module, library) - ) { - override fun modulesContent(module: ModuleInfo): ModuleContent<ModuleInfo> = modulesContent(module) - override fun createResolverForModule( - descriptor: ModuleDescriptor, - moduleInfo: ModuleInfo - ): ResolverForModule = JsResolverForModuleFactory( - CompilerEnvironment - ).createResolverForModule( - descriptor as ModuleDescriptorImpl, - projectContext.withModule(descriptor), - modulesContent(moduleInfo), - this, - LanguageVersionSettingsImpl.DEFAULT - ) - - override fun builtInsForModule(module: ModuleInfo): KotlinBuiltIns = DefaultBuiltIns.Instance - - override fun sdkDependency(module: ModuleInfo): ModuleInfo? = null - } - } - - private fun createNativeResolverForProject( - projectContext: ProjectContext, - module: ModuleInfo, - library: LibraryModuleInfo, - modulesContent: (ModuleInfo) -> ModuleContent<ModuleInfo> - ): ResolverForProject<ModuleInfo> { - return object : AbstractResolverForProject<ModuleInfo>( - "Dokka", - projectContext, - modules = module.dependencies() - ) { - override fun modulesContent(module: ModuleInfo): ModuleContent<ModuleInfo> = modulesContent(module) - override fun createResolverForModule( - descriptor: ModuleDescriptor, - moduleInfo: ModuleInfo - ): ResolverForModule { - (ApplicationManager.getApplication() as MockApplication).addComponent( - KotlinNativeLoadingMetadataCache::class.java, - KotlinNativeLoadingMetadataCache() - ) - - return NativeResolverForModuleFactory( - PlatformAnalysisParameters.Empty, - CompilerEnvironment, - KonanPlatforms.defaultKonanPlatform - ).createResolverForModule( - descriptor as ModuleDescriptorImpl, - projectContext.withModule(descriptor), - modulesContent(moduleInfo), - this, - LanguageVersionSettingsImpl.DEFAULT - ) - } - - override fun builtInsForModule(module: ModuleInfo): KotlinBuiltIns = DefaultBuiltIns.Instance - - override fun sdkDependency(module: ModuleInfo): ModuleInfo? = null - } - } - - private fun createJvmResolverForProject( - projectContext: ProjectContext, - module: ModuleInfo, - library: LibraryModuleInfo, - modulesContent: (ModuleInfo) -> ModuleContent<ModuleInfo>, - sourcesScope: GlobalSearchScope, - builtIns: KotlinBuiltIns - ): ResolverForProject<ModuleInfo> { - val javaRoots = classpath - .mapNotNull { - val rootFile = when (it.extension) { - "jar" -> StandardFileSystems.jar().findFileByPath("${it.absolutePath}${JAR_SEPARATOR}") - else -> StandardFileSystems.local().findFileByPath(it.absolutePath) - } - rootFile?.let { JavaRoot(it, JavaRoot.RootType.BINARY) } - } - - return object : AbstractResolverForProject<ModuleInfo>( - "Dokka", - projectContext, - modules = listOf(module, library) - ) { - override fun modulesContent(module: ModuleInfo): ModuleContent<ModuleInfo> = - when (module) { - library -> ModuleContent(module, emptyList(), GlobalSearchScope.notScope(sourcesScope)) - module -> ModuleContent(module, emptyList(), sourcesScope) - else -> throw IllegalArgumentException("Unexpected module info") - } - - override fun builtInsForModule(module: ModuleInfo): KotlinBuiltIns = builtIns - - override fun createResolverForModule( - descriptor: ModuleDescriptor, - moduleInfo: ModuleInfo - ): ResolverForModule = JvmResolverForModuleFactory( - JvmPlatformParameters({ content -> - JvmPackagePartProvider( - configuration.languageVersionSettings, - content.moduleContentScope - ) - .apply { - addRoots(javaRoots, messageCollector) - } - }, { - val file = (it as JavaClassImpl).psi.containingFile.virtualFile - if (file in sourcesScope) - module - else - library - }), - CompilerEnvironment, - KonanPlatforms.defaultKonanPlatform - ).createResolverForModule( - descriptor as ModuleDescriptorImpl, - projectContext.withModule(descriptor), - modulesContent(moduleInfo), - this, - LanguageVersionSettingsImpl.DEFAULT - ) - - override fun sdkDependency(module: ModuleInfo): ModuleInfo? = null - } - } - - fun loadLanguageVersionSettings(languageVersionString: String?, apiVersionString: String?) { - val languageVersion = LanguageVersion.fromVersionString(languageVersionString) ?: LanguageVersion.LATEST_STABLE - val apiVersion = - apiVersionString?.let { ApiVersion.parse(it) } ?: ApiVersion.createByLanguageVersion(languageVersion) - configuration.languageVersionSettings = LanguageVersionSettingsImpl(languageVersion, apiVersion) - } - - /** - * Classpath for this environment. - */ - val classpath: List<File> - get() = configuration.jvmClasspathRoots - - /** - * Adds list of paths to classpath. - * $paths: collection of files to add - */ - fun addClasspath(paths: List<File>) { - if (analysisPlatform == Platform.js) { - configuration.addAll(JSConfigurationKeys.LIBRARIES, paths.map { it.absolutePath }) - } - configuration.addJvmClasspathRoots(paths) - } - - /** - * Adds path to classpath. - * $path: path to add - */ - fun addClasspath(path: File) { - if (analysisPlatform == Platform.js) { - configuration.add(JSConfigurationKeys.LIBRARIES, path.absolutePath) - } - configuration.addJvmClasspathRoot(path) - } - - /** - * List of source roots for this environment. - */ - val sources: List<String> - get() = configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) - ?.filterIsInstance<KotlinSourceRoot>() - ?.map { it.path } ?: emptyList() - - /** - * Adds list of paths to source roots. - * $list: collection of files to add - */ - fun addSources(list: List<String>) { - list.forEach { - configuration.addKotlinSourceRoot(it) - val file = File(it) - if (file.isDirectory || file.extension == ".java") { - configuration.addJavaSourceRoot(file) - } - } - } - - fun addRoots(list: List<ContentRoot>) { - configuration.addAll(CLIConfigurationKeys.CONTENT_ROOTS, list) - } - - /** - * Disposes the environment and frees all associated resources. - */ - override fun dispose() { - Disposer.dispose(this) - } -} - -fun contentRootFromPath(path: String): ContentRoot { - val file = File(path) - return if (file.extension == "java") JavaSourceRoot(file, null) else KotlinSourceRoot(path, false) -} - - -class DokkaResolutionFacade( - override val project: Project, - override val moduleDescriptor: ModuleDescriptor, - val resolverForModule: ResolverForModule -) : ResolutionFacade { - override fun analyzeWithAllCompilerChecks(elements: Collection<KtElement>): AnalysisResult { - throw UnsupportedOperationException() - } - - override fun <T : Any> tryGetFrontendService(element: PsiElement, serviceClass: Class<T>): T? { - return resolverForModule.componentProvider.tryGetService(serviceClass) - } - - override fun resolveToDescriptor( - declaration: KtDeclaration, - bodyResolveMode: BodyResolveMode - ): DeclarationDescriptor { - return resolveSession.resolveToDescriptor(declaration) - } - - override fun analyze(elements: Collection<KtElement>, bodyResolveMode: BodyResolveMode): BindingContext { - throw UnsupportedOperationException() - } - - val resolveSession: ResolveSession get() = getFrontendService(ResolveSession::class.java) - - override fun analyze(element: KtElement, bodyResolveMode: BodyResolveMode): BindingContext { - if (element is KtDeclaration) { - val descriptor = resolveToDescriptor(element) - return object : BindingContext { - override fun <K : Any?, V : Any?> getKeys(p0: WritableSlice<K, V>?): Collection<K> { - throw UnsupportedOperationException() - } - - override fun getType(p0: KtExpression): KotlinType? { - throw UnsupportedOperationException() - } - - override fun <K : Any?, V : Any?> get(slice: ReadOnlySlice<K, V>?, key: K): V? { - if (key != element) { - throw UnsupportedOperationException() - } - return when { - slice == BindingContext.DECLARATION_TO_DESCRIPTOR -> descriptor as V - slice == BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER && (element as KtParameter).hasValOrVar() -> descriptor as V - else -> null - } - } - - override fun getDiagnostics(): Diagnostics { - throw UnsupportedOperationException() - } - - override fun addOwnDataTo(p0: BindingTrace, p1: Boolean) { - throw UnsupportedOperationException() - } - - override fun <K : Any?, V : Any?> getSliceContents(p0: ReadOnlySlice<K, V>): ImmutableMap<K, V> { - throw UnsupportedOperationException() - } - - } - } - throw UnsupportedOperationException() - } - - override fun <T : Any> getFrontendService(element: PsiElement, serviceClass: Class<T>): T { - throw UnsupportedOperationException() - } - - override fun <T : Any> getFrontendService(serviceClass: Class<T>): T { - return resolverForModule.componentProvider.getService(serviceClass) - } - - override fun <T : Any> getFrontendService(moduleDescriptor: ModuleDescriptor, serviceClass: Class<T>): T { - return resolverForModule.componentProvider.getService(serviceClass) - } - - override fun <T : Any> getIdeService(serviceClass: Class<T>): T { - throw UnsupportedOperationException() - } - -} diff --git a/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt b/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt deleted file mode 100644 index d9093760..00000000 --- a/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt +++ /dev/null @@ -1,42 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.PsiFile -import org.jetbrains.kotlin.analyzer.ModuleInfo -import org.jetbrains.kotlin.caches.resolve.KotlinCacheService -import org.jetbrains.kotlin.idea.resolve.ResolutionFacade -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.resolve.diagnostics.KotlinSuppressCache - - -class CoreKotlinCacheService(private val resolutionFacade: DokkaResolutionFacade) : KotlinCacheService { - override fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade { - return resolutionFacade - } - - override fun getResolutionFacade( - elements: List<KtElement>, - platform: org.jetbrains.kotlin.platform.TargetPlatform - ): ResolutionFacade { - return resolutionFacade - } - - override fun getResolutionFacadeByFile( - file: PsiFile, - platform: org.jetbrains.kotlin.platform.TargetPlatform - ): ResolutionFacade? { - return resolutionFacade - } - - override fun getResolutionFacadeByModuleInfo( - moduleInfo: ModuleInfo, - platform: org.jetbrains.kotlin.platform.TargetPlatform - ): ResolutionFacade? { - return resolutionFacade - } - - override fun getSuppressionCache(): KotlinSuppressCache { - throw UnsupportedOperationException() - } - -} - diff --git a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt b/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt deleted file mode 100644 index ef45e840..00000000 --- a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt +++ /dev/null @@ -1,569 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.openapi.Disposable -import com.intellij.openapi.components.BaseComponent -import com.intellij.openapi.extensions.ExtensionPointName -import com.intellij.openapi.module.Module -import com.intellij.openapi.project.Project -import com.intellij.openapi.projectRoots.Sdk -import com.intellij.openapi.projectRoots.SdkAdditionalData -import com.intellij.openapi.projectRoots.SdkModificator -import com.intellij.openapi.projectRoots.SdkTypeId -import com.intellij.openapi.roots.* -import com.intellij.openapi.roots.impl.ProjectOrderEnumerator -import com.intellij.openapi.util.Condition -import com.intellij.openapi.util.Key -import com.intellij.openapi.util.UserDataHolderBase -import com.intellij.openapi.vfs.StandardFileSystems -import com.intellij.openapi.vfs.VfsUtilCore.getVirtualFileForJar -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.openapi.vfs.VirtualFileFilter -import com.intellij.psi.search.GlobalSearchScope -import com.intellij.util.messages.MessageBus -import org.jetbrains.jps.model.module.JpsModuleSourceRootType -import org.jetbrains.kotlin.cli.common.config.ContentRoot -import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot -import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot -import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot -import org.picocontainer.PicoContainer -import java.io.File - -/** - * Workaround for the lack of ability to create a ProjectFileIndex implementation using only - * classes from projectModel-{api,impl}. - */ -class CoreProjectFileIndex(private val project: Project, contentRoots: List<ContentRoot>) : ProjectFileIndex, ModuleFileIndex { - override fun iterateContent(p0: ContentIterator, p1: VirtualFileFilter?): Boolean { - throw UnsupportedOperationException() - } - - override fun iterateContentUnderDirectory(p0: VirtualFile, p1: ContentIterator, p2: VirtualFileFilter?): Boolean { - throw UnsupportedOperationException() - } - - override fun isInLibrary(p0: VirtualFile): Boolean { - throw UnsupportedOperationException() - } - - val sourceRoots = contentRoots.filter { it !is JvmClasspathRoot } - val classpathRoots = contentRoots.filterIsInstance<JvmClasspathRoot>() - - val module: Module = object : UserDataHolderBase(), Module { - override fun isDisposed(): Boolean { - throw UnsupportedOperationException() - } - - override fun getOptionValue(p0: String): String? { - throw UnsupportedOperationException() - } - - override fun clearOption(p0: String) { - throw UnsupportedOperationException() - } - - override fun getName(): String = "<Dokka module>" - - override fun getModuleWithLibrariesScope(): GlobalSearchScope { - throw UnsupportedOperationException() - } - - override fun getModuleWithDependentsScope(): GlobalSearchScope { - throw UnsupportedOperationException() - } - - override fun getModuleContentScope(): GlobalSearchScope { - throw UnsupportedOperationException() - } - - override fun isLoaded(): Boolean { - throw UnsupportedOperationException() - } - - override fun setOption(p0: String, p1: String?) { - throw UnsupportedOperationException() - } - - override fun getModuleWithDependenciesScope(): GlobalSearchScope { - throw UnsupportedOperationException() - } - - override fun getModuleWithDependenciesAndLibrariesScope(p0: Boolean): GlobalSearchScope { - throw UnsupportedOperationException() - } - - override fun getProject(): Project = this@CoreProjectFileIndex.project - - override fun getModuleContentWithDependenciesScope(): GlobalSearchScope { - throw UnsupportedOperationException() - } - - override fun getModuleFilePath(): String { - throw UnsupportedOperationException() - } - - override fun getModuleTestsWithDependentsScope(): GlobalSearchScope { - throw UnsupportedOperationException() - } - - override fun getModuleScope(): GlobalSearchScope { - throw UnsupportedOperationException() - } - - override fun getModuleScope(p0: Boolean): GlobalSearchScope { - throw UnsupportedOperationException() - } - - override fun getModuleRuntimeScope(p0: Boolean): GlobalSearchScope { - throw UnsupportedOperationException() - } - - override fun getModuleFile(): VirtualFile? { - throw UnsupportedOperationException() - } - - override fun <T : Any?> getExtensions(p0: ExtensionPointName<T>): Array<out T> { - throw UnsupportedOperationException() - } - - override fun getComponent(p0: String): BaseComponent? { - throw UnsupportedOperationException() - } - - override fun <T : Any?> getComponent(p0: Class<T>, p1: T): T { - throw UnsupportedOperationException() - } - - override fun <T : Any?> getComponent(interfaceClass: Class<T>): T? { - if (interfaceClass == ModuleRootManager::class.java) { - return moduleRootManager as T - } - throw UnsupportedOperationException() - } - - override fun getDisposed(): Condition<*> { - throw UnsupportedOperationException() - } - - override fun <T : Any?> getComponents(p0: Class<T>): Array<out T> { - throw UnsupportedOperationException() - } - - override fun getPicoContainer(): PicoContainer { - throw UnsupportedOperationException() - } - - override fun hasComponent(p0: Class<*>): Boolean { - throw UnsupportedOperationException() - } - - override fun getMessageBus(): MessageBus { - throw UnsupportedOperationException() - } - - override fun dispose() { - throw UnsupportedOperationException() - } - } - - private val sdk: Sdk = object : Sdk, RootProvider { - override fun getFiles(rootType: OrderRootType): Array<out VirtualFile> = classpathRoots - .mapNotNull { StandardFileSystems.local().findFileByPath(it.file.path) } - .toTypedArray() - - override fun addRootSetChangedListener(p0: RootProvider.RootSetChangedListener) { - throw UnsupportedOperationException() - } - - override fun addRootSetChangedListener(p0: RootProvider.RootSetChangedListener, p1: Disposable) { - throw UnsupportedOperationException() - } - - override fun getUrls(p0: OrderRootType): Array<out String> { - throw UnsupportedOperationException() - } - - override fun removeRootSetChangedListener(p0: RootProvider.RootSetChangedListener) { - throw UnsupportedOperationException() - } - - override fun getSdkModificator(): SdkModificator { - throw UnsupportedOperationException() - } - - override fun getName(): String = "<dokka SDK>" - - override fun getRootProvider(): RootProvider = this - - override fun getHomePath(): String? { - throw UnsupportedOperationException() - } - - override fun getVersionString(): String? { - throw UnsupportedOperationException() - } - - override fun getSdkAdditionalData(): SdkAdditionalData? { - throw UnsupportedOperationException() - } - - override fun clone(): Any { - throw UnsupportedOperationException() - } - - override fun getSdkType(): SdkTypeId { - throw UnsupportedOperationException() - } - - override fun getHomeDirectory(): VirtualFile? { - throw UnsupportedOperationException() - } - - override fun <T : Any?> getUserData(p0: Key<T>): T? { - throw UnsupportedOperationException() - } - - override fun <T : Any?> putUserData(p0: Key<T>, p1: T?) { - throw UnsupportedOperationException() - } - } - - private val moduleSourceOrderEntry = object : ModuleSourceOrderEntry { - override fun getFiles(p0: OrderRootType): Array<VirtualFile> { - throw UnsupportedOperationException() - } - - override fun getUrls(p0: OrderRootType): Array<String> { - throw UnsupportedOperationException() - } - - override fun <R : Any?> accept(p0: RootPolicy<R>, p1: R?): R { - throw UnsupportedOperationException() - } - - - override fun getPresentableName(): String { - throw UnsupportedOperationException() - } - - override fun getOwnerModule(): Module = module - - - override fun isValid(): Boolean { - throw UnsupportedOperationException() - } - - override fun compareTo(other: OrderEntry?): Int { - throw UnsupportedOperationException() - } - - override fun getRootModel(): ModuleRootModel = moduleRootManager - - override fun isSynthetic(): Boolean { - throw UnsupportedOperationException() - } - } - - private val sdkOrderEntry = object : JdkOrderEntry { - override fun getFiles(p0: OrderRootType): Array<VirtualFile> { - throw UnsupportedOperationException() - } - - override fun getUrls(p0: OrderRootType): Array<String> { - throw UnsupportedOperationException() - } - - override fun <R : Any?> accept(p0: RootPolicy<R>, p1: R?): R { - throw UnsupportedOperationException() - } - - override fun getJdkName(): String? { - throw UnsupportedOperationException() - } - - override fun getJdk(): Sdk = sdk - - override fun getPresentableName(): String { - throw UnsupportedOperationException() - } - - override fun getOwnerModule(): Module { - throw UnsupportedOperationException() - } - - override fun isValid(): Boolean { - throw UnsupportedOperationException() - } - - override fun getRootFiles(p0: OrderRootType): Array<out VirtualFile> { - throw UnsupportedOperationException() - } - - override fun getRootUrls(p0: OrderRootType): Array<out String> { - throw UnsupportedOperationException() - } - - override fun compareTo(other: OrderEntry?): Int { - throw UnsupportedOperationException() - } - - override fun isSynthetic(): Boolean { - throw UnsupportedOperationException() - } - - } - - inner class MyModuleRootManager : ModuleRootManager() { - override fun getExternalSource(): ProjectModelExternalSource? { - throw UnsupportedOperationException() - } - - override fun getExcludeRoots(): Array<out VirtualFile> { - throw UnsupportedOperationException() - } - - override fun getContentEntries(): Array<out ContentEntry> { - throw UnsupportedOperationException() - } - - override fun getExcludeRootUrls(): Array<out String> { - throw UnsupportedOperationException() - } - - override fun <R : Any?> processOrder(p0: RootPolicy<R>, p1: R): R { - throw UnsupportedOperationException() - } - - override fun getSourceRoots(p0: Boolean): Array<out VirtualFile> { - throw UnsupportedOperationException() - } - - override fun getSourceRoots(): Array<out VirtualFile> { - throw UnsupportedOperationException() - } - - override fun getSourceRoots(p0: JpsModuleSourceRootType<*>): MutableList<VirtualFile> { - throw UnsupportedOperationException() - } - - override fun getSourceRoots(p0: MutableSet<out JpsModuleSourceRootType<*>>): MutableList<VirtualFile> { - throw UnsupportedOperationException() - } - - override fun getContentRoots(): Array<out VirtualFile> { - throw UnsupportedOperationException() - } - - override fun orderEntries(): OrderEnumerator = - ProjectOrderEnumerator(project, null).using(object : RootModelProvider { - override fun getModules(): Array<out Module> = arrayOf(module) - - override fun getRootModel(p0: Module): ModuleRootModel = this@MyModuleRootManager - }) - - override fun <T : Any?> getModuleExtension(p0: Class<T>): T { - throw UnsupportedOperationException() - } - - override fun getDependencyModuleNames(): Array<out String> { - throw UnsupportedOperationException() - } - - override fun getModule(): Module = this@CoreProjectFileIndex.module - - override fun isSdkInherited(): Boolean { - throw UnsupportedOperationException() - } - - override fun getOrderEntries(): Array<out OrderEntry> = arrayOf(moduleSourceOrderEntry, sdkOrderEntry) - - override fun getSourceRootUrls(): Array<out String> { - throw UnsupportedOperationException() - } - - override fun getSourceRootUrls(p0: Boolean): Array<out String> { - throw UnsupportedOperationException() - } - - override fun getSdk(): Sdk? { - throw UnsupportedOperationException() - } - - override fun getContentRootUrls(): Array<out String> { - throw UnsupportedOperationException() - } - - override fun getModuleDependencies(): Array<out Module> { - throw UnsupportedOperationException() - } - - override fun getModuleDependencies(p0: Boolean): Array<out Module> { - throw UnsupportedOperationException() - } - - override fun getModifiableModel(): ModifiableRootModel { - throw UnsupportedOperationException() - } - - override fun isDependsOn(p0: Module): Boolean { - throw UnsupportedOperationException() - } - - override fun getFileIndex(): ModuleFileIndex { - return this@CoreProjectFileIndex - } - - override fun getDependencies(): Array<out Module> { - throw UnsupportedOperationException() - } - - override fun getDependencies(p0: Boolean): Array<out Module> { - throw UnsupportedOperationException() - } - } - - val moduleRootManager = MyModuleRootManager() - - override fun getContentRootForFile(p0: VirtualFile): VirtualFile? { - throw UnsupportedOperationException() - } - - override fun getContentRootForFile(p0: VirtualFile, p1: Boolean): VirtualFile? { - throw UnsupportedOperationException() - } - - override fun getPackageNameByDirectory(p0: VirtualFile): String? { - throw UnsupportedOperationException() - } - - override fun isInLibrarySource(file: VirtualFile): Boolean = false - - override fun getClassRootForFile(file: VirtualFile): VirtualFile? = - classpathRoots.firstOrNull { it.contains(file) }?.let { StandardFileSystems.local().findFileByPath(it.file.path) } - - override fun getOrderEntriesForFile(file: VirtualFile): List<OrderEntry> = - if (classpathRoots.contains(file)) listOf(sdkOrderEntry) else emptyList() - - override fun isInLibraryClasses(file: VirtualFile): Boolean = classpathRoots.contains(file) - - override fun isExcluded(p0: VirtualFile): Boolean { - throw UnsupportedOperationException() - } - - override fun getSourceRootForFile(p0: VirtualFile): VirtualFile? { - throw UnsupportedOperationException() - } - - override fun isUnderIgnored(p0: VirtualFile): Boolean { - throw UnsupportedOperationException() - } - - override fun isLibraryClassFile(p0: VirtualFile): Boolean { - throw UnsupportedOperationException() - } - - override fun getModuleForFile(file: VirtualFile): Module? = - if (sourceRoots.contains(file)) module else null - - private fun List<ContentRoot>.contains(file: VirtualFile): Boolean = any { it.contains(file) } - - override fun getModuleForFile(p0: VirtualFile, p1: Boolean): Module? { - throw UnsupportedOperationException() - } - - override fun isInSource(p0: VirtualFile): Boolean { - throw UnsupportedOperationException() - } - - override fun isIgnored(p0: VirtualFile): Boolean { - throw UnsupportedOperationException() - } - - override fun isContentSourceFile(p0: VirtualFile): Boolean { - throw UnsupportedOperationException() - } - - override fun isInSourceContent(file: VirtualFile): Boolean = sourceRoots.contains(file) - - override fun iterateContent(p0: ContentIterator): Boolean { - throw UnsupportedOperationException() - } - - override fun isInContent(p0: VirtualFile): Boolean { - throw UnsupportedOperationException() - } - - override fun iterateContentUnderDirectory(p0: VirtualFile, p1: ContentIterator): Boolean { - throw UnsupportedOperationException() - } - - override fun isInTestSourceContent(file: VirtualFile): Boolean = false - - override fun isUnderSourceRootOfType(p0: VirtualFile, p1: MutableSet<out JpsModuleSourceRootType<*>>): Boolean { - throw UnsupportedOperationException() - } - - override fun getOrderEntryForFile(p0: VirtualFile): OrderEntry? { - throw UnsupportedOperationException() - } -} - -class CoreProjectRootManager(val projectFileIndex: CoreProjectFileIndex) : ProjectRootManager() { - override fun orderEntries(): OrderEnumerator { - throw UnsupportedOperationException() - } - - override fun orderEntries(p0: MutableCollection<out Module>): OrderEnumerator { - throw UnsupportedOperationException() - } - - override fun getContentRootsFromAllModules(): Array<out VirtualFile> { - throw UnsupportedOperationException() - } - - override fun setProjectSdk(p0: Sdk?) { - throw UnsupportedOperationException() - } - - override fun setProjectSdkName(p0: String) { - throw UnsupportedOperationException() - } - - override fun getModuleSourceRoots(p0: MutableSet<out JpsModuleSourceRootType<*>>): MutableList<VirtualFile> { - throw UnsupportedOperationException() - } - - override fun getContentSourceRoots(): Array<out VirtualFile> { - throw UnsupportedOperationException() - } - - override fun getFileIndex(): ProjectFileIndex = projectFileIndex - - override fun getProjectSdkName(): String? { - throw UnsupportedOperationException() - } - - override fun getProjectSdk(): Sdk? { - throw UnsupportedOperationException() - } - - override fun getContentRoots(): Array<out VirtualFile> { - throw UnsupportedOperationException() - } - - override fun getContentRootUrls(): MutableList<String> { - throw UnsupportedOperationException() - } - -} - -fun ContentRoot.contains(file: VirtualFile) = when (this) { - is JvmContentRoot -> { - val path = if (file.fileSystem.protocol == StandardFileSystems.JAR_PROTOCOL) - getVirtualFileForJar(file)?.path ?: file.path - else - file.path - File(path).startsWith(this.file.absoluteFile) - } - is KotlinSourceRoot -> File(file.path).startsWith(File(this.path).absoluteFile) - else -> false -} diff --git a/core/src/main/kotlin/Analysis/JavaResolveExtension.kt b/core/src/main/kotlin/Analysis/JavaResolveExtension.kt deleted file mode 100644 index 4a4c78e5..00000000 --- a/core/src/main/kotlin/Analysis/JavaResolveExtension.kt +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2010-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:JvmName("JavaResolutionUtils") - -package org.jetbrains.dokka - -import com.intellij.psi.* -import org.jetbrains.kotlin.asJava.unwrapped -import org.jetbrains.kotlin.caches.resolve.KotlinCacheService -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.idea.resolve.ResolutionFacade -import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.load.java.sources.JavaSourceElement -import org.jetbrains.kotlin.load.java.structure.* -import org.jetbrains.kotlin.load.java.structure.impl.* -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.platform.jvm.JvmPlatforms -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.MemberScope - -// TODO: Remove that file - -@JvmOverloads -fun PsiMethod.getJavaMethodDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? { - val method = originalElement as? PsiMethod ?: return null - if (method.containingClass == null || !Name.isValidIdentifier(method.name)) return null - val resolver = method.getJavaDescriptorResolver(resolutionFacade) - return when { - method.isConstructor -> resolver?.resolveConstructor(JavaConstructorImpl(method)) - else -> resolver?.resolveMethod(JavaMethodImpl(method)) - } -} - -@JvmOverloads -fun PsiClass.getJavaClassDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): ClassDescriptor? { - val psiClass = originalElement as? PsiClass ?: return null - return psiClass.getJavaDescriptorResolver(resolutionFacade)?.resolveClass(JavaClassImpl(psiClass)) -} - -@JvmOverloads -fun PsiField.getJavaFieldDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): PropertyDescriptor? { - val field = originalElement as? PsiField ?: return null - return field.getJavaDescriptorResolver(resolutionFacade)?.resolveField(JavaFieldImpl(field)) -} - -@JvmOverloads -fun PsiMember.getJavaMemberDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? { - return when (this) { - is PsiEnumConstant -> containingClass?.getJavaClassDescriptor(resolutionFacade) - is PsiClass -> getJavaClassDescriptor(resolutionFacade) - is PsiMethod -> getJavaMethodDescriptor(resolutionFacade) - is PsiField -> getJavaFieldDescriptor(resolutionFacade) - else -> null - } -} - -@JvmOverloads -fun PsiMember.getJavaOrKotlinMemberDescriptor(resolutionFacade: ResolutionFacade = javaResolutionFacade()): DeclarationDescriptor? { - val callable = unwrapped - return when (callable) { - is PsiMember -> getJavaMemberDescriptor(resolutionFacade) - is KtDeclaration -> { - val descriptor = resolutionFacade.resolveToDescriptor(callable) - if (descriptor is ClassDescriptor && this is PsiMethod) descriptor.unsubstitutedPrimaryConstructor else descriptor - } - else -> null - } -} - -private fun PsiElement.getJavaDescriptorResolver(resolutionFacade: ResolutionFacade): JavaDescriptorResolver? { - return resolutionFacade.tryGetFrontendService(this, JavaDescriptorResolver::class.java) -} - -private fun JavaDescriptorResolver.resolveMethod(method: JavaMethod): DeclarationDescriptor? { - return getContainingScope(method) - ?.getContributedDescriptors(nameFilter = { true }, kindFilter = DescriptorKindFilter.CALLABLES) - ?.filterIsInstance<DeclarationDescriptorWithSource>() - ?.findByJavaElement(method) -} - -private fun JavaDescriptorResolver.resolveConstructor(constructor: JavaConstructor): ConstructorDescriptor? { - return resolveClass(constructor.containingClass)?.constructors?.findByJavaElement(constructor) -} - -private fun JavaDescriptorResolver.resolveField(field: JavaField): PropertyDescriptor? { - return getContainingScope(field)?.getContributedVariables(field.name, NoLookupLocation.FROM_IDE)?.findByJavaElement(field) -} - -private fun JavaDescriptorResolver.getContainingScope(member: JavaMember): MemberScope? { - val containingClass = resolveClass(member.containingClass) - return if (member.isStatic) - containingClass?.staticScope - else - containingClass?.defaultType?.memberScope -} - -private fun <T : DeclarationDescriptorWithSource> Collection<T>.findByJavaElement(javaElement: JavaElement): T? { - return firstOrNull { member -> - val memberJavaElement = (member.original.source as? JavaSourceElement)?.javaElement - when { - memberJavaElement == javaElement -> - true - memberJavaElement is JavaElementImpl<*> && javaElement is JavaElementImpl<*> -> - memberJavaElement.psi.isEquivalentTo(javaElement.psi) - else -> - false - } - } -} - -fun PsiElement.javaResolutionFacade() = - KotlinCacheService.getInstance(project).getResolutionFacadeByFile(this.originalElement.containingFile, JvmPlatforms.defaultJvmPlatform)!! diff --git a/core/src/main/kotlin/CoreExtensions.kt b/core/src/main/kotlin/CoreExtensions.kt new file mode 100644 index 00000000..b8689154 --- /dev/null +++ b/core/src/main/kotlin/CoreExtensions.kt @@ -0,0 +1,29 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.plugability.ExtensionPoint +import org.jetbrains.dokka.renderers.Renderer +import org.jetbrains.dokka.transformers.documentation.DocumentableMerger +import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator +import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer +import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer +import org.jetbrains.dokka.transformers.pages.PageCreator +import org.jetbrains.dokka.transformers.pages.PageTransformer +import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator +import kotlin.reflect.KProperty + +object CoreExtensions { + val sourceToDocumentableTranslator by coreExtension<SourceToDocumentableTranslator>() + val preMergeDocumentableTransformer by coreExtension<PreMergeDocumentableTransformer>() + val documentableMerger by coreExtension<DocumentableMerger>() + val documentableTransformer by coreExtension<DocumentableTransformer>() + val documentableToPageTranslator by coreExtension<DocumentableToPageTranslator>() + val allModulePageCreator by coreExtension<PageCreator>() + val pageTransformer by coreExtension<PageTransformer>() + val allModulePageTransformer by coreExtension<PageTransformer>() + val renderer by coreExtension<Renderer>() + + private fun <T : Any> coreExtension() = object { + operator fun provideDelegate(thisRef: CoreExtensions, property: KProperty<*>): Lazy<ExtensionPoint<T>> = + lazy { ExtensionPoint<T>(thisRef::class.qualifiedName!!, property.name) } + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/DokkaBootstrap.kt b/core/src/main/kotlin/DokkaBootstrap.kt new file mode 100644 index 00000000..159172a5 --- /dev/null +++ b/core/src/main/kotlin/DokkaBootstrap.kt @@ -0,0 +1,11 @@ +package org.jetbrains.dokka + +import java.util.function.BiConsumer +import kotlin.jvm.Throws + +interface DokkaBootstrap { + fun configure(serializedConfigurationJSON: String, logger: BiConsumer<String, String>) + + @Throws(Throwable::class) + fun generate() +} diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt index b48b62d4..fabbc889 100644 --- a/core/src/main/kotlin/DokkaBootstrapImpl.kt +++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt @@ -1,49 +1,78 @@ package org.jetbrains.dokka -import com.google.gson.Gson import org.jetbrains.dokka.DokkaConfiguration.PackageOptions +import org.jetbrains.dokka.utilities.DokkaLogger import java.util.function.BiConsumer -fun parsePerPackageOptions(arg: String): List<PackageOptions> { - if (arg.isBlank()) return emptyList() +fun parsePerPackageOptions(args: List<String>): List<PackageOptions> = args.map { it.split(",") }.map { + val prefix = it.first() + if (prefix == "") + throw IllegalArgumentException( + "Please do not register packageOptions with all match pattern, use global settings instead" + ) - return arg.split(";").map { it.split(",") }.map { - val prefix = it.first() - if (prefix == "") - throw IllegalArgumentException("Please do not register packageOptions with all match pattern, use global settings instead") - val args = it.subList(1, it.size) - val deprecated = args.find { it.endsWith("deprecated") }?.startsWith("+") ?: true - val reportUndocumented = args.find { it.endsWith("warnUndocumented") }?.startsWith("+") ?: true - val privateApi = args.find { it.endsWith("privateApi") }?.startsWith("+") ?: false - val suppress = args.find { it.endsWith("suppress") }?.startsWith("+") ?: false - PackageOptionsImpl(prefix, includeNonPublic = privateApi, reportUndocumented = reportUndocumented, skipDeprecated = !deprecated, suppress = suppress) - } + val args = it.subList(1, it.size) + + val deprecated = args.find { it.endsWith("skipDeprecated") }?.startsWith("+") + ?: DokkaDefaults.skipDeprecated + + val reportUndocumented = args.find { it.endsWith("reportUndocumented") }?.startsWith("+") + ?: DokkaDefaults.reportUndocumented + + val privateApi = args.find { it.endsWith("includeNonPublic") }?.startsWith("+") + ?: DokkaDefaults.includeNonPublic + + val suppress = args.find { it.endsWith("suppress") }?.startsWith("+") + ?: DokkaDefaults.suppress + + PackageOptionsImpl( + prefix, + includeNonPublic = privateApi, + reportUndocumented = reportUndocumented, + skipDeprecated = !deprecated, + suppress = suppress + ) } + +/** + * Accessed with reflection + */ +@Suppress("unused") class DokkaBootstrapImpl : DokkaBootstrap { - private class DokkaProxyLogger(val consumer: BiConsumer<String, String>) : DokkaLogger { + class DokkaProxyLogger(val consumer: BiConsumer<String, String>) : DokkaLogger { + override var warningsCount: Int = 0 + override var errorsCount: Int = 0 + + override fun debug(message: String) { + consumer.accept("debug", message) + } + override fun info(message: String) { consumer.accept("info", message) } + override fun progress(message: String) { + consumer.accept("progress", message) + } + override fun warn(message: String) { - consumer.accept("warn", message) + consumer.accept("warn", message).also { warningsCount++ } } override fun error(message: String) { - consumer.accept("error", message) + consumer.accept("error", message).also { errorsCount++ } } } - lateinit var generator: DokkaGenerator - val gson = Gson() + private lateinit var generator: DokkaGenerator fun configure(logger: DokkaLogger, configuration: DokkaConfigurationImpl) = with(configuration) { - fun defaultLinks(config: PassConfigurationImpl): List<ExternalDocumentationLinkImpl> { + fun defaultLinks(config: DokkaSourceSetImpl): List<ExternalDocumentationLinkImpl> { val links = mutableListOf<ExternalDocumentationLinkImpl>() if (!config.noJdkLink) links += DokkaConfiguration.ExternalDocumentationLink @@ -57,20 +86,21 @@ class DokkaBootstrapImpl : DokkaBootstrap { return links } - val configurationWithLinks = - configuration.copy(passesConfigurations = - passesConfigurations - .map { - val links: List<ExternalDocumentationLinkImpl> = it.externalDocumentationLinks + defaultLinks(it) - it.copy(externalDocumentationLinks = links) - } - ) + val configurationWithLinks = configuration.copy( + sourceSets = sourceSets.map { + val links: List<ExternalDocumentationLinkImpl> = + it.externalDocumentationLinks + defaultLinks(it) + it.copy(externalDocumentationLinks = links) + } + ) generator = DokkaGenerator(configurationWithLinks, logger) } - override fun configure(logger: BiConsumer<String, String>, serializedConfigurationJSON: String) - = configure(DokkaProxyLogger(logger), gson.fromJson(serializedConfigurationJSON, DokkaConfigurationImpl::class.java)) + override fun configure(serializedConfigurationJSON: String, logger: BiConsumer<String, String>) = configure( + DokkaProxyLogger(logger), + DokkaConfigurationImpl(serializedConfigurationJSON) + ) override fun generate() = generator.generate() } diff --git a/core/src/main/kotlin/DokkaException.kt b/core/src/main/kotlin/DokkaException.kt new file mode 100644 index 00000000..0010249c --- /dev/null +++ b/core/src/main/kotlin/DokkaException.kt @@ -0,0 +1,3 @@ +package org.jetbrains.dokka + +class DokkaException(message: String) : RuntimeException(message) diff --git a/core/src/main/kotlin/DokkaGenerator.kt b/core/src/main/kotlin/DokkaGenerator.kt new file mode 100644 index 00000000..4262d890 --- /dev/null +++ b/core/src/main/kotlin/DokkaGenerator.kt @@ -0,0 +1,164 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.DokkaConfiguration.* +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.dokka.utilities.report + + +/** + * DokkaGenerator is the main entry point for generating documentation + * + * [generate] method has been split into submethods for test reasons + */ +class DokkaGenerator( + private val configuration: DokkaConfiguration, + private val logger: DokkaLogger +) { + fun generate() = timed(logger) { + report("Initializing plugins") + val context = initializePlugins(configuration, logger) + + report("Creating documentation models") + val modulesFromPlatforms = createDocumentationModels(context) + + report("Transforming documentation model before merging") + val transformedDocumentationBeforeMerge = transformDocumentationModelBeforeMerge(modulesFromPlatforms, context) + + report("Merging documentation models") + val documentationModel = mergeDocumentationModels(transformedDocumentationBeforeMerge, context) + + report("Transforming documentation model after merging") + val transformedDocumentation = transformDocumentationModelAfterMerge(documentationModel, context) + + report("Creating pages") + val pages = createPages(transformedDocumentation, context) + + report("Transforming pages") + val transformedPages = transformPages(pages, context) + + report("Rendering") + render(transformedPages, context) + + reportAfterRendering(context) + }.dump("\n\n === TIME MEASUREMENT ===\n") + + fun generateAllModulesPage() = timed { + report("Initializing plugins") + val context = initializePlugins(configuration, logger) + + report("Creating all modules page") + val pages = createAllModulePage(context) + + report("Transforming pages") + val transformedPages = transformAllModulesPage(pages, context) + + report("Rendering") + render(transformedPages, context) + }.dump("\n\n === TIME MEASUREMENT ===\n") + + + fun initializePlugins( + configuration: DokkaConfiguration, + logger: DokkaLogger, + additionalPlugins: List<DokkaPlugin> = emptyList() + ) = DokkaContext.create(configuration, logger, additionalPlugins) + + fun createDocumentationModels( + context: DokkaContext + ) = context.configuration.sourceSets + .flatMap { sourceSet -> translateSources(sourceSet, context) } + + fun transformDocumentationModelBeforeMerge( + modulesFromPlatforms: List<DModule>, + context: DokkaContext + ) = context[CoreExtensions.preMergeDocumentableTransformer].fold(modulesFromPlatforms) { acc, t -> t(acc) } + + fun mergeDocumentationModels( + modulesFromPlatforms: List<DModule>, + context: DokkaContext + ) = context.single(CoreExtensions.documentableMerger).invoke(modulesFromPlatforms, context) + + fun transformDocumentationModelAfterMerge( + documentationModel: DModule, + context: DokkaContext + ) = context[CoreExtensions.documentableTransformer].fold(documentationModel) { acc, t -> t(acc, context) } + + fun createPages( + transformedDocumentation: DModule, + context: DokkaContext + ) = context.single(CoreExtensions.documentableToPageTranslator).invoke(transformedDocumentation) + + fun createAllModulePage( + context: DokkaContext + ) = context.single(CoreExtensions.allModulePageCreator).invoke() + + fun transformPages( + pages: RootPageNode, + context: DokkaContext + ) = context[CoreExtensions.pageTransformer].fold(pages) { acc, t -> t(acc) } + + fun transformAllModulesPage( + pages: RootPageNode, + context: DokkaContext + ) = context[CoreExtensions.allModulePageTransformer].fold(pages) { acc, t -> t(acc) } + + fun render( + transformedPages: RootPageNode, + context: DokkaContext + ) { + val renderer = context.single(CoreExtensions.renderer) + renderer.render(transformedPages) + } + + fun reportAfterRendering(context: DokkaContext) { + context.unusedPoints.takeIf { it.isNotEmpty() }?.also { + logger.info("Unused extension points found: ${it.joinToString(", ")}") + } + + logger.report() + + if (context.configuration.failOnWarning && (logger.warningsCount > 0 || logger.errorsCount > 0)) { + throw DokkaException( + "Failed with warningCount=${logger.warningsCount} and errorCount=${logger.errorsCount}" + ) + } + } + + private fun translateSources(sourceSet: DokkaSourceSet, context: DokkaContext) = + context[CoreExtensions.sourceToDocumentableTranslator].map { + it.invoke(sourceSet, context) + } +} + +private class Timer(startTime: Long, private val logger: DokkaLogger?) { + private val steps = mutableListOf("" to startTime) + + fun report(name: String) { + logger?.progress(name) + steps += (name to System.currentTimeMillis()) + } + + fun dump(prefix: String = "") { + logger?.info(prefix) + val namePad = steps.map { it.first.length }.max() ?: 0 + val timePad = steps.windowed(2).map { (p1, p2) -> p2.second - p1.second }.max()?.toString()?.length ?: 0 + steps.windowed(2).forEach { (p1, p2) -> + if (p1.first.isNotBlank()) { + logger?.info("${p1.first.padStart(namePad)}: ${(p2.second - p1.second).toString().padStart(timePad)}") + } + } + } +} + +private fun timed(logger: DokkaLogger? = null, block: Timer.() -> Unit): Timer = + Timer(System.currentTimeMillis(), logger).apply { + try { + block() + } finally { + report("") + } + } diff --git a/core/src/main/kotlin/DokkaMultimoduleBootstrapImpl.kt b/core/src/main/kotlin/DokkaMultimoduleBootstrapImpl.kt new file mode 100644 index 00000000..c0726584 --- /dev/null +++ b/core/src/main/kotlin/DokkaMultimoduleBootstrapImpl.kt @@ -0,0 +1,29 @@ +/** + * Accessed with reflection + */ +@file:Suppress("unused") + +package org.jetbrains.dokka + +import org.jetbrains.dokka.DokkaBootstrapImpl.DokkaProxyLogger +import org.jetbrains.dokka.utilities.DokkaLogger +import java.util.function.BiConsumer + +class DokkaMultimoduleBootstrapImpl : DokkaBootstrap { + + private lateinit var generator: DokkaGenerator + + fun configure(logger: DokkaLogger, configuration: DokkaConfiguration) { + generator = DokkaGenerator(configuration, logger) + } + + override fun configure(serializedConfigurationJSON: String, logger: BiConsumer<String, String>) = configure( + DokkaProxyLogger(logger), + DokkaConfigurationImpl(serializedConfigurationJSON) + ) + + override fun generate() { + generator.generateAllModulesPage() + } + +} diff --git a/core/src/main/kotlin/DokkaVersion.kt b/core/src/main/kotlin/DokkaVersion.kt new file mode 100644 index 00000000..410058f3 --- /dev/null +++ b/core/src/main/kotlin/DokkaVersion.kt @@ -0,0 +1,10 @@ +package org.jetbrains.dokka + +import java.util.* + +object DokkaVersion { + val version: String by lazy { + val stream = javaClass.getResourceAsStream("/META-INF/dokka/dokka-version.properties") + Properties().apply { load(stream) }.getProperty("dokka-version") + } +} diff --git a/core/src/main/kotlin/Formats/AnalysisComponents.kt b/core/src/main/kotlin/Formats/AnalysisComponents.kt deleted file mode 100644 index d78d4a0c..00000000 --- a/core/src/main/kotlin/Formats/AnalysisComponents.kt +++ /dev/null @@ -1,45 +0,0 @@ -package org.jetbrains.dokka.Formats - -import com.google.inject.Binder -import org.jetbrains.dokka.* -import org.jetbrains.dokka.KotlinAsJavaElementSignatureProvider -import org.jetbrains.dokka.KotlinElementSignatureProvider -import org.jetbrains.dokka.ElementSignatureProvider -import org.jetbrains.dokka.Samples.DefaultSampleProcessingService -import org.jetbrains.dokka.Samples.SampleProcessingService -import org.jetbrains.dokka.Utilities.bind -import org.jetbrains.dokka.Utilities.toType -import kotlin.reflect.KClass - - -interface DefaultAnalysisComponentServices { - val packageDocumentationBuilderClass: KClass<out PackageDocumentationBuilder> - val javaDocumentationBuilderClass: KClass<out JavaDocumentationBuilder> - val sampleProcessingService: KClass<out SampleProcessingService> - val elementSignatureProvider: KClass<out ElementSignatureProvider> -} - -interface DefaultAnalysisComponent : FormatDescriptorAnalysisComponent, DefaultAnalysisComponentServices { - override fun configureAnalysis(binder: Binder): Unit = with(binder) { - bind<ElementSignatureProvider>() toType elementSignatureProvider - bind<PackageDocumentationBuilder>() toType packageDocumentationBuilderClass - bind<JavaDocumentationBuilder>() toType javaDocumentationBuilderClass - bind<SampleProcessingService>() toType sampleProcessingService - } -} - - -object KotlinAsJava : DefaultAnalysisComponentServices { - override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class - override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class - override val sampleProcessingService = DefaultSampleProcessingService::class - override val elementSignatureProvider = KotlinAsJavaElementSignatureProvider::class -} - - -object KotlinAsKotlin : DefaultAnalysisComponentServices { - override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class - override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class - override val sampleProcessingService = DefaultSampleProcessingService::class - override val elementSignatureProvider = KotlinElementSignatureProvider::class -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/FormatDescriptor.kt b/core/src/main/kotlin/Formats/FormatDescriptor.kt deleted file mode 100644 index 4bac8aa0..00000000 --- a/core/src/main/kotlin/Formats/FormatDescriptor.kt +++ /dev/null @@ -1,43 +0,0 @@ -package org.jetbrains.dokka.Formats - -import com.google.inject.Binder -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Utilities.bind -import org.jetbrains.dokka.Utilities.lazyBind -import org.jetbrains.dokka.Utilities.toOptional -import org.jetbrains.dokka.Utilities.toType -import kotlin.reflect.KClass - - -interface FormatDescriptorAnalysisComponent { - fun configureAnalysis(binder: Binder) -} - -interface FormatDescriptorOutputComponent { - fun configureOutput(binder: Binder) -} - -interface FormatDescriptor : FormatDescriptorAnalysisComponent, FormatDescriptorOutputComponent - - -abstract class FileGeneratorBasedFormatDescriptor : FormatDescriptor { - - override fun configureOutput(binder: Binder): Unit = with(binder) { - bind<Generator>() toType NodeLocationAwareGenerator::class - bind<NodeLocationAwareGenerator>() toType generatorServiceClass - bind(generatorServiceClass.java) // https://github.com/google/guice/issues/847 - - bind<LanguageService>() toType languageServiceClass - - lazyBind<OutlineFormatService>() toOptional (outlineServiceClass) - lazyBind<FormatService>() toOptional formatServiceClass - lazyBind<PackageListService>() toOptional packageListServiceClass - } - - abstract val formatServiceClass: KClass<out FormatService>? - abstract val outlineServiceClass: KClass<out OutlineFormatService>? - abstract val generatorServiceClass: KClass<out FileGenerator> - abstract val packageListServiceClass: KClass<out PackageListService>? - - open val languageServiceClass: KClass<out LanguageService> = KotlinLanguageService::class -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/FormatService.kt b/core/src/main/kotlin/Formats/FormatService.kt deleted file mode 100644 index 8f4855e3..00000000 --- a/core/src/main/kotlin/Formats/FormatService.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.jetbrains.dokka - -/** - * Abstract representation of a formatting service used to output documentation in desired format - * - * Bundled Formatters: - * * [HtmlFormatService] – outputs documentation to HTML format - * * [MarkdownFormatService] – outputs documentation in Markdown format - */ -interface FormatService { - /** Returns extension for output files */ - val extension: String - - /** extension which will be used for internal and external linking */ - val linkExtension: String - get() = extension - - fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder - - fun enumerateSupportFiles(callback: (resource: String, targetPath: String) -> Unit) { - } -} - -interface FormattedOutputBuilder { - /** Appends formatted content */ - fun appendNodes(nodes: Iterable<DocumentationNode>) -} - -/** Format content to [String] using specified [location] */ -fun FormatService.format(location: Location, nodes: Iterable<DocumentationNode>): String = StringBuilder().apply { - createOutputBuilder(this, location).appendNodes(nodes) -}.toString() diff --git a/core/src/main/kotlin/Formats/GFMFormatService.kt b/core/src/main/kotlin/Formats/GFMFormatService.kt deleted file mode 100644 index 036ec856..00000000 --- a/core/src/main/kotlin/Formats/GFMFormatService.kt +++ /dev/null @@ -1,61 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName - -open class GFMOutputBuilder( - to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String> -) : MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { - override fun appendTable(vararg columns: String, body: () -> Unit) { - to.appendln(columns.joinToString(" | ", "| ", " |")) - to.appendln("|" + "---|".repeat(columns.size)) - body() - } - - override fun appendUnorderedList(body: () -> Unit) { - if (inTableCell) { - wrapInTag("ul", body) - } else { - super.appendUnorderedList(body) - } - } - - override fun appendOrderedList(body: () -> Unit) { - if (inTableCell) { - wrapInTag("ol", body) - } else { - super.appendOrderedList(body) - } - } - - override fun appendListItem(body: () -> Unit) { - if (inTableCell) { - wrapInTag("li", body) - } else { - super.appendListItem(body) - } - } -} - -open class GFMFormatService( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - linkExtension: String, - impliedPlatforms: List<String> -) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) { - - @Inject constructor( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String> - ) : this(generator, signatureGenerator, "md", impliedPlatforms) - - override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - GFMOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) -} diff --git a/core/src/main/kotlin/Formats/HtmlFormatService.kt b/core/src/main/kotlin/Formats/HtmlFormatService.kt deleted file mode 100644 index d36ea0a2..00000000 --- a/core/src/main/kotlin/Formats/HtmlFormatService.kt +++ /dev/null @@ -1,166 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName -import java.io.File - -open class HtmlOutputBuilder(to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String>, - val templateService: HtmlTemplateService) - : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) -{ - override fun appendText(text: String) { - to.append(text.htmlEscape()) - } - - override fun appendSymbol(text: String) { - to.append("<span class=\"symbol\">${text.htmlEscape()}</span>") - } - - override fun appendKeyword(text: String) { - to.append("<span class=\"keyword\">${text.htmlEscape()}</span>") - } - - override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { - val id = signature?.let { " id=\"$it\"" }.orEmpty() - to.append("<span class=\"identifier\"$id>${text.htmlEscape()}</span>") - } - - override fun appendBlockCode(language: String, body: () -> Unit) { - val openTags = if (language.isNotBlank()) - "<pre><code class=\"lang-$language\">" - else - "<pre><code>" - wrap(openTags, "</code></pre>", body) - } - - override fun appendHeader(level: Int, body: () -> Unit) = - wrapInTag("h$level", body, newlineBeforeOpen = true, newlineAfterClose = true) - override fun appendParagraph(body: () -> Unit) = - wrapInTag("p", body, newlineBeforeOpen = true, newlineAfterClose = true) - - override fun appendSoftParagraph(body: () -> Unit) = appendParagraph(body) - - override fun appendLine() { - to.appendln("<br/>") - } - - override fun appendAnchor(anchor: String) { - to.appendln("<a name=\"${anchor.htmlEscape()}\"></a>") - } - - override fun appendTable(vararg columns: String, body: () -> Unit) = - wrapInTag("table", body, newlineAfterOpen = true, newlineAfterClose = true) - override fun appendTableBody(body: () -> Unit) = - wrapInTag("tbody", body, newlineAfterOpen = true, newlineAfterClose = true) - override fun appendTableRow(body: () -> Unit) = - wrapInTag("tr", body, newlineAfterOpen = true, newlineAfterClose = true) - override fun appendTableCell(body: () -> Unit) = - wrapInTag("td", body, newlineAfterOpen = true, newlineAfterClose = true) - - override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body) - - override fun appendStrong(body: () -> Unit) = wrapInTag("strong", body) - override fun appendEmphasis(body: () -> Unit) = wrapInTag("em", body) - override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body) - override fun appendCode(body: () -> Unit) = wrapInTag("code", body) - - override fun appendUnorderedList(body: () -> Unit) = wrapInTag("ul", body, newlineAfterClose = true) - override fun appendOrderedList(body: () -> Unit) = wrapInTag("ol", body, newlineAfterClose = true) - override fun appendListItem(body: () -> Unit) = wrapInTag("li", body, newlineAfterClose = true) - - override fun appendBreadcrumbSeparator() { - to.append(" / ") - } - - override fun appendNodes(nodes: Iterable<DocumentationNode>) { - templateService.appendHeader(to, getPageTitle(nodes), generator.relativePathToRoot(location)) - super.appendNodes(nodes) - templateService.appendFooter(to) - } - - override fun appendNonBreakingSpace() { - to.append(" ") - } - - override fun ensureParagraph() {} -} - -open class HtmlFormatService @Inject constructor(generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - val templateService: HtmlTemplateService, - @Named(impliedPlatformsName) val impliedPlatforms: List<String>) -: StructuredFormatService(generator, signatureGenerator, "html"), OutlineFormatService { - - override fun enumerateSupportFiles(callback: (String, String) -> Unit) { - callback("/dokka/styles/style.css", "style.css") - } - - override fun createOutputBuilder(to: StringBuilder, location: Location) = - HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) - - override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) { - templateService.appendHeader(to, "Module Contents", generator.relativePathToRoot(location)) - super.appendOutline(location, to, nodes) - templateService.appendFooter(to) - } - - override fun getOutlineFileName(location: Location): File { - return File("${location.path}-outline.html") - } - - override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) { - val link = ContentNodeDirectLink(node) - link.append(languageService.render(node, LanguageService.RenderMode.FULL)) - val tempBuilder = StringBuilder() - createOutputBuilder(tempBuilder, location).appendContent(link) - to.appendln("<a href=\"${location.path}\">$tempBuilder</a><br/>") - } - - override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { - to.appendln("<ul>") - body() - to.appendln("</ul>") - } -} - -fun getPageTitle(nodes: Iterable<DocumentationNode>): String? { - val breakdownByLocation = nodes.groupBy { node -> formatPageTitle(node) } - return breakdownByLocation.keys.singleOrNull() -} - -fun formatPageTitle(node: DocumentationNode): String { - val path = node.path - val moduleName = path.first().name - if (path.size == 1) { - return moduleName - } - - val qName = qualifiedNameForPageTitle(node) - return "$qName - $moduleName" -} - -private fun qualifiedNameForPageTitle(node: DocumentationNode): String { - if (node.kind == NodeKind.Package) { - var packageName = node.qualifiedName() - if (packageName.isEmpty()) { - packageName = "root package" - } - return packageName - } - - val path = node.path - var pathFromToplevelMember = path.dropWhile { it.kind !in NodeKind.classLike } - if (pathFromToplevelMember.isEmpty()) { - pathFromToplevelMember = path.dropWhile { it.kind != NodeKind.Property && it.kind != NodeKind.Function } - } - if (pathFromToplevelMember.isNotEmpty()) { - return pathFromToplevelMember.map { it.name }.filter { it.length > 0 }.joinToString(".") - } - return node.qualifiedName() -} diff --git a/core/src/main/kotlin/Formats/HtmlTemplateService.kt b/core/src/main/kotlin/Formats/HtmlTemplateService.kt deleted file mode 100644 index a65a7b18..00000000 --- a/core/src/main/kotlin/Formats/HtmlTemplateService.kt +++ /dev/null @@ -1,38 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -interface HtmlTemplateService { - fun appendHeader(to: StringBuilder, title: String?, basePath: File) - fun appendFooter(to: StringBuilder) - - companion object { - fun default(css: String? = null): HtmlTemplateService { - return object : HtmlTemplateService { - override fun appendFooter(to: StringBuilder) { - if (!to.endsWith('\n')) { - to.append('\n') - } - to.appendln("</BODY>") - to.appendln("</HTML>") - } - override fun appendHeader(to: StringBuilder, title: String?, basePath: File) { - to.appendln("<HTML>") - to.appendln("<HEAD>") - to.appendln("<meta charset=\"UTF-8\">") - if (title != null) { - to.appendln("<title>$title</title>") - } - if (css != null) { - val cssPath = basePath.resolve(css).toUnixString() - to.appendln("<link rel=\"stylesheet\" href=\"$cssPath\">") - } - to.appendln("</HEAD>") - to.appendln("<BODY>") - } - } - } - } -} - - diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt deleted file mode 100644 index 09bb2602..00000000 --- a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt +++ /dev/null @@ -1,117 +0,0 @@ -package org.jetbrains.dokka.Formats - -import org.jetbrains.dokka.* -import org.jetbrains.dokka.ExternalDocumentationLinkResolver.Companion.DOKKA_PARAM_PREFIX -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe -import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject -import org.jetbrains.kotlin.types.KotlinType - -class JavaLayoutHtmlPackageListService: PackageListService { - - private fun StringBuilder.appendParam(name: String, value: String) { - append(DOKKA_PARAM_PREFIX) - append(name) - append(":") - appendln(value) - } - - override fun formatPackageList(module: DocumentationModule): String { - val packages = module.members(NodeKind.Package).map { it.name } - - return buildString { - appendParam("format", "java-layout-html") - appendParam("mode", "kotlin") - for (p in packages) { - appendln(p) - } - } - } - -} - -class JavaLayoutHtmlInboundLinkResolutionService(private val paramMap: Map<String, List<String>>) : InboundExternalLinkResolutionService { - private fun getContainerPath(symbol: DeclarationDescriptor): String? { - return when (symbol) { - is PackageFragmentDescriptor -> symbol.fqName.asString().replace('.', '/') + "/" - is ClassifierDescriptor -> getContainerPath(symbol.findPackage()) + symbol.nameWithOuter() + ".html" - else -> null - } - } - - private fun DeclarationDescriptor.findPackage(): PackageFragmentDescriptor = - generateSequence(this) { it.containingDeclaration }.filterIsInstance<PackageFragmentDescriptor>().first() - - private fun ClassifierDescriptor.nameWithOuter(): String = - generateSequence(this) { it.containingDeclaration as? ClassifierDescriptor } - .toList().asReversed().joinToString(".") { it.name.asString() } - - private fun getPagePath(symbol: DeclarationDescriptor): String? { - return when (symbol) { - is PackageFragmentDescriptor -> getContainerPath(symbol) + "package-summary.html" - is EnumEntrySyntheticClassDescriptor -> getContainerPath(symbol.containingDeclaration) + "#" + symbol.signatureForAnchorUrlEncoded() - is ClassifierDescriptor -> getContainerPath(symbol) + "#" - is FunctionDescriptor, is PropertyDescriptor -> getContainerPath(symbol.containingDeclaration!!) + "#" + symbol.signatureForAnchorUrlEncoded() - else -> null - } - } - - private fun DeclarationDescriptor.signatureForAnchor(): String? { - - fun ReceiverParameterDescriptor.extractReceiverName(): String { - var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!! - if (receiverClass.isCompanionObject()) { - receiverClass = receiverClass.containingDeclaration!! - } else if (receiverClass is TypeParameterDescriptor) { - val upperBoundClass = receiverClass.upperBounds.singleOrNull()?.constructor?.declarationDescriptor - if (upperBoundClass != null) { - receiverClass = upperBoundClass - } - } - - return receiverClass.name.asString() - } - - fun KotlinType.qualifiedNameForSignature(): String { - val desc = constructor.declarationDescriptor - return desc?.fqNameUnsafe?.asString() ?: "<ERROR TYPE NAME>" - } - - fun StringBuilder.appendReceiverAndCompanion(desc: CallableDescriptor) { - if (desc.containingDeclaration.isCompanionObject()) { - append("Companion.") - } - desc.extensionReceiverParameter?.let { - append("(") - append(it.extractReceiverName()) - append(").") - } - } - - return when(this) { - is EnumEntrySyntheticClassDescriptor -> buildString { - append("ENUM_VALUE:") - append(name.asString()) - } - is FunctionDescriptor -> buildString { - appendReceiverAndCompanion(this@signatureForAnchor) - append(name.asString()) - valueParameters.joinTo(this, prefix = "(", postfix = ")") { - it.type.qualifiedNameForSignature() - } - } - is PropertyDescriptor -> buildString { - appendReceiverAndCompanion(this@signatureForAnchor) - append(name.asString()) - append(":") - append(returnType?.qualifiedNameForSignature()) - } - else -> null - } - } - - private fun DeclarationDescriptor.signatureForAnchorUrlEncoded(): String? = signatureForAnchor()?.urlEncoded() - - override fun getPath(symbol: DeclarationDescriptor) = getPagePath(symbol) -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt b/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt deleted file mode 100644 index 885cdf6c..00000000 --- a/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt +++ /dev/null @@ -1,91 +0,0 @@ -package org.jetbrains.dokka.Formats - -import com.google.inject.Binder -import com.google.inject.Inject -import kotlinx.html.li -import kotlinx.html.stream.appendHTML -import kotlinx.html.ul -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Samples.DefaultSampleProcessingService -import org.jetbrains.dokka.Utilities.bind -import org.jetbrains.dokka.Utilities.toType -import java.io.File - - -class JavaLayoutHtmlFormatDescriptor : FormatDescriptor, DefaultAnalysisComponent { - override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class - override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class - override val sampleProcessingService = DefaultSampleProcessingService::class - override val elementSignatureProvider = KotlinElementSignatureProvider::class - - override fun configureOutput(binder: Binder): Unit = with(binder) { - bind<Generator>() toType generatorServiceClass - } - - val formatServiceClass = JavaLayoutHtmlFormatService::class - val generatorServiceClass = JavaLayoutHtmlFormatGenerator::class -} - - -class JavaLayoutHtmlFormatService : FormatService { - override val extension: String - get() = TODO("not implemented") - - - override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder { - TODO("not implemented") - } -} - -class JavaLayoutHtmlFormatOutputBuilder : FormattedOutputBuilder { - override fun appendNodes(nodes: Iterable<DocumentationNode>) { - - } -} - - -class JavaLayoutHtmlFormatNavListBuilder @Inject constructor() : OutlineFormatService { - override fun getOutlineFileName(location: Location): File { - TODO() - } - - override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) { - with(to.appendHTML()) { - //a(href = ) - li { - when { - node.kind == NodeKind.Package -> appendOutline(location, to, node.members) - } - } - } - } - - override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { - with(to.appendHTML()) { - ul { body() } - } - } - -} - -class JavaLayoutHtmlFormatGenerator @Inject constructor( - private val outlineFormatService: OutlineFormatService -) : Generator { - override fun buildPages(nodes: Iterable<DocumentationNode>) { - - } - - override fun buildOutlines(nodes: Iterable<DocumentationNode>) { - for (node in nodes) { - if (node.kind == NodeKind.Module) { - //outlineFormatService.formatOutline() - } - } - } - - override fun buildSupportFiles() {} - - override fun buildPackageList(nodes: Iterable<DocumentationNode>) { - - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/JekyllFormatService.kt b/core/src/main/kotlin/Formats/JekyllFormatService.kt deleted file mode 100644 index a948dfa9..00000000 --- a/core/src/main/kotlin/Formats/JekyllFormatService.kt +++ /dev/null @@ -1,44 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName - -open class JekyllOutputBuilder(to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String>) - : MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { - override fun appendNodes(nodes: Iterable<DocumentationNode>) { - to.appendln("---") - appendFrontMatter(nodes, to) - to.appendln("---") - to.appendln("") - super.appendNodes(nodes) - } - - protected open fun appendFrontMatter(nodes: Iterable<DocumentationNode>, to: StringBuilder) { - to.appendln("title: ${getPageTitle(nodes)}") - } -} - - -open class JekyllFormatService( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - linkExtension: String, - impliedPlatforms: List<String> -) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) { - - @Inject constructor( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String> - ) : this(generator, signatureGenerator, "html", impliedPlatforms) - - override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - JekyllOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) - -} diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt deleted file mode 100644 index 3cdea156..00000000 --- a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt +++ /dev/null @@ -1,264 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName -import java.io.File - - -object EmptyHtmlTemplateService : HtmlTemplateService { - override fun appendFooter(to: StringBuilder) {} - - override fun appendHeader(to: StringBuilder, title: String?, basePath: File) {} -} - - -open class KotlinWebsiteHtmlOutputBuilder( - to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - val impliedPlatforms: List<String>, - templateService: HtmlTemplateService -) : HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) { - private var needHardLineBreaks = false - private var insideDiv = 0 - - override fun appendLine() {} - - override fun appendBreadcrumbs(path: Iterable<FormatLink>) { - if (path.count() > 1) { - to.append("<div class='api-docs-breadcrumbs'>") - super.appendBreadcrumbs(path) - to.append("</div>") - } - } - - override fun appendSinceKotlin(version: String) { - } - - override fun appendSinceKotlinWrapped(version: String) { - } - - override fun appendCode(body: () -> Unit) = wrapIfNotEmpty("<code>", "</code>", body) - - protected fun div(to: StringBuilder, cssClass: String, otherAttributes: String = "", block: () -> Unit) { - to.append("<div class=\"$cssClass\"$otherAttributes") - to.append(">") - insideDiv++ - block() - insideDiv-- - to.append("</div>\n") - } - - override fun appendAsSignature(node: ContentNode, block: () -> Unit) { - val contentLength = node.textLength - if (contentLength == 0) return - div(to, "signature") { - needHardLineBreaks = contentLength >= 62 - try { - block() - } finally { - needHardLineBreaks = false - } - } - } - - override fun appendAsOverloadGroup(to: StringBuilder, platforms: PlatformsData, block: () -> Unit) { - div(to, "overload-group", calculateDataAttributes(platforms)) { - block() - } - } - - override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body) - - override fun appendTable(vararg columns: String, body: () -> Unit) { - //to.appendln("<table class=\"api-docs-table\">") - div(to, "api-declarations-list") { - body() - } - //to.appendln("</table>") - } - - override fun appendTableBody(body: () -> Unit) { - //to.appendln("<tbody>") - body() - //to.appendln("</tbody>") - } - - override fun appendTableRow(body: () -> Unit) { - //to.appendln("<tr>") - body() - //to.appendln("</tr>") - } - - override fun appendTableCell(body: () -> Unit) { -// to.appendln("<td>") - body() -// to.appendln("\n</td>") - } - - override fun appendSymbol(text: String) { - to.append("<span class=\"symbol\">${text.htmlEscape()}</span>") - } - - override fun appendKeyword(text: String) { - to.append("<span class=\"keyword\">${text.htmlEscape()}</span>") - } - - override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { - val id = signature?.let { " id=\"$it\"" }.orEmpty() - to.append("<span class=\"${identifierClassName(kind)}\"$id>${text.htmlEscape()}</span>") - } - - override fun appendSoftLineBreak() { - if (needHardLineBreaks) - to.append("<br/>") - } - - override fun appendIndentedSoftLineBreak() { - if (needHardLineBreaks) { - to.append("<br/> ") - } - } - - private fun identifierClassName(kind: IdentifierKind) = when (kind) { - IdentifierKind.ParameterName -> "parameterName" - IdentifierKind.SummarizedTypeName -> "summarizedTypeName" - else -> "identifier" - } - - private data class PlatformsForElement( - val platformToVersion: Map<String, String> - ) - - private fun calculatePlatforms(platforms: PlatformsData): PlatformsForElement { - //val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion)?.removePrefix("Kotlin ") - val jreVersion = platforms.keys.filter(String::isJREVersion).min()?.takeUnless { it.endsWith("6") } - val targetPlatforms = platforms.filterNot { it.key.isJREVersion() } + - listOfNotNull(jreVersion?.let { it to platforms[it]!! }) - - return PlatformsForElement( - targetPlatforms.mapValues { (_, nodes) -> effectiveSinceKotlinForNodes(nodes) } - ) - } - - private fun calculateDataAttributes(platforms: PlatformsData): String { - val platformToVersion = calculatePlatforms(platforms).platformToVersion - val (platformNames, versions) = platformToVersion.toList().unzip() - return "data-platform=\"${platformNames.joinToString()}\" "+ - "data-kotlin-version=\"${versions.joinToString()}\"" - } - - override fun appendIndexRow(platforms: PlatformsData, block: () -> Unit) { -// if (platforms.isNotEmpty()) -// wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block) -// else -// appendTableRow(block) - div(to, "declarations", otherAttributes = " ${calculateDataAttributes(platforms)}") { - block() - } - } - - override fun appendPlatforms(platforms: PlatformsData) { - val platformToVersion = calculatePlatforms(platforms).platformToVersion - div(to, "tags") { - div(to, "spacer") {} - platformToVersion.entries.sortedBy { - platformSortWeight(it.key) - }.forEach { (platform, version) -> - div(to, "tags__tag platform tag-value-$platform", - otherAttributes = " data-tag-version=\"$version\"") { - to.append(platform) - } - } - div(to, "tags__tag kotlin-version") { - to.append(mergeVersions(platformToVersion.values.toList())) - } - } - } - - override fun appendAsNodeDescription(platforms: PlatformsData, block: () -> Unit) { - div(to, "node-page-main", otherAttributes = " ${calculateDataAttributes(platforms)}") { - block() - } - - } - - override fun appendBreadcrumbSeparator() { - to.append(" / ") - } - - override fun appendPlatformsAsText(platforms: PlatformsData) { - appendHeader(5) { - val filtered = platforms.keys.filterNot { it.isJREVersion() }.sortedBy { platformSortWeight(it) } - if (filtered.isNotEmpty()) { - to.append("For ") - filtered.joinTo(to) - } - } - } - - override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) { - div(to, "sample", otherAttributes = " data-min-compiler-version=\"1.3\"") { - appendBlockCode(language) { - imports() - wrap("\n\nfun main(args: Array<String>) {".htmlEscape(), "}") { - wrap("\n//sampleStart\n", "\n//sampleEnd\n", body) - } - } - } - } - - override fun appendSoftParagraph(body: () -> Unit) = appendParagraph(body) - - - override fun appendSectionWithTag(section: ContentSection) { - appendParagraph { - appendStrong { appendText(section.tag) } - appendText(" ") - appendContent(section) - } - } - - override fun appendAsPlatformDependentBlock(platforms: PlatformsData, block: (PlatformsData) -> Unit) { - if (platforms.isNotEmpty()) - wrap("<div ${calculateDataAttributes(platforms)}>", "</div>") { - block(platforms) - } - else - block(platforms) - } - - override fun appendAsSummaryGroup(platforms: PlatformsData, block: (PlatformsData) -> Unit) { - div(to, "summary-group", otherAttributes = " ${calculateDataAttributes(platforms)}") { - block(platforms) - } - } - - fun platformSortWeight(name: String) = when(name.toLowerCase()) { - "common" -> 0 - "jvm" -> 1 - "js" -> 3 - "native" -> 4 - else -> 2 // This is hack to support JRE/JUnit and so on - } -} - -class KotlinWebsiteHtmlFormatService @Inject constructor( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String>, - templateService: HtmlTemplateService -) : HtmlFormatService(generator, signatureGenerator, templateService, impliedPlatforms) { - - override fun enumerateSupportFiles(callback: (String, String) -> Unit) {} - - override fun createOutputBuilder(to: StringBuilder, location: Location) = - KotlinWebsiteHtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) -} - - -private fun String.isKotlinVersion() = this.startsWith("Kotlin") -private fun String.isJREVersion() = this.startsWith("JRE", ignoreCase=true)
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt deleted file mode 100644 index 216dd2ef..00000000 --- a/core/src/main/kotlin/Formats/MarkdownFormatService.kt +++ /dev/null @@ -1,250 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName -import java.util.* - -enum class ListKind { - Ordered, - Unordered -} - -private class ListState(val kind: ListKind, var size: Int = 1) { - fun getTagAndIncrement() = when (kind) { - ListKind.Ordered -> "${size++}. " - else -> "* " - } -} - -private val TWO_LINE_BREAKS = System.lineSeparator() + System.lineSeparator() - -open class MarkdownOutputBuilder(to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String>) - : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) -{ - private val listStack = ArrayDeque<ListState>() - protected var inTableCell = false - protected var inCodeBlock = false - private var lastTableCellStart = -1 - private var maxBackticksInCodeBlock = 0 - - private fun appendNewline() { - while (to.endsWith(' ')) { - to.setLength(to.length - 1) - } - to.appendln() - } - - private fun ensureNewline() { - if (inTableCell && listStack.isEmpty()) { - if (to.length != lastTableCellStart && !to.endsWith("<br>")) { - to.append("<br>") - } - } - else { - if (!endsWithNewline()) { - appendNewline() - } - } - } - - private fun endsWithNewline(): Boolean { - var index = to.length - 1 - while (index > 0) { - val c = to[index] - if (c != ' ') { - return c == '\n' - } - index-- - } - return false - } - - override fun ensureParagraph() { - if (!to.endsWith(TWO_LINE_BREAKS)) { - if (!to.endsWith('\n')) { - appendNewline() - } - appendNewline() - } - } - override fun appendBreadcrumbSeparator() { - to.append(" / ") - } - - private val backTickFindingRegex = """(`+)""".toRegex() - - override fun appendText(text: String) { - if (inCodeBlock) { - to.append(text) - val backTicks = backTickFindingRegex.findAll(text) - val longestBackTickRun = backTicks.map { it.value.length }.max() ?: 0 - maxBackticksInCodeBlock = maxBackticksInCodeBlock.coerceAtLeast(longestBackTickRun) - } - else { - if (text == "\n" && inTableCell) { - to.append(" ") - } else { - to.append(text.htmlEscape()) - } - } - } - - override fun appendCode(body: () -> Unit) { - inCodeBlock = true - val codeBlockStart = to.length - maxBackticksInCodeBlock = 0 - - wrapIfNotEmpty("`", "`", body, checkEndsWith = true) - - if (maxBackticksInCodeBlock > 0) { - val extraBackticks = "`".repeat(maxBackticksInCodeBlock) - to.insert(codeBlockStart, extraBackticks) - to.append(extraBackticks) - } - - inCodeBlock = false - } - - override fun appendUnorderedList(body: () -> Unit) { - listStack.push(ListState(ListKind.Unordered)) - body() - listStack.pop() - ensureNewline() - } - - override fun appendOrderedList(body: () -> Unit) { - listStack.push(ListState(ListKind.Ordered)) - body() - listStack.pop() - ensureNewline() - } - - override fun appendListItem(body: () -> Unit) { - ensureNewline() - to.append(listStack.peek()?.getTagAndIncrement()) - body() - ensureNewline() - } - - override fun appendStrong(body: () -> Unit) = wrap("**", "**", body) - override fun appendEmphasis(body: () -> Unit) = wrap("*", "*", body) - override fun appendStrikethrough(body: () -> Unit) = wrap("~~", "~~", body) - - override fun appendLink(href: String, body: () -> Unit) { - if (inCodeBlock) { - wrap("`[`", "`]($href)`", body) - } - else { - wrap("[", "]($href)", body) - } - } - - override fun appendLine() { - if (inTableCell) { - to.append("<br>") - } - else { - appendNewline() - } - } - - override fun appendAnchor(anchor: String) { - // no anchors in Markdown - } - - override fun appendParagraph(body: () -> Unit) { - when { - inTableCell -> { - ensureNewline() - body() - } - listStack.isNotEmpty() -> { - body() - ensureNewline() - } - else -> { - ensureParagraph() - body() - ensureParagraph() - } - } - } - - override fun appendHeader(level: Int, body: () -> Unit) { - when { - inTableCell -> { - body() - } - else -> { - ensureParagraph() - to.append("${"#".repeat(level)} ") - body() - ensureParagraph() - } - } - } - - override fun appendBlockCode(language: String, body: () -> Unit) { - inCodeBlock = true - ensureParagraph() - to.appendln(if (language.isEmpty()) "```" else "``` $language") - body() - ensureNewline() - to.appendln("```") - appendLine() - inCodeBlock = false - } - - override fun appendTable(vararg columns: String, body: () -> Unit) { - ensureParagraph() - body() - ensureParagraph() - } - - override fun appendTableBody(body: () -> Unit) { - body() - } - - override fun appendTableRow(body: () -> Unit) { - to.append("|") - body() - appendNewline() - } - - override fun appendTableCell(body: () -> Unit) { - to.append(" ") - inTableCell = true - lastTableCellStart = to.length - body() - inTableCell = false - to.append(" |") - } - - override fun appendNonBreakingSpace() { - if (inCodeBlock) { - to.append(" ") - } - else { - to.append(" ") - } - } -} - -open class MarkdownFormatService(generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - linkExtension: String, - val impliedPlatforms: List<String>) -: StructuredFormatService(generator, signatureGenerator, "md", linkExtension) { - @Inject constructor(generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(generator, signatureGenerator, "md", impliedPlatforms) - - override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) -} diff --git a/core/src/main/kotlin/Formats/OutlineService.kt b/core/src/main/kotlin/Formats/OutlineService.kt deleted file mode 100644 index 958e93af..00000000 --- a/core/src/main/kotlin/Formats/OutlineService.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -/** - * Service for building the outline of the package contents. - */ -interface OutlineFormatService { - fun getOutlineFileName(location: Location): File - - fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) - fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) - - /** Appends formatted outline to [StringBuilder](to) using specified [location] */ - fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) { - for (node in nodes) { - appendOutlineHeader(location, node, to) - if (node.members.any()) { - val sortedMembers = node.members.sortedBy { it.name.toLowerCase() } - appendOutlineLevel(to) { - appendOutline(location, to, sortedMembers) - } - } - } - } - - fun formatOutline(location: Location, nodes: Iterable<DocumentationNode>): String = - StringBuilder().apply { appendOutline(location, this, nodes) }.toString() -} diff --git a/core/src/main/kotlin/Formats/PackageListService.kt b/core/src/main/kotlin/Formats/PackageListService.kt deleted file mode 100644 index e675d927..00000000 --- a/core/src/main/kotlin/Formats/PackageListService.kt +++ /dev/null @@ -1,66 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject - - -interface PackageListService { - fun formatPackageList(module: DocumentationModule): String -} - -class DefaultPackageListService @Inject constructor( - val generator: NodeLocationAwareGenerator, - val formatService: FormatService -) : PackageListService { - - override fun formatPackageList(module: DocumentationModule): String { - val packages = mutableSetOf<String>() - val nonStandardLocations = mutableMapOf<String, String>() - - fun visit(node: DocumentationNode, relocated: Boolean = false) { - val nodeKind = node.kind - - when (nodeKind) { - NodeKind.Package -> { - packages.add(node.qualifiedName()) - node.members.forEach { visit(it) } - } - NodeKind.Signature -> { - if (relocated) - nonStandardLocations[node.name] = generator.relativePathToLocation(module, node.owner!!) - } - NodeKind.ExternalClass -> { - node.members.forEach { visit(it, relocated = true) } - } - NodeKind.GroupNode -> { - if (node.members.isNotEmpty()) { - // Only nodes only has single file is need to be relocated - // TypeAliases for example - node.origins - .filter { it.members.isEmpty() } - .forEach { visit(it, relocated = true) } - } - } - else -> { - if (nodeKind in NodeKind.classLike || nodeKind in NodeKind.memberLike) { - node.details(NodeKind.Signature).forEach { visit(it, relocated) } - node.members.forEach { visit(it, relocated) } - } - } - } - } - - module.members.forEach { visit(it) } - - return buildString { - appendln("\$dokka.linkExtension:${formatService.linkExtension}") - - nonStandardLocations.map { (signature, location) -> "\$dokka.location:$signature\u001f$location" } - .sorted().joinTo(this, separator = "\n", postfix = "\n") - - packages.sorted().joinTo(this, separator = "\n", postfix = "\n") - } - - } - -} - diff --git a/core/src/main/kotlin/Formats/StandardFormats.kt b/core/src/main/kotlin/Formats/StandardFormats.kt deleted file mode 100644 index 86f70a37..00000000 --- a/core/src/main/kotlin/Formats/StandardFormats.kt +++ /dev/null @@ -1,55 +0,0 @@ -package org.jetbrains.dokka.Formats - -import com.google.inject.Binder -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Samples.KotlinWebsiteSampleProcessingService -import org.jetbrains.dokka.Utilities.bind -import kotlin.reflect.KClass - -abstract class KotlinFormatDescriptorBase - : FileGeneratorBasedFormatDescriptor(), - DefaultAnalysisComponent, - DefaultAnalysisComponentServices by KotlinAsKotlin { - override val generatorServiceClass = FileGenerator::class - override val outlineServiceClass: KClass<out OutlineFormatService>? = null - override val packageListServiceClass: KClass<out PackageListService>? = DefaultPackageListService::class -} - -abstract class HtmlFormatDescriptorBase : FileGeneratorBasedFormatDescriptor(), DefaultAnalysisComponent { - override val formatServiceClass = HtmlFormatService::class - override val outlineServiceClass = HtmlFormatService::class - override val generatorServiceClass = FileGenerator::class - override val packageListServiceClass = DefaultPackageListService::class - - override fun configureOutput(binder: Binder): Unit = with(binder) { - super.configureOutput(binder) - bind<HtmlTemplateService>().toProvider { HtmlTemplateService.default("style.css") } - } -} - -class HtmlFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsKotlin - -class HtmlAsJavaFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsJava - -class KotlinWebsiteHtmlFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = KotlinWebsiteHtmlFormatService::class - override val sampleProcessingService = KotlinWebsiteSampleProcessingService::class - override val outlineServiceClass = YamlOutlineService::class - - override fun configureOutput(binder: Binder) = with(binder) { - super.configureOutput(binder) - bind<HtmlTemplateService>().toInstance(EmptyHtmlTemplateService) - } -} - -class JekyllFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = JekyllFormatService::class -} - -class MarkdownFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = MarkdownFormatService::class -} - -class GFMFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = GFMFormatService::class -} diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt deleted file mode 100644 index 76f9366f..00000000 --- a/core/src/main/kotlin/Formats/StructuredFormatService.kt +++ /dev/null @@ -1,1014 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.LanguageService.RenderMode -import org.jetbrains.kotlin.utils.keysToMap -import java.util.* - -data class FormatLink(val text: String, val href: String) - -private data class Summarized( - val data: List<SummarizedBySummary> -) { - - constructor(data: Map<ContentNode, Map<ContentNode, List<DocumentationNode>>>) : this( - data.entries.map { (summary, signatureToMember) -> - SummarizedBySummary( - summary, - signatureToMember.map { (signature, nodes) -> - SummarizedNodes(signature, nodes) - } - ) - } - ) - - data class SummarizedNodes(val content: ContentNode, val nodes: List<DocumentationNode>) { - val platforms = effectivePlatformsForMembers(nodes) - } - data class SummarizedBySummary(val content: ContentNode, val signatures: List<SummarizedNodes>) { - val platforms = effectivePlatformsForMembers(signatures.flatMap { it.nodes }) - val platformsOnSignature = !samePlatforms(signatures.map { it.platforms }) - } - - - fun computePlatformLevel(): PlatformPlacement { - if (data.any { it.platformsOnSignature }) { - return PlatformPlacement.Signature - } - if (samePlatforms(data.map { it.platforms })) { - return PlatformPlacement.Row - } - return PlatformPlacement.Summary - } - val platformPlacement: PlatformPlacement = computePlatformLevel() - val platforms = effectivePlatformsForMembers(data.flatMap { it.signatures.flatMap { it.nodes } }) - - - enum class PlatformPlacement { - Row, Summary, Signature - } -} - -abstract class StructuredOutputBuilder(val to: StringBuilder, - val location: Location, - val generator: NodeLocationAwareGenerator, - val languageService: LanguageService, - val extension: String, - impliedPlatforms: List<String>) : FormattedOutputBuilder { - - protected fun wrap(prefix: String, suffix: String, body: () -> Unit) { - to.append(prefix) - body() - to.append(suffix) - } - - protected fun wrapIfNotEmpty(prefix: String, suffix: String, body: () -> Unit, checkEndsWith: Boolean = false) { - val startLength = to.length - to.append(prefix) - body() - if (checkEndsWith && to.endsWith(suffix)) { - to.setLength(to.length - suffix.length) - } else if (to.length > startLength + prefix.length) { - to.append(suffix) - } else { - to.setLength(startLength) - } - } - - protected fun wrapInTag(tag: String, - body: () -> Unit, - newlineBeforeOpen: Boolean = false, - newlineAfterOpen: Boolean = false, - newlineAfterClose: Boolean = false) { - if (newlineBeforeOpen && !to.endsWith('\n')) to.appendln() - to.append("<$tag>") - if (newlineAfterOpen) to.appendln() - body() - to.append("</$tag>") - if (newlineAfterClose) to.appendln() - } - - protected abstract fun ensureParagraph() - - open fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) = appendBlockCode(language, body) - abstract fun appendBlockCode(language: String, body: () -> Unit) - abstract fun appendHeader(level: Int = 1, body: () -> Unit) - abstract fun appendParagraph(body: () -> Unit) - - open fun appendSoftParagraph(body: () -> Unit) { - ensureParagraph() - body() - } - - abstract fun appendLine() - abstract fun appendAnchor(anchor: String) - - abstract fun appendTable(vararg columns: String, body: () -> Unit) - abstract fun appendTableBody(body: () -> Unit) - abstract fun appendTableRow(body: () -> Unit) - abstract fun appendTableCell(body: () -> Unit) - - abstract fun appendText(text: String) - - open fun appendSinceKotlin(version: String) { - appendText("Since: ") - appendCode { appendText(version) } - } - - open fun appendSinceKotlinWrapped(version: String) { - wrap(" (", ")") { - appendSinceKotlin(version) - } - } - - open fun appendSectionWithTag(section: ContentSection) { - appendParagraph { - appendStrong { appendText(section.tag) } - appendLine() - appendContent(section) - } - } - - open fun appendAsPlatformDependentBlock(platforms: PlatformsData, block: (PlatformsData) -> Unit) { - block(platforms) - } - - open fun appendAsSummaryGroup(platforms: PlatformsData, block: (PlatformsData) -> Unit) { - appendAsPlatformDependentBlock(platforms, block) - } - - open fun appendSymbol(text: String) { - appendText(text) - } - - open fun appendKeyword(text: String) { - appendText(text) - } - - open fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { - appendText(text) - } - - open fun appendAsNodeDescription(platforms: PlatformsData, block: () -> Unit) { - block() - } - - fun appendEntity(text: String) { - to.append(text) - } - - abstract fun appendLink(href: String, body: () -> Unit) - - open fun appendLink(link: FormatLink) { - appendLink(link.href) { appendText(link.text) } - } - - abstract fun appendStrong(body: () -> Unit) - abstract fun appendStrikethrough(body: () -> Unit) - abstract fun appendEmphasis(body: () -> Unit) - abstract fun appendCode(body: () -> Unit) - abstract fun appendUnorderedList(body: () -> Unit) - abstract fun appendOrderedList(body: () -> Unit) - abstract fun appendListItem(body: () -> Unit) - - abstract fun appendBreadcrumbSeparator() - abstract fun appendNonBreakingSpace() - open fun appendSoftLineBreak() { - } - - open fun appendIndentedSoftLineBreak() { - } - - fun appendContent(content: List<ContentNode>) { - for (contentNode in content) { - appendContent(contentNode) - } - } - - open fun appendContent(content: ContentNode) { - when (content) { - is ContentText -> appendText(content.text) - is ContentSymbol -> appendSymbol(content.text) - is ContentKeyword -> appendKeyword(content.text) - is ContentIdentifier -> appendIdentifier(content.text, content.kind, content.signature) - is ContentNonBreakingSpace -> appendNonBreakingSpace() - is ContentSoftLineBreak -> appendSoftLineBreak() - is ContentIndentedSoftLineBreak -> appendIndentedSoftLineBreak() - is ContentEntity -> appendEntity(content.text) - is ContentStrong -> appendStrong { appendContent(content.children) } - is ContentStrikethrough -> appendStrikethrough { appendContent(content.children) } - is ContentCode -> appendCode { appendContent(content.children) } - is ContentEmphasis -> appendEmphasis { appendContent(content.children) } - is ContentUnorderedList -> appendUnorderedList { appendContent(content.children) } - is ContentOrderedList -> appendOrderedList { appendContent(content.children) } - is ContentListItem -> appendListItem { - val child = content.children.singleOrNull() - if (child is ContentParagraph) { - appendContent(child.children) - } else { - appendContent(content.children) - } - } - - is NodeRenderContent -> { - val node = content.node - appendContent(languageService.render(node, content.mode)) - } - is ContentNodeLink -> { - val node = content.node - val linkTo = if (node != null) locationHref(location, node, generator) else "#" - appendLinkIfNotThisPage(linkTo, content) - } - is ContentExternalLink -> appendLinkIfNotThisPage(content.href, content) - - is ContentParagraph -> { - if (!content.isEmpty()) { - appendParagraph { appendContent(content.children) } - } - } - - is ContentBlockSampleCode, is ContentBlockCode -> { - content as ContentBlockCode - fun ContentBlockCode.appendBlockCodeContent() { - children - .dropWhile { it is ContentText && it.text.isBlank() } - .forEach { appendContent(it) } - } - when (content) { - is ContentBlockSampleCode -> - appendSampleBlockCode(content.language, content.importsBlock::appendBlockCodeContent) { content.appendBlockCodeContent() } - is ContentBlockCode -> - appendBlockCode(content.language) { content.appendBlockCodeContent() } - } - } - is ContentHeading -> appendHeader(content.level) { appendContent(content.children) } - is ContentBlock -> appendContent(content.children) - } - } - - private fun appendLinkIfNotThisPage(href: String, content: ContentBlock) { - if (href == ".") { - appendContent(content.children) - } else { - appendLink(href) { appendContent(content.children) } - } - } - - open fun link( - from: DocumentationNode, - to: DocumentationNode, - name: (DocumentationNode) -> String = DocumentationNode::name - ): FormatLink = link(from, to, extension, name) - - open fun link( - from: DocumentationNode, - to: DocumentationNode, - extension: String, - name: (DocumentationNode) -> String = DocumentationNode::name - ): FormatLink = - FormatLink(name(to), generator.relativePathToLocation(from, to)) - - private fun DocumentationNode.isModuleOrPackage(): Boolean = - kind == NodeKind.Module || kind == NodeKind.Package - - protected open fun appendAsSignature(node: ContentNode, block: () -> Unit) { - block() - } - - protected open fun appendAsOverloadGroup(to: StringBuilder, platforms: PlatformsData, block: () -> Unit) { - block() - } - - protected open fun appendIndexRow(platforms: PlatformsData, block: () -> Unit) { - appendTableRow(block) - } - - protected open fun appendPlatformsAsText(platforms: PlatformsData) { - appendPlatforms(platforms) - } - - protected open fun appendPlatforms(platforms: PlatformsData) { - if (platforms.isNotEmpty()) { - appendText(platforms.keys.joinToString(prefix = "(", postfix = ") ")) - } - } - - protected open fun appendBreadcrumbs(path: Iterable<FormatLink>) { - for ((index, item) in path.withIndex()) { - if (index > 0) { - appendBreadcrumbSeparator() - } - appendLink(item) - } - } - - fun Content.getSectionsWithSubjects(): Map<String, List<ContentSection>> = - sections.filter { it.subjectName != null }.groupBy { it.tag } - - private fun ContentNode.appendSignature() { - if (this is ContentBlock && this.isEmpty()) { - return - } - - val signatureAsCode = ContentCode() - signatureAsCode.append(this) - appendContent(signatureAsCode) - } - - open inner class PageBuilder(val nodes: Iterable<DocumentationNode>, val noHeader: Boolean = false) { - open fun build() { - val breakdownByLocation = nodes.groupBy { node -> - node.path.filterNot { it.name.isEmpty() }.map { link(node, it) }.distinct() - } - - for ((path, nodes) in breakdownByLocation) { - if (!noHeader && path.isNotEmpty()) { - appendBreadcrumbs(path) - appendLine() - appendLine() - } - appendLocation(nodes.filter { it.kind != NodeKind.ExternalClass }) - } - } - - private fun appendLocation(nodes: Iterable<DocumentationNode>) { - val singleNode = nodes.singleOrNull() - if (singleNode != null && singleNode.isModuleOrPackage()) { - if (singleNode.kind == NodeKind.Package) { - val packageName = if (singleNode.name.isEmpty()) "<root>" else singleNode.name - appendHeader(2) { appendText("Package $packageName") } - } - appendContent(singleNode.content) - } else { - val breakdownByName = nodes.groupBy { node -> node.name } - for ((name, items) in breakdownByName) { - if (!noHeader) - appendHeader { appendText(name) } - appendDocumentation(items, singleNode != null) - } - } - } - - private fun appendDocumentation(overloads: Iterable<DocumentationNode>, isSingleNode: Boolean) { - val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node -> - when (node.kind) { - NodeKind.GroupNode -> node.origins.map { it.content } - else -> node.content - } - } - - if (breakdownBySummary.size == 1) { - val node = breakdownBySummary.values.single() - appendAsNodeDescription(effectivePlatformsForMembers(node)) { - formatOverloadGroup(node, isSingleNode) - } - } else { - for ((_, items) in breakdownBySummary) { - appendAsOverloadGroup(to, effectivePlatformsForMembers(items)) { - formatOverloadGroup(items) - } - } - } - } - - private fun formatOverloadGroup(items: List<DocumentationNode>, isSingleNode: Boolean = false) { - - val platformsPerGroup = samePlatforms( - items.flatMap { - if (it.kind == NodeKind.GroupNode) { - it.origins.groupBy { origin -> - languageService.render(origin) - }.values.map { origins -> effectivePlatformsForMembers(origins) } - } else { - listOf(effectivePlatformsForNode(it)) - } - } - ) - - if (platformsPerGroup) { - appendAsPlatformDependentBlock(effectivePlatformsForMembers(items)) { platforms -> - appendPlatforms(platforms) - } - } - for ((index, item) in items.withIndex()) { - if (index > 0) appendLine() - - if (item.kind == NodeKind.GroupNode) { - renderGroupNode(item, isSingleNode, !platformsPerGroup) - } else { - renderSimpleNode(item, isSingleNode, !platformsPerGroup) - } - - } - // All items have exactly the same documentation, so we can use any item to render it - val item = items.first() - // TODO: remove this block cause there is no one node with OverloadGroupNote detail - item.details(NodeKind.OverloadGroupNote).forEach { - appendContent(it.content) - } - - if (item.kind == NodeKind.GroupNode) { - val groupByContent = item.origins.groupBy { it.content } - if (groupByContent.count { !it.key.isEmpty() } > 1) { - if (groupByContent.size > 1) println("[mult] Found ov diff: ${generator.location(item).path}") - } - for ((content, origins) in groupByContent) { - if (content.isEmpty()) continue - appendAsPlatformDependentBlock(effectivePlatformsForMembers(origins)) { platforms -> - if (groupByContent.count { !it.key.isEmpty() } > 1) { - appendPlatformsAsText(platforms) - } - appendContent(content.summary) - content.appendDescription() - } - } - } else { - val platforms = effectivePlatformsForNode(item) - appendAsPlatformDependentBlock(platforms) { - appendContent(item.summary) - item.content.appendDescription() - } - } - } - - - fun renderSimpleNode(item: DocumentationNode, isSingleNode: Boolean, withPlatforms: Boolean = true) { - appendAsPlatformDependentBlock(effectivePlatformsForMembers(listOf(item))) { platforms -> - // TODO: use summarizesignatures - val rendered = languageService.render(item) - item.detailOrNull(NodeKind.Signature)?.let { - if (item.kind !in NodeKind.classLike || !isSingleNode) - appendAnchor(it.name) - } - if (withPlatforms) { - appendPlatforms(platforms) - } - appendAsSignature(rendered) { - appendCode { appendContent(rendered) } - item.appendSourceLink() - } - item.appendOverrides() - item.appendDeprecation() - } - } - - fun renderGroupNode(item: DocumentationNode, isSingleNode: Boolean, withPlatforms: Boolean = true) { - // TODO: use summarizesignatures - val groupBySignature = item.origins.groupBy { - languageService.render(it) - } - - for ((sign, nodes) in groupBySignature) { - appendAsPlatformDependentBlock(effectivePlatformsForMembers(nodes)) { platforms -> - val first = nodes.first() - first.detailOrNull(NodeKind.Signature)?.let { - if (item.kind !in NodeKind.classLike || !isSingleNode) - appendAnchor(it.name) - } - - if (withPlatforms) { - appendPlatforms(platforms) - } - - appendAsSignature(sign) { - appendCode { appendContent(sign) } - } - first.appendOverrides() - first.appendDeprecation() - } - - } - } - - private fun DocumentationNode.appendSourceLink() { - val sourceUrl = details(NodeKind.SourceUrl).firstOrNull() - if (sourceUrl != null) { - to.append(" ") - appendLink(sourceUrl.name) { to.append("(source)") } - } - } - - private fun DocumentationNode.appendOverrides() { - overrides.forEach { - appendParagraph { - to.append("Overrides ") - val location = generator.relativePathToLocation(this, it) - - appendLink(FormatLink(it.owner!!.name + "." + it.name, location)) - } - } - } - - private fun DocumentationNode.appendDeprecation() { - if (deprecation != null) { - val deprecationParameter = deprecation!!.details(NodeKind.Parameter).firstOrNull() - val deprecationValue = deprecationParameter?.details(NodeKind.Value)?.firstOrNull() - appendLine() - when { - deprecationValue != null -> { - appendStrong { to.append("Deprecated:") } - appendText(" " + deprecationValue.name.removeSurrounding("\"")) - appendLine() - appendLine() - } - deprecation?.content != Content.Empty -> { - appendStrong { to.append("Deprecated:") } - to.append(" ") - appendContent(deprecation!!.content) - } - else -> { - appendStrong { to.append("Deprecated") } - appendLine() - appendLine() - } - } - } - } - - -// protected fun platformsOfItems(items: List<DocumentationNode>): Set<String> { -// val platforms = items.asSequence().map { -// when (it.kind) { -// NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module -> platformsOfItems(it.members) -// NodeKind.GroupNode -> platformsOfItems(it.origins) -// else -> it.platformsToShow.toSet() -// } -// } -// -// fun String.isKotlinVersion() = this.startsWith("Kotlin") -// -// if (platforms.count() == 0) return emptySet() -// -// // Calculating common platforms for items -// return platforms.reduce { result, platformsOfItem -> -// val otherKotlinVersion = result.find { it.isKotlinVersion() } -// val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() } -// -// // When no Kotlin version specified, it means that version is 1.0 -// if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) { -// result.intersect(platformsOfItem) + mergeVersions(otherKotlinVersion, kotlinVersions) -// } else { -// result.intersect(platformsOfItem) -// } -// } -// } -// -// protected fun unionPlatformsOfItems(items: List<DocumentationNode>): Set<String> { -// val platforms = items.asSequence().map { -// when (it.kind) { -// NodeKind.GroupNode -> unionPlatformsOfItems(it.origins) -// else -> it.platformsToShow.toSet() -// } -// } -// -// fun String.isKotlinVersion() = this.startsWith("Kotlin") -// -// if (platforms.count() == 0) return emptySet() -// -// // Calculating common platforms for items -// return platforms.reduce { result, platformsOfItem -> -// val otherKotlinVersion = result.find { it.isKotlinVersion() } -// val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() } -// -// // When no Kotlin version specified, it means that version is 1.0 -// if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) { -// result.union(otherPlatforms) + mergeVersions(otherKotlinVersion, kotlinVersions) -// } else { -// result.union(otherPlatforms) -// } -// } -// } - -// val DocumentationNode.platformsToShow: List<String> -// get() = platforms - - private fun Content.appendDescription() { - if (description != ContentEmpty) { - appendContent(description) - } - - - getSectionsWithSubjects().forEach { - appendSectionWithSubject(it.key, it.value) - } - - for (section in sections.filter { it.subjectName == null }) { - appendSectionWithTag(section) - } - } - - fun appendSectionWithSubject(title: String, subjectSections: List<ContentSection>) { - appendHeader(3) { appendText(title) } - subjectSections.forEach { - val subjectName = it.subjectName - if (subjectName != null) { - appendSoftParagraph { - appendAnchor(subjectName) - appendCode { to.append(subjectName) } - to.append(" - ") - appendContent(it) - } - } - } - } - - fun appendOriginsGroupByContent(node: DocumentationNode) { - require(node.kind == NodeKind.GroupNode) - val groupByContent = - node.origins.groupBy { it.content } - .mapValues { (_, origins) -> - effectivePlatformsForMembers(origins) - } - .filterNot { it.key.isEmpty() } - .toList() - .sortedByDescending { it.second.size } - - if (groupByContent.size > 1) println("[mult] Found diff: ${generator.location(node).path}") - for ((content, platforms) in groupByContent) { - appendAsPlatformDependentBlock(platforms) { - if (groupByContent.size > 1) { - appendPlatformsAsText(platforms) - } - appendContent(content.summary) - content.appendDescription() - } - } - } - } - - inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false) : - PageBuilder(listOf(node), noHeader) { - - override fun build() { - super.build() - SectionsBuilder(node).build() - } - } - - inner class GroupNodePageBuilder(val node: DocumentationNode) : PageBuilder(listOf(node)) { - - override fun build() { - val breakdownByLocation = node.path.filterNot { it.name.isEmpty() }.map { link(node, it) } - - appendBreadcrumbs(breakdownByLocation) - appendLine() - appendLine() - appendHeader { appendText(node.name) } - - appendAsNodeDescription(effectivePlatformsForNode(node)) { - renderGroupNode(node, true) - - appendOriginsGroupByContent(node) - } - - SectionsBuilder(node).build() - } - } -// -// private fun unionPlatformsOfItems(items: List<DocumentationNode>): Set<String> { -// val platforms = items.flatMapTo(mutableSetOf<String>()) { -// when (it.kind) { -// NodeKind.GroupNode -> unionPlatformsOfItems(it.origins) -// else -> it.platforms -// } -// } -// -// return platforms -// } - - - inner class SectionsBuilder(val node: DocumentationNode): PageBuilder(listOf(node)) { - override fun build() { - if (node.kind == NodeKind.ExternalClass) { - appendSection("Extensions for ${node.name}", node.members) - return - } - - fun DocumentationNode.membersOrGroupMembers(predicate: (DocumentationNode) -> Boolean): List<DocumentationNode> { - return members.filter(predicate) + members(NodeKind.GroupNode).filter{ it.origins.isNotEmpty() && predicate(it.origins.first()) } - } - - fun DocumentationNode.membersOrGroupMembers(kind: NodeKind): List<DocumentationNode> { - return membersOrGroupMembers { it.kind == kind } - } - - appendSection("Packages", node.members(NodeKind.Package), platformsBasedOnMembers = true) - appendSection("Types", node.membersOrGroupMembers { it.kind in NodeKind.classLike /*&& it.kind != NodeKind.TypeAlias*/ && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception }) - appendSection("Annotations", node.membersOrGroupMembers(NodeKind.AnnotationClass)) - appendSection("Exceptions", node.membersOrGroupMembers(NodeKind.Exception)) - appendSection("Extensions for External Classes", node.members(NodeKind.ExternalClass)) - appendSection("Enum Values", node.membersOrGroupMembers(NodeKind.EnumItem), sortMembers = false, omitSamePlatforms = true) - appendSection("Constructors", node.membersOrGroupMembers(NodeKind.Constructor), omitSamePlatforms = true) - appendSection("Properties", node.membersOrGroupMembers(NodeKind.Property), omitSamePlatforms = true) - appendSection("Inherited Properties", node.inheritedMembers(NodeKind.Property)) - appendSection("Functions", node.membersOrGroupMembers(NodeKind.Function), omitSamePlatforms = true) - appendSection("Inherited Functions", node.inheritedMembers(NodeKind.Function)) - appendSection("Companion Object Properties", node.membersOrGroupMembers(NodeKind.CompanionObjectProperty), omitSamePlatforms = true) - appendSection("Inherited Companion Object Properties", node.inheritedCompanionObjectMembers(NodeKind.Property)) - appendSection("Companion Object Functions", node.membersOrGroupMembers(NodeKind.CompanionObjectFunction), omitSamePlatforms = true) - appendSection("Inherited Companion Object Functions", node.inheritedCompanionObjectMembers(NodeKind.Function)) - appendSection("Other members", node.members.filter { - it.kind !in setOf( - NodeKind.Class, - NodeKind.Interface, - NodeKind.Enum, - NodeKind.Object, - NodeKind.AnnotationClass, - NodeKind.Exception, - NodeKind.TypeAlias, - NodeKind.Constructor, - NodeKind.Property, - NodeKind.Package, - NodeKind.Function, - NodeKind.CompanionObjectProperty, - NodeKind.CompanionObjectFunction, - NodeKind.ExternalClass, - NodeKind.EnumItem, - NodeKind.AllTypes, - NodeKind.GroupNode - ) - }) - - val allExtensions = node.extensions - appendSection("Extension Properties", allExtensions.filter { it.kind == NodeKind.Property }) - appendSection("Extension Functions", allExtensions.filter { it.kind == NodeKind.Function }) - appendSection("Companion Object Extension Properties", allExtensions.filter { it.kind == NodeKind.CompanionObjectProperty }) - appendSection("Companion Object Extension Functions", allExtensions.filter { it.kind == NodeKind.CompanionObjectFunction }) - appendSection("Inheritors", - node.inheritors.filter { it.kind != NodeKind.EnumItem }) - - if (node.kind == NodeKind.Module) { - appendHeader(3) { to.append("Index") } - node.members(NodeKind.AllTypes).singleOrNull()?.let { allTypes -> - appendLink(link(node, allTypes) { "All Types" }) - } - } - } - - private fun appendSection(caption: String, members: List<DocumentationNode>, - sortMembers: Boolean = true, - omitSamePlatforms: Boolean = false, - platformsBasedOnMembers: Boolean = false) { - if (members.isEmpty()) return - - appendHeader(3) { appendText(caption) } - - val children = if (sortMembers) members.sortedBy { it.name.toLowerCase() } else members - val membersMap = children.groupBy { link(node, it) } - - - - appendTable("Name", "Summary") { - appendTableBody { - for ((memberLocation, membersList) in membersMap) { - val platforms = effectivePlatformsForMembers(membersList) -// val platforms = if (platformsBasedOnMembers) -// members.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms -// else -// elementPlatforms - - val summarized = computeSummarySignatures(membersList) - - appendIndexRow(platforms) { - appendTableCell { - if (summarized.platformPlacement == Summarized.PlatformPlacement.Row) { - appendPlatforms(platforms) - } - appendHeader(level = 4) { - // appendParagraph { - appendLink(memberLocation) - } - if (node.sinceKotlin != null) { - appendSinceKotlin(node.sinceKotlin.toString()) - } - - if (membersList.singleOrNull()?.sinceKotlin != null){ - appendSinceKotlinWrapped(membersList.single().sinceKotlin.toString()) - } -// } -// if (members.singleOrNull()?.kind != NodeKind.ExternalClass) { -// appendPlatforms(platforms) -// } -// } - } - appendTableCell { - appendSummarySignatures(summarized) - } - } - } - } - } - } - -// -// private fun platformsOfItems(items: List<DocumentationNode>, omitSamePlatforms: Boolean = true): Set<String> { -// if (items.all { it.kind != NodeKind.Package && it.kind != NodeKind.Module && it.kind != NodeKind.ExternalClass }) { -// return unionPlatformsOfItems(items) -// } -// -// val platforms = platformsOfItems(items) -// if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) { -// return platforms -// } -// return emptySet() -// } - - - - private fun computeSummarySignatures(items: List<DocumentationNode>): Summarized = - Summarized(items.groupBy { it.summary }.mapValues { (_, nodes) -> - val nodesToAppend = nodes.flatMap { if(it.kind == NodeKind.GroupNode) it.origins else listOf(it) } - - val summarySignature = languageService.summarizeSignatures(nodesToAppend) - if (summarySignature != null) { - mapOf(summarySignature to nodesToAppend) - } else { - nodesToAppend.groupBy { - languageService.render(it, RenderMode.SUMMARY) - } - } - }) - - - private fun appendSummarySignatures( - summarized: Summarized - ) { - for(summary in summarized.data) { - - appendAsSummaryGroup(summary.platforms) { - if (summarized.platformPlacement == Summarized.PlatformPlacement.Summary) { - appendPlatforms(summary.platforms) - } - appendContent(summary.content) - summary.signatures.subList(0, summary.signatures.size - 1).forEach { - appendSignatures( - it, - summarized.platformPlacement == Summarized.PlatformPlacement.Signature - ) - appendLine() - } - appendSignatures( - summary.signatures.last(), - summarized.platformPlacement == Summarized.PlatformPlacement.Signature - ) - } - - } - } - - private fun appendSignatures( - signature: Summarized.SummarizedNodes, - withPlatforms: Boolean - ) { - -// val platforms = if (platformsBasedOnMembers) -// items.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms -// else -// elementPlatforms - - - appendAsPlatformDependentBlock(signature.platforms) { - if (withPlatforms) { - appendPlatforms(signature.platforms) - } - appendAsSignature(signature.content) { - signature.content.appendSignature() - } - appendSoftLineBreak() - } - } - } - - private fun DocumentationNode.isClassLikeGroupNode(): Boolean { - if (kind != NodeKind.GroupNode) { - return false - } - - return origins.all { it.kind in NodeKind.classLike } - } - - - inner class AllTypesNodeBuilder(val node: DocumentationNode) - : PageBuilder(listOf(node)) { - - override fun build() { - appendContent(node.owner!!.summary) - appendHeader(3) { to.append("All Types") } - - appendTable("Name", "Summary") { - appendTableBody { - for (type in node.members) { - val platforms = effectivePlatformsForNode(type) - appendIndexRow(platforms) { - appendPlatforms(platforms) - if (type.kind == NodeKind.ExternalClass) { - val packageName = type.owner?.name - if (packageName != null) { - appendText(" (extensions in package $packageName)") - } - } - appendHeader(level = 5) { - appendLink(link(node, type) { - if (it.kind == NodeKind.ExternalClass) it.name else it.qualifiedName() - }) - } - - appendContent(type.summary) - } - } - } - } - } - } - - override fun appendNodes(nodes: Iterable<DocumentationNode>) { - val singleNode = nodes.singleOrNull() - when (singleNode?.kind) { - NodeKind.AllTypes -> AllTypesNodeBuilder(singleNode).build() - NodeKind.GroupNode -> GroupNodePageBuilder(singleNode).build() - null -> PageBuilder(nodes).build() - else -> SingleNodePageBuilder(singleNode).build() - } - } -} - -abstract class StructuredFormatService(val generator: NodeLocationAwareGenerator, - val languageService: LanguageService, - override val extension: String, - override final val linkExtension: String = extension) : FormatService { - -} - -typealias PlatformsData = Map<String, Set<DocumentationNode>> - -fun memberPlatforms(node: DocumentationNode): PlatformsData { - val members = when { - node.kind == NodeKind.GroupNode -> node.origins - node.kind in NodeKind.classLike -> emptyList() - node.kind in NodeKind.memberLike -> emptyList() - else -> node.members - } - - return members.map(::effectivePlatformsForNode).fold(mapOf(), ::mergePlatforms) -} - -fun mergePlatforms(a: PlatformsData, b: PlatformsData): PlatformsData { - val mutable = a.toMutableMap() - b.forEach { (name, declarations) -> - mutable.merge(name, declarations) { a, b -> a.union(b) } - } - return mutable -} - -fun effectivePlatformsForNode(node: DocumentationNode): PlatformsData { - val platforms = node.platforms + memberPlatforms(node).keys - return platforms.keysToMap { setOf(node) } -} - -fun effectivePlatformsForMembers(nodes: Collection<DocumentationNode>): PlatformsData { - return nodes.map { effectivePlatformsForNode(it) }.reduce(::mergePlatforms) -} - -fun mergeVersions(kotlinVersions: List<String>): String { - return kotlinVersions.distinct().min().orEmpty() -} - -fun effectiveSinceKotlinForNode(node: DocumentationNode, baseVersion: String = "1.0"): String { - val members = when { - node.kind == NodeKind.GroupNode -> node.origins - node.kind in NodeKind.classLike -> emptyList() - node.kind in NodeKind.memberLike -> emptyList() - else -> node.members - } - val newBase = node.sinceKotlin ?: baseVersion - val memberVersion = if (members.isNotEmpty()) effectiveSinceKotlinForNodes(members, newBase) else newBase - - return node.sinceKotlin ?: memberVersion -} - -fun effectiveSinceKotlinForNodes(nodes: Collection<DocumentationNode>, baseVersion: String = "1.0"): String { - val map = nodes.map { effectiveSinceKotlinForNode(it, baseVersion) } - return mergeVersions(map) -} - -fun samePlatforms(platformsPerNode: Collection<PlatformsData>): Boolean { - - val first = platformsPerNode.firstOrNull()?.keys ?: return true - return platformsPerNode.all { it.keys == first } -} - -fun locationHref( - from: Location, - to: DocumentationNode, - generator: NodeLocationAwareGenerator, - pathOnly: Boolean = false -): String { - val topLevelPage = to.references(RefKind.TopLevelPage).singleOrNull()?.to - if (topLevelPage != null) { - val signature = to.detailOrNull(NodeKind.Signature) - return from.relativePathTo( - generator.location(topLevelPage), - (signature?.name ?: to.name).takeUnless { pathOnly } - ) - } - return from.relativePathTo(generator.location(to)) -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/YamlOutlineService.kt b/core/src/main/kotlin/Formats/YamlOutlineService.kt deleted file mode 100644 index 3c92d8ff..00000000 --- a/core/src/main/kotlin/Formats/YamlOutlineService.kt +++ /dev/null @@ -1,26 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import java.io.File - -class YamlOutlineService @Inject constructor( - val generator: NodeLocationAwareGenerator, - val languageService: LanguageService -) : OutlineFormatService { - override fun getOutlineFileName(location: Location): File = File("${location.path}.yml") - - var outlineLevel = 0 - override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) { - val indent = " ".repeat(outlineLevel) - to.appendln("$indent- title: ${languageService.renderName(node)}") - to.appendln("$indent url: ${generator.relativePathToLocation(node.path.first(), node)}") - } - - override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { - val indent = " ".repeat(outlineLevel) - to.appendln("$indent content:") - outlineLevel++ - body() - outlineLevel-- - } -} diff --git a/core/src/main/kotlin/Generation/DocumentationMerger.kt b/core/src/main/kotlin/Generation/DocumentationMerger.kt deleted file mode 100644 index 53dc23a9..00000000 --- a/core/src/main/kotlin/Generation/DocumentationMerger.kt +++ /dev/null @@ -1,235 +0,0 @@ -package org.jetbrains.dokka.Generation - -import org.jetbrains.dokka.* - -class DocumentationMerger( - private val documentationModules: List<DocumentationModule>, - val logger: DokkaLogger -) { - private val producedNodeRefGraph: NodeReferenceGraph = NodeReferenceGraph() - private val signatureMap: Map<DocumentationNode, String> - private val oldToNewNodeMap: MutableMap<DocumentationNode, DocumentationNode> = mutableMapOf() - - init { - if (documentationModules.groupBy { it.name }.size > 1) { - throw IllegalArgumentException("Modules should have similar names: ${documentationModules.joinToString(", ") {it.name}}") - } - - signatureMap = documentationModules - .flatMap { it.nodeRefGraph.nodeMapView.entries } - .associate { (k, v) -> v to k } - - - documentationModules.map { it.nodeRefGraph } - .flatMap { it.references } - .forEach { producedNodeRefGraph.addReference(it) } - } - - private fun mergePackageReferences( - from: DocumentationNode, - packages: List<DocumentationReference> - ): List<DocumentationReference> { - val packagesByName = packages - .map { it.to } - .groupBy { it.name } - - val resultReferences = mutableListOf<DocumentationReference>() - for ((name, listOfPackages) in packagesByName) { - try { - val producedPackage = mergePackagesWithEqualNames(name, from, listOfPackages) - updatePendingReferences() - - resultReferences.add( - DocumentationReference(from, producedPackage, RefKind.Member) - ) - } catch (t: Throwable) { - val entries = listOfPackages.joinToString(",") { "references:${it.allReferences().size}" } - throw Error("Failed to merge package $name from $from with entries $entries. ${t.message}", t) - } - } - - return resultReferences - } - - private fun mergePackagesWithEqualNames( - name: String, - from: DocumentationNode, - packages: List<DocumentationNode> - ): DocumentationNode { - val mergedPackage = DocumentationNode(name, Content.Empty, NodeKind.Package) - - for (contentToAppend in packages.map { it.content }.distinct()) { - mergedPackage.updateContent { - for (otherChild in contentToAppend.children) { - children.add(otherChild) - } - } - } - - for (node in packages) { - oldToNewNodeMap[node] = mergedPackage - } - - val references = packages.flatMap { it.allReferences() } - val mergedReferences = mergeReferences(mergedPackage, references) - for (ref in mergedReferences) { - if (ref.kind == RefKind.Owner) { - continue - } - mergedPackage.addReference(ref) - } - - from.append(mergedPackage, RefKind.Member) - - return mergedPackage - } - - private fun mergeMemberGroupBy(it: DocumentationNode): String { - val signature = signatureMap[it] - - if (signature != null) { - return signature - } - - logger.error("Failed to find signature for $it in \n${it.allReferences().joinToString { "\n ${it.kind} ${it.to}" }}") - return "<ERROR>" - } - - private fun mergeMemberReferences( - from: DocumentationNode, - refs: List<DocumentationReference> - ): List<DocumentationReference> { - val membersBySignature: Map<String, List<DocumentationNode>> = refs.map { it.to } - .groupBy(this::mergeMemberGroupBy) - - val mergedMembers: MutableList<DocumentationReference> = mutableListOf() - for ((signature, members) in membersBySignature) { - val newNode = mergeMembersWithEqualSignature(signature, members) - - producedNodeRefGraph.register(signature, newNode) - updatePendingReferences() - from.append(newNode, RefKind.Member) - - mergedMembers.add(DocumentationReference(from, newNode, RefKind.Member)) - } - - return mergedMembers - } - - private fun mergeMembersWithEqualSignature( - signature: String, - nodes: List<DocumentationNode> - ): DocumentationNode { - require(nodes.isNotEmpty()) - - val singleNode = nodes.singleOrNull() - if (singleNode != null) { - singleNode.dropReferences { it.kind == RefKind.Owner } - return singleNode - } - - // Specialization processing - // Given (Common, JVM, JRE6, JS) and (JVM, JRE6) and (JVM, JRE7) - // Sorted: (JVM, JRE6), (JVM, JRE7), (Common, JVM, JRE6, JS) - // Should output: (JVM, JRE6), (JVM, JRE7), (Common, JS) - // Should not remove first platform - val nodesSortedByPlatformCount = nodes.sortedBy { it.platforms.size } - val allPlatforms = mutableSetOf<String>() - nodesSortedByPlatformCount.forEach { node -> - node.platforms - .filterNot { allPlatforms.add(it) } - .filter { it != node.platforms.first() } - .forEach { platform -> - node.dropReferences { it.kind == RefKind.Platform && it.to.name == platform } - } - } - - // TODO: Quick and dirty fox for merging extensions for external classes. Fix this probably in StructuredFormatService - // TODO: while refactoring documentation model - - val groupNode = if(nodes.first().kind == NodeKind.ExternalClass){ - DocumentationNode(nodes.first().name, Content.Empty, NodeKind.ExternalClass) - } else { - DocumentationNode(nodes.first().name, Content.Empty, NodeKind.GroupNode) - } - groupNode.appendTextNode(signature, NodeKind.Signature, RefKind.Detail) - - for (node in nodes) { - node.dropReferences { it.kind == RefKind.Owner } - groupNode.append(node, RefKind.Origin) - node.append(groupNode, RefKind.TopLevelPage) - - oldToNewNodeMap[node] = groupNode - } - - if (groupNode.kind == NodeKind.ExternalClass){ - val refs = nodes.flatMap { it.allReferences() }.filter { it.kind != RefKind.Owner && it.kind != RefKind.TopLevelPage } - refs.forEach { - if (it.kind != RefKind.Link) { - it.to.dropReferences { ref -> ref.kind == RefKind.Owner } - it.to.append(groupNode, RefKind.Owner) - } - groupNode.append(it.to, it.kind) - } - } - - // if nodes are classes, nested members should be also merged and - // inserted at the same level with class - if (nodes.all { it.kind in NodeKind.classLike }) { - val members = nodes.flatMap { it.allReferences() }.filter { it.kind == RefKind.Member } - val mergedMembers = mergeMemberReferences(groupNode, members) - - for (ref in mergedMembers) { - if (ref.kind == RefKind.Owner) { - continue - } - - groupNode.append(ref.to, RefKind.Member) - } - } - - return groupNode - } - - - private fun mergeReferences( - from: DocumentationNode, - refs: List<DocumentationReference> - ): List<DocumentationReference> { - val (refsToPackages, otherRefs) = refs.partition { it.to.kind == NodeKind.Package } - val mergedPackages = mergePackageReferences(from, refsToPackages) - - val (refsToMembers, refsNotToMembers) = otherRefs.partition { it.kind == RefKind.Member } - val mergedMembers = mergeMemberReferences(from, refsToMembers) - - return mergedPackages + mergedMembers + refsNotToMembers - } - - fun merge(): DocumentationModule { - val mergedDocumentationModule = DocumentationModule( - name = documentationModules.first().name, - content = documentationModules.first().content, - nodeRefGraph = producedNodeRefGraph - ) - - val refs = documentationModules.flatMap { - it.allReferences() - } - mergeReferences(mergedDocumentationModule, refs) - - return mergedDocumentationModule - } - - private fun updatePendingReferences() { - for (ref in producedNodeRefGraph.references) { - ref.lazyNodeFrom.update() - ref.lazyNodeTo.update() - } - } - - private fun NodeResolver.update() { - if (this is NodeResolver.Exact && exactNode in oldToNewNodeMap) { - exactNode = oldToNewNodeMap[exactNode]!! - } - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt deleted file mode 100644 index 90d7cfcc..00000000 --- a/core/src/main/kotlin/Generation/DokkaGenerator.kt +++ /dev/null @@ -1,223 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Guice -import com.google.inject.Injector -import com.intellij.openapi.util.Disposer -import com.intellij.openapi.vfs.VirtualFileManager -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiJavaFile -import com.intellij.psi.PsiManager -import org.jetbrains.dokka.Generation.DocumentationMerger -import org.jetbrains.dokka.Utilities.DokkaAnalysisModule -import org.jetbrains.dokka.Utilities.DokkaOutputModule -import org.jetbrains.dokka.Utilities.DokkaRunModule -import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity -import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.cli.common.messages.MessageRenderer -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.MemberDescriptor -import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer -import org.jetbrains.kotlin.resolve.TopDownAnalysisMode -import org.jetbrains.kotlin.utils.PathUtil -import java.io.File -import kotlin.system.measureTimeMillis - -class DokkaGenerator(val dokkaConfiguration: DokkaConfiguration, - val logger: DokkaLogger) { - - private val documentationModules: MutableList<DocumentationModule> = mutableListOf() - private val globalInjector = Guice.createInjector(DokkaRunModule(dokkaConfiguration)) - - - fun generate() = with(dokkaConfiguration) { - - - for (pass in passesConfigurations) { - val documentationModule = DocumentationModule(pass.moduleName) - appendSourceModule(pass, documentationModule) - documentationModules.add(documentationModule) - } - - val totalDocumentationModule = DocumentationMerger(documentationModules, logger).merge() - totalDocumentationModule.prepareForGeneration(dokkaConfiguration) - - val timeBuild = measureTimeMillis { - logger.info("Generating pages... ") - val outputInjector = globalInjector.createChildInjector(DokkaOutputModule(dokkaConfiguration, logger)) - val instance = outputInjector.getInstance(Generator::class.java) - instance.buildAll(totalDocumentationModule) - } - logger.info("done in ${timeBuild / 1000} secs") - } - - private fun appendSourceModule( - passConfiguration: DokkaConfiguration.PassConfiguration, - documentationModule: DocumentationModule - ) = with(passConfiguration) { - - val sourcePaths = passConfiguration.sourceRoots.map { it.path } - val environment = createAnalysisEnvironment(sourcePaths, passConfiguration) - - logger.info("Module: $moduleName") - logger.info("Output: ${File(dokkaConfiguration.outputDir)}") - logger.info("Sources: ${sourcePaths.joinToString()}") - logger.info("Classpath: ${environment.classpath.joinToString()}") - - logger.info("Analysing sources and libraries... ") - val startAnalyse = System.currentTimeMillis() - - val defaultPlatformAsList = passConfiguration.targets - val defaultPlatformsProvider = object : DefaultPlatformsProvider { - override fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String> { -// val containingFilePath = descriptor.sourcePsi()?.containingFile?.virtualFile?.canonicalPath -// ?.let { File(it).absolutePath } -// val sourceRoot = containingFilePath?.let { path -> sourceRoots.find { path.startsWith(it.path) } } - if (descriptor is MemberDescriptor && descriptor.isExpect) { - return defaultPlatformAsList.take(1) - } - return /*sourceRoot?.platforms ?: */defaultPlatformAsList - } - } - - val injector = globalInjector.createChildInjector( - DokkaAnalysisModule(environment, dokkaConfiguration, defaultPlatformsProvider, documentationModule.nodeRefGraph, passConfiguration, logger)) - - buildDocumentationModule(injector, documentationModule, { isNotSample(it, passConfiguration.samples) }, includes) - - val timeAnalyse = System.currentTimeMillis() - startAnalyse - logger.info("done in ${timeAnalyse / 1000} secs") - - Disposer.dispose(environment) - } - - fun createAnalysisEnvironment( - sourcePaths: List<String>, - passConfiguration: DokkaConfiguration.PassConfiguration - ): AnalysisEnvironment { - val environment = AnalysisEnvironment(DokkaMessageCollector(logger), passConfiguration.analysisPlatform) - - environment.apply { - if (analysisPlatform == Platform.jvm) { - addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre()) - } - // addClasspath(PathUtil.getKotlinPathsForCompiler().getRuntimePath()) - for (element in passConfiguration.classpath) { - addClasspath(File(element)) - } - - addSources(sourcePaths) - addSources(passConfiguration.samples) - - loadLanguageVersionSettings(passConfiguration.languageVersion, passConfiguration.apiVersion) - } - - return environment - } - - private fun isNotSample(file: PsiFile, samples: List<String>): Boolean { - val sourceFile = File(file.virtualFile!!.path) - return samples.none { sample -> - val canonicalSample = File(sample).canonicalPath - val canonicalSource = sourceFile.canonicalPath - canonicalSource.startsWith(canonicalSample) - } - } -} - -class DokkaMessageCollector(val logger: DokkaLogger) : MessageCollector { - override fun clear() { - seenErrors = false - } - - private var seenErrors = false - - override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) { - if (severity == CompilerMessageSeverity.ERROR) { - seenErrors = true - } - logger.error(MessageRenderer.PLAIN_FULL_PATHS.render(severity, message, location)) - } - - override fun hasErrors() = seenErrors -} - -fun buildDocumentationModule(injector: Injector, - documentationModule: DocumentationModule, - filesToDocumentFilter: (PsiFile) -> Boolean = { file -> true }, - includes: List<String> = listOf()) { - - val coreEnvironment = injector.getInstance(KotlinCoreEnvironment::class.java) - val fragmentFiles = coreEnvironment.getSourceFiles().filter(filesToDocumentFilter) - - val resolutionFacade = injector.getInstance(DokkaResolutionFacade::class.java) - val analyzer = resolutionFacade.getFrontendService(LazyTopDownAnalyzer::class.java) - analyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, fragmentFiles) - - val fragments = fragmentFiles.mapNotNull { resolutionFacade.resolveSession.getPackageFragment(it.packageFqName) } - .distinct() - - val packageDocs = injector.getInstance(PackageDocs::class.java) - for (include in includes) { - packageDocs.parse(include, fragments) - } - if (documentationModule.content.isEmpty()) { - documentationModule.updateContent { - for (node in packageDocs.moduleContent.children) { - append(node) - } - } - } - - parseJavaPackageDocs(packageDocs, coreEnvironment) - - 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) - with(injector.getInstance(JavaDocumentationBuilder::class.java)) { - javaFiles.map { appendFile(it, documentationModule, packageDocs.packageContent) } - } -} - -fun parseJavaPackageDocs(packageDocs: PackageDocs, coreEnvironment: KotlinCoreEnvironment) { - val contentRoots = coreEnvironment.configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) - ?.filterIsInstance<JavaSourceRoot>() - ?.map { it.file } - ?: listOf() - contentRoots.forEach { root -> - root.walkTopDown().filter { it.name == "overview.html" }.forEach { - packageDocs.parseJava(it.path, it.relativeTo(root).parent.replace("/", ".")) - } - } -} - - -fun KotlinCoreEnvironment.getJavaSourceFiles(): List<PsiJavaFile> { - val sourceRoots = configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) - ?.filterIsInstance<JavaSourceRoot>() - ?.map { it.file } - ?: listOf() - - val result = arrayListOf<PsiJavaFile>() - val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file") - sourceRoots.forEach { sourceRoot -> - sourceRoot.absoluteFile.walkTopDown().forEach { - val vFile = localFileSystem.findFileByPath(it.path) - if (vFile != null) { - val psiFile = PsiManager.getInstance(project).findFile(vFile) - if (psiFile is PsiJavaFile) { - result.add(psiFile) - } - } - } - } - return result -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Generation/FileGenerator.kt b/core/src/main/kotlin/Generation/FileGenerator.kt deleted file mode 100644 index ee2c068e..00000000 --- a/core/src/main/kotlin/Generation/FileGenerator.kt +++ /dev/null @@ -1,108 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import java.io.File -import java.io.IOException -import java.io.PrintWriter -import java.io.StringWriter - -class FileGenerator @Inject constructor(@Named("outputDir") override val root: File) : NodeLocationAwareGenerator { - - @set:Inject(optional = true) var outlineService: OutlineFormatService? = null - @set:Inject(optional = true) lateinit var formatService: FormatService - @set:Inject(optional = true) lateinit var dokkaConfiguration: DokkaConfiguration - @set:Inject(optional = true) var packageListService: PackageListService? = null - - private val createdFiles = mutableMapOf<File, List<String>>() - - private fun File.writeFileAndAssert(context: String, action: (File) -> Unit) { - //TODO: there is a possible refactoring to drop FileLocation - //TODO: aad File from API, Location#path. - //TODO: turn [Location] into a final class, - //TODO: Use [Location] all over the place without full - //TODO: reference to the real target path, - //TODO: it opens the way to safely track all files created - //TODO: to make sure no files were overwritten by mistake - //TODO: also, the NodeLocationAwareGenerator should be removed - - val writes = createdFiles.getOrDefault(this, listOf()) + context - createdFiles[this] = writes - if (writes.size > 1) { - println("ERROR. An attempt to write ${this.relativeTo(root)} several times!") - return - } - - try { - parentFile?.mkdirsOrFail() - action(this) - } catch (e : Throwable) { - println("Failed to write $this. ${e.message}") - e.printStackTrace() - } - } - - private fun File.mkdirsOrFail() { - if (!mkdirs() && !exists()) { - throw IOException("Failed to create directory $this") - } - } - - override fun location(node: DocumentationNode): FileLocation { - return FileLocation(fileForNode(node, formatService.linkExtension)) - } - - private fun fileForNode(node: DocumentationNode, extension: String = ""): File { - return File(root, relativePathToNode(node)).appendExtension(extension) - } - - private fun locationWithoutExtension(node: DocumentationNode): FileLocation { - return FileLocation(fileForNode(node)) - } - - override fun buildPages(nodes: Iterable<DocumentationNode>) { - - for ((file, items) in nodes.groupBy { fileForNode(it, formatService.extension) }) { - file.writeFileAndAssert("pages") { it -> - it.writeText(formatService.format(location(items.first()), items)) - } - - buildPages(items.filterNot { it.kind == NodeKind.AllTypes }.flatMap { it.members }) - } - } - - override fun buildOutlines(nodes: Iterable<DocumentationNode>) { - val outlineService = this.outlineService ?: return - for ((location, items) in nodes.groupBy { locationWithoutExtension(it) }) { - outlineService.getOutlineFileName(location).writeFileAndAssert("outlines") { file -> - file.writeText(outlineService.formatOutline(location, items)) - } - } - } - - override fun buildSupportFiles() { - formatService.enumerateSupportFiles { resource, targetPath -> - File(root, relativePathToNode(listOf(targetPath), false)).writeFileAndAssert("support files") { file -> - file.outputStream().use { - javaClass.getResourceAsStream(resource).copyTo(it) - } - } - } - } - - override fun buildPackageList(nodes: Iterable<DocumentationNode>) { - if (packageListService == null) return - - for (module in nodes) { - - val moduleRoot = location(module).file.parentFile - val packageListFile = File(moduleRoot, "package-list") - - val text = "\$dokka.format:${dokkaConfiguration.format}\n" + packageListService!!.formatPackageList(module as DocumentationModule) - - packageListFile.writeFileAndAssert("packages-list") { file -> - file.writeText(text) - } - } - } -} diff --git a/core/src/main/kotlin/Generation/Generator.kt b/core/src/main/kotlin/Generation/Generator.kt deleted file mode 100644 index 23286e29..00000000 --- a/core/src/main/kotlin/Generation/Generator.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -interface Generator { - fun buildPages(nodes: Iterable<DocumentationNode>) - fun buildOutlines(nodes: Iterable<DocumentationNode>) - fun buildSupportFiles() - fun buildPackageList(nodes: Iterable<DocumentationNode>) -} - -fun Generator.buildAll(nodes: Iterable<DocumentationNode>) { - buildPages(nodes) - buildOutlines(nodes) - buildSupportFiles() - buildPackageList(nodes) -} - -fun Generator.buildPage(node: DocumentationNode): Unit = buildPages(listOf(node)) - -fun Generator.buildOutline(node: DocumentationNode): Unit = buildOutlines(listOf(node)) - -fun Generator.buildAll(node: DocumentationNode): Unit = buildAll(listOf(node)) - - -interface NodeLocationAwareGenerator: Generator { - fun location(node: DocumentationNode): Location - val root: File -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt deleted file mode 100644 index d6743e60..00000000 --- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt +++ /dev/null @@ -1,361 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.intellij.openapi.util.text.StringUtil -import com.intellij.psi.* -import com.intellij.psi.impl.JavaConstantExpressionEvaluator -import com.intellij.psi.util.InheritanceUtil -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation -import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration -import org.jetbrains.kotlin.asJava.elements.KtLightElement -import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtModifierListOwner - -fun getSignature(element: PsiElement?) = when(element) { - is PsiPackage -> element.qualifiedName - is PsiClass -> element.qualifiedName - is PsiField -> element.containingClass!!.qualifiedName + "$" + element.name - is PsiMethod -> - methodSignature(element) - is PsiParameter -> { - val method = (element.parent.parent as PsiMethod) - methodSignature(method) - } - else -> null -} - -private fun methodSignature(method: PsiMethod): String { - return method.containingClass?.qualifiedName + "$" + method.name + "(" + - method.parameterList.parameters.map { it.type.typeSignature() }.joinToString(",") + ")" -} - -private fun PsiType.typeSignature(): String = when(this) { - is PsiArrayType -> "Array((${componentType.typeSignature()}))" - is PsiPrimitiveType -> "kotlin." + canonicalText.capitalize() - else -> mapTypeName(this) -} - -private fun mapTypeName(psiType: PsiType): String = when (psiType) { - is PsiPrimitiveType -> psiType.canonicalText - is PsiClassType -> psiType.resolve()?.qualifiedName ?: psiType.className - is PsiEllipsisType -> mapTypeName(psiType.componentType) - is PsiArrayType -> "kotlin.Array" - else -> psiType.canonicalText -} - -interface JavaDocumentationBuilder { - fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) -} - -class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { - private val passConfiguration: DokkaConfiguration.PassConfiguration - private val refGraph: NodeReferenceGraph - private val docParser: JavaDocumentationParser - - @Inject constructor( - passConfiguration: DokkaConfiguration.PassConfiguration, - refGraph: NodeReferenceGraph, - logger: DokkaLogger, - signatureProvider: ElementSignatureProvider, - externalDocumentationLinkResolver: ExternalDocumentationLinkResolver - ) { - this.passConfiguration = passConfiguration - this.refGraph = refGraph - this.docParser = JavadocParser(refGraph, logger, signatureProvider, externalDocumentationLinkResolver) - } - - constructor(passConfiguration: DokkaConfiguration.PassConfiguration, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) { - this.passConfiguration = passConfiguration - this.refGraph = refGraph - this.docParser = docParser - } - - override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) { - if (skipFile(file) || file.classes.all { skipElement(it) }) { - return - } - val packageNode = findOrCreatePackageNode(module, file.packageName, emptyMap(), refGraph) - appendClasses(packageNode, file.classes) - } - - fun appendClasses(packageNode: DocumentationNode, classes: Array<PsiClass>) { - packageNode.appendChildren(classes) { build() } - } - - fun register(element: PsiElement, node: DocumentationNode) { - val signature = getSignature(element) - if (signature != null) { - refGraph.register(signature, node) - } - } - - fun link(node: DocumentationNode, element: PsiElement?) { - val qualifiedName = getSignature(element) - if (qualifiedName != null) { - refGraph.link(node, qualifiedName, RefKind.Link) - } - } - - fun link(element: PsiElement?, node: DocumentationNode, kind: RefKind) { - val qualifiedName = getSignature(element) - if (qualifiedName != null) { - refGraph.link(qualifiedName, node, kind) - } - } - - fun nodeForElement(element: PsiNamedElement, - kind: NodeKind, - name: String = element.name ?: "<anonymous>", - register: Boolean = false): DocumentationNode { - val (docComment, deprecatedContent) = docParser.parseDocumentation(element) - val node = DocumentationNode(name, docComment, kind) - if (register) register(element, node) - if (element is PsiModifierListOwner) { - node.appendModifiers(element) - val modifierList = element.modifierList - if (modifierList != null) { - modifierList.annotations.filter { !ignoreAnnotation(it) }.forEach { - val annotation = it.build() - node.append(annotation, - if (it.qualifiedName == "java.lang.Deprecated") RefKind.Deprecation else RefKind.Annotation) - } - } - } - if (deprecatedContent != null) { - val deprecationNode = DocumentationNode("", deprecatedContent, NodeKind.Modifier) - node.append(deprecationNode, RefKind.Deprecation) - } - if (element is PsiDocCommentOwner && element.isDeprecated && node.deprecation == null) { - val deprecationNode = DocumentationNode("", Content.of(ContentText("Deprecated")), NodeKind.Modifier) - node.append(deprecationNode, RefKind.Deprecation) - } - return node - } - - fun ignoreAnnotation(annotation: PsiAnnotation) = when(annotation.qualifiedName) { - "java.lang.SuppressWarnings" -> true - else -> false - } - - fun <T : Any> DocumentationNode.appendChildren(elements: Array<T>, - kind: RefKind = RefKind.Member, - buildFn: T.() -> DocumentationNode) { - elements.forEach { - if (!skipElement(it)) { - append(it.buildFn(), kind) - } - } - } - - private fun skipFile(javaFile: PsiJavaFile): Boolean = passConfiguration.effectivePackageOptions(javaFile.packageName).suppress - - private fun skipElement(element: Any) = - skipElementByVisibility(element) || - hasSuppressDocTag(element) || - skipElementBySuppressedFiles(element) - - private fun skipElementByVisibility(element: Any): Boolean = - element is PsiModifierListOwner && - element !is PsiParameter && - !(passConfiguration.effectivePackageOptions((element.containingFile as? PsiJavaFile)?.packageName ?: "").includeNonPublic) && - (element.hasModifierProperty(PsiModifier.PRIVATE) || - element.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) || - element.isInternal()) - - private fun skipElementBySuppressedFiles(element: Any): Boolean = - element is PsiElement && element.containingFile.virtualFile?.path in passConfiguration.suppressedFiles - - private fun PsiElement.isInternal(): Boolean { - val ktElement = (this as? KtLightElement<*, *>)?.kotlinOrigin ?: return false - return (ktElement as? KtModifierListOwner)?.hasModifier(KtTokens.INTERNAL_KEYWORD) ?: false - } - - fun <T : Any> DocumentationNode.appendMembers(elements: Array<T>, buildFn: T.() -> DocumentationNode) = - appendChildren(elements, RefKind.Member, buildFn) - - fun <T : Any> DocumentationNode.appendDetails(elements: Array<T>, buildFn: T.() -> DocumentationNode) = - appendChildren(elements, RefKind.Detail, buildFn) - - fun PsiClass.build(): DocumentationNode { - val kind = when { - isAnnotationType -> NodeKind.AnnotationClass - isInterface -> NodeKind.Interface - isEnum -> NodeKind.Enum - isException() -> NodeKind.Exception - else -> NodeKind.Class - } - val node = nodeForElement(this, kind, register = isAnnotationType) - superTypes.filter { !ignoreSupertype(it) }.forEach { - node.appendType(it, NodeKind.Supertype) - val superClass = it.resolve() - if (superClass != null) { - link(superClass, node, RefKind.Inheritor) - } - } - - var methodsAndConstructors = methods - if (constructors.isEmpty()) { - // Having no constructor represents a class that only has an implicit/default constructor - // so we create one synthetically for documentation - val factory = JavaPsiFacade.getElementFactory(this.project) - methodsAndConstructors += factory.createMethodFromText("public $name() {}", this) - } - node.appendDetails(typeParameters) { build() } - node.appendMembers(methodsAndConstructors) { build() } - node.appendMembers(fields) { build() } - node.appendMembers(innerClasses) { build() } - register(this, node) - return node - } - - fun PsiClass.isException() = InheritanceUtil.isInheritor(this, "java.lang.Throwable") - - fun ignoreSupertype(psiType: PsiClassType): Boolean = - psiType.isClass("java.lang.Enum") || psiType.isClass("java.lang.Object") - - fun PsiClassType.isClass(qName: String): Boolean { - val shortName = qName.substringAfterLast('.') - if (className == shortName) { - val psiClass = resolve() - return psiClass?.qualifiedName == qName - } - return false - } - - fun PsiField.build(): DocumentationNode { - val node = nodeForElement(this, nodeKind()) - node.appendType(type) - - node.appendConstantValueIfAny(this) - register(this, node) - return node - } - - private fun DocumentationNode.appendConstantValueIfAny(field: PsiField) { - val modifierList = field.modifierList ?: return - val initializer = field.initializer ?: return - if (modifierList.hasExplicitModifier(PsiModifier.FINAL) && - modifierList.hasExplicitModifier(PsiModifier.STATIC)) { - val value = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false) - val text = when(value) { - null -> return // No value found - is String -> - "\"" + StringUtil.escapeStringCharacters(value) + "\"" - else -> value.toString() - } - append(DocumentationNode(text, Content.Empty, NodeKind.Value), RefKind.Detail) - } - } - - private fun PsiField.nodeKind(): NodeKind = when { - this is PsiEnumConstant -> NodeKind.EnumItem - else -> NodeKind.Field - } - - fun PsiMethod.build(): DocumentationNode { - val node = nodeForElement(this, nodeKind(), - if (isConstructor) "<init>" else name) - - if (!isConstructor) { - node.appendType(returnType) - } - node.appendDetails(parameterList.parameters) { build() } - node.appendDetails(typeParameters) { build() } - register(this, node) - return node - } - - private fun PsiMethod.nodeKind(): NodeKind = when { - isConstructor -> NodeKind.Constructor - else -> NodeKind.Function - } - - fun PsiParameter.build(): DocumentationNode { - val node = nodeForElement(this, NodeKind.Parameter) - node.appendType(type) - if (type is PsiEllipsisType) { - node.appendTextNode("vararg", NodeKind.Modifier, RefKind.Detail) - } - return node - } - - fun PsiTypeParameter.build(): DocumentationNode { - val node = nodeForElement(this, NodeKind.TypeParameter) - extendsListTypes.forEach { node.appendType(it, NodeKind.UpperBound) } - implementsListTypes.forEach { node.appendType(it, NodeKind.UpperBound) } - return node - } - - fun DocumentationNode.appendModifiers(element: PsiModifierListOwner) { - val modifierList = element.modifierList ?: return - - PsiModifier.MODIFIERS.forEach { - if (modifierList.hasExplicitModifier(it)) { - appendTextNode(it, NodeKind.Modifier) - } - } - } - - fun DocumentationNode.appendType(psiType: PsiType?, kind: NodeKind = NodeKind.Type) { - if (psiType == null) { - return - } - append(psiType.build(kind), RefKind.Detail) - } - - fun PsiType.build(kind: NodeKind = NodeKind.Type): DocumentationNode { - val name = mapTypeName(this) - val node = DocumentationNode(name, Content.Empty, kind) - if (this is PsiClassType) { - node.appendDetails(parameters) { build(NodeKind.Type) } - link(node, resolve()) - } - if (this is PsiArrayType && this !is PsiEllipsisType) { - node.append(componentType.build(NodeKind.Type), RefKind.Detail) - } - return node - } - - private fun lookupOrBuildClass(psiClass: PsiClass): DocumentationNode { - val existing = refGraph.lookup(getSignature(psiClass)!!) - if (existing != null) return existing - val new = psiClass.build() - val packageNode = findOrCreatePackageNode(null, (psiClass.containingFile as PsiJavaFile).packageName, emptyMap(), refGraph) - packageNode.append(new, RefKind.Member) - return new - } - - fun PsiAnnotation.build(): DocumentationNode { - - val original = when (this) { - is KtLightAbstractAnnotation -> clsDelegate - else -> this - } - val node = DocumentationNode(qualifiedName?.substringAfterLast(".") ?: "<?>", Content.Empty, NodeKind.Annotation) - val psiClass = original.nameReferenceElement?.resolve() as? PsiClass - if (psiClass != null && psiClass.isAnnotationType) { - node.append(lookupOrBuildClass(psiClass), RefKind.Link) - } - parameterList.attributes.forEach { - val parameter = DocumentationNode(it.name ?: "value", Content.Empty, NodeKind.Parameter) - val value = it.value - if (value != null) { - val valueText = (value as? PsiLiteralExpression)?.value as? String ?: value.text - val valueNode = DocumentationNode(valueText, Content.Empty, NodeKind.Value) - parameter.append(valueNode, RefKind.Detail) - } - node.append(parameter, RefKind.Detail) - } - return node - } -} - -fun hasSuppressDocTag(element: Any?): Boolean { - val declaration = (element as? KtLightDeclaration<*, *>)?.kotlinOrigin ?: return false - return PsiTreeUtil.findChildrenOfType(declaration.docComment, KDocTag::class.java).any { it.knownTag == KDocKnownTag.SUPPRESS } -} - diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt deleted file mode 100644 index 318bad28..00000000 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ /dev/null @@ -1,401 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.* -import com.intellij.psi.impl.source.tree.JavaDocElementType -import com.intellij.psi.javadoc.* -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.utils.keysToMap -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import org.jsoup.nodes.Node -import org.jsoup.nodes.TextNode -import java.net.URI - -data class JavadocParseResult(val content: Content, val deprecatedContent: Content?) { - companion object { - val Empty = JavadocParseResult(Content.Empty, null) - } -} - -interface JavaDocumentationParser { - fun parseDocumentation(element: PsiNamedElement): JavadocParseResult -} - -class JavadocParser( - private val refGraph: NodeReferenceGraph, - private val logger: DokkaLogger, - private val signatureProvider: ElementSignatureProvider, - private val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver -) : JavaDocumentationParser { - - private fun ContentSection.appendTypeElement(signature: String, selector: (DocumentationNode) -> DocumentationNode?) { - append(LazyContentBlock { - val node = refGraph.lookupOrWarn(signature, logger)?.let(selector) ?: return@LazyContentBlock emptyList() - listOf(ContentBlock().apply { - append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY)) - symbol(":") - text(" ") - }) - }) - } - - override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { - val docComment = (element as? PsiDocCommentOwner)?.docComment ?: return JavadocParseResult.Empty - val result = MutableContent() - var deprecatedContent: Content? = null - - val nodes = convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }, element) - val firstParagraphContents = nodes.takeWhile { it !is ContentParagraph } - val firstParagraph = ContentParagraph() - if (firstParagraphContents.isNotEmpty()) { - firstParagraphContents.forEach { firstParagraph.append(it) } - result.append(firstParagraph) - } - - result.appendAll(nodes.drop(firstParagraphContents.size)) - - if (element is PsiMethod) { - val tagsByName = element.searchInheritedTags() - for ((tagName, tags) in tagsByName) { - for ((tag, context) in tags) { - val section = result.addSection(javadocSectionDisplayName(tagName), tag.getSubjectName()) - val signature = signatureProvider.signature(element) - when (tagName) { - "param" -> { - section.appendTypeElement(signature) { - it.details - .find { node -> node.kind == NodeKind.Parameter && node.name == tag.getSubjectName() } - ?.detailOrNull(NodeKind.Type) - } - } - "return" -> { - section.appendTypeElement(signature) { it.detailOrNull(NodeKind.Type) } - } - } - section.appendAll(convertJavadocElements(tag.contentElements(), context)) - } - } - } - - docComment.tags.forEach { tag -> - when (tag.name) { - "see" -> result.convertSeeTag(tag) - "deprecated" -> { - deprecatedContent = Content().apply { - appendAll(convertJavadocElements(tag.contentElements(), element)) - } - } - in tagsToInherit -> {} - else -> { - val subjectName = tag.getSubjectName() - val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) - - section.appendAll(convertJavadocElements(tag.contentElements(), element)) - } - } - } - return JavadocParseResult(result, deprecatedContent) - } - - private val tagsToInherit = setOf("param", "return", "throws") - - private data class TagWithContext(val tag: PsiDocTag, val context: PsiNamedElement) - - private fun PsiMethod.searchInheritedTags(): Map<String, Collection<TagWithContext>> { - - val output = tagsToInherit.keysToMap { mutableMapOf<String?, TagWithContext>() } - - fun recursiveSearch(methods: Array<PsiMethod>) { - for (method in methods) { - recursiveSearch(method.findSuperMethods()) - } - for (method in methods) { - for (tag in method.docComment?.tags.orEmpty()) { - if (tag.name in tagsToInherit) { - output[tag.name]!![tag.getSubjectName()] = TagWithContext(tag, method) - } - } - } - } - - recursiveSearch(arrayOf(this)) - return output.mapValues { it.value.values } - } - - - private fun PsiDocTag.contentElements(): Iterable<PsiElement> { - val tagValueElements = children - .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME } - .dropWhile { it is PsiWhiteSpace } - .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS } - return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements - } - - private fun convertJavadocElements(elements: Iterable<PsiElement>, element: PsiNamedElement): List<ContentNode> { - val doc = Jsoup.parse(expandAllForElements(elements, element)) - return doc.body().childNodes().mapNotNull { - convertHtmlNode(it) - } - } - - private fun ContentBlock.appendAll(nodes: List<ContentNode>) { - nodes.forEach { append(it) } - } - - private fun expandAllForElements(elements: Iterable<PsiElement>, element: PsiNamedElement): String { - val htmlBuilder = StringBuilder() - elements.forEach { - if (it is PsiInlineDocTag) { - htmlBuilder.append(convertInlineDocTag(it, element)) - } else { - htmlBuilder.append(it.text) - } - } - return htmlBuilder.toString().trim() - } - - private fun convertHtmlNode(node: Node, insidePre: Boolean = false): ContentNode? { - if (node is TextNode) { - val text = if (insidePre) node.wholeText else node.text() - return ContentText(text) - } else if (node is Element) { - val childBlock = createBlock(node, insidePre) - - node.childNodes().forEach { - val child = convertHtmlNode(it, insidePre || childBlock is ContentBlockCode) - if (child != null) { - childBlock.append(child) - } - } - return childBlock - } - return null - } - - private fun createBlock(element: Element, insidePre: Boolean): ContentBlock = when (element.tagName()) { - "p" -> ContentParagraph() - "b", "strong" -> ContentStrong() - "i", "em" -> ContentEmphasis() - "s", "del" -> ContentStrikethrough() - "code" -> if (insidePre) ContentBlock() else ContentCode() - "pre" -> ContentBlockCode() - "ul" -> ContentUnorderedList() - "ol" -> ContentOrderedList() - "li" -> ContentListItem() - "a" -> createLink(element) - "br" -> ContentBlock().apply { hardLineBreak() } - else -> ContentBlock() - } - - private fun createLink(element: Element): ContentBlock { - return when { - element.hasAttr("docref") -> { - val docref = element.attr("docref") - ContentNodeLazyLink(docref) { refGraph.lookupOrWarn(docref, logger)} - } - element.hasAttr("href") -> { - val href = element.attr("href") - - val uri = try { - URI(href) - } catch (_: Exception) { - null - } - - if (uri?.isAbsolute == false) { - ContentLocalLink(href) - } else { - ContentExternalLink(href) - } - } - element.hasAttr("name") -> { - ContentBookmark(element.attr("name")) - } - else -> ContentBlock() - } - } - - private fun MutableContent.convertSeeTag(tag: PsiDocTag) { - val linkElement = tag.linkElement() ?: return - val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null) - - val valueElement = tag.referenceElement() - val externalLink = resolveExternalLink(valueElement) - val text = ContentText(linkElement.text) - - val linkSignature by lazy { resolveInternalLink(valueElement) } - val node = when { - externalLink != null -> { - val linkNode = ContentExternalLink(externalLink) - linkNode.append(text) - linkNode - } - linkSignature != null -> { - val linkNode = - ContentNodeLazyLink( - (tag.valueElement ?: linkElement).text - ) { refGraph.lookupOrWarn(linkSignature!!, logger) } - linkNode.append(text) - linkNode - } - else -> text - } - seeSection.append(node) - } - - private fun convertInlineDocTag(tag: PsiInlineDocTag, element: PsiNamedElement) = when (tag.name) { - "link", "linkplain" -> { - val valueElement = tag.referenceElement() - val externalLink = resolveExternalLink(valueElement) - val linkSignature by lazy { resolveInternalLink(valueElement) } - if (externalLink != null || linkSignature != null) { - val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text - val linkTarget = if (externalLink != null) "href=\"$externalLink\"" else "docref=\"$linkSignature\"" - val link = "<a $linkTarget>${labelText.htmlEscape()}</a>" - if (tag.name == "link") "<code>$link</code>" else link - } else if (valueElement != null) { - valueElement.text - } else { - "" - } - } - "code", "literal" -> { - val text = StringBuilder() - tag.dataElements.forEach { text.append(it.text) } - val escaped = text.toString().trimStart().htmlEscape() - if (tag.name == "code") "<code>$escaped</code>" else escaped - } - "inheritDoc" -> { - val result = (element as? PsiMethod)?.let { - // @{inheritDoc} is only allowed on functions - val parent = tag.parent - when (parent) { - is PsiDocComment -> element.findSuperDocCommentOrWarn() - is PsiDocTag -> element.findSuperDocTagOrWarn(parent) - else -> null - } - } - result ?: tag.text - } - else -> tag.text - } - - private fun PsiDocTag.referenceElement(): PsiElement? = - linkElement()?.let { - if (it.node.elementType == JavaDocElementType.DOC_REFERENCE_HOLDER) { - PsiTreeUtil.findChildOfType(it, PsiJavaCodeReferenceElement::class.java) - } else { - it - } - } - - private fun PsiDocTag.linkElement(): PsiElement? = - valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } - - private fun resolveExternalLink(valueElement: PsiElement?): String? { - val target = valueElement?.reference?.resolve() - if (target != null) { - return externalDocumentationLinkResolver.buildExternalDocumentationLink(target) - } - return null - } - - private fun resolveInternalLink(valueElement: PsiElement?): String? { - val target = valueElement?.reference?.resolve() - if (target != null) { - return signatureProvider.signature(target) - } - return null - } - - fun PsiDocTag.getSubjectName(): String? { - if (name == "param" || name == "throws" || name == "exception") { - return valueElement?.text - } - return null - } - - private fun PsiMethod.findSuperDocCommentOrWarn(): String { - val method = findFirstSuperMethodWithDocumentation(this) - if (method != null) { - val descriptionElements = method.docComment?.descriptionElements?.dropWhile { - it.text.trim().isEmpty() - } ?: return "" - - return expandAllForElements(descriptionElements, method) - } - logger.warn("No docs found on supertype with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}") - return "" - } - - - private fun PsiMethod.findSuperDocTagOrWarn(elementToExpand: PsiDocTag): String { - val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, this) - - if (result != null) { - val (method, tag) = result - - val contentElements = tag.contentElements().dropWhile { it.text.trim().isEmpty() } - - val expandedString = expandAllForElements(contentElements, method) - - return expandedString - } - logger.warn("No docs found on supertype for @${elementToExpand.name} ${elementToExpand.getSubjectName()} with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}") - return "" - } - - private fun findFirstSuperMethodWithDocumentation(current: PsiMethod): PsiMethod? { - val superMethods = current.findSuperMethods() - for (method in superMethods) { - val docs = method.docComment?.descriptionElements?.dropWhile { it.text.trim().isEmpty() } - if (!docs.isNullOrEmpty()) { - return method - } - } - for (method in superMethods) { - val result = findFirstSuperMethodWithDocumentation(method) - if (result != null) { - return result - } - } - - return null - } - - private fun findFirstSuperMethodWithDocumentationforTag(elementToExpand: PsiDocTag, current: PsiMethod): Pair<PsiMethod, PsiDocTag>? { - val superMethods = current.findSuperMethods() - val mappedFilteredTags = superMethods.map { - it to it.docComment?.tags?.filter { it.name == elementToExpand.name } - } - - for ((method, tags) in mappedFilteredTags) { - tags ?: continue - for (tag in tags) { - val (tagSubject, elementSubject) = when (tag.name) { - "throws" -> { - // match class names only for throws, ignore possibly fully qualified path - // TODO: Always match exactly here - tag.getSubjectName()?.split(".")?.last() to elementToExpand.getSubjectName()?.split(".")?.last() - } - else -> { - tag.getSubjectName() to elementToExpand.getSubjectName() - } - } - - if (tagSubject == elementSubject) { - return method to tag - } - } - } - - for (method in superMethods) { - val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, method) - if (result != null) { - return result - } - } - return null - } - -} diff --git a/core/src/main/kotlin/Kotlin/ContentBuilder.kt b/core/src/main/kotlin/Kotlin/ContentBuilder.kt deleted file mode 100644 index 573b41b6..00000000 --- a/core/src/main/kotlin/Kotlin/ContentBuilder.kt +++ /dev/null @@ -1,188 +0,0 @@ -package org.jetbrains.dokka - -import org.intellij.markdown.MarkdownElementTypes -import org.intellij.markdown.MarkdownTokenTypes -import org.intellij.markdown.html.entities.EntityConverter -import org.intellij.markdown.parser.LinkMap -import java.util.* - -class LinkResolver(private val linkMap: LinkMap, private val contentFactory: (String) -> ContentBlock) { - fun getLinkInfo(refLabel: String) = linkMap.getLinkInfo(refLabel) - fun resolve(href: String): ContentBlock = contentFactory(href) -} - -fun buildContent(tree: MarkdownNode, linkResolver: LinkResolver, inline: Boolean = false): MutableContent { - val result = MutableContent() - if (inline) { - buildInlineContentTo(tree, result, linkResolver) - } else { - buildContentTo(tree, result, linkResolver) - } - return result -} - -fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) { -// println(tree.toTestString()) - val nodeStack = ArrayDeque<ContentBlock>() - nodeStack.push(target) - - tree.visit { node, processChildren -> - val parent = nodeStack.peek() - - fun appendNodeWithChildren(content: ContentBlock) { - nodeStack.push(content) - processChildren() - parent.append(nodeStack.pop()) - } - - when (node.type) { - MarkdownElementTypes.ATX_1 -> appendNodeWithChildren(ContentHeading(1)) - MarkdownElementTypes.ATX_2 -> appendNodeWithChildren(ContentHeading(2)) - MarkdownElementTypes.ATX_3 -> appendNodeWithChildren(ContentHeading(3)) - MarkdownElementTypes.ATX_4 -> appendNodeWithChildren(ContentHeading(4)) - MarkdownElementTypes.ATX_5 -> appendNodeWithChildren(ContentHeading(5)) - MarkdownElementTypes.ATX_6 -> appendNodeWithChildren(ContentHeading(6)) - MarkdownElementTypes.UNORDERED_LIST -> appendNodeWithChildren(ContentUnorderedList()) - MarkdownElementTypes.ORDERED_LIST -> appendNodeWithChildren(ContentOrderedList()) - MarkdownElementTypes.LIST_ITEM -> appendNodeWithChildren(ContentListItem()) - MarkdownElementTypes.EMPH -> appendNodeWithChildren(ContentEmphasis()) - MarkdownElementTypes.STRONG -> appendNodeWithChildren(ContentStrong()) - MarkdownElementTypes.CODE_SPAN -> { - val startDelimiter = node.child(MarkdownTokenTypes.BACKTICK)?.text - if (startDelimiter != null) { - val text = node.text.substring(startDelimiter.length).removeSuffix(startDelimiter) - val codeSpan = ContentCode().apply { append(ContentText(text)) } - parent.append(codeSpan) - } - } - MarkdownElementTypes.CODE_BLOCK, - MarkdownElementTypes.CODE_FENCE -> { - val language = node.child(MarkdownTokenTypes.FENCE_LANG)?.text?.trim() ?: "" - appendNodeWithChildren(ContentBlockCode(language)) - } - MarkdownElementTypes.PARAGRAPH -> appendNodeWithChildren(ContentParagraph()) - - MarkdownElementTypes.INLINE_LINK -> { - val linkTextNode = node.child(MarkdownElementTypes.LINK_TEXT) - val destination = node.child(MarkdownElementTypes.LINK_DESTINATION) - if (linkTextNode != null) { - if (destination != null) { - val link = ContentExternalLink(destination.text) - renderLinkTextTo(linkTextNode, link, linkResolver) - parent.append(link) - } else { - val link = ContentExternalLink(linkTextNode.getLabelText()) - renderLinkTextTo(linkTextNode, link, linkResolver) - parent.append(link) - } - } - } - MarkdownElementTypes.SHORT_REFERENCE_LINK, - MarkdownElementTypes.FULL_REFERENCE_LINK -> { - val labelElement = node.child(MarkdownElementTypes.LINK_LABEL) - if (labelElement != null) { - val linkInfo = linkResolver.getLinkInfo(labelElement.text) - val labelText = labelElement.getLabelText() - val link = linkInfo?.let { linkResolver.resolve(it.destination.toString()) } ?: linkResolver.resolve(labelText) - val linkText = node.child(MarkdownElementTypes.LINK_TEXT) - if (linkText != null) { - renderLinkTextTo(linkText, link, linkResolver) - } else { - link.append(ContentText(labelText)) - } - parent.append(link) - } - } - MarkdownTokenTypes.WHITE_SPACE -> { - // Don't append first space if start of header (it is added during formatting later) - // v - // #### Some Heading - if (nodeStack.peek() !is ContentHeading || node.parent?.children?.first() != node) { - parent.append(ContentText(node.text)) - } - } - MarkdownTokenTypes.EOL -> { - if ((keepEol(nodeStack.peek()) && node.parent?.children?.last() != node) || - // Keep extra blank lines when processing lists (affects Markdown formatting) - (processingList(nodeStack.peek()) && node.previous?.type == MarkdownTokenTypes.EOL)) { - parent.append(ContentText(node.text)) - } - } - - MarkdownTokenTypes.CODE_LINE -> { - val content = ContentText(node.text) - if (parent is ContentBlockCode) { - parent.append(content) - } else { - parent.append(ContentBlockCode().apply { append(content) }) - } - } - - MarkdownTokenTypes.TEXT -> { - fun createEntityOrText(text: String): ContentNode { - if (text == "&" || text == """ || text == "<" || text == ">") { - return ContentEntity(text) - } - if (text == "&") { - return ContentEntity("&") - } - val decodedText = EntityConverter.replaceEntities(text, true, true) - if (decodedText != text) { - return ContentEntity(text) - } - return ContentText(text) - } - - parent.append(createEntityOrText(node.text)) - } - - MarkdownTokenTypes.EMPH -> { - val parentNodeType = node.parent?.type - if (parentNodeType != MarkdownElementTypes.EMPH && parentNodeType != MarkdownElementTypes.STRONG) { - parent.append(ContentText(node.text)) - } - } - - MarkdownTokenTypes.COLON, - MarkdownTokenTypes.SINGLE_QUOTE, - MarkdownTokenTypes.DOUBLE_QUOTE, - MarkdownTokenTypes.LT, - MarkdownTokenTypes.GT, - MarkdownTokenTypes.LPAREN, - MarkdownTokenTypes.RPAREN, - MarkdownTokenTypes.LBRACKET, - MarkdownTokenTypes.RBRACKET, - MarkdownTokenTypes.EXCLAMATION_MARK, - MarkdownTokenTypes.BACKTICK, - MarkdownTokenTypes.CODE_FENCE_CONTENT -> { - parent.append(ContentText(node.text)) - } - - MarkdownElementTypes.LINK_DEFINITION -> { - } - - else -> { - processChildren() - } - } - } -} - -private fun MarkdownNode.getLabelText() = children.filter { it.type == MarkdownTokenTypes.TEXT || it.type == MarkdownTokenTypes.EMPH || it.type == MarkdownTokenTypes.COLON }.joinToString("") { it.text } - -private fun keepEol(node: ContentNode) = node is ContentParagraph || node is ContentSection || node is ContentBlockCode -private fun processingList(node: ContentNode) = node is ContentOrderedList || node is ContentUnorderedList - -fun buildInlineContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) { - val inlineContent = tree.children.singleOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree) - inlineContent.forEach { - buildContentTo(it, target, linkResolver) - } -} - -fun renderLinkTextTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) { - val linkTextNodes = tree.children.drop(1).dropLast(1) - linkTextNodes.forEach { - buildContentTo(it, target, linkResolver) - } -} diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt deleted file mode 100644 index 88494581..00000000 --- a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt +++ /dev/null @@ -1,73 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink - -class DeclarationLinkResolver - @Inject constructor(val resolutionFacade: DokkaResolutionFacade, - val refGraph: NodeReferenceGraph, - val logger: DokkaLogger, - val passConfiguration: DokkaConfiguration.PassConfiguration, - val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver, - val elementSignatureProvider: ElementSignatureProvider) { - - - fun tryResolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock? { - val symbol = try { - val symbols = resolveKDocLink(resolutionFacade.resolveSession.bindingContext, - resolutionFacade, fromDescriptor, null, href.split('.').toList()) - findTargetSymbol(symbols) - } catch(e: Exception) { - null - } - - // don't include unresolved links in generated doc - // assume that if an href doesn't contain '/', it's not an attempt to reference an external file - if (symbol != null) { - val externalHref = externalDocumentationLinkResolver.buildExternalDocumentationLink(symbol) - if (externalHref != null) { - return ContentExternalLink(externalHref) - } - val signature = elementSignatureProvider.signature(symbol) - val referencedAt = fromDescriptor.signatureWithSourceLocation() - - return ContentNodeLazyLink(href) { - val target = refGraph.lookup(signature) - - if (target == null) { - logger.warn("Can't find node by signature `$signature`, referenced at $referencedAt. " + - "This is probably caused by invalid configuration of cross-module dependencies") - } - target - } - } - if ("/" in href) { - return ContentExternalLink(href) - } - return null - } - - fun resolveContentLink(fromDescriptor: DeclarationDescriptor, href: String) = - tryResolveContentLink(fromDescriptor, href) ?: run { - logger.warn("Unresolved link to $href in doc comment of ${fromDescriptor.signatureWithSourceLocation()}") - ContentExternalLink("#") - } - - fun findTargetSymbol(symbols: Collection<DeclarationDescriptor>): DeclarationDescriptor? { - if (symbols.isEmpty()) { - return null - } - val symbol = symbols.first() - if (symbol is CallableMemberDescriptor && symbol.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - return symbol.overriddenDescriptors.firstOrNull() - } - if (symbol is TypeAliasDescriptor && !symbol.isDocumented(passConfiguration)) { - return symbol.classDescriptor - } - return symbol - } - -} diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt deleted file mode 100644 index ce20aeec..00000000 --- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt +++ /dev/null @@ -1,177 +0,0 @@ -package org.jetbrains.dokka.Kotlin - -import com.google.inject.Inject -import com.intellij.psi.PsiDocCommentOwner -import com.intellij.psi.PsiNamedElement -import com.intellij.psi.util.PsiTreeUtil -import org.intellij.markdown.parser.LinkMap -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Samples.SampleProcessingService -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny -import org.jetbrains.kotlin.idea.kdoc.findKDoc -import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag -import org.jetbrains.kotlin.kdoc.psi.api.KDoc -import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag -import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor -import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.annotations.argumentValue -import org.jetbrains.kotlin.resolve.constants.StringValue -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered -import org.jetbrains.kotlin.resolve.source.PsiSourceElement - -class DescriptorDocumentationParser - @Inject constructor(val options: DokkaConfiguration.PassConfiguration, - val logger: DokkaLogger, - val linkResolver: DeclarationLinkResolver, - val resolutionFacade: DokkaResolutionFacade, - val refGraph: NodeReferenceGraph, - val sampleService: SampleProcessingService, - val signatureProvider: KotlinElementSignatureProvider, - val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver -) -{ - fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false, isDefaultNoArgConstructor: Boolean = false): Content = - parseDocumentationAndDetails(descriptor, inline, isDefaultNoArgConstructor).first - - fun parseDocumentationAndDetails(descriptor: DeclarationDescriptor, inline: Boolean = false, isDefaultNoArgConstructor: Boolean = false): Pair<Content, (DocumentationNode) -> Unit> { - if (descriptor is JavaClassDescriptor || descriptor is JavaCallableMemberDescriptor) { - return parseJavadoc(descriptor) - } - - val kdoc = descriptor.findKDoc() ?: findStdlibKDoc(descriptor) - if (kdoc == null) { - if (options.effectivePackageOptions(descriptor.fqNameSafe).reportUndocumented && !descriptor.isDeprecated() && - descriptor !is ValueParameterDescriptor && descriptor !is TypeParameterDescriptor && - descriptor !is PropertyAccessorDescriptor && !descriptor.isSuppressWarning()) { - logger.warn("No documentation for ${descriptor.signatureWithSourceLocation()}") - } - return Content.Empty to { node -> } - } - - val contextDescriptor = - (PsiTreeUtil.getParentOfType(kdoc, KDoc::class.java)?.context as? KtDeclaration) - ?.takeIf { it != descriptor.original.sourcePsi() } - ?.resolveToDescriptorIfAny() - ?: descriptor - - var kdocText = if (isDefaultNoArgConstructor) { - getConstructorTagContent(descriptor) ?: kdoc.getContent() - } else kdoc.getContent() - - // workaround for code fence parsing problem in IJ markdown parser - if (kdocText.endsWith("```") || kdocText.endsWith("~~~")) { - kdocText += "\n" - } - val tree = parseMarkdown(kdocText) - val linkMap = LinkMap.buildLinkMap(tree.node, kdocText) - val content = buildContent(tree, LinkResolver(linkMap) { href -> linkResolver.resolveContentLink(contextDescriptor, href) }, inline) - if (kdoc is KDocSection) { - val tags = kdoc.getTags() - tags.forEach { - when (it.knownTag) { - KDocKnownTag.SAMPLE -> - content.append(sampleService.resolveSample(contextDescriptor, it.getSubjectName(), it)) - KDocKnownTag.SEE -> - content.addTagToSeeAlso(contextDescriptor, it) - else -> { - val section = content.addSection(javadocSectionDisplayName(it.name), it.getSubjectName()) - val sectionContent = it.getContent() - val markdownNode = parseMarkdown(sectionContent) - buildInlineContentTo(markdownNode, section, LinkResolver(linkMap) { href -> linkResolver.resolveContentLink(contextDescriptor, href) }) - } - } - } - } - return content to { node -> } - } - - private fun getConstructorTagContent(descriptor: DeclarationDescriptor): String? { - return ((DescriptorToSourceUtils.descriptorToDeclaration(descriptor)?.navigationElement as? KtElement) as KtDeclaration).docComment?.findSectionByTag( - KDocKnownTag.CONSTRUCTOR - )?.getContent() - } - - - private fun DeclarationDescriptor.isSuppressWarning() : Boolean { - val suppressAnnotation = annotations.findAnnotation(FqName(Suppress::class.qualifiedName!!)) - return if (suppressAnnotation != null) { - @Suppress("UNCHECKED_CAST") - (suppressAnnotation.argumentValue("names")?.value as List<StringValue>).any { it.value == "NOT_DOCUMENTED" } - } else containingDeclaration?.isSuppressWarning() ?: false - } - - /** - * Special case for generating stdlib documentation (the Any class to which the override chain will resolve - * is not the same one as the Any class included in the source scope). - */ - fun findStdlibKDoc(descriptor: DeclarationDescriptor): KDocTag? { - if (descriptor !is CallableMemberDescriptor) { - return null - } - val name = descriptor.name.asString() - if (name == "equals" || name == "hashCode" || name == "toString") { - var deepestDescriptor: CallableMemberDescriptor = descriptor - while (!deepestDescriptor.overriddenDescriptors.isEmpty()) { - deepestDescriptor = deepestDescriptor.overriddenDescriptors.first() - } - if (DescriptorUtils.getFqName(deepestDescriptor.containingDeclaration).asString() == "kotlin.Any") { - val anyClassDescriptors = resolutionFacade.resolveSession.getTopLevelClassifierDescriptors( - FqName.fromSegments(listOf("kotlin", "Any")), NoLookupLocation.FROM_IDE) - anyClassDescriptors.forEach { - val anyMethod = (it as ClassDescriptor).getMemberScope(listOf()) - .getDescriptorsFiltered(DescriptorKindFilter.FUNCTIONS) { it == descriptor.name } - .single() - val kdoc = anyMethod.findKDoc() - if (kdoc != null) { - return kdoc - } - } - } - } - return null - } - - fun parseJavadoc(descriptor: DeclarationDescriptor): Pair<Content, (DocumentationNode) -> Unit> { - val psi = ((descriptor as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi - if (psi is PsiDocCommentOwner) { - val parseResult = JavadocParser( - refGraph, - logger, - signatureProvider, - externalDocumentationLinkResolver - ).parseDocumentation(psi as PsiNamedElement) - return parseResult.content to { node -> - parseResult.deprecatedContent?.let { - val deprecationNode = DocumentationNode("", it, NodeKind.Modifier) - node.append(deprecationNode, RefKind.Deprecation) - } - } - } - return Content.Empty to { node -> } - } - - fun KDocSection.getTags(): Array<KDocTag> = PsiTreeUtil.getChildrenOfType(this, KDocTag::class.java) ?: arrayOf() - - private fun MutableContent.addTagToSeeAlso(descriptor: DeclarationDescriptor, seeTag: KDocTag) { - val subjectName = seeTag.getSubjectName() - if (subjectName != null) { - val seeSection = findSectionByTag("See Also") ?: addSection("See Also", null) - val link = linkResolver.resolveContentLink(descriptor, subjectName) - link.append(ContentText(subjectName)) - val para = ContentParagraph() - para.append(link) - seeSection.append(para) - } - } - -} diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt deleted file mode 100644 index 13bbbb11..00000000 --- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt +++ /dev/null @@ -1,1166 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.intellij.openapi.util.text.StringUtil -import com.intellij.psi.PsiJavaFile -import org.jetbrains.dokka.DokkaConfiguration.PassConfiguration -import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.coroutines.hasFunctionOrSuspendFunctionType -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.annotations.Annotated -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 -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtModifierListOwner -import org.jetbrains.kotlin.psi.KtParameter -import org.jetbrains.kotlin.psi.addRemoveModifier.MODIFIERS_ORDER -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.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.immediateSupertypes -import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny -import org.jetbrains.kotlin.types.typeUtil.isTypeParameter -import org.jetbrains.kotlin.types.typeUtil.supertypes -import org.jetbrains.kotlin.util.supertypesWithAny -import com.google.inject.name.Named as GuiceNamed - -private fun isExtensionForExternalClass(extensionFunctionDescriptor: DeclarationDescriptor, - extensionReceiverDescriptor: DeclarationDescriptor, - allFqNames: Collection<FqName>): Boolean { - val extensionFunctionPackage = DescriptorUtils.getParentOfType(extensionFunctionDescriptor, PackageFragmentDescriptor::class.java) - val extensionReceiverPackage = DescriptorUtils.getParentOfType(extensionReceiverDescriptor, PackageFragmentDescriptor::class.java) - return extensionFunctionPackage != null && extensionReceiverPackage != null && - extensionFunctionPackage.fqName != extensionReceiverPackage.fqName && - extensionReceiverPackage.fqName !in allFqNames -} - -interface PackageDocumentationBuilder { - fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, - packageName: FqName, - packageNode: DocumentationNode, - declarations: List<DeclarationDescriptor>, - allFqNames: Collection<FqName>) -} - -interface DefaultPlatformsProvider { - 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, - val passConfiguration: DokkaConfiguration.PassConfiguration, - val refGraph: NodeReferenceGraph, - val platformNodeRegistry: PlatformNodeRegistry, - val logger: DokkaLogger, - val linkResolver: DeclarationLinkResolver, - val defaultPlatformsProvider: DefaultPlatformsProvider) { - val boringBuiltinClasses = setOf( - "kotlin.Unit", "kotlin.Byte", "kotlin.Short", "kotlin.Int", "kotlin.Long", "kotlin.Char", "kotlin.Boolean", - "kotlin.Float", "kotlin.Double", "kotlin.String", "kotlin.Array", "kotlin.Any") - val knownModifiers = setOf( - KtTokens.PUBLIC_KEYWORD, KtTokens.PROTECTED_KEYWORD, KtTokens.INTERNAL_KEYWORD, KtTokens.PRIVATE_KEYWORD, - KtTokens.OPEN_KEYWORD, KtTokens.FINAL_KEYWORD, KtTokens.ABSTRACT_KEYWORD, KtTokens.SEALED_KEYWORD, - KtTokens.OVERRIDE_KEYWORD, KtTokens.INLINE_KEYWORD) - - fun link(node: DocumentationNode, descriptor: DeclarationDescriptor, kind: RefKind) { - refGraph.link(node, descriptor.signature(), kind) - } - - fun link(fromDescriptor: DeclarationDescriptor?, toDescriptor: DeclarationDescriptor?, kind: RefKind) { - if (fromDescriptor != null && toDescriptor != null) { - refGraph.link(fromDescriptor.signature(), toDescriptor.signature(), kind) - } - } - - fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) { - refGraph.register(descriptor.signature(), node) - } - - 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) - return node - } - - private fun DocumentationNode.withModifiers(descriptor: DeclarationDescriptor): DocumentationNode { - if (descriptor is MemberDescriptor) { - appendVisibility(descriptor) - if (descriptor !is ConstructorDescriptor) { - appendModality(descriptor) - } - } - return this - } - - fun DocumentationNode.appendModality(descriptor: MemberDescriptor) { - var modality = descriptor.modality - if (modality == Modality.OPEN) { - val containingClass = descriptor.containingDeclaration as? ClassDescriptor - if (containingClass?.modality == Modality.FINAL) { - modality = Modality.FINAL - } - } - val modifier = modality.name.toLowerCase() - appendTextNode(modifier, NodeKind.Modifier) - } - - fun DocumentationNode.appendInline(descriptor: DeclarationDescriptor, psi: KtModifierListOwner) { - if (!psi.hasModifier(KtTokens.INLINE_KEYWORD)) return - if (descriptor is FunctionDescriptor - && descriptor.valueParameters.none { it.hasFunctionOrSuspendFunctionType }) return - appendTextNode(KtTokens.INLINE_KEYWORD.value, NodeKind.Modifier) - } - - fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) { - val modifier = descriptor.visibility.normalize().displayName - appendTextNode(modifier, NodeKind.Modifier) - } - - fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType, backref: Boolean) { - val unwrappedType = superType.unwrap() - if (unwrappedType is AbbreviatedType) { - appendSupertype(descriptor, unwrappedType.abbreviation, backref) - } else { - appendType(unwrappedType, NodeKind.Supertype) - val superclass = unwrappedType.constructor.declarationDescriptor - if (backref) { - link(superclass, descriptor, RefKind.Inheritor) - } - link(descriptor, superclass, RefKind.Superclass) - } - } - - fun DocumentationNode.appendProjection(projection: TypeProjection, kind: NodeKind = NodeKind.Type) { - if (projection.isStarProjection) { - appendTextNode("*", NodeKind.Type) - } else { - appendType(projection.type, kind, projection.projectionKind.label) - } - } - - fun DocumentationNode.appendType(kotlinType: KotlinType?, kind: NodeKind = NodeKind.Type, prefix: String = "") { - if (kotlinType == null) - return - (kotlinType.unwrap() as? AbbreviatedType)?.let { - return appendType(it.abbreviation) - } - - if (kotlinType.isDynamic()) { - append(DocumentationNode("dynamic", Content.Empty, kind), RefKind.Detail) - return - } - - val classifierDescriptor = kotlinType.constructor.declarationDescriptor - val name = when (classifierDescriptor) { - is ClassDescriptor -> { - if (classifierDescriptor.isCompanionObject) { - classifierDescriptor.containingDeclaration.name.asString() + - "." + classifierDescriptor.name.asString() - } else { - classifierDescriptor.name.asString() - } - } - is Named -> classifierDescriptor.name.asString() - else -> "<anonymous>" - } - val node = DocumentationNode(name, Content.Empty, kind) - if (prefix != "") { - node.appendTextNode(prefix, NodeKind.Modifier) - } - if (kotlinType.isNullabilityFlexible()) { - node.appendTextNode("!", NodeKind.NullabilityModifier) - } else if (kotlinType.isMarkedNullable) { - node.appendTextNode("?", NodeKind.NullabilityModifier) - } - if (classifierDescriptor != null) { - val externalLink = - linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor) - if (externalLink != null) { - 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 - ) - } - 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) { - node.appendProjection(typeArgument) - } - } - - fun ClassifierDescriptor.isBoringBuiltinClass(): Boolean = - DescriptorUtils.getFqName(this).asString() in boringBuiltinClasses - - fun DocumentationNode.appendAnnotations(annotated: Annotated) { - annotated.annotations.forEach { - it.build()?.let { annotationNode -> - if (annotationNode.isSinceKotlin()) { - appendSinceKotlin(annotationNode) - } - else { - val refKind = when { - it.isDocumented() -> - when { - annotationNode.isDeprecation() -> RefKind.Deprecation - else -> RefKind.Annotation - } - it.isHiddenInDocumentation() -> RefKind.HiddenAnnotation - else -> return@forEach - } - append(annotationNode, refKind) - } - - } - } - } - - 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) - .detail(NodeKind.Value) - .name.removeSurrounding("\"") - - sinceKotlin = kotlinVersion - } - - fun DocumentationNode.appendDefaultSinceKotlin() { - if (sinceKotlin == null) { - sinceKotlin = passConfiguration.sinceKotlin - } - } - - fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) { - val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return - KtTokens.MODIFIER_KEYWORDS_ARRAY.filter { - it !in knownModifiers - }.sortedBy { - MODIFIERS_ORDER.indexOf(it) - }.forEach { - if (psi.hasModifier(it)) { - appendTextNode(it.value, NodeKind.Modifier) - } - } - appendInline(descriptor, psi) - } - - fun DocumentationNode.appendDefaultPlatforms(descriptor: DeclarationDescriptor) { - for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor)) { - append(platformNodeRegistry[platform], RefKind.Platform) - } - } - - fun DocumentationNode.isDeprecation() = name == "Deprecated" || name == "deprecated" - - fun DocumentationNode.isSinceKotlin() = name == "SinceKotlin" && kind == NodeKind.Annotation - - fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) { - appendSourceLink(sourceElement.getPsi(), passConfiguration.sourceLinks) - } - - fun DocumentationNode.appendSignature(descriptor: DeclarationDescriptor) { - appendTextNode(descriptor.signature(), NodeKind.Signature, RefKind.Detail) - } - - fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: RefKind): DocumentationNode? { - if (!descriptor.isGenerated() && descriptor.isDocumented(passConfiguration)) { - val node = descriptor.build() - append(node, kind) - return node - } - return null - } - - fun createGroupNode(signature: String, nodes: List<DocumentationNode>) = (nodes.find { it.kind == NodeKind.GroupNode } ?: - DocumentationNode(nodes.first().name, Content.Empty, NodeKind.GroupNode).apply { - appendTextNode(signature, NodeKind.Signature, RefKind.Detail) - }) - .also { groupNode -> - nodes.forEach { node -> - if (node != groupNode) { - node.owner?.let { owner -> - node.dropReferences { it.to == owner && it.kind == RefKind.Owner } - owner.dropReferences { it.to == node && it.kind == RefKind.Member } - owner.append(groupNode, RefKind.Member) - } - groupNode.append(node, RefKind.Member) - } - } - } - - - fun DocumentationNode.appendOrUpdateMember(descriptor: DeclarationDescriptor) { - if (descriptor.isGenerated() || !descriptor.isDocumented(passConfiguration)) return - - val existingNode = refGraph.lookup(descriptor.signature()) - if (existingNode != null) { - if (existingNode.kind == NodeKind.TypeAlias && descriptor is ClassDescriptor - || existingNode.kind == NodeKind.Class && descriptor is TypeAliasDescriptor) { - val node = createGroupNode(descriptor.signature(), listOf(existingNode, descriptor.build())) - register(descriptor, node) - return - } - - existingNode.updatePlatforms(descriptor) - - if (descriptor is ClassDescriptor) { - val membersToDocument = descriptor.collectMembersToDocument() - for ((memberDescriptor, inheritedLinkKind, extraModifier) in membersToDocument) { - if (memberDescriptor is ClassDescriptor) { - existingNode.appendOrUpdateMember(memberDescriptor) // recurse into nested classes - } - else { - val existingMemberNode = refGraph.lookup(memberDescriptor.signature()) - if (existingMemberNode != null) { - existingMemberNode.updatePlatforms(memberDescriptor) - } - else { - existingNode.appendClassMember(memberDescriptor, inheritedLinkKind, extraModifier) - } - } - } - } - } - else { - appendChild(descriptor, RefKind.Member) - } - } - - private fun DocumentationNode.updatePlatforms(descriptor: DeclarationDescriptor) { - for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor) - platforms) { - append(platformNodeRegistry[platform], RefKind.Platform) - } - } - - fun DocumentationNode.appendClassMember(descriptor: DeclarationDescriptor, - inheritedLinkKind: RefKind = RefKind.InheritedMember, - extraModifier: String?) { - if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - val baseDescriptor = descriptor.overriddenDescriptors.firstOrNull() - if (baseDescriptor != null) { - link(this, baseDescriptor, inheritedLinkKind) - } - } else { - val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original - val child = appendChild(descriptorToUse, RefKind.Member) - if (extraModifier != null) { - child?.appendTextNode("static", NodeKind.Modifier) - } - } - } - - fun DocumentationNode.appendInPageChildren(descriptors: Iterable<DeclarationDescriptor>, kind: RefKind) { - descriptors.forEach { descriptor -> - val node = appendChild(descriptor, kind) - node?.addReferenceTo(this, RefKind.TopLevelPage) - } - } - - fun DocumentationModule.appendFragments(fragments: Collection<PackageFragmentDescriptor>, - packageContent: Map<String, Content>, - packageDocumentationBuilder: PackageDocumentationBuilder) { - val allFqNames = fragments.filter { it.isDocumented(passConfiguration) }.map { it.fqName }.distinct() - - for (packageName in allFqNames) { - if (packageName.isRoot && !passConfiguration.includeRootPackage) continue - val declarations = fragments.filter { it.fqName == packageName }.flatMap { it.getMemberScope().getContributedDescriptors() } - - if (passConfiguration.skipEmptyPackages && declarations.none { it.isDocumented(passConfiguration) }) continue - logger.info(" package $packageName: ${declarations.count()} declarations") - val packageNode = findOrCreatePackageNode(this, packageName.asString(), packageContent, this@DocumentationBuilder.refGraph) - packageDocumentationBuilder.buildPackageDocumentation(this@DocumentationBuilder, packageName, packageNode, - declarations, allFqNames) - } - - } - - fun propagateExtensionFunctionsToSubclasses( - fragments: Collection<PackageFragmentDescriptor>, - resolutionFacade: DokkaResolutionFacade - ) { - - 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 (passConfiguration.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 } - - fun isIgnoredReceiverType(type: KotlinType) = - type.isDynamic() || - type.isAnyOrNullableAny() || - (type.isTypeParameter() && type.immediateSupertypes().all { it.isAnyOrNullableAny() }) - - - for (extensionFunction in allExtensionFunctions) { - val extensionReceiverParameter = extensionFunction.extensionReceiverParameter!! - if (extensionFunction.dispatchReceiverParameter != null) continue - val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name] - ?.filter { fn -> fn.canShadow(extensionFunction) } - ?: emptyList() - - if (isIgnoredReceiverType(extensionReceiverParameter.type)) continue - 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) }) { - - 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) - } - } - } - } - - private fun ClassDescriptor.isExtensionApplicable(extensionFunction: CallableMemberDescriptor): Boolean { - 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>> { - val result = hashMapOf<ClassDescriptor, MutableList<ClassDescriptor>>() - classes.forEach { cls -> - TypeUtils.getAllSupertypes(cls.defaultType).forEach { supertype -> - val classDescriptor = supertype.constructor.declarationDescriptor as? ClassDescriptor - if (classDescriptor != null) { - val subtypesList = result.getOrPut(classDescriptor) { arrayListOf() } - subtypesList.add(cls) - } - } - } - return result - } - - private fun CallableMemberDescriptor.canShadow(other: CallableMemberDescriptor): Boolean { - if (this == other) return false - if (this is PropertyDescriptor && other is PropertyDescriptor) { - return true - } - if (this is FunctionDescriptor && other is FunctionDescriptor) { - val parameters1 = valueParameters - val parameters2 = other.valueParameters - if (parameters1.size != parameters2.size) { - return false - } - for ((p1, p2) in parameters1 zip parameters2) { - if (p1.type != p2.type) { - return false - } - } - return true - } - return false - } - - fun DeclarationDescriptor.build(): DocumentationNode = when (this) { - is ClassifierDescriptor -> build() - is ConstructorDescriptor -> build() - is PropertyDescriptor -> build() - is FunctionDescriptor -> build() - is ValueParameterDescriptor -> build() - is ReceiverParameterDescriptor -> build() - else -> throw IllegalStateException("Descriptor $this is not known") - } - - 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) - - if (!external) { - node.appendDefaultSinceKotlin() - node.appendAnnotations(this) - } - node.appendModifiers(this) - node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail) - - node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType) - - if (!external) { - node.appendSourceLink(source) - node.appendDefaultPlatforms(this) - } - register(this, node) - return node - } - - fun ClassDescriptor.build(external: Boolean = false): DocumentationNode { - val kind = when { - kind == ClassKind.OBJECT -> NodeKind.Object - kind == ClassKind.INTERFACE -> NodeKind.Interface - kind == ClassKind.ENUM_CLASS -> NodeKind.Enum - kind == ClassKind.ANNOTATION_CLASS -> NodeKind.AnnotationClass - kind == ClassKind.ENUM_ENTRY -> NodeKind.EnumItem - isSubclassOfThrowable() -> NodeKind.Exception - else -> NodeKind.Class - } - val node = nodeForDescriptor(this, kind, external) - register(this, node) - supertypesWithAnyPrecise().forEach { - node.appendSupertype(this, it, !external) - } - if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) { - node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail) - } - if (!external) { - for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) { - node.appendClassMember(descriptor, inheritedLinkKind, extraModifier) - } - node.appendDefaultSinceKotlin() - node.appendAnnotations(this) - } - node.appendModifiers(this) - if (!external) { - node.appendSourceLink(source) - node.appendDefaultPlatforms(this) - } - return node - } - - data class ClassMember(val descriptor: DeclarationDescriptor, - val inheritedLinkKind: RefKind = RefKind.InheritedMember, - val extraModifier: String? = null) - - fun ClassDescriptor.collectMembersToDocument(): List<ClassMember> { - val result = arrayListOf<ClassMember>() - if (kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) { - val constructorsToDocument = if (kind == ClassKind.ENUM_CLASS) - constructors.filter { it.valueParameters.size > 0 } - else - constructors - constructorsToDocument.mapTo(result) { ClassMember(it) } - } - - defaultType.memberScope.getContributedDescriptors() - .filter { it != companionObjectDescriptor } - .mapTo(result) { ClassMember(it) } - - staticScope.getContributedDescriptors() - .mapTo(result) { ClassMember(it, extraModifier = "static") } - - val companionObjectDescriptor = companionObjectDescriptor - if (companionObjectDescriptor != null && companionObjectDescriptor.isDocumented(passConfiguration)) { - val descriptors = companionObjectDescriptor.defaultType.memberScope.getContributedDescriptors() - val descriptorsToDocument = descriptors.filter { it !is CallableDescriptor || !it.isInheritedFromAny() } - descriptorsToDocument.mapTo(result) { - ClassMember(it, inheritedLinkKind = RefKind.InheritedCompanionObjectMember) - } - - if (companionObjectDescriptor.getAllSuperclassesWithoutAny().isNotEmpty() - || companionObjectDescriptor.getSuperInterfaces().isNotEmpty()) { - result += ClassMember(companionObjectDescriptor) - } - } - return result - } - - fun CallableDescriptor.isInheritedFromAny(): Boolean { - return findTopMostOverriddenDescriptors().any { - DescriptorUtils.getFqNameSafe(it.containingDeclaration).asString() == "kotlin.Any" - } - } - - fun ClassDescriptor.isSubclassOfThrowable(): Boolean = - defaultType.supertypes().any { it.constructor.declarationDescriptor == builtIns.throwable } - - fun ConstructorDescriptor.build(): DocumentationNode { - val node = nodeForDescriptor(this, NodeKind.Constructor) - node.appendInPageChildren(valueParameters, RefKind.Detail) - node.appendDefaultPlatforms(this) - node.appendDefaultSinceKotlin() - register(this, node) - return node - } - - private fun CallableMemberDescriptor.inCompanionObject(): Boolean { - val containingDeclaration = containingDeclaration - if ((containingDeclaration as? ClassDescriptor)?.isCompanionObject ?: false) { - return true - } - val receiver = extensionReceiverParameter - return (receiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor)?.isCompanionObject ?: false - } - - fun FunctionDescriptor.build(external: Boolean = false): DocumentationNode { - if (ErrorUtils.containsErrorTypeInParameters(this) || ErrorUtils.containsErrorType(this.returnType)) { - logger.warn("Found an unresolved type in ${signatureWithSourceLocation()}") - } - - val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function, external) - - node.appendInPageChildren(typeParameters, RefKind.Detail) - extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) } - node.appendInPageChildren(valueParameters, RefKind.Detail) - node.appendType(returnType) - if (!external) { - node.appendDefaultSinceKotlin() - } - node.appendAnnotations(this) - node.appendModifiers(this) - if (!external) { - node.appendSourceLink(source) - node.appendDefaultPlatforms(this) - } else { - node.appendExternalLink(this) - } - - overriddenDescriptors.forEach { - addOverrideLink(it, this) - } - - register(this, node) - return node - } - - fun addOverrideLink(baseClassFunction: CallableMemberDescriptor, overridingFunction: CallableMemberDescriptor) { - val source = baseClassFunction.original.source.getPsi() - if (source != null) { - link(overridingFunction, baseClassFunction, RefKind.Override) - } else { - baseClassFunction.overriddenDescriptors.forEach { - addOverrideLink(it, overridingFunction) - } - } - } - - 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) - if (!external) { - node.appendDefaultSinceKotlin() - } - node.appendAnnotations(this) - node.appendModifiers(this) - if (!external) { - node.appendSourceLink(source) - if (isVar) { - node.appendTextNode("var", NodeKind.Modifier) - } - - if (isConst) { - this.compileTimeInitializer?.toDocumentationNode()?.let { node.append(it, RefKind.Detail) } - } - - - 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) - } - - register(this, node) - return node - } - - fun DocumentationNode.addAccessorDocumentation(documentation: Content, prefix: String) { - if (documentation == Content.Empty) return - updateContent { - if (!documentation.children.isEmpty()) { - val section = addSection(prefix, null) - documentation.children.forEach { section.append(it) } - } - documentation.sections.forEach { - val section = addSection("$prefix ${it.tag}", it.subjectName) - it.children.forEach { section.append(it) } - } - } - } - - fun ValueParameterDescriptor.build(): DocumentationNode { - val node = nodeForDescriptor(this, NodeKind.Parameter) - node.appendType(varargElementType ?: type) - if (declaresDefaultValue()) { - val psi = source.getPsi() as? KtParameter - if (psi != null) { - val defaultValueText = psi.defaultValue?.text - if (defaultValueText != null) { - node.appendTextNode(defaultValueText, NodeKind.Value) - } - } - } - node.appendDefaultSinceKotlin() - node.appendAnnotations(this) - node.appendModifiers(this) - if (varargElementType != null && node.details(NodeKind.Modifier).none { it.name == "vararg" }) { - node.appendTextNode("vararg", NodeKind.Modifier) - } - register(this, node) - return node - } - - fun TypeParameterDescriptor.build(): DocumentationNode { - val doc = descriptorDocumentationParser.parseDocumentation(this) - val name = name.asString() - val prefix = variance.label - - val node = DocumentationNode(name, doc, NodeKind.TypeParameter) - if (prefix != "") { - node.appendTextNode(prefix, NodeKind.Modifier) - } - if (isReified) { - node.appendTextNode("reified", NodeKind.Modifier) - } - - for (constraint in upperBounds) { - if (KotlinBuiltIns.isDefaultBound(constraint)) { - continue - } - node.appendType(constraint, NodeKind.UpperBound) - } - register(this, node) - return node - } - - fun ReceiverParameterDescriptor.build(): DocumentationNode { - var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!! - if ((receiverClass as? ClassDescriptor)?.isCompanionObject ?: false) { - receiverClass = receiverClass.containingDeclaration!! - } else if (receiverClass is TypeParameterDescriptor) { - val upperBoundClass = receiverClass.upperBounds.singleOrNull()?.constructor?.declarationDescriptor - if (upperBoundClass != null) { - receiverClass = upperBoundClass - } - } - - if ((containingDeclaration as? FunctionDescriptor)?.dispatchReceiverParameter == null) { - link(receiverClass, containingDeclaration, RefKind.Extension) - } - - val node = DocumentationNode(name.asString(), Content.Empty, NodeKind.Receiver) - node.appendType(type) - register(this, node) - return node - } - - fun AnnotationDescriptor.build(): DocumentationNode? { - val annotationClass = type.constructor.declarationDescriptor - if (annotationClass == null || ErrorUtils.isError(annotationClass)) { - return null - } - val node = DocumentationNode(annotationClass.name.asString(), Content.Empty, NodeKind.Annotation) - allValueArguments.forEach { (name, value) -> - val valueNode = value.toDocumentationNode() - if (valueNode != null) { - val paramNode = DocumentationNode(name.asString(), Content.Empty, NodeKind.Parameter) - paramNode.append(valueNode, RefKind.Detail) - node.append(paramNode, RefKind.Detail) - } - } - return node - } - - fun ConstantValue<*>.toDocumentationNode(): DocumentationNode? = value.let { value -> - val text = when (value) { - is String -> - "\"" + StringUtil.escapeStringCharacters(value) + "\"" - is EnumEntrySyntheticClassDescriptor -> - value.containingDeclaration.name.asString() + "." + value.name.asString() - is Pair<*, *> -> { - val (classId, name) = value - if (classId is ClassId && name is Name) { - classId.shortClassName.asString() + "." + name.asString() - } else { - value.toString() - } - } - else -> "$value" - } - DocumentationNode(text, Content.Empty, NodeKind.Value) - } - - - fun DocumentationNode.getParentForPackageMember( - descriptor: DeclarationDescriptor, - externalClassNodes: MutableMap<FqName, DocumentationNode>, - allFqNames: Collection<FqName>, - packageName: FqName - ): DocumentationNode { - if (descriptor is CallableMemberDescriptor) { - val extensionClassDescriptor = descriptor.getExtensionClassDescriptor() - if (extensionClassDescriptor != null && isExtensionForExternalClass(descriptor, extensionClassDescriptor, allFqNames) && - !ErrorUtils.isError(extensionClassDescriptor)) { - val fqName = DescriptorUtils.getFqNameSafe(extensionClassDescriptor) - return externalClassNodes.getOrPut(fqName) { - val newNode = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.ExternalClass) - val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(extensionClassDescriptor) - if (externalLink != null) { - newNode.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link) - } - append(newNode, RefKind.Member) - refGraph.register("${packageName.asString()}:${extensionClassDescriptor.signature()}", newNode) - newNode - } - } - } - return this - } - -} - -fun DeclarationDescriptor.isDocumented(passConfiguration: DokkaConfiguration.PassConfiguration): Boolean { - return (passConfiguration.effectivePackageOptions(fqNameSafe).includeNonPublic - || this !is MemberDescriptor - || this.visibility.isPublicAPI) - && !isDocumentationSuppressed(passConfiguration) - && (!passConfiguration.effectivePackageOptions(fqNameSafe).skipDeprecated || !isDeprecated()) -} - -private fun DeclarationDescriptor.isGenerated() = this is CallableMemberDescriptor && kind != CallableMemberDescriptor.Kind.DECLARATION - -class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder { - override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, - packageName: FqName, - packageNode: DocumentationNode, - declarations: List<DeclarationDescriptor>, - allFqNames: Collection<FqName>) { - val externalClassNodes = hashMapOf<FqName, DocumentationNode>() - declarations.forEach { descriptor -> - with(documentationBuilder) { - if (descriptor.isDocumented(passConfiguration)) { - val parent = packageNode.getParentForPackageMember( - descriptor, - externalClassNodes, - allFqNames, - packageName - ) - parent.appendOrUpdateMember(descriptor) - } - } - } - } -} - -class KotlinJavaDocumentationBuilder -@Inject constructor(val resolutionFacade: DokkaResolutionFacade, - val documentationBuilder: DocumentationBuilder, - val passConfiguration: DokkaConfiguration.PassConfiguration, - val logger: DokkaLogger) : JavaDocumentationBuilder { - override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) { - val classDescriptors = file.classes.map { - it.getJavaClassDescriptor(resolutionFacade) - } - - if (classDescriptors.any { it != null && it.isDocumented(passConfiguration) }) { - val packageNode = findOrCreatePackageNode(module, file.packageName, packageContent, documentationBuilder.refGraph) - - for (descriptor in classDescriptors.filterNotNull()) { - with(documentationBuilder) { - packageNode.appendChild(descriptor, RefKind.Member) - } - } - } - } -} - -private val hiddenAnnotations = setOf( - KotlinBuiltIns.FQ_NAMES.parameterName.asString() -) - -private fun AnnotationDescriptor.isHiddenInDocumentation() = - type.constructor.declarationDescriptor?.fqNameSafe?.asString() in hiddenAnnotations - -private fun AnnotationDescriptor.isDocumented(): Boolean { - if (source.getPsi() != null && mustBeDocumented()) return true - val annotationClassName = type.constructor.declarationDescriptor?.fqNameSafe?.asString() - return annotationClassName == KotlinBuiltIns.FQ_NAMES.extensionFunctionType.asString() -} - -fun AnnotationDescriptor.mustBeDocumented(): Boolean { - val annotationClass = type.constructor.declarationDescriptor as? Annotated ?: return false - return annotationClass.isDocumentedAnnotation() -} - -fun DeclarationDescriptor.isDocumentationSuppressed(passConfiguration: DokkaConfiguration.PassConfiguration): Boolean { - - if (passConfiguration.effectivePackageOptions(fqNameSafe).suppress) return true - - val path = this.findPsi()?.containingFile?.virtualFile?.path - if (path != null) { - if (path in passConfiguration.suppressedFiles) return true - } - - val doc = findKDoc() - if (doc is KDocSection && doc.findTagByName("suppress") != null) return true - - return hasSuppressDocTag(sourcePsi()) -} - -fun DeclarationDescriptor.sourcePsi() = - ((original as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi - -fun DeclarationDescriptor.isDeprecated(): Boolean = annotations.any { - DescriptorUtils.getFqName(it.type.constructor.declarationDescriptor!!).asString() == "kotlin.Deprecated" -} || (this is ConstructorDescriptor && containingDeclaration.isDeprecated()) - -fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? { - val extensionReceiver = extensionReceiverParameter - if (extensionReceiver != null) { - val type = extensionReceiver.type - val receiverClass = type.constructor.declarationDescriptor as? ClassDescriptor - if (receiverClass?.isCompanionObject ?: false) { - return receiverClass?.containingDeclaration as? ClassifierDescriptor - } - return receiverClass - } - return null -} - -fun DeclarationDescriptor.signature(): String { - if (this != original) return original.signature() - return when (this) { - is ClassDescriptor, - is PackageFragmentDescriptor, - is PackageViewDescriptor, - is TypeAliasDescriptor -> DescriptorUtils.getFqName(this).asString() - - is PropertyDescriptor -> containingDeclaration.signature() + "$" + name + receiverSignature() - is FunctionDescriptor -> containingDeclaration.signature() + "$" + name + parameterSignature() - is ValueParameterDescriptor -> containingDeclaration.signature() + "/" + name - is TypeParameterDescriptor -> containingDeclaration.signature() + "*" + name - is ReceiverParameterDescriptor -> containingDeclaration.signature() + "/" + name - else -> throw UnsupportedOperationException("Don't know how to calculate signature for $this") - } -} - -fun PropertyDescriptor.receiverSignature(): String { - val receiver = extensionReceiverParameter - if (receiver != null) { - return "#" + receiver.type.signature() - } - return "" -} - -fun CallableMemberDescriptor.parameterSignature(): String { - val params = valueParameters.map { it.type }.toMutableList() - val extensionReceiver = extensionReceiverParameter - if (extensionReceiver != null) { - params.add(0, extensionReceiver.type) - } - return params.joinToString(prefix = "(", postfix = ")") { it.signature() } -} - -fun KotlinType.signature(): String { - val visited = hashSetOf<KotlinType>() - - fun KotlinType.signatureRecursive(): String { - if (this in visited) { - return "" - } - visited.add(this) - - val declarationDescriptor = constructor.declarationDescriptor ?: return "<null>" - val typeName = DescriptorUtils.getFqName(declarationDescriptor).asString() - if (arguments.isEmpty()) { - return typeName - } - return typeName + arguments.joinToString(prefix = "((", postfix = "))") { it.type.signatureRecursive() } - } - - return signatureRecursive() -} - -fun DeclarationDescriptor.signatureWithSourceLocation(): String { - val signature = signature() - val sourceLocation = sourceLocation() - return if (sourceLocation != null) "$signature ($sourceLocation)" else signature -} - -fun DeclarationDescriptor.sourceLocation(): String? { - val psi = sourcePsi() - if (psi != null) { - val fileName = psi.containingFile.name - val lineNumber = psi.lineNumber() - return if (lineNumber != null) "$fileName:$lineNumber" else fileName - } - return null -} - -fun DocumentationModule.prepareForGeneration(configuration: DokkaConfiguration) { - if (configuration.generateIndexPages) { - generateAllTypesNode() - } - nodeRefGraph.resolveReferences() -} - -fun DocumentationNode.generateAllTypesNode() { - val allTypes = members(NodeKind.Package) - .flatMap { it.members.filter { - it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass - || (it.kind == NodeKind.GroupNode && it.origins.all { it.kind in NodeKind.classLike }) } } - .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.').toLowerCase() else it.name.toLowerCase() } - - val allTypesNode = DocumentationNode("alltypes", Content.Empty, NodeKind.AllTypes) - for (typeNode in allTypes) { - allTypesNode.addReferenceTo(typeNode, RefKind.Member) - } - - append(allTypesNode, RefKind.Member) -} - -fun ClassDescriptor.supertypesWithAnyPrecise(): Collection<KotlinType> { - if (KotlinBuiltIns.isAny(this)) { - return emptyList() - } - return typeConstructor.supertypesWithAny() -} - -fun PassConfiguration.effectivePackageOptions(pack: String): DokkaConfiguration.PackageOptions { - val rootPackageOptions = PackageOptionsImpl("", includeNonPublic, reportUndocumented, skipDeprecated, false) - return perPackageOptions.firstOrNull { pack == it.prefix } - ?: perPackageOptions.firstOrNull { pack.startsWith(it.prefix + ".") } - ?: rootPackageOptions -} - -fun PassConfiguration.effectivePackageOptions(pack: FqName): DokkaConfiguration.PackageOptions = effectivePackageOptions(pack.asString()) - diff --git a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt deleted file mode 100644 index bd8b9cf5..00000000 --- a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt +++ /dev/null @@ -1,305 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.Singleton -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiMethod -import com.intellij.util.io.* -import org.jetbrains.dokka.Formats.FileGeneratorBasedFormatDescriptor -import org.jetbrains.dokka.Formats.FormatDescriptor -import org.jetbrains.dokka.Utilities.ServiceLocator -import org.jetbrains.dokka.Utilities.lookup -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor -import org.jetbrains.kotlin.load.java.descriptors.* -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.parents -import java.io.ByteArrayOutputStream -import java.io.PrintWriter -import java.net.HttpURLConnection -import java.net.URL -import java.net.URLConnection -import java.nio.file.Path -import java.nio.file.Paths -import java.security.MessageDigest -import javax.inject.Named -import kotlin.reflect.full.findAnnotation - -fun ByteArray.toHexString() = this.joinToString(separator = "") { "%02x".format(it) } - -typealias PackageFqNameToLocation = MutableMap<FqName, PackageListProvider.ExternalDocumentationRoot> - -@Singleton -class PackageListProvider @Inject constructor( - val configuration: DokkaConfiguration, - val logger: DokkaLogger -) { - val storage = mutableMapOf<DokkaConfiguration.ExternalDocumentationLink, PackageFqNameToLocation>() - - val cacheDir: Path? = when { - configuration.cacheRoot == "default" -> Paths.get(System.getProperty("user.home"), ".cache", "dokka") - configuration.cacheRoot != null -> Paths.get(configuration.cacheRoot) - else -> null - }?.resolve("packageListCache")?.apply { toFile().mkdirs() } - - val cachedProtocols = setOf("http", "https", "ftp") - - init { - for (conf in configuration.passesConfigurations) { - for (link in conf.externalDocumentationLinks) { - if (link in storage) { - continue - } - - try { - loadPackageList(link) - } catch (e: Exception) { - throw RuntimeException("Exception while loading package-list from $link", e) - } - } - - } - } - - - - fun URL.doOpenConnectionToReadContent(timeout: Int = 10000, redirectsAllowed: Int = 16): URLConnection { - val connection = this.openConnection() - connection.connectTimeout = timeout - connection.readTimeout = timeout - - when (connection) { - is HttpURLConnection -> { - return when (connection.responseCode) { - in 200..299 -> { - connection - } - HttpURLConnection.HTTP_MOVED_PERM, - HttpURLConnection.HTTP_MOVED_TEMP, - HttpURLConnection.HTTP_SEE_OTHER -> { - if (redirectsAllowed > 0) { - val newUrl = connection.getHeaderField("Location") - URL(newUrl).doOpenConnectionToReadContent(timeout, redirectsAllowed - 1) - } else { - throw RuntimeException("Too many redirects") - } - } - else -> { - throw RuntimeException("Unhandled http code: ${connection.responseCode}") - } - } - } - else -> return connection - } - } - - fun loadPackageList(link: DokkaConfiguration.ExternalDocumentationLink) { - - val packageListUrl = link.packageListUrl - val needsCache = packageListUrl.protocol in cachedProtocols - - val packageListStream = if (cacheDir != null && needsCache) { - val packageListLink = packageListUrl.toExternalForm() - - val digest = MessageDigest.getInstance("SHA-256") - val hash = digest.digest(packageListLink.toByteArray(Charsets.UTF_8)).toHexString() - val cacheEntry = cacheDir.resolve(hash).toFile() - - if (cacheEntry.exists()) { - try { - val connection = packageListUrl.doOpenConnectionToReadContent() - val originModifiedDate = connection.date - val cacheDate = cacheEntry.lastModified() - if (originModifiedDate > cacheDate || originModifiedDate == 0L) { - if (originModifiedDate == 0L) - logger.warn("No date header for $packageListUrl, downloading anyway") - else - logger.info("Renewing package-list from $packageListUrl") - connection.getInputStream().copyTo(cacheEntry.outputStream()) - } - } catch (e: Exception) { - logger.error("Failed to update package-list cache for $link") - val baos = ByteArrayOutputStream() - PrintWriter(baos).use { - e.printStackTrace(it) - } - baos.flush() - logger.error(baos.toString()) - } - } else { - logger.info("Downloading package-list from $packageListUrl") - packageListUrl.openStream().copyTo(cacheEntry.outputStream()) - } - cacheEntry.inputStream() - } else { - packageListUrl.doOpenConnectionToReadContent().getInputStream() - } - - val (params, packages) = - packageListStream - .bufferedReader() - .useLines { lines -> lines.partition { it.startsWith(ExternalDocumentationLinkResolver.DOKKA_PARAM_PREFIX) } } - - val paramsMap = params.asSequence() - .map { it.removePrefix(ExternalDocumentationLinkResolver.DOKKA_PARAM_PREFIX).split(":", limit = 2) } - .groupBy({ (key, _) -> key }, { (_, value) -> value }) - - val format = paramsMap["format"]?.singleOrNull() ?: "javadoc" - - val locations = paramsMap["location"].orEmpty() - .map { it.split("\u001f", limit = 2) } - .map { (key, value) -> key to value } - .toMap() - - - val defaultResolverDesc = ExternalDocumentationLinkResolver.services.getValue("dokka-default") - val resolverDesc = ExternalDocumentationLinkResolver.services[format] - ?: defaultResolverDesc.takeIf { format in formatsWithDefaultResolver } - ?: defaultResolverDesc.also { - logger.warn("Couldn't find InboundExternalLinkResolutionService(format = `$format`) for $link, using Dokka default") - } - - - val resolverClass = javaClass.classLoader.loadClass(resolverDesc.className).kotlin - - val constructors = resolverClass.constructors - - val constructor = constructors.singleOrNull() - ?: constructors.first { it.findAnnotation<Inject>() != null } - val resolver = constructor.call(paramsMap) as InboundExternalLinkResolutionService - - val rootInfo = ExternalDocumentationRoot(link.url, resolver, locations) - - val packageFqNameToLocation = mutableMapOf<FqName, ExternalDocumentationRoot>() - storage[link] = packageFqNameToLocation - - val fqNames = packages.map { FqName(it) } - for(name in fqNames) { - packageFqNameToLocation[name] = rootInfo - } - } - - - - class ExternalDocumentationRoot(val rootUrl: URL, val resolver: InboundExternalLinkResolutionService, val locations: Map<String, String>) { - override fun toString(): String = rootUrl.toString() - } - - companion object { - private val formatsWithDefaultResolver = - ServiceLocator - .allServices("format") - .filter { - val desc = ServiceLocator.lookup<FormatDescriptor>(it) as? FileGeneratorBasedFormatDescriptor - desc?.generatorServiceClass == FileGenerator::class - }.map { it.name } - .toSet() - - } - -} - -class ExternalDocumentationLinkResolver @Inject constructor( - val configuration: DokkaConfiguration, - val passConfiguration: DokkaConfiguration.PassConfiguration, - @Named("libraryResolutionFacade") val libraryResolutionFacade: DokkaResolutionFacade, - val logger: DokkaLogger, - val packageListProvider: PackageListProvider -) { - - val formats = mutableMapOf<String, InboundExternalLinkResolutionService>() - val packageFqNameToLocation = mutableMapOf<FqName, PackageListProvider.ExternalDocumentationRoot>() - - init { - val fqNameToLocationMaps = passConfiguration.externalDocumentationLinks - .mapNotNull { packageListProvider.storage[it] } - - for(map in fqNameToLocationMaps) { - packageFqNameToLocation.putAll(map) - } - } - - fun buildExternalDocumentationLink(element: PsiElement): String? { - return element.extractDescriptor(libraryResolutionFacade)?.let { - buildExternalDocumentationLink(it) - } - } - - fun buildExternalDocumentationLink(symbol: DeclarationDescriptor): String? { - val packageFqName: FqName = - when (symbol) { - is PackageFragmentDescriptor -> symbol.fqName - is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null - else -> return null - } - - val externalLocation = packageFqNameToLocation[packageFqName] ?: return null - - val path = externalLocation.locations[symbol.signature()] ?: - externalLocation.resolver.getPath(symbol) ?: return null - - return URL(externalLocation.rootUrl, path).toExternalForm() - } - - companion object { - const val DOKKA_PARAM_PREFIX = "\$dokka." - val services = ServiceLocator.allServices("inbound-link-resolver").associateBy { it.name } - } -} - - -interface InboundExternalLinkResolutionService { - fun getPath(symbol: DeclarationDescriptor): String? - - class Javadoc(paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService { - override fun getPath(symbol: DeclarationDescriptor): String? { - if (symbol is EnumEntrySyntheticClassDescriptor) { - return getPath(symbol.containingDeclaration)?.let { it + "#" + symbol.name.asString() } - } else if (symbol is JavaClassDescriptor) { - return DescriptorUtils.getFqName(symbol).asString().replace(".", "/") + ".html" - } else if (symbol is JavaCallableMemberDescriptor) { - val containingClass = symbol.containingDeclaration as? JavaClassDescriptor ?: return null - val containingClassLink = getPath(containingClass) - if (containingClassLink != null) { - if (symbol is JavaMethodDescriptor || symbol is JavaClassConstructorDescriptor) { - val psi = symbol.sourcePsi() as? PsiMethod - if (psi != null) { - val params = psi.parameterList.parameters.joinToString { it.type.canonicalText } - return containingClassLink + "#" + symbol.name + "(" + params + ")" - } - } else if (symbol is JavaPropertyDescriptor) { - return "$containingClassLink#${symbol.name}" - } - } - } - // TODO Kotlin javadoc - return null - } - } - - class Dokka(val paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService { - val extension = paramsMap["linkExtension"]?.singleOrNull() ?: error("linkExtension not provided for Dokka resolver") - - override fun getPath(symbol: DeclarationDescriptor): String? { - val leafElement = when (symbol) { - is CallableDescriptor, is TypeAliasDescriptor -> true - else -> false - } - val path = getPathWithoutExtension(symbol) - if (leafElement) return "$path.$extension" - else return "$path/index.$extension" - } - - private fun getPathWithoutExtension(symbol: DeclarationDescriptor): String { - return when { - symbol.containingDeclaration == null -> identifierToFilename(symbol.name.asString()) - symbol is PackageFragmentDescriptor -> identifierToFilename(symbol.fqName.asString()) - else -> getPathWithoutExtension(symbol.containingDeclaration!!) + '/' + identifierToFilename(symbol.name.asString()) - } - } - - } -} - diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt deleted file mode 100644 index ee9d8c51..00000000 --- a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt +++ /dev/null @@ -1,68 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.intellij.psi.JavaPsiFacade -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiNamedElement -import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser -import org.jetbrains.kotlin.asJava.elements.KtLightElement -import org.jetbrains.kotlin.asJava.elements.KtLightMethod -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtClass -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtParameter -import org.jetbrains.kotlin.psi.KtPropertyAccessor - -class KotlinAsJavaDocumentationBuilder - @Inject constructor(val kotlinAsJavaDocumentationParser: KotlinAsJavaDocumentationParser) : PackageDocumentationBuilder -{ - override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, - packageName: FqName, - packageNode: DocumentationNode, - declarations: List<DeclarationDescriptor>, - allFqNames: Collection<FqName>) { - val project = documentationBuilder.resolutionFacade.project - val psiPackage = JavaPsiFacade.getInstance(project).findPackage(packageName.asString()) - if (psiPackage == null) { - documentationBuilder.logger.error("Cannot find Java package by qualified name: ${packageName.asString()}") - return - } - - val javaDocumentationBuilder = JavaPsiDocumentationBuilder(documentationBuilder.passConfiguration, - documentationBuilder.refGraph, - kotlinAsJavaDocumentationParser) - - psiPackage.classes.filter { it is KtLightElement<*, *> }.filter { it.isVisibleInDocumentation() }.forEach { - javaDocumentationBuilder.appendClasses(packageNode, arrayOf(it)) - } - } - - fun PsiClass.isVisibleInDocumentation(): Boolean { - val origin: KtDeclaration = (this as KtLightElement<*, *>).kotlinOrigin as? KtDeclaration ?: return true - - return !origin.hasModifier(KtTokens.INTERNAL_KEYWORD) && !origin.hasModifier(KtTokens.PRIVATE_KEYWORD) - } -} - -class KotlinAsJavaDocumentationParser - @Inject constructor(val resolutionFacade: DokkaResolutionFacade, - val descriptorDocumentationParser: DescriptorDocumentationParser) : JavaDocumentationParser -{ - override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { - val kotlinLightElement = element as? KtLightElement<*, *> ?: return JavadocParseResult.Empty - val origin = kotlinLightElement.kotlinOrigin as? KtDeclaration ?: return JavadocParseResult.Empty - if (origin is KtParameter) { - // LazyDeclarationResolver does not support setter parameters - val grandFather = origin.parent?.parent - if (grandFather is KtPropertyAccessor) { - return JavadocParseResult.Empty - } - } - val isDefaultNoArgConstructor = kotlinLightElement is KtLightMethod && origin is KtClass - val descriptor = resolutionFacade.resolveToDescriptor(origin) - val content = descriptorDocumentationParser.parseDocumentation(descriptor, origin is KtParameter, isDefaultNoArgConstructor) - return JavadocParseResult(content, null) - } -} diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt deleted file mode 100644 index 20ea179e..00000000 --- a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.asJava.toLightElements -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.psi.KtElement - -class KotlinAsJavaElementSignatureProvider : ElementSignatureProvider { - - private fun PsiElement.javaLikePsi() = when { - this is KtElement -> toLightElements().firstOrNull() - else -> this - } - - override fun signature(forPsi: PsiElement): String { - return getSignature(forPsi.javaLikePsi()) ?: - "not implemented for $forPsi" - } - - override fun signature(forDesc: DeclarationDescriptor): String { - val sourcePsi = forDesc.sourcePsi() - return getSignature(sourcePsi?.javaLikePsi()) ?: - "not implemented for $forDesc with psi: $sourcePsi" - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt deleted file mode 100644 index c7187b23..00000000 --- a/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt +++ /dev/null @@ -1,33 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiMember -import com.intellij.psi.PsiPackage -import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade -import org.jetbrains.kotlin.asJava.elements.KtLightElement -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.BindingContext -import javax.inject.Inject - -class KotlinElementSignatureProvider @Inject constructor( - val resolutionFacade: DokkaResolutionFacade -) : ElementSignatureProvider { - override fun signature(forPsi: PsiElement): String { - return forPsi.extractDescriptor(resolutionFacade) - ?.let { signature(it) } - ?: run { "no desc for $forPsi in ${(forPsi as? PsiMember)?.containingClass}" } - } - - override fun signature(forDesc: DeclarationDescriptor): String = forDesc.signature() -} - - -fun PsiElement.extractDescriptor(resolutionFacade: DokkaResolutionFacade): DeclarationDescriptor? = - when (val forPsi = this) { - is KtLightClassForFacade -> resolutionFacade.moduleDescriptor.getPackage(forPsi.fqName) - is KtLightElement<*, *> -> (forPsi.kotlinOrigin!!).extractDescriptor(resolutionFacade) - is PsiPackage -> resolutionFacade.moduleDescriptor.getPackage(FqName(forPsi.qualifiedName)) - is PsiMember -> forPsi.getJavaOrKotlinMemberDescriptor(resolutionFacade) - else -> resolutionFacade.resolveSession.bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, forPsi] - } diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt deleted file mode 100644 index 7310610f..00000000 --- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt +++ /dev/null @@ -1,473 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.LanguageService.RenderMode - -/** - * Implements [LanguageService] and provides rendering of symbols in Kotlin language - */ -class KotlinLanguageService : CommonLanguageService() { - override fun showModifierInSummary(node: DocumentationNode): Boolean { - return node.name !in fullOnlyModifiers - } - - private val fullOnlyModifiers = - setOf("public", "protected", "private", "internal", "inline", "noinline", "crossinline", "reified") - - override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode { - return content { - when (node.kind) { - NodeKind.Package -> if (renderMode == RenderMode.FULL) renderPackage(node) - in NodeKind.classLike -> renderClass(node, renderMode) - - NodeKind.EnumItem, - NodeKind.ExternalClass -> if (renderMode == RenderMode.FULL) identifier(node.name) - - NodeKind.Parameter -> renderParameter(node, renderMode) - NodeKind.TypeParameter -> renderTypeParameter(node, renderMode) - NodeKind.Type, - NodeKind.UpperBound -> renderType(node, renderMode) - - NodeKind.Modifier -> renderModifier(this, node, renderMode) - NodeKind.Constructor, - NodeKind.Function, - NodeKind.CompanionObjectFunction -> renderFunction(node, renderMode) - NodeKind.Property, - NodeKind.CompanionObjectProperty -> renderProperty(node, renderMode) - else -> identifier(node.name) - } - } - } - - - override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? { - if (nodes.size < 2) return null - val receiverKind = nodes.getReceiverKind() ?: return null - val functionWithTypeParameter = nodes.firstOrNull { it.details(NodeKind.TypeParameter).any() } ?: return null - return content { - val typeParameter = functionWithTypeParameter.details(NodeKind.TypeParameter).first() - if (functionWithTypeParameter.kind == NodeKind.Function) { - renderFunction( - functionWithTypeParameter, - RenderMode.SUMMARY, - SummarizingMapper(receiverKind, typeParameter.name) - ) - } else { - renderProperty( - functionWithTypeParameter, - RenderMode.SUMMARY, - SummarizingMapper(receiverKind, typeParameter.name) - ) - } - } - } - - private fun List<DocumentationNode>.getReceiverKind(): ReceiverKind? { - val qNames = mapNotNull { it.getReceiverQName() } - if (qNames.size != size) - return null - - return ReceiverKind.values().firstOrNull { kind -> qNames.all { it in kind.classes } } - } - - private fun DocumentationNode.getReceiverQName(): String? { - if (kind != NodeKind.Function && kind != NodeKind.Property) return null - val receiver = details(NodeKind.Receiver).singleOrNull() ?: return null - return receiver.detail(NodeKind.Type).qualifiedNameFromType() - } - - companion object { - private val arrayClasses = setOf( - "kotlin.Array", - "kotlin.BooleanArray", - "kotlin.ByteArray", - "kotlin.CharArray", - "kotlin.ShortArray", - "kotlin.IntArray", - "kotlin.LongArray", - "kotlin.FloatArray", - "kotlin.DoubleArray" - ) - - private val arrayOrListClasses = setOf("kotlin.List") + arrayClasses - - private val iterableClasses = setOf( - "kotlin.Collection", - "kotlin.Sequence", - "kotlin.Iterable", - "kotlin.Map", - "kotlin.String", - "kotlin.CharSequence" - ) + arrayOrListClasses - } - - private enum class ReceiverKind(val receiverName: String, val classes: Collection<String>) { - ARRAY("any_array", arrayClasses), - ARRAY_OR_LIST("any_array_or_list", arrayOrListClasses), - ITERABLE("any_iterable", iterableClasses), - } - - interface SignatureMapper { - fun renderReceiver(receiver: DocumentationNode, to: ContentBlock) - } - - private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String) : SignatureMapper { - override fun renderReceiver(receiver: DocumentationNode, to: ContentBlock) { - to.append(ContentIdentifier(kind.receiverName, IdentifierKind.SummarizedTypeName)) - to.text("<$typeParameterName>") - } - } - - private fun ContentBlock.renderFunctionalTypeParameterName(node: DocumentationNode, renderMode: RenderMode) { - node.references(RefKind.HiddenAnnotation).map { it.to } - .find { it.name == "ParameterName" }?.let { - val parameterNameValue = it.detail(NodeKind.Parameter).detail(NodeKind.Value) - identifier(parameterNameValue.name.removeSurrounding("\""), IdentifierKind.ParameterName) - symbol(":") - nbsp() - } - } - - private fun ContentBlock.renderFunctionalType(node: DocumentationNode, renderMode: RenderMode) { - var typeArguments = node.details(NodeKind.Type) - - if (node.name.startsWith("Suspend")) { - keyword("suspend ") - } - - // lambda - val isExtension = node.annotations.any { it.name == "ExtensionFunctionType" } - if (isExtension) { - renderType(typeArguments.first(), renderMode) - symbol(".") - typeArguments = typeArguments.drop(1) - } - symbol("(") - renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) { - renderFunctionalTypeParameterName(it, renderMode) - renderType(it, renderMode) - } - symbol(")") - nbsp() - symbol("->") - nbsp() - renderType(typeArguments.last(), renderMode) - - } - - private fun DocumentationNode.isFunctionalType(): Boolean { - val typeArguments = details(NodeKind.Type) - val functionalTypeName = "Function${typeArguments.count() - 1}" - val suspendFunctionalTypeName = "Suspend$functionalTypeName" - return name == functionalTypeName || name == suspendFunctionalTypeName - } - - private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) { - if (node.name == "dynamic") { - keyword("dynamic") - return - } - - val nullabilityModifier = node.detailOrNull(NodeKind.NullabilityModifier) - - if (node.isFunctionalType()) { - if (nullabilityModifier != null) { - symbol("(") - renderFunctionalType(node, renderMode) - symbol(")") - symbol(nullabilityModifier.name) - } else { - renderFunctionalType(node, renderMode) - } - return - } - if (renderMode == RenderMode.FULL) { - renderAnnotationsForNode(node) - } - renderModifiersForNode(node, renderMode, true) - renderLinked(this, node) { - identifier(it.typeDeclarationClass?.classNodeNameWithOuterClass() ?: it.name, IdentifierKind.TypeName) - } - val typeArguments = node.details(NodeKind.Type) - if (typeArguments.isNotEmpty()) { - symbol("<") - renderList(typeArguments, noWrap = true) { - renderType(it, renderMode) - } - symbol(">") - } - - nullabilityModifier ?.apply { - symbol(nullabilityModifier.name) - } - } - - override fun renderModifier( - block: ContentBlock, - node: DocumentationNode, - renderMode: RenderMode, - nowrap: Boolean - ) { - when (node.name) { - "final", "public", "var", "expect", "actual", "external" -> { - } - else -> { - if (showModifierInSummary(node) || renderMode == RenderMode.FULL) { - super.renderModifier(block, node, renderMode, nowrap) - } - } - } - } - - private fun ContentBlock.renderTypeParameter(node: DocumentationNode, renderMode: RenderMode) { - renderModifiersForNode(node, renderMode, true) - - identifier(node.name) - - val constraints = node.details(NodeKind.UpperBound) - if (constraints.size == 1) { - nbsp() - symbol(":") - nbsp() - renderList(constraints, noWrap = true) { - renderType(it, renderMode) - } - } - } - - private fun ContentBlock.renderParameter(node: DocumentationNode, renderMode: RenderMode) { - if (renderMode == RenderMode.FULL) { - renderAnnotationsForNode(node) - } - renderModifiersForNode(node, renderMode) - identifier(node.name, IdentifierKind.ParameterName, node.detailOrNull(NodeKind.Signature)?.name) - symbol(":") - nbsp() - val parameterType = node.detail(NodeKind.Type) - renderType(parameterType, renderMode) - val valueNode = node.details(NodeKind.Value).firstOrNull() - if (valueNode != null) { - nbsp() - symbol("=") - nbsp() - text(valueNode.name) - } - } - - private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode, renderMode: RenderMode) { - val typeParameters = node.details(NodeKind.TypeParameter) - if (typeParameters.any()) { - symbol("<") - renderList(typeParameters) { - renderTypeParameter(it, renderMode) - } - symbol(">") - } - } - - private fun ContentBlock.renderExtraTypeParameterConstraints(node: DocumentationNode, renderMode: RenderMode) { - val parametersWithMultipleConstraints = - node.details(NodeKind.TypeParameter).filter { it.details(NodeKind.UpperBound).size > 1 } - val parametersWithConstraints = parametersWithMultipleConstraints - .flatMap { parameter -> - parameter.details(NodeKind.UpperBound).map { constraint -> parameter to constraint } - } - if (parametersWithMultipleConstraints.isNotEmpty()) { - keyword(" where ") - renderList(parametersWithConstraints) { - identifier(it.first.name) - nbsp() - symbol(":") - nbsp() - renderType(it.second, renderMode) - } - } - } - - private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) { - val supertypes = node.details(NodeKind.Supertype).filterNot { it.qualifiedNameFromType() in ignoredSupertypes } - if (supertypes.any()) { - nbsp() - symbol(":") - nbsp() - renderList(supertypes) { - indentedSoftLineBreak() - renderType(it, renderMode) - } - } - } - - private fun ContentBlock.renderAnnotationsForNode(node: DocumentationNode) { - node.annotations.forEach { - renderAnnotation(it) - } - } - - private fun ContentBlock.renderAnnotation(node: DocumentationNode) { - identifier("@" + node.name, IdentifierKind.AnnotationName) - val parameters = node.details(NodeKind.Parameter) - if (!parameters.isEmpty()) { - symbol("(") - renderList(parameters) { - text(it.detail(NodeKind.Value).name) - } - symbol(")") - } - text(" ") - } - - private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) { - if (renderMode == RenderMode.FULL) { - renderAnnotationsForNode(node) - } - renderModifiersForNode(node, renderMode) - when (node.kind) { - NodeKind.Class, - NodeKind.AnnotationClass, - NodeKind.Exception, - NodeKind.Enum -> keyword("class ") - NodeKind.Interface -> keyword("interface ") - NodeKind.EnumItem -> keyword("enum val ") - NodeKind.Object -> keyword("object ") - NodeKind.TypeAlias -> keyword("typealias ") - else -> throw IllegalArgumentException("Node $node is not a class-like object") - } - - identifierOrDeprecated(node) - renderTypeParametersForNode(node, renderMode) - renderSupertypesForNode(node, renderMode) - renderExtraTypeParameterConstraints(node, renderMode) - - if (node.kind == NodeKind.TypeAlias) { - nbsp() - symbol("=") - nbsp() - renderType(node.detail(NodeKind.TypeAliasUnderlyingType), renderMode) - } - } - - private fun ContentBlock.renderFunction( - node: DocumentationNode, - renderMode: RenderMode, - signatureMapper: SignatureMapper? = null - ) { - if (renderMode == RenderMode.FULL) { - renderAnnotationsForNode(node) - } - renderModifiersForNode(node, renderMode) - when (node.kind) { - NodeKind.Constructor -> identifier(node.owner!!.name) - NodeKind.Function, - NodeKind.CompanionObjectFunction -> keyword("fun ") - else -> throw IllegalArgumentException("Node $node is not a function-like object") - } - renderTypeParametersForNode(node, renderMode) - if (node.details(NodeKind.TypeParameter).any()) { - text(" ") - } - - renderReceiver(node, renderMode, signatureMapper) - - if (node.kind != NodeKind.Constructor) - identifierOrDeprecated(node) - - symbol("(") - val parameters = node.details(NodeKind.Parameter) - renderList(parameters) { - indentedSoftLineBreak() - renderParameter(it, renderMode) - } - if (needReturnType(node)) { - if (parameters.isNotEmpty()) { - softLineBreak() - } - symbol(")") - symbol(": ") - renderType(node.detail(NodeKind.Type), renderMode) - } else { - symbol(")") - } - renderExtraTypeParameterConstraints(node, renderMode) - } - - private fun ContentBlock.renderReceiver( - node: DocumentationNode, - renderMode: RenderMode, - signatureMapper: SignatureMapper? - ) { - val receiver = node.details(NodeKind.Receiver).singleOrNull() - if (receiver != null) { - if (signatureMapper != null) { - signatureMapper.renderReceiver(receiver, this) - } else { - val type = receiver.detail(NodeKind.Type) - - if (type.isFunctionalType()) { - symbol("(") - renderFunctionalType(type, renderMode) - symbol(")") - } else { - renderType(type, renderMode) - } - } - symbol(".") - } - } - - private fun needReturnType(node: DocumentationNode) = when (node.kind) { - NodeKind.Constructor -> false - else -> !node.isUnitReturnType() - } - - fun DocumentationNode.isUnitReturnType(): Boolean = - detail(NodeKind.Type).hiddenLinks.firstOrNull()?.qualifiedName() == "kotlin.Unit" - - private fun ContentBlock.renderProperty( - node: DocumentationNode, - renderMode: RenderMode, - signatureMapper: SignatureMapper? = null - ) { - if (renderMode == RenderMode.FULL) { - renderAnnotationsForNode(node) - } - renderModifiersForNode(node, renderMode) - when (node.kind) { - NodeKind.Property, - NodeKind.CompanionObjectProperty -> keyword("${node.getPropertyKeyword()} ") - else -> throw IllegalArgumentException("Node $node is not a property") - } - renderTypeParametersForNode(node, renderMode) - if (node.details(NodeKind.TypeParameter).any()) { - text(" ") - } - - renderReceiver(node, renderMode, signatureMapper) - - identifierOrDeprecated(node) - symbol(": ") - renderType(node.detail(NodeKind.Type), renderMode) - renderExtraTypeParameterConstraints(node, renderMode) - } - - fun DocumentationNode.getPropertyKeyword() = - if (details(NodeKind.Modifier).any { it.name == "var" }) "var" else "val" - - fun ContentBlock.identifierOrDeprecated(node: DocumentationNode) { - if (node.deprecation != null) { - val strike = ContentStrikethrough() - strike.identifier(node.name) - append(strike) - } else { - identifier(node.name) - } - } -} - -fun DocumentationNode.qualifiedNameFromType(): String { - return details.firstOrNull { it.kind == NodeKind.QualifiedName }?.name - ?: (links.firstOrNull() ?: hiddenLinks.firstOrNull())?.qualifiedName() - ?: name -} - - -val DocumentationNode.typeDeclarationClass - get() = (links.firstOrNull { it.kind in NodeKind.classLike } ?: externalType) diff --git a/core/src/main/kotlin/Languages/CommonLanguageService.kt b/core/src/main/kotlin/Languages/CommonLanguageService.kt deleted file mode 100644 index ddc95d32..00000000 --- a/core/src/main/kotlin/Languages/CommonLanguageService.kt +++ /dev/null @@ -1,84 +0,0 @@ -package org.jetbrains.dokka - - -abstract class CommonLanguageService : LanguageService { - - protected fun ContentBlock.renderPackage(node: DocumentationNode) { - keyword("package") - nbsp() - identifier(node.name) - } - - override fun renderName(node: DocumentationNode): String { - return when (node.kind) { - NodeKind.Constructor -> node.owner!!.name - else -> node.name - } - } - - open fun renderModifier( - block: ContentBlock, - node: DocumentationNode, - renderMode: LanguageService.RenderMode, - nowrap: Boolean = false - ) = with(block) { - keyword(node.name) - if (nowrap) { - nbsp() - } else { - text(" ") - } - } - - protected fun renderLinked( - block: ContentBlock, - node: DocumentationNode, - body: ContentBlock.(DocumentationNode) -> Unit - ) = with(block) { - val to = node.links.firstOrNull() - if (to == null) - body(node) - else - link(to) { - this.body(node) - } - } - - protected fun <T> ContentBlock.renderList( - nodes: List<T>, separator: String = ", ", - noWrap: Boolean = false, renderItem: (T) -> Unit - ) { - if (nodes.none()) - return - renderItem(nodes.first()) - nodes.drop(1).forEach { - if (noWrap) { - symbol(separator.removeSuffix(" ")) - nbsp() - } else { - symbol(separator) - } - renderItem(it) - } - } - - abstract fun showModifierInSummary(node: DocumentationNode): Boolean - - protected fun ContentBlock.renderModifiersForNode( - node: DocumentationNode, - renderMode: LanguageService.RenderMode, - nowrap: Boolean = false - ) { - val modifiers = node.details(NodeKind.Modifier) - for (it in modifiers) { - if (node.kind == NodeKind.Interface && it.name == "abstract") - continue - if (renderMode == LanguageService.RenderMode.SUMMARY && !showModifierInSummary(it)) { - continue - } - renderModifier(this, it, renderMode, nowrap) - } - } - - -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Languages/JavaLanguageService.kt b/core/src/main/kotlin/Languages/JavaLanguageService.kt deleted file mode 100644 index ad66123b..00000000 --- a/core/src/main/kotlin/Languages/JavaLanguageService.kt +++ /dev/null @@ -1,171 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.LanguageService.RenderMode - -/** - * Implements [LanguageService] and provides rendering of symbols in Java language - */ -class JavaLanguageService : LanguageService { - override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode { - return ContentText(when (node.kind) { - NodeKind.Package -> renderPackage(node) - in NodeKind.classLike -> renderClass(node) - - NodeKind.TypeParameter -> renderTypeParameter(node) - NodeKind.Type, - NodeKind.UpperBound -> renderType(node) - - NodeKind.Constructor, - NodeKind.Function -> renderFunction(node) - NodeKind.Property -> renderProperty(node) - else -> "${node.kind}: ${node.name}" - }) - } - - override fun renderName(node: DocumentationNode): String { - return when (node.kind) { - NodeKind.Constructor -> node.owner!!.name - else -> node.name - } - } - - override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null - - private fun renderPackage(node: DocumentationNode): String { - return "package ${node.name}" - } - - private fun renderModifier(node: DocumentationNode): String { - return when (node.name) { - "open" -> "" - "internal" -> "" - else -> node.name - } - } - - fun getArrayElementType(node: DocumentationNode): DocumentationNode? = when (node.qualifiedName()) { - "kotlin.Array" -> - node.details(NodeKind.Type).singleOrNull()?.let { et -> getArrayElementType(et) ?: et } ?: - DocumentationNode("Object", node.content, NodeKind.ExternalClass) - - "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", - "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> - DocumentationNode(node.name.removeSuffix("Array").toLowerCase(), node.content, NodeKind.Type) - - else -> null - } - - fun getArrayDimension(node: DocumentationNode): Int = when (node.qualifiedName()) { - "kotlin.Array" -> - 1 + (node.details(NodeKind.Type).singleOrNull()?.let { getArrayDimension(it) } ?: 0) - - "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", - "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> - 1 - else -> 0 - } - - fun renderType(node: DocumentationNode): String { - return when (node.name) { - "Unit" -> "void" - "Int" -> "int" - "Long" -> "long" - "Double" -> "double" - "Float" -> "float" - "Char" -> "char" - "Boolean" -> "bool" - // TODO: render arrays - else -> node.name - } - } - - private fun renderTypeParameter(node: DocumentationNode): String { - val constraints = node.details(NodeKind.UpperBound) - return if (constraints.none()) - node.name - else { - node.name + " extends " + constraints.joinToString { renderType(node) } - } - } - - private fun renderParameter(node: DocumentationNode): String { - return "${renderType(node.detail(NodeKind.Type))} ${node.name}" - } - - private fun renderTypeParametersForNode(node: DocumentationNode): String { - return StringBuilder().apply { - val typeParameters = node.details(NodeKind.TypeParameter) - if (typeParameters.any()) { - append("<") - append(typeParameters.joinToString { renderTypeParameter(it) }) - append("> ") - } - }.toString() - } - - private fun renderModifiersForNode(node: DocumentationNode): String { - val modifiers = node.details(NodeKind.Modifier).map { renderModifier(it) }.filter { it != "" } - if (modifiers.none()) - return "" - return modifiers.joinToString(" ", postfix = " ") - } - - private fun renderClass(node: DocumentationNode): String { - return StringBuilder().apply { - when (node.kind) { - NodeKind.Class -> append("class ") - NodeKind.Interface -> append("interface ") - NodeKind.Enum -> append("enum ") - NodeKind.EnumItem -> append("enum value ") - NodeKind.Object -> append("class ") - else -> throw IllegalArgumentException("Node $node is not a class-like object") - } - - append(node.name) - append(renderTypeParametersForNode(node)) - }.toString() - } - - private fun renderFunction(node: DocumentationNode): String { - return StringBuilder().apply { - when (node.kind) { - NodeKind.Constructor -> append(node.owner?.name) - NodeKind.Function -> { - append(renderTypeParametersForNode(node)) - append(renderType(node.detail(NodeKind.Type))) - append(" ") - append(node.name) - } - else -> throw IllegalArgumentException("Node $node is not a function-like object") - } - - val receiver = node.details(NodeKind.Receiver).singleOrNull() - append("(") - if (receiver != null) - (listOf(receiver) + node.details(NodeKind.Parameter)).joinTo(this) { renderParameter(it) } - else - node.details(NodeKind.Parameter).joinTo(this) { renderParameter(it) } - - append(")") - }.toString() - } - - private fun renderProperty(node: DocumentationNode): String { - return StringBuilder().apply { - when (node.kind) { - NodeKind.Property -> append("val ") - else -> throw IllegalArgumentException("Node $node is not a property") - } - append(renderTypeParametersForNode(node)) - val receiver = node.details(NodeKind.Receiver).singleOrNull() - if (receiver != null) { - append(renderType(receiver.detail(NodeKind.Type))) - append(".") - } - - append(node.name) - append(": ") - append(renderType(node.detail(NodeKind.Type))) - }.toString() - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Languages/LanguageService.kt b/core/src/main/kotlin/Languages/LanguageService.kt deleted file mode 100644 index b0f4bbc9..00000000 --- a/core/src/main/kotlin/Languages/LanguageService.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.jetbrains.dokka - -/** - * Provides facility for rendering [DocumentationNode] as a language-dependent declaration - */ -interface LanguageService { - enum class RenderMode { - /** Brief signature (used in a list of all members of the class). */ - SUMMARY, - /** Full signature (used in the page describing the member itself */ - FULL - } - - /** - * Renders a [node] as a class, function, property or other signature in a target language. - * @param node A [DocumentationNode] to render - * @return [ContentNode] which is a root for a rich content tree suitable for formatting with [FormatService] - */ - fun render(node: DocumentationNode, renderMode: RenderMode = RenderMode.FULL): ContentNode - - /** - * Tries to summarize the signatures of the specified documentation nodes in a compact representation. - * Returns the representation if successful, or null if the signatures could not be summarized. - */ - fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? - - /** - * Renders [node] as a named representation in the target language - * - * For example: - * ${code org.jetbrains.dokka.example} - * - * $node: A [DocumentationNode] to render - * $returns: [String] which is a string representation of the node's name - */ - fun renderName(node: DocumentationNode): String -} - -fun example(service: LanguageService, node: DocumentationNode) { - println("Node name: ${service.renderName(node)}") -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Languages/NewJavaLanguageService.kt b/core/src/main/kotlin/Languages/NewJavaLanguageService.kt deleted file mode 100644 index 992cd090..00000000 --- a/core/src/main/kotlin/Languages/NewJavaLanguageService.kt +++ /dev/null @@ -1,197 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.LanguageService.RenderMode - -/** - * Implements [LanguageService] and provides rendering of symbols in Java language - */ -class NewJavaLanguageService : CommonLanguageService() { - override fun showModifierInSummary(node: DocumentationNode): Boolean { - return true - } - - override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode { - return content { - (when (node.kind) { - NodeKind.Package -> renderPackage(node) - in NodeKind.classLike -> renderClass(node, renderMode) - - NodeKind.Modifier -> renderModifier(this, node, renderMode) - NodeKind.TypeParameter -> renderTypeParameter(node) - NodeKind.Type, - NodeKind.UpperBound -> renderType(node) - NodeKind.Parameter -> renderParameter(node) - NodeKind.Constructor, - NodeKind.Function -> renderFunction(node) - NodeKind.Property -> renderProperty(node) - else -> "${node.kind}: ${node.name}" - }) - } - } - - override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null - - - override fun renderModifier(block: ContentBlock, node: DocumentationNode, renderMode: RenderMode, nowrap: Boolean) { - when (node.name) { - "open", "internal" -> { - } - else -> super.renderModifier(block, node, renderMode, nowrap) - } - } - - fun getArrayElementType(node: DocumentationNode): DocumentationNode? = when (node.qualifiedName()) { - "kotlin.Array" -> - node.details(NodeKind.Type).singleOrNull()?.let { et -> getArrayElementType(et) ?: et } - ?: DocumentationNode("Object", node.content, NodeKind.ExternalClass) - - "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", - "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> - DocumentationNode(node.name.removeSuffix("Array").toLowerCase(), node.content, NodeKind.Type) - - else -> null - } - - fun getArrayDimension(node: DocumentationNode): Int = when (node.qualifiedName()) { - "kotlin.Array" -> - 1 + (node.details(NodeKind.Type).singleOrNull()?.let { getArrayDimension(it) } ?: 0) - - "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", - "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> - 1 - else -> 0 - } - - fun ContentBlock.renderType(node: DocumentationNode) { - when (node.name) { - "Unit" -> identifier("void") - "Int" -> identifier("int") - "Long" -> identifier("long") - "Double" -> identifier("double") - "Float" -> identifier("float") - "Char" -> identifier("char") - "Boolean" -> identifier("bool") - // TODO: render arrays - else -> renderLinked(this, node) { - identifier(node.name) - } - } - } - - private fun ContentBlock.renderTypeParameter(node: DocumentationNode) { - val constraints = node.details(NodeKind.UpperBound) - if (constraints.none()) - identifier(node.name) - else { - identifier(node.name) - text(" ") - keyword("extends") - text(" ") - constraints.forEach { renderType(node) } - } - } - - private fun ContentBlock.renderParameter(node: DocumentationNode) { - renderType(node.detail(NodeKind.Type)) - text(" ") - identifier(node.name) - } - - private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode) { - val typeParameters = node.details(NodeKind.TypeParameter) - if (typeParameters.any()) { - symbol("<") - renderList(typeParameters, noWrap = true) { - renderTypeParameter(it) - } - symbol(">") - text(" ") - } - } - -// private fun renderModifiersForNode(node: DocumentationNode): String { -// val modifiers = node.details(NodeKind.Modifier).map { renderModifier(it) }.filter { it != "" } -// if (modifiers.none()) -// return "" -// return modifiers.joinToString(" ", postfix = " ") -// } - - private fun ContentBlock.renderClassKind(node: DocumentationNode) { - when (node.kind) { - NodeKind.Interface -> { - keyword("interface") - } - NodeKind.EnumItem -> { - keyword("enum value") - } - NodeKind.Enum -> { - keyword("enum") - } - NodeKind.Class, NodeKind.Exception, NodeKind.Object -> { - keyword("class") - } - else -> throw IllegalArgumentException("Node $node is not a class-like object") - } - text(" ") - } - - private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) { - renderModifiersForNode(node, renderMode) - renderClassKind(node) - - identifier(node.name) - renderTypeParametersForNode(node) - } - - private fun ContentBlock.renderParameters(nodes: List<DocumentationNode>) { - renderList(nodes) { - renderParameter(it) - } - } - - private fun ContentBlock.renderFunction(node: DocumentationNode) { - when (node.kind) { - NodeKind.Constructor -> identifier(node.owner?.name ?: "") - NodeKind.Function -> { - renderTypeParametersForNode(node) - renderType(node.detail(NodeKind.Type)) - text(" ") - identifier(node.name) - - } - else -> throw IllegalArgumentException("Node $node is not a function-like object") - } - - val receiver = node.details(NodeKind.Receiver).singleOrNull() - symbol("(") - if (receiver != null) - renderParameters(listOf(receiver) + node.details(NodeKind.Parameter)) - else - renderParameters(node.details(NodeKind.Parameter)) - - symbol(")") - } - - private fun ContentBlock.renderProperty(node: DocumentationNode) { - - when (node.kind) { - NodeKind.Property -> { - keyword("val") - text(" ") - } - else -> throw IllegalArgumentException("Node $node is not a property") - } - renderTypeParametersForNode(node) - val receiver = node.details(NodeKind.Receiver).singleOrNull() - if (receiver != null) { - renderType(receiver.detail(NodeKind.Type)) - symbol(".") - } - - identifier(node.name) - symbol(":") - text(" ") - renderType(node.detail(NodeKind.Type)) - - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Locations/Location.kt b/core/src/main/kotlin/Locations/Location.kt deleted file mode 100644 index 63c9b913..00000000 --- a/core/src/main/kotlin/Locations/Location.kt +++ /dev/null @@ -1,76 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -interface Location { - val path: String get - fun relativePathTo(other: Location, anchor: String? = null): String -} - -/** - * Represents locations in the documentation in the form of [path](File). - * - * $file: [File] for this location - * $path: [String] representing path of this location - */ -data class FileLocation(val file: File) : Location { - override val path: String - get() = file.path - - override fun relativePathTo(other: Location, anchor: String?): String { - if (other !is FileLocation) { - throw IllegalArgumentException("$other is not a FileLocation") - } - if (file.path.substringBeforeLast(".") == other.file.path.substringBeforeLast(".") && anchor == null) { - return "./${file.name}" - } - val ownerFolder = file.parentFile!! - val relativePath = ownerFolder.toPath().relativize(other.file.toPath()).toString().replace(File.separatorChar, '/') - return if (anchor == null) relativePath else "$relativePath#$anchor" - } -} - -fun relativePathToNode(qualifiedName: List<String>, hasMembers: Boolean): String { - val parts = qualifiedName.map { identifierToFilename(it) }.filterNot { it.isEmpty() } - return if (!hasMembers) { - // leaf node, use file in owner's folder - parts.joinToString("/") - } else { - parts.joinToString("/") + (if (parts.none()) "" else "/") + "index" - } -} - -fun nodeActualQualifier(node: DocumentationNode): List<DocumentationNode> { - val topLevelPage = node.references(RefKind.TopLevelPage).singleOrNull()?.to - if (topLevelPage != null) { - return nodeActualQualifier(topLevelPage) - } - return node.owner?.let { nodeActualQualifier(it) }.orEmpty() + node -} - -fun relativePathToNode(node: DocumentationNode): String { - val qualifier = nodeActualQualifier(node) - return relativePathToNode( - qualifier.map { it.name }, - qualifier.last().members.any() - ) -} - - -fun identifierToFilename(path: String): String { - if (path.isEmpty()) return "--root--" - val escaped = path.replace('<', '-').replace('>', '-') - val lowercase = escaped.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() } - return if (lowercase == "index") "--index--" else lowercase -} - -fun NodeLocationAwareGenerator.relativePathToLocation(owner: DocumentationNode, node: DocumentationNode): String { - return location(owner).relativePathTo(location(node), null) -} - -fun NodeLocationAwareGenerator.relativePathToRoot(from: Location): File { - val file = File(from.path).parentFile - return root.relativeTo(file) -} - -fun File.toUnixString() = toString().replace(File.separatorChar, '/') diff --git a/core/src/main/kotlin/Markdown/MarkdownProcessor.kt b/core/src/main/kotlin/Markdown/MarkdownProcessor.kt deleted file mode 100644 index 2c8f7a73..00000000 --- a/core/src/main/kotlin/Markdown/MarkdownProcessor.kt +++ /dev/null @@ -1,53 +0,0 @@ -package org.jetbrains.dokka - -import org.intellij.markdown.IElementType -import org.intellij.markdown.MarkdownElementTypes -import org.intellij.markdown.ast.ASTNode -import org.intellij.markdown.ast.LeafASTNode -import org.intellij.markdown.ast.getTextInNode -import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor -import org.intellij.markdown.parser.MarkdownParser - -class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val markdown: String) { - val children: List<MarkdownNode> = node.children.map { MarkdownNode(it, this, markdown) } - val type: IElementType get() = node.type - val text: String get() = node.getTextInNode(markdown).toString() - fun child(type: IElementType): MarkdownNode? = children.firstOrNull { it.type == type } - - val previous get() = parent?.children?.getOrNull(parent.children.indexOf(this) - 1) - - override fun toString(): String = StringBuilder().apply { presentTo(this) }.toString() -} - -fun MarkdownNode.visit(action: (MarkdownNode, () -> Unit) -> Unit) { - action(this) { - for (child in children) { - child.visit(action) - } - } -} - -fun MarkdownNode.toTestString(): String { - val sb = StringBuilder() - var level = 0 - visit { node, visitChildren -> - sb.append(" ".repeat(level * 2)) - node.presentTo(sb) - sb.appendln() - level++ - visitChildren() - level-- - } - return sb.toString() -} - -private fun MarkdownNode.presentTo(sb: StringBuilder) { - sb.append(type.toString()) - sb.append(":" + text.replace("\n", "\u23CE")) -} - -fun parseMarkdown(markdown: String): MarkdownNode { - if (markdown.isEmpty()) - return MarkdownNode(LeafASTNode(MarkdownElementTypes.MARKDOWN_FILE, 0, 0), null, markdown) - return MarkdownNode(MarkdownParser(CommonMarkFlavourDescriptor()).buildMarkdownTreeFromString(markdown), null, markdown) -} diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt deleted file mode 100644 index 5530e1b4..00000000 --- a/core/src/main/kotlin/Model/Content.kt +++ /dev/null @@ -1,290 +0,0 @@ -package org.jetbrains.dokka - -interface ContentNode { - val textLength: Int -} - -object ContentEmpty : ContentNode { - override val textLength: Int get() = 0 -} - -open class ContentBlock() : ContentNode { - open val children = arrayListOf<ContentNode>() - - fun append(node: ContentNode) { - children.add(node) - } - - fun isEmpty() = children.isEmpty() - - override fun equals(other: Any?): Boolean = - other is ContentBlock && javaClass == other.javaClass && children == other.children - - override fun hashCode(): Int = - children.hashCode() - - override val textLength: Int - get() = children.sumBy { it.textLength } -} - -class NodeRenderContent( - val node: DocumentationNode, - val mode: LanguageService.RenderMode -): ContentNode { - override val textLength: Int - get() = 0 //TODO: Clarify? -} - -class LazyContentBlock(private val fillChildren: () -> List<ContentNode>) : ContentBlock() { - private var computed = false - override val children: ArrayList<ContentNode> - get() { - if (!computed) { - computed = true - children.addAll(fillChildren()) - } - return super.children - } - - override fun equals(other: Any?): Boolean { - return other is LazyContentBlock && other.fillChildren == fillChildren && super.equals(other) - } - - override fun hashCode(): Int { - return super.hashCode() + 31 * fillChildren.hashCode() - } -} - -enum class IdentifierKind { - TypeName, - ParameterName, - AnnotationName, - SummarizedTypeName, - Other -} - -data class ContentText(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -data class ContentKeyword(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -data class ContentIdentifier(val text: String, - val kind: IdentifierKind = IdentifierKind.Other, - val signature: String? = null) : ContentNode { - override val textLength: Int - get() = text.length -} - -data class ContentSymbol(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -data class ContentEntity(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -object ContentNonBreakingSpace: ContentNode { - override val textLength: Int - get() = 1 -} - -object ContentSoftLineBreak: ContentNode { - override val textLength: Int - get() = 0 -} - -object ContentIndentedSoftLineBreak: ContentNode { - override val textLength: Int - get() = 0 -} - -class ContentParagraph() : ContentBlock() -class ContentEmphasis() : ContentBlock() -class ContentStrong() : ContentBlock() -class ContentStrikethrough() : ContentBlock() -class ContentCode() : ContentBlock() -open class ContentBlockCode(val language: String = "") : ContentBlock() -class ContentBlockSampleCode(language: String = "kotlin", val importsBlock: ContentBlockCode = ContentBlockCode(language)) : ContentBlockCode(language) - -abstract class ContentNodeLink() : ContentBlock() { - abstract val node: DocumentationNode? - abstract val text: String? -} - -object ContentHardLineBreak : ContentNode { - override val textLength: Int - get() = 0 -} - -class ContentNodeDirectLink(override val node: DocumentationNode): ContentNodeLink() { - override fun equals(other: Any?): Boolean = - super.equals(other) && other is ContentNodeDirectLink && node.name == other.node.name - - override fun hashCode(): Int = - children.hashCode() * 31 + node.name.hashCode() - - override val text: String? = null -} - -class ContentNodeLazyLink(val linkText: String, val lazyNode: () -> DocumentationNode?): ContentNodeLink() { - override val node: DocumentationNode? get() = lazyNode() - - override fun equals(other: Any?): Boolean = - super.equals(other) && other is ContentNodeLazyLink && linkText == other.linkText - - override fun hashCode(): Int = - children.hashCode() * 31 + linkText.hashCode() - - override val text: String? = linkText -} - -class ContentExternalLink(val href : String) : ContentBlock() { - override fun equals(other: Any?): Boolean = - super.equals(other) && other is ContentExternalLink && href == other.href - - override fun hashCode(): Int = - children.hashCode() * 31 + href.hashCode() -} - -data class ContentBookmark(val name: String): ContentBlock() -data class ContentLocalLink(val href: String) : ContentBlock() - -class ContentUnorderedList() : ContentBlock() -class ContentOrderedList() : ContentBlock() -class ContentListItem() : ContentBlock() - -class ContentHeading(val level: Int) : ContentBlock() - -class ContentSection(val tag: String, val subjectName: String?) : ContentBlock() { - override fun equals(other: Any?): Boolean = - super.equals(other) && other is ContentSection && tag == other.tag && subjectName == other.subjectName - - override fun hashCode(): Int = - children.hashCode() * 31 * 31 + tag.hashCode() * 31 + (subjectName?.hashCode() ?: 0) -} - -object ContentTags { - const val Description = "Description" - const val SeeAlso = "See Also" - const val Return = "Return" - const val Exceptions = "Exceptions" -} - -fun content(body: ContentBlock.() -> Unit): ContentBlock { - val block = ContentBlock() - block.body() - return block -} - -fun ContentBlock.text(value: String) = append(ContentText(value)) -fun ContentBlock.keyword(value: String) = append(ContentKeyword(value)) -fun ContentBlock.symbol(value: String) = append(ContentSymbol(value)) - -fun ContentBlock.identifier(value: String, kind: IdentifierKind = IdentifierKind.Other, signature: String? = null) { - append(ContentIdentifier(value, kind, signature)) -} - -fun ContentBlock.nbsp() = append(ContentNonBreakingSpace) -fun ContentBlock.softLineBreak() = append(ContentSoftLineBreak) -fun ContentBlock.indentedSoftLineBreak() = append(ContentIndentedSoftLineBreak) -fun ContentBlock.hardLineBreak() = append(ContentHardLineBreak) - -fun ContentBlock.strong(body: ContentBlock.() -> Unit) { - val strong = ContentStrong() - strong.body() - append(strong) -} - -fun ContentBlock.code(body: ContentBlock.() -> Unit) { - val code = ContentCode() - code.body() - append(code) -} - -fun ContentBlock.link(to: DocumentationNode, body: ContentBlock.() -> Unit) { - val block = if (to.kind == NodeKind.ExternalLink) - ContentExternalLink(to.name) - else - ContentNodeDirectLink(to) - - block.body() - append(block) -} - -open class Content(): ContentBlock() { - open val sections: List<ContentSection> get() = emptyList() - open val summary: ContentNode get() = ContentEmpty - open val description: ContentNode get() = ContentEmpty - - fun findSectionByTag(tag: String): ContentSection? = - sections.firstOrNull { tag.equals(it.tag, ignoreCase = true) } - - companion object { - val Empty = object: Content() { - override fun toString(): String { - return "EMPTY_CONTENT" - } - } - - fun of(vararg child: ContentNode): Content { - val result = MutableContent() - child.forEach { result.append(it) } - return result - } - } -} - -open class MutableContent() : Content() { - private val sectionList = arrayListOf<ContentSection>() - override val sections: List<ContentSection> - get() = sectionList - - fun addSection(tag: String?, subjectName: String?): ContentSection { - val section = ContentSection(tag ?: "", subjectName) - sectionList.add(section) - return section - } - - override val summary: ContentNode get() = children.firstOrNull() ?: ContentEmpty - - override val description: ContentNode by lazy { - val descriptionNodes = children.drop(1) - if (descriptionNodes.isEmpty()) { - ContentEmpty - } else { - val result = ContentSection(ContentTags.Description, null) - result.children.addAll(descriptionNodes) - result - } - } - - override fun equals(other: Any?): Boolean { - if (other !is Content) - return false - return sections == other.sections && children == other.children - } - - override fun hashCode(): Int { - return sections.map { it.hashCode() }.sum() - } - - override fun toString(): String { - if (sections.isEmpty()) - return "<empty>" - return (listOf(summary, description) + sections).joinToString() - } -} - -fun javadocSectionDisplayName(sectionName: String?): String? = - when(sectionName) { - "param" -> "Parameters" - "throws", "exception" -> ContentTags.Exceptions - else -> sectionName?.capitalize() - } diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt deleted file mode 100644 index 311b46e4..00000000 --- a/core/src/main/kotlin/Model/DocumentationNode.kt +++ /dev/null @@ -1,276 +0,0 @@ -package org.jetbrains.dokka - -import java.util.* - -enum class NodeKind { - Unknown, - - Package, - Class, - Interface, - Enum, - AnnotationClass, - Exception, - EnumItem, - Object, - TypeAlias, - - Constructor, - Function, - Property, - Field, - - CompanionObjectProperty, - CompanionObjectFunction, - - Parameter, - Receiver, - TypeParameter, - Type, - Supertype, - UpperBound, - LowerBound, - - TypeAliasUnderlyingType, - - Modifier, - NullabilityModifier, - - Module, - - ExternalClass, - Annotation, - - Value, - - SourceUrl, - SourcePosition, - Signature, - - ExternalLink, - QualifiedName, - Platform, - - AllTypes, - - /** - * A note which is rendered once on a page documenting a group of overloaded functions. - * Needs to be generated equally on all overloads. - */ - OverloadGroupNote, - - GroupNode; - - companion object { - val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object, TypeAlias) - val memberLike = setOf(Function, Property, Field, Constructor, CompanionObjectFunction, CompanionObjectProperty, EnumItem) - } -} - -open class DocumentationNode(val name: String, - content: Content, - val kind: NodeKind) { - - private val references = LinkedHashSet<DocumentationReference>() - - var content: Content = content - private set - - val summary: ContentNode get() = when (kind) { - NodeKind.GroupNode -> this.origins - .map { it.content } - .firstOrNull { !it.isEmpty() } - ?.summary ?: ContentEmpty - else -> content.summary - } - - - val owner: DocumentationNode? - get() = references(RefKind.Owner).singleOrNull()?.to - val details: List<DocumentationNode> - get() = references(RefKind.Detail).map { it.to } - val members: List<DocumentationNode> - get() = references(RefKind.Member).map { it.to } - val origins: List<DocumentationNode> - get() = references(RefKind.Origin).map { it.to } - - val inheritedMembers: List<DocumentationNode> - get() = references(RefKind.InheritedMember).map { it.to } - val allInheritedMembers: List<DocumentationNode> - get() = recursiveInheritedMembers() - val inheritedCompanionObjectMembers: List<DocumentationNode> - get() = references(RefKind.InheritedCompanionObjectMember).map { it.to } - val extensions: List<DocumentationNode> - get() = references(RefKind.Extension).map { it.to } - val inheritors: List<DocumentationNode> - get() = references(RefKind.Inheritor).map { it.to } - val overrides: List<DocumentationNode> - get() = references(RefKind.Override).map { it.to } - val links: List<DocumentationNode> - get() = references(RefKind.Link).map { it.to } - val hiddenLinks: List<DocumentationNode> - get() = references(RefKind.HiddenLink).map { it.to } - val annotations: List<DocumentationNode> - get() = references(RefKind.Annotation).map { it.to } - val deprecation: DocumentationNode? - 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() - - var sinceKotlin: String? - get() = references(RefKind.SinceKotlin).singleOrNull()?.to?.name - set(value) { - dropReferences { it.kind == RefKind.SinceKotlin } - if (value != null) { - append(DocumentationNode(value, Content.Empty, NodeKind.Value), RefKind.SinceKotlin) - } - } - - val supertypes: List<DocumentationNode> - get() = details(NodeKind.Supertype) - - val superclassType: DocumentationNode? - get() = when (kind) { - NodeKind.Supertype -> { - (links + listOfNotNull(externalType)).firstOrNull { it.kind in NodeKind.classLike }?.superclassType - } - NodeKind.Interface -> null - in NodeKind.classLike -> supertypes.firstOrNull { - (it.links + listOfNotNull(it.externalType)).any { it.isSuperclassFor(this) } - } - 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) { - references.add(DocumentationReference(this, to, kind)) - } - - fun addReference(reference: DocumentationReference) { - references.add(reference) - } - - fun dropReferences(predicate: (DocumentationReference) -> Boolean) { - references.removeAll(predicate) - } - - fun addAllReferencesFrom(other: DocumentationNode) { - references.addAll(other.references) - } - - fun updateContent(body: MutableContent.() -> Unit) { - if (content !is MutableContent) { - content = MutableContent() - } - (content as MutableContent).body() - } - fun details(kind: NodeKind): List<DocumentationNode> = details.filter { it.kind == kind } - fun members(kind: NodeKind): List<DocumentationNode> = members.filter { it.kind == kind } - fun inheritedMembers(kind: NodeKind): List<DocumentationNode> = inheritedMembers.filter { it.kind == kind } - fun inheritedCompanionObjectMembers(kind: NodeKind): List<DocumentationNode> = inheritedCompanionObjectMembers.filter { it.kind == kind } - fun links(kind: NodeKind): List<DocumentationNode> = links.filter { it.kind == kind } - - fun detail(kind: NodeKind): DocumentationNode = details.single { it.kind == kind } - fun detailOrNull(kind: NodeKind): DocumentationNode? = details.singleOrNull { it.kind == kind } - fun member(kind: NodeKind): DocumentationNode = members.single { it.kind == kind } - fun link(kind: NodeKind): DocumentationNode = links.single { it.kind == kind } - - - fun references(kind: RefKind): List<DocumentationReference> = references.filter { it.kind == kind } - fun allReferences(): Set<DocumentationReference> = references - - override fun toString(): String { - return "$kind:$name" - } -} - -class DocumentationModule(name: String, content: Content = Content.Empty, val nodeRefGraph: NodeReferenceGraph = NodeReferenceGraph()) - : DocumentationNode(name, content, NodeKind.Module) { - -} - -val DocumentationNode.path: List<DocumentationNode> - get() { - val parent = owner ?: return listOf(this) - return parent.path + this - } - -fun findOrCreatePackageNode(module: DocumentationNode?, packageName: String, packageContent: Map<String, Content>, refGraph: NodeReferenceGraph): DocumentationNode { - val node = refGraph.lookup(packageName) ?: run { - val newNode = DocumentationNode( - packageName, - packageContent.getOrElse(packageName) { Content.Empty }, - NodeKind.Package - ) - - refGraph.register(packageName, newNode) - newNode - } - if (module != null && node !in module.members) { - module.append(node, RefKind.Member) - } - return node -} - -fun DocumentationNode.append(child: DocumentationNode, kind: RefKind) { - addReferenceTo(child, kind) - when (kind) { - RefKind.Detail -> child.addReferenceTo(this, RefKind.Owner) - RefKind.Member -> child.addReferenceTo(this, RefKind.Owner) - RefKind.Owner -> child.addReferenceTo(this, RefKind.Member) - RefKind.Origin -> child.addReferenceTo(this, RefKind.Owner) - else -> { /* Do not add any links back for other types */ - } - } -} - -fun DocumentationNode.appendTextNode(text: String, - kind: NodeKind, - refKind: RefKind = RefKind.Detail) { - append(DocumentationNode(text, Content.Empty, kind), refKind) -} - -fun DocumentationNode.qualifiedName(): String { - if (kind == NodeKind.Type) { - return qualifiedNameFromType() - } else if (kind == NodeKind.Package) { - return name - } - return path.dropWhile { it.kind == NodeKind.Module }.map { it.name }.filter { it.isNotEmpty() }.joinToString(".") -} - -fun DocumentationNode.simpleName() = name.substringAfterLast('.') - -private fun DocumentationNode.recursiveInheritedMembers(): List<DocumentationNode> { - val allInheritedMembers = mutableListOf<DocumentationNode>() - recursiveInheritedMembers(allInheritedMembers) - return allInheritedMembers -} - -private fun DocumentationNode.recursiveInheritedMembers(allInheritedMembers: MutableList<DocumentationNode>) { - allInheritedMembers.addAll(inheritedMembers) - System.out.println(allInheritedMembers.size) - inheritedMembers.groupBy { it.owner!! } .forEach { (node, _) -> - node.recursiveInheritedMembers(allInheritedMembers) - } -} - -private fun DocumentationNode.isSuperclassFor(node: DocumentationNode): Boolean { - return when(node.kind) { - NodeKind.Object, NodeKind.Class, NodeKind.Enum -> kind == NodeKind.Class - NodeKind.Exception -> kind == NodeKind.Class || kind == NodeKind.Exception - else -> false - } -} - -fun DocumentationNode.classNodeNameWithOuterClass(): String { - assert(kind in NodeKind.classLike) - return path.dropWhile { it.kind == NodeKind.Package || it.kind == NodeKind.Module }.joinToString(separator = ".") { it.name } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Model/DocumentationReference.kt b/core/src/main/kotlin/Model/DocumentationReference.kt deleted file mode 100644 index 0b890a78..00000000 --- a/core/src/main/kotlin/Model/DocumentationReference.kt +++ /dev/null @@ -1,126 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Singleton - -enum class RefKind { - Owner, - Member, - InheritedMember, - InheritedCompanionObjectMember, - Detail, - Link, - HiddenLink, - Extension, - Inheritor, - Superclass, - Override, - Annotation, - HiddenAnnotation, - Deprecation, - TopLevelPage, - Platform, - ExternalType, - Origin, - SinceKotlin -} - -data class DocumentationReference(val from: DocumentationNode, val to: DocumentationNode, val kind: RefKind) { -} - -sealed class NodeResolver { - abstract fun resolve(nodeRephGraph: NodeReferenceGraph): DocumentationNode? - class BySignature(var signature: String) : NodeResolver() { - override fun resolve(nodeRephGraph: NodeReferenceGraph): DocumentationNode? { - return nodeRephGraph.lookup(signature) - } - } - - class Exact(var exactNode: DocumentationNode) : NodeResolver() { - override fun resolve(nodeRephGraph: NodeReferenceGraph): DocumentationNode? { - return exactNode - } - } -} - -class PendingDocumentationReference(val lazyNodeFrom: NodeResolver, - val lazyNodeTo: NodeResolver, - val kind: RefKind) { - fun resolve(nodeRephGraph: NodeReferenceGraph) { - val fromNode = lazyNodeFrom.resolve(nodeRephGraph) - val toNode = lazyNodeTo.resolve(nodeRephGraph) - if (fromNode != null && toNode != null) { - fromNode.addReferenceTo(toNode, kind) - } - } -} - -class NodeReferenceGraph { - private val nodeMap = hashMapOf<String, DocumentationNode>() - val nodeMapView: Map<String, DocumentationNode> - get() = HashMap(nodeMap) - - val references = arrayListOf<PendingDocumentationReference>() - - fun register(signature: String, node: DocumentationNode) { - nodeMap[signature] = node - } - - fun link(fromNode: DocumentationNode, toSignature: String, kind: RefKind) { - references.add( - PendingDocumentationReference( - NodeResolver.Exact(fromNode), - NodeResolver.BySignature(toSignature), - kind - )) - } - - fun link(fromSignature: String, toNode: DocumentationNode, kind: RefKind) { - references.add( - PendingDocumentationReference( - NodeResolver.BySignature(fromSignature), - NodeResolver.Exact(toNode), - kind - ) - ) - } - - fun link(fromSignature: String, toSignature: String, kind: RefKind) { - references.add( - PendingDocumentationReference( - NodeResolver.BySignature(fromSignature), - NodeResolver.BySignature(toSignature), - kind - ) - ) - } - - fun addReference(reference: PendingDocumentationReference) { - references.add(reference) - } - - fun lookup(signature: String) = nodeMap[signature] - - fun lookupOrWarn(signature: String, logger: DokkaLogger): DocumentationNode? { - val result = nodeMap[signature] - if (result == null) { - logger.warn("Can't find node by signature `$signature`." + - "This is probably caused by invalid configuration of cross-module dependencies") - } - return result - } - - fun resolveReferences() { - references.forEach { it.resolve(this) } - } -} - -@Singleton -class PlatformNodeRegistry { - private val platformNodes = hashMapOf<String, DocumentationNode>() - - operator fun get(platform: String): DocumentationNode { - return platformNodes.getOrPut(platform) { - DocumentationNode(platform, Content.Empty, NodeKind.Platform) - } - } -} diff --git a/core/src/main/kotlin/Model/ElementSignatureProvider.kt b/core/src/main/kotlin/Model/ElementSignatureProvider.kt deleted file mode 100644 index e8fdde6e..00000000 --- a/core/src/main/kotlin/Model/ElementSignatureProvider.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor - -interface ElementSignatureProvider { - fun signature(forDesc: DeclarationDescriptor): String - fun signature(forPsi: PsiElement): String -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Model/PackageDocs.kt b/core/src/main/kotlin/Model/PackageDocs.kt deleted file mode 100644 index b24efc5d..00000000 --- a/core/src/main/kotlin/Model/PackageDocs.kt +++ /dev/null @@ -1,136 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.Singleton -import com.intellij.ide.highlighter.JavaFileType -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiFileFactory -import com.intellij.psi.util.PsiTreeUtil -import com.intellij.util.LocalTimeCounter -import com.intellij.openapi.util.text.StringUtil -import org.intellij.markdown.MarkdownElementTypes -import org.intellij.markdown.MarkdownTokenTypes -import org.intellij.markdown.parser.LinkMap -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor -import java.io.File - -@Singleton -class PackageDocs - @Inject constructor(val linkResolver: DeclarationLinkResolver?, - val logger: DokkaLogger, - val environment: KotlinCoreEnvironment, - val refGraph: NodeReferenceGraph, - val elementSignatureProvider: ElementSignatureProvider) -{ - val moduleContent: MutableContent = MutableContent() - private val _packageContent: MutableMap<String, MutableContent> = hashMapOf() - val packageContent: Map<String, Content> - get() = _packageContent - - fun parse(fileName: String, linkResolveContext: List<PackageFragmentDescriptor>) { - val file = File(fileName) - if (file.exists()) { - val text = StringUtil.convertLineSeparators(file.readText()) - val tree = parseMarkdown(text) - val linkMap = LinkMap.buildLinkMap(tree.node, text) - var targetContent: MutableContent = moduleContent - tree.children.forEach { - if (it.type == MarkdownElementTypes.ATX_1) { - val headingText = it.child(MarkdownTokenTypes.ATX_CONTENT)?.text - if (headingText != null) { - targetContent = findTargetContent(headingText.trimStart()) - } - } else { - buildContentTo(it, targetContent, LinkResolver(linkMap) { resolveContentLink(fileName, it, linkResolveContext) }) - } - } - } else { - logger.warn("Include file $file was not found.") - } - } - - private fun parseHtmlAsJavadoc(text: String, packageName: String, file: File) { - val javadocText = text - .replace("*/", "*/") - .removeSurrounding("<html>", "</html>", true).trim() - .removeSurrounding("<body>", "</body>", true) - .lineSequence() - .map { "* $it" } - .joinToString (separator = "\n", prefix = "/**\n", postfix = "\n*/") - parseJavadoc(javadocText, packageName, file) - } - - private fun CharSequence.removeSurrounding(prefix: CharSequence, suffix: CharSequence, ignoringCase: Boolean = false): CharSequence { - if ((length >= prefix.length + suffix.length) && startsWith(prefix, ignoringCase) && endsWith(suffix, ignoringCase)) { - return subSequence(prefix.length, length - suffix.length) - } - return subSequence(0, length) - } - - - private fun parseJavadoc(text: String, packageName: String, file: File) { - - val psiFileFactory = PsiFileFactory.getInstance(environment.project) - val psiFile = psiFileFactory.createFileFromText( - file.nameWithoutExtension + ".java", - JavaFileType.INSTANCE, - "package $packageName; $text\npublic class C {}", - LocalTimeCounter.currentTime(), - false, - true - ) - - val psiClass = PsiTreeUtil.getChildOfType(psiFile, PsiClass::class.java)!! - val parser = JavadocParser(refGraph, logger, elementSignatureProvider, linkResolver?.externalDocumentationLinkResolver!!) - findOrCreatePackageContent(packageName).apply { - val content = parser.parseDocumentation(psiClass).content - children.addAll(content.children) - content.sections.forEach { - addSection(it.tag, it.subjectName).children.addAll(it.children) - } - } - } - - - fun parseJava(fileName: String, packageName: String) { - val file = File(fileName) - if (file.exists()) { - val text = file.readText() - - val trimmedText = text.trim() - - if (trimmedText.startsWith("/**")) { - parseJavadoc(text, packageName, file) - } else if (trimmedText.toLowerCase().startsWith("<html>")) { - parseHtmlAsJavadoc(trimmedText, packageName, file) - } - } - } - - private fun findTargetContent(heading: String): MutableContent { - if (heading.startsWith("Module") || heading.startsWith("module")) { - return moduleContent - } - if (heading.startsWith("Package") || heading.startsWith("package")) { - return findOrCreatePackageContent(heading.substring("package".length).trim()) - } - return findOrCreatePackageContent(heading) - } - - private fun findOrCreatePackageContent(packageName: String) = - _packageContent.getOrPut(packageName) { MutableContent() } - - private fun resolveContentLink(fileName: String, href: String, linkResolveContext: List<PackageFragmentDescriptor>): ContentBlock { - if (linkResolver != null) { - linkResolveContext - .asSequence() - .map { p -> linkResolver.tryResolveContentLink(p, href) } - .filterNotNull() - .firstOrNull() - ?.let { return it } - } - logger.warn("Unresolved link to `$href` in include ($fileName)") - return ContentExternalLink("#") - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Model/SourceLinks.kt b/core/src/main/kotlin/Model/SourceLinks.kt deleted file mode 100644 index 99ee362e..00000000 --- a/core/src/main/kotlin/Model/SourceLinks.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.PsiDocumentManager -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiNameIdentifierOwner -import org.jetbrains.dokka.DokkaConfiguration.SourceLinkDefinition -import org.jetbrains.kotlin.psi.psiUtil.startOffset -import java.io.File - - -fun DocumentationNode.appendSourceLink(psi: PsiElement?, sourceLinks: List<SourceLinkDefinition>) { - val path = psi?.containingFile?.virtualFile?.path ?: return - val canonicalPath = File(path).canonicalPath - - val target = if (psi is PsiNameIdentifierOwner) psi.nameIdentifier else psi - val pair = determineSourceLinkDefinition(canonicalPath, sourceLinks) - if (pair != null) { - val (sourceLinkDefinition, sourceLinkCanonicalPath) = pair - var url = determineUrl(canonicalPath, sourceLinkDefinition, sourceLinkCanonicalPath) - if (sourceLinkDefinition.lineSuffix != null) { - val line = target?.lineNumber() - if (line != null) { - url += sourceLinkDefinition.lineSuffix + line.toString() - } - } - append(DocumentationNode(url, Content.Empty, NodeKind.SourceUrl), RefKind.Detail) - } - - if (target != null) { - append(DocumentationNode(target.sourcePosition(), Content.Empty, NodeKind.SourcePosition), RefKind.Detail) - } -} - -private fun determineSourceLinkDefinition( - canonicalPath: String, - sourceLinks: List<SourceLinkDefinition> -): Pair<SourceLinkDefinition, String>? { - return sourceLinks - .asSequence() - .map { it to File(it.path).canonicalPath } - .firstOrNull { (_, sourceLinkCanonicalPath) -> - canonicalPath.startsWith(sourceLinkCanonicalPath) - } -} - -private fun determineUrl( - canonicalPath: String, - sourceLinkDefinition: SourceLinkDefinition, - sourceLinkCanonicalPath: String -): String { - val relativePath = canonicalPath.substring(sourceLinkCanonicalPath.length) - val relativeUrl = relativePath.replace('\\', '/').removePrefix("/") - return "${sourceLinkDefinition.url.removeSuffix("/")}/$relativeUrl" -} - -private fun PsiElement.sourcePosition(): String { - val path = containingFile.virtualFile.path - val lineNumber = lineNumber() - val columnNumber = columnNumber() - - return when { - lineNumber == null -> path - columnNumber == null -> "$path:$lineNumber" - else -> "$path:$lineNumber:$columnNumber" - } -} - -fun PsiElement.lineNumber(): Int? { - val doc = PsiDocumentManager.getInstance(project).getDocument(containingFile) - // IJ uses 0-based line-numbers; external source browsers use 1-based - return doc?.getLineNumber(textRange.startOffset)?.plus(1) -} - -fun PsiElement.columnNumber(): Int? { - val doc = PsiDocumentManager.getInstance(project).getDocument(containingFile) ?: return null - val lineNumber = doc.getLineNumber(textRange.startOffset) - return startOffset - doc.getLineStartOffset(lineNumber) -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt deleted file mode 100644 index da74495f..00000000 --- a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt +++ /dev/null @@ -1,103 +0,0 @@ -package org.jetbrains.dokka.Samples - -import com.google.inject.Inject -import com.intellij.psi.PsiElement -import org.jetbrains.dokka.* -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.PackageViewDescriptor -import org.jetbrains.kotlin.idea.kdoc.getKDocLinkResolutionScope -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtBlockExpression -import org.jetbrains.kotlin.psi.KtDeclarationWithBody -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.ResolutionScope - - -open class DefaultSampleProcessingService -@Inject constructor(val configuration: DokkaConfiguration, - val logger: DokkaLogger, - val resolutionFacade: DokkaResolutionFacade) - : SampleProcessingService { - - override fun resolveSample(descriptor: DeclarationDescriptor, functionName: String?, kdocTag: KDocTag): ContentNode { - if (functionName == null) { - logger.warn("Missing function name in @sample in ${descriptor.signature()}") - return ContentBlockSampleCode().apply { append(ContentText("//Missing function name in @sample")) } - } - val bindingContext = BindingContext.EMPTY - val symbol = resolveKDocLink(bindingContext, resolutionFacade, descriptor, kdocTag, functionName.split(".")).firstOrNull() - if (symbol == null) { - logger.warn("Unresolved function $functionName in @sample in ${descriptor.signature()}") - return ContentBlockSampleCode().apply { append(ContentText("//Unresolved: $functionName")) } - } - val psiElement = DescriptorToSourceUtils.descriptorToDeclaration(symbol) - if (psiElement == null) { - logger.warn("Can't find source for function $functionName in @sample in ${descriptor.signature()}") - return ContentBlockSampleCode().apply { append(ContentText("//Source not found: $functionName")) } - } - - val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd() - val lines = text.split("\n") - val indent = lines.filter(String::isNotBlank).map { it.takeWhile(Char::isWhitespace).count() }.min() ?: 0 - val finalText = lines.joinToString("\n") { it.drop(indent) } - - return ContentBlockSampleCode(importsBlock = processImports(psiElement)).apply { append(ContentText(finalText)) } - } - - protected open fun processSampleBody(psiElement: PsiElement): String = when (psiElement) { - is KtDeclarationWithBody -> { - val bodyExpression = psiElement.bodyExpression - when (bodyExpression) { - is KtBlockExpression -> bodyExpression.text.removeSurrounding("{", "}") - else -> bodyExpression!!.text - } - } - else -> psiElement.text - } - - protected open fun processImports(psiElement: PsiElement): ContentBlockCode { - val psiFile = psiElement.containingFile - if (psiFile is KtFile) { - return ContentBlockCode("kotlin").apply { - append(ContentText(psiFile.importList?.text ?: "")) - } - } else { - return ContentBlockCode("") - } - } - - private fun resolveInScope(functionName: String, scope: ResolutionScope): DeclarationDescriptor? { - var currentScope = scope - val parts = functionName.split('.') - - var symbol: DeclarationDescriptor? = null - - for (part in parts) { - // short name - val symbolName = Name.identifier(part) - val partSymbol = currentScope.getContributedDescriptors(DescriptorKindFilter.ALL) { it == symbolName }.firstOrNull { it.name == symbolName } - - if (partSymbol == null) { - symbol = null - break - } - @Suppress("IfThenToElvis") - currentScope = if (partSymbol is ClassDescriptor) - partSymbol.defaultType.memberScope - else if (partSymbol is PackageViewDescriptor) - partSymbol.memberScope - else - getKDocLinkResolutionScope(resolutionFacade, partSymbol) - symbol = partSymbol - } - - return symbol - } -} - diff --git a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt deleted file mode 100644 index a67306f4..00000000 --- a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt +++ /dev/null @@ -1,197 +0,0 @@ -package org.jetbrains.dokka.Samples - -import com.google.inject.Inject -import com.intellij.psi.PsiDocumentManager -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiElementVisitor -import com.intellij.psi.PsiWhiteSpace -import com.intellij.psi.impl.source.tree.LeafPsiElement -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.dokka.* -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.allChildren -import org.jetbrains.kotlin.psi.psiUtil.prevLeaf -import org.jetbrains.kotlin.psi.psiUtil.startOffset -import org.jetbrains.kotlin.resolve.ImportPath -import java.io.PrintWriter -import java.io.StringWriter - - -open class KotlinWebsiteSampleProcessingService -@Inject constructor(dokkaConfiguration: DokkaConfiguration, - logger: DokkaLogger, - resolutionFacade: DokkaResolutionFacade) - : DefaultSampleProcessingService(dokkaConfiguration, logger, resolutionFacade) { - - private class SampleBuilder : KtTreeVisitorVoid() { - val builder = StringBuilder() - val text: String - get() = builder.toString() - - val errors = mutableListOf<ConvertError>() - - data class ConvertError(val e: Exception, val text: String, val loc: String) - - fun KtValueArgument.extractStringArgumentValue() = - (getArgumentExpression() as KtStringTemplateExpression) - .entries.joinToString("") { it.text } - - - fun convertAssertPrints(expression: KtCallExpression) { - val (argument, commentArgument) = expression.valueArguments - builder.apply { - append("println(") - append(argument.text) - append(") // ") - append(commentArgument.extractStringArgumentValue()) - } - } - - fun convertAssertTrueFalse(expression: KtCallExpression, expectedResult: Boolean) { - val (argument) = expression.valueArguments - builder.apply { - expression.valueArguments.getOrNull(1)?.let { - append("// ${it.extractStringArgumentValue()}") - val ws = expression.prevLeaf { it is PsiWhiteSpace } - append(ws?.text ?: "\n") - } - append("println(\"") - append(argument.text) - append(" is \${") - append(argument.text) - append("}\") // $expectedResult") - } - } - - fun convertAssertFails(expression: KtCallExpression) { - val valueArguments = expression.valueArguments - - val funcArgument: KtValueArgument - val message: KtValueArgument? - - if (valueArguments.size == 1) { - message = null - funcArgument = valueArguments.first() - } else { - message = valueArguments.first() - funcArgument = valueArguments.last() - } - - builder.apply { - val argument = funcArgument.extractFunctionalArgumentText() - append(argument.lines().joinToString(separator = "\n") { "// $it" }) - append(" // ") - if (message != null) { - append(message.extractStringArgumentValue()) - } - append(" will fail") - } - } - - private fun KtValueArgument.extractFunctionalArgumentText(): String { - return if (getArgumentExpression() is KtLambdaExpression) - PsiTreeUtil.findChildOfType(this, KtBlockExpression::class.java)?.text ?: "" - else - text - } - - fun convertAssertFailsWith(expression: KtCallExpression) { - val (funcArgument) = expression.valueArguments - val (exceptionType) = expression.typeArguments - builder.apply { - val argument = funcArgument.extractFunctionalArgumentText() - append(argument.lines().joinToString(separator = "\n") { "// $it" }) - append(" // will fail with ") - append(exceptionType.text) - } - } - - override fun visitCallExpression(expression: KtCallExpression) { - when (expression.calleeExpression?.text) { - "assertPrints" -> convertAssertPrints(expression) - "assertTrue" -> convertAssertTrueFalse(expression, expectedResult = true) - "assertFalse" -> convertAssertTrueFalse(expression, expectedResult = false) - "assertFails" -> convertAssertFails(expression) - "assertFailsWith" -> convertAssertFailsWith(expression) - else -> super.visitCallExpression(expression) - } - } - - private fun reportProblemConvertingElement(element: PsiElement, e: Exception) { - val text = element.text - val document = PsiDocumentManager.getInstance(element.project).getDocument(element.containingFile) - - val lineInfo = if (document != null) { - val lineNumber = document.getLineNumber(element.startOffset) - "$lineNumber, ${element.startOffset - document.getLineStartOffset(lineNumber)}" - } else { - "offset: ${element.startOffset}" - } - errors += ConvertError(e, text, lineInfo) - } - - override fun visitElement(element: PsiElement) { - if (element is LeafPsiElement) - builder.append(element.text) - - element.acceptChildren(object : PsiElementVisitor() { - override fun visitElement(element: PsiElement) { - try { - element.accept(this@SampleBuilder) - } catch (e: Exception) { - try { - reportProblemConvertingElement(element, e) - } finally { - builder.append(element.text) //recover - } - } - } - }) - } - - } - - private fun PsiElement.buildSampleText(): String { - val sampleBuilder = SampleBuilder() - this.accept(sampleBuilder) - - sampleBuilder.errors.forEach { - val sw = StringWriter() - val pw = PrintWriter(sw) - it.e.printStackTrace(pw) - - logger.error("${containingFile.name}: (${it.loc}): Exception thrown while converting \n```\n${it.text}\n```\n$sw") - } - return sampleBuilder.text - } - - val importsToIgnore = arrayOf("samples.*", "samples.Sample").map { ImportPath.fromString(it) } - - override fun processImports(psiElement: PsiElement): ContentBlockCode { - val psiFile = psiElement.containingFile - if (psiFile is KtFile) { - return ContentBlockCode("kotlin").apply { - append(ContentText("\n")) - psiFile.importList?.let { - it.allChildren.filter { - it !is KtImportDirective || it.importPath !in importsToIgnore - }.forEach { append(ContentText(it.text)) } - } - } - } - return super.processImports(psiElement) - } - - override fun processSampleBody(psiElement: PsiElement) = when (psiElement) { - is KtDeclarationWithBody -> { - val bodyExpression = psiElement.bodyExpression - val bodyExpressionText = bodyExpression!!.buildSampleText() - when (bodyExpression) { - is KtBlockExpression -> bodyExpressionText.removeSurrounding("{", "}") - else -> bodyExpressionText - } - } - else -> psiElement.buildSampleText() - } -} - diff --git a/core/src/main/kotlin/Samples/SampleProcessingService.kt b/core/src/main/kotlin/Samples/SampleProcessingService.kt deleted file mode 100644 index 86c917cf..00000000 --- a/core/src/main/kotlin/Samples/SampleProcessingService.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.jetbrains.dokka.Samples - -import org.jetbrains.dokka.ContentNode -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag - -interface SampleProcessingService { - fun resolveSample(descriptor: DeclarationDescriptor, functionName: String?, kdocTag: KDocTag): ContentNode -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Utilities/DokkaLogging.kt b/core/src/main/kotlin/Utilities/DokkaLogging.kt deleted file mode 100644 index 1ef52837..00000000 --- a/core/src/main/kotlin/Utilities/DokkaLogging.kt +++ /dev/null @@ -1,27 +0,0 @@ -package org.jetbrains.dokka - -interface DokkaLogger { - fun info(message: String) - fun warn(message: String) - fun error(message: String) -} - -object DokkaConsoleLogger : DokkaLogger { - var warningCount: Int = 0 - - override fun info(message: String) = println(message) - override fun warn(message: String) { - println("WARN: $message") - warningCount++ - } - - override fun error(message: String) = println("ERROR: $message") - - fun report() { - if (warningCount > 0) { - println("generation completed with $warningCount warnings") - } else { - println("generation completed successfully") - } - } -} diff --git a/core/src/main/kotlin/Utilities/DokkaModules.kt b/core/src/main/kotlin/Utilities/DokkaModules.kt deleted file mode 100644 index 919ec30f..00000000 --- a/core/src/main/kotlin/Utilities/DokkaModules.kt +++ /dev/null @@ -1,85 +0,0 @@ -package org.jetbrains.dokka.Utilities - -import com.google.inject.Binder -import com.google.inject.Module -import com.google.inject.TypeLiteral -import com.google.inject.binder.AnnotatedBindingBuilder -import com.google.inject.name.Names -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Formats.FormatDescriptor -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import java.io.File -import kotlin.reflect.KClass - -const val impliedPlatformsName = "impliedPlatforms" - -class DokkaRunModule(val configuration: DokkaConfiguration) : Module { - override fun configure(binder: Binder) { - binder.bind<DokkaConfiguration>().toInstance(configuration) - binder.bind(StringListType).annotatedWith(Names.named(impliedPlatformsName)).toInstance(configuration.impliedPlatforms) - - binder.bind(File::class.java).annotatedWith(Names.named("outputDir")).toInstance(File(configuration.outputDir)) - } - -} - -class DokkaAnalysisModule(val environment: AnalysisEnvironment, - val configuration: DokkaConfiguration, - val defaultPlatformsProvider: DefaultPlatformsProvider, - val nodeReferenceGraph: NodeReferenceGraph, - val passConfiguration: DokkaConfiguration.PassConfiguration, - val logger: DokkaLogger) : Module { - override fun configure(binder: Binder) { - binder.bind<DokkaLogger>().toInstance(logger) - - val coreEnvironment = environment.createCoreEnvironment() - binder.bind<KotlinCoreEnvironment>().toInstance(coreEnvironment) - - val (dokkaResolutionFacade, libraryResolutionFacade) = environment.createResolutionFacade(coreEnvironment) - binder.bind<DokkaResolutionFacade>().toInstance(dokkaResolutionFacade) - binder.bind<DokkaResolutionFacade>().annotatedWith(Names.named("libraryResolutionFacade")).toInstance(libraryResolutionFacade) - - binder.bind<DokkaConfiguration.PassConfiguration>().toInstance(passConfiguration) - - binder.bind<DefaultPlatformsProvider>().toInstance(defaultPlatformsProvider) - - binder.bind<NodeReferenceGraph>().toInstance(nodeReferenceGraph) - - val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", configuration.format) - descriptor.configureAnalysis(binder) - } -} - -object StringListType : TypeLiteral<@JvmSuppressWildcards List<String>>() - -class DokkaOutputModule(val configuration: DokkaConfiguration, - val logger: DokkaLogger) : Module { - override fun configure(binder: Binder) { - binder.bind<DokkaLogger>().toInstance(logger) - - val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", configuration.format) - - descriptor.configureOutput(binder) - } -} - -private inline fun <reified T: Any> Binder.registerCategory(category: String) { - ServiceLocator.allServices(category).forEach { - @Suppress("UNCHECKED_CAST") - bind(T::class.java).annotatedWith(Names.named(it.name)).to(T::class.java.classLoader.loadClass(it.className) as Class<T>) - } -} - -private inline fun <reified Base : Any, reified T : Base> Binder.bindNameAnnotated(name: String) { - bind(Base::class.java).annotatedWith(Names.named(name)).to(T::class.java) -} - - -inline fun <reified T: Any> Binder.bind(): AnnotatedBindingBuilder<T> = bind(T::class.java) - -inline fun <reified T: Any> Binder.lazyBind(): Lazy<AnnotatedBindingBuilder<T>> = lazy { bind(T::class.java) } - -inline infix fun <reified T: Any, TKClass: KClass<out T>> Lazy<AnnotatedBindingBuilder<T>>.toOptional(kClass: TKClass?) = - kClass?.let { value toType it } - -inline infix fun <reified T: Any, TKClass: KClass<out T>> AnnotatedBindingBuilder<T>.toType(kClass: TKClass) = to(kClass.java) diff --git a/core/src/main/kotlin/Utilities/Links.kt b/core/src/main/kotlin/Utilities/Links.kt deleted file mode 100644 index 34423e4e..00000000 --- a/core/src/main/kotlin/Utilities/Links.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.jetbrains.dokka.Utilities - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.ExternalDocumentationLinkImpl - -fun DokkaConfiguration.PassConfiguration.defaultLinks(): List<ExternalDocumentationLinkImpl> { - val links = mutableListOf<ExternalDocumentationLinkImpl>() - if (!noJdkLink) - links += DokkaConfiguration.ExternalDocumentationLink - .Builder("https://docs.oracle.com/javase/${jdkVersion}/docs/api/") - .build() as ExternalDocumentationLinkImpl - - if (!noStdlibLink) - links += DokkaConfiguration.ExternalDocumentationLink - .Builder("https://kotlinlang.org/api/latest/jvm/stdlib/") - .build() as ExternalDocumentationLinkImpl - return links -} diff --git a/core/src/main/kotlin/Utilities/Path.kt b/core/src/main/kotlin/Utilities/Path.kt deleted file mode 100644 index 05838499..00000000 --- a/core/src/main/kotlin/Utilities/Path.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -fun File.appendExtension(extension: String) = if (extension.isEmpty()) this else File(path + "." + extension) diff --git a/core/src/main/kotlin/configuration.kt b/core/src/main/kotlin/configuration.kt new file mode 100644 index 00000000..eaee351b --- /dev/null +++ b/core/src/main/kotlin/configuration.kt @@ -0,0 +1,149 @@ +@file:Suppress("FunctionName") + +package org.jetbrains.dokka + +import com.google.gson.Gson +import java.io.File +import java.io.Serializable +import java.net.URL + +object DokkaDefaults { + const val outputDir = "./dokka" + const val format: String = "html" + val cacheRoot: String? = null + const val offlineMode: Boolean = false + const val failOnWarning: Boolean = false + + const val includeNonPublic: Boolean = false + const val includeRootPackage: Boolean = false + const val reportUndocumented: Boolean = false + const val skipEmptyPackages: Boolean = false + const val skipDeprecated: Boolean = false + const val jdkVersion: Int = 8 + const val noStdlibLink: Boolean = false + const val noJdkLink: Boolean = false + val analysisPlatform: Platform = Platform.DEFAULT + const val suppress: Boolean = false +} + +enum class Platform(val key: String) { + jvm("jvm"), + js("js"), + native("native"), + common("common"); + + companion object { + val DEFAULT = jvm + + fun fromString(key: String): Platform { + return when (key.toLowerCase()) { + jvm.key -> jvm + js.key -> js + native.key -> native + common.key -> common + else -> throw IllegalArgumentException("Unrecognized platform: $key") + } + } + } +} + +data class DokkaSourceSetID( + val moduleName: String, + val sourceSetName: String +) : Serializable { + override fun toString(): String { + return "$moduleName/$sourceSetName" + } +} + +fun DokkaConfigurationImpl(json: String): DokkaConfigurationImpl { + return Gson().fromJson(json, DokkaConfigurationImpl::class.java) +} + +fun DokkaConfiguration.toJson(): String { + return Gson().toJson(this) +} + +interface DokkaConfiguration { + val outputDir: String + val cacheRoot: String? + val offlineMode: Boolean + val failOnWarning: Boolean + val sourceSets: List<DokkaSourceSet> + val modules: List<DokkaModuleDescription> + val pluginsClasspath: List<File> + val pluginsConfiguration: Map<String, String> + + interface DokkaSourceSet { + val sourceSetID: DokkaSourceSetID + val displayName: String + val moduleDisplayName: String + val classpath: List<String> + val sourceRoots: List<SourceRoot> + val dependentSourceSets: Set<DokkaSourceSetID> + val samples: List<String> + val includes: List<String> + val includeNonPublic: Boolean + val includeRootPackage: Boolean + val reportUndocumented: Boolean + val skipEmptyPackages: Boolean + val skipDeprecated: Boolean + val jdkVersion: Int + val sourceLinks: List<SourceLinkDefinition> + val perPackageOptions: List<PackageOptions> + val externalDocumentationLinks: List<ExternalDocumentationLink> + val languageVersion: String? + val apiVersion: String? + val noStdlibLink: Boolean + val noJdkLink: Boolean + val suppressedFiles: List<String> + val analysisPlatform: Platform + } + + interface SourceRoot { + val path: String + } + + interface SourceLinkDefinition { + val path: String + val url: String + val lineSuffix: String? + } + + interface DokkaModuleDescription { + val name: String + val path: String + val docFile: String + } + + interface PackageOptions { + val prefix: String + val includeNonPublic: Boolean + val reportUndocumented: Boolean? + val skipDeprecated: Boolean + val suppress: Boolean + } + + interface ExternalDocumentationLink { + val url: URL + val packageListUrl: URL + + open class Builder( + open var url: URL? = null, + open var packageListUrl: URL? = null + ) { + + constructor(root: String, packageList: String? = null) : this(URL(root), packageList?.let { URL(it) }) + + fun build(): ExternalDocumentationLink = + if (packageListUrl != null && url != null) + ExternalDocumentationLinkImpl(url!!, packageListUrl!!) + else if (url != null) + ExternalDocumentationLinkImpl(url!!, URL(url!!, "package-list")) + else + throw IllegalArgumentException("url or url && packageListUrl must not be null for external documentation link") + } + } +} + + diff --git a/core/src/main/kotlin/defaultConfiguration.kt b/core/src/main/kotlin/defaultConfiguration.kt new file mode 100644 index 00000000..02274e5d --- /dev/null +++ b/core/src/main/kotlin/defaultConfiguration.kt @@ -0,0 +1,83 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import java.io.File +import java.net.URL +import java.io.Serializable + +data class DokkaConfigurationImpl( + override val outputDir: String, + override val cacheRoot: String?, + override val offlineMode: Boolean, + override val sourceSets: List<DokkaSourceSetImpl>, + override val pluginsClasspath: List<File>, + override val pluginsConfiguration: Map<String, String>, + override val modules: List<DokkaModuleDescriptionImpl>, + override val failOnWarning: Boolean +) : DokkaConfiguration + +data class DokkaSourceSetImpl( + override val moduleDisplayName: String, + override val displayName: String, + override val sourceSetID: DokkaSourceSetID, + override val classpath: List<String>, + override val sourceRoots: List<SourceRootImpl>, + override val dependentSourceSets: Set<DokkaSourceSetID>, + override val samples: List<String>, + override val includes: List<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 sourceLinks: List<SourceLinkDefinitionImpl>, + override val perPackageOptions: List<PackageOptionsImpl>, + override var externalDocumentationLinks: List<ExternalDocumentationLinkImpl>, + override val languageVersion: String?, + override val apiVersion: String?, + override val noStdlibLink: Boolean, + override val noJdkLink: Boolean, + override val suppressedFiles: List<String>, + override val analysisPlatform: Platform +) : DokkaSourceSet + +data class DokkaModuleDescriptionImpl( + override val name: String, + override val path: String, + override val docFile: String +) : DokkaConfiguration.DokkaModuleDescription + +data class SourceRootImpl( + override val path: String +) : DokkaConfiguration.SourceRoot + +data class SourceLinkDefinitionImpl( + override val path: String, + override val url: String, + override val lineSuffix: String? +) : DokkaConfiguration.SourceLinkDefinition { + companion object { + fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinitionImpl { + val (path, urlAndLine) = srcLink.split('=') + return SourceLinkDefinitionImpl( + File(path).canonicalPath, + urlAndLine.substringBefore("#"), + urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#$it" }) + } + } +} + +data class PackageOptionsImpl( + override val prefix: String, + override val includeNonPublic: Boolean, + override val reportUndocumented: Boolean?, + override val skipDeprecated: Boolean, + override val suppress: Boolean +) : DokkaConfiguration.PackageOptions + + +data class ExternalDocumentationLinkImpl( + override val url: URL, + override val packageListUrl: URL +) : DokkaConfiguration.ExternalDocumentationLink, Serializable diff --git a/core/src/main/kotlin/javadoc/docbase.kt b/core/src/main/kotlin/javadoc/docbase.kt deleted file mode 100644 index 0bf72ccf..00000000 --- a/core/src/main/kotlin/javadoc/docbase.kt +++ /dev/null @@ -1,539 +0,0 @@ -package org.jetbrains.dokka.javadoc - -import com.sun.javadoc.* -import org.jetbrains.dokka.* -import java.lang.reflect.Modifier.* -import java.util.* -import kotlin.reflect.KClass - -private interface HasModule { - val module: ModuleNodeAdapter -} - -private interface HasDocumentationNode { - val node: DocumentationNode -} - -open class DocumentationNodeBareAdapter(override val node: DocumentationNode) : Doc, HasDocumentationNode { - private var rawCommentText_: String? = null - - override fun name(): String = node.name - override fun position(): SourcePosition? = SourcePositionAdapter(node) - - override fun inlineTags(): Array<out Tag>? = emptyArray() - override fun firstSentenceTags(): Array<out Tag>? = emptyArray() - override fun tags(): Array<out Tag> = emptyArray() - override fun tags(tagname: String?): Array<out Tag>? = tags().filter { it.kind() == tagname || it.kind() == "@$tagname" }.toTypedArray() - override fun seeTags(): Array<out SeeTag>? = tags().filterIsInstance<SeeTag>().toTypedArray() - override fun commentText(): String = "" - - override fun setRawCommentText(rawDocumentation: String?) { - rawCommentText_ = rawDocumentation ?: "" - } - - override fun getRawCommentText(): String = rawCommentText_ ?: "" - - override fun isError(): Boolean = false - override fun isException(): Boolean = node.kind == NodeKind.Exception - override fun isEnumConstant(): Boolean = node.kind == NodeKind.EnumItem - override fun isEnum(): Boolean = node.kind == NodeKind.Enum - override fun isMethod(): Boolean = node.kind == NodeKind.Function - override fun isInterface(): Boolean = node.kind == NodeKind.Interface - override fun isField(): Boolean = node.kind == NodeKind.Field - override fun isClass(): Boolean = node.kind == NodeKind.Class - override fun isAnnotationType(): Boolean = node.kind == NodeKind.AnnotationClass - override fun isConstructor(): Boolean = node.kind == NodeKind.Constructor - override fun isOrdinaryClass(): Boolean = node.kind == NodeKind.Class - override fun isAnnotationTypeElement(): Boolean = node.kind == NodeKind.Annotation - - override fun compareTo(other: Any?): Int = when (other) { - !is DocumentationNodeAdapter -> 1 - else -> node.name.compareTo(other.node.name) - } - - override fun equals(other: Any?): Boolean = node.qualifiedName() == (other as? DocumentationNodeAdapter)?.node?.qualifiedName() - override fun hashCode(): Int = node.name.hashCode() - - override fun isIncluded(): Boolean = node.kind != NodeKind.ExternalClass -} - - -// TODO think of source position instead of null -// TODO tags -open class DocumentationNodeAdapter(override val module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeBareAdapter(node), HasModule { - override fun inlineTags(): Array<out Tag> = buildInlineTags(module, this, node.content).toTypedArray() - override fun firstSentenceTags(): Array<out Tag> = buildInlineTags(module, this, node.summary).toTypedArray() - - override fun tags(): Array<out Tag> { - val result = ArrayList<Tag>(buildInlineTags(module, this, node.content)) - node.content.sections.flatMapTo(result) { - when (it.tag) { - ContentTags.SeeAlso -> buildInlineTags(module, this, it) - else -> emptyList<Tag>() - } - } - - node.deprecation?.let { - val content = it.content.asText() - result.add(TagImpl(this, "deprecated", content ?: "")) - } - - return result.toTypedArray() - } -} - -// should be extension property but can't because of KT-8745 -private fun <T> nodeAnnotations(self: T): List<AnnotationDescAdapter> where T : HasModule, T : HasDocumentationNode - = self.node.annotations.map { AnnotationDescAdapter(self.module, it) } - -private fun DocumentationNode.hasAnnotation(klass: KClass<*>) = klass.qualifiedName in annotations.map { it.qualifiedName() } -private fun DocumentationNode.hasModifier(name: String) = details(NodeKind.Modifier).any { it.name == name } - - -class PackageAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), PackageDoc { - private val allClasses = listOf(node).collectAllTypesRecursively() - - override fun findClass(className: String?): ClassDoc? = - allClasses.get(className)?.let { ClassDocumentationNodeAdapter(module, it) } - - override fun annotationTypes(): Array<out AnnotationTypeDoc> = emptyArray() - override fun annotations(): Array<out AnnotationDesc> = node.members(NodeKind.AnnotationClass).map { AnnotationDescAdapter(module, it) }.toTypedArray() - override fun exceptions(): Array<out ClassDoc> = node.members(NodeKind.Exception).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() - override fun ordinaryClasses(): Array<out ClassDoc> = node.members(NodeKind.Class).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() - override fun interfaces(): Array<out ClassDoc> = node.members(NodeKind.Interface).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() - override fun errors(): Array<out ClassDoc> = emptyArray() - override fun enums(): Array<out ClassDoc> = node.members(NodeKind.Enum).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() - override fun allClasses(filter: Boolean): Array<out ClassDoc> = allClasses.values.map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() - override fun allClasses(): Array<out ClassDoc> = allClasses(true) - - override fun isIncluded(): Boolean = node.name in module.allPackages -} - -class AnnotationTypeDocAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ClassDocumentationNodeAdapter(module, node), AnnotationTypeDoc { - override fun elements(): Array<out AnnotationTypeElementDoc>? = emptyArray() // TODO -} - -class AnnotationDescAdapter(val module: ModuleNodeAdapter, val node: DocumentationNode) : AnnotationDesc { - override fun annotationType(): AnnotationTypeDoc? = AnnotationTypeDocAdapter(module, node.links.find { it.kind == NodeKind.AnnotationClass } ?: node) // TODO ????? - override fun isSynthesized(): Boolean = false - override fun elementValues(): Array<out AnnotationDesc.ElementValuePair>? = emptyArray() // TODO -} - -open class ProgramElementAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), ProgramElementDoc { - override fun isPublic(): Boolean = node.hasModifier("public") || node.hasModifier("internal") - override fun isPackagePrivate(): Boolean = false - override fun isStatic(): Boolean = node.hasModifier("static") - override fun modifierSpecifier(): Int = visibilityModifier or (if (isStatic) STATIC else 0) - private val visibilityModifier - get() = when { - isPublic -> PUBLIC - isPrivate -> PRIVATE - isProtected -> PROTECTED - else -> 0 - } - override fun qualifiedName(): String? = node.qualifiedName() - override fun annotations(): Array<out AnnotationDesc>? = nodeAnnotations(this).toTypedArray() - override fun modifiers(): String? = "public ${if (isStatic) "static" else ""}".trim() - override fun isProtected(): Boolean = node.hasModifier("protected") - - override fun isFinal(): Boolean = node.hasModifier("final") - - override fun containingPackage(): PackageDoc? { - if (node.kind == NodeKind.Type) { - return null - } - - var owner: DocumentationNode? = node - while (owner != null) { - if (owner.kind == NodeKind.Package) { - return PackageAdapter(module, owner) - } - owner = owner.owner - } - - return null - } - - override fun containingClass(): ClassDoc? { - if (node.kind == NodeKind.Type) { - return null - } - - var owner = node.owner - while (owner != null) { - if (owner.kind in NodeKind.classLike) { - return ClassDocumentationNodeAdapter(module, owner) - } - owner = owner.owner - } - - return null - } - - override fun isPrivate(): Boolean = node.hasModifier("private") - override fun isIncluded(): Boolean = containingPackage()?.isIncluded ?: false && containingClass()?.let { it.isIncluded } ?: true -} - -open class TypeAdapter(override val module: ModuleNodeAdapter, override val node: DocumentationNode) : Type, HasDocumentationNode, HasModule { - private val javaLanguageService = JavaLanguageService() - - override fun qualifiedTypeName(): String = javaLanguageService.getArrayElementType(node)?.qualifiedNameFromType() ?: node.qualifiedNameFromType() - override fun typeName(): String = (javaLanguageService.getArrayElementType(node)?.simpleName() ?: node.simpleName()) + dimension() - override fun simpleTypeName(): String = typeName() // TODO difference typeName() vs simpleTypeName() - - override fun dimension(): String = Collections.nCopies(javaLanguageService.getArrayDimension(node), "[]").joinToString("") - override fun isPrimitive(): Boolean = simpleTypeName() in setOf("int", "long", "short", "byte", "char", "double", "float", "boolean", "void") - - override fun asClassDoc(): ClassDoc? = if (isPrimitive) null else - elementType?.asClassDoc() ?: - when (node.kind) { - in NodeKind.classLike, - NodeKind.ExternalClass, - NodeKind.Exception -> module.classNamed(qualifiedTypeName()) ?: ClassDocumentationNodeAdapter(module, node) - - else -> when { - node.links.isNotEmpty() -> TypeAdapter(module, node.links.first()).asClassDoc() - else -> ClassDocumentationNodeAdapter(module, node) // TODO ? - } - } - - override fun asTypeVariable(): TypeVariable? = if (node.kind == NodeKind.TypeParameter) TypeVariableAdapter(module, node) else null - override fun asParameterizedType(): ParameterizedType? = - if (node.details(NodeKind.Type).isNotEmpty() && javaLanguageService.getArrayElementType(node) == null) - ParameterizedTypeAdapter(module, node) - else - null - - override fun asAnnotationTypeDoc(): AnnotationTypeDoc? = if (node.kind == NodeKind.AnnotationClass) AnnotationTypeDocAdapter(module, node) else null - override fun asAnnotatedType(): AnnotatedType? = if (node.annotations.isNotEmpty()) AnnotatedTypeAdapter(module, node) else null - override fun getElementType(): Type? = javaLanguageService.getArrayElementType(node)?.let { et -> TypeAdapter(module, et) } - override fun asWildcardType(): WildcardType? = null - - override fun toString(): String = qualifiedTypeName() + dimension() - override fun hashCode(): Int = node.name.hashCode() - override fun equals(other: Any?): Boolean = other is TypeAdapter && toString() == other.toString() -} - -class NotAnnotatedTypeAdapter(typeAdapter: AnnotatedTypeAdapter) : Type by typeAdapter { - override fun asAnnotatedType() = null -} - -class AnnotatedTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), AnnotatedType { - override fun underlyingType(): Type? = NotAnnotatedTypeAdapter(this) - override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray() -} - -class WildcardTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), WildcardType { - override fun extendsBounds(): Array<out Type> = node.details(NodeKind.UpperBound).map { TypeAdapter(module, it) }.toTypedArray() - override fun superBounds(): Array<out Type> = node.details(NodeKind.LowerBound).map { TypeAdapter(module, it) }.toTypedArray() -} - -class TypeVariableAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), TypeVariable { - override fun owner(): ProgramElementDoc = node.owner!!.let<DocumentationNode, ProgramElementDoc> { owner -> - when (owner.kind) { - NodeKind.Function, - NodeKind.Constructor -> ExecutableMemberAdapter(module, owner) - - NodeKind.Class, - NodeKind.Interface, - NodeKind.Enum -> ClassDocumentationNodeAdapter(module, owner) - - else -> ProgramElementAdapter(module, node.owner!!) - } - } - - override fun bounds(): Array<out Type>? = node.details(NodeKind.UpperBound).map { TypeAdapter(module, it) }.toTypedArray() - override fun annotations(): Array<out AnnotationDesc>? = node.members(NodeKind.Annotation).map { AnnotationDescAdapter(module, it) }.toTypedArray() - - override fun qualifiedTypeName(): String = node.name - override fun simpleTypeName(): String = node.name - override fun typeName(): String = node.name - - override fun hashCode(): Int = node.name.hashCode() - override fun equals(other: Any?): Boolean = other is Type && other.typeName() == typeName() && other.asTypeVariable()?.owner() == owner() - - override fun asTypeVariable(): TypeVariableAdapter = this -} - -class ParameterizedTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), ParameterizedType { - override fun typeArguments(): Array<out Type> = node.details(NodeKind.Type).map { TypeVariableAdapter(module, it) }.toTypedArray() - override fun superclassType(): Type? = - node.lookupSuperClasses(module) - .firstOrNull { it.kind == NodeKind.Class || it.kind == NodeKind.ExternalClass } - ?.let { ClassDocumentationNodeAdapter(module, it) } - - override fun interfaceTypes(): Array<out Type> = - node.lookupSuperClasses(module) - .filter { it.kind == NodeKind.Interface } - .map { ClassDocumentationNodeAdapter(module, it) } - .toTypedArray() - - override fun containingType(): Type? = when (node.owner?.kind) { - NodeKind.Package -> null - NodeKind.Class, - NodeKind.Interface, - NodeKind.Object, - NodeKind.Enum -> ClassDocumentationNodeAdapter(module, node.owner!!) - - else -> null - } -} - -class ParameterAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), Parameter { - override fun typeName(): String? = type()?.typeName() - override fun type(): Type? = TypeAdapter(module, node.detail(NodeKind.Type)) - override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray() -} - -class ReceiverParameterAdapter(module: ModuleNodeAdapter, val receiverType: DocumentationNode, val parent: ExecutableMemberAdapter) : DocumentationNodeAdapter(module, receiverType), Parameter { - override fun typeName(): String? = receiverType.name - override fun type(): Type? = TypeAdapter(module, receiverType) - override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray() - override fun name(): String = tryName("receiver") - - private tailrec fun tryName(name: String): String = when (name) { - in parent.parameters().drop(1).map { it.name() } -> tryName("$$name") - else -> name - } -} - -fun classOf(fqName: String, kind: NodeKind = NodeKind.Class) = DocumentationNode(fqName.substringAfterLast(".", fqName), Content.Empty, kind).let { node -> - val pkg = fqName.substringBeforeLast(".", "") - if (pkg.isNotEmpty()) { - node.append(DocumentationNode(pkg, Content.Empty, NodeKind.Package), RefKind.Owner) - } - - node -} - -private fun DocumentationNode.hasNonEmptyContent() = - this.content.summary !is ContentEmpty || this.content.description !is ContentEmpty || this.content.sections.isNotEmpty() - - -open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ProgramElementAdapter(module, node), ExecutableMemberDoc { - - override fun isSynthetic(): Boolean = false - override fun isNative(): Boolean = node.annotations.any { it.name == "native" } - - override fun thrownExceptions(): Array<out ClassDoc> = emptyArray() // TODO - override fun throwsTags(): Array<out ThrowsTag> = - node.content.sections - .filter { it.tag == ContentTags.Exceptions && it.subjectName != null } - .map { ThrowsTagAdapter(this, ClassDocumentationNodeAdapter(module, classOf(it.subjectName!!, NodeKind.Exception)), it.children) } - .toTypedArray() - - override fun isVarArgs(): Boolean = node.details(NodeKind.Parameter).last().hasModifier("vararg") - - override fun isSynchronized(): Boolean = node.annotations.any { it.name == "synchronized" } - - override fun paramTags(): Array<out ParamTag> = - collectParamTags(NodeKind.Parameter, sectionFilter = { it.subjectName in parameters().map { it.name() } }) - - override fun thrownExceptionTypes(): Array<out Type> = emptyArray() - override fun receiverType(): Type? = receiverNode()?.let { receiver -> TypeAdapter(module, receiver) } - override fun flatSignature(): String = node.details(NodeKind.Parameter).map { JavaLanguageService().renderType(it) }.joinToString(", ", "(", ")") - override fun signature(): String = node.details(NodeKind.Parameter).map { JavaLanguageService().renderType(it) }.joinToString(", ", "(", ")") // TODO it should be FQ types - - override fun parameters(): Array<out Parameter> = - ((receiverNode()?.let { receiver -> listOf<Parameter>(ReceiverParameterAdapter(module, receiver, this)) } ?: emptyList()) - + node.details(NodeKind.Parameter).map { ParameterAdapter(module, it) } - ).toTypedArray() - - override fun typeParameters(): Array<out TypeVariable> = node.details(NodeKind.TypeParameter).map { TypeVariableAdapter(module, it) }.toTypedArray() - - override fun typeParamTags(): Array<out ParamTag> = - collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } }) - - private fun receiverNode() = node.details(NodeKind.Receiver).let { receivers -> - when { - receivers.isNotEmpty() -> receivers.single().detail(NodeKind.Type) - else -> null - } - } -} - -class ConstructorAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ExecutableMemberAdapter(module, node), ConstructorDoc { - override fun name(): String = node.owner?.name ?: throw IllegalStateException("No owner for $node") - - override fun containingClass(): ClassDoc? { - return super.containingClass() - } -} - -class MethodAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ExecutableMemberAdapter(module, node), MethodDoc { - override fun overrides(meth: MethodDoc?): Boolean = false // TODO - - override fun overriddenType(): Type? = node.overrides.firstOrNull()?.owner?.let { owner -> TypeAdapter(module, owner) } - - override fun overriddenMethod(): MethodDoc? = node.overrides.map { MethodAdapter(module, it) }.firstOrNull() - override fun overriddenClass(): ClassDoc? = overriddenMethod()?.containingClass() - - override fun isAbstract(): Boolean = false // TODO - - override fun isDefault(): Boolean = false - - override fun returnType(): Type = TypeAdapter(module, node.detail(NodeKind.Type)) - - override fun tags(tagname: String?) = super.tags(tagname) - - override fun tags(): Array<out Tag> { - val tags = super.tags().toMutableList() - node.content.findSectionByTag(ContentTags.Return)?.let { - tags += ReturnTagAdapter(module, this, it.children) - } - - return tags.toTypedArray() - } -} - -class FieldAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ProgramElementAdapter(module, node), FieldDoc { - override fun isSynthetic(): Boolean = false - - override fun constantValueExpression(): String? = node.detailOrNull(NodeKind.Value)?.let { it.name } - override fun constantValue(): Any? = constantValueExpression() - - override fun type(): Type = TypeAdapter(module, node.detail(NodeKind.Type)) - override fun isTransient(): Boolean = node.hasAnnotation(Transient::class) - override fun serialFieldTags(): Array<out SerialFieldTag> = emptyArray() - - override fun isVolatile(): Boolean = node.hasAnnotation(Volatile::class) -} -open class ClassDocumentationNodeAdapter(module: ModuleNodeAdapter, val classNode: DocumentationNode) - : ProgramElementAdapter(module, classNode), - Type by TypeAdapter(module, classNode), - ClassDoc, - AnnotationTypeDoc { - - override fun elements(): Array<out AnnotationTypeElementDoc>? = emptyArray() // TODO - - override fun name(): String { - val parent = classNode.owner - if (parent?.kind in NodeKind.classLike) { - return parent!!.name + "." + classNode.name - } - return classNode.simpleName() - } - - override fun qualifiedName(): String? { - return super.qualifiedName() - } - override fun constructors(filter: Boolean): Array<out ConstructorDoc> = classNode.members(NodeKind.Constructor).map { ConstructorAdapter(module, it) }.toTypedArray() - override fun constructors(): Array<out ConstructorDoc> = constructors(true) - override fun importedPackages(): Array<out PackageDoc> = emptyArray() - override fun importedClasses(): Array<out ClassDoc>? = emptyArray() - override fun typeParameters(): Array<out TypeVariable> = classNode.details(NodeKind.TypeParameter).map { TypeVariableAdapter(module, it) }.toTypedArray() - override fun asTypeVariable(): TypeVariable? = if (classNode.kind == NodeKind.Class) TypeVariableAdapter(module, classNode) else null - override fun isExternalizable(): Boolean = interfaces().any { it.qualifiedName() == "java.io.Externalizable" } - override fun definesSerializableFields(): Boolean = false - override fun methods(filter: Boolean): Array<out MethodDoc> = classNode.members(NodeKind.Function).map { MethodAdapter(module, it) }.toTypedArray() // TODO include get/set methods - override fun methods(): Array<out MethodDoc> = methods(true) - override fun enumConstants(): Array<out FieldDoc>? = classNode.members(NodeKind.EnumItem).map { FieldAdapter(module, it) }.toTypedArray() - override fun isAbstract(): Boolean = classNode.details(NodeKind.Modifier).any { it.name == "abstract" } - override fun interfaceTypes(): Array<out Type> = classNode.lookupSuperClasses(module) - .filter { it.kind == NodeKind.Interface } - .map { ClassDocumentationNodeAdapter(module, it) } - .toTypedArray() - - override fun interfaces(): Array<out ClassDoc> = classNode.lookupSuperClasses(module) - .filter { it.kind == NodeKind.Interface } - .map { ClassDocumentationNodeAdapter(module, it) } - .toTypedArray() - - override fun typeParamTags(): Array<out ParamTag> = - collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } }) - - override fun fields(): Array<out FieldDoc> = fields(true) - override fun fields(filter: Boolean): Array<out FieldDoc> = classNode.members(NodeKind.Field).map { FieldAdapter(module, it) }.toTypedArray() - - override fun findClass(className: String?): ClassDoc? = null // TODO !!! - override fun serializableFields(): Array<out FieldDoc> = emptyArray() - override fun superclassType(): Type? = classNode.lookupSuperClasses(module).singleOrNull { it.kind == NodeKind.Class }?.let { ClassDocumentationNodeAdapter(module, it) } - override fun serializationMethods(): Array<out MethodDoc> = emptyArray() // TODO - override fun superclass(): ClassDoc? = classNode.lookupSuperClasses(module).singleOrNull { it.kind == NodeKind.Class }?.let { ClassDocumentationNodeAdapter(module, it) } - override fun isSerializable(): Boolean = false // TODO - override fun subclassOf(cd: ClassDoc?): Boolean { - if (cd == null) { - return false - } - - val expectedFQName = cd.qualifiedName() - val types = arrayListOf(classNode) - val visitedTypes = HashSet<String>() - - while (types.isNotEmpty()) { - val type = types.removeAt(types.lastIndex) - val fqName = type.qualifiedName() - - if (expectedFQName == fqName) { - return true - } - - visitedTypes.add(fqName) - types.addAll(type.details(NodeKind.Supertype).filter { it.qualifiedName() !in visitedTypes }) - } - - return false - } - - override fun innerClasses(): Array<out ClassDoc> = classNode.members(NodeKind.Class).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() - override fun innerClasses(filter: Boolean): Array<out ClassDoc> = innerClasses() -} - -fun DocumentationNode.lookupSuperClasses(module: ModuleNodeAdapter) = - details(NodeKind.Supertype) - .map { it.links.firstOrNull() }.mapNotNull { module.allTypes[it?.qualifiedName()] } - -fun List<DocumentationNode>.collectAllTypesRecursively(): Map<String, DocumentationNode> { - val result = hashMapOf<String, DocumentationNode>() - - fun DocumentationNode.collectTypesRecursively() { - val classLikeMembers = NodeKind.classLike.flatMap { members(it) } - classLikeMembers.forEach { - result.put(it.qualifiedName(), it) - it.collectTypesRecursively() - } - } - - forEach { it.collectTypesRecursively() } - return result -} - -class ModuleNodeAdapter(val module: DocumentationModule, val reporter: DocErrorReporter, val outputPath: String) : DocumentationNodeBareAdapter(module), DocErrorReporter by reporter, RootDoc { - val allPackages = module.members(NodeKind.Package).associateBy { it.name } - val allTypes = module.members(NodeKind.Package).collectAllTypesRecursively() - - override fun packageNamed(name: String?): PackageDoc? = allPackages[name]?.let { PackageAdapter(this, it) } - - override fun classes(): Array<out ClassDoc> = - allTypes.values.map { ClassDocumentationNodeAdapter(this, it) }.toTypedArray() - - override fun options(): Array<out Array<String>> = arrayOf( - arrayOf("-d", outputPath), - arrayOf("-docencoding", "UTF-8"), - arrayOf("-charset", "UTF-8"), - arrayOf("-keywords") - ) - - override fun specifiedPackages(): Array<out PackageDoc>? = module.members(NodeKind.Package).map { PackageAdapter(this, it) }.toTypedArray() - - override fun classNamed(qualifiedName: String?): ClassDoc? = - allTypes[qualifiedName]?.let { ClassDocumentationNodeAdapter(this, it) } - - override fun specifiedClasses(): Array<out ClassDoc> = classes() -} - -private fun DocumentationNodeAdapter.collectParamTags(kind: NodeKind, sectionFilter: (ContentSection) -> Boolean) = - (node.details(kind) - .filter(DocumentationNode::hasNonEmptyContent) - .map { ParamTagAdapter(module, this, it.name, true, it.content.children) } - - + node.content.sections - .filter(sectionFilter) - .map { - ParamTagAdapter(module, this, it.subjectName ?: "?", true, - it.children.filterNot { contentNode -> contentNode is LazyContentBlock } - ) - } - ) - .distinctBy { it.parameterName } - .toTypedArray()
\ No newline at end of file diff --git a/core/src/main/kotlin/javadoc/dokka-adapters.kt b/core/src/main/kotlin/javadoc/dokka-adapters.kt deleted file mode 100644 index 1329876a..00000000 --- a/core/src/main/kotlin/javadoc/dokka-adapters.kt +++ /dev/null @@ -1,42 +0,0 @@ -package org.jetbrains.dokka.javadoc - -import com.google.inject.Binder -import com.google.inject.Inject -import com.sun.tools.doclets.formats.html.HtmlDoclet -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Formats.DefaultAnalysisComponent -import org.jetbrains.dokka.Formats.DefaultAnalysisComponentServices -import org.jetbrains.dokka.Formats.FormatDescriptor -import org.jetbrains.dokka.Formats.KotlinAsJava -import org.jetbrains.dokka.Utilities.bind -import org.jetbrains.dokka.Utilities.toType - -class JavadocGenerator @Inject constructor(val configuration: DokkaConfiguration, val logger: DokkaLogger) : Generator { - - override fun buildPages(nodes: Iterable<DocumentationNode>) { - val module = nodes.single() as DocumentationModule - - HtmlDoclet.start(ModuleNodeAdapter(module, StandardReporter(logger), configuration.outputDir)) - } - - override fun buildOutlines(nodes: Iterable<DocumentationNode>) { - // no outline could be generated separately - } - - override fun buildSupportFiles() { - } - - override fun buildPackageList(nodes: Iterable<DocumentationNode>) { - // handled by javadoc itself - } -} - -class JavadocFormatDescriptor : - FormatDescriptor, - DefaultAnalysisComponent, - DefaultAnalysisComponentServices by KotlinAsJava { - - override fun configureOutput(binder: Binder): Unit = with(binder) { - bind<Generator>() toType JavadocGenerator::class - } -} diff --git a/core/src/main/kotlin/javadoc/reporter.kt b/core/src/main/kotlin/javadoc/reporter.kt deleted file mode 100644 index fc38368c..00000000 --- a/core/src/main/kotlin/javadoc/reporter.kt +++ /dev/null @@ -1,34 +0,0 @@ -package org.jetbrains.dokka.javadoc - -import com.sun.javadoc.DocErrorReporter -import com.sun.javadoc.SourcePosition -import org.jetbrains.dokka.DokkaLogger - -class StandardReporter(val logger: DokkaLogger) : DocErrorReporter { - override fun printWarning(msg: String?) { - logger.warn(msg.toString()) - } - - override fun printWarning(pos: SourcePosition?, msg: String?) { - logger.warn(format(pos, msg)) - } - - override fun printError(msg: String?) { - logger.error(msg.toString()) - } - - override fun printError(pos: SourcePosition?, msg: String?) { - logger.error(format(pos, msg)) - } - - override fun printNotice(msg: String?) { - logger.info(msg.toString()) - } - - override fun printNotice(pos: SourcePosition?, msg: String?) { - logger.info(format(pos, msg)) - } - - private fun format(pos: SourcePosition?, msg: String?) = - if (pos == null) msg.toString() else "${pos.file()}:${pos.line()}:${pos.column()}: $msg" -}
\ No newline at end of file diff --git a/core/src/main/kotlin/javadoc/source-position.kt b/core/src/main/kotlin/javadoc/source-position.kt deleted file mode 100644 index 6125f968..00000000 --- a/core/src/main/kotlin/javadoc/source-position.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.jetbrains.dokka.javadoc - -import com.sun.javadoc.SourcePosition -import org.jetbrains.dokka.DocumentationNode -import org.jetbrains.dokka.NodeKind -import java.io.File - -class SourcePositionAdapter(val docNode: DocumentationNode) : SourcePosition { - - private val sourcePositionParts: List<String> by lazy { - docNode.details(NodeKind.SourcePosition).firstOrNull()?.name?.split(":") ?: emptyList() - } - - override fun file(): File? = if (sourcePositionParts.isEmpty()) null else File(sourcePositionParts[0]) - - override fun line(): Int = sourcePositionParts.getOrNull(1)?.toInt() ?: -1 - - override fun column(): Int = sourcePositionParts.getOrNull(2)?.toInt() ?: -1 -} diff --git a/core/src/main/kotlin/javadoc/tags.kt b/core/src/main/kotlin/javadoc/tags.kt deleted file mode 100644 index 99c9bfff..00000000 --- a/core/src/main/kotlin/javadoc/tags.kt +++ /dev/null @@ -1,228 +0,0 @@ -package org.jetbrains.dokka.javadoc - -import com.sun.javadoc.* -import org.jetbrains.dokka.* -import java.util.* - -class TagImpl(val holder: Doc, val name: String, val text: String): Tag { - override fun text(): String? = text - - override fun holder(): Doc = holder - override fun firstSentenceTags(): Array<out Tag>? = arrayOf() - override fun inlineTags(): Array<out Tag>? = arrayOf() - - override fun name(): String = name - override fun kind(): String = name - - override fun position(): SourcePosition = holder.position() -} - -class TextTag(val holder: Doc, val content: ContentText) : Tag { - val plainText: String - get() = content.text - - override fun name(): String = "Text" - override fun kind(): String = name() - override fun text(): String? = plainText - override fun inlineTags(): Array<out Tag> = arrayOf(this) - override fun holder(): Doc = holder - override fun firstSentenceTags(): Array<out Tag> = arrayOf(this) - override fun position(): SourcePosition = holder.position() -} - -abstract class SeeTagAdapter(val holder: Doc, val content: ContentNodeLink) : SeeTag { - override fun position(): SourcePosition? = holder.position() - override fun name(): String = "@see" - override fun kind(): String = "@see" - override fun holder(): Doc = holder - - override fun text(): String? = content.node?.name ?: "(?)" -} - -class SeeExternalLinkTagAdapter(val holder: Doc, val link: ContentExternalLink) : SeeTag { - override fun position(): SourcePosition = holder.position() - override fun text(): String = label() - override fun inlineTags(): Array<out Tag> = emptyArray() // TODO - - override fun label(): String { - val label = link.asText() ?: link.href - return "<a href=\"${link.href}\">$label</a>" - } - - override fun referencedPackage(): PackageDoc? = null - override fun referencedClass(): ClassDoc? = null - override fun referencedMemberName(): String? = null - override fun referencedClassName(): String? = null - override fun referencedMember(): MemberDoc? = null - override fun holder(): Doc = holder - override fun firstSentenceTags(): Array<out Tag> = inlineTags() - override fun name(): String = "@link" - override fun kind(): String = "@see" -} - -fun ContentBlock.asText(): String? { - val contentText = children.singleOrNull() as? ContentText - return contentText?.text -} - -class SeeMethodTagAdapter(holder: Doc, val method: MethodAdapter, content: ContentNodeLink) : SeeTagAdapter(holder, content) { - override fun referencedMember(): MemberDoc = method - override fun referencedMemberName(): String = method.name() - override fun referencedPackage(): PackageDoc? = null - override fun referencedClass(): ClassDoc? = method.containingClass() - override fun referencedClassName(): String = method.containingClass()?.name() ?: "" - override fun label(): String = content.text ?: "${method.containingClass()?.name()}.${method.name()}" - - override fun inlineTags(): Array<out Tag> = emptyArray() // TODO - override fun firstSentenceTags(): Array<out Tag> = inlineTags() // TODO -} - -class SeeClassTagAdapter(holder: Doc, val clazz: ClassDocumentationNodeAdapter, content: ContentNodeLink) : SeeTagAdapter(holder, content) { - override fun referencedMember(): MemberDoc? = null - override fun referencedMemberName(): String? = null - override fun referencedPackage(): PackageDoc? = null - override fun referencedClass(): ClassDoc = clazz - override fun referencedClassName(): String = clazz.name() - override fun label(): String = "${clazz.classNode.kind.name.toLowerCase()} ${clazz.name()}" - - override fun inlineTags(): Array<out Tag> = emptyArray() // TODO - override fun firstSentenceTags(): Array<out Tag> = inlineTags() // TODO -} - -class ParamTagAdapter(val module: ModuleNodeAdapter, - val holder: Doc, - val parameterName: String, - val typeParameter: Boolean, - val content: List<ContentNode>) : ParamTag { - - constructor(module: ModuleNodeAdapter, holder: Doc, parameterName: String, isTypeParameter: Boolean, content: ContentNode) - : this(module, holder, parameterName, isTypeParameter, listOf(content)) { - } - - override fun name(): String = "@param" - override fun kind(): String = name() - override fun holder(): Doc = holder - override fun position(): SourcePosition? = holder.position() - - override fun text(): String = "@param $parameterName ${parameterComment()}" // Seems has no effect, so used for debug - override fun inlineTags(): Array<out Tag> = buildInlineTags(module, holder, content).toTypedArray() - override fun firstSentenceTags(): Array<out Tag> = arrayOf(TextTag(holder, ContentText(text()))) - - override fun isTypeParameter(): Boolean = typeParameter - override fun parameterComment(): String = content.toString() // TODO - override fun parameterName(): String = parameterName -} - - -class ThrowsTagAdapter(val holder: Doc, val type: ClassDocumentationNodeAdapter, val content: List<ContentNode>) : ThrowsTag { - override fun name(): String = "@throws" - override fun kind(): String = name() - override fun holder(): Doc = holder - override fun position(): SourcePosition? = holder.position() - - override fun text(): String = "${name()} ${exceptionName()} ${exceptionComment()}" - override fun inlineTags(): Array<out Tag> = buildInlineTags(type.module, holder, content).toTypedArray() - override fun firstSentenceTags(): Array<out Tag> = emptyArray() - - override fun exceptionComment(): String = content.toString() - override fun exceptionType(): Type = type - override fun exception(): ClassDoc = type - override fun exceptionName(): String = type.qualifiedTypeName() -} - -class ReturnTagAdapter(val module: ModuleNodeAdapter, val holder: Doc, val content: List<ContentNode>) : Tag { - override fun name(): String = "@return" - override fun kind() = name() - override fun holder() = holder - override fun position(): SourcePosition? = holder.position() - - override fun text(): String = "@return $content" // Seems has no effect, so used for debug - override fun inlineTags(): Array<Tag> = buildInlineTags(module, holder, content).toTypedArray() - override fun firstSentenceTags(): Array<Tag> = inlineTags() -} - -fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, tags: List<ContentNode>): List<Tag> = ArrayList<Tag>().apply { tags.forEach { buildInlineTags(module, holder, it, this) } } - -fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, root: ContentNode): List<Tag> = ArrayList<Tag>().apply { buildInlineTags(module, holder, root, this) } - -private fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, nodes: List<ContentNode>, result: MutableList<Tag>) { - nodes.forEach { - buildInlineTags(module, holder, it, result) - } -} - - -private fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, node: ContentNode, result: MutableList<Tag>) { - fun surroundWith(module: ModuleNodeAdapter, holder: Doc, prefix: String, postfix: String, node: ContentBlock, result: MutableList<Tag>) { - if (node.children.isNotEmpty()) { - val open = TextTag(holder, ContentText(prefix)) - val close = TextTag(holder, ContentText(postfix)) - - result.add(open) - buildInlineTags(module, holder, node.children, result) - - if (result.last() === open) { - result.removeAt(result.lastIndex) - } else { - result.add(close) - } - } - } - - fun surroundWith(module: ModuleNodeAdapter, holder: Doc, prefix: String, postfix: String, node: ContentNode, result: MutableList<Tag>) { - if (node !is ContentEmpty) { - val open = TextTag(holder, ContentText(prefix)) - val close = TextTag(holder, ContentText(postfix)) - - result.add(open) - buildInlineTags(module, holder, node, result) - if (result.last() === open) { - result.removeAt(result.lastIndex) - } else { - result.add(close) - } - } - } - - when (node) { - is ContentText -> result.add(TextTag(holder, node)) - is ContentNodeLink -> { - val target = node.node - when (target?.kind) { - NodeKind.Function -> result.add(SeeMethodTagAdapter(holder, MethodAdapter(module, node.node!!), node)) - - in NodeKind.classLike -> result.add(SeeClassTagAdapter(holder, ClassDocumentationNodeAdapter(module, node.node!!), node)) - - else -> buildInlineTags(module, holder, node.children, result) - } - } - is ContentExternalLink -> result.add(SeeExternalLinkTagAdapter(holder, node)) - is ContentCode -> surroundWith(module, holder, "<code>", "</code>", node, result) - is ContentBlockCode -> surroundWith(module, holder, "<code><pre>", "</pre></code>", node, result) - is ContentEmpty -> {} - is ContentEmphasis -> surroundWith(module, holder, "<em>", "</em>", node, result) - is ContentHeading -> surroundWith(module, holder, "<h${node.level}>", "</h${node.level}>", node, result) - is ContentEntity -> result.add(TextTag(holder, ContentText(node.text))) // TODO ?? - is ContentIdentifier -> result.add(TextTag(holder, ContentText(node.text))) // TODO - is ContentKeyword -> result.add(TextTag(holder, ContentText(node.text))) // TODO - is ContentListItem -> surroundWith(module, holder, "<li>", "</li>", node, result) - is ContentOrderedList -> surroundWith(module, holder, "<ol>", "</ol>", node, result) - is ContentUnorderedList -> surroundWith(module, holder, "<ul>", "</ul>", node, result) - is ContentParagraph -> surroundWith(module, holder, "<p>", "</p>", node, result) - is ContentSection -> surroundWith(module, holder, "<p>", "</p>", node, result) // TODO how section should be represented? - is ContentNonBreakingSpace -> result.add(TextTag(holder, ContentText(" "))) - is ContentStrikethrough -> surroundWith(module, holder, "<strike>", "</strike>", node, result) - is ContentStrong -> surroundWith(module, holder, "<strong>", "</strong>", node, result) - is ContentSymbol -> result.add(TextTag(holder, ContentText(node.text))) // TODO? - is Content -> { - surroundWith(module, holder, "<p>", "</p>", node.summary, result) - surroundWith(module, holder, "<p>", "</p>", node.description, result) - } - is ContentBlock -> { - surroundWith(module, holder, "", "", node, result) - } - is ContentHardLineBreak -> result.add(TextTag(holder, ContentText("<br/>"))) - - else -> result.add(TextTag(holder, ContentText("$node"))) - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/links/DRI.kt b/core/src/main/kotlin/links/DRI.kt new file mode 100644 index 00000000..4a555e71 --- /dev/null +++ b/core/src/main/kotlin/links/DRI.kt @@ -0,0 +1,105 @@ +package org.jetbrains.dokka.links + +import org.jetbrains.dokka.model.ClassKind + +/** + * [DRI] stands for DokkaResourceIdentifier + */ +data class DRI( + val packageName: String? = null, + val classNames: String? = null, + val callable: Callable? = null, + val target: DriTarget = PointingToDeclaration, + val extra: String? = null +) { + override fun toString(): String = + "${packageName.orEmpty()}/${classNames.orEmpty()}/${callable?.name.orEmpty()}/${callable?.signature() + .orEmpty()}/$target/${extra.orEmpty()}" + + companion object { + val topLevel = DRI() + } +} + +val DriOfUnit = DRI("kotlin", "Unit") +val DriOfAny = DRI("kotlin", "Any") + +fun DRI.withClass(name: String) = copy(classNames = if (classNames.isNullOrBlank()) name else "$classNames.$name") + +fun DRI.withTargetToDeclaration() = copy(target = PointingToDeclaration) + +val DRI.parent: DRI + get() = when { + extra != null -> copy(extra = null) + target != PointingToDeclaration -> copy(target = PointingToDeclaration) + callable != null -> copy(callable = null) + classNames != null -> copy(classNames = classNames.substringBeforeLast(".", "").takeIf { it.isNotBlank() }) + else -> DRI.topLevel + } + +val DRI.sureClassNames + get() = classNames ?: throw IllegalStateException("Malformed DRI. It requires classNames in this context.") + +data class Callable( + val name: String, + val receiver: TypeReference? = null, + val params: List<TypeReference> +) { + fun signature() = "${receiver?.toString().orEmpty()}#${params.joinToString("#")}" + + companion object +} + +sealed class TypeReference { + companion object +} + +data class JavaClassReference(val name: String) : TypeReference() { + override fun toString(): String = name +} + +data class TypeParam(val bounds: List<TypeReference>) : TypeReference() + +data class TypeConstructor( + val fullyQualifiedName: String, + val params: List<TypeReference> +) : TypeReference() { + override fun toString() = fullyQualifiedName + + (if (params.isNotEmpty()) "[${params.joinToString(",")}]" else "") +} + +object SelfType : TypeReference() { + override fun toString() = "^" +} + +data class Nullable(val wrapped: TypeReference) : TypeReference() { + override fun toString() = "$wrapped?" +} + +object StarProjection : TypeReference() { + override fun toString() = "*" +} + +sealed class DriTarget { + override fun toString(): String = this.javaClass.simpleName + + companion object +} + +data class PointingToGenericParameters(val parameterIndex: Int) : DriTarget() { + override fun toString(): String = "PointingToGenericParameters($parameterIndex)" +} + +object PointingToDeclaration : DriTarget() + +data class PointingToCallableParameters(val parameterIndex: Int) : DriTarget() { + override fun toString(): String = "PointingToCallableParameters($parameterIndex)" +} + +fun DriTarget.nextTarget(): DriTarget = when (this) { + is PointingToGenericParameters -> PointingToGenericParameters(this.parameterIndex + 1) + is PointingToCallableParameters -> PointingToCallableParameters(this.parameterIndex + 1) + else -> this +} + +data class DriWithKind(val dri: DRI, val kind: ClassKind) diff --git a/core/src/main/kotlin/model/Documentable.kt b/core/src/main/kotlin/model/Documentable.kt new file mode 100644 index 00000000..2c3e1323 --- /dev/null +++ b/core/src/main/kotlin/model/Documentable.kt @@ -0,0 +1,398 @@ +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.DriWithKind +import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.model.properties.WithExtraProperties + + +abstract class Documentable : WithChildren<Documentable> { + abstract val name: String? + abstract val dri: DRI + abstract val documentation: SourceSetDependent<DocumentationNode> + abstract val sourceSets: Set<DokkaSourceSet> + abstract val expectPresentInSet: DokkaSourceSet? + + override fun toString(): String = + "${javaClass.simpleName}($dri)" + + override fun equals(other: Any?) = + other is Documentable && this.dri == other.dri // TODO: https://github.com/Kotlin/dokka/pull/667#discussion_r382555806 + + override fun hashCode() = dri.hashCode() +} + +typealias SourceSetDependent<T> = Map<DokkaSourceSet, T> + +interface WithExpectActual { + val sources: SourceSetDependent<DocumentableSource> +} + +interface WithScope { + val functions: List<DFunction> + val properties: List<DProperty> + val classlikes: List<DClasslike> +} + +interface WithVisibility { + val visibility: SourceSetDependent<Visibility> +} + +interface WithType { + val type: Bound +} + +interface WithAbstraction { + val modifier: SourceSetDependent<Modifier> +} + +sealed class Modifier(val name: String) +sealed class KotlinModifier(name: String) : Modifier(name) { + object Abstract : KotlinModifier("abstract") + object Open : KotlinModifier("open") + object Final : KotlinModifier("final") + object Sealed : KotlinModifier("sealed") + object Empty : KotlinModifier("") +} + +sealed class JavaModifier(name: String) : Modifier(name) { + object Abstract : JavaModifier("abstract") + object Final : JavaModifier("final") + object Empty : JavaModifier("") +} + +interface WithCompanion { + val companion: DObject? +} + +interface WithConstructors { + val constructors: List<DFunction> +} + +interface WithGenerics { + val generics: List<DTypeParameter> +} + +interface WithSupertypes { + val supertypes: SourceSetDependent<List<DriWithKind>> +} + +interface Callable : WithVisibility, WithType, WithAbstraction, WithExpectActual { + val receiver: DParameter? +} + +sealed class DClasslike : Documentable(), WithScope, WithVisibility, WithExpectActual + +data class DModule( + override val name: String, + val packages: List<DPackage>, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet? = null, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DModule> = PropertyContainer.empty() +) : Documentable(), WithExtraProperties<DModule> { + override val dri: DRI = DRI.topLevel + override val children: List<Documentable> + get() = packages + + override fun withNewExtras(newExtras: PropertyContainer<DModule>) = copy(extra = newExtras) +} + +data class DPackage( + override val dri: DRI, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + val typealiases: List<DTypeAlias>, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet? = null, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DPackage> = PropertyContainer.empty() +) : Documentable(), WithScope, WithExtraProperties<DPackage> { + override val name = dri.packageName.orEmpty() + override val children: List<Documentable> + get() = (properties + functions + classlikes) + + override fun withNewExtras(newExtras: PropertyContainer<DPackage>) = copy(extra = newExtras) +} + +data class DClass( + override val dri: DRI, + override val name: String, + override val constructors: List<DFunction>, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + override val sources: SourceSetDependent<DocumentableSource>, + override val visibility: SourceSetDependent<Visibility>, + override val companion: DObject?, + override val generics: List<DTypeParameter>, + override val supertypes: SourceSetDependent<List<DriWithKind>>, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val modifier: SourceSetDependent<Modifier>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DClass> = PropertyContainer.empty() +) : DClasslike(), WithAbstraction, WithCompanion, WithConstructors, WithGenerics, WithSupertypes, + WithExtraProperties<DClass> { + + override val children: List<Documentable> + get() = (functions + properties + classlikes + constructors) + + override fun withNewExtras(newExtras: PropertyContainer<DClass>) = copy(extra = newExtras) +} + +data class DEnum( + override val dri: DRI, + override val name: String, + val entries: List<DEnumEntry>, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent<DocumentableSource>, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + override val visibility: SourceSetDependent<Visibility>, + override val companion: DObject?, + override val constructors: List<DFunction>, + override val supertypes: SourceSetDependent<List<DriWithKind>>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DEnum> = PropertyContainer.empty() +) : DClasslike(), WithCompanion, WithConstructors, WithSupertypes, WithExtraProperties<DEnum> { + override val children: List<Documentable> + get() = (entries + functions + properties + classlikes + constructors) + + override fun withNewExtras(newExtras: PropertyContainer<DEnum>) = copy(extra = newExtras) +} + +data class DEnumEntry( + override val dri: DRI, + override val name: String, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DEnumEntry> = PropertyContainer.empty() +) : Documentable(), WithScope, WithExtraProperties<DEnumEntry> { + override val children: List<Documentable> + get() = (functions + properties + classlikes) + + override fun withNewExtras(newExtras: PropertyContainer<DEnumEntry>) = copy(extra = newExtras) +} + +data class DFunction( + override val dri: DRI, + override val name: String, + val isConstructor: Boolean, + val parameters: List<DParameter>, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent<DocumentableSource>, + override val visibility: SourceSetDependent<Visibility>, + override val type: Bound, + override val generics: List<DTypeParameter>, + override val receiver: DParameter?, + override val modifier: SourceSetDependent<Modifier>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DFunction> = PropertyContainer.empty() +) : Documentable(), Callable, WithGenerics, WithExtraProperties<DFunction> { + override val children: List<Documentable> + get() = parameters + + override fun withNewExtras(newExtras: PropertyContainer<DFunction>) = copy(extra = newExtras) +} + +data class DInterface( + override val dri: DRI, + override val name: String, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent<DocumentableSource>, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + override val visibility: SourceSetDependent<Visibility>, + override val companion: DObject?, + override val generics: List<DTypeParameter>, + override val supertypes: SourceSetDependent<List<DriWithKind>>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DInterface> = PropertyContainer.empty() +) : DClasslike(), WithCompanion, WithGenerics, WithSupertypes, WithExtraProperties<DInterface> { + override val children: List<Documentable> + get() = (functions + properties + classlikes) + + override fun withNewExtras(newExtras: PropertyContainer<DInterface>) = copy(extra = newExtras) +} + +data class DObject( + override val name: String?, + override val dri: DRI, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent<DocumentableSource>, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + override val visibility: SourceSetDependent<Visibility>, + override val supertypes: SourceSetDependent<List<DriWithKind>>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DObject> = PropertyContainer.empty() +) : DClasslike(), WithSupertypes, WithExtraProperties<DObject> { + override val children: List<Documentable> + get() = (functions + properties + classlikes) as List<Documentable> + + override fun withNewExtras(newExtras: PropertyContainer<DObject>) = copy(extra = newExtras) +} + +data class DAnnotation( + override val name: String, + override val dri: DRI, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent<DocumentableSource>, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + override val visibility: SourceSetDependent<Visibility>, + override val companion: DObject?, + override val constructors: List<DFunction>, + override val generics: List<DTypeParameter>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DAnnotation> = PropertyContainer.empty() +) : DClasslike(), WithCompanion, WithConstructors, WithExtraProperties<DAnnotation>, WithGenerics { + override val children: List<Documentable> + get() = (functions + properties + classlikes + constructors) + + override fun withNewExtras(newExtras: PropertyContainer<DAnnotation>) = copy(extra = newExtras) +} + +data class DProperty( + override val dri: DRI, + override val name: String, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent<DocumentableSource>, + override val visibility: SourceSetDependent<Visibility>, + override val type: Bound, + override val receiver: DParameter?, + val setter: DFunction?, + val getter: DFunction?, + override val modifier: SourceSetDependent<Modifier>, + override val sourceSets: Set<DokkaSourceSet>, + override val generics: List<DTypeParameter>, + override val extra: PropertyContainer<DProperty> = PropertyContainer.empty() +) : Documentable(), Callable, WithExtraProperties<DProperty>, WithGenerics { + override val children: List<Nothing> + get() = emptyList() + + override fun withNewExtras(newExtras: PropertyContainer<DProperty>) = copy(extra = newExtras) +} + +// TODO: treat named Parameters and receivers differently +data class DParameter( + override val dri: DRI, + override val name: String?, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + val type: Bound, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DParameter> = PropertyContainer.empty() +) : Documentable(), WithExtraProperties<DParameter> { + override val children: List<Nothing> + get() = emptyList() + + override fun withNewExtras(newExtras: PropertyContainer<DParameter>) = copy(extra = newExtras) +} + +data class DTypeParameter( + override val dri: DRI, + override val name: String, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + val bounds: List<Bound>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DTypeParameter> = PropertyContainer.empty() +) : Documentable(), WithExtraProperties<DTypeParameter> { + override val children: List<Nothing> + get() = emptyList() + + override fun withNewExtras(newExtras: PropertyContainer<DTypeParameter>) = copy(extra = newExtras) +} + +data class DTypeAlias( + override val dri: DRI, + override val name: String, + override val type: Bound, + val underlyingType: SourceSetDependent<Bound>, + override val visibility: SourceSetDependent<Visibility>, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DTypeAlias> = PropertyContainer.empty() +) : Documentable(), WithType, WithVisibility, WithExtraProperties<DTypeAlias> { + override val children: List<Nothing> + get() = emptyList() + + override fun withNewExtras(newExtras: PropertyContainer<DTypeAlias>) = copy(extra = newExtras) +} + +sealed class Projection +sealed class Bound : Projection() +data class OtherParameter(val declarationDRI: DRI, val name: String) : Bound() +object Star : Projection() +data class TypeConstructor( + val dri: DRI, + val projections: List<Projection>, + val modifier: FunctionModifiers = FunctionModifiers.NONE +) : Bound() + +data class Nullable(val inner: Bound) : Bound() +data class Variance(val kind: Kind, val inner: Bound) : Projection() { + enum class Kind { In, Out } +} + +data class PrimitiveJavaType(val name: String) : Bound() +object Void : Bound() +object JavaObject : Bound() +object Dynamic : Bound() +data class UnresolvedBound(val name: String) : Bound() + +enum class FunctionModifiers { + NONE, FUNCTION, EXTENSION +} + +private fun String.shorten(maxLength: Int) = lineSequence().first().let { + if (it.length != length || it.length > maxLength) it.take(maxLength - 3) + "..." else it +} + +fun Documentable.dfs(predicate: (Documentable) -> Boolean): Documentable? = + if (predicate(this)) { + this + } else { + this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() + } + +sealed class Visibility(val name: String) +sealed class KotlinVisibility(name: String) : Visibility(name) { + object Public : KotlinVisibility("public") + object Private : KotlinVisibility("private") + object Protected : KotlinVisibility("protected") + object Internal : KotlinVisibility("internal") +} + +sealed class JavaVisibility(name: String) : Visibility(name) { + object Public : JavaVisibility("public") + object Private : JavaVisibility("private") + object Protected : JavaVisibility("protected") + object Default : JavaVisibility("") +} + +fun <T> SourceSetDependent<T>?.orEmpty(): SourceSetDependent<T> = this ?: emptyMap() + +interface DocumentableSource { + val path: String +} diff --git a/core/src/main/kotlin/model/WithChildren.kt b/core/src/main/kotlin/model/WithChildren.kt new file mode 100644 index 00000000..589bcd2a --- /dev/null +++ b/core/src/main/kotlin/model/WithChildren.kt @@ -0,0 +1,64 @@ +package org.jetbrains.dokka.model + +interface WithChildren<out T> { + val children: List<T> +} + +inline fun <reified T> WithChildren<*>.firstChildOfTypeOrNull(): T? = + children.filterIsInstance<T>().firstOrNull() + +inline fun <reified T> WithChildren<*>.firstChildOfTypeOrNull(predicate: (T) -> Boolean): T? = + children.filterIsInstance<T>().firstOrNull(predicate) + +inline fun <reified T> WithChildren<*>.firstChildOfType(): T = + children.filterIsInstance<T>().first() + +inline fun <reified T> WithChildren<*>.firstChildOfType(predicate: (T) -> Boolean): T = + children.filterIsInstance<T>().first(predicate) + +inline fun <reified T> WithChildren<WithChildren<*>>.firstMemberOfType(): T where T : WithChildren<*> { + return withDescendants().filterIsInstance<T>().first() +} + +inline fun <reified T> WithChildren<WithChildren<*>>.firstMemberOfTypeOrNull(): T? where T : WithChildren<*> { + return withDescendants().filterIsInstance<T>().firstOrNull() +} + +fun <T> T.withDescendants(): Sequence<T> where T : WithChildren<T> { + return sequence { + yield(this@withDescendants) + children.forEach { child -> + yieldAll(child.withDescendants()) + } + } +} + +@JvmName("withDescendantsProjection") +fun WithChildren<*>.withDescendants(): Sequence<Any?> { + return sequence { + yield(this@withDescendants) + children.forEach { child -> + if (child is WithChildren<*>) { + yieldAll(child.withDescendants()) + } + } + } +} + +@JvmName("withDescendantsAny") +fun WithChildren<Any>.withDescendants(): Sequence<Any> { + return sequence { + yield(this@withDescendants) + children.forEach { child -> + if (child is WithChildren<*>) { + yieldAll(child.withDescendants().filterNotNull()) + } + } + } +} + +fun <T> T.dfs(predicate: (T) -> Boolean): T? where T : WithChildren<T> = if (predicate(this)) { + this +} else { + children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() +} diff --git a/core/src/main/kotlin/model/additionalExtras.kt b/core/src/main/kotlin/model/additionalExtras.kt new file mode 100644 index 00000000..94d0e751 --- /dev/null +++ b/core/src/main/kotlin/model/additionalExtras.kt @@ -0,0 +1,75 @@ +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.properties.ExtraProperty +import org.jetbrains.dokka.model.properties.MergeStrategy + +class AdditionalModifiers(val content: SourceSetDependent<Set<ExtraModifiers>>) : ExtraProperty<Documentable> { + companion object : ExtraProperty.Key<Documentable, AdditionalModifiers> { + override fun mergeStrategyFor( + left: AdditionalModifiers, + right: AdditionalModifiers + ): MergeStrategy<Documentable> = MergeStrategy.Replace(AdditionalModifiers(left.content + right.content)) + } + + override fun equals(other: Any?): Boolean = + if (other is AdditionalModifiers) other.content == content else false + + override fun hashCode() = content.hashCode() + override val key: ExtraProperty.Key<Documentable, *> = AdditionalModifiers +} + +fun SourceSetDependent<Set<ExtraModifiers>>.toAdditionalModifiers() = AdditionalModifiers(this) + +class Annotations(val content: SourceSetDependent<List<Annotation>>) : ExtraProperty<Documentable> { + companion object : ExtraProperty.Key<Documentable, Annotations> { + override fun mergeStrategyFor(left: Annotations, right: Annotations): MergeStrategy<Documentable> = + MergeStrategy.Replace(Annotations(left.content + right.content)) + } + + override val key: ExtraProperty.Key<Documentable, *> = Annotations + + data class Annotation(val dri: DRI, val params: Map<String, AnnotationParameterValue>, val mustBeDocumented: Boolean = false) { + override fun equals(other: Any?): Boolean = when (other) { + is Annotation -> dri == other.dri + else -> false + } + + override fun hashCode(): Int = dri.hashCode() + } +} + +fun SourceSetDependent<List<Annotations.Annotation>>.toAnnotations() = Annotations(this) + +sealed class AnnotationParameterValue +data class AnnotationValue(val annotation: Annotations.Annotation) : AnnotationParameterValue() +data class ArrayValue(val value: List<AnnotationParameterValue>) : AnnotationParameterValue() +data class EnumValue(val enumName: String, val enumDri: DRI) : AnnotationParameterValue() +data class ClassValue(val className: String, val classDRI: DRI) : AnnotationParameterValue() +data class StringValue(val value: String) : AnnotationParameterValue() + + +object PrimaryConstructorExtra : ExtraProperty<DFunction>, ExtraProperty.Key<DFunction, PrimaryConstructorExtra> { + override val key: ExtraProperty.Key<DFunction, *> = this +} + +data class ActualTypealias(val underlyingType: SourceSetDependent<Bound>) : ExtraProperty<DClasslike> { + companion object : ExtraProperty.Key<DClasslike, ActualTypealias> { + override fun mergeStrategyFor( + left: ActualTypealias, + right: ActualTypealias + ) = + MergeStrategy.Replace(ActualTypealias(left.underlyingType + right.underlyingType)) + } + + override val key: ExtraProperty.Key<DClasslike, ActualTypealias> = ActualTypealias +} + +data class ConstructorValues(val values: SourceSetDependent<List<String>>) : ExtraProperty<DEnumEntry>{ + companion object : ExtraProperty.Key<DEnumEntry, ConstructorValues> { + override fun mergeStrategyFor(left: ConstructorValues, right: ConstructorValues) = + MergeStrategy.Replace(ConstructorValues(left.values + right.values)) + } + + override val key: ExtraProperty.Key<DEnumEntry, ConstructorValues> = ConstructorValues +}
\ No newline at end of file diff --git a/core/src/main/kotlin/model/classKinds.kt b/core/src/main/kotlin/model/classKinds.kt new file mode 100644 index 00000000..be8c47b0 --- /dev/null +++ b/core/src/main/kotlin/model/classKinds.kt @@ -0,0 +1,20 @@ +package org.jetbrains.dokka.model + +interface ClassKind + +enum class KotlinClassKindTypes : ClassKind { + CLASS, + INTERFACE, + ENUM_CLASS, + ENUM_ENTRY, + ANNOTATION_CLASS, + OBJECT; +} + +enum class JavaClassKindTypes : ClassKind { + CLASS, + INTERFACE, + ENUM_CLASS, + ENUM_ENTRY, + ANNOTATION_CLASS; +} diff --git a/core/src/main/kotlin/model/defaultValues.kt b/core/src/main/kotlin/model/defaultValues.kt new file mode 100644 index 00000000..ab6cd376 --- /dev/null +++ b/core/src/main/kotlin/model/defaultValues.kt @@ -0,0 +1,13 @@ +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.model.properties.ExtraProperty +import org.jetbrains.dokka.model.properties.MergeStrategy + +class DefaultValue(val value: String): ExtraProperty<DParameter> { + companion object : ExtraProperty.Key<DParameter, DefaultValue> { + override fun mergeStrategyFor(left: DefaultValue, right: DefaultValue): MergeStrategy<DParameter> = MergeStrategy.Remove // TODO pass a logger somehow and log this + } + + override val key: ExtraProperty.Key<DParameter, *> + get() = Companion +}
\ No newline at end of file diff --git a/core/src/main/kotlin/model/doc/DocTag.kt b/core/src/main/kotlin/model/doc/DocTag.kt new file mode 100644 index 00000000..dc2cd2be --- /dev/null +++ b/core/src/main/kotlin/model/doc/DocTag.kt @@ -0,0 +1,96 @@ +package org.jetbrains.dokka.model.doc + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.WithChildren + +sealed class DocTag( + override val children: List<DocTag>, + val params: Map<String, String> +) : WithChildren<DocTag> { + override fun equals(other: Any?): Boolean = + ( + other != null && + other::class == this::class && + this.children == (other as DocTag).children && + this.params == other.params + ) + + override fun hashCode(): Int = children.hashCode() + params.hashCode() +} + +class A(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Big(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class B(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class BlockQuote(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +object Br : DocTag(emptyList(), emptyMap()) +class Cite(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +sealed class Code(children: List<DocTag>, params: Map<String, String>) : DocTag(children, params) +class CodeInline(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : Code(children, params) +class CodeBlock(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : Code(children, params) +class Dd(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Dfn(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Dir(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Div(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Dl(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Dt(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Em(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Font(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Footer(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Frame(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class FrameSet(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class H1(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class H2(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class H3(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class H4(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class H5(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class H6(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Head(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Header(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Html(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class I(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class IFrame(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Img(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Input(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Li(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Link(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Listing(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Main(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Menu(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Meta(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Nav(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class NoFrames(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class NoScript(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Ol(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class P(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Pre(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Script(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Section(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Small(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Span(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Strikethrough(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Strong(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Sub(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Sup(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Table(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Text(val body: String = "", children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) { + override fun equals(other: Any?): Boolean = super.equals(other) && this.body == (other as Text).body + override fun hashCode(): Int = super.hashCode() + body.hashCode() +} +class TBody(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Td(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class TFoot(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Th(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class THead(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Title(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Tr(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Tt(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class U(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Ul(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Var(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class DocumentationLink(val dri: DRI, children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) { + override fun equals(other: Any?): Boolean = super.equals(other) && this.dri == (other as DocumentationLink).dri + override fun hashCode(): Int = super.hashCode() + dri.hashCode() +} +object HorizontalRule : DocTag(emptyList(), emptyMap()) +class Index(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class CustomDocTag(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) diff --git a/core/src/main/kotlin/model/doc/DocumentationNode.kt b/core/src/main/kotlin/model/doc/DocumentationNode.kt new file mode 100644 index 00000000..6eb26a6a --- /dev/null +++ b/core/src/main/kotlin/model/doc/DocumentationNode.kt @@ -0,0 +1,5 @@ +package org.jetbrains.dokka.model.doc + +import org.jetbrains.dokka.model.WithChildren + +data class DocumentationNode(override val children: List<TagWrapper>): WithChildren<TagWrapper> diff --git a/core/src/main/kotlin/model/doc/TagWrapper.kt b/core/src/main/kotlin/model/doc/TagWrapper.kt new file mode 100644 index 00000000..095f3eaf --- /dev/null +++ b/core/src/main/kotlin/model/doc/TagWrapper.kt @@ -0,0 +1,39 @@ +package org.jetbrains.dokka.model.doc + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.WithChildren + +sealed class TagWrapper(val root: DocTag) : WithChildren<DocTag> { + + override val children: List<DocTag> + get() = root.children + + override fun equals(other: Any?): Boolean = + ( + other != null && + this::class == other::class && + this.root == (other as TagWrapper).root + ) + + override fun hashCode(): Int = root.hashCode() +} +sealed class NamedTagWrapper(root: DocTag, val name: String) : TagWrapper(root) { + override fun equals(other: Any?): Boolean = super.equals(other) && this.name == (other as NamedTagWrapper).name + override fun hashCode(): Int = super.hashCode() + name.hashCode() +} + +class Description(root: DocTag) : TagWrapper(root) +class Author(root: DocTag) : TagWrapper(root) +class Version(root: DocTag) : TagWrapper(root) +class Since(root: DocTag) : TagWrapper(root) +class See(root: DocTag, name: String, val address: DRI?) : NamedTagWrapper(root, name) +class Param(root: DocTag, name: String) : NamedTagWrapper(root, name) +class Return(root: DocTag) : TagWrapper(root) +class Receiver(root: DocTag) : TagWrapper(root) +class Constructor(root: DocTag) : TagWrapper(root) +class Throws(root: DocTag, name: String) : NamedTagWrapper(root, name) +class Sample(root: DocTag, name: String) : NamedTagWrapper(root, name) +class Deprecated(root: DocTag) : TagWrapper(root) +class Property(root: DocTag, name: String) : NamedTagWrapper(root, name) +class Suppress(root: DocTag) : TagWrapper(root) +class CustomTagWrapper(root: DocTag, name: String) : NamedTagWrapper(root, name) diff --git a/core/src/main/kotlin/model/documentableProperties.kt b/core/src/main/kotlin/model/documentableProperties.kt new file mode 100644 index 00000000..cd6a9335 --- /dev/null +++ b/core/src/main/kotlin/model/documentableProperties.kt @@ -0,0 +1,27 @@ +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.properties.ExtraProperty +import org.jetbrains.dokka.model.properties.MergeStrategy + +data class InheritedFunction(val inheritedFrom: SourceSetDependent<DRI?>) : ExtraProperty<DFunction> { + companion object : ExtraProperty.Key<DFunction, InheritedFunction> { + override fun mergeStrategyFor(left: InheritedFunction, right: InheritedFunction) = MergeStrategy.Replace( + InheritedFunction(left.inheritedFrom + right.inheritedFrom) + ) + } + + fun isInherited(sourceSetDependent: DokkaSourceSet): Boolean = inheritedFrom[sourceSetDependent] != null + + override val key: ExtraProperty.Key<DFunction, *> = InheritedFunction +} + +data class ImplementedInterfaces(val interfaces: SourceSetDependent<List<DRI>>) : ExtraProperty<Documentable> { + companion object : ExtraProperty.Key<Documentable, ImplementedInterfaces> { + override fun mergeStrategyFor(left: ImplementedInterfaces, right: ImplementedInterfaces) = + MergeStrategy.Replace(ImplementedInterfaces(left.interfaces + right.interfaces)) + } + + override val key: ExtraProperty.Key<Documentable, *> = ImplementedInterfaces +}
\ No newline at end of file diff --git a/core/src/main/kotlin/model/documentableUtils.kt b/core/src/main/kotlin/model/documentableUtils.kt new file mode 100644 index 00000000..287cf313 --- /dev/null +++ b/core/src/main/kotlin/model/documentableUtils.kt @@ -0,0 +1,22 @@ +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet + +fun <T> SourceSetDependent<T>.filtered(sourceSets: Set<DokkaSourceSet>) = filter { it.key in sourceSets } +fun DokkaSourceSet?.filtered(sourceSets: Set<DokkaSourceSet>) = takeIf { this in sourceSets } + +fun DTypeParameter.filter(filteredSet: Set<DokkaSourceSet>) = + if (filteredSet.containsAll(sourceSets)) this + else { + val intersection = filteredSet.intersect(sourceSets) + if (intersection.isEmpty()) null + else DTypeParameter( + dri, + name, + documentation.filtered(intersection), + expectPresentInSet?.takeIf { intersection.contains(expectPresentInSet) }, + bounds, + intersection, + extra + ) + } diff --git a/core/src/main/kotlin/model/extraModifiers.kt b/core/src/main/kotlin/model/extraModifiers.kt new file mode 100644 index 00000000..efaa3d60 --- /dev/null +++ b/core/src/main/kotlin/model/extraModifiers.kt @@ -0,0 +1,62 @@ +package org.jetbrains.dokka.model + +sealed class ExtraModifiers(val name: String) { + + sealed class KotlinOnlyModifiers(name: String) : ExtraModifiers(name) { + object Inline : KotlinOnlyModifiers("inline") + object Infix : KotlinOnlyModifiers("infix") + object External : KotlinOnlyModifiers("external") + object Suspend : KotlinOnlyModifiers("suspend") + object Reified : KotlinOnlyModifiers("reified") + object CrossInline : KotlinOnlyModifiers("crossinline") + object NoInline : KotlinOnlyModifiers("noinline") + object Override : KotlinOnlyModifiers("override") + object Data : KotlinOnlyModifiers("data") + object Const : KotlinOnlyModifiers("const") + object Inner : KotlinOnlyModifiers("inner") + object LateInit : KotlinOnlyModifiers("lateinit") + object Operator : KotlinOnlyModifiers("operator") + object TailRec : KotlinOnlyModifiers("tailrec") + object VarArg : KotlinOnlyModifiers("vararg") + object Fun : KotlinOnlyModifiers("fun") + } + + sealed class JavaOnlyModifiers(name: String) : ExtraModifiers(name) { + object Static : JavaOnlyModifiers("static") + object Native : JavaOnlyModifiers("native") + object Synchronized : JavaOnlyModifiers("synchronized") + object StrictFP : JavaOnlyModifiers("strictfp") + object Transient : JavaOnlyModifiers("transient") + object Volatile : JavaOnlyModifiers("volatile") + object Transitive : JavaOnlyModifiers("transitive") + } + + companion object { + fun valueOf(str: String) = when (str) { + "inline" -> KotlinOnlyModifiers.Inline + "infix" -> KotlinOnlyModifiers.Infix + "external" -> KotlinOnlyModifiers.External + "suspend" -> KotlinOnlyModifiers.Suspend + "reified" -> KotlinOnlyModifiers.Reified + "crossinline" -> KotlinOnlyModifiers.CrossInline + "noinline" -> KotlinOnlyModifiers.NoInline + "override" -> KotlinOnlyModifiers.Override + "data" -> KotlinOnlyModifiers.Data + "const" -> KotlinOnlyModifiers.Const + "inner" -> KotlinOnlyModifiers.Inner + "lateinit" -> KotlinOnlyModifiers.LateInit + "operator" -> KotlinOnlyModifiers.Operator + "tailrec" -> KotlinOnlyModifiers.TailRec + "vararg" -> KotlinOnlyModifiers.VarArg + "static" -> JavaOnlyModifiers.Static + "native" -> JavaOnlyModifiers.Native + "synchronized" -> JavaOnlyModifiers.Synchronized + "strictfp" -> JavaOnlyModifiers.StrictFP + "transient" -> JavaOnlyModifiers.Transient + "volatile" -> JavaOnlyModifiers.Volatile + "transitive" -> JavaOnlyModifiers.Transitive + "fun" -> KotlinOnlyModifiers.Fun + else -> throw IllegalArgumentException("There is no Extra Modifier for given name $str") + } + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/model/properties/PropertyContainer.kt b/core/src/main/kotlin/model/properties/PropertyContainer.kt new file mode 100644 index 00000000..6009bfe0 --- /dev/null +++ b/core/src/main/kotlin/model/properties/PropertyContainer.kt @@ -0,0 +1,60 @@ +package org.jetbrains.dokka.model.properties + +class PropertyContainer<C : Any> internal constructor( + @PublishedApi internal val map: Map<ExtraProperty.Key<C, *>, ExtraProperty<C>> +) { + operator fun <D : C> plus(prop: ExtraProperty<D>): PropertyContainer<D> = + PropertyContainer(map + (prop.key to prop)) + + // TODO: Add logic for caching calculated properties + inline operator fun <reified T : Any> get(key: ExtraProperty.Key<C, T>): T? = when (val prop = map[key]) { + is T? -> prop + else -> throw ClassCastException("Property for $key stored under not matching key type.") + } + + inline fun <reified T : Any> allOfType(): List<T> = map.values.filterIsInstance<T>() + fun <D : C> addAll(extras: Collection<ExtraProperty<D>>): PropertyContainer<D> = + PropertyContainer(map + extras.map { p -> p.key to p }) + + companion object { + fun <T : Any> empty(): PropertyContainer<T> = PropertyContainer(emptyMap()) + fun <T : Any> withAll(vararg extras: ExtraProperty<T>) = empty<T>().addAll(extras.toList()) + fun <T : Any> withAll(extras: Collection<ExtraProperty<T>>) = empty<T>().addAll(extras) + } +} + +operator fun <D: Any> PropertyContainer<D>.plus(prop: ExtraProperty<D>?): PropertyContainer<D> = + if (prop == null) this else PropertyContainer(map + (prop.key to prop)) + +interface WithExtraProperties<C : Any> { + val extra: PropertyContainer<C> + + fun withNewExtras(newExtras: PropertyContainer<C>): C +} + +fun <C> C.mergeExtras(left: C, right: C): C where C : Any, C : WithExtraProperties<C> { + val aggregatedExtras: List<List<ExtraProperty<C>>> = + (left.extra.map.values + right.extra.map.values) + .groupBy { it.key } + .values + .map { it.distinct() } + + val (unambiguous, toMerge) = aggregatedExtras.partition { it.size == 1 } + + @Suppress("UNCHECKED_CAST") + val strategies: List<MergeStrategy<C>> = toMerge.map { (l, r) -> + (l.key as ExtraProperty.Key<C, ExtraProperty<C>>).mergeStrategyFor(l, r) + } + + strategies.filterIsInstance<MergeStrategy.Fail>().firstOrNull()?.error?.invoke() + + val replaces: List<ExtraProperty<C>> = + strategies.filterIsInstance<MergeStrategy.Replace<C>>().map { it.newProperty } + + val needingFullMerge: List<(preMerged: C, left: C, right: C) -> C> = + strategies.filterIsInstance<MergeStrategy.Full<C>>().map { it.merger } + + val newExtras = PropertyContainer((unambiguous.flatten() + replaces).associateBy { it.key }) + + return needingFullMerge.fold(withNewExtras(newExtras)) { acc, merger -> merger(acc, left, right) } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/model/properties/properties.kt b/core/src/main/kotlin/model/properties/properties.kt new file mode 100644 index 00000000..7010d0df --- /dev/null +++ b/core/src/main/kotlin/model/properties/properties.kt @@ -0,0 +1,22 @@ +package org.jetbrains.dokka.model.properties + +interface ExtraProperty<in C : Any> { + interface Key<in C : Any, T : Any> { + fun mergeStrategyFor(left: T, right: T): MergeStrategy<C> = MergeStrategy.Fail { + throw NotImplementedError("Property merging for $this is not implemented") + } + } + + val key: Key<C, *> +} + +interface CalculatedProperty<in C : Any, T : Any> : ExtraProperty.Key<C, T> { + fun calculate(subject: C): T +} + +sealed class MergeStrategy<in C> { + class Replace<in C : Any>(val newProperty: ExtraProperty<C>) : MergeStrategy<C>() + object Remove : MergeStrategy<Any>() + class Full<C : Any>(val merger: (preMerged: C, left: C, right: C) -> C) : MergeStrategy<C>() + class Fail(val error: () -> Nothing) : MergeStrategy<Any>() +} diff --git a/core/src/main/kotlin/pages/ContentNodes.kt b/core/src/main/kotlin/pages/ContentNodes.kt new file mode 100644 index 00000000..5129dfcf --- /dev/null +++ b/core/src/main/kotlin/pages/ContentNodes.kt @@ -0,0 +1,266 @@ +package org.jetbrains.dokka.pages + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.WithChildren +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.model.properties.WithExtraProperties + +data class DCI(val dri: Set<DRI>, val kind: Kind) { + override fun toString() = "$dri[$kind]" +} + +interface ContentNode : WithExtraProperties<ContentNode>, WithChildren<ContentNode> { + val dci: DCI + val sourceSets: Set<DokkaSourceSet> + val style: Set<Style> + + fun hasAnyContent(): Boolean + + override val children: List<ContentNode> + get() = emptyList() +} + +/** Simple text */ +data class ContentText( + val text: String, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style> = emptySet(), + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentNode { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentNode = copy(extra = newExtras) + + override fun hasAnyContent(): Boolean = !text.isBlank() +} + +// TODO: Remove +data class ContentBreakLine( + override val sourceSets: Set<DokkaSourceSet>, + override val dci: DCI = DCI(emptySet(), ContentKind.Empty), + override val style: Set<Style> = emptySet(), + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentNode { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentNode = copy(extra = newExtras) + + override fun hasAnyContent(): Boolean = true +} + +/** Headers */ +data class ContentHeader( + override val children: List<ContentNode>, + val level: Int, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentComposite { + constructor(level: Int, c: ContentComposite) : this(c.children, level, c.dci, c.sourceSets, c.style, c.extra) + + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentHeader = copy(extra = newExtras) +} + +interface ContentCode : ContentComposite + +/** Code blocks */ +data class ContentCodeBlock( + override val children: List<ContentNode>, + val language: String, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentCode { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentCodeBlock = copy(extra = newExtras) +} + +data class ContentCodeInline( + override val children: List<ContentNode>, + val language: String, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentCode { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentCodeInline = copy(extra = newExtras) +} + +/** Union type replacement */ +interface ContentLink : ContentComposite + +/** All links to classes, packages, etc. that have te be resolved */ +data class ContentDRILink( + override val children: List<ContentNode>, + val address: DRI, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style> = emptySet(), + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentLink { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentDRILink = copy(extra = newExtras) +} + +/** All links that do not need to be resolved */ +data class ContentResolvedLink( + override val children: List<ContentNode>, + val address: String, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style> = emptySet(), + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentLink { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentResolvedLink = + copy(extra = newExtras) +} + +/** Embedded resources like images */ +data class ContentEmbeddedResource( + override val children: List<ContentNode> = emptyList(), + val address: String, + val altText: String?, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style> = emptySet(), + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentLink { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentEmbeddedResource = + copy(extra = newExtras) +} + +/** Logical grouping of [ContentNode]s */ +interface ContentComposite : ContentNode { + override val children: List<ContentNode> // overwrite to make it abstract once again + + override fun hasAnyContent(): Boolean = children.any { it.hasAnyContent() } +} + +/** Tables */ +data class ContentTable( + val header: List<ContentGroup>, + override val children: List<ContentGroup>, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentComposite { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentTable = copy(extra = newExtras) +} + +/** Lists */ +data class ContentList( + override val children: List<ContentNode>, + val ordered: Boolean, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentComposite { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentList = copy(extra = newExtras) +} + +/** Default group, eg. for blocks of Functions, Properties, etc. **/ +data class ContentGroup( + override val children: List<ContentNode>, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentComposite { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentGroup = copy(extra = newExtras) +} + +/** + * @property groupID is used for finding and copying [ContentDivergentInstance]s when merging [ContentPage]s + */ +data class ContentDivergentGroup( + override val children: List<ContentDivergentInstance>, + override val dci: DCI, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode>, + val groupID: GroupID, + val implicitlySourceSetHinted: Boolean = true +) : ContentComposite { + data class GroupID(val name: String) + + override val sourceSets: Set<DokkaSourceSet> + get() = children.flatMap { it.sourceSets }.distinct().toSet() + + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentDivergentGroup = + copy(extra = newExtras) +} + +/** Instance of a divergent content */ +data class ContentDivergentInstance( + val before: ContentNode?, + val divergent: ContentNode, + val after: ContentNode?, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentComposite { + override val children: List<ContentNode> + get() = listOfNotNull(before, divergent, after) + + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentDivergentInstance = + copy(extra = newExtras) +} + +data class PlatformHintedContent( + val inner: ContentNode, + override val sourceSets: Set<DokkaSourceSet> +) : ContentComposite { + override val children = listOf(inner) + + override val dci: DCI + get() = inner.dci + + override val extra: PropertyContainer<ContentNode> + get() = inner.extra + + override val style: Set<Style> + get() = inner.style + + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>) = + throw UnsupportedOperationException("This method should not be called on this PlatformHintedContent") +} + +interface Style +interface Kind + +enum class ContentKind : Kind { + + Comment, Constructors, Functions, Parameters, Properties, Classlikes, Packages, Symbol, Sample, Main, BriefComment, + Empty, Source, TypeAliases, Cover, Inheritors, SourceSetDependentHint, Extensions, Annotations; + + companion object { + private val platformTagged = + setOf(Constructors, Functions, Properties, Classlikes, Packages, Source, TypeAliases, Inheritors, Extensions) + + fun shouldBePlatformTagged(kind: Kind): Boolean = kind in platformTagged + } +} + +enum class TextStyle : Style { + Bold, Italic, Strong, Strikethrough, Paragraph, Block, Span, Monospace, Indented, Cover, UnderCoverText, BreakableAfter, Breakable +} + +enum class ContentStyle : Style { + RowTitle, TabbedContent, WithExtraAttributes, RunnableSample, InDocumentationAnchor +} + +object CommentTable : Style + +object MultimoduleTable : Style + +fun ContentNode.dfs(predicate: (ContentNode) -> Boolean): ContentNode? = if (predicate(this)) { + this +} else { + if (this is ContentComposite) { + this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() + } else { + null + } +} + +fun ContentNode.hasStyle(style: Style) = this.style.contains(style) diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt new file mode 100644 index 00000000..71ec8597 --- /dev/null +++ b/core/src/main/kotlin/pages/PageNodes.kt @@ -0,0 +1,181 @@ +package org.jetbrains.dokka.pages + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.WithChildren +import java.util.* + +interface PageNode: WithChildren<PageNode> { + val name: String + + fun modified( + name: String = this.name, + children: List<PageNode> = this.children + ): PageNode +} + +interface ContentPage: PageNode { + val content: ContentNode + val dri: Set<DRI> + val documentable: Documentable? + val embeddedResources: List<String> + + fun modified( + name: String = this.name, + content: ContentNode = this.content, + dri: Set<DRI> = this.dri, + embeddedResources: List<String> = this.embeddedResources, + children: List<PageNode> = this.children + ): ContentPage +} + +abstract class RootPageNode: PageNode { + val parentMap: Map<PageNode, PageNode> by lazy { + IdentityHashMap<PageNode, PageNode>().apply { + fun process(parent: PageNode) { + parent.children.forEach { child -> + put(child, parent) + process(child) + } + } + process(this@RootPageNode) + } + } + + fun transformPageNodeTree(operation: (PageNode) -> PageNode) = + this.transformNode(operation) as RootPageNode + + fun transformContentPagesTree(operation: (ContentPage) -> ContentPage) = transformPageNodeTree { + if (it is ContentPage) operation(it) else it + } + + private fun PageNode.transformNode(operation: (PageNode) -> PageNode): PageNode = + operation(this).let { newNode -> + newNode.modified(children = newNode.children.map { it.transformNode(operation) }) + } + + abstract override fun modified( + name: String, + children: List<PageNode> + ): RootPageNode +} + +class ModulePageNode( + override val name: String, + override val content: ContentNode, + override val documentable: Documentable?, + override val children: List<PageNode>, + override val embeddedResources: List<String> = listOf() +) : RootPageNode(), ContentPage { + override val dri: Set<DRI> = setOf(DRI.topLevel) + + override fun modified(name: String, children: List<PageNode>): ModulePageNode = + modified(name = name, content = this.content, dri = dri, children = children) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ): ModulePageNode = + if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this + else ModulePageNode(name, content, documentable, children, embeddedResources) +} + +class PackagePageNode( + override val name: String, + override val content: ContentNode, + override val dri: Set<DRI>, + override val documentable: Documentable?, + override val children: List<PageNode>, + override val embeddedResources: List<String> = listOf() +) : ContentPage { + override fun modified(name: String, children: List<PageNode>): PackagePageNode = + modified(name = name, content = this.content, children = children) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ): PackagePageNode = + if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this + else PackagePageNode(name, content, dri, documentable, children, embeddedResources) +} + +class ClasslikePageNode( + override val name: String, + override val content: ContentNode, + override val dri: Set<DRI>, + override val documentable: Documentable?, + override val children: List<PageNode>, + override val embeddedResources: List<String> = listOf() +) : ContentPage { + override fun modified(name: String, children: List<PageNode>): ClasslikePageNode = + modified(name = name, content = this.content, children = children) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ): ClasslikePageNode = + if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this + else ClasslikePageNode(name, content, dri, documentable, children, embeddedResources) +} + +class MemberPageNode( + override val name: String, + override val content: ContentNode, + override val dri: Set<DRI>, + override val documentable: Documentable?, + override val children: List<PageNode> = emptyList(), + override val embeddedResources: List<String> = listOf() +) : ContentPage { + override fun modified(name: String, children: List<PageNode>): MemberPageNode = + modified(name = name, content = this.content, children = children) as MemberPageNode + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ): MemberPageNode = + if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this + else MemberPageNode(name, content, dri, documentable, children, embeddedResources) +} + + +class MultimoduleRootPageNode( + override val name: String, + override val dri: Set<DRI>, + override val content: ContentNode, + override val embeddedResources: List<String> = emptyList() +) : RootPageNode(), ContentPage { + + override val children: List<PageNode> = emptyList() + + override val documentable: Documentable? = null + + override fun modified(name: String, children: List<PageNode>): RootPageNode = + MultimoduleRootPageNode(name, dri, content, embeddedResources) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ) = + if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this + else MultimoduleRootPageNode(name, dri, content, embeddedResources) +} + +inline fun <reified T: PageNode> PageNode.children() = children.filterIsInstance<T>() + +private infix fun <T> List<T>.shallowEq(other: List<T>) = + this === other || (this.size == other.size && (this zip other).all { (a, b) -> a === b }) diff --git a/core/src/main/kotlin/pages/RendererSpecificPage.kt b/core/src/main/kotlin/pages/RendererSpecificPage.kt new file mode 100644 index 00000000..85e6d530 --- /dev/null +++ b/core/src/main/kotlin/pages/RendererSpecificPage.kt @@ -0,0 +1,40 @@ +package org.jetbrains.dokka.pages + +import org.jetbrains.dokka.renderers.Renderer +import kotlin.reflect.KClass + +interface RendererSpecificPage : PageNode { + val strategy: RenderingStrategy +} + +class RendererSpecificRootPage( + override val name: String, + override val children: List<PageNode>, + override val strategy: RenderingStrategy +) : RootPageNode(), RendererSpecificPage { + override fun modified(name: String, children: List<PageNode>): RendererSpecificRootPage = + RendererSpecificRootPage(name, children, strategy) +} + +class RendererSpecificResourcePage( + override val name: String, + override val children: List<PageNode>, + override val strategy: RenderingStrategy +): RendererSpecificPage { + override fun modified(name: String, children: List<PageNode>): RendererSpecificResourcePage = + RendererSpecificResourcePage(name, children, strategy) +} + +sealed class RenderingStrategy { + class Callback(val instructions: Renderer.(PageNode) -> String): RenderingStrategy() + data class Copy(val from: String) : RenderingStrategy() + data class Write(val text: String) : RenderingStrategy() + object DoNothing : RenderingStrategy() + + companion object { + inline operator fun <reified T: Renderer> invoke(crossinline instructions: T.(PageNode) -> String) = + Callback { if (this is T) instructions(it) else throw WrongRendererTypeException(T::class) } + } +} + +data class WrongRendererTypeException(val expectedType: KClass<*>): Exception()
\ No newline at end of file diff --git a/core/src/main/kotlin/pages/contentNodeProperties.kt b/core/src/main/kotlin/pages/contentNodeProperties.kt new file mode 100644 index 00000000..67acef6d --- /dev/null +++ b/core/src/main/kotlin/pages/contentNodeProperties.kt @@ -0,0 +1,12 @@ +package org.jetbrains.dokka.pages + +import org.jetbrains.dokka.model.properties.ExtraProperty + +class SimpleAttr(val extraKey: String, val extraValue: String) : ExtraProperty<ContentNode> { + data class SimpleAttrKey(val key: String) : ExtraProperty.Key<ContentNode, SimpleAttr> + override val key: ExtraProperty.Key<ContentNode, SimpleAttr> = SimpleAttrKey(extraKey) + + companion object { + fun header(value: String) = SimpleAttr("data-togglable", value) + } +} diff --git a/core/src/main/kotlin/pages/utils.kt b/core/src/main/kotlin/pages/utils.kt new file mode 100644 index 00000000..c9039416 --- /dev/null +++ b/core/src/main/kotlin/pages/utils.kt @@ -0,0 +1,31 @@ +package org.jetbrains.dokka.pages + +import kotlin.reflect.KClass + +inline fun <reified T : ContentNode, R : ContentNode> R.mapTransform(noinline operation: (T) -> T): R = + mapTransform(T::class, operation) + +@PublishedApi +@Suppress("UNCHECKED_CAST") +internal fun <T : ContentNode, R : ContentNode> R.mapTransform(type: KClass<T>, operation: (T) -> T): R { + if (this::class == type) { + return operation(this as T) as R + } + val new = when (this) { + is ContentGroup -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentHeader -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentCodeBlock -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentCodeInline -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentTable -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentList -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentDivergentGroup -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentDivergentInstance -> this.copy( + before = before?.mapTransform(type, operation), + divergent = divergent.mapTransform(type, operation), + after = after?.mapTransform(type, operation) + ) + is PlatformHintedContent -> this.copy(inner.mapTransform(type, operation)) + else -> this + } + return new as R +} diff --git a/core/src/main/kotlin/plugability/DefaultExtensions.kt b/core/src/main/kotlin/plugability/DefaultExtensions.kt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/core/src/main/kotlin/plugability/DefaultExtensions.kt diff --git a/core/src/main/kotlin/plugability/DokkaContext.kt b/core/src/main/kotlin/plugability/DokkaContext.kt new file mode 100644 index 00000000..323039e9 --- /dev/null +++ b/core/src/main/kotlin/plugability/DokkaContext.kt @@ -0,0 +1,223 @@ +package org.jetbrains.dokka.plugability + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.utilities.DokkaLogger +import java.io.File +import java.net.URLClassLoader +import java.util.* +import kotlin.reflect.KClass +import kotlin.reflect.full.createInstance + + +interface DokkaContext { + fun <T : DokkaPlugin> plugin(kclass: KClass<T>): T? + + operator fun <T, E> get(point: E): List<T> + where T : Any, E : ExtensionPoint<T> + + fun <T, E> single(point: E): T where T : Any, E : ExtensionPoint<T> + + val logger: DokkaLogger + val configuration: DokkaConfiguration + val unusedPoints: Collection<ExtensionPoint<*>> + + + companion object { + fun create( + configuration: DokkaConfiguration, + logger: DokkaLogger, + pluginOverrides: List<DokkaPlugin> + ): DokkaContext = + DokkaContextConfigurationImpl(logger, configuration).apply { + // File(it.path) is a workaround for an incorrect filesystem in a File instance returned by Gradle. + configuration.pluginsClasspath.map { File(it.path).toURI().toURL() } + .toTypedArray() + .let { URLClassLoader(it, this.javaClass.classLoader) } + .also { checkClasspath(it) } + .let { ServiceLoader.load(DokkaPlugin::class.java, it) } + .let { it + pluginOverrides } + .forEach { install(it) } + topologicallySortAndPrune() + }.also { it.logInitialisationInfo() } + } +} + +inline fun <reified T : DokkaPlugin> DokkaContext.plugin(): T = plugin(T::class) + ?: throw java.lang.IllegalStateException("Plugin ${T::class.qualifiedName} is not present in context.") + +interface DokkaContextConfiguration { + fun installExtension(extension: Extension<*, *, *>) +} + +private class DokkaContextConfigurationImpl( + override val logger: DokkaLogger, + override val configuration: DokkaConfiguration +) : DokkaContext, DokkaContextConfiguration { + private val plugins = mutableMapOf<KClass<*>, DokkaPlugin>() + private val pluginStubs = mutableMapOf<KClass<*>, DokkaPlugin>() + val extensions = mutableMapOf<ExtensionPoint<*>, MutableList<Extension<*, *, *>>>() + val pointsUsed: MutableSet<ExtensionPoint<*>> = mutableSetOf() + val pointsPopulated: MutableSet<ExtensionPoint<*>> = mutableSetOf() + override val unusedPoints: Set<ExtensionPoint<*>> + get() = pointsPopulated - pointsUsed + + private enum class State { + UNVISITED, + VISITING, + VISITED; + } + + private sealed class Suppression { + data class ByExtension(val extension: Extension<*, *, *>) : Suppression() { + override fun toString() = extension.toString() + } + + data class ByPlugin(val plugin: DokkaPlugin) : Suppression() { + override fun toString() = "Plugin ${plugin::class.qualifiedName}" + } + } + + private val rawExtensions = mutableListOf<Extension<*, *, *>>() + private val rawAdjacencyList = mutableMapOf<Extension<*, *, *>, MutableList<Extension<*, *, *>>>() + private val suppressedExtensions = mutableMapOf<Extension<*, *, *>, MutableList<Suppression>>() + + fun topologicallySortAndPrune() { + pointsPopulated.clear() + extensions.clear() + + val overridesInfo = processOverrides() + val extensionsToSort = overridesInfo.keys + val adjacencyList = translateAdjacencyList(overridesInfo) + + val verticesWithState = extensionsToSort.associateWithTo(mutableMapOf()) { State.UNVISITED } + val result: MutableList<Extension<*, *, *>> = mutableListOf() + + fun visit(n: Extension<*, *, *>) { + val state = verticesWithState[n] + if (state == State.VISITED) + return + if (state == State.VISITING) + throw Error("Detected cycle in plugins graph") + verticesWithState[n] = State.VISITING + adjacencyList[n]?.forEach { visit(it) } + verticesWithState[n] = State.VISITED + result += n + } + + extensionsToSort.forEach(::visit) + + val filteredResult = result.asReversed().filterNot { it in suppressedExtensions } + + filteredResult.mapTo(pointsPopulated) { it.extensionPoint } + filteredResult.groupByTo(extensions) { it.extensionPoint } + } + + private fun processOverrides(): Map<Extension<*, *, *>, Set<Extension<*, *, *>>> { + val buckets = rawExtensions.associateWithTo(mutableMapOf()) { setOf(it) } + suppressedExtensions.forEach { (extension, suppressions) -> + val mergedBucket = suppressions.filterIsInstance<Suppression.ByExtension>() + .map { it.extension } + .plus(extension) + .flatMap { buckets[it].orEmpty() } + .toSet() + mergedBucket.forEach { buckets[it] = mergedBucket } + } + return buckets.values.distinct().associateBy(::findNotOverridden) + } + + private fun findNotOverridden(bucket: Set<Extension<*, *, *>>): Extension<*, *, *> { + val filtered = bucket.filter { it !in suppressedExtensions } + return filtered.singleOrNull() ?: throw IllegalStateException("Conflicting overrides: $filtered") + } + + private fun translateAdjacencyList( + overridesInfo: Map<Extension<*, *, *>, Set<Extension<*, *, *>>> + ): Map<Extension<*, *, *>, List<Extension<*, *, *>>> { + val reverseOverrideInfo = overridesInfo.flatMap { (ext, set) -> set.map { it to ext } }.toMap() + return rawAdjacencyList.mapNotNull { (ext, list) -> + reverseOverrideInfo[ext]?.to(list.mapNotNull { reverseOverrideInfo[it] }) + }.toMap() + } + + @Suppress("UNCHECKED_CAST") + override operator fun <T, E> get(point: E) where T : Any, E : ExtensionPoint<T> = + actions(point).also { pointsUsed += point }.orEmpty() as List<T> + + @Suppress("UNCHECKED_CAST") + override fun <T, E> single(point: E): T where T : Any, E : ExtensionPoint<T> { + fun throwBadArity(substitution: String): Nothing = throw IllegalStateException( + "$point was expected to have exactly one extension registered, but $substitution found." + ) + pointsUsed += point + + val extensions = extensions[point].orEmpty() as List<Extension<T, *, *>> + return when (extensions.size) { + 0 -> throwBadArity("none was") + 1 -> extensions.single().action.get(this) + else -> throwBadArity("many were") + } + } + + private fun <E : ExtensionPoint<*>> actions(point: E) = extensions[point]?.map { it.action.get(this) } + + @Suppress("UNCHECKED_CAST") + override fun <T : DokkaPlugin> plugin(kclass: KClass<T>) = (plugins[kclass] ?: pluginStubFor(kclass)) as T + + private fun <T : DokkaPlugin> pluginStubFor(kclass: KClass<T>): DokkaPlugin = + pluginStubs.getOrPut(kclass) { kclass.createInstance().also { it.context = this } } + + fun install(plugin: DokkaPlugin) { + plugins[plugin::class] = plugin + plugin.context = this + plugin.internalInstall(this, this.configuration) + + if (plugin is WithUnsafeExtensionSuppression) { + plugin.extensionsSuppressed.forEach { + suppressedExtensions.listFor(it) += Suppression.ByPlugin(plugin) + } + } + } + + override fun installExtension(extension: Extension<*, *, *>) { + rawExtensions += extension + + if (extension.ordering is OrderingKind.ByDsl) { + val orderDsl = OrderDsl() + orderDsl.(extension.ordering.block)() + + rawAdjacencyList.listFor(extension) += orderDsl.following.toList() + orderDsl.previous.forEach { rawAdjacencyList.listFor(it) += extension } + } + + if (extension.override is OverrideKind.Present) { + suppressedExtensions.listFor(extension.override.overriden) += Suppression.ByExtension(extension) + } + } + + fun logInitialisationInfo() { + val pluginNames = plugins.values.map { it::class.qualifiedName.toString() } + + val loadedListForDebug = extensions.run { keys + values.flatten() }.toList() + .joinToString(prefix = "[\n", separator = ",\n", postfix = "\n]") { "\t$it" } + + val suppressedList = suppressedExtensions.asSequence() + .joinToString(prefix = "[\n", separator = ",\n", postfix = "\n]") { + "\t${it.key} by " + (it.value.singleOrNull() ?: it.value) + } + + logger.info("Loaded plugins: $pluginNames") + logger.info("Loaded: $loadedListForDebug") + logger.info("Suppressed: $suppressedList") + } +} + +private fun checkClasspath(classLoader: URLClassLoader) { + classLoader.findResource(DokkaContext::class.java.name.replace('.', '/') + ".class")?.also { + throw AssertionError( + "Dokka API found on plugins classpath. This will lead to subtle bugs. " + + "Please fix your plugins dependencies or exclude dokka api artifact from plugin classpath" + ) + } +} + +private fun <K, V> MutableMap<K, MutableList<V>>.listFor(key: K) = getOrPut(key, ::mutableListOf) diff --git a/core/src/main/kotlin/plugability/DokkaPlugin.kt b/core/src/main/kotlin/plugability/DokkaPlugin.kt new file mode 100644 index 00000000..2c755a49 --- /dev/null +++ b/core/src/main/kotlin/plugability/DokkaPlugin.kt @@ -0,0 +1,82 @@ +package org.jetbrains.dokka.plugability + +import com.google.gson.Gson +import org.jetbrains.dokka.DokkaConfiguration +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.createInstance + +abstract class DokkaPlugin { + private val extensionDelegates = mutableListOf<KProperty<*>>() + + @PublishedApi + internal var context: DokkaContext? = null + + protected inline fun <reified T : DokkaPlugin> plugin(): T = context?.plugin(T::class) ?: throwIllegalQuery() + + protected fun <T : Any> extensionPoint() = + object : ReadOnlyProperty<DokkaPlugin, ExtensionPoint<T>> { + override fun getValue(thisRef: DokkaPlugin, property: KProperty<*>) = ExtensionPoint<T>( + thisRef::class.qualifiedName ?: throw AssertionError("Plugin must be named class"), + property.name + ) + } + + protected fun <T : Any> extending(definition: ExtendingDSL.() -> Extension<T, *, *>) = ExtensionProvider(definition) + + protected class ExtensionProvider<T : Any> internal constructor( + private val definition: ExtendingDSL.() -> Extension<T, *, *> + ) { + operator fun provideDelegate(thisRef: DokkaPlugin, property: KProperty<*>) = lazy { + ExtendingDSL( + thisRef::class.qualifiedName ?: throw AssertionError("Plugin must be named class"), + property.name + ).definition() + }.also { thisRef.extensionDelegates += property } + } + + internal fun internalInstall(ctx: DokkaContextConfiguration, configuration: DokkaConfiguration) { + extensionDelegates.asSequence() + .filterIsInstance<KProperty1<DokkaPlugin, Extension<*, *, *>>>() // should be always true + .map { it.get(this) } + .forEach { if (configuration.(it.condition)()) ctx.installExtension(it) } + } +} + +interface WithUnsafeExtensionSuppression { + val extensionsSuppressed: List<Extension<*, *, *>> +} + +interface Configurable { + val pluginsConfiguration: Map<String, String> +} + +interface ConfigurableBlock + +inline fun <reified P : DokkaPlugin, reified T : ConfigurableBlock> Configurable.pluginConfiguration(block: T.() -> Unit) { + val instance = T::class.createInstance().apply(block) + + val mutablePluginsConfiguration = pluginsConfiguration as MutableMap<String, String> + mutablePluginsConfiguration[P::class.qualifiedName!!] = Gson().toJson(instance, T::class.java) +} + +inline fun <reified P : DokkaPlugin, reified E : Any> P.query(extension: P.() -> ExtensionPoint<E>): List<E> = + context?.let { it[extension()] } ?: throwIllegalQuery() + +inline fun <reified P : DokkaPlugin, reified E : Any> P.querySingle(extension: P.() -> ExtensionPoint<E>): E = + context?.single(extension()) ?: throwIllegalQuery() + +fun throwIllegalQuery(): Nothing = + throw IllegalStateException("Querying about plugins is only possible with dokka context initialised") + +inline fun <reified T : DokkaPlugin, reified R : ConfigurableBlock> configuration(context: DokkaContext): ReadOnlyProperty<Any?, R> { + return object : ReadOnlyProperty<Any?, R> { + override fun getValue(thisRef: Any?, property: KProperty<*>): R { + return context.configuration.pluginsConfiguration[T::class.qualifiedName + ?: throw AssertionError("Plugin must be named class")].let { + Gson().fromJson(it, R::class.java) + } + } + } +} diff --git a/core/src/main/kotlin/plugability/LazyEvaluated.kt b/core/src/main/kotlin/plugability/LazyEvaluated.kt new file mode 100644 index 00000000..c0c271f4 --- /dev/null +++ b/core/src/main/kotlin/plugability/LazyEvaluated.kt @@ -0,0 +1,16 @@ +package org.jetbrains.dokka.plugability + +internal class LazyEvaluated<T : Any> private constructor(private val recipe: ((DokkaContext) -> T)? = null, private var value: T? = null) { + + internal fun get(context: DokkaContext): T { + if(value == null) { + value = recipe?.invoke(context) + } + return value ?: throw AssertionError("Incorrect initialized LazyEvaluated instance") + } + + companion object { + fun <T : Any> fromInstance(value: T) = LazyEvaluated(value = value) + fun <T : Any> fromRecipe(recipe: (DokkaContext) -> T) = LazyEvaluated(recipe = recipe) + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/plugability/extensions.kt b/core/src/main/kotlin/plugability/extensions.kt new file mode 100644 index 00000000..c6dd0b85 --- /dev/null +++ b/core/src/main/kotlin/plugability/extensions.kt @@ -0,0 +1,87 @@ +package org.jetbrains.dokka.plugability + +import org.jetbrains.dokka.DokkaConfiguration + +data class ExtensionPoint<T : Any> internal constructor( + internal val pluginClass: String, + internal val pointName: String +) { + override fun toString() = "ExtensionPoint: $pluginClass/$pointName" +} + +sealed class OrderingKind { + object None : OrderingKind() + class ByDsl(val block: (OrderDsl.() -> Unit)) : OrderingKind() +} + +sealed class OverrideKind { + object None : OverrideKind() + class Present(val overriden: Extension<*, *, *>) : OverrideKind() +} + +class Extension<T : Any, Ordering : OrderingKind, Override : OverrideKind> internal constructor( + internal val extensionPoint: ExtensionPoint<T>, + internal val pluginClass: String, + internal val extensionName: String, + internal val action: LazyEvaluated<T>, + internal val ordering: Ordering, + internal val override: Override, + internal val conditions: List<DokkaConfiguration.() -> Boolean> +) { + override fun toString() = "Extension: $pluginClass/$extensionName" + + override fun equals(other: Any?) = + if (other is Extension<*, *, *>) this.pluginClass == other.pluginClass && this.extensionName == other.extensionName + else false + + override fun hashCode() = listOf(pluginClass, extensionName).hashCode() + + val condition: DokkaConfiguration.() -> Boolean + get() = { conditions.all { it(this) } } +} + +private fun <T : Any> Extension( + extensionPoint: ExtensionPoint<T>, + pluginClass: String, + extensionName: String, + action: LazyEvaluated<T> +) = Extension(extensionPoint, pluginClass, extensionName, action, OrderingKind.None, OverrideKind.None, emptyList()) + +@DslMarker +annotation class ExtensionsDsl + +@ExtensionsDsl +class ExtendingDSL(private val pluginClass: String, private val extensionName: String) { + + infix fun <T : Any> ExtensionPoint<T>.with(action: T) = + Extension(this, this@ExtendingDSL.pluginClass, extensionName, LazyEvaluated.fromInstance(action)) + + infix fun <T : Any> ExtensionPoint<T>.providing(action: (DokkaContext) -> T) = + Extension(this, this@ExtendingDSL.pluginClass, extensionName, LazyEvaluated.fromRecipe(action)) + + infix fun <T : Any, Override : OverrideKind> Extension<T, OrderingKind.None, Override>.order( + block: OrderDsl.() -> Unit + ) = Extension(extensionPoint, pluginClass, extensionName, action, OrderingKind.ByDsl(block), override, conditions) + + infix fun <T : Any, Override : OverrideKind, Ordering: OrderingKind> Extension<T, Ordering, Override>.applyIf( + condition: DokkaConfiguration.() -> Boolean + ) = Extension(extensionPoint, pluginClass, extensionName, action, ordering, override, conditions + condition) + + infix fun <T : Any, Override : OverrideKind, Ordering: OrderingKind> Extension<T, Ordering, Override>.override( + overriden: Extension<T, *, *> + ) = Extension(extensionPoint, pluginClass, extensionName, action, ordering, OverrideKind.Present(overriden), conditions) +} + +@ExtensionsDsl +class OrderDsl { + internal val previous = mutableSetOf<Extension<*, *, *>>() + internal val following = mutableSetOf<Extension<*, *, *>>() + + fun after(vararg extensions: Extension<*, *, *>) { + previous += extensions + } + + fun before(vararg extensions: Extension<*, *, *>) { + following += extensions + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/renderers/Renderer.kt b/core/src/main/kotlin/renderers/Renderer.kt new file mode 100644 index 00000000..10235f21 --- /dev/null +++ b/core/src/main/kotlin/renderers/Renderer.kt @@ -0,0 +1,7 @@ +package org.jetbrains.dokka.renderers + +import org.jetbrains.dokka.pages.RootPageNode + +interface Renderer { + fun render(root: RootPageNode) +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/documentation/DocumentableMerger.kt b/core/src/main/kotlin/transformers/documentation/DocumentableMerger.kt new file mode 100644 index 00000000..c8ae9c02 --- /dev/null +++ b/core/src/main/kotlin/transformers/documentation/DocumentableMerger.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.transformers.documentation + +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.plugability.DokkaContext + +interface DocumentableMerger { + operator fun invoke(modules: Collection<DModule>, context: DokkaContext): DModule +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/documentation/DocumentableToPageTranslator.kt b/core/src/main/kotlin/transformers/documentation/DocumentableToPageTranslator.kt new file mode 100644 index 00000000..a4daba63 --- /dev/null +++ b/core/src/main/kotlin/transformers/documentation/DocumentableToPageTranslator.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka.transformers.documentation + +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.pages.ModulePageNode +import org.jetbrains.dokka.pages.RootPageNode + +interface DocumentableToPageTranslator { + operator fun invoke(module: DModule): RootPageNode +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/documentation/DocumentableTransformer.kt b/core/src/main/kotlin/transformers/documentation/DocumentableTransformer.kt new file mode 100644 index 00000000..3eb4704e --- /dev/null +++ b/core/src/main/kotlin/transformers/documentation/DocumentableTransformer.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.transformers.documentation + +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.plugability.DokkaContext + +interface DocumentableTransformer { + operator fun invoke(original: DModule, context: DokkaContext): DModule +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt b/core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt new file mode 100644 index 00000000..b67a1d57 --- /dev/null +++ b/core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.transformers.documentation + +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.plugability.DokkaContext + +interface PreMergeDocumentableTransformer { + operator fun invoke(modules: List<DModule>): List<DModule> +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/pages/PageCreator.kt b/core/src/main/kotlin/transformers/pages/PageCreator.kt new file mode 100644 index 00000000..f74b5efa --- /dev/null +++ b/core/src/main/kotlin/transformers/pages/PageCreator.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.transformers.pages + +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext + +interface PageCreator { + operator fun invoke(): RootPageNode +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/pages/PageTransformer.kt b/core/src/main/kotlin/transformers/pages/PageTransformer.kt new file mode 100644 index 00000000..218d9821 --- /dev/null +++ b/core/src/main/kotlin/transformers/pages/PageTransformer.kt @@ -0,0 +1,7 @@ +package org.jetbrains.dokka.transformers.pages + +import org.jetbrains.dokka.pages.RootPageNode + +interface PageTransformer { + operator fun invoke(input: RootPageNode): RootPageNode +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/pages/PageTransformerBuilders.kt b/core/src/main/kotlin/transformers/pages/PageTransformerBuilders.kt new file mode 100644 index 00000000..291b72ef --- /dev/null +++ b/core/src/main/kotlin/transformers/pages/PageTransformerBuilders.kt @@ -0,0 +1,22 @@ +package org.jetbrains.dokka.transformers.pages + +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.pages.RootPageNode + +fun pageScanner(block: PageNode.() -> Unit) = object : PageTransformer { + override fun invoke(input: RootPageNode): RootPageNode = input.invokeOnAll(block) as RootPageNode +} + +fun pageMapper(block: PageNode.() -> PageNode) = object : PageTransformer { + override fun invoke(input: RootPageNode): RootPageNode = input.alterChildren(block) as RootPageNode +} + +fun pageStructureTransformer(block: RootPageNode.() -> RootPageNode) = object : PageTransformer { + override fun invoke(input: RootPageNode): RootPageNode = block(input) +} + +fun PageNode.invokeOnAll(block: PageNode.() -> Unit): PageNode = + this.also(block).also { it.children.forEach { it.invokeOnAll(block) } } + +fun PageNode.alterChildren(block: PageNode.() -> PageNode): PageNode = + block(this).modified(children = this.children.map { it.alterChildren(block) })
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/sources/SourceToDocumentableTranslator.kt b/core/src/main/kotlin/transformers/sources/SourceToDocumentableTranslator.kt new file mode 100644 index 00000000..6bc8fb14 --- /dev/null +++ b/core/src/main/kotlin/transformers/sources/SourceToDocumentableTranslator.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka.transformers.sources + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.plugability.DokkaContext + +interface SourceToDocumentableTranslator { + fun invoke(sourceSet: DokkaSourceSet, context: DokkaContext): DModule +}
\ No newline at end of file diff --git a/core/src/main/kotlin/utilities/DokkaLogging.kt b/core/src/main/kotlin/utilities/DokkaLogging.kt new file mode 100644 index 00000000..6b8ed5d2 --- /dev/null +++ b/core/src/main/kotlin/utilities/DokkaLogging.kt @@ -0,0 +1,38 @@ +package org.jetbrains.dokka.utilities + +interface DokkaLogger { + var warningsCount: Int + var errorsCount: Int + fun debug(message: String) + fun info(message: String) + fun progress(message: String) + fun warn(message: String) + fun error(message: String) +} + +fun DokkaLogger.report() { + if (DokkaConsoleLogger.warningsCount > 0 || DokkaConsoleLogger.errorsCount > 0) { + info("Generation completed with ${DokkaConsoleLogger.warningsCount} warning" + + (if(DokkaConsoleLogger.warningsCount == 1) "" else "s") + + " and ${DokkaConsoleLogger.errorsCount} error" + + if(DokkaConsoleLogger.errorsCount == 1) "" else "s" + ) + } else { + info("generation completed successfully") + } +} + +object DokkaConsoleLogger : DokkaLogger { + override var warningsCount: Int = 0 + override var errorsCount: Int = 0 + + override fun debug(message: String)= println(message) + + override fun progress(message: String) = println("PROGRESS: $message") + + override fun info(message: String) = println(message) + + override fun warn(message: String) = println("WARN: $message").also { warningsCount++ } + + override fun error(message: String) = println("ERROR: $message").also { errorsCount++ } +} diff --git a/core/src/main/kotlin/Utilities/Html.kt b/core/src/main/kotlin/utilities/Html.kt index de1ce1a5..3226ca9d 100644 --- a/core/src/main/kotlin/Utilities/Html.kt +++ b/core/src/main/kotlin/utilities/Html.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka +package org.jetbrains.dokka.utilities import java.net.URLEncoder @@ -9,4 +9,7 @@ import java.net.URLEncoder */ fun String.htmlEscape(): String = replace("&", "&").replace("<", "<").replace(">", ">") -fun String.urlEncoded(): String = URLEncoder.encode(this, "UTF-8")
\ No newline at end of file +fun String.urlEncoded(): String = URLEncoder.encode(this, "UTF-8") + +fun String.formatToEndWithHtml() = + if (endsWith(".html") || contains(Regex("\\.html#"))) this else "$this.html"
\ No newline at end of file diff --git a/core/src/main/kotlin/Utilities/ServiceLocator.kt b/core/src/main/kotlin/utilities/ServiceLocator.kt index eda83422..00c9ae9f 100644 --- a/core/src/main/kotlin/Utilities/ServiceLocator.kt +++ b/core/src/main/kotlin/utilities/ServiceLocator.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.Utilities +package org.jetbrains.dokka.utilities import java.io.File import java.net.URISyntaxException diff --git a/core/src/main/kotlin/Utilities/Uri.kt b/core/src/main/kotlin/utilities/Uri.kt index 9827c624..089b3cff 100644 --- a/core/src/main/kotlin/Utilities/Uri.kt +++ b/core/src/main/kotlin/utilities/Uri.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka +package org.jetbrains.dokka.utilities import java.net.URI diff --git a/core/src/main/kotlin/utilities/cast.kt b/core/src/main/kotlin/utilities/cast.kt new file mode 100644 index 00000000..d4a8d73d --- /dev/null +++ b/core/src/main/kotlin/utilities/cast.kt @@ -0,0 +1,5 @@ +package org.jetbrains.dokka.utilities + +inline fun <reified T> Any.cast(): T { + return this as T +} diff --git a/core/src/main/kotlin/utilities/nodeDebug.kt b/core/src/main/kotlin/utilities/nodeDebug.kt new file mode 100644 index 00000000..0e8c61f7 --- /dev/null +++ b/core/src/main/kotlin/utilities/nodeDebug.kt @@ -0,0 +1,50 @@ +package org.jetbrains.dokka.utilities + +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.pages.* + +const val DOWN = '\u2503' +const val BRANCH = '\u2523' +const val LAST = '\u2517' + +fun Documentable.pretty(prefix: String = "", isLast: Boolean = true): String { + val nextPrefix = prefix + (if (isLast) ' ' else DOWN) + ' ' + + return prefix + (if (isLast) LAST else BRANCH) + this.toString() + + children.dropLast(1) + .map { it.pretty(nextPrefix, false) } + .plus(children.lastOrNull()?.pretty(nextPrefix)) + .filterNotNull() + .takeIf { it.isNotEmpty() } + ?.joinToString(prefix = "\n", separator = "") + .orEmpty() + if (children.isEmpty()) "\n" else "" +} + +//fun Any.genericPretty(prefix: String = "", isLast: Boolean = true): String { +// val nextPrefix = prefix + (if (isLast) ' ' else DOWN) + ' ' +// +// return prefix + (if (isLast) LAST else BRANCH) + this.stringify() + +// allChildren().dropLast(1) +// .map { it.genericPretty(nextPrefix, false) } +// .plus(allChildren().lastOrNull()?.genericPretty(nextPrefix)) +// .filterNotNull() +// .takeIf { it.isNotEmpty() } +// ?.joinToString(prefix = "\n", separator = "") +// .orEmpty() + if (allChildren().isEmpty()) "\n" else "" +//} +private fun Any.stringify() = when(this) { + is ContentNode -> toString() + this.dci + is ContentPage -> this.name + this::class.simpleName + else -> toString() +} +//private fun Any.allChildren() = when(this){ +// is PageNode -> children + content +// is ContentBlock -> this.children +// is ContentHeader -> this.items +// is ContentStyle -> this.items +// is ContentSymbol -> this.parts +// is ContentComment -> this.parts +// is ContentGroup -> this.children +// is ContentList -> this.items +// else -> emptyList() +//} diff --git a/core/src/main/resources/META-INF/MANIFEST.MF b/core/src/main/resources/META-INF/MANIFEST.MF index 78fabddc..9d885be5 100644 --- a/core/src/main/resources/META-INF/MANIFEST.MF +++ b/core/src/main/resources/META-INF/MANIFEST.MF @@ -1,4 +1 @@ Manifest-Version: 1.0 -Class-Path: kotlin-plugin.jar -Main-Class: org.jetbrains.dokka.DokkaPackage - diff --git a/core/src/main/resources/META-INF/dokka/dokka-version.properties b/core/src/main/resources/META-INF/dokka/dokka-version.properties new file mode 100644 index 00000000..6b2e2bcd --- /dev/null +++ b/core/src/main/resources/META-INF/dokka/dokka-version.properties @@ -0,0 +1 @@ +dokka-version=<dokka-version> diff --git a/core/src/main/resources/dokka/format/gfm.properties b/core/src/main/resources/dokka/format/gfm.properties deleted file mode 100644 index 5e8f7aa8..00000000 --- a/core/src/main/resources/dokka/format/gfm.properties +++ /dev/null @@ -1,2 +0,0 @@ -class=org.jetbrains.dokka.Formats.GFMFormatDescriptor -description=Produces documentation in GitHub-flavored markdown format diff --git a/core/src/main/resources/dokka/format/html-as-java.properties b/core/src/main/resources/dokka/format/html-as-java.properties deleted file mode 100644 index f598f377..00000000 --- a/core/src/main/resources/dokka/format/html-as-java.properties +++ /dev/null @@ -1,2 +0,0 @@ -class=org.jetbrains.dokka.Formats.HtmlAsJavaFormatDescriptor -description=Produces output in HTML format using Java syntax
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/html.properties b/core/src/main/resources/dokka/format/html.properties deleted file mode 100644 index 7881dfae..00000000 --- a/core/src/main/resources/dokka/format/html.properties +++ /dev/null @@ -1,2 +0,0 @@ -class=org.jetbrains.dokka.Formats.HtmlFormatDescriptor -description=Produces output in HTML format
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/java-layout-html.properties b/core/src/main/resources/dokka/format/java-layout-html.properties deleted file mode 100644 index fbb2bbed..00000000 --- a/core/src/main/resources/dokka/format/java-layout-html.properties +++ /dev/null @@ -1,2 +0,0 @@ -class=org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptor -description=Produces Kotlin Style Docs with Javadoc like layout
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/javadoc.properties b/core/src/main/resources/dokka/format/javadoc.properties deleted file mode 100644 index a0d8a945..00000000 --- a/core/src/main/resources/dokka/format/javadoc.properties +++ /dev/null @@ -1,2 +0,0 @@ -class=org.jetbrains.dokka.javadoc.JavadocFormatDescriptor -description=Produces Javadoc, with Kotlin declarations as Java view
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/jekyll.properties b/core/src/main/resources/dokka/format/jekyll.properties deleted file mode 100644 index b11401a4..00000000 --- a/core/src/main/resources/dokka/format/jekyll.properties +++ /dev/null @@ -1,2 +0,0 @@ -class=org.jetbrains.dokka.Formats.JekyllFormatDescriptor -description=Produces documentation in Jekyll format
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/kotlin-website-html.properties b/core/src/main/resources/dokka/format/kotlin-website-html.properties deleted file mode 100644 index f4c320b9..00000000 --- a/core/src/main/resources/dokka/format/kotlin-website-html.properties +++ /dev/null @@ -1,2 +0,0 @@ -class=org.jetbrains.dokka.Formats.KotlinWebsiteHtmlFormatDescriptor -description=Generates Kotlin website documentation
\ No newline at end of file diff --git a/core/src/main/resources/dokka/format/markdown.properties b/core/src/main/resources/dokka/format/markdown.properties deleted file mode 100644 index 6217a6df..00000000 --- a/core/src/main/resources/dokka/format/markdown.properties +++ /dev/null @@ -1,2 +0,0 @@ -class=org.jetbrains.dokka.Formats.MarkdownFormatDescriptor -description=Produces documentation in markdown format
\ No newline at end of file diff --git a/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties b/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties deleted file mode 100644 index c484a920..00000000 --- a/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties +++ /dev/null @@ -1,2 +0,0 @@ -class=org.jetbrains.dokka.InboundExternalLinkResolutionService$Dokka -description=Uses Dokka Default resolver
\ No newline at end of file diff --git a/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties b/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties deleted file mode 100644 index 3b61eabe..00000000 --- a/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties +++ /dev/null @@ -1,2 +0,0 @@ -class=org.jetbrains.dokka.Formats.JavaLayoutHtmlInboundLinkResolutionService -description=Resolver for JavaLayoutHtml
\ No newline at end of file diff --git a/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties b/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties deleted file mode 100644 index 0d5d7d17..00000000 --- a/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties +++ /dev/null @@ -1,2 +0,0 @@ -class=org.jetbrains.dokka.InboundExternalLinkResolutionService$Javadoc -description=Uses Javadoc Default resolver
\ No newline at end of file diff --git a/core/src/main/resources/dokka/styles/style.css b/core/src/main/resources/dokka/styles/style.css deleted file mode 100644 index 914be69d..00000000 --- a/core/src/main/resources/dokka/styles/style.css +++ /dev/null @@ -1,283 +0,0 @@ -@import url(https://fonts.googleapis.com/css?family=Open+Sans:300i,400,700); - -body, table { - padding:50px; - font:14px/1.5 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif; - color:#555; - font-weight:300; - margin-left: auto; - margin-right: auto; - max-width: 1440px; -} - -.keyword { - color:black; - font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; - font-size:12px; -} - -.symbol { - font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; - font-size:12px; -} - -.identifier { - color: darkblue; - font-size:12px; - font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; -} - -h1, h2, h3, h4, h5, h6 { - color:#222; - margin:0 0 20px; -} - -p, ul, ol, table, pre, dl { - margin:0 0 20px; -} - -h1, h2, h3 { - line-height:1.1; -} - -h1 { - font-size:28px; -} - -h2 { - color:#393939; -} - -h3, h4, h5, h6 { - color:#494949; -} - -a { - color:#258aaf; - font-weight:400; - text-decoration:none; -} - -a:hover { - color: inherit; - text-decoration:underline; -} - -a small { - font-size:11px; - color:#555; - margin-top:-0.6em; - display:block; -} - -.wrapper { - width:860px; - margin:0 auto; -} - -blockquote { - border-left:1px solid #e5e5e5; - margin:0; - padding:0 0 0 20px; - font-style:italic; -} - -code, pre { - font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; - color:#333; - font-size:12px; -} - -pre { - display: block; -/* - padding:8px 8px; - background: #f8f8f8; - border-radius:5px; - border:1px solid #e5e5e5; -*/ - overflow-x: auto; -} - -table { - width:100%; - border-collapse:collapse; -} - -th, td { - text-align:left; - vertical-align: top; - padding:5px 10px; -} - -dt { - color:#444; - font-weight:700; -} - -th { - color:#444; -} - -img { - max-width:100%; -} - -header { - width:270px; - float:left; - position:fixed; -} - -header ul { - list-style:none; - height:40px; - - padding:0; - - background: #eee; - background: -moz-linear-gradient(top, #f8f8f8 0%, #dddddd 100%); - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd)); - background: -webkit-linear-gradient(top, #f8f8f8 0%,#dddddd 100%); - background: -o-linear-gradient(top, #f8f8f8 0%,#dddddd 100%); - background: -ms-linear-gradient(top, #f8f8f8 0%,#dddddd 100%); - background: linear-gradient(top, #f8f8f8 0%,#dddddd 100%); - - border-radius:5px; - border:1px solid #d2d2d2; - box-shadow:inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0; - width:270px; -} - -header li { - width:89px; - float:left; - border-right:1px solid #d2d2d2; - height:40px; -} - -header ul a { - line-height:1; - font-size:11px; - color:#999; - display:block; - text-align:center; - padding-top:6px; - height:40px; -} - -strong { - color:#222; - font-weight:700; -} - -header ul li + li { - width:88px; - border-left:1px solid #fff; -} - -header ul li + li + li { - border-right:none; - width:89px; -} - -header ul a strong { - font-size:14px; - display:block; - color:#222; -} - -section { - width:500px; - float:right; - padding-bottom:50px; -} - -small { - font-size:11px; -} - -hr { - border:0; - background:#e5e5e5; - height:1px; - margin:0 0 20px; -} - -footer { - width:270px; - float:left; - position:fixed; - bottom:50px; -} - -@media print, screen and (max-width: 960px) { - - div.wrapper { - width:auto; - margin:0; - } - - header, section, footer { - float:none; - position:static; - width:auto; - } - - header { - padding-right:320px; - } - - section { - border:1px solid #e5e5e5; - border-width:1px 0; - padding:20px 0; - margin:0 0 20px; - } - - header a small { - display:inline; - } - - header ul { - position:absolute; - right:50px; - top:52px; - } -} - -@media print, screen and (max-width: 720px) { - body { - word-wrap:break-word; - } - - header { - padding:0; - } - - header ul, header p.view { - position:static; - } - - pre, code { - word-wrap:normal; - } -} - -@media print, screen and (max-width: 480px) { - body { - padding:15px; - } - - header ul { - display:none; - } -} - -@media print { - body { - padding:0.4in; - font-size:12pt; - color:#444; - } -} diff --git a/core/src/test/kotlin/DokkaConfigurationTestImplementations.kt b/core/src/test/kotlin/DokkaConfigurationTestImplementations.kt deleted file mode 100644 index a6f427b1..00000000 --- a/core/src/test/kotlin/DokkaConfigurationTestImplementations.kt +++ /dev/null @@ -1,81 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.Platform -import java.io.File - - -data class SourceLinkDefinitionImpl(override val path: String, - override val url: String, - override val lineSuffix: String?) : DokkaConfiguration.SourceLinkDefinition { - companion object { - fun parseSourceLinkDefinition(srcLink: String): DokkaConfiguration.SourceLinkDefinition { - val (path, urlAndLine) = srcLink.split('=') - return SourceLinkDefinitionImpl( - File(path).canonicalPath, - urlAndLine.substringBefore("#"), - urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#$it" }) - } - } -} - -class SourceRootImpl(path: String) : DokkaConfiguration.SourceRoot { - override val path: String = File(path).absolutePath - - companion object { - fun parseSourceRoot(sourceRoot: String): DokkaConfiguration.SourceRoot = SourceRootImpl(sourceRoot) - } -} - -data class PackageOptionsImpl(override val prefix: String, - override val includeNonPublic: Boolean = false, - override val reportUndocumented: Boolean = true, - override val skipDeprecated: Boolean = false, - override val suppress: Boolean = false) : DokkaConfiguration.PackageOptions - - class DokkaConfigurationImpl( - override val outputDir: String = "", - override val format: String = "html", - override val generateIndexPages: Boolean = false, - override val cacheRoot: String? = null, - override val impliedPlatforms: List<String> = emptyList(), - override val passesConfigurations: List<DokkaConfiguration.PassConfiguration> = emptyList() -) : DokkaConfiguration - -class PassConfigurationImpl ( - override val classpath: List<String> = emptyList(), - override val moduleName: String = "", - override val sourceRoots: List<DokkaConfiguration.SourceRoot> = emptyList(), - override val samples: List<String> = emptyList(), - override val includes: List<String> = emptyList(), - override val includeNonPublic: Boolean = false, - override val includeRootPackage: Boolean = false, - override val reportUndocumented: Boolean = false, - override val skipEmptyPackages: Boolean = false, - override val skipDeprecated: Boolean = false, - override val jdkVersion: Int = 6, - override val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition> = emptyList(), - override val perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(), - externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink> = emptyList(), - override val languageVersion: String? = null, - override val apiVersion: String? = null, - override val noStdlibLink: Boolean = false, - override val noJdkLink: Boolean = false, - override val suppressedFiles: List<String> = emptyList(), - override val collectInheritedExtensionsFromLibraries: Boolean = false, - override val analysisPlatform: Platform = Platform.DEFAULT, - override val targets: List<String> = emptyList(), - override val sinceKotlin: String? = null -): DokkaConfiguration.PassConfiguration { - private val defaultLinks = run { - val links = mutableListOf<DokkaConfiguration.ExternalDocumentationLink>() - if (!noJdkLink) - links += DokkaConfiguration.ExternalDocumentationLink.Builder("https://docs.oracle.com/javase/$jdkVersion/docs/api/").build() - - if (!noStdlibLink) - links += DokkaConfiguration.ExternalDocumentationLink.Builder("https://kotlinlang.org/api/latest/jvm/stdlib/").build() - links - } - override val externalDocumentationLinks = defaultLinks + externalDocumentationLinks -} - diff --git a/core/src/test/kotlin/NodeSelect.kt b/core/src/test/kotlin/NodeSelect.kt deleted file mode 100644 index fe0394f9..00000000 --- a/core/src/test/kotlin/NodeSelect.kt +++ /dev/null @@ -1,90 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.DocumentationNode -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.RefKind - -class SelectBuilder { - private val root = ChainFilterNode(SubgraphTraverseFilter(), null) - private var activeNode = root - private val chainEnds = mutableListOf<SelectFilter>() - - fun withName(name: String) = matching { it.name == name } - - fun withKind(kind: NodeKind) = matching{ it.kind == kind } - - fun matching(block: (DocumentationNode) -> Boolean) { - attachFilterAndMakeActive(PredicateFilter(block)) - } - - fun subgraph() { - attachFilterAndMakeActive(SubgraphTraverseFilter()) - } - - fun subgraphOf(kind: RefKind) { - attachFilterAndMakeActive(DirectEdgeFilter(kind)) - } - - private fun attachFilterAndMakeActive(next: SelectFilter) { - activeNode = ChainFilterNode(next, activeNode) - } - - private fun endChain() { - chainEnds += activeNode - } - - fun build(): SelectFilter { - endChain() - return CombineFilterNode(chainEnds) - } -} - -private class ChainFilterNode(val filter: SelectFilter, val previous: SelectFilter?): SelectFilter() { - override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> { - return filter.select(previous?.select(roots) ?: roots) - } -} - -private class CombineFilterNode(val previous: List<SelectFilter>): SelectFilter() { - override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> { - return previous.asSequence().flatMap { it.select(roots) } - } -} - -abstract class SelectFilter { - abstract fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> -} - -private class SubgraphTraverseFilter: SelectFilter() { - override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> { - val visited = mutableSetOf<DocumentationNode>() - return roots.flatMap { - generateSequence(listOf(it)) { nodes -> - nodes.flatMap { it.allReferences() } - .map { it.to } - .filter { visited.add(it) } - .takeUnless { it.isEmpty() } - } - }.flatten() - } - -} - -private class PredicateFilter(val condition: (DocumentationNode) -> Boolean): SelectFilter() { - override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> { - return roots.filter(condition) - } -} - -private class DirectEdgeFilter(val kind: RefKind): SelectFilter() { - override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> { - return roots.flatMap { it.references(kind).asSequence() }.map { it.to } - } -} - - -fun selectNodes(root: DocumentationNode, block: SelectBuilder.() -> Unit): List<DocumentationNode> { - val builder = SelectBuilder() - builder.apply(block) - return builder.build().select(sequenceOf(root)).toMutableSet().toList() -}
\ No newline at end of file diff --git a/core/src/test/kotlin/TestAPI.kt b/core/src/test/kotlin/TestAPI.kt deleted file mode 100644 index 4f9af761..00000000 --- a/core/src/test/kotlin/TestAPI.kt +++ /dev/null @@ -1,353 +0,0 @@ -package org.jetbrains.dokka.tests - -import com.google.inject.Guice -import com.intellij.openapi.application.PathManager -import com.intellij.openapi.util.Disposer -import com.intellij.openapi.util.io.FileUtil -import com.intellij.rt.execution.junit.FileComparisonFailure -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Utilities.DokkaAnalysisModule -import org.jetbrains.dokka.Utilities.DokkaRunModule -import org.jetbrains.kotlin.cli.common.config.ContentRoot -import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity -import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.utils.PathUtil -import org.junit.Assert -import org.junit.Assert.fail -import java.io.File - -data class ModelConfig( - val roots: Array<ContentRoot> = arrayOf(), - val withJdk: Boolean = false, - val withKotlinRuntime: Boolean = false, - val format: String = "html", - val includeNonPublic: Boolean = true, - val perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(), - val analysisPlatform: Platform = Platform.DEFAULT, - val defaultPlatforms: List<String> = emptyList(), - val noStdlibLink: Boolean = true, - val collectInheritedExtensionsFromLibraries: Boolean = false, - val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition> = emptyList() -) - -fun verifyModel( - modelConfig: ModelConfig, - verifier: (DocumentationModule) -> Unit -) { - val documentation = DocumentationModule("test") - - val passConfiguration = PassConfigurationImpl( - includeNonPublic = modelConfig.includeNonPublic, - skipEmptyPackages = false, - includeRootPackage = true, - sourceLinks = modelConfig.sourceLinks, - perPackageOptions = modelConfig.perPackageOptions, - noStdlibLink = modelConfig.noStdlibLink, - noJdkLink = false, - languageVersion = null, - apiVersion = null, - collectInheritedExtensionsFromLibraries = modelConfig.collectInheritedExtensionsFromLibraries - ) - val configuration = DokkaConfigurationImpl( - outputDir = "", - format = modelConfig.format, - generateIndexPages = false, - cacheRoot = "default", - passesConfigurations = listOf(passConfiguration) - ) - - appendDocumentation(documentation, configuration, passConfiguration, modelConfig) - documentation.prepareForGeneration(configuration) - - verifier(documentation) -} - -fun appendDocumentation( - documentation: DocumentationModule, - dokkaConfiguration: DokkaConfiguration, - passConfiguration: DokkaConfiguration.PassConfiguration, - modelConfig: ModelConfig -) { - val messageCollector = object : MessageCollector { - override fun clear() { - - } - - override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) { - when (severity) { - CompilerMessageSeverity.STRONG_WARNING, - CompilerMessageSeverity.WARNING, - CompilerMessageSeverity.LOGGING, - CompilerMessageSeverity.OUTPUT, - CompilerMessageSeverity.INFO, - CompilerMessageSeverity.ERROR -> { - println("$severity: $message at $location") - } - CompilerMessageSeverity.EXCEPTION -> { - fail("$severity: $message at $location") - } - } - } - - override fun hasErrors() = false - } - - val environment = AnalysisEnvironment(messageCollector, modelConfig.analysisPlatform) - environment.apply { - if (modelConfig.withJdk || modelConfig.withKotlinRuntime) { - addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre()) - } - if (modelConfig.withKotlinRuntime) { - if (analysisPlatform == Platform.jvm) { - val kotlinStrictfpRoot = PathManager.getResourceRoot(Strictfp::class.java, "/kotlin/jvm/Strictfp.class") - addClasspath(File(kotlinStrictfpRoot)) - } - if (analysisPlatform == Platform.js) { - val kotlinStdlibJsRoot = PathManager.getResourceRoot(Any::class.java, "/kotlin/jquery") - addClasspath(File(kotlinStdlibJsRoot)) - } - - if (analysisPlatform == Platform.common) { - // TODO: Feels hacky - val kotlinStdlibCommonRoot = ClassLoader.getSystemResource("kotlin/UInt.kotlin_metadata") - addClasspath(File(kotlinStdlibCommonRoot.file.replace("file:", "").replaceAfter(".jar", ""))) - } - } - addRoots(modelConfig.roots.toList()) - - loadLanguageVersionSettings(passConfiguration.languageVersion, passConfiguration.apiVersion) - } - val defaultPlatformsProvider = object : DefaultPlatformsProvider { - override fun getDefaultPlatforms(descriptor: DeclarationDescriptor) = modelConfig.defaultPlatforms - } - - val globalInjector = Guice.createInjector( - DokkaRunModule(dokkaConfiguration) - ) - - val injector = globalInjector.createChildInjector( - DokkaAnalysisModule( - environment, - dokkaConfiguration, - defaultPlatformsProvider, - documentation.nodeRefGraph, - passConfiguration, - DokkaConsoleLogger - ) - ) - - buildDocumentationModule(injector, documentation) - Disposer.dispose(environment) -} - -fun checkSourceExistsAndVerifyModel( - source: String, - modelConfig: ModelConfig = ModelConfig(), - verifier: (DocumentationModule) -> Unit -) { - require(File(source).exists()) { - "Cannot find test data file $source" - } - verifyModel( - ModelConfig( - roots = arrayOf(contentRootFromPath(source)), - withJdk = modelConfig.withJdk, - withKotlinRuntime = modelConfig.withKotlinRuntime, - format = modelConfig.format, - includeNonPublic = modelConfig.includeNonPublic, - sourceLinks = modelConfig.sourceLinks, - analysisPlatform = modelConfig.analysisPlatform - ), - - verifier = verifier - ) -} - -fun verifyPackageMember( - source: String, - modelConfig: ModelConfig = ModelConfig(), - verifier: (DocumentationNode) -> Unit -) { - checkSourceExistsAndVerifyModel( - source, - modelConfig = ModelConfig( - withJdk = modelConfig.withJdk, - withKotlinRuntime = modelConfig.withKotlinRuntime, - analysisPlatform = modelConfig.analysisPlatform - ) - ) { model -> - val pkg = model.members.single() - verifier(pkg.members.single()) - } -} - -fun verifyJavaModel( - source: String, - modelConfig: ModelConfig = ModelConfig(), - verifier: (DocumentationModule) -> Unit -) { - val tempDir = FileUtil.createTempDirectory("dokka", "") - try { - val sourceFile = File(source) - FileUtil.copy(sourceFile, File(tempDir, sourceFile.name)) - verifyModel( - ModelConfig( - roots = arrayOf(JavaSourceRoot(tempDir, null)), - withJdk = true, - withKotlinRuntime = modelConfig.withKotlinRuntime, - analysisPlatform = modelConfig.analysisPlatform - ), - verifier = verifier - ) - } finally { - FileUtil.delete(tempDir) - } -} - -fun verifyJavaPackageMember( - source: String, - modelConfig: ModelConfig = ModelConfig(), - verifier: (DocumentationNode) -> Unit -) { - verifyJavaModel(source, modelConfig) { model -> - val pkg = model.members.single() - verifier(pkg.members.single()) - } -} - -fun verifyOutput( - modelConfig: ModelConfig = ModelConfig(), - outputExtension: String, - outputGenerator: (DocumentationModule, StringBuilder) -> Unit -) { - verifyModel(modelConfig) { - verifyModelOutput(it, outputExtension, modelConfig.roots.first().path, outputGenerator) - } -} - -fun verifyOutput( - path: String, - outputExtension: String, - modelConfig: ModelConfig = ModelConfig(), - outputGenerator: (DocumentationModule, StringBuilder) -> Unit -) { - verifyOutput( - ModelConfig( - roots = arrayOf(contentRootFromPath(path)) + modelConfig.roots, - withJdk = modelConfig.withJdk, - withKotlinRuntime = modelConfig.withKotlinRuntime, - format = modelConfig.format, - includeNonPublic = modelConfig.includeNonPublic, - analysisPlatform = modelConfig.analysisPlatform, - noStdlibLink = modelConfig.noStdlibLink, - collectInheritedExtensionsFromLibraries = modelConfig.collectInheritedExtensionsFromLibraries - ), - outputExtension, - outputGenerator - ) -} - -fun verifyModelOutput( - it: DocumentationModule, - outputExtension: String, - sourcePath: String, - outputGenerator: (DocumentationModule, StringBuilder) -> Unit -) { - val output = StringBuilder() - outputGenerator(it, output) - val ext = outputExtension.removePrefix(".") - val expectedFile = File(sourcePath.replaceAfterLast(".", ext, sourcePath + "." + ext)) - assertEqualsIgnoringSeparators(expectedFile, output.toString()) -} - -fun verifyJavaOutput( - path: String, - outputExtension: String, - modelConfig: ModelConfig = ModelConfig(), - outputGenerator: (DocumentationModule, StringBuilder) -> Unit -) { - verifyJavaModel(path, modelConfig) { model -> - verifyModelOutput(model, outputExtension, path, outputGenerator) - } -} - -fun assertEqualsIgnoringSeparators(expectedFile: File, output: String) { - if (!expectedFile.exists()) expectedFile.createNewFile() - val expectedText = expectedFile.readText().replace("\r\n", "\n") - val actualText = output.replace("\r\n", "\n") - - if (expectedText != actualText) - throw FileComparisonFailure("", expectedText, actualText, expectedFile.canonicalPath) -} - -fun assertEqualsIgnoringSeparators(expectedOutput: String, output: String) { - Assert.assertEquals(expectedOutput.replace("\r\n", "\n"), output.replace("\r\n", "\n")) -} - -fun StringBuilder.appendChildren(node: ContentBlock): StringBuilder { - for (child in node.children) { - val childText = child.toTestString() - append(childText) - } - return this -} - -fun StringBuilder.appendNode(node: ContentNode): StringBuilder { - when (node) { - is ContentText -> { - append(node.text) - } - is ContentEmphasis -> append("*").appendChildren(node).append("*") - is ContentBlockCode -> { - if (node.language.isNotBlank()) - appendln("[code lang=${node.language}]") - else - appendln("[code]") - appendChildren(node) - appendln() - appendln("[/code]") - } - is ContentNodeLink -> { - append("[") - appendChildren(node) - append(" -> ") - append(node.node.toString()) - append("]") - } - is ContentBlock -> { - appendChildren(node) - } - is NodeRenderContent -> { - append("render(") - append(node.node) - append(",") - append(node.mode) - append(")") - } - is ContentSymbol -> { - append(node.text) - } - is ContentEmpty -> { /* nothing */ - } - else -> throw IllegalStateException("Don't know how to format node $node") - } - return this -} - -fun ContentNode.toTestString(): String { - val node = this - return StringBuilder().apply { - appendNode(node) - }.toString() -} - -val ContentRoot.path: String - get() = when (this) { - is KotlinSourceRoot -> path - is JavaSourceRoot -> file.path - else -> throw UnsupportedOperationException() - } diff --git a/core/src/test/kotlin/format/FileGeneratorTestCase.kt b/core/src/test/kotlin/format/FileGeneratorTestCase.kt deleted file mode 100644 index ef9e815d..00000000 --- a/core/src/test/kotlin/format/FileGeneratorTestCase.kt +++ /dev/null @@ -1,35 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.* -import org.junit.Before -import org.junit.Rule -import org.junit.rules.TemporaryFolder - - -abstract class FileGeneratorTestCase { - abstract val formatService: FormatService - - @get:Rule - var folder = TemporaryFolder() - - val fileGenerator = FileGenerator(folder.apply { create() }.root) - - @Before - fun bindGenerator() { - fileGenerator.formatService = formatService - } - - fun buildPagesAndReadInto(nodes: List<DocumentationNode>, sb: StringBuilder) = with(fileGenerator) { - buildPages(nodes) - val byLocations = nodes.groupBy { location(it) } - byLocations.forEach { (loc, _) -> - if (byLocations.size > 1) { - if (sb.isNotBlank() && !sb.endsWith('\n')) { - sb.appendln() - } - sb.appendln("<!-- File: ${loc.file.relativeTo(root).toUnixString()} -->") - } - sb.append(loc.file.readText()) - } - } -}
\ No newline at end of file diff --git a/core/src/test/kotlin/format/GFMFormatTest.kt b/core/src/test/kotlin/format/GFMFormatTest.kt deleted file mode 100644 index 60de7d29..00000000 --- a/core/src/test/kotlin/format/GFMFormatTest.kt +++ /dev/null @@ -1,36 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.GFMFormatService -import org.jetbrains.dokka.KotlinLanguageService -import org.jetbrains.dokka.Platform -import org.junit.Test - -abstract class BaseGFMFormatTest(val analysisPlatform: Platform) : FileGeneratorTestCase() { - override val formatService = GFMFormatService(fileGenerator, KotlinLanguageService(), listOf()) - private val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - - - @Test - fun sample() { - verifyGFMNodeByName("sample", "Foo", defaultModelConfig) - } - - @Test - fun listInTableCell() { - verifyGFMNodeByName("listInTableCell", "Foo", defaultModelConfig) - } - - private fun verifyGFMNodeByName(fileName: String, name: String, modelConfig: ModelConfig) { - verifyOutput("testdata/format/gfm/$fileName.kt", ".md", modelConfig) { model, output -> - buildPagesAndReadInto( - model.members.single().members.filter { it.name == name }, - output - ) - } - } -} - - -class JsGFMFormatTest : BaseGFMFormatTest(Platform.js) -class JvmGFMFormatTest : BaseGFMFormatTest(Platform.jvm) -class CommonGFMFormatTest : BaseGFMFormatTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/format/HtmlFormatTest.kt b/core/src/test/kotlin/format/HtmlFormatTest.kt deleted file mode 100644 index 60e29006..00000000 --- a/core/src/test/kotlin/format/HtmlFormatTest.kt +++ /dev/null @@ -1,192 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.* -import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot -import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot -import org.junit.Test -import java.io.File - -abstract class BaseHtmlFormatTest(val analysisPlatform: Platform): FileGeneratorTestCase() { - protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - override val formatService = HtmlFormatService(fileGenerator, KotlinLanguageService(), HtmlTemplateService.default(), listOf()) - - @Test fun classWithCompanionObject() { - verifyHtmlNode("classWithCompanionObject", defaultModelConfig) - } - - @Test fun htmlEscaping() { - verifyHtmlNode("htmlEscaping", defaultModelConfig) - } - - @Test fun overloads() { - verifyHtmlNodes("overloads", defaultModelConfig) { model -> model.members } - } - - @Test fun overloadsWithDescription() { - verifyHtmlNode("overloadsWithDescription", defaultModelConfig) - } - - @Test fun overloadsWithDifferentDescriptions() { - verifyHtmlNode("overloadsWithDifferentDescriptions", defaultModelConfig) - } - - @Test fun deprecated() { - verifyOutput("testdata/format/deprecated.kt", ".package.html", defaultModelConfig) { model, output -> - buildPagesAndReadInto(model.members, output) - } - verifyOutput("testdata/format/deprecated.kt", ".class.html", defaultModelConfig) { model, output -> - buildPagesAndReadInto(model.members.single().members, output) - } - } - - @Test fun brokenLink() { - verifyHtmlNode("brokenLink", defaultModelConfig) - } - - @Test fun codeSpan() { - verifyHtmlNode("codeSpan", defaultModelConfig) - } - - @Test fun parenthesis() { - verifyHtmlNode("parenthesis", defaultModelConfig) - } - - @Test fun bracket() { - verifyHtmlNode("bracket", defaultModelConfig) - } - - @Test fun see() { - verifyHtmlNode("see", defaultModelConfig) - } - - @Test fun tripleBackticks() { - verifyHtmlNode("tripleBackticks", defaultModelConfig) - } - - @Test fun typeLink() { - verifyHtmlNodes("typeLink", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Bar" } } - } - - @Test fun parameterAnchor() { - verifyHtmlNode("parameterAnchor", defaultModelConfig) - } - - @Test fun codeBlock() { - verifyHtmlNode("codeBlock", defaultModelConfig) - } - @Test fun orderedList() { - verifyHtmlNodes("orderedList", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Bar" } } - } - - @Test fun linkWithLabel() { - verifyHtmlNodes("linkWithLabel", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Bar" } } - } - - @Test fun entity() { - verifyHtmlNodes("entity", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Bar" } } - } - - @Test fun uninterpretedEmphasisCharacters() { - verifyHtmlNode("uninterpretedEmphasisCharacters", defaultModelConfig) - } - - @Test fun markdownInLinks() { - verifyHtmlNode("markdownInLinks", defaultModelConfig) - } - - @Test fun returnWithLink() { - verifyHtmlNode("returnWithLink", defaultModelConfig) - } - - @Test fun linkWithStarProjection() { - verifyHtmlNode("linkWithStarProjection", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun functionalTypeWithNamedParameters() { - verifyHtmlNode("functionalTypeWithNamedParameters", defaultModelConfig) - } - - @Test fun sinceKotlin() { - verifyHtmlNode("sinceKotlin", defaultModelConfig) - } - - @Test fun blankLineInsideCodeBlock() { - verifyHtmlNode("blankLineInsideCodeBlock", defaultModelConfig) - } - - @Test fun indentedCodeBlock() { - verifyHtmlNode("indentedCodeBlock", defaultModelConfig) - } - - private fun verifyHtmlNode(fileName: String, modelConfig: ModelConfig = ModelConfig()) { - verifyHtmlNodes(fileName, modelConfig) { model -> model.members.single().members } - } - - private fun verifyHtmlNodes(fileName: String, - modelConfig: ModelConfig = ModelConfig(), - nodeFilter: (DocumentationModule) -> List<DocumentationNode>) { - verifyOutput("testdata/format/$fileName.kt", ".html", modelConfig) { model, output -> - buildPagesAndReadInto(nodeFilter(model), output) - } - } - - protected fun verifyJavaHtmlNode(fileName: String, modelConfig: ModelConfig = ModelConfig()) { - verifyJavaHtmlNodes(fileName, modelConfig) { model -> model.members.single().members } - } - - protected fun verifyJavaHtmlNodes(fileName: String, - modelConfig: ModelConfig = ModelConfig(), - nodeFilter: (DocumentationModule) -> List<DocumentationNode>) { - verifyJavaOutput("testdata/format/$fileName.java", ".html", modelConfig) { model, output -> - buildPagesAndReadInto(nodeFilter(model), output) - } - } -} - -class JSHtmlFormatTest: BaseHtmlFormatTest(Platform.js) - -class JVMHtmlFormatTest: BaseHtmlFormatTest(Platform.jvm) { - @Test - fun javaSeeTag() { - verifyJavaHtmlNode("javaSeeTag", defaultModelConfig) - } - - @Test fun javaDeprecated() { - verifyJavaHtmlNodes("javaDeprecated", defaultModelConfig) { model -> - model.members.single().members.single { it.name == "Foo" }.members.filter { it.name == "foo" } - } - } - - @Test fun crossLanguageKotlinExtendsJava() { - verifyOutput( - ModelConfig( - roots = arrayOf( - KotlinSourceRoot("testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt", false), - JavaSourceRoot(File("testdata/format/crossLanguage/kotlinExtendsJava"), null) - ), - analysisPlatform = analysisPlatform - ), ".html") { model, output -> - buildPagesAndReadInto( - model.members.single().members.filter { it.name == "Bar" }, - output - ) - } - } - - @Test fun javaLinkTag() { - verifyJavaHtmlNode("javaLinkTag", defaultModelConfig) - } - - @Test fun javaLinkTagWithLabel() { - verifyJavaHtmlNode("javaLinkTagWithLabel", defaultModelConfig) - } - - @Test fun javaSupertypeLink() { - verifyJavaHtmlNodes("JavaSupertype", defaultModelConfig) { model -> - model.members.single().members.single { it.name == "JavaSupertype" }.members.filter { it.name == "Bar" } - } - } - -} - -class CommonHtmlFormatTest: BaseHtmlFormatTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt deleted file mode 100644 index ebab5f36..00000000 --- a/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt +++ /dev/null @@ -1,123 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Generation.DocumentationMerger -import org.junit.Test - -abstract class BaseKotlinWebSiteHtmlFormatTest(val analysisPlatform: Platform): FileGeneratorTestCase() { - val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - override val formatService = KotlinWebsiteHtmlFormatService(fileGenerator, KotlinLanguageService(), listOf(), EmptyHtmlTemplateService) - - @Test fun dropImport() { - verifyKWSNodeByName("dropImport", "foo", defaultModelConfig) - } - - @Test fun sample() { - verifyKWSNodeByName("sample", "foo", defaultModelConfig) - } - - @Test fun sampleWithAsserts() { - verifyKWSNodeByName("sampleWithAsserts", "a", defaultModelConfig) - } - - @Test fun newLinesInSamples() { - verifyKWSNodeByName("newLinesInSamples", "foo", defaultModelConfig) - } - - @Test fun newLinesInImportList() { - verifyKWSNodeByName("newLinesInImportList", "foo", defaultModelConfig) - } - - @Test fun returnTag() { - verifyKWSNodeByName("returnTag", "indexOf", defaultModelConfig) - } - - @Test fun overloadGroup() { - verifyKWSNodeByName("overloadGroup", "magic", defaultModelConfig) - } - - @Test fun dataTags() { - val module = buildMultiplePlatforms("dataTags") - verifyMultiplatformPackage(module, "dataTags") - } - - @Test fun dataTagsInGroupNode() { - val path = "dataTagsInGroupNode" - val module = buildMultiplePlatforms(path) - verifyModelOutput(module, ".html", "testdata/format/website-html/$path/multiplatform.kt") { model, output -> - buildPagesAndReadInto( - listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }), - output - ) - } - verifyMultiplatformPackage(module, path) - } - - private fun verifyKWSNodeByName(fileName: String, name: String, modelConfig: ModelConfig) { - verifyOutput( - "testdata/format/website-html/$fileName.kt", - ".html", - ModelConfig(analysisPlatform = modelConfig.analysisPlatform, format = "kotlin-website-html") - ) { model, output -> - buildPagesAndReadInto(model.members.single().members.filter { it.name == name }, output) - } - } - - private fun buildMultiplePlatforms(path: String): DocumentationModule { - val moduleName = "test" - val passConfiguration = PassConfigurationImpl( - noStdlibLink = true, - noJdkLink = true, - languageVersion = null, - apiVersion = null - ) - - val dokkaConfiguration = DokkaConfigurationImpl( - outputDir = "", - format = "kotlin-website-html", - generateIndexPages = false, - passesConfigurations = listOf( - passConfiguration - ) - - ) - - val module1 = DocumentationModule(moduleName) - appendDocumentation( - module1, dokkaConfiguration, passConfiguration, ModelConfig( - roots = arrayOf(contentRootFromPath("testdata/format/website-html/$path/jvm.kt")), - defaultPlatforms = listOf("JVM") - ) - ) - - val module2 = DocumentationModule(moduleName) - appendDocumentation( - module2, dokkaConfiguration, passConfiguration, ModelConfig( - roots = arrayOf(contentRootFromPath("testdata/format/website-html/$path/jre7.kt")), - defaultPlatforms = listOf("JVM", "JRE7") - ) - ) - - val module3 = DocumentationModule(moduleName) - appendDocumentation( - module3, dokkaConfiguration, passConfiguration, ModelConfig( - roots = arrayOf(contentRootFromPath("testdata/format/website-html/$path/js.kt")), - defaultPlatforms = listOf("JS") - ) - ) - - return DocumentationMerger(listOf(module1, module2, module3), DokkaConsoleLogger).merge() - } - - private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) { - verifyModelOutput(module, ".package.html", "testdata/format/website-html/$path/multiplatform.kt") { model, output -> - buildPagesAndReadInto(model.members, output) - } - } - -} -class JsKotlinWebSiteHtmlFormatTest: BaseKotlinWebSiteHtmlFormatTest(Platform.js) - -class JvmKotlinWebSiteHtmlFormatTest: BaseKotlinWebSiteHtmlFormatTest(Platform.jvm) - -class CommonKotlinWebSiteHtmlFormatTest: BaseKotlinWebSiteHtmlFormatTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/format/MarkdownFormatTest.kt b/core/src/test/kotlin/format/MarkdownFormatTest.kt deleted file mode 100644 index 4984e1d5..00000000 --- a/core/src/test/kotlin/format/MarkdownFormatTest.kt +++ /dev/null @@ -1,615 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Generation.DocumentationMerger -import org.junit.Test - -abstract class BaseMarkdownFormatTest(val analysisPlatform: Platform): FileGeneratorTestCase() { - override val formatService = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf()) - - protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - - @Test fun emptyDescription() { - verifyMarkdownNode("emptyDescription", defaultModelConfig) - } - - @Test fun classWithCompanionObject() { - verifyMarkdownNode("classWithCompanionObject", defaultModelConfig) - } - - @Test fun annotations() { - verifyMarkdownNode("annotations", defaultModelConfig) - } - - @Test fun annotationClass() { - verifyMarkdownNode("annotationClass", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - verifyMarkdownPackage("annotationClass", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun enumClass() { - verifyOutput("testdata/format/enumClass.kt", ".md", defaultModelConfig) { model, output -> - buildPagesAndReadInto(model.members.single().members, output) - } - verifyOutput("testdata/format/enumClass.kt", ".value.md", defaultModelConfig) { model, output -> - val enumClassNode = model.members.single().members[0] - buildPagesAndReadInto( - enumClassNode.members.filter { it.name == "LOCAL_CONTINUE_AND_BREAK" }, - output - ) - } - } - - @Test fun varargsFunction() { - verifyMarkdownNode("varargsFunction", defaultModelConfig) - } - - @Test fun overridingFunction() { - verifyMarkdownNodes("overridingFunction", defaultModelConfig) { model-> - val classMembers = model.members.single().members.first { it.name == "D" }.members - classMembers.filter { it.name == "f" } - } - } - - @Test fun propertyVar() { - verifyMarkdownNode("propertyVar", defaultModelConfig) - } - - @Test fun functionWithDefaultParameter() { - verifyMarkdownNode("functionWithDefaultParameter", defaultModelConfig) - } - - @Test fun accessor() { - verifyMarkdownNodes("accessor", defaultModelConfig) { model -> - model.members.single().members.first { it.name == "C" }.members.filter { it.name == "x" } - } - } - - @Test fun paramTag() { - verifyMarkdownNode("paramTag", defaultModelConfig) - } - - @Test fun throwsTag() { - verifyMarkdownNode("throwsTag", defaultModelConfig) - } - - @Test fun typeParameterBounds() { - verifyMarkdownNode("typeParameterBounds", defaultModelConfig) - } - - @Test fun typeParameterVariance() { - verifyMarkdownNode("typeParameterVariance", defaultModelConfig) - } - - @Test fun typeProjectionVariance() { - verifyMarkdownNode("typeProjectionVariance", defaultModelConfig) - } - - @Test fun codeBlockNoHtmlEscape() { - verifyMarkdownNodeByName("codeBlockNoHtmlEscape", "hackTheArithmetic", defaultModelConfig) - } - - @Test fun companionObjectExtension() { - verifyMarkdownNodeByName("companionObjectExtension", "Foo", defaultModelConfig) - } - - @Test fun starProjection() { - verifyMarkdownNode("starProjection", defaultModelConfig) - } - - @Test fun extensionFunctionParameter() { - verifyMarkdownNode("extensionFunctionParameter", defaultModelConfig) - } - - @Test fun summarizeSignatures() { - verifyMarkdownNodes("summarizeSignatures", defaultModelConfig) { model -> model.members } - } - - @Test fun reifiedTypeParameter() { - verifyMarkdownNode("reifiedTypeParameter", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun suspendInlineFunctionOrder() { - verifyMarkdownNode("suspendInlineFunction", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun inlineSuspendFunctionOrderChanged() { - verifyMarkdownNode("inlineSuspendFunction", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun annotatedTypeParameter() { - verifyMarkdownNode("annotatedTypeParameter", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun inheritedMembers() { - verifyMarkdownNodeByName("inheritedMembers", "Bar", defaultModelConfig) - } - - @Test fun inheritedExtensions() { - verifyMarkdownNodeByName("inheritedExtensions", "Bar", defaultModelConfig) - } - - @Test fun genericInheritedExtensions() { - verifyMarkdownNodeByName("genericInheritedExtensions", "Bar", defaultModelConfig) - } - - @Test fun arrayAverage() { - verifyMarkdownNodeByName("arrayAverage", "XArray", defaultModelConfig) - } - - @Test fun multipleTypeParameterConstraints() { - verifyMarkdownNode("multipleTypeParameterConstraints", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun inheritedCompanionObjectProperties() { - verifyMarkdownNodeByName("inheritedCompanionObjectProperties", "C", defaultModelConfig) - } - - @Test fun shadowedExtensionFunctions() { - verifyMarkdownNodeByName("shadowedExtensionFunctions", "Bar", defaultModelConfig) - } - - @Test fun inapplicableExtensionFunctions() { - verifyMarkdownNodeByName("inapplicableExtensionFunctions", "Bar", defaultModelConfig) - } - - @Test fun receiverParameterTypeBound() { - verifyMarkdownNodeByName("receiverParameterTypeBound", "Foo", defaultModelConfig) - } - - @Test fun extensionWithDocumentedReceiver() { - verifyMarkdownNodes("extensionWithDocumentedReceiver", defaultModelConfig) { model -> - model.members.single().members.single().members.filter { it.name == "fn" } - } - } - - @Test fun codeBlock() { - verifyMarkdownNode("codeBlock", defaultModelConfig) - } - - @Test fun exclInCodeBlock() { - verifyMarkdownNodeByName("exclInCodeBlock", "foo", defaultModelConfig) - } - - @Test fun backtickInCodeBlock() { - verifyMarkdownNodeByName("backtickInCodeBlock", "foo", defaultModelConfig) - } - - @Test fun qualifiedNameLink() { - verifyMarkdownNodeByName("qualifiedNameLink", "foo", - ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun functionalTypeWithNamedParameters() { - verifyMarkdownNode("functionalTypeWithNamedParameters", defaultModelConfig) - } - - @Test fun typeAliases() { - verifyMarkdownNode("typeAliases", defaultModelConfig) - verifyMarkdownPackage("typeAliases", defaultModelConfig) - } - - @Test fun sampleByShortName() { - verifyMarkdownNode("sampleByShortName", defaultModelConfig) - } - - - @Test fun suspendParam() { - verifyMarkdownNode("suspendParam", defaultModelConfig) - verifyMarkdownPackage("suspendParam", defaultModelConfig) - } - - @Test fun sinceKotlin() { - verifyMarkdownNode("sinceKotlin", defaultModelConfig) - verifyMarkdownPackage("sinceKotlin", defaultModelConfig) - } - - @Test fun sinceKotlinWide() { - verifyMarkdownPackage("sinceKotlinWide", defaultModelConfig) - } - - @Test fun dynamicType() { - verifyMarkdownNode("dynamicType", defaultModelConfig) - } - - @Test fun dynamicExtension() { - verifyMarkdownNodes("dynamicExtension", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Foo" } } - } - - @Test fun memberExtension() { - verifyMarkdownNodes("memberExtension", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Foo" } } - } - - @Test fun renderFunctionalTypeInParenthesisWhenItIsReceiver() { - verifyMarkdownNode("renderFunctionalTypeInParenthesisWhenItIsReceiver", defaultModelConfig) - } - - @Test fun multiplePlatforms() { - verifyMultiplatformPackage(buildMultiplePlatforms("multiplatform/simple"), "multiplatform/simple") - } - - @Test fun multiplePlatformsMerge() { - verifyMultiplatformPackage(buildMultiplePlatforms("multiplatform/merge"), "multiplatform/merge") - } - - @Test fun multiplePlatformsMergeMembers() { - val module = buildMultiplePlatforms("multiplatform/mergeMembers") - verifyModelOutput(module, ".md", "testdata/format/multiplatform/mergeMembers/foo.kt") { model, output -> - buildPagesAndReadInto(model.members.single().members, output) - } - } - - @Test fun multiplePlatformsOmitRedundant() { - val module = buildMultiplePlatforms("multiplatform/omitRedundant") - verifyModelOutput(module, ".md", "testdata/format/multiplatform/omitRedundant/foo.kt") { model, output -> - buildPagesAndReadInto(model.members.single().members, output) - } - } - - @Test fun multiplePlatformsImplied() { - val module = buildMultiplePlatforms("multiplatform/implied") - verifyModelOutput(module, ".md", "testdata/format/multiplatform/implied/foo.kt") { model, output -> - val service = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf("JVM", "JS")) - fileGenerator.formatService = service - buildPagesAndReadInto(model.members.single().members, output) - } - } - - @Test fun packagePlatformsWithExtExtensions() { - val path = "multiplatform/packagePlatformsWithExtExtensions" - val module = DocumentationModule("test") - val passConfiguration = PassConfigurationImpl( - noStdlibLink = true, - noJdkLink = true, - languageVersion = null, - apiVersion = null - ) - - val dokkaConfiguration = DokkaConfigurationImpl( - outputDir = "", - format = "html", - generateIndexPages = false, - passesConfigurations = listOf( - passConfiguration - ) - ) - - appendDocumentation(module, dokkaConfiguration, passConfiguration, ModelConfig( - roots = arrayOf(contentRootFromPath("testdata/format/$path/jvm.kt")), - defaultPlatforms = listOf("JVM"), - withKotlinRuntime = true, - analysisPlatform = analysisPlatform - ) - ) - verifyMultiplatformIndex(module, path) - verifyMultiplatformPackage(module, path) - } - - @Test fun multiplePlatformsPackagePlatformFromMembers() { - val path = "multiplatform/packagePlatformsFromMembers" - val module = buildMultiplePlatforms(path) - verifyMultiplatformIndex(module, path) - verifyMultiplatformPackage(module, path) - } - - @Test fun multiplePlatformsGroupNode() { - val path = "multiplatform/groupNode" - val module = buildMultiplePlatforms(path) - verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.kt") { model, output -> - buildPagesAndReadInto( - listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }), - output - ) - } - verifyMultiplatformPackage(module, path) - } - - @Test fun multiplePlatformsBreadcrumbsInMemberOfMemberOfGroupNode() { - val path = "multiplatform/breadcrumbsInMemberOfMemberOfGroupNode" - val module = buildMultiplePlatforms(path) - verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.kt") { model, output -> - buildPagesAndReadInto( - listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }?.member(NodeKind.Function)), - output - ) - } - } - - @Test fun linksInEmphasis() { - verifyMarkdownNode("linksInEmphasis", defaultModelConfig) - } - - @Test fun linksInStrong() { - verifyMarkdownNode("linksInStrong", defaultModelConfig) - } - - @Test fun linksInHeaders() { - verifyMarkdownNode("linksInHeaders", defaultModelConfig) - } - - @Test fun tokensInEmphasis() { - verifyMarkdownNode("tokensInEmphasis", defaultModelConfig) - } - - @Test fun tokensInStrong() { - verifyMarkdownNode("tokensInStrong", defaultModelConfig) - } - - @Test fun tokensInHeaders() { - verifyMarkdownNode("tokensInHeaders", defaultModelConfig) - } - - @Test fun unorderedLists() { - verifyMarkdownNode("unorderedLists", defaultModelConfig) - } - - @Test fun nestedLists() { - verifyMarkdownNode("nestedLists", defaultModelConfig) - } - - @Test fun referenceLink() { - verifyMarkdownNode("referenceLink", defaultModelConfig) - } - - @Test fun externalReferenceLink() { - verifyMarkdownNode("externalReferenceLink", defaultModelConfig) - } - - @Test fun newlineInTableCell() { - verifyMarkdownPackage("newlineInTableCell", defaultModelConfig) - } - - @Test fun indentedCodeBlock() { - verifyMarkdownNode("indentedCodeBlock", defaultModelConfig) - } - - @Test fun receiverReference() { - verifyMarkdownNode("receiverReference", defaultModelConfig) - } - - @Test fun extensionScope() { - verifyMarkdownNodeByName("extensionScope", "test", defaultModelConfig) - } - - @Test fun typeParameterReference() { - verifyMarkdownNode("typeParameterReference", defaultModelConfig) - } - - @Test fun notPublishedTypeAliasAutoExpansion() { - verifyMarkdownNodeByName("notPublishedTypeAliasAutoExpansion", "foo", ModelConfig( - analysisPlatform = analysisPlatform, - includeNonPublic = false - )) - } - - @Test fun companionImplements() { - verifyMarkdownNodeByName("companionImplements", "Foo", defaultModelConfig) - } - - - private fun buildMultiplePlatforms(path: String): DocumentationModule { - val moduleName = "test" - val passConfiguration = PassConfigurationImpl( - noStdlibLink = true, - noJdkLink = true, - languageVersion = null, - apiVersion = null - ) - val dokkaConfiguration = DokkaConfigurationImpl( - outputDir = "", - format = "html", - generateIndexPages = false, - passesConfigurations = listOf( - passConfiguration - ) - - ) - val module1 = DocumentationModule(moduleName) - appendDocumentation( - module1, dokkaConfiguration, passConfiguration, ModelConfig( - roots = arrayOf(contentRootFromPath("testdata/format/$path/jvm.kt")), - defaultPlatforms = listOf("JVM"), - analysisPlatform = Platform.jvm - ) - ) - - val module2 = DocumentationModule(moduleName) - appendDocumentation( - module2, dokkaConfiguration, passConfiguration, ModelConfig( - roots = arrayOf(contentRootFromPath("testdata/format/$path/js.kt")), - defaultPlatforms = listOf("JS"), - analysisPlatform = Platform.js - ) - ) - - return DocumentationMerger(listOf(module1, module2), DokkaConsoleLogger).merge() - } - - private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) { - verifyModelOutput(module, ".package.md", "testdata/format/$path/multiplatform.kt") { model, output -> - buildPagesAndReadInto(model.members, output) - } - } - - private fun verifyMultiplatformIndex(module: DocumentationModule, path: String) { - verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.index.kt") { - model, output -> - val service = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf()) - fileGenerator.formatService = service - buildPagesAndReadInto(listOf(model), output) - } - } - - @Test fun blankLineInsideCodeBlock() { - verifyMarkdownNode("blankLineInsideCodeBlock", defaultModelConfig) - } - - protected fun verifyMarkdownPackage(fileName: String, modelConfig: ModelConfig = ModelConfig()) { - verifyOutput("testdata/format/$fileName.kt", ".package.md", modelConfig) { model, output -> - buildPagesAndReadInto(model.members, output) - } - } - - protected fun verifyMarkdownNode(fileName: String, modelConfig: ModelConfig = ModelConfig()) { - verifyMarkdownNodes(fileName, modelConfig) { model -> model.members.single().members } - } - - protected fun verifyMarkdownNodes( - fileName: String, - modelConfig: ModelConfig = ModelConfig(), - nodeFilter: (DocumentationModule) -> List<DocumentationNode> - ) { - verifyOutput( - "testdata/format/$fileName.kt", - ".md", - modelConfig - ) { model, output -> - buildPagesAndReadInto(nodeFilter(model), output) - } - } - - protected fun verifyJavaMarkdownNode(fileName: String, modelConfig: ModelConfig = ModelConfig()) { - verifyJavaMarkdownNodes(fileName, modelConfig) { model -> model.members.single().members } - } - - protected fun verifyJavaMarkdownNodes(fileName: String, modelConfig: ModelConfig = ModelConfig(), nodeFilter: (DocumentationModule) -> List<DocumentationNode>) { - verifyJavaOutput("testdata/format/$fileName.java", ".md", modelConfig) { model, output -> - buildPagesAndReadInto(nodeFilter(model), output) - } - } - - protected fun verifyMarkdownNodeByName( - fileName: String, - name: String, - modelConfig: ModelConfig = ModelConfig() - ) { - verifyMarkdownNodes(fileName, modelConfig) { model-> - val nodesWithName = model.members.single().members.filter { it.name == name } - if (nodesWithName.isEmpty()) { - throw IllegalArgumentException("Found no nodes named $name") - } - nodesWithName - } - } - - @Test fun nullableTypeParameterFunction() { - verifyMarkdownNode("nullableTypeParameterFunction", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } -} - -class JSMarkdownFormatTest: BaseMarkdownFormatTest(Platform.js) - -class JVMMarkdownFormatTest: BaseMarkdownFormatTest(Platform.jvm) { - - @Test - fun enumRef() { - verifyMarkdownNode("enumRef", defaultModelConfig) - } - - @Test - fun javaCodeLiteralTags() { - verifyJavaMarkdownNode("javaCodeLiteralTags", defaultModelConfig) - } - - @Test - fun nullability() { - verifyMarkdownNode("nullability", defaultModelConfig) - } - - @Test - fun exceptionClass() { - verifyMarkdownNode( - "exceptionClass", ModelConfig( - analysisPlatform = analysisPlatform, - withKotlinRuntime = true - ) - ) - verifyMarkdownPackage( - "exceptionClass", ModelConfig( - analysisPlatform = analysisPlatform, - withKotlinRuntime = true - ) - ) - } - - @Test - fun operatorOverloading() { - verifyMarkdownNodes("operatorOverloading", defaultModelConfig) { model-> - model.members.single().members.single { it.name == "C" }.members.filter { it.name == "plus" } - } - } - - @Test - fun extensions() { - verifyOutput("testdata/format/extensions.kt", ".package.md", defaultModelConfig) { model, output -> - buildPagesAndReadInto(model.members, output) - } - verifyOutput("testdata/format/extensions.kt", ".class.md", defaultModelConfig) { model, output -> - buildPagesAndReadInto(model.members.single().members, output) - } - } - - @Test - fun summarizeSignaturesProperty() { - verifyMarkdownNodes("summarizeSignaturesProperty", defaultModelConfig) { model -> model.members } - } - - @Test - fun javaSpaceInAuthor() { - verifyJavaMarkdownNode("javaSpaceInAuthor", defaultModelConfig) - } - - @Test - fun javaCodeInParam() { - verifyJavaMarkdownNodes("javaCodeInParam", defaultModelConfig) { - selectNodes(it) { - subgraphOf(RefKind.Member) - withKind(NodeKind.Function) - } - } - } - - @Test - fun annotationParams() { - verifyMarkdownNode("annotationParams", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun inheritedLink() { - val filePath = "testdata/format/inheritedLink" - verifyOutput( - filePath, - ".md", - ModelConfig( - roots = arrayOf( - contentRootFromPath("$filePath.kt"), - contentRootFromPath("$filePath.1.kt") - ), - withJdk = true, - withKotlinRuntime = true, - includeNonPublic = false, - analysisPlatform = analysisPlatform - - ) - ) { model, output -> - buildPagesAndReadInto(model.members.single { it.name == "p2" }.members.single().members, output) - } - } - - @Test - fun javadocOrderedList() { - verifyJavaMarkdownNodes("javadocOrderedList", defaultModelConfig) { model -> - model.members.single().members.filter { it.name == "Bar" } - } - } - - @Test - fun jdkLinks() { - verifyMarkdownNode("jdkLinks", ModelConfig(withKotlinRuntime = true, analysisPlatform = analysisPlatform)) - } - - @Test - fun javadocHtml() { - verifyJavaMarkdownNode("javadocHtml", defaultModelConfig) - } -} - -class CommonMarkdownFormatTest: BaseMarkdownFormatTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/format/PackageDocsTest.kt b/core/src/test/kotlin/format/PackageDocsTest.kt deleted file mode 100644 index 3ff5f123..00000000 --- a/core/src/test/kotlin/format/PackageDocsTest.kt +++ /dev/null @@ -1,92 +0,0 @@ -package org.jetbrains.dokka.tests.format - -import com.intellij.openapi.Disposable -import com.intellij.openapi.util.Disposer -import com.nhaarman.mockito_kotlin.any -import com.nhaarman.mockito_kotlin.doAnswer -import com.nhaarman.mockito_kotlin.eq -import com.nhaarman.mockito_kotlin.mock -import org.jetbrains.dokka.* -import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators -import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreProjectEnvironment -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Before -import org.junit.Test -import java.io.File - -class PackageDocsTest { - - private lateinit var testDisposable: Disposable - - @Before - fun setup() { - testDisposable = Disposer.newDisposable() - } - - @After - fun cleanup() { - Disposer.dispose(testDisposable) - } - - fun createPackageDocs(linkResolver: DeclarationLinkResolver?): PackageDocs { - val environment = KotlinCoreEnvironment.createForTests(testDisposable, CompilerConfiguration.EMPTY, EnvironmentConfigFiles.JVM_CONFIG_FILES) - return PackageDocs(linkResolver, DokkaConsoleLogger, environment, mock(), mock()) - } - - @Test fun verifyParse() { - - val docs = createPackageDocs(null) - docs.parse("testdata/packagedocs/stdlib.md", emptyList()) - val packageContent = docs.packageContent["kotlin"]!! - val block = (packageContent.children.single() as ContentBlock).children.first() as ContentText - assertEquals("Core functions and types", block.text) - } - - @Test fun testReferenceLinksInPackageDocs() { - val mockLinkResolver = mock<DeclarationLinkResolver> { - val exampleCom = "https://example.com" - on { tryResolveContentLink(any(), eq(exampleCom)) } doAnswer { ContentExternalLink(exampleCom) } - } - - val mockPackageDescriptor = mock<PackageFragmentDescriptor> {} - - val docs = createPackageDocs(mockLinkResolver) - docs.parse("testdata/packagedocs/referenceLinks.md", listOf(mockPackageDescriptor)) - - checkMarkdownOutput(docs, "testdata/packagedocs/referenceLinks") - } - - fun checkMarkdownOutput(docs: PackageDocs, expectedFilePrefix: String) { - - val generator = FileGenerator(File("")) - - val out = StringBuilder() - val outputBuilder = MarkdownOutputBuilder( - out, - FileLocation(generator.root), - generator, - KotlinLanguageService(), - ".md", - emptyList() - ) - fun checkOutput(content: Content, filePostfix: String) { - outputBuilder.appendContent(content) - val expectedFile = File(expectedFilePrefix + filePostfix) - assertEqualsIgnoringSeparators(expectedFile, out.toString()) - out.setLength(0) - } - - checkOutput(docs.moduleContent, ".module.md") - - docs.packageContent.forEach { - (name, content) -> - checkOutput(content, ".$name.md") - } - - } -} diff --git a/core/src/test/kotlin/issues/IssuesTest.kt b/core/src/test/kotlin/issues/IssuesTest.kt deleted file mode 100644 index da5acd6e..00000000 --- a/core/src/test/kotlin/issues/IssuesTest.kt +++ /dev/null @@ -1,34 +0,0 @@ -package issues - -import org.jetbrains.dokka.DocumentationNode -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.tests.ModelConfig -import org.jetbrains.dokka.tests.checkSourceExistsAndVerifyModel -import org.junit.Test -import kotlin.test.assertEquals - -abstract class BaseIssuesTest(val analysisPlatform: Platform) { - val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - - @Test - fun errorClasses() { - checkSourceExistsAndVerifyModel("testdata/issues/errorClasses.kt", - modelConfig = ModelConfig(analysisPlatform = analysisPlatform, withJdk = true, withKotlinRuntime = true)) { model -> - val cls = model.members.single().members.single() - - fun DocumentationNode.returnType() = this.details.find { it.kind == NodeKind.Type }?.name - assertEquals("Test", cls.members[1].returnType()) - assertEquals("Test", cls.members[2].returnType()) - assertEquals("Test", cls.members[3].returnType()) - assertEquals("List", cls.members[4].returnType()) - assertEquals("String", cls.members[5].returnType()) - assertEquals("String", cls.members[6].returnType()) - assertEquals("String", cls.members[7].returnType()) - } - } -} - -class JSIssuesTest: BaseIssuesTest(Platform.js) -class JVMIssuesTest: BaseIssuesTest(Platform.jvm) -class CommonIssuesTest: BaseIssuesTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/javadoc/JavadocTest.kt b/core/src/test/kotlin/javadoc/JavadocTest.kt deleted file mode 100644 index 1c4dd258..00000000 --- a/core/src/test/kotlin/javadoc/JavadocTest.kt +++ /dev/null @@ -1,333 +0,0 @@ -package org.jetbrains.dokka.javadoc - -import com.sun.javadoc.Tag -import com.sun.javadoc.Type -import org.jetbrains.dokka.DokkaConsoleLogger -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.tests.ModelConfig -import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators -import org.jetbrains.dokka.tests.checkSourceExistsAndVerifyModel -import org.junit.Assert.* -import org.junit.Test -import java.lang.reflect.Modifier.* - -class JavadocTest { - val defaultModelConfig = ModelConfig(analysisPlatform = Platform.jvm) - - @Test fun testTypes() { - verifyJavadoc("testdata/javadoc/types.kt", ModelConfig(analysisPlatform = Platform.jvm, withJdk = true)) { doc -> - val classDoc = doc.classNamed("foo.TypesKt")!! - val method = classDoc.methods().find { it.name() == "foo" }!! - - val type = method.returnType() - assertFalse(type.asClassDoc().isIncluded) - assertEquals("java.lang.String", type.qualifiedTypeName()) - assertEquals("java.lang.String", type.asClassDoc().qualifiedName()) - - val params = method.parameters() - assertTrue(params[0].type().isPrimitive) - assertFalse(params[1].type().asClassDoc().isIncluded) - } - } - - @Test fun testObject() { - verifyJavadoc("testdata/javadoc/obj.kt", defaultModelConfig) { doc -> - val classDoc = doc.classNamed("foo.O") - assertNotNull(classDoc) - - val companionDoc = doc.classNamed("foo.O.Companion") - assertNotNull(companionDoc) - - val pkgDoc = doc.packageNamed("foo")!! - assertEquals(2, pkgDoc.allClasses().size) - } - } - - @Test fun testException() { - verifyJavadoc( - "testdata/javadoc/exception.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val classDoc = doc.classNamed("foo.MyException")!! - val member = classDoc.methods().find { it.name() == "foo" } - assertEquals(classDoc, member!!.containingClass()) - } - } - - @Test fun testByteArray() { - verifyJavadoc( - "testdata/javadoc/bytearr.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val classDoc = doc.classNamed("foo.ByteArray")!! - assertNotNull(classDoc.asClassDoc()) - - val member = classDoc.methods().find { it.name() == "foo" }!! - assertEquals("[]", member.returnType().dimension()) - } - } - - @Test fun testStringArray() { - verifyJavadoc( - "testdata/javadoc/stringarr.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val classDoc = doc.classNamed("foo.Foo")!! - assertNotNull(classDoc.asClassDoc()) - - val member = classDoc.methods().find { it.name() == "main" }!! - val paramType = member.parameters()[0].type() - assertNull(paramType.asParameterizedType()) - assertEquals("String[]", paramType.typeName()) - assertEquals("String", paramType.asClassDoc().name()) - } - } - - @Test fun testJvmName() { - verifyJavadoc( - "testdata/javadoc/jvmname.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val classDoc = doc.classNamed("foo.Apple")!! - assertNotNull(classDoc.asClassDoc()) - - val member = classDoc.methods().find { it.name() == "_tree" } - assertNotNull(member) - } - } - - @Test fun testLinkWithParam() { - verifyJavadoc( - "testdata/javadoc/paramlink.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val classDoc = doc.classNamed("demo.Apple")!! - assertNotNull(classDoc.asClassDoc()) - val tags = classDoc.inlineTags().filterIsInstance<SeeTagAdapter>() - assertEquals(2, tags.size) - val linkTag = tags[1] as SeeMethodTagAdapter - assertEquals("cutIntoPieces", linkTag.method.name()) - } - } - - @Test fun testInternalVisibility() { - verifyJavadoc( - "testdata/javadoc/internal.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true, includeNonPublic = false) - ) { doc -> - val classDoc = doc.classNamed("foo.Person")!! - val constructors = classDoc.constructors() - assertEquals(1, constructors.size) - assertEquals(1, constructors.single().parameters().size) - } - } - - @Test fun testSuppress() { - verifyJavadoc( - "testdata/javadoc/suppress.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - assertNull(doc.classNamed("Some")) - assertNull(doc.classNamed("SomeAgain")) - assertNull(doc.classNamed("Interface")) - val classSame = doc.classNamed("Same")!! - assertTrue(classSame.fields().isEmpty()) - assertTrue(classSame.methods().isEmpty()) - } - } - - @Test fun testTypeAliases() { - verifyJavadoc( - "testdata/javadoc/typealiases.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - assertNull(doc.classNamed("B")) - assertNull(doc.classNamed("D")) - - assertEquals("A", doc.classNamed("C")!!.superclass().name()) - val methodParamType = doc.classNamed("TypealiasesKt")!!.methods() - .find { it.name() == "some" }!!.parameters().first() - .type() - assertEquals("kotlin.jvm.functions.Function1", methodParamType.qualifiedTypeName()) - assertEquals("? super A, C", - methodParamType.asParameterizedType().typeArguments().joinToString(transform = Type::qualifiedTypeName) - ) - } - } - - @Test fun testKDocKeywordsOnMethod() { - verifyJavadoc( - "testdata/javadoc/kdocKeywordsOnMethod.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val method = doc.classNamed("KdocKeywordsOnMethodKt")!!.methods()[0] - assertEquals("@return [ContentText(text=value of a)]", method.tags("return").first().text()) - assertEquals("@param a [ContentText(text=Some string)]", method.paramTags().first().text()) - assertEquals("@throws FireException [ContentText(text=in case of fire)]", method.throwsTags().first().text()) - } - } - - @Test - fun testBlankLineInsideCodeBlock() { - verifyJavadoc( - "testdata/javadoc/blankLineInsideCodeBlock.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val method = doc.classNamed("BlankLineInsideCodeBlockKt")!!.methods()[0] - val text = method.inlineTags().joinToString(separator = "", transform = Tag::text) - assertEqualsIgnoringSeparators(""" - <p><code><pre> - This is a test - of Dokka's code blocks. - Here is a blank line. - - The previous line was blank. - </pre></code></p> - """.trimIndent(), text) - } - } - - @Test - fun testCompanionMethodReference() { - verifyJavadoc("testdata/javadoc/companionMethodReference.kt", defaultModelConfig) { doc -> - val classDoc = doc.classNamed("foo.TestClass")!! - val tag = classDoc.inlineTags().filterIsInstance<SeeMethodTagAdapter>().first() - assertEquals("TestClass.Companion", tag.referencedClassName()) - assertEquals("test", tag.referencedMemberName()) - } - } - - @Test - fun testVararg() { - verifyJavadoc("testdata/javadoc/vararg.kt") { doc -> - val classDoc = doc.classNamed("VarargKt")!! - val methods = classDoc.methods() - methods.single { it.name() == "vararg" }.let { method -> - assertTrue(method.isVarArgs) - assertEquals("int", method.parameters().last().typeName()) - } - methods.single { it.name() == "varargInMiddle" }.let { method -> - assertFalse(method.isVarArgs) - assertEquals("int[]", method.parameters()[1].typeName()) - } - } - } - - @Test - fun shouldHaveValidVisibilityModifiers() { - verifyJavadoc("testdata/javadoc/visibilityModifiers.kt", ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true)) { doc -> - val classDoc = doc.classNamed("foo.Apple")!! - val methods = classDoc.methods() - - val getName = methods[0] - val setName = methods[1] - val getWeight = methods[2] - val setWeight = methods[3] - val getRating = methods[4] - val setRating = methods[5] - val getCode = methods[6] - val color = classDoc.fields()[3] - val code = classDoc.fields()[4] - - assertTrue(getName.isProtected) - assertEquals(PROTECTED, getName.modifierSpecifier()) - assertTrue(setName.isProtected) - assertEquals(PROTECTED, setName.modifierSpecifier()) - - assertTrue(getWeight.isPublic) - assertEquals(PUBLIC, getWeight.modifierSpecifier()) - assertTrue(setWeight.isPublic) - assertEquals(PUBLIC, setWeight.modifierSpecifier()) - - assertTrue(getRating.isPublic) - assertEquals(PUBLIC, getRating.modifierSpecifier()) - assertTrue(setRating.isPublic) - assertEquals(PUBLIC, setRating.modifierSpecifier()) - - assertTrue(getCode.isPublic) - assertEquals(PUBLIC or STATIC, getCode.modifierSpecifier()) - - assertEquals(methods.size, 7) - - assertTrue(color.isPrivate) - assertEquals(PRIVATE, color.modifierSpecifier()) - - assertTrue(code.isPrivate) - assertTrue(code.isStatic) - assertEquals(PRIVATE or STATIC, code.modifierSpecifier()) - } - } - - @Test - fun shouldNotHaveDuplicatedConstructorParameters() { - verifyJavadoc("testdata/javadoc/constructorParameters.kt") { doc -> - val classDoc = doc.classNamed("bar.Banana")!! - val paramTags = classDoc.constructors()[0].paramTags() - - assertEquals(3, paramTags.size) - } - } - - @Test fun shouldHaveAllFunctionMarkedAsDeprecated() { - verifyJavadoc("testdata/javadoc/deprecated.java") { doc -> - val classDoc = doc.classNamed("bar.Banana")!! - - classDoc.methods().forEach { method -> - assertTrue(method.tags().any { it.kind() == "deprecated" }) - } - } - } - - @Test - fun testDefaultNoArgConstructor() { - verifyJavadoc("testdata/javadoc/defaultNoArgConstructor.kt") { doc -> - val classDoc = doc.classNamed("foo.Peach")!! - assertTrue(classDoc.constructors()[0].tags()[2].text() == "print peach") - } - } - - @Test - fun testNoArgConstructor() { - verifyJavadoc("testdata/javadoc/noArgConstructor.kt") { doc -> - val classDoc = doc.classNamed("foo.Plum")!! - assertTrue(classDoc.constructors()[0].tags()[2].text() == "print plum") - } - } - - @Test - fun testArgumentReference() { - verifyJavadoc("testdata/javadoc/argumentReference.kt") { doc -> - val classDoc = doc.classNamed("ArgumentReferenceKt")!! - val method = classDoc.methods().first() - val tag = method.seeTags().first() - assertEquals("argNamedError", tag.referencedMemberName()) - assertEquals("error", tag.label()) - } - } - - @Test - fun functionParameters() { - verifyJavadoc("testdata/javadoc/functionParameters.java") { doc -> - val tags = doc.classNamed("bar.Foo")!!.methods().first().paramTags() - assertEquals((tags.first() as ParamTagAdapter).content.size, 1) - assertEquals((tags[1] as ParamTagAdapter).content.size, 1) - } - } - - private fun verifyJavadoc(name: String, - modelConfig: ModelConfig = ModelConfig(), - callback: (ModuleNodeAdapter) -> Unit) { - - checkSourceExistsAndVerifyModel(name, - ModelConfig( - analysisPlatform = Platform.jvm, - format = "javadoc", - withJdk = modelConfig.withJdk, - withKotlinRuntime = modelConfig.withKotlinRuntime, - includeNonPublic = modelConfig.includeNonPublic - )) { model -> - val doc = ModuleNodeAdapter(model, StandardReporter(DokkaConsoleLogger), "") - callback(doc) - } - } -} diff --git a/core/src/test/kotlin/markdown/ParserTest.kt b/core/src/test/kotlin/markdown/ParserTest.kt deleted file mode 100644 index b0ec68ff..00000000 --- a/core/src/test/kotlin/markdown/ParserTest.kt +++ /dev/null @@ -1,154 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.junit.Test -import org.jetbrains.dokka.toTestString -import org.jetbrains.dokka.parseMarkdown -import org.junit.Ignore - -@Ignore public class ParserTest { - fun runTestFor(text : String) { - println("MD: ---") - println(text) - val markdownTree = parseMarkdown(text) - println("AST: ---") - println(markdownTree.toTestString()) - println() - } - - @Test fun text() { - runTestFor("text") - } - - @Test fun textWithSpaces() { - runTestFor("text and string") - } - - @Test fun textWithColon() { - runTestFor("text and string: cool!") - } - - @Test fun link() { - runTestFor("text [links]") - } - - @Test fun linkWithHref() { - runTestFor("text [links](http://google.com)") - } - - @Test fun multiline() { - runTestFor( - """ -text -and -string -""") - } - - @Test fun para() { - runTestFor( - """ -paragraph number -one - -paragraph -number two -""") - } - - @Test fun bulletList() { - runTestFor( - """* list item 1 -* list item 2 -""") - } - - @Test fun bulletListWithLines() { - runTestFor( - """ -* list item 1 - continue 1 -* list item 2 - continue 2 - """) - } - - @Test fun bulletListStrong() { - runTestFor( - """ -* list *item* 1 - continue 1 -* list *item* 2 - continue 2 - """) - } - - @Test fun emph() { - runTestFor("*text*") - } - - @Test fun underscoresNoEmph() { - runTestFor("text_with_underscores") - } - - @Test fun emphUnderscores() { - runTestFor("_text_") - } - - @Test fun singleStar() { - runTestFor("Embedded*Star") - } - - @Test fun directive() { - runTestFor("A text \${code with.another.value} with directive") - } - - @Test fun emphAndEmptySection() { - runTestFor("*text*\n\$sec:\n") - } - - @Test fun emphAndSection() { - runTestFor("*text*\n\$sec: some text\n") - } - - @Test fun emphAndBracedSection() { - runTestFor("Text *bold* text \n\${sec}: some text") - } - - @Test fun section() { - runTestFor( - "Plain text \n\$one: Summary \n\${two}: Description with *emphasis* \n\${An example of a section}: Example") - } - - @Test fun anonymousSection() { - runTestFor("Summary\n\nDescription\n") - } - - @Test fun specialSection() { - runTestFor( - "Plain text \n\$\$summary: Summary \n\${\$description}: Description \n\${\$An example of a section}: Example") - } - - @Test fun emptySection() { - runTestFor( - "Plain text \n\$summary:") - } - - val b = "$" - @Test fun pair() { - runTestFor( - """Represents a generic pair of two values. - -There is no meaning attached to values in this class, it can be used for any purpose. -Pair exhibits value semantics, i.e. two pairs are equal if both components are equal. - -An example of decomposing it into values: -${b}{code test.tuples.PairTest.pairMultiAssignment} - -${b}constructor: Creates new instance of [Pair] -${b}first: First value -${b}second: Second value"""" - ) - } - -} - diff --git a/core/src/test/kotlin/model/ClassTest.kt b/core/src/test/kotlin/model/ClassTest.kt deleted file mode 100644 index 35ec1d09..00000000 --- a/core/src/test/kotlin/model/ClassTest.kt +++ /dev/null @@ -1,318 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.Content -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.RefKind -import org.junit.Assert -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test - -abstract class BaseClassTest(val analysisPlatform: Platform) { - protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - @Test fun emptyClass() { - checkSourceExistsAndVerifyModel("testdata/classes/emptyClass.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(NodeKind.Class, kind) - assertEquals("Klass", name) - assertEquals(Content.Empty, content) - assertEquals("<init>", members.single().name) - assertTrue(links.none()) - } - } - } - - @Test fun emptyObject() { - checkSourceExistsAndVerifyModel("testdata/classes/emptyObject.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(NodeKind.Object, kind) - assertEquals("Obj", name) - assertEquals(Content.Empty, content) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun classWithConstructor() { - checkSourceExistsAndVerifyModel("testdata/classes/classWithConstructor.kt", defaultModelConfig) { model -> - with (model.members.single().members.single()) { - assertEquals(NodeKind.Class, kind) - assertEquals("Klass", name) - assertEquals(Content.Empty, content) - assertTrue(links.none()) - - assertEquals(1, members.count()) - with(members.elementAt(0)) { - assertEquals("<init>", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Constructor, kind) - assertEquals(3, details.count()) - assertEquals("public", details.elementAt(0).name) - with(details.elementAt(2)) { - assertEquals("name", name) - assertEquals(NodeKind.Parameter, kind) - assertEquals(Content.Empty, content) - assertEquals("String", detail(NodeKind.Type).name) - assertTrue(links.none()) - assertTrue(members.none()) - } - assertTrue(links.none()) - assertTrue(members.none()) - } - } - } - } - - @Test fun classWithFunction() { - checkSourceExistsAndVerifyModel("testdata/classes/classWithFunction.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(NodeKind.Class, kind) - assertEquals("Klass", name) - assertEquals(Content.Empty, content) - assertTrue(links.none()) - - assertEquals(2, members.count()) - with(members.elementAt(0)) { - assertEquals("<init>", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Constructor, kind) - assertEquals(2, details.count()) - assertEquals("public", details.elementAt(0).name) - assertTrue(links.none()) - assertTrue(members.none()) - } - with(members.elementAt(1)) { - assertEquals("fn", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Function, kind) - assertEquals("Unit", detail(NodeKind.Type).name) - assertTrue(links.none()) - assertTrue(members.none()) - } - } - } - } - - @Test fun classWithProperty() { - checkSourceExistsAndVerifyModel("testdata/classes/classWithProperty.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(NodeKind.Class, kind) - assertEquals("Klass", name) - assertEquals(Content.Empty, content) - assertTrue(links.none()) - - assertEquals(2, members.count()) - with(members.elementAt(0)) { - assertEquals("<init>", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Constructor, kind) - assertEquals(2, details.count()) - assertEquals("public", details.elementAt(0).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - with(members.elementAt(1)) { - assertEquals("name", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Property, kind) - assertEquals("String", detail(NodeKind.Type).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - } - - @Test fun classWithCompanionObject() { - checkSourceExistsAndVerifyModel("testdata/classes/classWithCompanionObject.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(NodeKind.Class, kind) - assertEquals("Klass", name) - assertEquals(Content.Empty, content) - assertTrue(links.none()) - - assertEquals(3, members.count()) - with(members.elementAt(0)) { - assertEquals("<init>", name) - assertEquals(Content.Empty, content) - } - with(members.elementAt(1)) { - assertEquals("x", name) - assertEquals(NodeKind.CompanionObjectProperty, kind) - assertTrue(members.none()) - assertTrue(links.none()) - } - with(members.elementAt(2)) { - assertEquals("foo", name) - assertEquals(NodeKind.CompanionObjectFunction, kind) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - } - - @Test fun dataClass() { - verifyPackageMember("testdata/classes/dataClass.kt", defaultModelConfig) { cls -> - val modifiers = cls.details(NodeKind.Modifier).map { it.name } - assertTrue("data" in modifiers) - } - } - - @Test fun sealedClass() { - verifyPackageMember("testdata/classes/sealedClass.kt", defaultModelConfig) { cls -> - val modifiers = cls.details(NodeKind.Modifier).map { it.name } - assertEquals(1, modifiers.count { it == "sealed" }) - } - } - - @Test fun annotatedClassWithAnnotationParameters() { - checkSourceExistsAndVerifyModel( - "testdata/classes/annotatedClassWithAnnotationParameters.kt", - defaultModelConfig - ) { model -> - with(model.members.single().members.single()) { - with(deprecation!!) { - assertEquals("Deprecated", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Annotation, kind) - assertEquals(1, details.count()) - with(details[0]) { - assertEquals(NodeKind.Parameter, kind) - assertEquals(1, details.count()) - with(details[0]) { - assertEquals(NodeKind.Value, kind) - assertEquals("\"should no longer be used\"", name) - } - } - } - } - } - } - - @Test fun notOpenClass() { - checkSourceExistsAndVerifyModel("testdata/classes/notOpenClass.kt", defaultModelConfig) { model -> - with(model.members.single().members.first { it.name == "D"}.members.first { it.name == "f" }) { - val modifiers = details(NodeKind.Modifier) - assertEquals(2, modifiers.size) - assertEquals("final", modifiers[1].name) - - val overrideReferences = references(RefKind.Override) - assertEquals(1, overrideReferences.size) - } - } - } - - @Test fun indirectOverride() { - checkSourceExistsAndVerifyModel("testdata/classes/indirectOverride.kt", defaultModelConfig) { model -> - with(model.members.single().members.first { it.name == "E"}.members.first { it.name == "foo" }) { - val modifiers = details(NodeKind.Modifier) - assertEquals(2, modifiers.size) - assertEquals("final", modifiers[1].name) - - val overrideReferences = references(RefKind.Override) - assertEquals(1, overrideReferences.size) - } - } - } - - @Test fun innerClass() { - verifyPackageMember("testdata/classes/innerClass.kt", defaultModelConfig) { cls -> - val innerClass = cls.members.single { it.name == "D" } - val modifiers = innerClass.details(NodeKind.Modifier) - assertEquals(3, modifiers.size) - assertEquals("inner", modifiers[2].name) - } - } - - @Test fun companionObjectExtension() { - checkSourceExistsAndVerifyModel("testdata/classes/companionObjectExtension.kt", defaultModelConfig) { model -> - val pkg = model.members.single() - val cls = pkg.members.single { it.name == "Foo" } - val extensions = cls.extensions.filter { it.kind == NodeKind.CompanionObjectProperty } - assertEquals(1, extensions.size) - } - } - - @Test fun secondaryConstructor() { - verifyPackageMember("testdata/classes/secondaryConstructor.kt", defaultModelConfig) { cls -> - val constructors = cls.members(NodeKind.Constructor) - assertEquals(2, constructors.size) - with (constructors.first { it.details(NodeKind.Parameter).size == 1}) { - assertEquals("<init>", name) - assertEquals("This is a secondary constructor.", summary.toTestString()) - } - } - } - - @Test fun sinceKotlin() { - checkSourceExistsAndVerifyModel("testdata/classes/sinceKotlin.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("1.1", sinceKotlin) - } - } - } - - @Test fun privateCompanionObject() { - checkSourceExistsAndVerifyModel( - "testdata/classes/privateCompanionObject.kt", - modelConfig = ModelConfig(analysisPlatform = analysisPlatform, includeNonPublic = false) - ) { model -> - with(model.members.single().members.single()) { - assertEquals(0, members(NodeKind.CompanionObjectFunction).size) - assertEquals(0, members(NodeKind.CompanionObjectProperty).size) - } - } - } - -} - -class JSClassTest: BaseClassTest(Platform.js) {} - -class JVMClassTest: BaseClassTest(Platform.jvm) { - @Test - fun annotatedClass() { - verifyPackageMember("testdata/classes/annotatedClass.kt", ModelConfig( - analysisPlatform = analysisPlatform, - withKotlinRuntime = true - ) - ) { cls -> - Assert.assertEquals(1, cls.annotations.count()) - with(cls.annotations[0]) { - Assert.assertEquals("Strictfp", name) - Assert.assertEquals(Content.Empty, content) - Assert.assertEquals(NodeKind.Annotation, kind) - } - } - } - - - @Test fun javaAnnotationClass() { - checkSourceExistsAndVerifyModel( - "testdata/classes/javaAnnotationClass.kt", - modelConfig = ModelConfig(analysisPlatform = analysisPlatform, withJdk = true) - ) { model -> - with(model.members.single().members.single()) { - Assert.assertEquals(1, annotations.count()) - with(annotations[0]) { - Assert.assertEquals("Retention", name) - Assert.assertEquals(Content.Empty, content) - Assert.assertEquals(NodeKind.Annotation, kind) - with(details[0]) { - Assert.assertEquals(NodeKind.Parameter, kind) - Assert.assertEquals(1, details.count()) - with(details[0]) { - Assert.assertEquals(NodeKind.Value, kind) - Assert.assertEquals("RetentionPolicy.SOURCE", name) - } - } - } - } - } - } - -} - -class CommonClassTest: BaseClassTest(Platform.common) {}
\ No newline at end of file diff --git a/core/src/test/kotlin/model/CommentTest.kt b/core/src/test/kotlin/model/CommentTest.kt deleted file mode 100644 index 08aa3572..00000000 --- a/core/src/test/kotlin/model/CommentTest.kt +++ /dev/null @@ -1,190 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.junit.Test -import org.junit.Assert.* -import org.jetbrains.dokka.* - -abstract class BaseCommentTest(val analysisPlatform: Platform) { - val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - @Test fun codeBlockComment() { - checkSourceExistsAndVerifyModel("testdata/comments/codeBlockComment.kt", defaultModelConfig) { model -> - with(model.members.single().members.first()) { - assertEqualsIgnoringSeparators("""[code lang=brainfuck] - | - |++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. - | - |[/code] - |""".trimMargin(), - content.toTestString()) - } - with(model.members.single().members.last()) { - assertEqualsIgnoringSeparators("""[code] - | - |a + b - c - | - |[/code] - |""".trimMargin(), - content.toTestString()) - } - } - } - - @Test fun emptyDoc() { - checkSourceExistsAndVerifyModel("testdata/comments/emptyDoc.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(Content.Empty, content) - } - } - } - - @Test fun emptyDocButComment() { - checkSourceExistsAndVerifyModel("testdata/comments/emptyDocButComment.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(Content.Empty, content) - } - } - } - - @Test fun multilineDoc() { - checkSourceExistsAndVerifyModel("testdata/comments/multilineDoc.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("doc1", content.summary.toTestString()) - assertEquals("doc2\ndoc3", content.description.toTestString()) - } - } - } - - @Test fun multilineDocWithComment() { - checkSourceExistsAndVerifyModel("testdata/comments/multilineDocWithComment.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("doc1", content.summary.toTestString()) - assertEquals("doc2\ndoc3", content.description.toTestString()) - } - } - } - - @Test fun oneLineDoc() { - checkSourceExistsAndVerifyModel("testdata/comments/oneLineDoc.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("doc", content.summary.toTestString()) - } - } - } - - @Test fun oneLineDocWithComment() { - checkSourceExistsAndVerifyModel("testdata/comments/oneLineDocWithComment.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("doc", content.summary.toTestString()) - } - } - } - - @Test fun oneLineDocWithEmptyLine() { - checkSourceExistsAndVerifyModel("testdata/comments/oneLineDocWithEmptyLine.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("doc", content.summary.toTestString()) - } - } - } - - @Test fun emptySection() { - checkSourceExistsAndVerifyModel("testdata/comments/emptySection.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Summary", content.summary.toTestString()) - assertEquals(1, content.sections.count()) - with (content.findSectionByTag("one")!!) { - assertEquals("One", tag) - assertEquals("", toTestString()) - } - } - } - } - - @Test fun quotes() { - checkSourceExistsAndVerifyModel("testdata/comments/quotes.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("it's \"useful\"", content.summary.toTestString()) - } - } - } - - @Test fun section1() { - checkSourceExistsAndVerifyModel("testdata/comments/section1.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Summary", content.summary.toTestString()) - assertEquals(1, content.sections.count()) - with (content.findSectionByTag("one")!!) { - assertEquals("One", tag) - assertEquals("section one", toTestString()) - } - } - } - } - - @Test fun section2() { - checkSourceExistsAndVerifyModel("testdata/comments/section2.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Summary", content.summary.toTestString()) - assertEquals(2, content.sections.count()) - with (content.findSectionByTag("one")!!) { - assertEquals("One", tag) - assertEquals("section one", toTestString()) - } - with (content.findSectionByTag("two")!!) { - assertEquals("Two", tag) - assertEquals("section two", toTestString()) - } - } - } - } - - @Test fun multilineSection() { - checkSourceExistsAndVerifyModel("testdata/comments/multilineSection.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Summary", content.summary.toTestString()) - assertEquals(1, content.sections.count()) - with (content.findSectionByTag("one")!!) { - assertEquals("One", tag) - assertEquals("""line one -line two""", toTestString()) - } - } - } - } - - @Test fun directive() { - checkSourceExistsAndVerifyModel("testdata/comments/directive.kt", defaultModelConfig) { model -> - with(model.members.single().members.first()) { - assertEquals("Summary", content.summary.toTestString()) - with (content.description) { - assertEqualsIgnoringSeparators(""" - |[code lang=kotlin] - |if (true) { - | println(property) - |} - |[/code] - |[code lang=kotlin] - |if (true) { - | println(property) - |} - |[/code] - |[code lang=kotlin] - |if (true) { - | println(property) - |} - |[/code] - |[code lang=kotlin] - |if (true) { - | println(property) - |} - |[/code] - |""".trimMargin(), toTestString()) - } - } - } - } -} - -class JSCommentTest: BaseCommentTest(Platform.js) -class JVMCommentTest: BaseCommentTest(Platform.jvm) -class CommonCommentTest: BaseCommentTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/model/DocumentableTest.kt b/core/src/test/kotlin/model/DocumentableTest.kt new file mode 100644 index 00000000..a801d549 --- /dev/null +++ b/core/src/test/kotlin/model/DocumentableTest.kt @@ -0,0 +1,108 @@ +package model + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.PropertyContainer +import kotlin.test.Test +import kotlin.test.assertEquals + +class DocumentableTest { + + @Test + fun withDescendents() { + val dClass = DClass( + dri = DRI(), + name = "TestClass", + constructors = emptyList(), + classlikes = emptyList(), + companion = null, + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + visibility = emptyMap(), + generics = emptyList(), + modifier = emptyMap(), + properties = emptyList(), + sources = emptyMap(), + sourceSets = emptySet(), + supertypes = emptyMap(), + functions = listOf( + DFunction( + dri = DRI(), + name = "function0", + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + visibility = emptyMap(), + generics = emptyList(), + modifier = emptyMap(), + sources = emptyMap(), + sourceSets = emptySet(), + type = Void, + receiver = null, + isConstructor = false, + parameters = listOf( + DParameter( + dri = DRI(), + name = "f0p0", + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + sourceSets = emptySet(), + type = Void + ), + DParameter( + dri = DRI(), + name = "f0p1", + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + sourceSets = emptySet(), + type = Void + ) + ) + ), + DFunction( + dri = DRI(), + name = "function1", + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + visibility = emptyMap(), + generics = emptyList(), + modifier = emptyMap(), + sources = emptyMap(), + sourceSets = emptySet(), + type = Void, + receiver = null, + isConstructor = false, + parameters = listOf( + DParameter( + dri = DRI(), + name = "f1p0", + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + sourceSets = emptySet(), + type = Void + ), + DParameter( + dri = DRI(), + name = "f1p1", + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + sourceSets = emptySet(), + type = Void + ) + ) + ) + ) + ) + + assertEquals( + listOf("TestClass", "function0", "f0p0", "f0p1", "function1", "f1p0", "f1p1"), + dClass.withDescendants().map { it.name }.toList() + ) + } +}
\ No newline at end of file diff --git a/core/src/test/kotlin/model/FunctionTest.kt b/core/src/test/kotlin/model/FunctionTest.kt deleted file mode 100644 index 4c6bfb74..00000000 --- a/core/src/test/kotlin/model/FunctionTest.kt +++ /dev/null @@ -1,281 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.Content -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.Platform -import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters -import org.junit.Assert -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test - -abstract class BaseFunctionTest(val analysisPlatform: Platform) { - protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - @Test fun function() { - checkSourceExistsAndVerifyModel("testdata/functions/function.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("fn", name) - assertEquals(NodeKind.Function, kind) - assertEquals("Function fn", content.summary.toTestString()) - assertEquals("Unit", detail(NodeKind.Type).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun functionWithReceiver() { - checkSourceExistsAndVerifyModel("testdata/functions/functionWithReceiver.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("kotlin.String", name) - assertEquals(NodeKind.ExternalClass, kind) - assertEquals(2, members.count()) - with(members[0]) { - assertEquals("fn", name) - assertEquals(NodeKind.Function, kind) - assertEquals("Function with receiver", content.summary.toTestString()) - assertEquals("public", details.elementAt(0).name) - assertEquals("final", details.elementAt(1).name) - with(details.elementAt(3)) { - assertEquals("<this>", name) - assertEquals(NodeKind.Receiver, kind) - assertEquals(Content.Empty, content) - assertEquals("String", details.single().name) - assertTrue(members.none()) - assertTrue(links.none()) - } - assertEquals("Unit", details.elementAt(4).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - with(members[1]) { - assertEquals("fn", name) - assertEquals(NodeKind.Function, kind) - } - } - } - } - - @Test fun genericFunction() { - checkSourceExistsAndVerifyModel("testdata/functions/genericFunction.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("generic", name) - assertEquals(NodeKind.Function, kind) - assertEquals("generic function", content.summary.toTestString()) - - assertEquals("private", details.elementAt(0).name) - assertEquals("final", details.elementAt(1).name) - with(details.elementAt(3)) { - assertEquals("T", name) - assertEquals(NodeKind.TypeParameter, kind) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - assertEquals("Unit", details.elementAt(4).name) - - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - @Test fun genericFunctionWithConstraints() { - checkSourceExistsAndVerifyModel("testdata/functions/genericFunctionWithConstraints.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("generic", name) - assertEquals(NodeKind.Function, kind) - assertEquals("generic function", content.summary.toTestString()) - - val functionDetails = details - assertEquals("public", functionDetails.elementAt(0).name) - assertEquals("final", functionDetails.elementAt(1).name) - with(functionDetails.elementAt(3)) { - assertEquals("T", name) - assertEquals(NodeKind.TypeParameter, kind) - assertEquals(Content.Empty, content) - with(details.single()) { - assertEquals("R", name) - assertEquals(NodeKind.UpperBound, kind) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.singleOrNull() == functionDetails.elementAt(4)) - } - assertTrue(members.none()) - assertTrue(links.none()) - } - with(functionDetails.elementAt(4)) { - assertEquals("R", name) - assertEquals(NodeKind.TypeParameter, kind) - assertEquals(Content.Empty, content) - assertTrue(members.none()) - assertTrue(links.none()) - } - assertEquals("Unit", functionDetails.elementAt(5).name) - - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun functionWithParams() { - checkSourceExistsAndVerifyModel("testdata/functions/functionWithParams.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("function", name) - assertEquals(NodeKind.Function, kind) - assertEquals("Multiline", content.summary.toTestString()) - assertEquals("""Function -Documentation""", content.description.toTestString()) - - assertEquals("public", details.elementAt(0).name) - assertEquals("final", details.elementAt(1).name) - with(details.elementAt(3)) { - assertEquals("x", name) - assertEquals(NodeKind.Parameter, kind) - assertEquals("parameter", content.summary.toTestString()) - assertEquals("Int", detail(NodeKind.Type).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - assertEquals("Unit", details.elementAt(4).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun functionWithNotDocumentedAnnotation() { - verifyPackageMember("testdata/functions/functionWithNotDocumentedAnnotation.kt", defaultModelConfig) { func -> - assertEquals(0, func.annotations.count()) - } - } - - @Test fun inlineFunction() { - verifyPackageMember("testdata/functions/inlineFunction.kt", defaultModelConfig) { func -> - val modifiers = func.details(NodeKind.Modifier).map { it.name } - assertTrue("inline" in modifiers) - } - } - - @Test fun suspendFunction() { - verifyPackageMember("testdata/functions/suspendFunction.kt") { func -> - val modifiers = func.details(NodeKind.Modifier).map { it.name } - assertTrue("suspend" in modifiers) - } - } - - @Test fun suspendInlineFunctionOrder() { - verifyPackageMember("testdata/functions/suspendInlineFunction.kt") { func -> - val modifiers = func.details(NodeKind.Modifier).map { it.name }.filter { - it == "suspend" || it == "inline" - } - - assertEquals(listOf("suspend", "inline"), modifiers) - } - } - - @Test fun inlineSuspendFunctionOrderChanged() { - verifyPackageMember("testdata/functions/inlineSuspendFunction.kt") { func -> - val modifiers = func.details(NodeKind.Modifier).map { it.name }.filter { - it == "suspend" || it == "inline" - } - - assertEquals(listOf("suspend", "inline"), modifiers) - } - } - - @Test fun functionWithAnnotatedParam() { - checkSourceExistsAndVerifyModel("testdata/functions/functionWithAnnotatedParam.kt", defaultModelConfig) { model -> - with(model.members.single().members.single { it.name == "function" }) { - with(details(NodeKind.Parameter).first()) { - assertEquals(1, annotations.count()) - with(annotations[0]) { - assertEquals("Fancy", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Annotation, kind) - } - } - } - } - } - - @Test fun functionWithNoinlineParam() { - verifyPackageMember("testdata/functions/functionWithNoinlineParam.kt", defaultModelConfig) { func -> - with(func.details(NodeKind.Parameter).first()) { - val modifiers = details(NodeKind.Modifier).map { it.name } - assertTrue("noinline" in modifiers) - } - } - } - - @Test fun annotatedFunctionWithAnnotationParameters() { - checkSourceExistsAndVerifyModel( - "testdata/functions/annotatedFunctionWithAnnotationParameters.kt", - defaultModelConfig - ) { model -> - with(model.members.single().members.single { it.name == "f" }) { - assertEquals(1, annotations.count()) - with(annotations[0]) { - assertEquals("Fancy", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Annotation, kind) - assertEquals(1, details.count()) - with(details[0]) { - assertEquals(NodeKind.Parameter, kind) - assertEquals(1, details.count()) - with(details[0]) { - assertEquals(NodeKind.Value, kind) - assertEquals("1", name) - } - } - } - } - } - } - - @Test fun functionWithDefaultParameter() { - checkSourceExistsAndVerifyModel("testdata/functions/functionWithDefaultParameter.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - with(details.elementAt(3)) { - val value = details(NodeKind.Value) - assertEquals(1, value.count()) - with(value[0]) { - assertEquals("\"\"", name) - } - } - } - } - } - - @Test fun sinceKotlin() { - checkSourceExistsAndVerifyModel("testdata/functions/sinceKotlin.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("1.1", sinceKotlin) - } - } - } -} - -class JSFunctionTest: BaseFunctionTest(Platform.js) - -class JVMFunctionTest: BaseFunctionTest(Platform.jvm) { - @Test - fun annotatedFunction() { - verifyPackageMember("testdata/functions/annotatedFunction.kt", ModelConfig( - analysisPlatform = Platform.jvm, - withKotlinRuntime = true - )) { func -> - Assert.assertEquals(1, func.annotations.count()) - with(func.annotations[0]) { - Assert.assertEquals("Strictfp", name) - Assert.assertEquals(Content.Empty, content) - Assert.assertEquals(NodeKind.Annotation, kind) - } - } - } - -} - -class CommonFunctionTest: BaseFunctionTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/model/JavaTest.kt b/core/src/test/kotlin/model/JavaTest.kt deleted file mode 100644 index da9da624..00000000 --- a/core/src/test/kotlin/model/JavaTest.kt +++ /dev/null @@ -1,210 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.RefKind -import org.junit.Assert.* -import org.junit.Ignore -import org.junit.Test - -public class JavaTest { - private val defaultModelConfig = ModelConfig(analysisPlatform = Platform.jvm) - @Test fun function() { - verifyJavaPackageMember("testdata/java/member.java", defaultModelConfig) { cls -> - assertEquals("Test", cls.name) - assertEquals(NodeKind.Class, cls.kind) - with(cls.members(NodeKind.Function).single()) { - assertEquals("fn", name) - assertEquals("Summary for Function", content.summary.toTestString().trimEnd()) - assertEquals(3, content.sections.size) - with(content.sections[0]) { - assertEquals("Parameters", tag) - assertEquals("name", subjectName) - assertEquals("render(Type:String,SUMMARY): is String parameter", toTestString()) - } - with(content.sections[1]) { - assertEquals("Parameters", tag) - assertEquals("value", subjectName) - assertEquals("render(Type:Int,SUMMARY): is int parameter", toTestString()) - } - with(content.sections[2]) { - assertEquals("Author", tag) - assertEquals("yole", toTestString()) - } - assertEquals("Unit", detail(NodeKind.Type).name) - assertTrue(members.none()) - assertTrue(links.none()) - with(details.first { it.name == "name" }) { - assertEquals(NodeKind.Parameter, kind) - assertEquals("String", detail(NodeKind.Type).name) - } - with(details.first { it.name == "value" }) { - assertEquals(NodeKind.Parameter, kind) - assertEquals("Int", detail(NodeKind.Type).name) - } - } - } - } - - @Test fun memberWithModifiers() { - verifyJavaPackageMember("testdata/java/memberWithModifiers.java", defaultModelConfig) { cls -> - val modifiers = cls.details(NodeKind.Modifier).map { it.name } - assertTrue("abstract" in modifiers) - with(cls.members.single { it.name == "fn" }) { - assertEquals("protected", details[0].name) - } - with(cls.members.single { it.name == "openFn" }) { - assertEquals("open", details[1].name) - } - } - } - - @Test fun superClass() { - verifyJavaPackageMember("testdata/java/superClass.java", defaultModelConfig) { cls -> - val superTypes = cls.details(NodeKind.Supertype) - assertEquals(2, superTypes.size) - assertEquals("Exception", superTypes[0].name) - assertEquals("Cloneable", superTypes[1].name) - } - } - - @Test fun arrayType() { - verifyJavaPackageMember("testdata/java/arrayType.java", defaultModelConfig) { cls -> - with(cls.members(NodeKind.Function).single()) { - val type = detail(NodeKind.Type) - assertEquals("Array", type.name) - assertEquals("String", type.detail(NodeKind.Type).name) - with(details(NodeKind.Parameter).single()) { - val parameterType = detail(NodeKind.Type) - assertEquals("IntArray", parameterType.name) - } - } - } - } - - @Test fun typeParameter() { - verifyJavaPackageMember("testdata/java/typeParameter.java", defaultModelConfig) { cls -> - val typeParameters = cls.details(NodeKind.TypeParameter) - with(typeParameters.single()) { - assertEquals("T", name) - with(detail(NodeKind.UpperBound)) { - assertEquals("Comparable", name) - assertEquals("T", detail(NodeKind.Type).name) - } - } - with(cls.members(NodeKind.Function).single()) { - val methodTypeParameters = details(NodeKind.TypeParameter) - with(methodTypeParameters.single()) { - assertEquals("E", name) - } - } - } - } - - @Test fun constructors() { - verifyJavaPackageMember("testdata/java/constructors.java", defaultModelConfig) { cls -> - val constructors = cls.members(NodeKind.Constructor) - assertEquals(2, constructors.size) - with(constructors[0]) { - assertEquals("<init>", name) - } - } - } - - @Test fun innerClass() { - verifyJavaPackageMember("testdata/java/InnerClass.java", defaultModelConfig) { cls -> - val innerClass = cls.members(NodeKind.Class).single() - assertEquals("D", innerClass.name) - } - } - - @Test fun varargs() { - verifyJavaPackageMember("testdata/java/varargs.java", defaultModelConfig) { cls -> - val fn = cls.members(NodeKind.Function).single() - val param = fn.detail(NodeKind.Parameter) - assertEquals("vararg", param.details(NodeKind.Modifier).first().name) - val psiType = param.detail(NodeKind.Type) - assertEquals("String", psiType.name) - assertTrue(psiType.details(NodeKind.Type).isEmpty()) - } - } - - @Test fun fields() { - verifyJavaPackageMember("testdata/java/field.java", defaultModelConfig) { cls -> - val i = cls.members(NodeKind.Property).single { it.name == "i" } - assertEquals("Int", i.detail(NodeKind.Type).name) - assertTrue("var" in i.details(NodeKind.Modifier).map { it.name }) - - val s = cls.members(NodeKind.Property).single { it.name == "s" } - assertEquals("String", s.detail(NodeKind.Type).name) - assertFalse("var" in s.details(NodeKind.Modifier).map { it.name }) - assertTrue("static" in s.details(NodeKind.Modifier).map { it.name }) - } - } - - @Test fun staticMethod() { - verifyJavaPackageMember("testdata/java/staticMethod.java", defaultModelConfig) { cls -> - val m = cls.members(NodeKind.Function).single { it.name == "foo" } - assertTrue("static" in m.details(NodeKind.Modifier).map { it.name }) - } - } - - /** - * `@suppress` not supported in Java! - * - * [Proposed tags](https://www.oracle.com/technetwork/java/javase/documentation/proposed-tags-142378.html) - * Proposed tag `@exclude` for it, but not supported yet - */ - @Ignore("@suppress not supported in Java!") @Test fun suppressTag() { - verifyJavaPackageMember("testdata/java/suppressTag.java", defaultModelConfig) { cls -> - assertEquals(1, cls.members(NodeKind.Function).size) - } - } - - @Test fun annotatedAnnotation() { - verifyJavaPackageMember("testdata/java/annotatedAnnotation.java", defaultModelConfig) { cls -> - assertEquals(1, cls.annotations.size) - with(cls.annotations[0]) { - assertEquals(1, details.count()) - with(details[0]) { - assertEquals(NodeKind.Parameter, kind) - assertEquals(1, details.count()) - with(details[0]) { - assertEquals(NodeKind.Value, kind) - assertEquals("[AnnotationTarget.FIELD, AnnotationTarget.CLASS, AnnotationTarget.FILE, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER]", name) - } - } - } - } - } - - @Test fun deprecation() { - verifyJavaPackageMember("testdata/java/deprecation.java", defaultModelConfig) { cls -> - val fn = cls.members(NodeKind.Function).single() - assertEquals("This should no longer be used", fn.deprecation!!.content.toTestString()) - } - } - - @Test fun javaLangObject() { - verifyJavaPackageMember("testdata/java/javaLangObject.java", defaultModelConfig) { cls -> - val fn = cls.members(NodeKind.Function).single() - assertEquals("Any", fn.detail(NodeKind.Type).name) - } - } - - @Test fun enumValues() { - verifyJavaPackageMember("testdata/java/enumValues.java", defaultModelConfig) { cls -> - val superTypes = cls.details(NodeKind.Supertype) - assertEquals(1, superTypes.size) - assertEquals(1, cls.members(NodeKind.EnumItem).size) - } - } - - @Test fun inheritorLinks() { - verifyJavaPackageMember("testdata/java/InheritorLinks.java", defaultModelConfig) { cls -> - val fooClass = cls.members.single { it.name == "Foo" } - val inheritors = fooClass.references(RefKind.Inheritor) - assertEquals(1, inheritors.size) - } - } -} diff --git a/core/src/test/kotlin/model/KotlinAsJavaTest.kt b/core/src/test/kotlin/model/KotlinAsJavaTest.kt deleted file mode 100644 index c29776be..00000000 --- a/core/src/test/kotlin/model/KotlinAsJavaTest.kt +++ /dev/null @@ -1,64 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.DocumentationModule -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.RefKind -import org.junit.Assert -import org.junit.Assert.assertEquals -import org.junit.Test - -class KotlinAsJavaTest { - @Test fun function() { - verifyModelAsJava("testdata/functions/function.kt") { model -> - val pkg = model.members.single() - - val facadeClass = pkg.members.single { it.name == "FunctionKt" } - assertEquals(NodeKind.Class, facadeClass.kind) - - val fn = facadeClass.members.single { it.kind == NodeKind.Function} - assertEquals("fn", fn.name) - } - } - - @Test fun propertyWithComment() { - verifyModelAsJava("testdata/comments/oneLineDoc.kt") { model -> - val facadeClass = model.members.single().members.single { it.name == "OneLineDocKt" } - val getter = facadeClass.members.single { it.name == "getProperty" } - assertEquals(NodeKind.Function, getter.kind) - assertEquals("doc", getter.content.summary.toTestString()) - } - } - - - @Test fun constants() { - verifyModelAsJava("testdata/java/constants.java") { cls -> - selectNodes(cls) { - subgraphOf(RefKind.Member) - matching { it.name == "constStr" || it.name == "refConst" } - }.forEach { - assertEquals("In $it", "\"some value\"", it.detailOrNull(NodeKind.Value)?.name) - } - val nullConstNode = selectNodes(cls) { - subgraphOf(RefKind.Member) - withName("nullConst") - }.single() - - Assert.assertNull(nullConstNode.detailOrNull(NodeKind.Value)) - } - } -} - -fun verifyModelAsJava(source: String, - modelConfig: ModelConfig = ModelConfig(), - verifier: (DocumentationModule) -> Unit) { - checkSourceExistsAndVerifyModel( - source, - modelConfig = ModelConfig( - withJdk = modelConfig.withJdk, - withKotlinRuntime = modelConfig.withKotlinRuntime, - format = "html-as-java", - analysisPlatform = Platform.jvm), - verifier = verifier - ) -} diff --git a/core/src/test/kotlin/model/LinkTest.kt b/core/src/test/kotlin/model/LinkTest.kt deleted file mode 100644 index 6526a4db..00000000 --- a/core/src/test/kotlin/model/LinkTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.ContentBlock -import org.jetbrains.dokka.ContentNodeLazyLink -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.Platform -import org.junit.Assert.assertEquals -import org.junit.Test - -abstract class BaseLinkTest(val analysisPlatform: Platform) { - private val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - @Test fun linkToSelf() { - checkSourceExistsAndVerifyModel("testdata/links/linkToSelf.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Foo", name) - assertEquals(NodeKind.Class, kind) - assertEquals("This is link to [Foo -> Class:Foo]", content.summary.toTestString()) - } - } - } - - @Test fun linkToExternalSite() { - checkSourceExistsAndVerifyModel("testdata/links/linkToExternalSite.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Foo", name) - assertEquals(NodeKind.Class, kind) - assertEquals("This is link to http://example.com/#example", content.summary.toTestString()) - } - } - } - - @Test fun linkToMember() { - checkSourceExistsAndVerifyModel("testdata/links/linkToMember.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Foo", name) - assertEquals(NodeKind.Class, kind) - assertEquals("This is link to [member -> Function:member]", content.summary.toTestString()) - } - } - } - - @Test fun linkToConstantWithUnderscores() { - checkSourceExistsAndVerifyModel("testdata/links/linkToConstantWithUnderscores.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Foo", name) - assertEquals(NodeKind.Class, kind) - assertEquals("This is link to [MY_CONSTANT_VALUE -> CompanionObjectProperty:MY_CONSTANT_VALUE]", content.summary.toTestString()) - } - } - } - - @Test fun linkToQualifiedMember() { - checkSourceExistsAndVerifyModel("testdata/links/linkToQualifiedMember.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Foo", name) - assertEquals(NodeKind.Class, kind) - assertEquals("This is link to [Foo.member -> Function:member]", content.summary.toTestString()) - } - } - } - - @Test fun linkToParam() { - checkSourceExistsAndVerifyModel("testdata/links/linkToParam.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Foo", name) - assertEquals(NodeKind.Function, kind) - assertEquals("This is link to [param -> Parameter:param]", content.summary.toTestString()) - } - } - } - - @Test fun linkToPackage() { - checkSourceExistsAndVerifyModel("testdata/links/linkToPackage.kt", defaultModelConfig) { model -> - val packageNode = model.members.single() - with(packageNode) { - assertEquals(this.name, "test.magic") - } - with(packageNode.members.single()) { - assertEquals("Magic", name) - assertEquals(NodeKind.Class, kind) - assertEquals("Basic implementations of [Magic -> Class:Magic] are located in [test.magic -> Package:test.magic] package", content.summary.toTestString()) - assertEquals(packageNode, ((this.content.summary as ContentBlock).children.filterIsInstance<ContentNodeLazyLink>().last()).lazyNode.invoke()) - } - } - } - -} - -class JSLinkTest: BaseLinkTest(Platform.js) -class JVMLinkTest: BaseLinkTest(Platform.jvm) -class CommonLinkTest: BaseLinkTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/model/PackageTest.kt b/core/src/test/kotlin/model/PackageTest.kt deleted file mode 100644 index 47c88385..00000000 --- a/core/src/test/kotlin/model/PackageTest.kt +++ /dev/null @@ -1,136 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.* -import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot -import org.junit.Assert.* -import org.junit.Test - -abstract class BasePackageTest(val analysisPlatform: Platform) { - val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - @Test fun rootPackage() { - checkSourceExistsAndVerifyModel("testdata/packages/rootPackage.kt", defaultModelConfig) { model -> - with(model.members.single()) { - assertEquals(NodeKind.Package, kind) - assertEquals("", name) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun simpleNamePackage() { - checkSourceExistsAndVerifyModel("testdata/packages/simpleNamePackage.kt", defaultModelConfig) { model -> - with(model.members.single()) { - assertEquals(NodeKind.Package, kind) - assertEquals("simple", name) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun dottedNamePackage() { - checkSourceExistsAndVerifyModel("testdata/packages/dottedNamePackage.kt", defaultModelConfig) { model -> - with(model.members.single()) { - assertEquals(NodeKind.Package, kind) - assertEquals("dot.name", name) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun multipleFiles() { - verifyModel( - ModelConfig( - roots = arrayOf( - KotlinSourceRoot("testdata/packages/dottedNamePackage.kt", false), - KotlinSourceRoot("testdata/packages/simpleNamePackage.kt", false) - ), - analysisPlatform = analysisPlatform - ) - ) { model -> - assertEquals(2, model.members.count()) - with(model.members.single { it.name == "simple" }) { - assertEquals(NodeKind.Package, kind) - assertEquals("simple", name) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - with(model.members.single { it.name == "dot.name" }) { - assertEquals(NodeKind.Package, kind) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun multipleFilesSamePackage() { - verifyModel( - ModelConfig( - roots = arrayOf( - KotlinSourceRoot("testdata/packages/simpleNamePackage.kt", false), - KotlinSourceRoot("testdata/packages/simpleNamePackage2.kt", false) - ), - analysisPlatform = analysisPlatform - ) - ) { model -> - assertEquals(1, model.members.count()) - with(model.members.elementAt(0)) { - assertEquals(NodeKind.Package, kind) - assertEquals("simple", name) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun classAtPackageLevel() { - verifyModel( - ModelConfig( - roots = arrayOf(KotlinSourceRoot("testdata/packages/classInPackage.kt", false)), - analysisPlatform = analysisPlatform - ) - ) { model -> - assertEquals(1, model.members.count()) - with(model.members.elementAt(0)) { - assertEquals(NodeKind.Package, kind) - assertEquals("simple.name", name) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertEquals(1, members.size) - assertTrue(links.none()) - } - } - } - - @Test fun suppressAtPackageLevel() { - verifyModel( - ModelConfig( - roots = arrayOf(KotlinSourceRoot("testdata/packages/classInPackage.kt", false)), - perPackageOptions = listOf( - PackageOptionsImpl(prefix = "simple.name", suppress = true) - ), - analysisPlatform = analysisPlatform - ) - ) { model -> - assertEquals(0, model.members.count()) - } - } -} - -class JSPackageTest : BasePackageTest(Platform.js) -class JVMPackageTest : BasePackageTest(Platform.jvm) -class CommonPackageTest : BasePackageTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/model/PropertyTest.kt b/core/src/test/kotlin/model/PropertyTest.kt deleted file mode 100644 index 9f070862..00000000 --- a/core/src/test/kotlin/model/PropertyTest.kt +++ /dev/null @@ -1,129 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.* -import org.junit.Assert -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test - -abstract class BasePropertyTest(val analysisPlatform: Platform) { - - protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - @Test fun valueProperty() { - checkSourceExistsAndVerifyModel("testdata/properties/valueProperty.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("property", name) - assertEquals(NodeKind.Property, kind) - assertEquals(Content.Empty, content) - assertEquals("String", detail(NodeKind.Type).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun variableProperty() { - checkSourceExistsAndVerifyModel("testdata/properties/variableProperty.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("property", name) - assertEquals(NodeKind.Property, kind) - assertEquals(Content.Empty, content) - assertEquals("String", detail(NodeKind.Type).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun valuePropertyWithGetter() { - checkSourceExistsAndVerifyModel("testdata/properties/valuePropertyWithGetter.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("property", name) - assertEquals(NodeKind.Property, kind) - assertEquals(Content.Empty, content) - assertEquals("String", detail(NodeKind.Type).name) - assertTrue(links.none()) - assertTrue(members.none()) - } - } - } - - @Test fun variablePropertyWithAccessors() { - checkSourceExistsAndVerifyModel("testdata/properties/variablePropertyWithAccessors.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("property", name) - assertEquals(NodeKind.Property, kind) - assertEquals(Content.Empty, content) - assertEquals("String", detail(NodeKind.Type).name) - val modifiers = details(NodeKind.Modifier).map { it.name } - assertTrue("final" in modifiers) - assertTrue("public" in modifiers) - assertTrue("var" in modifiers) - assertTrue(links.none()) - assertTrue(members.none()) - } - } - } - - @Test fun propertyWithReceiver() { - checkSourceExistsAndVerifyModel( - "testdata/properties/propertyWithReceiver.kt", - defaultModelConfig - ) { model -> - with(model.members.single().members.single()) { - assertEquals("kotlin.String", name) - assertEquals(NodeKind.ExternalClass, kind) - with(members.single()) { - assertEquals("foobar", name) - assertEquals(NodeKind.Property, kind) - } - } - } - } - - @Test fun propertyOverride() { - checkSourceExistsAndVerifyModel("testdata/properties/propertyOverride.kt", defaultModelConfig) { model -> - with(model.members.single().members.single { it.name == "Bar" }.members.single { it.name == "xyzzy"}) { - assertEquals("xyzzy", name) - val override = references(RefKind.Override).single().to - assertEquals("xyzzy", override.name) - assertEquals("Foo", override.owner!!.name) - } - } - } - - @Test fun sinceKotlin() { - checkSourceExistsAndVerifyModel("testdata/properties/sinceKotlin.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("1.1", sinceKotlin) - } - } - } -} - -class JSPropertyTest: BasePropertyTest(Platform.js) {} - -class JVMPropertyTest : BasePropertyTest(Platform.jvm) { - @Test - fun annotatedProperty() { - checkSourceExistsAndVerifyModel( - "testdata/properties/annotatedProperty.kt", - modelConfig = ModelConfig( - analysisPlatform = analysisPlatform, - withKotlinRuntime = true - ) - ) { model -> - with(model.members.single().members.single()) { - Assert.assertEquals(1, annotations.count()) - with(annotations[0]) { - Assert.assertEquals("Strictfp", name) - Assert.assertEquals(Content.Empty, content) - Assert.assertEquals(NodeKind.Annotation, kind) - } - } - } - } - -} - -class CommonPropertyTest: BasePropertyTest(Platform.common) {}
\ No newline at end of file diff --git a/core/src/test/kotlin/model/SourceLinksErrorTest.kt b/core/src/test/kotlin/model/SourceLinksErrorTest.kt deleted file mode 100644 index 9812569d..00000000 --- a/core/src/test/kotlin/model/SourceLinksErrorTest.kt +++ /dev/null @@ -1,35 +0,0 @@ -package org.jetbrains.dokka.tests.model - -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.SourceLinkDefinitionImpl -import org.jetbrains.dokka.tests.ModelConfig -import org.jetbrains.dokka.tests.checkSourceExistsAndVerifyModel -import org.junit.Assert -import org.junit.Test -import java.io.File - -class SourceLinksErrorTest { - - @Test - fun absolutePath_notMatching() { - val sourceLink = SourceLinkDefinitionImpl(File("testdata/nonExisting").absolutePath, "http://...", null) - verifyNoSourceUrl(sourceLink) - } - - @Test - fun relativePath_notMatching() { - val sourceLink = SourceLinkDefinitionImpl("testdata/nonExisting", "http://...", null) - verifyNoSourceUrl(sourceLink) - } - - private fun verifyNoSourceUrl(sourceLink: SourceLinkDefinitionImpl) { - checkSourceExistsAndVerifyModel("testdata/sourceLinks/dummy.kt", ModelConfig(sourceLinks = listOf(sourceLink))) { model -> - with(model.members.single().members.single()) { - Assert.assertEquals("foo", name) - Assert.assertEquals(NodeKind.Function, kind) - Assert.assertTrue("should not have source urls", details(NodeKind.SourceUrl).isEmpty()) - } - } - } -} - diff --git a/core/src/test/kotlin/model/SourceLinksTest.kt b/core/src/test/kotlin/model/SourceLinksTest.kt deleted file mode 100644 index a4ba870c..00000000 --- a/core/src/test/kotlin/model/SourceLinksTest.kt +++ /dev/null @@ -1,75 +0,0 @@ -package org.jetbrains.dokka.tests.model - -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.SourceLinkDefinitionImpl -import org.jetbrains.dokka.tests.ModelConfig -import org.jetbrains.dokka.tests.checkSourceExistsAndVerifyModel -import org.junit.Assert -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import java.io.File - -@RunWith(Parameterized::class) -class SourceLinksTest( - private val srcLink: String, - private val url: String, - private val lineSuffix: String?, - private val expectedUrl: String -) { - - @Test - fun test() { - val link = if(srcLink.contains(sourceLinks)){ - srcLink.substringBeforeLast(sourceLinks) + sourceLinks - } else { - srcLink.substringBeforeLast(testdata) + testdata - } - val sourceLink = SourceLinkDefinitionImpl(link, url, lineSuffix) - - checkSourceExistsAndVerifyModel(filePath, ModelConfig(sourceLinks = listOf(sourceLink))) { model -> - with(model.members.single().members.single()) { - Assert.assertEquals("foo", name) - Assert.assertEquals(NodeKind.Function, kind) - Assert.assertEquals(expectedUrl, details(NodeKind.SourceUrl).single().name) - } - } - } - - companion object { - private const val testdata = "testdata" - private const val sourceLinks = "sourceLinks" - private const val dummy = "dummy.kt" - private const val pathSuffix = "$sourceLinks/$dummy" - private const val filePath = "$testdata/$pathSuffix" - private const val url = "https://example.com" - - @Parameterized.Parameters(name = "{index}: {0}, {1}, {2} = {3}") - @JvmStatic - fun data(): Collection<Array<String?>> { - val longestPath = File(testdata).absolutePath.removeSuffix("/") + "/../$testdata/" - val maxLength = longestPath.length - val list = listOf( - arrayOf(File(testdata).absolutePath.removeSuffix("/"), "$url/$pathSuffix"), - arrayOf(File("$testdata/$sourceLinks").absolutePath.removeSuffix("/") + "/", "$url/$dummy"), - arrayOf(longestPath, "$url/$pathSuffix"), - - arrayOf(testdata, "$url/$pathSuffix"), - arrayOf("./$testdata", "$url/$pathSuffix"), - arrayOf("../core/$testdata", "$url/$pathSuffix"), - arrayOf("$testdata/$sourceLinks", "$url/$dummy"), - arrayOf("./$testdata/../$testdata/$sourceLinks", "$url/$dummy") - ) - - return list.map { arrayOf(it[0].padEnd(maxLength, '_'), url, null, it[1]) } + - listOf( - // check that it also works if url ends with / - arrayOf((File(testdata).absolutePath.removeSuffix("/") + "/").padEnd(maxLength, '_'), "$url/", null, "$url/$pathSuffix"), - // check if line suffix work - arrayOf<String?>("../core/../core/./$testdata/$sourceLinks/".padEnd(maxLength, '_'), "$url/", "#L", "$url/$dummy#L4") - ) - } - } - -} - diff --git a/core/src/test/kotlin/model/TypeAliasTest.kt b/core/src/test/kotlin/model/TypeAliasTest.kt deleted file mode 100644 index 71976dc3..00000000 --- a/core/src/test/kotlin/model/TypeAliasTest.kt +++ /dev/null @@ -1,132 +0,0 @@ -package org.jetbrains.dokka.tests - -import junit.framework.TestCase.assertEquals -import org.jetbrains.dokka.Content -import org.jetbrains.dokka.NodeKind -import org.junit.Test - -class TypeAliasTest { - @Test - fun testSimple() { - checkSourceExistsAndVerifyModel("testdata/typealias/simple.kt") { - val pkg = it.members.single() - with(pkg.member(NodeKind.TypeAlias)) { - assertEquals(Content.Empty, content) - assertEquals("B", name) - assertEquals("A", detail(NodeKind.TypeAliasUnderlyingType).name) - } - } - } - - @Test - fun testInheritanceFromTypeAlias() { - checkSourceExistsAndVerifyModel("testdata/typealias/inheritanceFromTypeAlias.kt") { - val pkg = it.members.single() - with(pkg.member(NodeKind.TypeAlias)) { - assertEquals(Content.Empty, content) - assertEquals("Same", name) - assertEquals("Some", detail(NodeKind.TypeAliasUnderlyingType).name) - assertEquals("My", inheritors.single().name) - } - with(pkg.members(NodeKind.Class).find { it.name == "My" }!!) { - assertEquals("Same", detail(NodeKind.Supertype).name) - } - } - } - - @Test - fun testChain() { - checkSourceExistsAndVerifyModel("testdata/typealias/chain.kt") { - val pkg = it.members.single() - with(pkg.members(NodeKind.TypeAlias).find { it.name == "B" }!!) { - assertEquals(Content.Empty, content) - assertEquals("A", detail(NodeKind.TypeAliasUnderlyingType).name) - } - with(pkg.members(NodeKind.TypeAlias).find { it.name == "C" }!!) { - assertEquals(Content.Empty, content) - assertEquals("B", detail(NodeKind.TypeAliasUnderlyingType).name) - } - } - } - - @Test - fun testDocumented() { - checkSourceExistsAndVerifyModel("testdata/typealias/documented.kt") { - val pkg = it.members.single() - with(pkg.member(NodeKind.TypeAlias)) { - assertEquals("Just typealias", content.summary.toTestString()) - } - } - } - - @Test - fun testDeprecated() { - checkSourceExistsAndVerifyModel("testdata/typealias/deprecated.kt") { - val pkg = it.members.single() - with(pkg.member(NodeKind.TypeAlias)) { - assertEquals(Content.Empty, content) - assertEquals("Deprecated", deprecation!!.name) - assertEquals("\"Not mainstream now\"", deprecation!!.detail(NodeKind.Parameter).detail(NodeKind.Value).name) - } - } - } - - @Test - fun testGeneric() { - checkSourceExistsAndVerifyModel("testdata/typealias/generic.kt") { - val pkg = it.members.single() - with(pkg.members(NodeKind.TypeAlias).find { it.name == "B" }!!) { - assertEquals("Any", detail(NodeKind.TypeAliasUnderlyingType).detail(NodeKind.Type).name) - } - - with(pkg.members(NodeKind.TypeAlias).find { it.name == "C" }!!) { - assertEquals("T", detail(NodeKind.TypeAliasUnderlyingType).detail(NodeKind.Type).name) - assertEquals("T", detail(NodeKind.TypeParameter).name) - } - } - } - - @Test - fun testFunctional() { - checkSourceExistsAndVerifyModel("testdata/typealias/functional.kt") { - val pkg = it.members.single() - with(pkg.member(NodeKind.TypeAlias)) { - assertEquals("Function1", detail(NodeKind.TypeAliasUnderlyingType).name) - val typeParams = detail(NodeKind.TypeAliasUnderlyingType).details(NodeKind.Type) - assertEquals("A", typeParams.first().name) - assertEquals("B", typeParams.last().name) - } - - with(pkg.member(NodeKind.Function)) { - assertEquals("Spell", detail(NodeKind.Parameter).detail(NodeKind.Type).name) - } - } - } - - @Test - fun testAsTypeBoundWithVariance() { - checkSourceExistsAndVerifyModel("testdata/typealias/asTypeBoundWithVariance.kt") { - val pkg = it.members.single() - with(pkg.members(NodeKind.Class).find { it.name == "C" }!!) { - val tParam = detail(NodeKind.TypeParameter) - assertEquals("out", tParam.detail(NodeKind.Modifier).name) - assertEquals("B", tParam.detail(NodeKind.Type).link(NodeKind.TypeAlias).name) - } - - with(pkg.members(NodeKind.Class).find { it.name == "D" }!!) { - val tParam = detail(NodeKind.TypeParameter) - assertEquals("in", tParam.detail(NodeKind.Modifier).name) - assertEquals("B", tParam.detail(NodeKind.Type).link(NodeKind.TypeAlias).name) - } - } - } - - @Test - fun sinceKotlin() { - checkSourceExistsAndVerifyModel("testdata/typealias/sinceKotlin.kt") { model -> - with(model.members.single().members.single()) { - assertEquals("1.1", sinceKotlin) - } - } - } -}
\ No newline at end of file diff --git a/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker deleted file mode 100644 index ca6ee9ce..00000000 --- a/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker +++ /dev/null @@ -1 +0,0 @@ -mock-maker-inline
\ No newline at end of file |