• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.jetbrains.dokka.Samples
2 
3 import com.google.inject.Inject
4 import com.intellij.psi.PsiElement
5 import org.jetbrains.dokka.*
6 import org.jetbrains.kotlin.descriptors.ClassDescriptor
7 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
8 import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
9 import org.jetbrains.kotlin.idea.kdoc.getKDocLinkResolutionScope
10 import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
11 import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
12 import org.jetbrains.kotlin.name.Name
13 import org.jetbrains.kotlin.psi.KtBlockExpression
14 import org.jetbrains.kotlin.psi.KtDeclarationWithBody
15 import org.jetbrains.kotlin.psi.KtFile
16 import org.jetbrains.kotlin.resolve.BindingContext
17 import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
18 import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
19 import org.jetbrains.kotlin.resolve.scopes.ResolutionScope
20 
21 
22 open class DefaultSampleProcessingService
23 @Inject constructor(val options: DocumentationOptions,
24                     val logger: DokkaLogger,
25                     val resolutionFacade: DokkaResolutionFacade)
26     : SampleProcessingService {
27 
resolveSamplenull28     override fun resolveSample(descriptor: DeclarationDescriptor, functionName: String?, kdocTag: KDocTag): ContentNode {
29         if (functionName == null) {
30             logger.warn("Missing function name in @sample in ${descriptor.signature()}")
31             return ContentBlockSampleCode().apply { append(ContentText("//Missing function name in @sample")) }
32         }
33         val bindingContext = BindingContext.EMPTY
34         val symbol = resolveKDocLink(bindingContext, resolutionFacade, descriptor, kdocTag, functionName.split(".")).firstOrNull()
35         if (symbol == null) {
36             logger.warn("Unresolved function $functionName in @sample in ${descriptor.signature()}")
37             return ContentBlockSampleCode().apply { append(ContentText("//Unresolved: $functionName")) }
38         }
39         val psiElement = DescriptorToSourceUtils.descriptorToDeclaration(symbol)
40         if (psiElement == null) {
41             logger.warn("Can't find source for function $functionName in @sample in ${descriptor.signature()}")
42             return ContentBlockSampleCode().apply { append(ContentText("//Source not found: $functionName")) }
43         }
44 
45         val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd()
46         val lines = text.split("\n")
47         val indent = lines.filter(String::isNotBlank).map { it.takeWhile(Char::isWhitespace).count() }.min() ?: 0
48         val finalText = lines.map { it.drop(indent) }.joinToString("\n")
49 
50         return ContentBlockSampleCode(importsBlock = processImports(psiElement)).apply { append(ContentText(finalText)) }
51     }
52 
processSampleBodynull53     protected open fun processSampleBody(psiElement: PsiElement): String = when (psiElement) {
54         is KtDeclarationWithBody -> {
55             val bodyExpression = psiElement.bodyExpression
56             when (bodyExpression) {
57                 is KtBlockExpression -> bodyExpression.text.removeSurrounding("{", "}")
58                 else -> bodyExpression!!.text
59             }
60         }
61         else -> psiElement.text
62     }
63 
processImportsnull64     protected open fun processImports(psiElement: PsiElement): ContentBlockCode {
65         val psiFile = psiElement.containingFile
66         if (psiFile is KtFile) {
67             return ContentBlockCode("kotlin").apply {
68                 append(ContentText(psiFile.importList?.text ?: ""))
69             }
70         } else {
71             return ContentBlockCode("")
72         }
73     }
74 
resolveInScopenull75     private fun resolveInScope(functionName: String, scope: ResolutionScope): DeclarationDescriptor? {
76         var currentScope = scope
77         val parts = functionName.split('.')
78 
79         var symbol: DeclarationDescriptor? = null
80 
81         for (part in parts) {
82             // short name
83             val symbolName = Name.identifier(part)
84             val partSymbol = currentScope.getContributedDescriptors(DescriptorKindFilter.ALL, { it == symbolName })
85                     .filter { it.name == symbolName }
86                     .firstOrNull()
87 
88             if (partSymbol == null) {
89                 symbol = null
90                 break
91             }
92             @Suppress("IfThenToElvis")
93             currentScope = if (partSymbol is ClassDescriptor)
94                 partSymbol.defaultType.memberScope
95             else if (partSymbol is PackageViewDescriptor)
96                 partSymbol.memberScope
97             else
98                 getKDocLinkResolutionScope(resolutionFacade, partSymbol)
99             symbol = partSymbol
100         }
101 
102         return symbol
103     }
104 }
105 
106