diff options
Diffstat (limited to 'core/src/main/kotlin')
107 files changed, 2670 insertions, 10414 deletions
| 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() +//} | 
