• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<lambda>null1 package org.jetbrains.dokka
2 
3 import com.google.inject.Guice
4 import com.google.inject.Injector
5 import com.intellij.openapi.util.Disposer
6 import com.intellij.openapi.vfs.VirtualFileManager
7 import com.intellij.psi.PsiFile
8 import com.intellij.psi.PsiJavaFile
9 import com.intellij.psi.PsiManager
10 import org.jetbrains.dokka.DokkaConfiguration.SourceRoot
11 import org.jetbrains.dokka.Utilities.DokkaAnalysisModule
12 import org.jetbrains.dokka.Utilities.DokkaOutputModule
13 import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
14 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
15 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
16 import org.jetbrains.kotlin.cli.common.messages.MessageCollector
17 import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
18 import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
19 import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
20 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
21 import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
22 import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
23 import java.io.File
24 import kotlin.system.measureTimeMillis
25 
26 class DokkaGenerator(val logger: DokkaLogger,
27                      val classpath: List<String>,
28                      val sources: List<SourceRoot>,
29                      val samples: List<String>,
30                      val includes: List<String>,
31                      val moduleName: String,
32                      val options: DocumentationOptions) {
33 
34     private val documentationModule = DocumentationModule(moduleName)
35 
36     fun generate() {
37         val sourcesGroupedByPlatform = sources.groupBy { it.platforms.firstOrNull() }
38         for ((platform, roots) in sourcesGroupedByPlatform) {
39             appendSourceModule(platform, roots)
40         }
41         documentationModule.prepareForGeneration(options)
42 
43         val timeBuild = measureTimeMillis {
44             logger.info("Generating pages... ")
45             val outputInjector = Guice.createInjector(DokkaOutputModule(options, logger))
46             outputInjector.getInstance(Generator::class.java).buildAll(documentationModule)
47         }
48         logger.info("done in ${timeBuild / 1000} secs")
49     }
50 
51     private fun appendSourceModule(defaultPlatform: String?, sourceRoots: List<SourceRoot>) {
52         val sourcePaths = sourceRoots.map { it.path }
53         val environment = createAnalysisEnvironment(sourcePaths)
54 
55         logger.info("Module: $moduleName")
56         logger.info("Output: ${File(options.outputDir)}")
57         logger.info("Sources: ${sourcePaths.joinToString()}")
58         logger.info("Classpath: ${environment.classpath.joinToString()}")
59 
60         logger.info("Analysing sources and libraries... ")
61         val startAnalyse = System.currentTimeMillis()
62 
63         val defaultPlatformAsList = defaultPlatform?.let { listOf(it) }.orEmpty()
64         val defaultPlatformsProvider = object : DefaultPlatformsProvider {
65             override fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String> {
66                 val containingFilePath = descriptor.sourcePsi()?.containingFile?.virtualFile?.canonicalPath
67                         ?.let { File(it).absolutePath }
68                 val sourceRoot = containingFilePath?.let { path -> sourceRoots.find { path.startsWith(it.path) } }
69                 return sourceRoot?.platforms ?: defaultPlatformAsList
70             }
71         }
72 
73         val injector = Guice.createInjector(
74                 DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentationModule.nodeRefGraph, logger))
75 
76         buildDocumentationModule(injector, documentationModule, { isNotSample(it) }, includes)
77 
78         val timeAnalyse = System.currentTimeMillis() - startAnalyse
79         logger.info("done in ${timeAnalyse / 1000} secs")
80 
81         Disposer.dispose(environment)
82     }
83 
84     fun createAnalysisEnvironment(sourcePaths: List<String>): AnalysisEnvironment {
85         val environment = AnalysisEnvironment(DokkaMessageCollector(logger))
86 
87         environment.apply {
88             //addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre())
89             //   addClasspath(PathUtil.getKotlinPathsForCompiler().getRuntimePath())
90             for (element in this@DokkaGenerator.classpath) {
91                 addClasspath(File(element))
92             }
93 
94             addSources(sourcePaths)
95             addSources(this@DokkaGenerator.samples)
96 
97             loadLanguageVersionSettings(options.languageVersion, options.apiVersion)
98         }
99 
100         return environment
101     }
102 
103     fun isNotSample(file: PsiFile): Boolean {
104         val sourceFile = File(file.virtualFile!!.path)
105         return samples.none { sample ->
106             val canonicalSample = File(sample).canonicalPath
107             val canonicalSource = sourceFile.canonicalPath
108             canonicalSource.startsWith(canonicalSample)
109         }
110     }
111 }
112 
113 class DokkaMessageCollector(val logger: DokkaLogger) : MessageCollector {
clearnull114     override fun clear() {
115         seenErrors = false
116     }
117 
118     private var seenErrors = false
119 
reportnull120     override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
121         if (severity == CompilerMessageSeverity.ERROR) {
122             seenErrors = true
123         }
124         logger.error(MessageRenderer.PLAIN_FULL_PATHS.render(severity, message, location))
125     }
126 
hasErrorsnull127     override fun hasErrors() = seenErrors
128 }
129 
130 fun buildDocumentationModule(injector: Injector,
131                              documentationModule: DocumentationModule,
132                              filesToDocumentFilter: (PsiFile) -> Boolean = { file -> true },
133                              includes: List<String> = listOf()) {
134 
135     val coreEnvironment = injector.getInstance(KotlinCoreEnvironment::class.java)
136     val fragmentFiles = coreEnvironment.getSourceFiles().filter(filesToDocumentFilter)
137 
138     val resolutionFacade = injector.getInstance(DokkaResolutionFacade::class.java)
139     val analyzer = resolutionFacade.getFrontendService(LazyTopDownAnalyzer::class.java)
140     analyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, fragmentFiles)
141 
142     val fragments = fragmentFiles
<lambda>null143             .map { resolutionFacade.resolveSession.getPackageFragment(it.packageFqName) }
144             .filterNotNull()
145             .distinct()
146 
147     val packageDocs = injector.getInstance(PackageDocs::class.java)
148     for (include in includes) {
149         packageDocs.parse(include, fragments)
150     }
151     if (documentationModule.content.isEmpty()) {
<lambda>null152         documentationModule.updateContent {
153             for (node in packageDocs.moduleContent.children) {
154                 append(node)
155             }
156         }
157     }
158 
159     parseJavaPackageDocs(packageDocs, coreEnvironment)
160 
<lambda>null161     with(injector.getInstance(DocumentationBuilder::class.java)) {
162         documentationModule.appendFragments(fragments, packageDocs.packageContent,
163                 injector.getInstance(PackageDocumentationBuilder::class.java))
164 
165         propagateExtensionFunctionsToSubclasses(fragments, resolutionFacade)
166     }
167 
168     val javaFiles = coreEnvironment.getJavaSourceFiles().filter(filesToDocumentFilter)
<lambda>null169     with(injector.getInstance(JavaDocumentationBuilder::class.java)) {
170         javaFiles.map { appendFile(it, documentationModule, packageDocs.packageContent) }
171     }
172 }
173 
parseJavaPackageDocsnull174 fun parseJavaPackageDocs(packageDocs: PackageDocs, coreEnvironment: KotlinCoreEnvironment) {
175     val contentRoots = coreEnvironment.configuration.get(CLIConfigurationKeys.CONTENT_ROOTS)
176             ?.filterIsInstance<JavaSourceRoot>()
177             ?.map { it.file }
178             ?: listOf()
179     contentRoots.forEach { root ->
180         root.walkTopDown().filter { it.name == "overview.html" }.forEach {
181             packageDocs.parseJava(it.path, it.relativeTo(root).parent.replace("/", "."))
182         }
183     }
184 }
185 
186 
KotlinCoreEnvironmentnull187 fun KotlinCoreEnvironment.getJavaSourceFiles(): List<PsiJavaFile> {
188     val sourceRoots = configuration.get(CLIConfigurationKeys.CONTENT_ROOTS)
189             ?.filterIsInstance<JavaSourceRoot>()
190             ?.map { it.file }
191             ?: listOf()
192 
193     val result = arrayListOf<PsiJavaFile>()
194     val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file")
195     sourceRoots.forEach { sourceRoot ->
196         sourceRoot.absoluteFile.walkTopDown().forEach {
197             val vFile = localFileSystem.findFileByPath(it.path)
198             if (vFile != null) {
199                 val psiFile = PsiManager.getInstance(project).findFile(vFile)
200                 if (psiFile is PsiJavaFile) {
201                     result.add(psiFile)
202                 }
203             }
204         }
205     }
206     return result
207 }
208