aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Formats/StructuredFormatService.kt8
-rw-r--r--src/Kotlin/DocumentationBuilder.kt46
-rw-r--r--src/Kotlin/KotlinLanguageService.kt7
-rw-r--r--src/Model/DocumentationNode.kt2
-rw-r--r--src/main.kt28
-rw-r--r--test/data/format/propertyVar.kt1
-rw-r--r--test/data/format/propertyVar.md12
-rw-r--r--test/src/TestAPI.kt2
-rw-r--r--test/src/format/MarkdownFormatTest.kt6
-rw-r--r--test/src/model/PropertyTest.kt3
10 files changed, 107 insertions, 8 deletions
diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt
index bfb69d1d..458fda35 100644
--- a/src/Formats/StructuredFormatService.kt
+++ b/src/Formats/StructuredFormatService.kt
@@ -121,6 +121,7 @@ public abstract class StructuredFormatService(val locationService: LocationServi
appendBlockCode(to, formatText(location, languageService.render(it)))
it.appendOverrides(to)
it.appendDeprecation(to)
+ it.appendSourceLink(to)
}
appendLine(to, summary)
appendLine(to)
@@ -148,6 +149,13 @@ public abstract class StructuredFormatService(val locationService: LocationServi
}
}
+ private fun DocumentationNode.appendSourceLink(to: StringBuilder) {
+ val sourceUrl = details(DocumentationNode.Kind.SourceUrl).firstOrNull()
+ if (sourceUrl != null) {
+ appendLine(to, formatLink("Source", sourceUrl.name))
+ }
+ }
+
fun appendLocation(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
val breakdownByName = nodes.groupBy { node -> node.name }
for ((name, items) in breakdownByName) {
diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt
index 5af4c9f1..6403f14c 100644
--- a/src/Kotlin/DocumentationBuilder.kt
+++ b/src/Kotlin/DocumentationBuilder.kt
@@ -13,9 +13,15 @@ import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
import org.jetbrains.kotlin.resolve.source.getPsi
+import java.io.File
+import com.intellij.psi.PsiDocumentManager
+import com.intellij.psi.PsiNameIdentifierOwner
+import com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.psi.JetParameter
-public data class DocumentationOptions(val includeNonPublic: Boolean = false)
+public data class DocumentationOptions(val includeNonPublic: Boolean = false,
+ val sourceLinks: List<SourceLinkDefinition>)
private fun isSamePackage(descriptor1: DeclarationDescriptor, descriptor2: DeclarationDescriptor): Boolean {
val package1 = DescriptorUtils.getParentOfType(descriptor1, javaClass<PackageFragmentDescriptor>())
@@ -144,6 +150,36 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
}
}
+ fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) {
+ val psi = getTargetElement(sourceElement)
+ val path = psi?.getContainingFile()?.getVirtualFile()?.getPath()
+ if (path == null) {
+ return
+ }
+ val absPath = File(path).getAbsolutePath()
+ val linkDef = findSourceLinkDefinition(absPath)
+ if (linkDef != null) {
+ var url = linkDef.url + path.substring(linkDef.path.length())
+ if (linkDef.lineSuffix != null) {
+ val doc = PsiDocumentManager.getInstance(psi!!.getProject()).getDocument(psi.getContainingFile())
+ if (doc != null) {
+ // IJ uses 0-based line-numbers; external source browsers use 1-based
+ val line = doc.getLineNumber(psi.getTextRange().getStartOffset()) + 1
+ url += linkDef.lineSuffix + line.toString()
+ }
+ }
+ append(DocumentationNode(url, Content.Empty, DocumentationNode.Kind.SourceUrl),
+ DocumentationReference.Kind.Detail);
+ }
+ }
+
+ private fun getTargetElement(sourceElement: SourceElement): PsiElement? {
+ val psi = sourceElement.getPsi()
+ return if (psi is PsiNameIdentifierOwner) psi.getNameIdentifier() else psi
+ }
+
+ fun findSourceLinkDefinition(path: String) = options.sourceLinks.firstOrNull { path.startsWith(it.path) }
+
fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
// do not include generated code
if (descriptor is CallableMemberDescriptor && descriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION)
@@ -234,6 +270,7 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
DocumentationReference.Kind.Member)
}
node.appendAnnotations(this)
+ node.appendSourceLink(getSource())
register(this, node)
return node
}
@@ -265,6 +302,8 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
node.appendType(getReturnType())
node.appendAnnotations(this)
+ node.appendSourceLink(getSource())
+
register(this, node)
return node
@@ -287,6 +326,11 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
node.appendType(getReturnType())
node.appendAnnotations(this)
+ node.appendSourceLink(getSource())
+ if (isVar()) {
+ node.append(DocumentationNode("var", Content.Empty, DocumentationNode.Kind.Modifier),
+ DocumentationReference.Kind.Detail)
+ }
getGetter()?.let {
if (!it.isDefault())
node.appendChild(it, DocumentationReference.Kind.Member)
diff --git a/src/Kotlin/KotlinLanguageService.kt b/src/Kotlin/KotlinLanguageService.kt
index 3e9192c4..80a76791 100644
--- a/src/Kotlin/KotlinLanguageService.kt
+++ b/src/Kotlin/KotlinLanguageService.kt
@@ -108,7 +108,7 @@ class KotlinLanguageService : LanguageService {
private fun ContentNode.renderModifier(node: DocumentationNode) {
when (node.name) {
- "final", "internal" -> {}
+ "final", "internal", "var" -> {}
else -> {
keyword(node.name)
text(" ")
@@ -242,7 +242,7 @@ class KotlinLanguageService : LanguageService {
renderAnnotationsForNode(node)
when (node.kind) {
DocumentationNode.Kind.Property,
- DocumentationNode.Kind.ClassObjectProperty -> keyword("val ")
+ DocumentationNode.Kind.ClassObjectProperty -> keyword("${node.getPropertyKeyword()} ")
else -> throw IllegalArgumentException("Node $node is not a property")
}
renderTypeParametersForNode(node)
@@ -257,6 +257,9 @@ class KotlinLanguageService : LanguageService {
renderType(node.detail(DocumentationNode.Kind.Type))
}
+ fun DocumentationNode.getPropertyKeyword() =
+ if (details(DocumentationNode.Kind.Modifier).any { it.name == "var" }) "var" else "val"
+
fun ContentNode.identifierOrDeprecated(node: DocumentationNode) {
if (node.deprecation != null) {
val strike = ContentStrikethrough()
diff --git a/src/Model/DocumentationNode.kt b/src/Model/DocumentationNode.kt
index 6e707cce..a666b486 100644
--- a/src/Model/DocumentationNode.kt
+++ b/src/Model/DocumentationNode.kt
@@ -97,6 +97,8 @@ public open class DocumentationNode(val name: String,
Annotation
Value
+
+ SourceUrl
}
}
diff --git a/src/main.kt b/src/main.kt
index 24853be5..989e8a36 100644
--- a/src/main.kt
+++ b/src/main.kt
@@ -6,7 +6,6 @@ import org.jetbrains.kotlin.cli.common.messages.*
import org.jetbrains.kotlin.cli.common.arguments.*
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.name.FqName
class DokkaArguments {
@@ -14,6 +13,10 @@ class DokkaArguments {
ValueDescription("<path>")
public var src: String = ""
+ Argument(value = "srcLink", description = "Mapping between a source directory and a Web site for browsing the code")
+ ValueDescription("<path>=<url>[#lineSuffix]")
+ public var srcLink: String = ""
+
Argument(value = "include", description = "Markdown files to load (allows many paths separated by the system path separator)")
ValueDescription("<path>")
public var include: String = ""
@@ -40,6 +43,15 @@ class DokkaArguments {
}
+class SourceLinkDefinition(val path: String, val url: String, val lineSuffix: String?)
+
+private fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinition {
+ val (path, urlAndLine) = srcLink.split('=')
+ return SourceLinkDefinition(File(path).getAbsolutePath(),
+ urlAndLine.substringBefore("#"),
+ urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#" + it })
+}
+
public fun main(args: Array<String>) {
val arguments = DokkaArguments()
val freeArgs: List<String> = Args.parse(arguments, args) ?: listOf()
@@ -47,6 +59,15 @@ public fun main(args: Array<String>) {
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: <path>=<url>[#lineSuffix]. No source links will be generated.")
+ }
+ listOf()
+ }
+
val environment = AnalysisEnvironment(MessageCollectorPlainTextToStream.PLAIN_TEXT_TO_SYSTEM_ERR) {
addClasspath(PathUtil.getJdkClassesRoots())
// addClasspath(PathUtil.getKotlinPathsForCompiler().getRuntimePath())
@@ -79,7 +100,7 @@ public fun main(args: Array<String>) {
}
}
val fragments = fragmentFiles.map { session.getPackageFragment(it.getPackageFqName()) }.filterNotNull().distinct()
- val options = DocumentationOptions()
+ val options = DocumentationOptions(false, sourceLinks)
val documentationBuilder = DocumentationBuilder(session, options)
with(documentationBuilder) {
@@ -133,4 +154,5 @@ public fun main(args: Array<String>) {
println()
println("Done.")
Disposer.dispose(environment)
-} \ No newline at end of file
+}
+
diff --git a/test/data/format/propertyVar.kt b/test/data/format/propertyVar.kt
new file mode 100644
index 00000000..88be1a7a
--- /dev/null
+++ b/test/data/format/propertyVar.kt
@@ -0,0 +1 @@
+var x = 1 \ No newline at end of file
diff --git a/test/data/format/propertyVar.md b/test/data/format/propertyVar.md
new file mode 100644
index 00000000..6b14fca3
--- /dev/null
+++ b/test/data/format/propertyVar.md
@@ -0,0 +1,12 @@
+[test](out.md) / [](out.md) / [x](out.md)
+
+
+# x
+
+
+```
+var x: Int
+```
+
+
+
diff --git a/test/src/TestAPI.kt b/test/src/TestAPI.kt
index 4d64014e..f3c5f8c8 100644
--- a/test/src/TestAPI.kt
+++ b/test/src/TestAPI.kt
@@ -32,7 +32,7 @@ public fun verifyModel(vararg files: String, verifier: (DocumentationModule) ->
addSources(files.toList())
}
- val options = DocumentationOptions(includeNonPublic = true)
+ val options = DocumentationOptions(includeNonPublic = true, sourceLinks = listOf<SourceLinkDefinition>())
val documentation = environment.withContext { environment, session ->
val fragments = environment.getSourceFiles().map { session.getPackageFragment(it.getPackageFqName()) }.filterNotNull().distinct()
diff --git a/test/src/format/MarkdownFormatTest.kt b/test/src/format/MarkdownFormatTest.kt
index 65713891..5a4bacb2 100644
--- a/test/src/format/MarkdownFormatTest.kt
+++ b/test/src/format/MarkdownFormatTest.kt
@@ -70,6 +70,12 @@ public class MarkdownFormatTest {
}
+ Test fun propertyVar() {
+ verifyOutput("test/data/format/propertyVar.kt", ".md") { model, output ->
+ markdownService.appendNodes(tempLocation, output, model.members.single().members)
+ }
+ }
+
Test fun functionWithDefaultParameter() {
verifyOutput("test/data/format/functionWithDefaultParameter.kt", ".md") { model, output ->
markdownService.appendNodes(tempLocation, output, model.members.single().members)
diff --git a/test/src/model/PropertyTest.kt b/test/src/model/PropertyTest.kt
index 14c43f78..d6de84bb 100644
--- a/test/src/model/PropertyTest.kt
+++ b/test/src/model/PropertyTest.kt
@@ -57,11 +57,12 @@ public class PropertyTest {
assertEquals("property", name)
assertEquals(DocumentationNode.Kind.Property, kind)
assertEquals(Content.Empty, content)
- assertEquals(3, details.count())
+ assertEquals(4, details.count())
assertEquals("String", detail(DocumentationNode.Kind.Type).name)
val modifiers = details(DocumentationNode.Kind.Modifier).map { it.name }
assertTrue("final" in modifiers)
assertTrue("internal" in modifiers)
+ assertTrue("var" in modifiers)
assertTrue(links.none())
assertEquals(2, members.count())