Skip to content

Commit e5b62de

Browse files
authored
Merge pull request #21 from olafurpg/documentation
2 parents fe0b7b0 + d5c79e2 commit e5b62de

File tree

10 files changed

+190
-45
lines changed

10 files changed

+190
-45
lines changed

semanticdb-kotlinc/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ dependencies {
4646
testImplementation(kotlin("reflect"))
4747
testImplementation(kotlin("script-runtime", "1.5.0"))
4848

49-
snapshotsImplementation("com.sourcegraph", "lsif-java_2.13", "0.5.6")
49+
snapshotsImplementation("com.sourcegraph", "lsif-java_2.13", "0.6.12")
5050
}
5151

5252
tasks.withType<KotlinCompile> {

semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/kotlin/snapshots/Class.kt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ package snapshots
22
// ^^^^^^^^^ reference snapshots/
33

44
class Class constructor(private var banana: Int, apple: String) :
5-
// ^^^^^ definition snapshots/Class# Class
6-
// ^^^^^^^^^^^ definition snapshots/Class#`<init>`(). Class
7-
// ^^^^^^ definition snapshots/Class#banana. banana
8-
// ^^^^^^ definition snapshots/Class#getBanana(). banana
9-
// ^^^^^^ definition snapshots/Class#setBanana(). banana
10-
// ^^^^^^ definition snapshots/Class#`<init>`().(banana) banana
5+
// ^^^^^ definition snapshots/Class# public final class Class : kotlin.Throwable
6+
// ^^^^^^^^^^^ definition snapshots/Class#`<init>`(). public constructor Class(banana: kotlin.Int, apple: kotlin.String)
7+
// ^^^^^^ definition snapshots/Class#banana. private final var banana: kotlin.Int
8+
// ^^^^^^ definition snapshots/Class#getBanana(). private final var banana: kotlin.Int
9+
// ^^^^^^ definition snapshots/Class#setBanana(). private final var banana: kotlin.Int
10+
// ^^^^^^ definition snapshots/Class#`<init>`().(banana) value-parameter banana: kotlin.Int
1111
// ^^^ reference kotlin/Int#
12-
// ^^^^^ definition snapshots/Class#`<init>`().(apple) apple
12+
// ^^^^^ definition snapshots/Class#`<init>`().(apple) value-parameter apple: kotlin.String
1313
// ^^^^^^ reference kotlin/String#
1414
Throwable(banana.toString() + apple) {
1515
// ^^^^^^^^^ reference kotlin/Throwable#`<init>`().
@@ -23,25 +23,25 @@ class Class constructor(private var banana: Int, apple: String) :
2323
}
2424

2525
val asdf =
26-
// ^^^^ definition snapshots/Class#asdf. asdf
27-
// ^^^^ definition snapshots/Class#getAsdf(). asdf
26+
// ^^^^ definition snapshots/Class#asdf. public final val asdf: kotlin.Any
27+
// ^^^^ definition snapshots/Class#getAsdf(). public final val asdf: kotlin.Any
2828
object {
2929
fun doStuff() = Unit
30-
// ^^^^^^^ definition local0 doStuff
30+
// ^^^^^^^ definition local0 public final fun doStuff()
3131
// ^^^^ reference kotlin/Unit#
3232
}
3333

3434
constructor() : this(1, "")
35-
//^^^^^^^^^^^ definition snapshots/Class#`<init>`(+1). Class
35+
//^^^^^^^^^^^ definition snapshots/Class#`<init>`(+1). public constructor Class()
3636

3737
constructor(banana: Int) : this(banana, "")
38-
//^^^^^^^^^^^ definition snapshots/Class#`<init>`(+2). Class
39-
// ^^^^^^ definition snapshots/Class#`<init>`(+2).(banana) banana
38+
//^^^^^^^^^^^ definition snapshots/Class#`<init>`(+2). public constructor Class(banana: kotlin.Int)
39+
// ^^^^^^ definition snapshots/Class#`<init>`(+2).(banana) value-parameter banana: kotlin.Int
4040
// ^^^ reference kotlin/Int#
4141
// ^^^^^^ reference snapshots/Class#`<init>`(+2).(banana)
4242

4343
fun run() {
44-
// ^^^ definition snapshots/Class#run(). run
44+
// ^^^ definition snapshots/Class#run(). public final fun run()
4545
println(Class::class)
4646
// ^^^^^^^ reference kotlin/io/ConsoleKt#println(+1).
4747
// ^^^^^ reference snapshots/Class#

semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/kotlin/snapshots/CompanionOwner.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ package snapshots
22
// ^^^^^^^^^ reference snapshots/
33

44
class CompanionOwner {
5-
// ^^^^^^^^^^^^^^ definition snapshots/CompanionOwner# CompanionOwner
6-
// ^^^^^^^^^^^^^^ definition snapshots/CompanionOwner#`<init>`(). CompanionOwner
5+
// ^^^^^^^^^^^^^^ definition snapshots/CompanionOwner# public final class CompanionOwner
6+
// ^^^^^^^^^^^^^^ definition snapshots/CompanionOwner#`<init>`(). public constructor CompanionOwner()
77
companion object {
8-
// ^^^^^^^^^ definition snapshots/CompanionOwner#Companion# Companion
8+
// ^^^^^^^^^ definition snapshots/CompanionOwner#Companion# public companion object
99
fun create(): CompanionOwner = CompanionOwner()
10-
// ^^^^^^ definition snapshots/CompanionOwner#Companion#create(). create
10+
// ^^^^^^ definition snapshots/CompanionOwner#Companion#create(). public final fun create(): snapshots.CompanionOwner
1111
// ^^^^^^^^^^^^^^ reference snapshots/CompanionOwner#
1212
// ^^^^^^^^^^^^^^ reference snapshots/CompanionOwner#`<init>`().
1313
}
1414
fun create(): Int = CompanionOwner.create().hashCode()
15-
// ^^^^^^ definition snapshots/CompanionOwner#create(). create
15+
// ^^^^^^ definition snapshots/CompanionOwner#create(). public final fun create(): kotlin.Int
1616
// ^^^ reference kotlin/Int#
1717
// ^^^^^^^^^^^^^^ reference snapshots/CompanionOwner#Companion#
1818
// ^^^^^^ reference snapshots/CompanionOwner#Companion#create().
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package snapshots
2+
// ^^^^^^^^^ reference snapshots/
3+
4+
import java.io.Serializable
5+
// ^^^^ reference java/
6+
// ^^ reference java/io/
7+
// ^^^^^^^^^^^^ reference java/io/Serializable#
8+
9+
abstract class DocstringSuperclass
10+
// ^^^^^^^^^^^^^^^^^^^ definition snapshots/DocstringSuperclass# public abstract class DocstringSuperclass
11+
// ^^^^^^^^^^^^^^^^^^^ definition snapshots/DocstringSuperclass#`<init>`(). public constructor DocstringSuperclass()
12+
/** Example class docstring. */
13+
class Docstrings : DocstringSuperclass(), Serializable {
14+
// ^^^^^^^^^^ definition snapshots/Docstrings# public final class Docstrings : snapshots.DocstringSuperclass, java.io.Serializable
15+
// ^^^^^^^^^^ definition snapshots/Docstrings#`<init>`(). public constructor Docstrings()
16+
// ^^^^^^^^^^^^^^^^^^^ reference snapshots/DocstringSuperclass#`<init>`().
17+
// ^^^^^^^^^^^^ reference java/io/Serializable#
18+
}
19+
20+
/** Example method docstring. */
21+
fun docstrings() { }
22+
// ^^^^^^^^^^ definition snapshots/DocstringsKt#docstrings(). public fun docstrings()

semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/kotlin/snapshots/Functions.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package snapshots
22
// ^^^^^^^^^ reference snapshots/
33

44
fun sampleText(x: String = "") {
5-
// ^^^^^^^^^^ definition snapshots/FunctionsKt#sampleText(). sampleText
6-
// ^ definition snapshots/FunctionsKt#sampleText().(x) x
5+
// ^^^^^^^^^^ definition snapshots/FunctionsKt#sampleText(). public fun sampleText(x: kotlin.String = ...)
6+
// ^ definition snapshots/FunctionsKt#sampleText().(x) value-parameter x: kotlin.String = ...
77
// ^^^^^^ reference kotlin/String#
88
println(x)
99
//^^^^^^^ reference kotlin/io/ConsoleKt#println(+1).

semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/kotlin/snapshots/Lambdas.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,27 @@ package snapshots
22
// ^^^^^^^^^ reference snapshots/
33

44
val x = arrayListOf<String>().forEachIndexed { i, s -> println("$i $s") }
5-
// ^ definition snapshots/LambdasKt#x. x
6-
// ^ definition snapshots/LambdasKt#getX(). x
5+
// ^ definition snapshots/LambdasKt#x. public val x: kotlin.Unit
6+
// ^ definition snapshots/LambdasKt#getX(). public val x: kotlin.Unit
77
// ^^^^^^^^^^^ reference kotlin/collections/CollectionsKt#arrayListOf().
88
// ^^^^^^ reference kotlin/String#
99
// ^^^^^^^^^^^^^^ reference kotlin/collections/CollectionsKt#forEachIndexed(+9).
10-
// ^ definition local0 i
11-
// ^ definition local1 s
10+
// ^ definition local0 value-parameter i: kotlin.Int
11+
// ^ definition local1 value-parameter s: kotlin.String
1212
// ^^^^^^^ reference kotlin/io/ConsoleKt#println(+1).
1313
// ^ reference local0
1414
// ^ reference local1
1515

1616
val y = "fdsa".run { this.toByteArray() }
17-
// ^ definition snapshots/LambdasKt#y. y
18-
// ^ definition snapshots/LambdasKt#getY(). y
17+
// ^ definition snapshots/LambdasKt#y. public val y: kotlin.ByteArray
18+
// ^ definition snapshots/LambdasKt#getY(). public val y: kotlin.ByteArray
1919
// ^^^ reference kotlin/StandardKt#run(+1).
20-
// ^^^^ reference
20+
// ^^^^ reference
2121
// ^^^^^^^^^^^ reference kotlin/text/StringsKt#toByteArray().
2222

2323
val z = y.let { it.size }
24-
// ^ definition snapshots/LambdasKt#z. z
25-
// ^ definition snapshots/LambdasKt#getZ(). z
24+
// ^ definition snapshots/LambdasKt#z. public val z: kotlin.Int
25+
// ^ definition snapshots/LambdasKt#getZ(). public val z: kotlin.Int
2626
// ^ reference snapshots/LambdasKt#y.
2727
// ^ reference snapshots/LambdasKt#getY().
2828
// ^^^ reference kotlin/StandardKt#let().

semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/kotlin/snapshots/ObjectKt.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import java.lang.RuntimeException
77
// ^^^^^^^^^^^^^^^^ reference java/lang/RuntimeException#
88

99
object ObjectKt {
10-
// ^^^^^^^^ definition snapshots/ObjectKt# ObjectKt
10+
// ^^^^^^^^ definition snapshots/ObjectKt# public object ObjectKt
1111
fun fail(message: String?): Nothing {
12-
// ^^^^ definition snapshots/ObjectKt#fail(). fail
13-
// ^^^^^^^ definition snapshots/ObjectKt#fail().(message) message
12+
// ^^^^ definition snapshots/ObjectKt#fail(). public final fun fail(message: kotlin.String?): kotlin.Nothing
13+
// ^^^^^^^ definition snapshots/ObjectKt#fail().(message) value-parameter message: kotlin.String?
1414
// ^^^^^^ reference kotlin/String#
1515
// ^^^^^^^ reference kotlin/Nothing#
1616
throw RuntimeException(message)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package snapshots
2+
3+
import java.io.Serializable
4+
5+
abstract class DocstringSuperclass
6+
/** Example class docstring. */
7+
class Docstrings : DocstringSuperclass(), Serializable {
8+
}
9+
10+
/** Example method docstring. */
11+
fun docstrings() { }

semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbTextDocumentBuilder.kt

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,24 @@ package com.sourcegraph.semanticdb_kotlinc
22

33
import com.sourcegraph.semanticdb_kotlinc.Semanticdb.SymbolOccurrence.Role
44
import java.lang.IllegalArgumentException
5+
import java.lang.StringBuilder
56
import java.nio.file.Path
67
import java.nio.file.Paths
78
import java.security.MessageDigest
89
import kotlin.contracts.ExperimentalContracts
910
import kotlin.text.Charsets.UTF_8
1011
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
12+
import org.jetbrains.kotlin.backend.common.serialization.metadata.findKDocString
1113
import org.jetbrains.kotlin.com.intellij.lang.java.JavaLanguage
1214
import org.jetbrains.kotlin.com.intellij.navigation.NavigationItem
1315
import org.jetbrains.kotlin.com.intellij.psi.PsiElement
1416
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
17+
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
1518
import org.jetbrains.kotlin.idea.KotlinLanguage
1619
import org.jetbrains.kotlin.psi.KtConstructor
1720
import org.jetbrains.kotlin.psi.KtFile
1821
import org.jetbrains.kotlin.psi.KtPropertyAccessor
22+
import org.jetbrains.kotlin.renderer.DescriptorRenderer
1923

2024
@ExperimentalContracts
2125
class SemanticdbTextDocumentBuilder(
@@ -55,6 +59,7 @@ class SemanticdbTextDocumentBuilder(
5559
return SymbolInformation {
5660
this.symbol = symbol.toString()
5761
this.displayName = displayName(element)
62+
this.documentation = semanticdbDocumentation(descriptor)
5863
this.language =
5964
when (element.language) {
6065
is KotlinLanguage -> Semanticdb.Language.KOTLIN
@@ -102,6 +107,61 @@ class SemanticdbTextDocumentBuilder(
102107
"%02X".format(it)
103108
}
104109

110+
private fun semanticdbDocumentation(
111+
descriptor: DeclarationDescriptor
112+
): Semanticdb.Documentation = Documentation {
113+
format = Semanticdb.Documentation.Format.MARKDOWN
114+
val signature =
115+
DescriptorRenderer.COMPACT_WITH_MODIFIERS
116+
.withOptions {
117+
withSourceFileForTopLevel = true
118+
unitReturnType = false
119+
}
120+
.render(descriptor)
121+
val kdoc =
122+
when (descriptor) {
123+
is DeclarationDescriptorWithSource -> descriptor.findKDocString() ?: ""
124+
else -> ""
125+
}
126+
message = "```kt\n$signature\n```${stripKDocAsterisks(kdoc)}"
127+
}
128+
129+
// Returns the kdoc string with all leading and trailing "/*" tokens removed. Naive
130+
// implementation that can
131+
// be replaced with a utility method from the compiler in the future, if one exists.
132+
private fun stripKDocAsterisks(kdoc: String): String {
133+
if (kdoc.isEmpty()) return kdoc
134+
val out = StringBuilder().append("\n\n").append("----").append("\n")
135+
kdoc.lineSequence().forEach { line ->
136+
var start = 0
137+
while (start < line.length && line[start].isWhitespace()) {
138+
start++
139+
}
140+
if (start < line.length && line[start] == '/') {
141+
start++
142+
}
143+
while (start < line.length && line[start] == '*') {
144+
start++
145+
}
146+
var end = line.length - 1
147+
if (end > start && line[end] == '/') {
148+
end--
149+
}
150+
while (end > start && line[end] == '*') {
151+
end--
152+
}
153+
while (end > start && line[end].isWhitespace()) {
154+
end--
155+
}
156+
start = minOf(start, line.length - 1)
157+
if (end > start) {
158+
end++
159+
}
160+
out.append("\n").append(line, start, end)
161+
}
162+
return out.toString()
163+
}
164+
105165
companion object {
106166
private fun displayName(element: PsiElement): String =
107167
when (element) {

semanticdb-kotlinc/src/test/kotlin/com/sourcegraph/semanticdb_kotlinc/test/AnalyzerTest.kt

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,25 @@ import com.tschuchort.compiletesting.KotlinCompilation
88
import com.tschuchort.compiletesting.PluginOption
99
import com.tschuchort.compiletesting.SourceFile
1010
import io.kotest.assertions.assertSoftly
11+
import io.kotest.assertions.fail
1112
import io.kotest.assertions.withClue
1213
import io.kotest.matchers.collections.shouldContain
1314
import io.kotest.matchers.shouldBe
15+
import io.kotest.matchers.shouldNotBe
1416
import java.io.File
1517
import java.nio.file.Path
1618
import kotlin.contracts.ExperimentalContracts
1719
import kotlin.test.Test
20+
import kotlin.test.assertEquals
21+
import org.intellij.lang.annotations.Language
1822
import org.junit.jupiter.api.io.TempDir
1923

2024
@ExperimentalContracts
2125
class AnalyzerTest {
22-
@Test
23-
fun `basic test`(@TempDir path: Path) {
24-
val buildPath = File(path.resolve("build").toString()).apply { mkdir() }
25-
26-
val source =
27-
SourceFile.testKt(
28-
"""
29-
package sample
30-
class Banana {
31-
fun foo() { }
32-
}""")
3326

27+
fun compileSemanticdb(path: Path, @Language("kotlin") code: String): TextDocument {
28+
val buildPath = File(path.resolve("build").toString()).apply { mkdir() }
29+
val source = SourceFile.testKt(code)
3430
lateinit var document: TextDocument
3531

3632
val result =
@@ -49,6 +45,21 @@ class AnalyzerTest {
4945
.compile()
5046

5147
result.exitCode shouldBe KotlinCompilation.ExitCode.OK
48+
document shouldNotBe null
49+
return document
50+
}
51+
52+
@Test
53+
fun `basic test`(@TempDir path: Path) {
54+
val document =
55+
compileSemanticdb(
56+
path,
57+
"""
58+
package sample
59+
class Banana {
60+
fun foo() { }
61+
}""")
62+
5263
val occurrences =
5364
arrayOf(
5465
SymbolOccurrence {
@@ -91,11 +102,21 @@ class AnalyzerTest {
91102
symbol = "sample/Banana#"
92103
language = KOTLIN
93104
displayName = "Banana"
105+
documentation =
106+
Documentation {
107+
format = Semanticdb.Documentation.Format.MARKDOWN
108+
message = "```kt\npublic final class Banana\n```"
109+
}
94110
},
95111
SymbolInformation {
96112
symbol = "sample/Banana#foo()."
97113
language = KOTLIN
98114
displayName = "foo"
115+
documentation =
116+
Documentation {
117+
format = Semanticdb.Documentation.Format.MARKDOWN
118+
message = "```kt\npublic final fun foo()\n```"
119+
}
99120
})
100121
assertSoftly(document.symbolsList) { withClue(this) { symbols.forEach(::shouldContain) } }
101122
}
@@ -536,4 +557,35 @@ class AnalyzerTest {
536557

537558
result.exitCode shouldBe KotlinCompilation.ExitCode.OK
538559
}
560+
561+
@Test
562+
fun documentation(@TempDir path: Path) {
563+
val document =
564+
compileSemanticdb(
565+
path,
566+
"""
567+
package sample
568+
import java.io.Serializable
569+
abstract class DocstringSuperclass
570+
571+
/** Example class docstring */
572+
class Docstrings: DocstringSuperclass(), Serializable
573+
574+
/**
575+
* Example method docstring
576+
*
577+
**/
578+
inline fun docstrings(msg: String): Int { return msg.length }
579+
""".trimIndent())
580+
document.assertDocumentation("sample/Docstrings#", "Example class docstring")
581+
document.assertDocumentation("sample/TestKt#docstrings().", "Example method docstring")
582+
}
583+
584+
private fun TextDocument.assertDocumentation(symbol: String, expectedDocumentation: String) {
585+
val markdown =
586+
this.symbolsList.find { it.symbol == symbol }?.documentation?.message
587+
?: fail("no documentation for symbol $symbol")
588+
val obtainedDocumentation = markdown.split("----").last().trim()
589+
assertEquals(expectedDocumentation, obtainedDocumentation)
590+
}
539591
}

0 commit comments

Comments
 (0)