1 package org.jetbrains.dokka.Samples 2 3 import com.google.inject.Inject 4 import com.intellij.psi.PsiElement 5 import com.intellij.psi.PsiWhiteSpace 6 import com.intellij.psi.impl.source.tree.LeafPsiElement 7 import com.intellij.psi.util.PsiTreeUtil 8 import org.jetbrains.dokka.* 9 import org.jetbrains.kotlin.psi.* 10 import org.jetbrains.kotlin.psi.psiUtil.allChildren 11 import org.jetbrains.kotlin.psi.psiUtil.prevLeaf 12 import org.jetbrains.kotlin.resolve.ImportPath 13 14 open class KotlinWebsiteSampleProcessingService 15 @Inject constructor(options: DocumentationOptions, 16 logger: DokkaLogger, 17 resolutionFacade: DokkaResolutionFacade) 18 : DefaultSampleProcessingService(options, logger, resolutionFacade) { 19 20 private class SampleBuilder : KtTreeVisitorVoid() { 21 val builder = StringBuilder() 22 val text: String 23 get() = builder.toString() 24 extractStringArgumentValuenull25 fun KtValueArgument.extractStringArgumentValue() = 26 (getArgumentExpression() as KtStringTemplateExpression) 27 .entries.joinToString("") { it.text } 28 29 convertAssertPrintsnull30 fun convertAssertPrints(expression: KtCallExpression) { 31 val (argument, commentArgument) = expression.valueArguments 32 builder.apply { 33 append("println(") 34 append(argument.text) 35 append(") // ") 36 append(commentArgument.extractStringArgumentValue()) 37 } 38 } 39 convertAssertTrueFalsenull40 fun convertAssertTrueFalse(expression: KtCallExpression, expectedResult: Boolean) { 41 val (argument) = expression.valueArguments 42 builder.apply { 43 expression.valueArguments.getOrNull(1)?.let { 44 append("// ${it.extractStringArgumentValue()}") 45 val ws = expression.prevLeaf { it is PsiWhiteSpace } 46 append(ws?.text ?: "\n") 47 } 48 append("println(\"") 49 append(argument.text) 50 append(" is \${") 51 append(argument.text) 52 append("}\") // $expectedResult") 53 } 54 } 55 convertAssertFailsnull56 fun convertAssertFails(expression: KtCallExpression) { 57 val (message, funcArgument) = expression.valueArguments 58 builder.apply { 59 val argument = if (funcArgument.getArgumentExpression() is KtLambdaExpression) 60 PsiTreeUtil.findChildOfType(funcArgument, KtBlockExpression::class.java)?.text ?: "" 61 else 62 funcArgument.text 63 append(argument.lines().joinToString(separator = "\n") { "// $it" }) 64 append(" // ") 65 append(message.extractStringArgumentValue()) 66 append(" will fail") 67 } 68 } 69 convertAssertFailsWithnull70 fun convertAssertFailsWith(expression: KtCallExpression) { 71 val (funcArgument) = expression.valueArguments 72 val (exceptionType) = expression.typeArguments 73 builder.apply { 74 val argument = if (funcArgument.firstChild is KtLambdaExpression) 75 PsiTreeUtil.findChildOfType(funcArgument, KtBlockExpression::class.java)?.text ?: "" 76 else 77 funcArgument.text 78 append(argument.lines().joinToString(separator = "\n") { "// $it" }) 79 append(" // will fail with ") 80 append(exceptionType.text) 81 } 82 } 83 visitCallExpressionnull84 override fun visitCallExpression(expression: KtCallExpression) { 85 when (expression.calleeExpression?.text) { 86 "assertPrints" -> convertAssertPrints(expression) 87 "assertTrue" -> convertAssertTrueFalse(expression, expectedResult = true) 88 "assertFalse" -> convertAssertTrueFalse(expression, expectedResult = false) 89 "assertFails" -> convertAssertFails(expression) 90 "assertFailsWith" -> convertAssertFailsWith(expression) 91 else -> super.visitCallExpression(expression) 92 } 93 } 94 visitElementnull95 override fun visitElement(element: PsiElement) { 96 if (element is LeafPsiElement) 97 builder.append(element.text) 98 super.visitElement(element) 99 } 100 } 101 PsiElementnull102 private fun PsiElement.buildSampleText(): String { 103 val sampleBuilder = SampleBuilder() 104 this.accept(sampleBuilder) 105 return sampleBuilder.text 106 } 107 <lambda>null108 val importsToIgnore = arrayOf("samples.*").map { ImportPath.fromString(it) } 109 processImportsnull110 override fun processImports(psiElement: PsiElement): ContentBlockCode { 111 val psiFile = psiElement.containingFile 112 if (psiFile is KtFile) { 113 return ContentBlockCode("kotlin").apply { 114 append(ContentText("\n")) 115 psiFile.importList?.let { 116 it.allChildren.filter { 117 it !is KtImportDirective || it.importPath !in importsToIgnore 118 }.forEach { append(ContentText(it.text)) } 119 } 120 } 121 } 122 return super.processImports(psiElement) 123 } 124 processSampleBodynull125 override fun processSampleBody(psiElement: PsiElement) = when (psiElement) { 126 is KtDeclarationWithBody -> { 127 val bodyExpression = psiElement.bodyExpression 128 val bodyExpressionText = bodyExpression!!.buildSampleText() 129 when (bodyExpression) { 130 is KtBlockExpression -> bodyExpressionText.removeSurrounding("{", "}") 131 else -> bodyExpressionText 132 } 133 } 134 else -> psiElement.buildSampleText() 135 } 136 } 137 138