From 39631054c58df5841ea268b7002b820ec55f6e0a Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 3 Dec 2015 16:22:11 +0100 Subject: restructure Dokka build to use Gradle for everything except for the Maven plugin --- src/Analysis/AnalysisEnvironment.kt | 210 -------- src/Analysis/CoreProjectFileIndex.kt | 550 --------------------- src/Formats/FormatDescriptor.kt | 12 - src/Formats/FormatService.kt | 20 - src/Formats/HtmlFormatService.kt | 165 ------- src/Formats/HtmlTemplateService.kt | 34 -- src/Formats/JekyllFormatService.kt | 22 - src/Formats/KotlinWebsiteFormatService.kt | 121 ----- src/Formats/MarkdownFormatService.kt | 117 ----- src/Formats/OutlineService.kt | 29 -- src/Formats/StandardFormats.kt | 38 -- src/Formats/StructuredFormatService.kt | 367 -------------- src/Formats/YamlOutlineService.kt | 24 - src/Generation/ConsoleGenerator.kt | 42 -- src/Generation/FileGenerator.kt | 57 --- src/Generation/Generator.kt | 19 - src/Java/JavaPsiDocumentationBuilder.kt | 266 ---------- src/Java/JavadocParser.kt | 170 ------- src/Kotlin/ContentBuilder.kt | 132 ----- src/Kotlin/DeclarationLinkResolver.kt | 43 -- src/Kotlin/DescriptorDocumentationParser.kt | 199 -------- src/Kotlin/DocumentationBuilder.kt | 653 ------------------------- src/Kotlin/KotlinAsJavaDocumentationBuilder.kt | 64 --- src/Kotlin/KotlinLanguageService.kt | 409 ---------------- src/Languages/JavaLanguageService.kt | 162 ------ src/Languages/LanguageService.kt | 41 -- src/Locations/FoldersLocationService.kt | 29 -- src/Locations/LocationService.kt | 78 --- src/Locations/SingleFolderLocationService.kt | 19 - src/Markdown/MarkdownProcessor.kt | 50 -- src/Model/Content.kt | 231 --------- src/Model/DocumentationNode.kt | 162 ------ src/Model/DocumentationReference.kt | 61 --- src/Model/PackageDocs.kt | 60 --- src/Model/SourceLinks.kt | 56 --- src/Utilities/DokkaModule.kt | 73 --- src/Utilities/Html.kt | 8 - src/Utilities/Path.kt | 5 - src/Utilities/ServiceLocator.kt | 78 --- src/main.kt | 262 ---------- 40 files changed, 5138 deletions(-) delete mode 100644 src/Analysis/AnalysisEnvironment.kt delete mode 100644 src/Analysis/CoreProjectFileIndex.kt delete mode 100644 src/Formats/FormatDescriptor.kt delete mode 100644 src/Formats/FormatService.kt delete mode 100644 src/Formats/HtmlFormatService.kt delete mode 100644 src/Formats/HtmlTemplateService.kt delete mode 100644 src/Formats/JekyllFormatService.kt delete mode 100644 src/Formats/KotlinWebsiteFormatService.kt delete mode 100644 src/Formats/MarkdownFormatService.kt delete mode 100644 src/Formats/OutlineService.kt delete mode 100644 src/Formats/StandardFormats.kt delete mode 100644 src/Formats/StructuredFormatService.kt delete mode 100644 src/Formats/YamlOutlineService.kt delete mode 100644 src/Generation/ConsoleGenerator.kt delete mode 100644 src/Generation/FileGenerator.kt delete mode 100644 src/Generation/Generator.kt delete mode 100644 src/Java/JavaPsiDocumentationBuilder.kt delete mode 100644 src/Java/JavadocParser.kt delete mode 100644 src/Kotlin/ContentBuilder.kt delete mode 100644 src/Kotlin/DeclarationLinkResolver.kt delete mode 100644 src/Kotlin/DescriptorDocumentationParser.kt delete mode 100644 src/Kotlin/DocumentationBuilder.kt delete mode 100644 src/Kotlin/KotlinAsJavaDocumentationBuilder.kt delete mode 100644 src/Kotlin/KotlinLanguageService.kt delete mode 100644 src/Languages/JavaLanguageService.kt delete mode 100644 src/Languages/LanguageService.kt delete mode 100644 src/Locations/FoldersLocationService.kt delete mode 100644 src/Locations/LocationService.kt delete mode 100644 src/Locations/SingleFolderLocationService.kt delete mode 100644 src/Markdown/MarkdownProcessor.kt delete mode 100644 src/Model/Content.kt delete mode 100644 src/Model/DocumentationNode.kt delete mode 100644 src/Model/DocumentationReference.kt delete mode 100644 src/Model/PackageDocs.kt delete mode 100644 src/Model/SourceLinks.kt delete mode 100644 src/Utilities/DokkaModule.kt delete mode 100644 src/Utilities/Html.kt delete mode 100644 src/Utilities/Path.kt delete mode 100644 src/Utilities/ServiceLocator.kt delete mode 100644 src/main.kt (limited to 'src') diff --git a/src/Analysis/AnalysisEnvironment.kt b/src/Analysis/AnalysisEnvironment.kt deleted file mode 100644 index a5e35a0e..00000000 --- a/src/Analysis/AnalysisEnvironment.kt +++ /dev/null @@ -1,210 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.core.CoreApplicationEnvironment -import com.intellij.core.CoreModuleManager -import com.intellij.mock.MockComponentManager -import com.intellij.openapi.Disposable -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.psi.PsiElement -import com.intellij.psi.search.GlobalSearchScope -import org.jetbrains.kotlin.analyzer.AnalysisResult -import org.jetbrains.kotlin.analyzer.ModuleContent -import org.jetbrains.kotlin.analyzer.ModuleInfo -import org.jetbrains.kotlin.analyzer.ResolverForModule -import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys -import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot -import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot -import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots -import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots -import org.jetbrains.kotlin.config.CommonConfigurationKeys -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.config.ContentRoot -import org.jetbrains.kotlin.config.KotlinSourceRoot -import org.jetbrains.kotlin.container.getService -import org.jetbrains.kotlin.context.ProjectContext -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.idea.caches.resolve.KotlinCacheService -import org.jetbrains.kotlin.idea.caches.resolve.KotlinOutOfBlockCompletionModificationTracker -import org.jetbrains.kotlin.idea.caches.resolve.LibraryModificationTracker -import org.jetbrains.kotlin.idea.resolve.ResolutionFacade -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.CompilerEnvironment -import org.jetbrains.kotlin.resolve.jvm.JvmAnalyzerFacade -import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters -import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -import org.jetbrains.kotlin.resolve.lazy.ResolveSession -import java.io.File - -/** - * 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 - */ -public class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable { - val configuration = CompilerConfiguration(); - - init { - configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) - } - - fun createCoreEnvironment(): KotlinCoreEnvironment { - val environment = KotlinCoreEnvironment.createForProduction(this, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) - val projectComponentManager = environment.project as MockComponentManager - - val projectFileIndex = CoreProjectFileIndex(environment.project, - environment.configuration.getList(CommonConfigurationKeys.CONTENT_ROOTS)) - - val moduleManager = object : CoreModuleManager(environment.project, this) { - override fun getModules(): Array = 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)) - projectComponentManager.registerService(LibraryModificationTracker::class.java, - LibraryModificationTracker(environment.project)) - projectComponentManager.registerService(KotlinCacheService::class.java, - KotlinCacheService(environment.project)) - projectComponentManager.registerService(KotlinOutOfBlockCompletionModificationTracker::class.java, - KotlinOutOfBlockCompletionModificationTracker()) - return environment - } - - fun createResolutionFacade(environment: KotlinCoreEnvironment): DokkaResolutionFacade { - val projectContext = ProjectContext(environment.project) - val sourceFiles = environment.getSourceFiles() - - val module = object : ModuleInfo { - override val name: Name = Name.special("") - override fun dependencies(): List = listOf(this) - } - val resolverForProject = JvmAnalyzerFacade.setupResolverForProject( - "Dokka", - projectContext, - listOf(module), - { ModuleContent(sourceFiles, GlobalSearchScope.allScope(environment.project)) }, - JvmPlatformParameters { module }, - CompilerEnvironment - ) - - val resolverForModule = resolverForProject.resolverForModule(module) - return DokkaResolutionFacade(environment.project, resolverForProject.descriptorForModule(module), resolverForModule) - } - - /** - * Classpath for this environment. - */ - public val classpath: List - get() = configuration.jvmClasspathRoots - - /** - * Adds list of paths to classpath. - * $paths: collection of files to add - */ - public fun addClasspath(paths: List) { - configuration.addJvmClasspathRoots(paths) - } - - /** - * Adds path to classpath. - * $path: path to add - */ - public fun addClasspath(path: File) { - configuration.addJvmClasspathRoot(path) - } - - /** - * List of source roots for this environment. - */ - public val sources: List - get() = configuration.get(CommonConfigurationKeys.CONTENT_ROOTS) - ?.filterIsInstance() - ?.map { it.path } ?: emptyList() - - /** - * Adds list of paths to source roots. - * $list: collection of files to add - */ - public fun addSources(list: List) { - list.forEach { - configuration.add(CommonConfigurationKeys.CONTENT_ROOTS, contentRootFromPath(it)) - } - } - - public fun addRoots(list: List) { - configuration.addAll(CommonConfigurationKeys.CONTENT_ROOTS, list) - } - - /** - * Disposes the environment and frees all associated resources. - */ - public override fun dispose() { - Disposer.dispose(this) - } -} - -public fun contentRootFromPath(path: String): ContentRoot { - val file = File(path) - return if (file.extension == "java") JavaSourceRoot(file, null) else KotlinSourceRoot(path) -} - - -class DokkaResolutionFacade(override val project: Project, - override val moduleDescriptor: ModuleDescriptor, - val resolverForModule: ResolverForModule) : ResolutionFacade { - - val resolveSession: ResolveSession get() = getFrontendService(ResolveSession::class.java) - - override fun analyze(element: KtElement, bodyResolveMode: BodyResolveMode): BindingContext { - throw UnsupportedOperationException() - } - - override fun analyzeFullyAndGetResult(elements: Collection): AnalysisResult { - throw UnsupportedOperationException() - } - - override fun getFrontendService(element: PsiElement, serviceClass: Class): T { - throw UnsupportedOperationException() - } - - override fun getFrontendService(serviceClass: Class): T { - return resolverForModule.componentProvider.getService(serviceClass) - } - - override fun getFrontendService(moduleDescriptor: ModuleDescriptor, serviceClass: Class): T { - throw UnsupportedOperationException() - } - - override fun getIdeService(serviceClass: Class): T { - throw UnsupportedOperationException() - } - - override fun resolveToDescriptor(declaration: KtDeclaration): DeclarationDescriptor { - return resolveSession.resolveToDescriptor(declaration) - } -} diff --git a/src/Analysis/CoreProjectFileIndex.kt b/src/Analysis/CoreProjectFileIndex.kt deleted file mode 100644 index a1362fde..00000000 --- a/src/Analysis/CoreProjectFileIndex.kt +++ /dev/null @@ -1,550 +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.VirtualFile -import com.intellij.psi.search.GlobalSearchScope -import com.intellij.util.messages.MessageBus -import org.jetbrains.jps.model.module.JpsModuleSourceRootType -import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot -import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot -import org.jetbrains.kotlin.config.ContentRoot -import org.jetbrains.kotlin.config.KotlinSourceRoot -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(val project: Project, contentRoots: List) : ProjectFileIndex, ModuleFileIndex { - val sourceRoots = contentRoots.filter { it !is JvmClasspathRoot } - val classpathRoots = contentRoots.filterIsInstance() - - 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 = "" - - 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 = 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 getExtensions(p0: ExtensionPointName): Array { - throw UnsupportedOperationException() - } - - override fun getComponent(p0: String): BaseComponent? { - throw UnsupportedOperationException() - } - - override fun getComponent(p0: Class, p1: T): T { - throw UnsupportedOperationException() - } - - override fun getComponent(interfaceClass: Class): T? { - if (interfaceClass == ModuleRootManager::class.java) { - return moduleRootManager as T - } - throw UnsupportedOperationException() - } - - override fun getDisposed(): Condition<*> { - throw UnsupportedOperationException() - } - - override fun getComponents(p0: Class): Array { - 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 = classpathRoots - .map { StandardFileSystems.local().findFileByPath(it.file.path) } - .filterNotNull() - .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 { - throw UnsupportedOperationException() - } - - override fun removeRootSetChangedListener(p0: RootProvider.RootSetChangedListener) { - throw UnsupportedOperationException() - } - - override fun getSdkModificator(): SdkModificator { - throw UnsupportedOperationException() - } - - override fun getName(): String = "" - - 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 getUserData(p0: Key): T? { - throw UnsupportedOperationException() - } - - override fun putUserData(p0: Key, p1: T?) { - throw UnsupportedOperationException() - } - } - - private val moduleSourceOrderEntry = object : ModuleSourceOrderEntry { - override fun getFiles(p0: OrderRootType?): Array { - throw UnsupportedOperationException() - } - - override fun getPresentableName(): String { - throw UnsupportedOperationException() - } - - override fun getUrls(p0: OrderRootType?): Array { - throw UnsupportedOperationException() - } - - override fun getOwnerModule(): Module = module - - override fun accept(p0: RootPolicy?, p1: R?): R { - throw UnsupportedOperationException() - } - - 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 getJdkName(): String? { - throw UnsupportedOperationException() - } - - override fun getJdk(): Sdk = sdk - - override fun getFiles(p0: OrderRootType?): Array { - throw UnsupportedOperationException() - } - - override fun getPresentableName(): String { - throw UnsupportedOperationException() - } - - override fun getUrls(p0: OrderRootType?): Array { - throw UnsupportedOperationException() - } - - override fun getOwnerModule(): Module { - throw UnsupportedOperationException() - } - - override fun accept(p0: RootPolicy?, p1: R?): R { - throw UnsupportedOperationException() - } - - override fun isValid(): Boolean { - throw UnsupportedOperationException() - } - - override fun getRootFiles(p0: OrderRootType?): Array? { - throw UnsupportedOperationException() - } - - override fun getRootUrls(p0: OrderRootType?): Array? { - throw UnsupportedOperationException() - } - - override fun compareTo(other: OrderEntry?): Int { - throw UnsupportedOperationException() - } - - override fun isSynthetic(): Boolean { - throw UnsupportedOperationException() - } - - } - - inner class MyModuleRootManager : ModuleRootManager() { - override fun getExcludeRoots(): Array { - throw UnsupportedOperationException() - } - - override fun getContentEntries(): Array { - throw UnsupportedOperationException() - } - - override fun getExcludeRootUrls(): Array { - throw UnsupportedOperationException() - } - - override fun processOrder(p0: RootPolicy?, p1: R): R { - throw UnsupportedOperationException() - } - - override fun getSourceRoots(p0: Boolean): Array { - throw UnsupportedOperationException() - } - - override fun getSourceRoots(): Array { - throw UnsupportedOperationException() - } - - override fun getSourceRoots(p0: JpsModuleSourceRootType<*>): MutableList { - throw UnsupportedOperationException() - } - - override fun getSourceRoots(p0: MutableSet>): MutableList { - throw UnsupportedOperationException() - } - - override fun getContentRoots(): Array { - throw UnsupportedOperationException() - } - - override fun orderEntries(): OrderEnumerator = - ProjectOrderEnumerator(project, null).using(object : RootModelProvider { - override fun getModules(): Array = arrayOf(module) - - override fun getRootModel(p0: Module): ModuleRootModel = this@MyModuleRootManager - }) - - override fun getModuleExtension(p0: Class?): T { - throw UnsupportedOperationException() - } - - override fun getDependencyModuleNames(): Array { - throw UnsupportedOperationException() - } - - override fun getModule(): Module = module - - override fun isSdkInherited(): Boolean { - throw UnsupportedOperationException() - } - - override fun getOrderEntries(): Array = arrayOf(moduleSourceOrderEntry, sdkOrderEntry) - - override fun getSourceRootUrls(): Array { - throw UnsupportedOperationException() - } - - override fun getSourceRootUrls(p0: Boolean): Array { - throw UnsupportedOperationException() - } - - override fun getSdk(): Sdk? { - throw UnsupportedOperationException() - } - - override fun getContentRootUrls(): Array { - throw UnsupportedOperationException() - } - - override fun getModuleDependencies(): Array { - throw UnsupportedOperationException() - } - - override fun getModuleDependencies(p0: Boolean): Array { - 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 { - throw UnsupportedOperationException() - } - - override fun getDependencies(p0: Boolean): Array { - 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 = - 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.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>): 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): OrderEnumerator { - throw UnsupportedOperationException() - } - - override fun getContentRootsFromAllModules(): Array? { - throw UnsupportedOperationException() - } - - override fun setProjectSdk(p0: Sdk?) { - throw UnsupportedOperationException() - } - - override fun setProjectSdkName(p0: String?) { - throw UnsupportedOperationException() - } - - override fun getModuleSourceRoots(p0: MutableSet>): MutableList { - throw UnsupportedOperationException() - } - - override fun getContentSourceRoots(): Array { - throw UnsupportedOperationException() - } - - override fun getFileIndex(): ProjectFileIndex = projectFileIndex - - override fun getProjectSdkName(): String? { - throw UnsupportedOperationException() - } - - override fun getProjectSdk(): Sdk? { - throw UnsupportedOperationException() - } - - override fun getContentRoots(): Array { - throw UnsupportedOperationException() - } - - override fun getContentRootUrls(): MutableList { - throw UnsupportedOperationException() - } - -} - -fun ContentRoot.contains(file: VirtualFile) = when (this) { - is JvmContentRoot -> { - val path = if (file.fileSystem.protocol == StandardFileSystems.JAR_PROTOCOL) - StandardFileSystems.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/src/Formats/FormatDescriptor.kt b/src/Formats/FormatDescriptor.kt deleted file mode 100644 index 0c7ca794..00000000 --- a/src/Formats/FormatDescriptor.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.jetbrains.dokka.Formats - -import org.jetbrains.dokka.* -import kotlin.reflect.KClass - -public interface FormatDescriptor { - val formatServiceClass: KClass? - val outlineServiceClass: KClass? - val generatorServiceClass: KClass - val packageDocumentationBuilderClass: KClass - val javaDocumentationBuilderClass: KClass -} diff --git a/src/Formats/FormatService.kt b/src/Formats/FormatService.kt deleted file mode 100644 index 7e66a6b7..00000000 --- a/src/Formats/FormatService.kt +++ /dev/null @@ -1,20 +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 - * * [TextFormatService] – outputs documentation in Text format - */ -public interface FormatService { - /** Returns extension for output files */ - val extension: String - - /** Appends formatted content to [StringBuilder](to) using specified [location] */ - fun appendNodes(location: Location, to: StringBuilder, nodes: Iterable) -} - -/** Format content to [String] using specified [location] */ -fun FormatService.format(location: Location, nodes: Iterable): String = StringBuilder().apply { appendNodes(location, this, nodes) }.toString() diff --git a/src/Formats/HtmlFormatService.kt b/src/Formats/HtmlFormatService.kt deleted file mode 100644 index 2c461905..00000000 --- a/src/Formats/HtmlFormatService.kt +++ /dev/null @@ -1,165 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import java.io.File -import java.nio.file.Paths - -public open class HtmlFormatService @Inject constructor(@Named("folders") locationService: LocationService, - signatureGenerator: LanguageService, - val templateService: HtmlTemplateService) -: StructuredFormatService(locationService, signatureGenerator, "html"), OutlineFormatService { - override public fun formatText(text: String): String { - return text.htmlEscape() - } - - override fun formatSymbol(text: String): String { - return "${formatText(text)}" - } - - override fun formatKeyword(text: String): String { - return "${formatText(text)}" - } - - override fun formatIdentifier(text: String, kind: IdentifierKind): String { - return "${formatText(text)}" - } - - override fun appendBlockCode(to: StringBuilder, line: String, language: String) { - to.append("
")
-        to.append(line)
-        to.append("
") - } - - override fun appendHeader(to: StringBuilder, text: String, level: Int) { - to.appendln("${text}") - } - - override fun appendParagraph(to: StringBuilder, text: String) { - to.appendln("

${text}

") - } - - override fun appendLine(to: StringBuilder, text: String) { - to.appendln("${text}
") - } - - override fun appendLine(to: StringBuilder) { - to.appendln("
") - } - - override fun appendAnchor(to: StringBuilder, anchor: String) { - to.appendln("") - } - - override fun appendTable(to: StringBuilder, body: () -> Unit) { - to.appendln("") - body() - to.appendln("
") - } - - override fun appendTableHeader(to: StringBuilder, body: () -> Unit) { - to.appendln("") - body() - to.appendln("") - } - - override fun appendTableBody(to: StringBuilder, body: () -> Unit) { - to.appendln("") - body() - to.appendln("") - } - - override fun appendTableRow(to: StringBuilder, body: () -> Unit) { - to.appendln("") - body() - to.appendln("") - } - - override fun appendTableCell(to: StringBuilder, body: () -> Unit) { - to.appendln("") - body() - to.appendln("") - } - - override fun formatLink(text: String, href: String): String { - return "${text}" - } - - override fun formatStrong(text: String): String { - return "${text}" - } - - override fun formatEmphasis(text: String): String { - return "${text}" - } - - override fun formatStrikethrough(text: String): String { - return "${text}" - } - - override fun formatCode(code: String): String { - return "${code}" - } - - override fun formatUnorderedList(text: String): String = "
    ${text}
" - override fun formatOrderedList(text: String): String = "
    ${text}
" - - override fun formatListItem(text: String, kind: ListKind): String { - return "
  • ${text}
  • " - } - - override fun formatBreadcrumbs(items: Iterable): String { - return items.map { formatLink(it) }.joinToString(" / ") - } - - - override fun appendNodes(location: Location, to: StringBuilder, nodes: Iterable) { - templateService.appendHeader(to, getPageTitle(nodes), calcPathToRoot(location)) - super.appendNodes(location, to, nodes) - templateService.appendFooter(to) - } - - override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable) { - templateService.appendHeader(to, "Module Contents", calcPathToRoot(location)) - super.appendOutline(location, to, nodes) - templateService.appendFooter(to) - } - - private fun calcPathToRoot(location: Location) = Paths.get(location.path).parent.relativize(Paths.get(locationService.root.path + '/')) - - 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 signature = formatText(location, link) - to.appendln("${signature}
    ") - } - - override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { - to.appendln("
      ") - body() - to.appendln("
    ") - } - - override fun formatNonBreakingSpace(): String = " " -} - -fun getPageTitle(nodes: Iterable): String? { - val breakdownByLocation = nodes.groupBy { node -> formatPageTitle(node) } - return breakdownByLocation.keys.singleOrNull() -} - -fun formatPageTitle(node: DocumentationNode): String { - val path = node.path - if (path.size == 1) { - return path.first().name - } - val qualifiedName = node.qualifiedName() - if (qualifiedName.length == 0 && path.size == 2) { - return path.first().name + " / root package" - } - return path.first().name + " / " + qualifiedName -} diff --git a/src/Formats/HtmlTemplateService.kt b/src/Formats/HtmlTemplateService.kt deleted file mode 100644 index ae42a31b..00000000 --- a/src/Formats/HtmlTemplateService.kt +++ /dev/null @@ -1,34 +0,0 @@ -package org.jetbrains.dokka - -import java.nio.file.Path - -public interface HtmlTemplateService { - fun appendHeader(to: StringBuilder, title: String?, basePath: Path) - fun appendFooter(to: StringBuilder) - - companion object { - public fun default(css: String? = null): HtmlTemplateService { - return object : HtmlTemplateService { - override fun appendFooter(to: StringBuilder) { - to.appendln("") - to.appendln("") - } - override fun appendHeader(to: StringBuilder, title: String?, basePath: Path) { - to.appendln("") - to.appendln("") - if (title != null) { - to.appendln("$title") - } - if (css != null) { - val cssPath = basePath.resolve(css) - to.appendln("") - } - to.appendln("") - to.appendln("") - } - } - } - } -} - - diff --git a/src/Formats/JekyllFormatService.kt b/src/Formats/JekyllFormatService.kt deleted file mode 100644 index f81257d6..00000000 --- a/src/Formats/JekyllFormatService.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject - -open class JekyllFormatService - @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService, - linkExtension: String = "md") -: MarkdownFormatService(locationService, signatureGenerator, linkExtension) { - - override fun appendNodes(location: Location, to: StringBuilder, nodes: Iterable) { - to.appendln("---") - appendFrontMatter(nodes, to) - to.appendln("---") - to.appendln("") - super.appendNodes(location, to, nodes) - } - - protected open fun appendFrontMatter(nodes: Iterable, to: StringBuilder) { - to.appendln("title: ${getPageTitle(nodes)}") - } -} \ No newline at end of file diff --git a/src/Formats/KotlinWebsiteFormatService.kt b/src/Formats/KotlinWebsiteFormatService.kt deleted file mode 100644 index 4eda7910..00000000 --- a/src/Formats/KotlinWebsiteFormatService.kt +++ /dev/null @@ -1,121 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject - -public class KotlinWebsiteFormatService @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService) -: JekyllFormatService(locationService, signatureGenerator, "html") { - private var needHardLineBreaks = false - - override fun appendFrontMatter(nodes: Iterable, to: StringBuilder) { - super.appendFrontMatter(nodes, to) - to.appendln("layout: api") - } - - override public fun formatBreadcrumbs(items: Iterable): String { - items.drop(1) - - if (items.count() > 1) { - return "
    " + - items.map { formatLink(it) }.joinToString(" / ") + - "
    " - } - - return "" - } - - override public fun formatCode(code: String): String = if (code.length > 0) "$code" else "" - - override fun formatStrikethrough(text: String): String = "$text" - - override fun appendAsSignature(to: StringBuilder, node: ContentNode, block: () -> Unit) { - val contentLength = node.textLength - if (contentLength == 0) return - to.append("
    ") - needHardLineBreaks = contentLength >= 62 - try { - block() - } finally { - needHardLineBreaks = false - } - to.append("
    ") - } - - override fun appendAsOverloadGroup(to: StringBuilder, block: () -> Unit) { - to.append("
    \n") - block() - to.append("
    \n") - } - - override fun formatLink(text: String, href: String): String { - return "${text}" - } - - override fun appendTable(to: StringBuilder, body: () -> Unit) { - to.appendln("") - body() - to.appendln("
    ") - } - - override fun appendTableHeader(to: StringBuilder, body: () -> Unit) { - to.appendln("") - body() - to.appendln("") - } - - override fun appendTableBody(to: StringBuilder, body: () -> Unit) { - to.appendln("") - body() - to.appendln("") - } - - override fun appendTableRow(to: StringBuilder, body: () -> Unit) { - to.appendln("") - body() - to.appendln("") - } - - override fun appendTableCell(to: StringBuilder, body: () -> Unit) { - to.appendln("") - body() - to.appendln("\n") - } - - override public fun appendBlockCode(to: StringBuilder, line: String, language: String) { - if (language.isNotEmpty()) { - super.appendBlockCode(to, line, language) - } else { - to.append("
    ")
    -            to.append(line.trimStart())
    -            to.append("
    ") - } - } - - override fun formatSymbol(text: String): String { - return "${formatText(text)}" - } - - override fun formatKeyword(text: String): String { - return "${formatText(text)}" - } - - override fun formatIdentifier(text: String, kind: IdentifierKind): String { - return "${formatText(text)}" - } - - override fun formatSoftLineBreak(): String = if (needHardLineBreaks) - "
    " - else - "" - - override fun formatIndentedSoftLineBreak(): String = if (needHardLineBreaks) - "
        " - else - "" - - private fun identifierClassName(kind: IdentifierKind) = when(kind) { - IdentifierKind.ParameterName -> "parameterName" - IdentifierKind.SummarizedTypeName -> "summarizedTypeName" - else -> "identifier" - } -} diff --git a/src/Formats/MarkdownFormatService.kt b/src/Formats/MarkdownFormatService.kt deleted file mode 100644 index f694ae3e..00000000 --- a/src/Formats/MarkdownFormatService.kt +++ /dev/null @@ -1,117 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject - - -public open class MarkdownFormatService - @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService, - linkExtension: String = "md") -: StructuredFormatService(locationService, signatureGenerator, "md", linkExtension) { - override public fun formatBreadcrumbs(items: Iterable): String { - return items.map { formatLink(it) }.joinToString(" / ") - } - - override public fun formatText(text: String): String { - return text.htmlEscape() - } - - override fun formatSymbol(text: String): String { - return text.htmlEscape() - } - - override fun formatKeyword(text: String): String { - return text.htmlEscape() - } - override fun formatIdentifier(text: String, kind: IdentifierKind): String { - return text.htmlEscape() - } - - override public fun formatCode(code: String): String { - return "`$code`" - } - - override public fun formatUnorderedList(text: String): String = text + "\n" - override public fun formatOrderedList(text: String): String = text + "\n" - - override fun formatListItem(text: String, kind: ListKind): String { - val itemText = if (text.endsWith("\n")) text else text + "\n" - return if (kind == ListKind.Unordered) "* $itemText" else "1. $itemText" - } - - override public fun formatStrong(text: String): String { - return "**$text**" - } - - override fun formatEmphasis(text: String): String { - return "*$text*" - } - - override fun formatStrikethrough(text: String): String { - return "~~$text~~" - } - - override fun formatLink(text: String, href: String): String { - return "[$text]($href)" - } - - override public fun appendLine(to: StringBuilder) { - to.appendln() - } - - override public fun appendLine(to: StringBuilder, text: String) { - to.appendln(text) - } - - override fun appendAnchor(to: StringBuilder, anchor: String) { - // no anchors in Markdown - } - - override public fun appendParagraph(to: StringBuilder, text: String) { - to.appendln() - to.appendln(text) - to.appendln() - } - - override public fun appendHeader(to: StringBuilder, text: String, level: Int) { - appendLine(to) - appendLine(to, "${"#".repeat(level)} $text") - appendLine(to) - } - - override public fun appendBlockCode(to: StringBuilder, line: String, language: String) { - appendLine(to) - to.appendln("``` ${language}") - to.appendln(line) - to.appendln("```") - appendLine(to) - } - - override fun appendTable(to: StringBuilder, body: () -> Unit) { - to.appendln() - body() - to.appendln() - } - - override fun appendTableHeader(to: StringBuilder, body: () -> Unit) { - body() - } - - override fun appendTableBody(to: StringBuilder, body: () -> Unit) { - body() - } - - override fun appendTableRow(to: StringBuilder, body: () -> Unit) { - to.append("|") - body() - to.appendln() - } - - override fun appendTableCell(to: StringBuilder, body: () -> Unit) { - to.append(" ") - body() - to.append(" |") - } - - override fun formatNonBreakingSpace(): String = " " -} diff --git a/src/Formats/OutlineService.kt b/src/Formats/OutlineService.kt deleted file mode 100644 index 6626cf51..00000000 --- a/src/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. - */ -public interface OutlineFormatService { - fun getOutlineFileName(location: Location): File - - public fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) - public fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) - - /** Appends formatted outline to [StringBuilder](to) using specified [location] */ - public fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable) { - for (node in nodes) { - appendOutlineHeader(location, node, to) - if (node.members.any()) { - val sortedMembers = node.members.sortedBy { it.name } - appendOutlineLevel(to) { - appendOutline(location, to, sortedMembers) - } - } - } - } - - fun formatOutline(location: Location, nodes: Iterable): String = - StringBuilder().apply { appendOutline(location, this, nodes) }.toString() -} diff --git a/src/Formats/StandardFormats.kt b/src/Formats/StandardFormats.kt deleted file mode 100644 index 94e1b115..00000000 --- a/src/Formats/StandardFormats.kt +++ /dev/null @@ -1,38 +0,0 @@ -package org.jetbrains.dokka.Formats - -import org.jetbrains.dokka.* - -abstract class KotlinFormatDescriptorBase : FormatDescriptor { - override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class - override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class - - override val generatorServiceClass = FileGenerator::class -} - -class HtmlFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = HtmlFormatService::class - override val outlineServiceClass = HtmlFormatService::class -} - -class HtmlAsJavaFormatDescriptor : FormatDescriptor { - override val formatServiceClass = HtmlFormatService::class - override val outlineServiceClass = HtmlFormatService::class - override val generatorServiceClass = FileGenerator::class - override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class - override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class -} - -class KotlinWebsiteFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = KotlinWebsiteFormatService::class - override val outlineServiceClass = YamlOutlineService::class -} - -class JekyllFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = JekyllFormatService::class - override val outlineServiceClass = null -} - -class MarkdownFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = MarkdownFormatService::class - override val outlineServiceClass = null -} diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt deleted file mode 100644 index 32a2b68a..00000000 --- a/src/Formats/StructuredFormatService.kt +++ /dev/null @@ -1,367 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.LanguageService.RenderMode -import java.util.* - -data class FormatLink(val text: String, val href: String) - -enum class ListKind { - Ordered, - Unordered -} - -abstract class StructuredFormatService(locationService: LocationService, - val languageService: LanguageService, - override val extension: String, - val linkExtension: String = extension) : FormatService { - val locationService: LocationService = locationService.withExtension(linkExtension) - - abstract fun appendBlockCode(to: StringBuilder, line: String, language: String) - abstract fun appendHeader(to: StringBuilder, text: String, level: Int = 1) - abstract fun appendParagraph(to: StringBuilder, text: String) - abstract fun appendLine(to: StringBuilder, text: String) - abstract fun appendLine(to: StringBuilder) - abstract fun appendAnchor(to: StringBuilder, anchor: String) - - abstract fun appendTable(to: StringBuilder, body: () -> Unit) - abstract fun appendTableHeader(to: StringBuilder, body: () -> Unit) - abstract fun appendTableBody(to: StringBuilder, body: () -> Unit) - abstract fun appendTableRow(to: StringBuilder, body: () -> Unit) - abstract fun appendTableCell(to: StringBuilder, body: () -> Unit) - - abstract fun formatText(text: String): String - abstract fun formatSymbol(text: String): String - abstract fun formatKeyword(text: String): String - abstract fun formatIdentifier(text: String, kind: IdentifierKind): String - fun formatEntity(text: String): String = text - abstract fun formatLink(text: String, href: String): String - open fun formatLink(link: FormatLink): String = formatLink(formatText(link.text), link.href) - abstract fun formatStrong(text: String): String - abstract fun formatStrikethrough(text: String): String - abstract fun formatEmphasis(text: String): String - abstract fun formatCode(code: String): String - abstract fun formatUnorderedList(text: String): String - abstract fun formatOrderedList(text: String): String - abstract fun formatListItem(text: String, kind: ListKind): String - abstract fun formatBreadcrumbs(items: Iterable): String - abstract fun formatNonBreakingSpace(): String - open fun formatSoftLineBreak(): String = "" - open fun formatIndentedSoftLineBreak(): String = "" - - open fun formatText(location: Location, nodes: Iterable, listKind: ListKind = ListKind.Unordered): String { - return nodes.map { formatText(location, it, listKind) }.joinToString("") - } - - open fun formatText(location: Location, content: ContentNode, listKind: ListKind = ListKind.Unordered): String { - return StringBuilder().apply { - when (content) { - is ContentText -> append(formatText(content.text)) - is ContentSymbol -> append(formatSymbol(content.text)) - is ContentKeyword -> append(formatKeyword(content.text)) - is ContentIdentifier -> append(formatIdentifier(content.text, content.kind)) - is ContentNonBreakingSpace -> append(formatNonBreakingSpace()) - is ContentSoftLineBreak -> append(formatSoftLineBreak()) - is ContentIndentedSoftLineBreak -> append(formatIndentedSoftLineBreak()) - is ContentEntity -> append(formatEntity(content.text)) - is ContentStrong -> append(formatStrong(formatText(location, content.children))) - is ContentStrikethrough -> append(formatStrikethrough(formatText(location, content.children))) - is ContentCode -> append(formatCode(formatText(location, content.children))) - is ContentEmphasis -> append(formatEmphasis(formatText(location, content.children))) - is ContentUnorderedList -> append(formatUnorderedList(formatText(location, content.children, ListKind.Unordered))) - is ContentOrderedList -> append(formatOrderedList(formatText(location, content.children, ListKind.Ordered))) - is ContentListItem -> append(formatListItem(formatText(location, content.children), listKind)) - - is ContentNodeLink -> { - val node = content.node - val linkTo = if (node != null) locationHref(location, node) else "#" - val linkText = formatText(location, content.children) - if (linkTo == ".") { - append(linkText) - } else { - append(formatLink(linkText, linkTo)) - } - } - is ContentExternalLink -> { - val linkText = formatText(location, content.children) - if (content.href == ".") { - append(linkText) - } else { - append(formatLink(linkText, content.href)) - } - } - is ContentParagraph -> appendParagraph(this, formatText(location, content.children)) - is ContentBlockCode -> appendBlockCode(this, formatText(location, content.children), content.language) - is ContentHeading -> appendHeader(this, formatText(location, content.children), content.level) - is ContentBlock -> append(formatText(location, content.children)) - } - }.toString() - } - - open fun link(from: DocumentationNode, to: DocumentationNode): FormatLink = link(from, to, extension) - - open fun link(from: DocumentationNode, to: DocumentationNode, extension: String): FormatLink { - return FormatLink(to.name, locationService.relativePathToLocation(from, to)) - } - - fun locationHref(from: Location, to: DocumentationNode): String { - val topLevelPage = to.references(DocumentationReference.Kind.TopLevelPage).singleOrNull()?.to - if (topLevelPage != null) { - return from.relativePathTo(locationService.location(topLevelPage), to.name) - } - return from.relativePathTo(locationService.location(to)) - } - - fun appendDocumentation(location: Location, to: StringBuilder, overloads: Iterable) { - val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node -> node.content } - - for ((summary, items) in breakdownBySummary) { - appendAsOverloadGroup(to) { - items.forEach { - val rendered = languageService.render(it) - appendAsSignature(to, rendered) { - to.append(formatCode(formatText(location, rendered))) - it.appendSourceLink(to) - } - it.appendOverrides(to) - it.appendDeprecation(location, to) - } - // All items have exactly the same documentation, so we can use any item to render it - val item = items.first() - item.details(DocumentationNode.Kind.OverloadGroupNote).forEach { - to.append(formatText(location, it.content)) - } - to.append(formatText(location, item.content.summary)) - appendDescription(location, to, item) - appendLine(to) - appendLine(to) - } - } - } - - private fun DocumentationNode.isModuleOrPackage(): Boolean = - kind == DocumentationNode.Kind.Module || kind == DocumentationNode.Kind.Package - - protected open fun appendAsSignature(to: StringBuilder, node: ContentNode, block: () -> Unit) { - block() - } - - protected open fun appendAsOverloadGroup(to: StringBuilder, block: () -> Unit) { - block() - } - - fun appendDescription(location: Location, to: StringBuilder, node: DocumentationNode) { - if (node.content.description != ContentEmpty) { - appendLine(to, formatText(location, node.content.description)) - appendLine(to) - } - node.content.getSectionsWithSubjects().forEach { - appendSectionWithSubject(it.key, location, it.value, to) - } - - for (section in node.content.sections.filter { it.subjectName == null }) { - appendLine(to, formatStrong(formatText(section.tag))) - appendLine(to, formatText(location, section)) - } - } - - fun Content.getSectionsWithSubjects(): Map> = - sections.filter { it.subjectName != null }.groupBy { it.tag } - - fun appendSectionWithSubject(title: String, location: Location, subjectSections: List, to: StringBuilder) { - appendHeader(to, title, 3) - subjectSections.forEach { - val subjectName = it.subjectName - if (subjectName != null) { - appendAnchor(to, subjectName) - to.append(formatCode(subjectName)).append(" - ") - to.append(formatText(location, it)) - appendLine(to) - } - } - } - - private fun DocumentationNode.appendOverrides(to: StringBuilder) { - overrides.forEach { - to.append("Overrides ") - val location = locationService.relativePathToLocation(this, it) - appendLine(to, formatLink(FormatLink(it.owner!!.name + "." + it.name, location))) - } - } - - private fun DocumentationNode.appendDeprecation(location: Location, to: StringBuilder) { - if (deprecation != null) { - val deprecationParameter = deprecation!!.details(DocumentationNode.Kind.Parameter).firstOrNull() - val deprecationValue = deprecationParameter?.details(DocumentationNode.Kind.Value)?.firstOrNull() - if (deprecationValue != null) { - to.append(formatStrong("Deprecated:")).append(" ") - appendLine(to, formatText(deprecationValue.name.removeSurrounding("\""))) - appendLine(to) - } else if (deprecation?.content != Content.Empty) { - to.append(formatStrong("Deprecated:")).append(" ") - to.append(formatText(location, deprecation!!.content)) - } else { - appendLine(to, formatStrong("Deprecated")) - appendLine(to) - } - } - } - - private fun DocumentationNode.appendSourceLink(to: StringBuilder) { - val sourceUrl = details(DocumentationNode.Kind.SourceUrl).firstOrNull() - if (sourceUrl != null) { - to.append(" ") - appendLine(to, formatLink("(source)", sourceUrl.name)) - } else { - appendLine(to) - } - } - - fun appendLocation(location: Location, to: StringBuilder, nodes: Iterable) { - val singleNode = nodes.singleOrNull() - if (singleNode != null && singleNode.isModuleOrPackage()) { - if (singleNode.kind == DocumentationNode.Kind.Package) { - appendHeader(to, "Package " + formatText(singleNode.name), 2) - } - to.append(formatText(location, singleNode.content)) - } else { - val breakdownByName = nodes.groupBy { node -> node.name } - for ((name, items) in breakdownByName) { - appendHeader(to, formatText(name)) - appendDocumentation(location, to, items) - } - } - } - - private fun appendSection(location: Location, caption: String, nodes: List, node: DocumentationNode, to: StringBuilder) { - if (nodes.any()) { - appendHeader(to, caption, 3) - - val children = nodes.sortedBy { it.name } - val membersMap = children.groupBy { link(node, it) } - - appendTable(to) { - appendTableBody(to) { - for ((memberLocation, members) in membersMap) { - appendTableRow(to) { - appendTableCell(to) { - to.append(formatLink(memberLocation)) - } - appendTableCell(to) { - val breakdownBySummary = members.groupBy { formatText(location, it.summary) } - for ((summary, items) in breakdownBySummary) { - appendSummarySignatures(items, location, to) - if (!summary.isEmpty()) { - to.append(summary) - } - } - } - } - } - } - } - } - } - - private fun appendSummarySignatures(items: List, location: Location, to: StringBuilder) { - val summarySignature = languageService.summarizeSignatures(items) - if (summarySignature != null) { - appendAsSignature(to, summarySignature) { - appendLine(to, summarySignature.signatureToText(location)) - } - return - } - val renderedSignatures = items.map { languageService.render(it, RenderMode.SUMMARY) } - renderedSignatures.subList(0, renderedSignatures.size - 1).forEach { - appendAsSignature(to, it) { - appendLine(to, it.signatureToText(location)) - } - } - appendAsSignature(to, renderedSignatures.last()) { - to.append(renderedSignatures.last().signatureToText(location)) - } - } - - private fun ContentNode.signatureToText(location: Location): String { - return if (this is ContentBlock && this.isEmpty()) { - "" - } else { - val signatureAsCode = ContentCode() - signatureAsCode.append(this) - formatText(location, signatureAsCode) - } - } - - override fun appendNodes(location: Location, to: StringBuilder, nodes: Iterable) { - val breakdownByLocation = nodes.groupBy { node -> - formatBreadcrumbs(node.path.filterNot { it.name.isEmpty() }.map { link(node, it) }) - } - - for ((breadcrumbs, items) in breakdownByLocation) { - appendLine(to, breadcrumbs) - appendLine(to) - appendLocation(location, to, items.filter { it.kind != DocumentationNode.Kind.ExternalClass }) - } - - for (node in nodes) { - if (node.kind == DocumentationNode.Kind.ExternalClass) { - appendSection(location, "Extensions for ${node.name}", node.members, node, to) - continue - } - - appendSection(location, "Packages", node.members(DocumentationNode.Kind.Package), node, to) - appendSection(location, "Types", node.members.filter { it.kind in DocumentationNode.Kind.classLike }, node, to) - appendSection(location, "Extensions for External Classes", node.members(DocumentationNode.Kind.ExternalClass), node, to) - appendSection(location, "Enum Values", node.members(DocumentationNode.Kind.EnumItem), node, to) - appendSection(location, "Constructors", node.members(DocumentationNode.Kind.Constructor), node, to) - appendSection(location, "Properties", node.members(DocumentationNode.Kind.Property), node, to) - appendSection(location, "Inherited Properties", node.inheritedMembers(DocumentationNode.Kind.Property), node, to) - appendSection(location, "Functions", node.members(DocumentationNode.Kind.Function), node, to) - appendSection(location, "Inherited Functions", node.inheritedMembers(DocumentationNode.Kind.Function), node, to) - appendSection(location, "Companion Object Properties", node.members(DocumentationNode.Kind.CompanionObjectProperty), node, to) - appendSection(location, "Companion Object Functions", node.members(DocumentationNode.Kind.CompanionObjectFunction), node, to) - appendSection(location, "Other members", node.members.filter { - it.kind !in setOf( - DocumentationNode.Kind.Class, - DocumentationNode.Kind.Interface, - DocumentationNode.Kind.Enum, - DocumentationNode.Kind.Object, - DocumentationNode.Kind.AnnotationClass, - DocumentationNode.Kind.Constructor, - DocumentationNode.Kind.Property, - DocumentationNode.Kind.Package, - DocumentationNode.Kind.Function, - DocumentationNode.Kind.CompanionObjectProperty, - DocumentationNode.Kind.CompanionObjectFunction, - DocumentationNode.Kind.ExternalClass, - DocumentationNode.Kind.EnumItem - ) - }, node, to) - - val allExtensions = collectAllExtensions(node) - appendSection(location, "Extension Properties", allExtensions.filter { it.kind == DocumentationNode.Kind.Property }, node, to) - appendSection(location, "Extension Functions", allExtensions.filter { it.kind == DocumentationNode.Kind.Function }, node, to) - appendSection(location, "Companion Object Extension Properties", allExtensions.filter { it.kind == DocumentationNode.Kind.CompanionObjectProperty }, node, to) - appendSection(location, "Companion Object Extension Functions", allExtensions.filter { it.kind == DocumentationNode.Kind.CompanionObjectFunction }, node, to) - appendSection(location, "Inheritors", - node.inheritors.filter { it.kind != DocumentationNode.Kind.EnumItem }, node, to) - appendSection(location, "Links", node.links, node, to) - - } - } - - private fun collectAllExtensions(node: DocumentationNode): Collection { - val result = LinkedHashSet() - val visited = hashSetOf() - - fun collect(node: DocumentationNode) { - if (!visited.add(node)) return - result.addAll(node.extensions) - node.references(DocumentationReference.Kind.Superclass).forEach { collect(it.to) } - } - - collect(node) - - return result - - } -} \ No newline at end of file diff --git a/src/Formats/YamlOutlineService.kt b/src/Formats/YamlOutlineService.kt deleted file mode 100644 index 7968824c..00000000 --- a/src/Formats/YamlOutlineService.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import java.io.File - -class YamlOutlineService @Inject constructor(val locationService: LocationService, - 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: ${locationService.location(node).path}") - } - - override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { - val indent = " ".repeat(outlineLevel) - to.appendln("$indent content:") - outlineLevel++ - body() - outlineLevel-- - } -} diff --git a/src/Generation/ConsoleGenerator.kt b/src/Generation/ConsoleGenerator.kt deleted file mode 100644 index 803a16e4..00000000 --- a/src/Generation/ConsoleGenerator.kt +++ /dev/null @@ -1,42 +0,0 @@ -package org.jetbrains.dokka - -public class ConsoleGenerator(val signatureGenerator: LanguageService, val locationService: LocationService) { - val IndentStep = " " - - public fun generate(node: DocumentationNode, indent: String = "") { - println("@${locationService.location(node).path}") - generateHeader(node, indent) - //generateDetails(node, indent) - generateMembers(node, indent) - generateLinks(node, indent) - } - - public fun generateHeader(node: DocumentationNode, indent: String = "") { - println(indent + signatureGenerator.render(node)) - val docString = node.content.toString() - if (!docString.isEmpty()) - println("$indent\"${docString.replace("\n", "\n$indent")}\"") - println() - } - - public fun generateMembers(node: DocumentationNode, indent: String = "") { - val items = node.members.sortedBy { it.name } - for (child in items) - generate(child, indent + IndentStep) - } - - public fun generateDetails(node: DocumentationNode, indent: String = "") { - val items = node.details - for (child in items) - generate(child, indent + " ") - } - - public fun generateLinks(node: DocumentationNode, indent: String = "") { - val items = node.links - if (items.isEmpty()) - return - println("$indent Links") - for (child in items) - generate(child, indent + " ") - } -} \ No newline at end of file diff --git a/src/Generation/FileGenerator.kt b/src/Generation/FileGenerator.kt deleted file mode 100644 index a762bae3..00000000 --- a/src/Generation/FileGenerator.kt +++ /dev/null @@ -1,57 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import java.io.File -import java.io.FileOutputStream -import java.io.IOException -import java.io.OutputStreamWriter - -public class FileGenerator @Inject constructor(val locationService: FileLocationService) : Generator { - - @set:Inject(optional = true) var outlineService: OutlineFormatService? = null - @set:Inject(optional = true) lateinit var formatService: FormatService - - override fun buildPages(nodes: Iterable) { - val specificLocationService = locationService.withExtension(formatService.extension) - - for ((location, items) in nodes.groupBy { specificLocationService.location(it) }) { - val file = location.file - file.parentFile?.mkdirsOrFail() - try { - FileOutputStream(file).use { - OutputStreamWriter(it, Charsets.UTF_8).use { - it.write(formatService.format(location, items)) - } - } - } catch (e: Throwable) { - println(e) - } - buildPages(items.flatMap { it.members }) - } - } - - override fun buildOutlines(nodes: Iterable) { - val outlineService = this.outlineService ?: return - for ((location, items) in nodes.groupBy { locationService.location(it) }) { - val file = outlineService.getOutlineFileName(location) - file.parentFile?.mkdirsOrFail() - FileOutputStream(file).use { - OutputStreamWriter(it, Charsets.UTF_8).use { - it.write(outlineService.formatOutline(location, items)) - } - } - } - } - - override fun buildSupportFiles() { - FileOutputStream(locationService.location(listOf("style.css"), false).file).use { - javaClass.getResourceAsStream("/dokka/styles/style.css").copyTo(it) - } - } -} - -private fun File.mkdirsOrFail() { - if (!mkdirs() && !exists()) { - throw IOException("Failed to create directory $this") - } -} \ No newline at end of file diff --git a/src/Generation/Generator.kt b/src/Generation/Generator.kt deleted file mode 100644 index ac10a6a5..00000000 --- a/src/Generation/Generator.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.jetbrains.dokka - -public interface Generator { - fun buildPages(nodes: Iterable) - fun buildOutlines(nodes: Iterable) - fun buildSupportFiles() -} - -fun Generator.buildAll(nodes: Iterable) { - buildPages(nodes) - buildOutlines(nodes) - buildSupportFiles() -} - -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)) diff --git a/src/Java/JavaPsiDocumentationBuilder.kt b/src/Java/JavaPsiDocumentationBuilder.kt deleted file mode 100644 index 3c9875cd..00000000 --- a/src/Java/JavaPsiDocumentationBuilder.kt +++ /dev/null @@ -1,266 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.intellij.psi.* -import org.jetbrains.dokka.DocumentationNode.Kind - -fun getSignature(element: PsiElement?) = when(element) { - is PsiClass -> element.qualifiedName - is PsiField -> element.containingClass!!.qualifiedName + "#" + element.name - is PsiMethod -> - element.containingClass!!.qualifiedName + "#" + element.name + "(" + - element.parameterList.parameters.map { it.type.typeSignature() }.joinToString(",") + ")" - else -> null -} - -private fun PsiType.typeSignature(): String = when(this) { - is PsiArrayType -> "Array<${componentType.typeSignature()}>" - 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 -> "Array" - else -> psiType.canonicalText -} - -interface JavaDocumentationBuilder { - fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map) -} - -class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { - private val options: DocumentationOptions - private val refGraph: NodeReferenceGraph - private val docParser: JavaDocumentationParser - - @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph) { - this.options = options - this.refGraph = refGraph - this.docParser = JavadocParser(refGraph) - } - - constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) { - this.options = options - this.refGraph = refGraph - this.docParser = docParser - } - - override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map) { - if (file.classes.all { skipElement(it) }) { - return - } - val packageNode = module.findOrCreatePackageNode(file.packageName, emptyMap()) - appendClasses(packageNode, file.classes) - } - - fun appendClasses(packageNode: DocumentationNode, classes: Array) { - 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, DocumentationReference.Kind.Link) - } - } - - fun link(element: PsiElement?, node: DocumentationNode, kind: DocumentationReference.Kind) { - val qualifiedName = getSignature(element) - if (qualifiedName != null) { - refGraph.link(qualifiedName, node, kind) - } - } - - fun nodeForElement(element: PsiNamedElement, - kind: Kind, - name: String = element.name ?: ""): DocumentationNode { - val (docComment, deprecatedContent) = docParser.parseDocumentation(element) - val node = DocumentationNode(name, docComment, kind) - 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") DocumentationReference.Kind.Deprecation else DocumentationReference.Kind.Annotation) - } - } - } - if (deprecatedContent != null) { - val deprecationNode = DocumentationNode("", deprecatedContent, Kind.Modifier) - node.append(deprecationNode, DocumentationReference.Kind.Deprecation) - } - if (element is PsiDocCommentOwner && element.isDeprecated && node.deprecation == null) { - val deprecationNode = DocumentationNode("", Content.of(ContentText("Deprecated")), Kind.Modifier) - node.append(deprecationNode, DocumentationReference.Kind.Deprecation) - } - return node - } - - fun ignoreAnnotation(annotation: PsiAnnotation) = when(annotation.qualifiedName) { - "java.lang.SuppressWarnings" -> true - else -> false - } - - fun DocumentationNode.appendChildren(elements: Array, - kind: DocumentationReference.Kind = DocumentationReference.Kind.Member, - buildFn: T.() -> DocumentationNode) { - elements.forEach { - if (!skipElement(it)) { - append(it.buildFn(), kind) - } - } - } - - private fun skipElement(element: Any) = skipElementByVisibility(element) || hasSuppressTag(element) - - private fun skipElementByVisibility(element: Any): Boolean = - !options.includeNonPublic && element is PsiModifierListOwner && - (element.hasModifierProperty(PsiModifier.PRIVATE) || element.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) - - private fun hasSuppressTag(element: Any) = - element is PsiDocCommentOwner && element.docComment?.let { it.findTagByName("suppress") != null } ?: false - - fun DocumentationNode.appendMembers(elements: Array, buildFn: T.() -> DocumentationNode) = - appendChildren(elements, DocumentationReference.Kind.Member, buildFn) - - fun DocumentationNode.appendDetails(elements: Array, buildFn: T.() -> DocumentationNode) = - appendChildren(elements, DocumentationReference.Kind.Detail, buildFn) - - fun PsiClass.build(): DocumentationNode { - val kind = when { - isInterface -> DocumentationNode.Kind.Interface - isEnum -> DocumentationNode.Kind.Enum - isAnnotationType -> DocumentationNode.Kind.AnnotationClass - else -> DocumentationNode.Kind.Class - } - val node = nodeForElement(this, kind) - superTypes.filter { !ignoreSupertype(it) }.forEach { - node.appendType(it, Kind.Supertype) - val superClass = it.resolve() - if (superClass != null) { - link(superClass, node, DocumentationReference.Kind.Inheritor) - } - } - node.appendDetails(typeParameters) { build() } - node.appendMembers(methods) { build() } - node.appendMembers(fields) { build() } - node.appendMembers(innerClasses) { build() } - register(this, node) - return node - } - - 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.appendModifiers(this) - register(this, node) - return node - } - - private fun PsiField.nodeKind(): Kind = when { - this is PsiEnumConstant -> Kind.EnumItem - else -> Kind.Field - } - - fun PsiMethod.build(): DocumentationNode { - val node = nodeForElement(this, nodeKind(), - if (isConstructor) "" 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(): Kind = when { - isConstructor -> Kind.Constructor - else -> Kind.Function - } - - fun PsiParameter.build(): DocumentationNode { - val node = nodeForElement(this, Kind.Parameter) - node.appendType(type) - if (type is PsiEllipsisType) { - node.appendTextNode("vararg", Kind.Modifier, DocumentationReference.Kind.Detail) - } - return node - } - - fun PsiTypeParameter.build(): DocumentationNode { - val node = nodeForElement(this, Kind.TypeParameter) - extendsListTypes.forEach { node.appendType(it, Kind.UpperBound) } - implementsListTypes.forEach { node.appendType(it, Kind.UpperBound) } - return node - } - - fun DocumentationNode.appendModifiers(element: PsiModifierListOwner) { - val modifierList = element.modifierList ?: return - - PsiModifier.MODIFIERS.forEach { - if (modifierList.hasExplicitModifier(it)) { - appendTextNode(it, Kind.Modifier) - } - } - } - - fun DocumentationNode.appendType(psiType: PsiType?, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type) { - if (psiType == null) { - return - } - append(psiType.build(kind), DocumentationReference.Kind.Detail) - } - - fun PsiType.build(kind: DocumentationNode.Kind = DocumentationNode.Kind.Type): DocumentationNode { - val name = mapTypeName(this) - val node = DocumentationNode(name, Content.Empty, kind) - if (this is PsiClassType) { - node.appendDetails(parameters) { build(Kind.Type) } - link(node, resolve()) - } - if (this is PsiArrayType && this !is PsiEllipsisType) { - node.append(componentType.build(Kind.Type), DocumentationReference.Kind.Detail) - } - return node - } - - fun PsiAnnotation.build(): DocumentationNode { - val node = DocumentationNode(nameReferenceElement?.text ?: "", Content.Empty, DocumentationNode.Kind.Annotation) - parameterList.attributes.forEach { - val parameter = DocumentationNode(it.name ?: "value", Content.Empty, DocumentationNode.Kind.Parameter) - val value = it.value - if (value != null) { - val valueText = (value as? PsiLiteralExpression)?.value as? String ?: value.text - val valueNode = DocumentationNode(valueText, Content.Empty, DocumentationNode.Kind.Value) - parameter.append(valueNode, DocumentationReference.Kind.Detail) - } - node.append(parameter, DocumentationReference.Kind.Detail) - } - return node - } -} diff --git a/src/Java/JavadocParser.kt b/src/Java/JavadocParser.kt deleted file mode 100644 index 1378a5a7..00000000 --- a/src/Java/JavadocParser.kt +++ /dev/null @@ -1,170 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.* -import com.intellij.psi.javadoc.PsiDocTag -import com.intellij.psi.javadoc.PsiDocTagValue -import com.intellij.psi.javadoc.PsiDocToken -import com.intellij.psi.javadoc.PsiInlineDocTag -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import org.jsoup.nodes.Node -import org.jsoup.nodes.TextNode - -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) : JavaDocumentationParser { - override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { - val docComment = (element as? PsiDocCommentOwner)?.docComment - if (docComment == null) return JavadocParseResult.Empty - val result = MutableContent() - var deprecatedContent: Content? = null - val para = ContentParagraph() - result.append(para) - para.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }) - docComment.tags.forEach { tag -> - when(tag.name) { - "see" -> result.convertSeeTag(tag) - "deprecated" -> { - deprecatedContent = Content() - deprecatedContent!!.convertJavadocElements(tag.contentElements()) - } - else -> { - val subjectName = tag.getSubjectName() - val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) - - section.convertJavadocElements(tag.contentElements()) - } - } - } - return JavadocParseResult(result, deprecatedContent) - } - - private fun PsiDocTag.contentElements(): Iterable { - 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 ContentBlock.convertJavadocElements(elements: Iterable) { - val htmlBuilder = StringBuilder() - elements.forEach { - if (it is PsiInlineDocTag) { - htmlBuilder.append(convertInlineDocTag(it)) - } else { - htmlBuilder.append(it.text) - } - } - val doc = Jsoup.parse(htmlBuilder.toString().trimStart()) - doc.body().childNodes().forEach { - convertHtmlNode(it) - } - } - - private fun ContentBlock.convertHtmlNode(node: Node) { - if (node is TextNode) { - append(ContentText(node.text())) - } else if (node is Element) { - val childBlock = createBlock(node) - node.childNodes().forEach { - childBlock.convertHtmlNode(it) - } - append(childBlock) - } - } - - private fun createBlock(element: Element): ContentBlock = when(element.tagName()) { - "p" -> ContentParagraph() - "b", "strong" -> ContentStrong() - "i", "em" -> ContentEmphasis() - "s", "del" -> ContentStrikethrough() - "code" -> ContentCode() - "pre" -> ContentBlockCode() - "ul" -> ContentUnorderedList() - "ol" -> ContentOrderedList() - "li" -> ContentListItem() - "a" -> createLink(element) - else -> ContentBlock() - } - - private fun createLink(element: Element): ContentBlock { - val docref = element.attr("docref") - if (docref != null) { - return ContentNodeLazyLink(docref, { -> refGraph.lookup(docref)}) - } - val href = element.attr("href") - if (href != null) { - return ContentExternalLink(href) - } else { - return ContentBlock() - } - } - - private fun MutableContent.convertSeeTag(tag: PsiDocTag) { - val linkElement = tag.linkElement() - if (linkElement == null) { - return - } - val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null) - val linkSignature = resolveLink(linkElement) - val text = ContentText(linkElement.text) - if (linkSignature != null) { - val linkNode = ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookup(linkSignature)}) - linkNode.append(text) - seeSection.append(linkNode) - } else { - seeSection.append(text) - } - } - - private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) { - "link", "linkplain" -> { - val valueElement = tag.linkElement() - val linkSignature = resolveLink(valueElement) - if (linkSignature != null) { - val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text - val link = "${labelText.htmlEscape()}" - if (tag.name == "link") "$link" 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") "$escaped" else escaped - } - else -> tag.text - } - - private fun PsiDocTag.linkElement(): PsiElement? = - valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } - - private fun resolveLink(valueElement: PsiElement?): String? { - val target = valueElement?.reference?.resolve() - if (target != null) { - return getSignature(target) - } - return null - } - - fun PsiDocTag.getSubjectName(): String? { - if (name == "param" || name == "throws" || name == "exception") { - return valueElement?.text - } - return null - } -} diff --git a/src/Kotlin/ContentBuilder.kt b/src/Kotlin/ContentBuilder.kt deleted file mode 100644 index c4bb18de..00000000 --- a/src/Kotlin/ContentBuilder.kt +++ /dev/null @@ -1,132 +0,0 @@ -package org.jetbrains.dokka - -import org.intellij.markdown.MarkdownElementTypes -import org.intellij.markdown.MarkdownTokenTypes -import org.intellij.markdown.html.entities.EntityConverter -import java.util.* - -public fun buildContent(tree: MarkdownNode, linkResolver: (String) -> ContentBlock, inline: Boolean = false): MutableContent { - val result = MutableContent() - if (inline) { - buildInlineContentTo(tree, result, linkResolver) - } - else { - buildContentTo(tree, result, linkResolver) - } - return result -} - -public fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: (String) -> ContentBlock) { -// println(tree.toTestString()) - val nodeStack = ArrayDeque() - 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 -> appendNodeWithChildren(ContentCode()) - MarkdownElementTypes.CODE_BLOCK, - MarkdownElementTypes.CODE_FENCE -> appendNodeWithChildren(ContentBlockCode()) - MarkdownElementTypes.PARAGRAPH -> appendNodeWithChildren(ContentParagraph()) - - MarkdownElementTypes.INLINE_LINK -> { - val label = node.child(MarkdownElementTypes.LINK_TEXT)?.child(MarkdownTokenTypes.TEXT) - val destination = node.child(MarkdownElementTypes.LINK_DESTINATION) - if (label != null) { - if (destination != null) { - val link = ContentExternalLink(destination.text) - link.append(ContentText(label.text)) - parent.append(link) - } else { - val link = ContentExternalLink(label.text) - link.append(ContentText(label.text)) - parent.append(link) - } - } - } - MarkdownElementTypes.SHORT_REFERENCE_LINK, - MarkdownElementTypes.FULL_REFERENCE_LINK -> { - val label = node.child(MarkdownElementTypes.LINK_LABEL)?.child(MarkdownTokenTypes.TEXT) - if (label != null) { - val link = linkResolver(label.text) - val linkText = node.child(MarkdownElementTypes.LINK_TEXT)?.child(MarkdownTokenTypes.TEXT) - link.append(ContentText(linkText?.text ?: label.text)) - parent.append(link) - } - } - MarkdownTokenTypes.WHITE_SPACE, - MarkdownTokenTypes.EOL -> { - if (keepWhitespace(nodeStack.peek()) && node.parent?.children?.last() != node) { - parent.append(ContentText(node.text)) - } - } - - MarkdownTokenTypes.CODE -> { - val block = ContentBlockCode() - block.append(ContentText(node.text)) - parent.append(block) - } - - 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.COLON, - MarkdownTokenTypes.DOUBLE_QUOTE, - MarkdownTokenTypes.LT, - MarkdownTokenTypes.GT, - MarkdownTokenTypes.LPAREN, - MarkdownTokenTypes.RPAREN, - MarkdownTokenTypes.LBRACKET, - MarkdownTokenTypes.RBRACKET, - MarkdownTokenTypes.CODE_FENCE_CONTENT -> { - parent.append(ContentText(node.text)) - } - else -> { - processChildren() - } - } - } -} - -private fun keepWhitespace(node: ContentNode) = node is ContentParagraph || node is ContentSection - -public fun buildInlineContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: (String) -> ContentBlock) { - val inlineContent = tree.children.singleOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree) - inlineContent.forEach { - buildContentTo(it, target, linkResolver) - } -} - diff --git a/src/Kotlin/DeclarationLinkResolver.kt b/src/Kotlin/DeclarationLinkResolver.kt deleted file mode 100644 index 2569bc71..00000000 --- a/src/Kotlin/DeclarationLinkResolver.kt +++ /dev/null @@ -1,43 +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.idea.kdoc.resolveKDocLink - -class DeclarationLinkResolver - @Inject constructor(val resolutionFacade: DokkaResolutionFacade, - val refGraph: NodeReferenceGraph, - val logger: DokkaLogger) { - fun resolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock { - val symbol = try { - val symbols = resolveKDocLink(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) { - return ContentNodeLazyLink(href, { -> refGraph.lookup(symbol.signature()) }) - } - if ("/" in href) { - return ContentExternalLink(href) - } - logger.warn("Unresolved link to $href in doc comment of ${fromDescriptor.signatureWithSourceLocation()}") - return ContentExternalLink("#") - } - - fun findTargetSymbol(symbols: Collection): DeclarationDescriptor? { - if (symbols.isEmpty()) { - return null - } - val symbol = symbols.first() - if (symbol is CallableMemberDescriptor && symbol.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - return symbol.overriddenDescriptors.firstOrNull() - } - return symbol - } - -} \ No newline at end of file diff --git a/src/Kotlin/DescriptorDocumentationParser.kt b/src/Kotlin/DescriptorDocumentationParser.kt deleted file mode 100644 index b7705ec9..00000000 --- a/src/Kotlin/DescriptorDocumentationParser.kt +++ /dev/null @@ -1,199 +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.jetbrains.dokka.* -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.idea.kdoc.KDocFinder -import org.jetbrains.kotlin.idea.kdoc.getResolutionScope -import org.jetbrains.kotlin.incremental.components.NoLookupLocation -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.name.Name -import org.jetbrains.kotlin.psi.KtBlockExpression -import org.jetbrains.kotlin.psi.KtDeclarationWithBody -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.ResolutionScope -import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered -import org.jetbrains.kotlin.resolve.source.PsiSourceElement - -class DescriptorDocumentationParser - @Inject constructor(val options: DocumentationOptions, - val logger: DokkaLogger, - val linkResolver: DeclarationLinkResolver, - val resolutionFacade: DokkaResolutionFacade, - val refGraph: NodeReferenceGraph) -{ - fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false): Content = - parseDocumentationAndDetails(descriptor, inline).first - - fun parseDocumentationAndDetails(descriptor: DeclarationDescriptor, inline: Boolean = false): Pair Unit> { - if (descriptor is JavaClassDescriptor || descriptor is JavaCallableMemberDescriptor) { - return parseJavadoc(descriptor) - } - - val kdoc = KDocFinder.findKDoc(descriptor) ?: findStdlibKDoc(descriptor) - if (kdoc == null) { - if (options.reportUndocumented && !descriptor.isDeprecated() && - descriptor !is ValueParameterDescriptor && descriptor !is TypeParameterDescriptor && - descriptor !is PropertyAccessorDescriptor) { - logger.warn("No documentation for ${descriptor.signatureWithSourceLocation()}") - } - return Content.Empty to { node -> } - } - var kdocText = kdoc.getContent() - // workaround for code fence parsing problem in IJ markdown parser - if (kdocText.endsWith("```") || kdocText.endsWith("~~~")) { - kdocText += "\n" - } - val tree = parseMarkdown(kdocText) - val content = buildContent(tree, { href -> linkResolver.resolveContentLink(descriptor, href) }, inline) - if (kdoc is KDocSection) { - val tags = kdoc.getTags() - tags.forEach { - when (it.name) { - "sample" -> - content.append(functionBody(descriptor, it.getSubjectName())) - "see" -> - content.addTagToSeeAlso(descriptor, it) - else -> { - val section = content.addSection(javadocSectionDisplayName(it.name), it.getSubjectName()) - val sectionContent = it.getContent() - val markdownNode = parseMarkdown(sectionContent) - buildInlineContentTo(markdownNode, section, { href -> linkResolver.resolveContentLink(descriptor, href) }) - } - } - } - } - return content to { node -> } - } - - /** - * 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.getTopLevelClassDescriptors( - FqName.fromSegments(listOf("kotlin", "Any")), NoLookupLocation.FROM_IDE) - anyClassDescriptors.forEach { - val anyMethod = it.getMemberScope(listOf()) - .getDescriptorsFiltered(DescriptorKindFilter.FUNCTIONS, { it == descriptor.name }) - .single() - val kdoc = KDocFinder.findKDoc(anyMethod) - if (kdoc != null) { - return kdoc - } - } - } - } - return null - } - - fun parseJavadoc(descriptor: DeclarationDescriptor): Pair Unit> { - val psi = ((descriptor as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi - if (psi is PsiDocCommentOwner) { - val parseResult = JavadocParser(refGraph).parseDocumentation(psi as PsiNamedElement) - return parseResult.content to { node -> - parseResult.deprecatedContent?.let { - val deprecationNode = DocumentationNode("", it, DocumentationNode.Kind.Modifier) - node.append(deprecationNode, DocumentationReference.Kind.Deprecation) - } - } - } - return Content.Empty to { node -> } - } - - fun KDocSection.getTags(): Array = 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) - } - } - - private fun functionBody(descriptor: DeclarationDescriptor, functionName: String?): ContentNode { - if (functionName == null) { - logger.warn("Missing function name in @sample in ${descriptor.signature()}") - return ContentBlockCode().let() { it.append(ContentText("Missing function name in @sample")); it } - } - val scope = getResolutionScope(resolutionFacade, descriptor) - val rootPackage = resolutionFacade.moduleDescriptor.getPackage(FqName.ROOT) - val rootScope = rootPackage.memberScope - val symbol = resolveInScope(functionName, scope) ?: resolveInScope(functionName, rootScope) - if (symbol == null) { - logger.warn("Unresolved function $functionName in @sample in ${descriptor.signature()}") - return ContentBlockCode().let() { it.append(ContentText("Unresolved: $functionName")); it } - } - val psiElement = DescriptorToSourceUtils.descriptorToDeclaration(symbol) - if (psiElement == null) { - logger.warn("Can't find source for function $functionName in @sample in ${descriptor.signature()}") - return ContentBlockCode().let() { it.append(ContentText("Source not found: $functionName")); it } - } - - val text = when (psiElement) { - is KtDeclarationWithBody -> ContentBlockCode().let() { - val bodyExpression = psiElement.bodyExpression - when (bodyExpression) { - is KtBlockExpression -> bodyExpression.text.removeSurrounding("{", "}") - else -> bodyExpression!!.text - } - } - else -> psiElement.text - } - - val lines = text.trimEnd().split("\n".toRegex()).toTypedArray().filterNot { it.length == 0 } - val indent = lines.map { it.takeWhile { it.isWhitespace() }.count() }.min() ?: 0 - val finalText = lines.map { it.drop(indent) }.joinToString("\n") - return ContentBlockCode("kotlin").let() { it.append(ContentText(finalText)); it } - } - - 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.guess(part) - val partSymbol = currentScope.getContributedDescriptors(DescriptorKindFilter.ALL, { it == symbolName }) - .filter { it.name == symbolName } - .firstOrNull() - - if (partSymbol == null) { - symbol = null - break - } - currentScope = if (partSymbol is ClassDescriptor) - partSymbol.defaultType.memberScope - else - getResolutionScope(resolutionFacade, partSymbol) - symbol = partSymbol - } - - return symbol - } -} diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt deleted file mode 100644 index 6551ded6..00000000 --- a/src/Kotlin/DocumentationBuilder.kt +++ /dev/null @@ -1,653 +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.DocumentationNode.Kind -import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -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.caches.resolve.KotlinCacheService -import org.jetbrains.kotlin.idea.caches.resolve.getModuleInfo -import org.jetbrains.kotlin.idea.kdoc.KDocFinder -import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtModifierListOwner -import org.jetbrains.kotlin.psi.KtParameter -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant -import org.jetbrains.kotlin.resolve.constants.ConstantValue -import org.jetbrains.kotlin.resolve.constants.TypedCompileTimeConstant -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.isDocumentedAnnotation -import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver -import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform -import org.jetbrains.kotlin.resolve.source.PsiSourceElement -import org.jetbrains.kotlin.resolve.source.getPsi -import org.jetbrains.kotlin.types.ErrorUtils -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.TypeProjection - -public data class DocumentationOptions(val outputDir: String, - val outputFormat: String, - val includeNonPublic: Boolean = false, - val reportUndocumented: Boolean = true, - val skipEmptyPackages: Boolean = true, - val skipDeprecated: Boolean = false, - val sourceLinks: List) - -private fun isSamePackage(descriptor1: DeclarationDescriptor, descriptor2: DeclarationDescriptor): Boolean { - val package1 = DescriptorUtils.getParentOfType(descriptor1, PackageFragmentDescriptor::class.java) - val package2 = DescriptorUtils.getParentOfType(descriptor2, PackageFragmentDescriptor::class.java) - return package1 != null && package2 != null && package1.fqName == package2.fqName -} - -interface PackageDocumentationBuilder { - fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, - packageName: FqName, - packageNode: DocumentationNode, - declarations: List) -} - -class DocumentationBuilder - @Inject constructor(val resolutionFacade: DokkaResolutionFacade, - val descriptorDocumentationParser: DescriptorDocumentationParser, - val options: DocumentationOptions, - val refGraph: NodeReferenceGraph, - val logger: DokkaLogger) -{ - val visibleToDocumentation = setOf(Visibilities.PROTECTED, Visibilities.PUBLIC) - 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) - - fun link(node: DocumentationNode, descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) { - refGraph.link(node, descriptor.signature(), kind) - } - - fun link(fromDescriptor: DeclarationDescriptor?, toDescriptor: DeclarationDescriptor?, kind: DocumentationReference.Kind) { - if (fromDescriptor != null && toDescriptor != null) { - refGraph.link(fromDescriptor.signature(), toDescriptor.signature(), kind) - } - } - - fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) { - refGraph.register(descriptor.signature(), node) - } - - fun nodeForDescriptor(descriptor: T, kind: Kind): DocumentationNode where T : DeclarationDescriptor, T : Named { - val (doc, callback) = descriptorDocumentationParser.parseDocumentationAndDetails(descriptor, kind == Kind.Parameter) - val node = DocumentationNode(descriptor.name.asString(), doc, kind).withModifiers(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, DocumentationNode.Kind.Modifier) - } - - fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) { - val modifier = descriptor.visibility.normalize().displayName - appendTextNode(modifier, DocumentationNode.Kind.Modifier) - } - - fun DocumentationNode.appendSupertypes(descriptor: ClassDescriptor) { - val superTypes = descriptor.typeConstructor.supertypes - for (superType in superTypes) { - if (!ignoreSupertype(superType)) { - appendType(superType, DocumentationNode.Kind.Supertype) - val superclass = superType?.constructor?.declarationDescriptor - link(superclass, descriptor, DocumentationReference.Kind.Inheritor) - link(descriptor, superclass, DocumentationReference.Kind.Superclass) - } - } - } - - private fun ignoreSupertype(superType: KotlinType): Boolean { - val superClass = superType.constructor.declarationDescriptor as? ClassDescriptor - if (superClass != null) { - val fqName = DescriptorUtils.getFqNameSafe(superClass).asString() - return fqName == "kotlin.Annotation" || fqName == "kotlin.Enum" || fqName == "kotlin.Any" - } - return false - } - - fun DocumentationNode.appendProjection(projection: TypeProjection, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type) { - if (projection.isStarProjection) { - appendTextNode("*", Kind.Type) - } - else { - appendType(projection.type, kind, projection.projectionKind.label) - } - } - - fun DocumentationNode.appendType(kotlinType: KotlinType?, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type, prefix: String = "") { - if (kotlinType == null) - 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 -> "" - } - val node = DocumentationNode(name, Content.Empty, kind) - if (prefix != "") { - node.appendTextNode(prefix, Kind.Modifier) - } - if (kotlinType.isMarkedNullable) { - node.appendTextNode("?", Kind.NullabilityModifier) - } - if (classifierDescriptor != null) { - link(node, classifierDescriptor, - if (classifierDescriptor.isBoringBuiltinClass()) DocumentationReference.Kind.HiddenLink else DocumentationReference.Kind.Link) - } - - append(node, DocumentationReference.Kind.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.filter { it.isDocumented() }.forEach { - val annotationNode = it.build() - if (annotationNode != null) { - append(annotationNode, - if (annotationNode.isDeprecation()) DocumentationReference.Kind.Deprecation else DocumentationReference.Kind.Annotation) - } - } - } - - fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) { - val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return - KtTokens.MODIFIER_KEYWORDS_ARRAY.filter { it !in knownModifiers }.forEach { - if (psi.hasModifier(it)) { - appendTextNode(it.value, Kind.Modifier) - } - } - } - - fun DocumentationNode.isDeprecation() = name == "Deprecated" || name == "deprecated" - - fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) { - appendSourceLink(sourceElement.getPsi(), options.sourceLinks) - } - - fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind): DocumentationNode? { - // do not include generated code - if (descriptor is CallableMemberDescriptor && descriptor.kind != CallableMemberDescriptor.Kind.DECLARATION) - return null - - if (descriptor.isDocumented()) { - val node = descriptor.build() - append(node, kind) - return node - } - return null - } - - fun DeclarationDescriptor.isDocumented(): Boolean { - return (options.includeNonPublic - || this !is MemberDescriptor - || this.visibility in visibleToDocumentation) && - !isDocumentationSuppressed() && - (!options.skipDeprecated || !isDeprecated()) - } - - fun DocumentationNode.appendMembers(descriptors: Iterable): List { - val nodes = descriptors.map { descriptor -> - if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - val baseDescriptor = descriptor.overriddenDescriptors.firstOrNull() - if (baseDescriptor != null) { - link(this, baseDescriptor, DocumentationReference.Kind.InheritedMember) - } - null - } - else { - val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original - appendChild(descriptorToUse, DocumentationReference.Kind.Member) - } - } - return nodes.filterNotNull() - } - - fun DocumentationNode.appendInPageChildren(descriptors: Iterable, kind: DocumentationReference.Kind) { - descriptors.forEach { descriptor -> - val node = appendChild(descriptor, kind) - node?.addReferenceTo(this, DocumentationReference.Kind.TopLevelPage) - } - } - - fun DocumentationModule.appendFragments(fragments: Collection, - packageContent: Map, - packageDocumentationBuilder: PackageDocumentationBuilder) { - val allFqNames = fragments.map { it.fqName }.distinct() - - for (packageName in allFqNames) { - val declarations = fragments.filter { it.fqName == packageName }.flatMap { it.getMemberScope().getContributedDescriptors() } - - if (options.skipEmptyPackages && declarations.none { it.isDocumented() }) continue - logger.info(" package $packageName: ${declarations.count()} declarations") - val packageNode = findOrCreatePackageNode(packageName.asString(), packageContent) - packageDocumentationBuilder.buildPackageDocumentation(this@DocumentationBuilder, packageName, packageNode, declarations) - } - } - - fun DeclarationDescriptor.build(): DocumentationNode = when (this) { - is ClassDescriptor -> build() - is ConstructorDescriptor -> build() - is PropertyDescriptor -> build() - is FunctionDescriptor -> build() - is TypeParameterDescriptor -> build() - is ValueParameterDescriptor -> build() - is ReceiverParameterDescriptor -> build() - else -> throw IllegalStateException("Descriptor $this is not known") - } - - fun ClassDescriptor.build(): DocumentationNode { - val kind = when (kind) { - ClassKind.OBJECT -> Kind.Object - ClassKind.INTERFACE -> Kind.Interface - ClassKind.ENUM_CLASS -> Kind.Enum - ClassKind.ANNOTATION_CLASS -> Kind.AnnotationClass - ClassKind.ENUM_ENTRY -> Kind.EnumItem - else -> Kind.Class - } - val node = nodeForDescriptor(this, kind) - node.appendSupertypes(this) - if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) { - node.appendInPageChildren(typeConstructor.parameters, DocumentationReference.Kind.Detail) - val constructorsToDocument = if (getKind() == ClassKind.ENUM_CLASS) - constructors.filter { it.valueParameters.size > 0 } - else - constructors - node.appendMembers(constructorsToDocument) - } - val members = defaultType.memberScope.getContributedDescriptors().filter { it != companionObjectDescriptor } - node.appendMembers(members) - node.appendMembers(staticScope.getContributedDescriptors()).forEach { - it.appendTextNode("static", Kind.Modifier) - } - val companionObjectDescriptor = companionObjectDescriptor - if (companionObjectDescriptor != null) { - node.appendMembers(companionObjectDescriptor.defaultType.memberScope.getContributedDescriptors()) - } - node.appendAnnotations(this) - node.appendModifiers(this) - node.appendSourceLink(source) - register(this, node) - return node - } - - fun ConstructorDescriptor.build(): DocumentationNode { - val node = nodeForDescriptor(this, Kind.Constructor) - node.appendInPageChildren(valueParameters, DocumentationReference.Kind.Detail) - 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(): DocumentationNode { - if (ErrorUtils.containsErrorType(this)) { - logger.warn("Found an unresolved type in ${signatureWithSourceLocation()}") - } - - val node = nodeForDescriptor(this, if (inCompanionObject()) Kind.CompanionObjectFunction else Kind.Function) - - node.appendInPageChildren(typeParameters, DocumentationReference.Kind.Detail) - extensionReceiverParameter?.let { node.appendChild(it, DocumentationReference.Kind.Detail) } - node.appendInPageChildren(valueParameters, DocumentationReference.Kind.Detail) - node.appendType(returnType) - node.appendAnnotations(this) - node.appendModifiers(this) - node.appendSourceLink(source) - - 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, DocumentationReference.Kind.Override) - } else { - baseClassFunction.overriddenDescriptors.forEach { - addOverrideLink(it, overridingFunction) - } - } - } - - fun PropertyDescriptor.build(): DocumentationNode { - val node = nodeForDescriptor(this, if (inCompanionObject()) Kind.CompanionObjectProperty else Kind.Property) - node.appendInPageChildren(typeParameters, DocumentationReference.Kind.Detail) - extensionReceiverParameter?.let { node.appendChild(it, DocumentationReference.Kind.Detail) } - node.appendType(returnType) - node.appendAnnotations(this) - node.appendModifiers(this) - node.appendSourceLink(source) - if (isVar) { - node.appendTextNode("var", DocumentationNode.Kind.Modifier) - } - getter?.let { - if (!it.isDefault) { - node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter") - } - } - setter?.let { - if (!it.isDefault) { - node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter") - } - } - - 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, Kind.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, Kind.Value) - } - } - } - node.appendAnnotations(this) - node.appendModifiers(this) - if (varargElementType != null && node.details(Kind.Modifier).none { it.name == "vararg" }) { - node.appendTextNode("vararg", Kind.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, DocumentationNode.Kind.TypeParameter) - if (prefix != "") { - node.appendTextNode(prefix, Kind.Modifier) - } - if (isReified) { - node.appendTextNode("reified", Kind.Modifier) - } - - for (constraint in upperBounds) { - if (KotlinBuiltIns.isDefaultBound(constraint)) { - continue - } - node.appendType(constraint, Kind.UpperBound) - } - - for (constraint in lowerBounds) { - if (KotlinBuiltIns.isNothing(constraint)) - continue - node.appendType(constraint, Kind.LowerBound) - } - return node - } - - fun ReceiverParameterDescriptor.build(): DocumentationNode { - var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!! - if ((receiverClass as? ClassDescriptor)?.isCompanionObject ?: false) { - receiverClass = receiverClass.containingDeclaration!! - } - link(receiverClass, - containingDeclaration, - DocumentationReference.Kind.Extension) - - val node = DocumentationNode(name.asString(), Content.Empty, Kind.Receiver) - node.appendType(type) - 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, DocumentationNode.Kind.Annotation) - val arguments = allValueArguments.toList().sortedBy { it.first.index } - arguments.forEach { - val valueNode = it.second.toDocumentationNode() - if (valueNode != null) { - val paramNode = DocumentationNode(it.first.name.asString(), Content.Empty, DocumentationNode.Kind.Parameter) - paramNode.append(valueNode, DocumentationReference.Kind.Detail) - node.append(paramNode, DocumentationReference.Kind.Detail) - } - } - return node - } - - fun CompileTimeConstant.build(): DocumentationNode? = when (this) { - is TypedCompileTimeConstant -> constantValue.toDocumentationNode() - else -> null - } - - fun ConstantValue<*>.toDocumentationNode(): DocumentationNode? = value?.let { value -> - when (value) { - is String -> - "\"" + StringUtil.escapeStringCharacters(value) + "\"" - is EnumEntrySyntheticClassDescriptor -> - value.containingDeclaration.name.asString() + "." + value.name.asString() - else -> value.toString() - }.let { valueString -> - DocumentationNode(valueString, Content.Empty, DocumentationNode.Kind.Value) - } - } -} - -class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder { - override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, - packageName: FqName, - packageNode: DocumentationNode, - declarations: List) { - val externalClassNodes = hashMapOf() - declarations.forEach { descriptor -> - with(documentationBuilder) { - if (descriptor.isDocumented()) { - val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes) - parent.appendChild(descriptor, DocumentationReference.Kind.Member) - } - } - } - } -} - -class KotlinJavaDocumentationBuilder - @Inject constructor(val documentationBuilder: DocumentationBuilder, - val logger: DokkaLogger) : JavaDocumentationBuilder -{ - override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map) { - val packageNode = module.findOrCreatePackageNode(file.packageName, packageContent) - - file.classes.forEach { - val javaDescriptorResolver = KotlinCacheService.getInstance(file.project).getProjectService(JvmPlatform, - it.getModuleInfo(), JavaDescriptorResolver::class.java) - - val descriptor = javaDescriptorResolver.resolveClass(JavaClassImpl(it)) - if (descriptor == null) { - logger.warn("Cannot find descriptor for Java class ${it.qualifiedName}") - } - else { - with(documentationBuilder) { - packageNode.appendChild(descriptor, DocumentationReference.Kind.Member) - } - } - } - } -} - -private fun AnnotationDescriptor.isDocumented(): Boolean { - if (source.getPsi() != null && mustBeDocumented()) return true - val annotationClassName = type.constructor.declarationDescriptor?.fqNameSafe?.asString() - return annotationClassName == "kotlin.Extension" -} - -fun AnnotationDescriptor.mustBeDocumented(): Boolean { - val annotationClass = type.constructor.declarationDescriptor as? Annotated ?: return false - return annotationClass.isDocumentedAnnotation() -} - -fun DeclarationDescriptor.isDocumentationSuppressed(): Boolean { - val doc = KDocFinder.findKDoc(this) - return doc is KDocSection && doc.findTagByName("suppress") != null -} - -fun DeclarationDescriptor.isDeprecated(): Boolean = annotations.any { - DescriptorUtils.getFqName(it.type.constructor.declarationDescriptor!!).asString() == "kotlin.Deprecated" -} || (this is ConstructorDescriptor && containingDeclaration.isDeprecated()) - -fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor, - externalClassNodes: MutableMap): DocumentationNode { - if (descriptor is CallableMemberDescriptor) { - val extensionClassDescriptor = descriptor.getExtensionClassDescriptor() - if (extensionClassDescriptor != null && !isSamePackage(descriptor, extensionClassDescriptor) && - !ErrorUtils.isError(extensionClassDescriptor)) { - val fqName = DescriptorUtils.getFqNameSafe(extensionClassDescriptor) - return externalClassNodes.getOrPut(fqName, { - val newNode = DocumentationNode(fqName.asString(), Content.Empty, Kind.ExternalClass) - append(newNode, DocumentationReference.Kind.Member) - newNode - }) - } - } - return this -} - -fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? { - val extensionReceiver = extensionReceiverParameter - if (extensionReceiver != null) { - val type = extensionReceiver.type - return type.constructor.declarationDescriptor as? ClassDescriptor - } - return null -} - -fun DeclarationDescriptor.signature(): String = when(this) { - is ClassDescriptor, is PackageFragmentDescriptor -> 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 - - 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 }.toArrayList() - val extensionReceiver = extensionReceiverParameter - if (extensionReceiver != null) { - params.add(0, extensionReceiver.type) - } - return "(" + params.map { it.signature() }.joinToString() + ")" -} - -fun KotlinType.signature(): String { - val declarationDescriptor = constructor.declarationDescriptor ?: return "" - val typeName = DescriptorUtils.getFqName(declarationDescriptor).asString() - if (typeName == "Array" && arguments.size == 1) { - return "Array<" + arguments.first().type.signature() + ">" - } - return typeName -} - -fun DeclarationDescriptor.signatureWithSourceLocation(): String { - val signature = signature() - val sourceLocation = sourceLocation() - return if (sourceLocation != null) "$signature ($sourceLocation)" else signature -} - -fun DeclarationDescriptor.sourceLocation(): String? { - if (this is DeclarationDescriptorWithSource) { - val psi = (this.source as? PsiSourceElement)?.getPsi() - if (psi != null) { - val fileName = psi.containingFile.name - val lineNumber = psi.lineNumber() - return if (lineNumber != null) "$fileName:$lineNumber" else fileName - } - } - return null -} diff --git a/src/Kotlin/KotlinAsJavaDocumentationBuilder.kt b/src/Kotlin/KotlinAsJavaDocumentationBuilder.kt deleted file mode 100644 index 7a1d591c..00000000 --- a/src/Kotlin/KotlinAsJavaDocumentationBuilder.kt +++ /dev/null @@ -1,64 +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.KtLightElement -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.name.FqName -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) { - 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.options, - 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<*, *>).getOrigin() - return origin?.hasModifier(KtTokens.INTERNAL_KEYWORD) != true && - origin?.hasModifier(KtTokens.PRIVATE_KEYWORD) != true - } -} - -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.getOrigin() ?: 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 descriptor = resolutionFacade.resolveToDescriptor(origin) - val content = descriptorDocumentationParser.parseDocumentation(descriptor, origin is KtParameter) - return JavadocParseResult(content, null) - } -} diff --git a/src/Kotlin/KotlinLanguageService.kt b/src/Kotlin/KotlinLanguageService.kt deleted file mode 100644 index 0d39f410..00000000 --- a/src/Kotlin/KotlinLanguageService.kt +++ /dev/null @@ -1,409 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.LanguageService.RenderMode - -/** - * Implements [LanguageService] and provides rendering of symbols in Kotlin language - */ -class KotlinLanguageService : LanguageService { - private val fullOnlyModifiers = setOf("public", "protected", "private", "inline", "noinline", "crossinline", "reified") - - override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode { - return content { - when (node.kind) { - DocumentationNode.Kind.Package -> if (renderMode == RenderMode.FULL) renderPackage(node) - in DocumentationNode.Kind.classLike -> renderClass(node, renderMode) - - DocumentationNode.Kind.EnumItem, - DocumentationNode.Kind.ExternalClass -> if (renderMode == RenderMode.FULL) identifier(node.name) - - DocumentationNode.Kind.TypeParameter -> renderTypeParameter(node, renderMode) - DocumentationNode.Kind.Type, - DocumentationNode.Kind.UpperBound -> renderType(node, renderMode) - - DocumentationNode.Kind.Modifier -> renderModifier(node) - DocumentationNode.Kind.Constructor, - DocumentationNode.Kind.Function, - DocumentationNode.Kind.CompanionObjectFunction -> renderFunction(node, renderMode) - DocumentationNode.Kind.Property, - DocumentationNode.Kind.CompanionObjectProperty -> renderProperty(node, renderMode) - else -> identifier(node.name) - } - } - } - - override fun renderName(node: DocumentationNode): String { - return when (node.kind) { - DocumentationNode.Kind.Constructor -> node.owner!!.name - else -> node.name - } - } - - override fun summarizeSignatures(nodes: List): ContentNode? { - if (nodes.size < 2) return null - val receiverKind = nodes.getReceiverKind() ?: return null - val functionWithTypeParameter = nodes.firstOrNull { it.details(DocumentationNode.Kind.TypeParameter).any() } ?: return null - return content { - val typeParameter = functionWithTypeParameter.details(DocumentationNode.Kind.TypeParameter).first() - if (functionWithTypeParameter.kind == DocumentationNode.Kind.Function) { - renderFunction(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name)) - } - else { - renderProperty(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name)) - } - } - } - - private fun List.getReceiverKind(): ReceiverKind? { - val qNames = map { it.getReceiverQName() }.filterNotNull() - if (qNames.size != size) - return null - - return ReceiverKind.values.firstOrNull { kind -> qNames.all { it in kind.classes } } - } - - private fun DocumentationNode.getReceiverQName(): String? { - if (kind != DocumentationNode.Kind.Function && kind != DocumentationNode.Kind.Property) return null - val receiver = details(DocumentationNode.Kind.Receiver).singleOrNull() ?: return null - return receiver.detail(DocumentationNode.Kind.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) { - 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.renderPackage(node: DocumentationNode) { - keyword("package") - text(" ") - identifier(node.name) - } - - private fun ContentBlock.renderList(nodes: List, separator: String = ", ", - noWrap: Boolean = false, renderItem: (DocumentationNode) -> Unit) { - if (nodes.none()) - return - renderItem(nodes.first()) - nodes.drop(1).forEach { - if (noWrap) { - symbol(separator.removeSuffix(" ")) - nbsp() - } else { - symbol(separator) - } - renderItem(it) - } - } - - private fun ContentBlock.renderLinked(node: DocumentationNode, body: ContentBlock.(DocumentationNode)->Unit) { - val to = node.links.firstOrNull() - if (to == null) - body(node) - else - link(to) { - body(node) - } - } - - private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) { - var typeArguments = node.details(DocumentationNode.Kind.Type) - if (node.name == "Function${typeArguments.count() - 1}") { - // lambda - val isExtension = node.annotations.any { it.name == "Extension" } - if (isExtension) { - renderType(typeArguments.first(), renderMode) - symbol(".") - typeArguments = typeArguments.drop(1) - } - symbol("(") - renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) { - renderType(it, renderMode) - } - symbol(")") - nbsp() - symbol("->") - nbsp() - renderType(typeArguments.last(), renderMode) - return - } - if (renderMode == RenderMode.FULL) { - renderAnnotationsForNode(node) - } - renderModifiersForNode(node, renderMode, true) - renderLinked(node) { identifier(it.name, IdentifierKind.TypeName) } - if (typeArguments.any()) { - symbol("<") - renderList(typeArguments, noWrap = true) { - renderType(it, renderMode) - } - symbol(">") - } - val nullabilityModifier = node.details(DocumentationNode.Kind.NullabilityModifier).singleOrNull() - if (nullabilityModifier != null) { - symbol(nullabilityModifier.name) - } - } - - private fun ContentBlock.renderModifier(node: DocumentationNode, nowrap: Boolean = false) { - when (node.name) { - "final", "public", "var" -> {} - else -> { - keyword(node.name) - if (nowrap) { - nbsp() - } - else { - text(" ") - } - } - } - } - - private fun ContentBlock.renderTypeParameter(node: DocumentationNode, renderMode: RenderMode) { - renderModifiersForNode(node, renderMode, true) - - identifier(node.name) - - val constraints = node.details(DocumentationNode.Kind.UpperBound) - if (constraints.any()) { - 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) - symbol(":") - nbsp() - val parameterType = node.detail(DocumentationNode.Kind.Type) - renderType(parameterType, renderMode) - val valueNode = node.details(DocumentationNode.Kind.Value).firstOrNull() - if (valueNode != null) { - nbsp() - symbol("=") - nbsp() - text(valueNode.name) - } - } - - private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode, renderMode: RenderMode) { - val typeParameters = node.details(DocumentationNode.Kind.TypeParameter) - if (typeParameters.any()) { - symbol("<") - renderList(typeParameters) { - renderTypeParameter(it, renderMode) - } - symbol(">") - } - } - - private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) { - val supertypes = node.details(DocumentationNode.Kind.Supertype) - if (supertypes.any()) { - nbsp() - symbol(":") - nbsp() - renderList(supertypes) { - indentedSoftLineBreak() - renderType(it, renderMode) - } - } - } - - private fun ContentBlock.renderModifiersForNode(node: DocumentationNode, - renderMode: RenderMode, - nowrap: Boolean = false) { - val modifiers = node.details(DocumentationNode.Kind.Modifier) - for (it in modifiers) { - if (node.kind == org.jetbrains.dokka.DocumentationNode.Kind.Interface && it.name == "abstract") - continue - if (renderMode == RenderMode.SUMMARY && it.name in fullOnlyModifiers) { - continue - } - renderModifier(it, nowrap) - } - } - - 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(DocumentationNode.Kind.Parameter) - if (!parameters.isEmpty()) { - symbol("(") - renderList(parameters) { - text(it.detail(DocumentationNode.Kind.Value).name) - } - symbol(")") - } - text(" ") - } - - private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) { - if (renderMode == RenderMode.FULL) { - renderAnnotationsForNode(node) - } - renderModifiersForNode(node, renderMode) - when (node.kind) { - DocumentationNode.Kind.Class, - DocumentationNode.Kind.AnnotationClass, - DocumentationNode.Kind.Enum -> keyword("class ") - DocumentationNode.Kind.Interface -> keyword("interface ") - DocumentationNode.Kind.EnumItem -> keyword("enum val ") - DocumentationNode.Kind.Object -> keyword("object ") - else -> throw IllegalArgumentException("Node $node is not a class-like object") - } - - identifierOrDeprecated(node) - renderTypeParametersForNode(node, renderMode) - renderSupertypesForNode(node, renderMode) - } - - private fun ContentBlock.renderFunction(node: DocumentationNode, - renderMode: RenderMode, - signatureMapper: SignatureMapper? = null) { - if (renderMode == RenderMode.FULL) { - renderAnnotationsForNode(node) - } - renderModifiersForNode(node, renderMode) - when (node.kind) { - DocumentationNode.Kind.Constructor -> identifier(node.owner!!.name) - DocumentationNode.Kind.Function, - DocumentationNode.Kind.CompanionObjectFunction -> keyword("fun ") - else -> throw IllegalArgumentException("Node $node is not a function-like object") - } - renderTypeParametersForNode(node, renderMode) - if (node.details(DocumentationNode.Kind.TypeParameter).any()) { - text(" ") - } - - renderReceiver(node, renderMode, signatureMapper) - - if (node.kind != org.jetbrains.dokka.DocumentationNode.Kind.Constructor) - identifierOrDeprecated(node) - - symbol("(") - val parameters = node.details(DocumentationNode.Kind.Parameter) - renderList(parameters) { - indentedSoftLineBreak() - renderParameter(it, renderMode) - } - if (needReturnType(node)) { - if (parameters.isNotEmpty()) { - softLineBreak() - } - symbol(")") - symbol(": ") - renderType(node.detail(DocumentationNode.Kind.Type), renderMode) - } - else { - symbol(")") - } - } - - private fun ContentBlock.renderReceiver(node: DocumentationNode, renderMode: RenderMode, signatureMapper: SignatureMapper?) { - val receiver = node.details(DocumentationNode.Kind.Receiver).singleOrNull() - if (receiver != null) { - if (signatureMapper != null) { - signatureMapper.renderReceiver(receiver, this) - } else { - renderType(receiver.detail(DocumentationNode.Kind.Type), renderMode) - } - symbol(".") - } - } - - private fun needReturnType(node: DocumentationNode) = when(node.kind) { - DocumentationNode.Kind.Constructor -> false - else -> !node.isUnitReturnType() - } - - fun DocumentationNode.isUnitReturnType(): Boolean = - detail(DocumentationNode.Kind.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) { - DocumentationNode.Kind.Property, - DocumentationNode.Kind.CompanionObjectProperty -> keyword("${node.getPropertyKeyword()} ") - else -> throw IllegalArgumentException("Node $node is not a property") - } - renderTypeParametersForNode(node, renderMode) - if (node.details(DocumentationNode.Kind.TypeParameter).any()) { - text(" ") - } - - renderReceiver(node, renderMode, signatureMapper) - - identifierOrDeprecated(node) - symbol(": ") - renderType(node.detail(DocumentationNode.Kind.Type), renderMode) - } - - fun DocumentationNode.getPropertyKeyword() = - if (details(DocumentationNode.Kind.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() = (links.firstOrNull() ?: hiddenLinks.firstOrNull())?.qualifiedName() ?: name diff --git a/src/Languages/JavaLanguageService.kt b/src/Languages/JavaLanguageService.kt deleted file mode 100644 index 7e40beff..00000000 --- a/src/Languages/JavaLanguageService.kt +++ /dev/null @@ -1,162 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.DocumentationNode.Kind -import org.jetbrains.dokka.LanguageService.RenderMode - -/** - * Implements [LanguageService] and provides rendering of symbols in Java language - */ -public class JavaLanguageService : LanguageService { - override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode { - return ContentText(when (node.kind) { - Kind.Package -> renderPackage(node) - in Kind.classLike -> renderClass(node) - - Kind.TypeParameter -> renderTypeParameter(node) - Kind.Type, - Kind.UpperBound -> renderType(node) - - Kind.Constructor, - Kind.Function -> renderFunction(node) - Kind.Property -> renderProperty(node) - else -> "${node.kind}: ${node.name}" - }) - } - - override fun renderName(node: DocumentationNode): String { - return when (node.kind) { - Kind.Constructor -> node.owner!!.name - else -> node.name - } - } - - override fun summarizeSignatures(nodes: List): 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 - } - } - - public fun getArrayElementType(node: DocumentationNode): DocumentationNode? = when (node.name) { - "Array" -> node.details(Kind.Type).singleOrNull()?.let { et -> getArrayElementType(et) ?: et } ?: DocumentationNode("Object", node.content, DocumentationNode.Kind.ExternalClass) - "IntArray", "LongArray", "ShortArray", "ByteArray", "CharArray", "DoubleArray", "FloatArray", "BooleanArray" -> DocumentationNode(node.name.removeSuffix("Array").toLowerCase(), node.content, DocumentationNode.Kind.Type) - else -> null - } - - public fun getArrayDimension(node: DocumentationNode): Int = when (node.name) { - "Array" -> 1 + (node.details(DocumentationNode.Kind.Type).singleOrNull()?.let { getArrayDimension(it) } ?: 0) - "IntArray", "LongArray", "ShortArray", "ByteArray", "CharArray", "DoubleArray", "FloatArray", "BooleanArray" -> 1 - else -> 0 - } - - public 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(Kind.UpperBound) - return if (constraints.none()) - node.name - else { - node.name + " extends " + constraints.map { renderType(node) }.joinToString() - } - } - - private fun renderParameter(node: DocumentationNode): String { - return "${renderType(node.detail(Kind.Type))} ${node.name}" - } - - private fun renderTypeParametersForNode(node: DocumentationNode): String { - return StringBuilder().apply { - val typeParameters = node.details(Kind.TypeParameter) - if (typeParameters.any()) { - append("<") - append(typeParameters.map { renderTypeParameter(it) }.joinToString()) - append("> ") - } - }.toString() - } - - private fun renderModifiersForNode(node: DocumentationNode): String { - val modifiers = node.details(Kind.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) { - Kind.Class -> append("class ") - Kind.Interface -> append("interface ") - Kind.Enum -> append("enum ") - Kind.EnumItem -> append("enum value ") - Kind.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) { - Kind.Constructor -> append(node.owner?.name) - Kind.Function -> { - append(renderTypeParametersForNode(node)) - append(renderType(node.detail(Kind.Type))) - append(" ") - append(node.name) - } - else -> throw IllegalArgumentException("Node $node is not a function-like object") - } - - val receiver = node.details(Kind.Receiver).singleOrNull() - append("(") - if (receiver != null) - (listOf(receiver) + node.details(Kind.Parameter)).map { renderParameter(it) }.joinTo(this) - else - node.details(Kind.Parameter).map { renderParameter(it) }.joinTo(this) - - append(")") - }.toString() - } - - private fun renderProperty(node: DocumentationNode): String { - return StringBuilder().apply { - when (node.kind) { - Kind.Property -> append("val ") - else -> throw IllegalArgumentException("Node $node is not a property") - } - append(renderTypeParametersForNode(node)) - val receiver = node.details(Kind.Receiver).singleOrNull() - if (receiver != null) { - append(renderType(receiver.detail(Kind.Type))) - append(".") - } - - append(node.name) - append(": ") - append(renderType(node.detail(Kind.Type))) - }.toString() - } -} \ No newline at end of file diff --git a/src/Languages/LanguageService.kt b/src/Languages/LanguageService.kt deleted file mode 100644 index b0f4bbc9..00000000 --- a/src/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): 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/src/Locations/FoldersLocationService.kt b/src/Locations/FoldersLocationService.kt deleted file mode 100644 index 89b34ed1..00000000 --- a/src/Locations/FoldersLocationService.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import java.io.File - -public fun FoldersLocationService(root: String): FoldersLocationService = FoldersLocationService(File(root), "") -public class FoldersLocationService @Inject constructor(@Named("outputDir") val rootFile: File, val extension: String) : FileLocationService { - override val root: Location - get() = FileLocation(rootFile) - - override fun withExtension(newExtension: String): FileLocationService { - return if (extension.isEmpty()) FoldersLocationService(rootFile, newExtension) else this - } - - override fun location(qualifiedName: List, hasMembers: Boolean): FileLocation { - return FileLocation(File(rootFile, relativePathToNode(qualifiedName, hasMembers)).appendExtension(extension)) - } -} - -fun relativePathToNode(qualifiedName: List, 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" - } -} diff --git a/src/Locations/LocationService.kt b/src/Locations/LocationService.kt deleted file mode 100644 index 80bc0236..00000000 --- a/src/Locations/LocationService.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -public 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). - * - * Locations are provided by [LocationService.location] function. - * - * $file: [File] for this location - * $path: [String] representing path of this location - */ -public 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 "." - } - val ownerFolder = file.parentFile!! - val relativePath = ownerFolder.toPath().relativize(other.file.toPath()).toString() - return if (anchor == null) relativePath else relativePath + "#" + anchor - } -} - -/** - * Provides means of retrieving locations for [DocumentationNode](documentation nodes) - * - * `LocationService` determines where documentation for particular node should be generated - * - * * [FoldersLocationService] – represent packages and types as folders, members as files in those folders. - * * [SingleFolderLocationService] – all documentation is generated into single folder using fully qualified names - * for file names. - */ -public interface LocationService { - fun withExtension(newExtension: String) = this - - fun location(node: DocumentationNode): Location = location(node.path.map { it.name }, node.members.any()) - - /** - * Calculates a location corresponding to the specified [qualifiedName]. - * @param hasMembers if true, the node for which the location is calculated has member nodes. - */ - fun location(qualifiedName: List, hasMembers: Boolean): Location - - val root: Location -} - - -public interface FileLocationService: LocationService { - override fun withExtension(newExtension: String): FileLocationService = this - - override fun location(node: DocumentationNode): FileLocation = location(node.path.map { it.name }, node.members.any()) - override fun location(qualifiedName: List, hasMembers: Boolean): FileLocation -} - - -public fun identifierToFilename(path: String): String { - val escaped = path.replace('<', '-').replace('>', '-') - val lowercase = escaped.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() } - return if (lowercase == "index") "--index--" else lowercase -} - -/** - * Returns relative location between two nodes. Used for relative links in documentation. - */ -fun LocationService.relativePathToLocation(owner: DocumentationNode, node: DocumentationNode): String { - return location(owner).relativePathTo(location(node), null) -} diff --git a/src/Locations/SingleFolderLocationService.kt b/src/Locations/SingleFolderLocationService.kt deleted file mode 100644 index e313ac28..00000000 --- a/src/Locations/SingleFolderLocationService.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import java.io.File - -public fun SingleFolderLocationService(root: String): SingleFolderLocationService = SingleFolderLocationService(File(root), "") -public class SingleFolderLocationService @Inject constructor(@Named("outputDir") val rootFile: File, val extension: String) : FileLocationService { - override fun withExtension(newExtension: String): FileLocationService = - SingleFolderLocationService(rootFile, newExtension) - - override fun location(qualifiedName: List, hasMembers: Boolean): FileLocation { - val filename = qualifiedName.map { identifierToFilename(it) }.joinToString("-") - return FileLocation(File(rootFile, filename).appendExtension(extension)) - } - - override val root: Location - get() = FileLocation(rootFile) -} \ No newline at end of file diff --git a/src/Markdown/MarkdownProcessor.kt b/src/Markdown/MarkdownProcessor.kt deleted file mode 100644 index 99caddc4..00000000 --- a/src/Markdown/MarkdownProcessor.kt +++ /dev/null @@ -1,50 +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.flavours.commonmark.CommonMarkFlavourDescriptor -import org.intellij.markdown.parser.MarkdownParser - -class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val markdown: String) { - val children: List = node.children.map { MarkdownNode(it, this, markdown) } - val type: IElementType get() = node.type - val text: String get() = markdown.substring(node.startOffset, node.endOffset) - fun child(type: IElementType): MarkdownNode? = children.firstOrNull { it.type == type } - - 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) - } - } -} - -public fun MarkdownNode.toTestString(): String { - val sb = StringBuilder() - var level = 0 - visit { node, visitChildren -> - sb.append(" ".repeat(level * 2)) - node.presentTo(sb) - level++ - visitChildren() - level-- - } - return sb.toString() -} - -private fun MarkdownNode.presentTo(sb: StringBuilder) { - sb.append(type.toString()) - sb.append(":" + text.replace("\n", "\u23CE")) - sb.appendln() -} - -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/src/Model/Content.kt b/src/Model/Content.kt deleted file mode 100644 index 6556b09e..00000000 --- a/src/Model/Content.kt +++ /dev/null @@ -1,231 +0,0 @@ -package org.jetbrains.dokka - -public interface ContentNode { - val textLength: Int -} - -public object ContentEmpty : ContentNode { - override val textLength: Int get() = 0 -} - -public open class ContentBlock() : ContentNode { - val children = arrayListOf() - - 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 } -} - -enum class IdentifierKind { - TypeName, - ParameterName, - AnnotationName, - SummarizedTypeName, - Other -} - -public data class ContentText(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -public data class ContentKeyword(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -public data class ContentIdentifier(val text: String, val kind: IdentifierKind = IdentifierKind.Other) : ContentNode { - override val textLength: Int - get() = text.length -} - -public data class ContentSymbol(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -public data class ContentEntity(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -public object ContentNonBreakingSpace: ContentNode { - override val textLength: Int - get() = 1 -} - -public object ContentSoftLineBreak: ContentNode { - override val textLength: Int - get() = 0 -} - -public object ContentIndentedSoftLineBreak: ContentNode { - override val textLength: Int - get() = 0 -} - -public class ContentParagraph() : ContentBlock() -public class ContentEmphasis() : ContentBlock() -public class ContentStrong() : ContentBlock() -public class ContentStrikethrough() : ContentBlock() -public class ContentCode() : ContentBlock() -public class ContentBlockCode(val language: String = "") : ContentBlock() - -public abstract class ContentNodeLink() : ContentBlock() { - abstract val node: DocumentationNode? -} - -public 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() -} - -public 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() -} - -public 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() -} - -public class ContentUnorderedList() : ContentBlock() -public class ContentOrderedList() : ContentBlock() -public class ContentListItem() : ContentBlock() - -public class ContentHeading(val level: Int) : ContentBlock() - -public class ContentSection(public val tag: String, public 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) -} - -public object ContentTags { - val Description = "Description" - val SeeAlso = "See Also" -} - -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) = append(ContentIdentifier(value, kind)) -fun ContentBlock.nbsp() = append(ContentNonBreakingSpace) -fun ContentBlock.softLineBreak() = append(ContentSoftLineBreak) -fun ContentBlock.indentedSoftLineBreak() = append(ContentIndentedSoftLineBreak) - -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 = ContentNodeDirectLink(to) - block.body() - append(block) -} - -public open class Content(): ContentBlock() { - public open val sections: List get() = emptyList() - public open val summary: ContentNode get() = ContentEmpty - public open val description: ContentNode get() = ContentEmpty - - fun findSectionByTag(tag: String): ContentSection? = - sections.firstOrNull { tag.equals(it.tag, ignoreCase = true) } - - companion object { - val Empty = Content() - - fun of(vararg child: ContentNode): Content { - val result = MutableContent() - child.forEach { result.append(it) } - return result - } - } -} - -public open class MutableContent() : Content() { - private val sectionList = arrayListOf() - public override val sections: List - get() = sectionList - - fun addSection(tag: String?, subjectName: String?): ContentSection { - val section = ContentSection(tag ?: "", subjectName) - sectionList.add(section) - return section - } - - public override val summary: ContentNode get() = children.firstOrNull() ?: ContentEmpty - - public 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 "" - return (listOf(summary, description) + sections).joinToString() - } -} - -fun javadocSectionDisplayName(sectionName: String?): String? = - when(sectionName) { - "param" -> "Parameters" - "throws", "exception" -> "Exceptions" - else -> sectionName?.capitalize() - } diff --git a/src/Model/DocumentationNode.kt b/src/Model/DocumentationNode.kt deleted file mode 100644 index 52881f65..00000000 --- a/src/Model/DocumentationNode.kt +++ /dev/null @@ -1,162 +0,0 @@ -package org.jetbrains.dokka - -import java.util.* - -public open class DocumentationNode(val name: String, - content: Content, - val kind: DocumentationNode.Kind) { - - private val references = LinkedHashSet() - - var content: Content = content - private set - - public val summary: ContentNode get() = content.summary - - public val owner: DocumentationNode? - get() = references(DocumentationReference.Kind.Owner).singleOrNull()?.to - public val details: List - get() = references(DocumentationReference.Kind.Detail).map { it.to } - public val members: List - get() = references(DocumentationReference.Kind.Member).map { it.to } - public val inheritedMembers: List - get() = references(DocumentationReference.Kind.InheritedMember).map { it.to } - public val extensions: List - get() = references(DocumentationReference.Kind.Extension).map { it.to } - public val inheritors: List - get() = references(DocumentationReference.Kind.Inheritor).map { it.to } - public val overrides: List - get() = references(DocumentationReference.Kind.Override).map { it.to } - public val links: List - get() = references(DocumentationReference.Kind.Link).map { it.to } - public val hiddenLinks: List - get() = references(DocumentationReference.Kind.HiddenLink).map { it.to } - public val annotations: List - get() = references(DocumentationReference.Kind.Annotation).map { it.to } - public val deprecation: DocumentationNode? - get() = references(DocumentationReference.Kind.Deprecation).singleOrNull()?.to - - // TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice - public fun addReferenceTo(to: DocumentationNode, kind: DocumentationReference.Kind) { - references.add(DocumentationReference(this, to, kind)) - } - - public fun addAllReferencesFrom(other: DocumentationNode) { - references.addAll(other.references) - } - - public fun updateContent(body: MutableContent.() -> Unit) { - if (content !is MutableContent) { - content = MutableContent() - } - (content as MutableContent).body() - } - - public fun details(kind: DocumentationNode.Kind): List = details.filter { it.kind == kind } - public fun members(kind: DocumentationNode.Kind): List = members.filter { it.kind == kind } - public fun inheritedMembers(kind: DocumentationNode.Kind): List = inheritedMembers.filter { it.kind == kind } - public fun links(kind: DocumentationNode.Kind): List = links.filter { it.kind == kind } - - public fun detail(kind: DocumentationNode.Kind): DocumentationNode = details.filter { it.kind == kind }.single() - public fun member(kind: DocumentationNode.Kind): DocumentationNode = members.filter { it.kind == kind }.single() - public fun link(kind: DocumentationNode.Kind): DocumentationNode = links.filter { it.kind == kind }.single() - - public fun references(kind: DocumentationReference.Kind): List = references.filter { it.kind == kind } - public fun allReferences(): Set = references - - public override fun toString(): String { - return "$kind:$name" - } - - public enum class Kind { - Unknown, - - Package, - Class, - Interface, - Enum, - AnnotationClass, - EnumItem, - Object, - - Constructor, - Function, - Property, - Field, - - CompanionObjectProperty, - CompanionObjectFunction, - - Parameter, - Receiver, - TypeParameter, - Type, - Supertype, - UpperBound, - LowerBound, - Exception, - - Modifier, - NullabilityModifier, - - Module, - - ExternalClass, - Annotation, - - Value, - - SourceUrl, - SourcePosition, - - /** - * A note which is rendered once on a page documenting a group of overloaded functions. - * Needs to be generated equally on all overloads. - */ - OverloadGroupNote; - - companion object { - val classLike = setOf(Class, Interface, Enum, AnnotationClass, Object) - } - } -} - -public class DocumentationModule(name: String, content: Content = Content.Empty) - : DocumentationNode(name, content, DocumentationNode.Kind.Module) { -} - -val DocumentationNode.path: List - get() { - val parent = owner ?: return listOf(this) - return parent.path + this - } - -fun DocumentationNode.findOrCreatePackageNode(packageName: String, packageContent: Map): DocumentationNode { - val existingNode = members(DocumentationNode.Kind.Package).firstOrNull { it.name == packageName } - if (existingNode != null) { - return existingNode - } - val newNode = DocumentationNode(packageName, - packageContent.getOrElse(packageName) { Content.Empty }, - DocumentationNode.Kind.Package) - append(newNode, DocumentationReference.Kind.Member) - return newNode -} - -fun DocumentationNode.append(child: DocumentationNode, kind: DocumentationReference.Kind) { - addReferenceTo(child, kind) - when (kind) { - DocumentationReference.Kind.Detail -> child.addReferenceTo(this, DocumentationReference.Kind.Owner) - DocumentationReference.Kind.Member -> child.addReferenceTo(this, DocumentationReference.Kind.Owner) - DocumentationReference.Kind.Owner -> child.addReferenceTo(this, DocumentationReference.Kind.Member) - else -> { /* Do not add any links back for other types */ } - } -} - -fun DocumentationNode.appendTextNode(text: String, - kind: DocumentationNode.Kind, - refKind: DocumentationReference.Kind = DocumentationReference.Kind.Detail) { - append(DocumentationNode(text, Content.Empty, kind), refKind) -} - -fun DocumentationNode.qualifiedName() = path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".") diff --git a/src/Model/DocumentationReference.kt b/src/Model/DocumentationReference.kt deleted file mode 100644 index 898c92d7..00000000 --- a/src/Model/DocumentationReference.kt +++ /dev/null @@ -1,61 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Singleton - -public data class DocumentationReference(val from: DocumentationNode, val to: DocumentationNode, val kind: DocumentationReference.Kind) { - public enum class Kind { - Owner, - Member, - InheritedMember, - Detail, - Link, - HiddenLink, - Extension, - Inheritor, - Superclass, - Override, - Annotation, - Deprecation, - TopLevelPage - } -} - -class PendingDocumentationReference(val lazyNodeFrom: () -> DocumentationNode?, - val lazyNodeTo: () -> DocumentationNode?, - val kind: DocumentationReference.Kind) { - fun resolve() { - val fromNode = lazyNodeFrom() - val toNode = lazyNodeTo() - if (fromNode != null && toNode != null) { - fromNode.addReferenceTo(toNode, kind) - } - } -} - -@Singleton -class NodeReferenceGraph() { - private val nodeMap = hashMapOf() - val references = arrayListOf() - - fun register(signature: String, node: DocumentationNode) { - nodeMap.put(signature, node) - } - - fun link(fromNode: DocumentationNode, toSignature: String, kind: DocumentationReference.Kind) { - references.add(PendingDocumentationReference({ -> fromNode}, { -> nodeMap[toSignature]}, kind)) - } - - fun link(fromSignature: String, toNode: DocumentationNode, kind: DocumentationReference.Kind) { - references.add(PendingDocumentationReference({ -> nodeMap[fromSignature]}, { -> toNode}, kind)) - } - - fun link(fromSignature: String, toSignature: String, kind: DocumentationReference.Kind) { - references.add(PendingDocumentationReference({ -> nodeMap[fromSignature]}, { -> nodeMap[toSignature]}, kind)) - } - - fun lookup(signature: String): DocumentationNode? = nodeMap[signature] - - fun resolveReferences() { - references.forEach { it.resolve() } - } -} diff --git a/src/Model/PackageDocs.kt b/src/Model/PackageDocs.kt deleted file mode 100644 index 044c73d8..00000000 --- a/src/Model/PackageDocs.kt +++ /dev/null @@ -1,60 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.Singleton -import org.intellij.markdown.MarkdownElementTypes -import org.intellij.markdown.MarkdownTokenTypes -import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyPackageDescriptor -import java.io.File - -@Singleton -public class PackageDocs - @Inject constructor(val linkResolver: DeclarationLinkResolver?, - val logger: DokkaLogger) -{ - public val moduleContent: MutableContent = MutableContent() - private val _packageContent: MutableMap = hashMapOf() - public val packageContent: Map - get() = _packageContent - - fun parse(fileName: String, linkResolveContext: LazyPackageDescriptor?) { - val file = File(fileName) - if (file.exists()) { - val text = file.readText() - val tree = parseMarkdown(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, { resolveContentLink(it, linkResolveContext) }) - } - } - } else { - logger.warn("Include file $file was not found.") - } - } - - 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(href: String, linkResolveContext: LazyPackageDescriptor?): ContentBlock { - if (linkResolveContext != null && linkResolver != null) { - return linkResolver.resolveContentLink(linkResolveContext, href) - } - return ContentExternalLink("#") - } -} \ No newline at end of file diff --git a/src/Model/SourceLinks.kt b/src/Model/SourceLinks.kt deleted file mode 100644 index 956bfe4b..00000000 --- a/src/Model/SourceLinks.kt +++ /dev/null @@ -1,56 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.PsiElement -import java.io.File -import com.intellij.psi.PsiDocumentManager -import com.intellij.psi.PsiNameIdentifierOwner -import org.jetbrains.kotlin.psi.psiUtil.startOffset - -class SourceLinkDefinition(val path: String, val url: String, val lineSuffix: String?) - -fun DocumentationNode.appendSourceLink(psi: PsiElement?, sourceLinks: List) { - val path = psi?.containingFile?.virtualFile?.path ?: return - - val target = if (psi is PsiNameIdentifierOwner) psi.nameIdentifier else psi - val absPath = File(path).absolutePath - val linkDef = sourceLinks.firstOrNull { absPath.startsWith(it.path) } - if (linkDef != null) { - var url = linkDef.url + path.substring(linkDef.path.length) - if (linkDef.lineSuffix != null) { - val line = target?.lineNumber() - if (line != null) { - url += linkDef.lineSuffix + line.toString() - } - } - append(DocumentationNode(url, Content.Empty, DocumentationNode.Kind.SourceUrl), - DocumentationReference.Kind.Detail); - } - - if (target != null) { - append(DocumentationNode(target.sourcePosition(), Content.Empty, DocumentationNode.Kind.SourcePosition), DocumentationReference.Kind.Detail) - } -} - -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/src/Utilities/DokkaModule.kt b/src/Utilities/DokkaModule.kt deleted file mode 100644 index 1eb82313..00000000 --- a/src/Utilities/DokkaModule.kt +++ /dev/null @@ -1,73 +0,0 @@ -package org.jetbrains.dokka.Utilities - -import com.google.inject.Binder -import com.google.inject.Module -import com.google.inject.Provider -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 - -class DokkaModule(val environment: AnalysisEnvironment, - val options: DocumentationOptions, - val logger: DokkaLogger) : Module { - override fun configure(binder: Binder) { - binder.bind(File::class.java).annotatedWith(Names.named("outputDir")).toInstance(File(options.outputDir)) - - binder.bindNameAnnotated("singleFolder") - binder.bindNameAnnotated("singleFolder") - binder.bindNameAnnotated("folders") - binder.bindNameAnnotated("folders") - - // defaults - binder.bind(LocationService::class.java).to(FoldersLocationService::class.java) - binder.bind(FileLocationService::class.java).to(FoldersLocationService::class.java) - binder.bind(LanguageService::class.java).to(KotlinLanguageService::class.java) - - binder.bind(HtmlTemplateService::class.java).toProvider(object : Provider { - override fun get(): HtmlTemplateService = HtmlTemplateService.default("style.css") - }) - - binder.registerCategory("language") - binder.registerCategory("outline") - binder.registerCategory("format") - binder.registerCategory("generator") - - val descriptor = ServiceLocator.lookup("format", options.outputFormat) - - descriptor.outlineServiceClass?.let { clazz -> - binder.bind(OutlineFormatService::class.java).to(clazz.java) - } - descriptor.formatServiceClass?.let { clazz -> - binder.bind(FormatService::class.java).to(clazz.java) - } - binder.bind().to(descriptor.packageDocumentationBuilderClass.java) - binder.bind().to(descriptor.javaDocumentationBuilderClass.java) - - binder.bind().to(descriptor.generatorServiceClass.java) - - val coreEnvironment = environment.createCoreEnvironment() - binder.bind().toInstance(coreEnvironment) - - val dokkaResolutionFacade = environment.createResolutionFacade(coreEnvironment) - binder.bind().toInstance(dokkaResolutionFacade) - - binder.bind().toInstance(options) - binder.bind().toInstance(logger) - } -} - -private inline fun 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) - } -} - -private inline fun Binder.bindNameAnnotated(name: String) { - bind(Base::class.java).annotatedWith(Names.named(name)).to(T::class.java) -} - - -inline fun Binder.bind() = bind(T::class.java) diff --git a/src/Utilities/Html.kt b/src/Utilities/Html.kt deleted file mode 100644 index ce3a1982..00000000 --- a/src/Utilities/Html.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.jetbrains.dokka - - -/** - * Replaces symbols reserved in HTML with their respective entities. - * Replaces & with &, < with < and > with > - */ -public fun String.htmlEscape(): String = replace("&", "&").replace("<", "<").replace(">", ">") diff --git a/src/Utilities/Path.kt b/src/Utilities/Path.kt deleted file mode 100644 index 05838499..00000000 --- a/src/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/src/Utilities/ServiceLocator.kt b/src/Utilities/ServiceLocator.kt deleted file mode 100644 index 7a5aff79..00000000 --- a/src/Utilities/ServiceLocator.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.jetbrains.dokka.Utilities - -import java.io.File -import java.util.* -import java.util.jar.JarFile -import java.util.zip.ZipEntry - -data class ServiceDescriptor(val name: String, val category: String, val description: String?, val className: String) - -class ServiceLookupException(message: String) : Exception(message) - -public object ServiceLocator { - public fun lookup(clazz: Class, category: String, implementationName: String): T { - val descriptor = lookupDescriptor(category, implementationName) - val loadedClass = javaClass.classLoader.loadClass(descriptor.className) - val constructor = loadedClass.constructors - .filter { it.parameterTypes.isEmpty() } - .firstOrNull() ?: throw ServiceLookupException("Class ${descriptor.className} has no corresponding constructor") - - val implementationRawType: Any = if (constructor.parameterTypes.isEmpty()) constructor.newInstance() else constructor.newInstance(constructor) - - if (!clazz.isInstance(implementationRawType)) { - throw ServiceLookupException("Class ${descriptor.className} is not a subtype of ${clazz.name}") - } - - @Suppress("UNCHECKED_CAST") - return implementationRawType as T - } - - private fun lookupDescriptor(category: String, implementationName: String): ServiceDescriptor { - val properties = javaClass.classLoader.getResourceAsStream("dokka/$category/$implementationName.properties")?.use { stream -> - Properties().let { properties -> - properties.load(stream) - properties - } - } ?: throw ServiceLookupException("No implementation with name $implementationName found in category $category") - - val className = properties["class"]?.toString() ?: throw ServiceLookupException("Implementation $implementationName has no class configured") - - return ServiceDescriptor(implementationName, category, properties["description"]?.toString(), className) - } - - fun allServices(category: String): List { - val entries = this.javaClass.classLoader.getResources("dokka/$category")?.toList() ?: emptyList() - - return entries.flatMap { - when (it.protocol) { - "file" -> File(it.file).listFiles()?.filter { it.extension == "properties" }?.map { lookupDescriptor(category, it.nameWithoutExtension) } ?: emptyList() - "jar" -> { - val file = JarFile(it.file.removePrefix("file:").substringBefore("!")) - try { - val jarPath = it.file.substringAfterLast("!").removePrefix("/") - file.entries() - .asSequence() - .filter { entry -> !entry.isDirectory && entry.path == jarPath && entry.extension == "properties" } - .map { entry -> - lookupDescriptor(category, entry.fileName.substringBeforeLast(".")) - }.toList() - } finally { - file.close() - } - } - else -> emptyList() - } - } - } -} - -public inline fun ServiceLocator.lookup(category: String, implementationName: String): T = lookup(T::class.java, category, implementationName) - -private val ZipEntry.fileName: String - get() = name.substringAfterLast("/", name) - -private val ZipEntry.path: String - get() = name.substringBeforeLast("/", "").removePrefix("/") - -private val ZipEntry.extension: String? - get() = fileName.let { fn -> if ("." in fn) fn.substringAfterLast(".") else null } diff --git a/src/main.kt b/src/main.kt deleted file mode 100644 index 5a7514c4..00000000 --- a/src/main.kt +++ /dev/null @@ -1,262 +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 com.sampullara.cli.Args -import com.sampullara.cli.Argument -import org.jetbrains.dokka.Utilities.DokkaModule -import org.jetbrains.kotlin.cli.common.arguments.ValueDescription -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.config.CommonConfigurationKeys -import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzerForTopLevel -import org.jetbrains.kotlin.resolve.TopDownAnalysisMode -import org.jetbrains.kotlin.utils.PathUtil -import java.io.File -import kotlin.util.measureTimeMillis - -class DokkaArguments { - @set:Argument(value = "src", description = "Source file or directory (allows many paths separated by the system path separator)") - @ValueDescription("") - public var src: String = "" - - @set:Argument(value = "srcLink", description = "Mapping between a source directory and a Web site for browsing the code") - @ValueDescription("=[#lineSuffix]") - public var srcLink: String = "" - - @set:Argument(value = "include", description = "Markdown files to load (allows many paths separated by the system path separator)") - @ValueDescription("") - public var include: String = "" - - @set:Argument(value = "samples", description = "Source root for samples") - @ValueDescription("") - public var samples: String = "" - - @set:Argument(value = "output", description = "Output directory path") - @ValueDescription("") - public var outputDir: String = "out/doc/" - - @set:Argument(value = "format", description = "Output format (text, html, markdown, jekyll, kotlin-website)") - @ValueDescription("") - public var outputFormat: String = "html" - - @set:Argument(value = "module", description = "Name of the documentation module") - @ValueDescription("") - public var moduleName: String = "" - - @set:Argument(value = "classpath", description = "Classpath for symbol resolution") - @ValueDescription("") - public var classpath: String = "" - - @set:Argument(value = "nodeprecated", description = "Exclude deprecated members from documentation") - public var nodeprecated: Boolean = false - -} - -private fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinition { - val (path, urlAndLine) = srcLink.split('=') - return SourceLinkDefinition(File(path).absolutePath, - urlAndLine.substringBefore("#"), - urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#" + it }) -} - -public fun main(args: Array) { - val arguments = DokkaArguments() - val freeArgs: List = Args.parse(arguments, args) ?: listOf() - val sources = if (arguments.src.isNotEmpty()) arguments.src.split(File.pathSeparatorChar).toList() + freeArgs else freeArgs - val samples = if (arguments.samples.isNotEmpty()) arguments.samples.split(File.pathSeparatorChar).toList() else listOf() - val includes = if (arguments.include.isNotEmpty()) arguments.include.split(File.pathSeparatorChar).toList() else listOf() - - val sourceLinks = if (arguments.srcLink.isNotEmpty() && arguments.srcLink.contains("=")) - listOf(parseSourceLinkDefinition(arguments.srcLink)) - else { - if (arguments.srcLink.isNotEmpty()) { - println("Warning: Invalid -srcLink syntax. Expected: =[#lineSuffix]. No source links will be generated.") - } - listOf() - } - - val classPath = arguments.classpath.split(File.pathSeparatorChar).toList() - val generator = DokkaGenerator( - DokkaConsoleLogger, - classPath, - sources, - samples, - includes, - arguments.moduleName, - arguments.outputDir.let { if (it.endsWith('/')) it else it + '/' }, - arguments.outputFormat, - sourceLinks, - arguments.nodeprecated) - - generator.generate() - DokkaConsoleLogger.report() -} - -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") - } - } -} - -class DokkaMessageCollector(val logger: DokkaLogger): MessageCollector { - override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation) { - logger.error(MessageRenderer.PLAIN_FULL_PATHS.render(severity, message, location)) - } -} - -class DokkaGenerator(val logger: DokkaLogger, - val classpath: List, - val sources: List, - val samples: List, - val includes: List, - val moduleName: String, - val outputDir: String, - val outputFormat: String, - val sourceLinks: List, - val skipDeprecated: Boolean = false) { - fun generate() { - val environment = createAnalysisEnvironment() - - logger.info("Module: $moduleName") - logger.info("Output: ${File(outputDir)}") - logger.info("Sources: ${environment.sources.joinToString()}") - logger.info("Classpath: ${environment.classpath.joinToString()}") - - logger.info("Analysing sources and libraries... ") - val startAnalyse = System.currentTimeMillis() - - val options = DocumentationOptions(outputDir, outputFormat, false, sourceLinks = sourceLinks, skipDeprecated = skipDeprecated) - - val injector = Guice.createInjector(DokkaModule(environment, options, logger)) - - val documentation = buildDocumentationModule(injector, moduleName, { isSample(it) }, includes) - - val timeAnalyse = System.currentTimeMillis() - startAnalyse - logger.info("done in ${timeAnalyse / 1000} secs") - - val timeBuild = measureTimeMillis { - logger.info("Generating pages... ") - injector.getInstance(Generator::class.java).buildAll(documentation) - } - logger.info("done in ${timeBuild / 1000} secs") - - Disposer.dispose(environment) - } - - fun createAnalysisEnvironment(): AnalysisEnvironment { - val environment = AnalysisEnvironment(DokkaMessageCollector(logger)) - - environment.apply { - addClasspath(PathUtil.getJdkClassesRoots()) - // addClasspath(PathUtil.getKotlinPathsForCompiler().getRuntimePath()) - for (element in this@DokkaGenerator.classpath) { - addClasspath(File(element)) - } - - addSources(this@DokkaGenerator.sources) - addSources(this@DokkaGenerator.samples) - } - - return environment - } - - fun isSample(file: PsiFile): Boolean { - val sourceFile = File(file.virtualFile!!.path) - return samples.none { sample -> - val canonicalSample = File(sample).canonicalPath - val canonicalSource = sourceFile.canonicalPath - canonicalSource.startsWith(canonicalSample) - } - } -} - -fun buildDocumentationModule(injector: Injector, - moduleName: String, - filesToDocumentFilter: (PsiFile) -> Boolean = { file -> true }, - includes: List = listOf()): DocumentationModule { - - val coreEnvironment = injector.getInstance(KotlinCoreEnvironment::class.java) - val fragmentFiles = coreEnvironment.getSourceFiles().filter(filesToDocumentFilter) - - val resolutionFacade = injector.getInstance(DokkaResolutionFacade::class.java) - val analyzer = resolutionFacade.getFrontendService(LazyTopDownAnalyzerForTopLevel::class.java) - analyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, fragmentFiles) - - val fragments = fragmentFiles - .map { resolutionFacade.resolveSession.getPackageFragment(it.packageFqName) } - .filterNotNull() - .distinct() - - val packageDocs = injector.getInstance(PackageDocs::class.java) - for (include in includes) { - packageDocs.parse(include, fragments.firstOrNull()) - } - val documentationModule = DocumentationModule(moduleName, packageDocs.moduleContent) - - with(injector.getInstance(DocumentationBuilder::class.java)) { - documentationModule.appendFragments(fragments, packageDocs.packageContent, - injector.getInstance(PackageDocumentationBuilder::class.java)) - } - - val javaFiles = coreEnvironment.getJavaSourceFiles().filter(filesToDocumentFilter) - with(injector.getInstance(JavaDocumentationBuilder::class.java)) { - javaFiles.map { appendFile(it, documentationModule, packageDocs.packageContent) } - } - - injector.getInstance(NodeReferenceGraph::class.java).resolveReferences() - - return documentationModule -} - - -fun KotlinCoreEnvironment.getJavaSourceFiles(): List { - val sourceRoots = configuration.get(CommonConfigurationKeys.CONTENT_ROOTS) - ?.filterIsInstance() - ?.map { it.file } - ?: listOf() - - val result = arrayListOf() - 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 -} -- cgit