Skip to content

Commit

Permalink
refactor: update comment builder design
Browse files Browse the repository at this point in the history
  • Loading branch information
phodal committed Jan 7, 2024
1 parent 1931b16 commit 61ef273
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,61 @@
package cc.unitmesh.core.comment

import cc.unitmesh.core.SupportedLang
import chapi.domain.core.CodePosition
import kotlinx.serialization.Serializable

@Serializable
data class CodeComment(
val content: String,
val position: CodePosition
)
) {
companion object {
/**
* Re-indent the comment to remove the leading spaces.
*
* @param content the comment content to be re-indented
* @return the re-indented comment content
*/
private fun reIndentComment(content: String): String {
val lines = content.split("\n")
val indent = lines[1].takeWhile { it == ' ' }
val linesWithoutIndent = lines.map { it.removePrefix(indent) }

// except the first line, every line should have one leading space
val linesWithLeadingSpace = linesWithoutIndent.mapIndexed { index, line ->
if (index == 0) {
line
} else {
" $line"
}
}

return linesWithLeadingSpace.joinToString("\n")
}

/**
* Extracts the Kotlin documentation comments (KDoc) from the given code.
*
* @param code the Kotlin code from which to extract the KDoc comments
* @return a list of pairs, where each pair contains the line number and the extracted KDoc comment
*/
fun extractKdocComments(code: String, language: SupportedLang): List<CodeComment> {
val pattern = Regex("""/\*\*[^*]*\*+([^/*][^*]*\*+)*/""")

val matches = pattern.findAll(code)

return matches.map { match ->
val commentContent = match.value.trimIndent()
val startLine = code.substring(0, match.range.first).count { it == '\n' } + 1
val stopLine = code.substring(0, match.range.last).count { it == '\n' } + 1
val startLinePosition = match.range.first - code.lastIndexOf('\n', match.range.first) - 1
val stopLinePosition = match.range.last - code.lastIndexOf('\n', match.range.last) - 1

val position = CodePosition(startLine, startLinePosition, stopLine, stopLinePosition)
val content = Companion.reIndentComment(commentContent)

CodeComment(content, position)
}.toList()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package cc.unitmesh.pick.builder.comment

import cc.unitmesh.core.SupportedLang
import cc.unitmesh.core.SupportedLang.*
import cc.unitmesh.core.comment.DocInstruction
import cc.unitmesh.core.completion.TypedIns
import cc.unitmesh.core.completion.TypedInsBuilder
import cc.unitmesh.pick.builder.comment.JvmCommentBuilder
import cc.unitmesh.pick.worker.job.JobContext
import chapi.domain.core.CodeContainer

class DocumentationTypedInsBuilder(val context: JobContext) : TypedInsBuilder {
private val kotlinCommentBuilder = JvmCommentBuilder("kotlin")
private val javaCommentBuilder = JvmCommentBuilder("java", DocInstruction.JAVA)
private val kotlinCommentBuilder = JvmCommentBuilder(SupportedLang.KOTLIN)
private val javaCommentBuilder = JvmCommentBuilder(SupportedLang.JAVA, DocInstruction.JAVA)

override fun build(container: CodeContainer): List<TypedIns> {
val language = context.project.language
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package cc.unitmesh.pick.builder.comment

import cc.unitmesh.core.SupportedLang
import cc.unitmesh.core.comment.CodeComment
import cc.unitmesh.core.comment.CommentBuilder
import cc.unitmesh.core.comment.DocInstruction
import cc.unitmesh.core.comment.TypedCommentIns
import cc.unitmesh.pick.builder.comment.ins.ClassCommentIns
import cc.unitmesh.pick.builder.comment.ins.MethodCommentIns
import chapi.domain.core.CodeContainer
import chapi.domain.core.CodePosition

private const val DOC_THRESHOLD = 5

class JvmCommentBuilder(val language: String, override val docInstruction: DocInstruction = DocInstruction.KOTLIN) :
class JvmCommentBuilder(val language: SupportedLang, override val docInstruction: DocInstruction = DocInstruction.KOTLIN) :
CommentBuilder {

/**
Expand All @@ -23,80 +23,32 @@ class JvmCommentBuilder(val language: String, override val docInstruction: DocIn
*/
override fun build(code: String, container: CodeContainer): List<TypedCommentIns> {
val posComments = try {
extractKdocComments(code)
CodeComment.extractKdocComments(code, language)
} catch (e: Exception) {
emptyList()
}

val startLineCommentMap: Map<Int, CodeComment> =
posComments.filter { it.content.isNotBlank() && it.content.length >= DOC_THRESHOLD }.associateBy {
it.position.StopLine
}
it.position.StopLine
}

val comments = mutableListOf<TypedCommentIns>()

container.DataStructures.forEach { dataStruct ->
val classComment = startLineCommentMap[dataStruct.Position.StartLine - 1]
classComment?.let { comments.add(ClassCommentIns(docInstruction, dataStruct, it, language = language)) }
classComment?.let { comments.add(ClassCommentIns(docInstruction, dataStruct, it, language = language.name)) }

val methodCommentIns =
dataStruct.Functions.filter { it.Name != "constructor" && it.Name != "PrimaryConstructor" }
.map { function ->
val functionComment = startLineCommentMap[function.Position.StartLine - 1] ?: return@map null
MethodCommentIns(docInstruction, function, functionComment, dataStruct, language = language)
MethodCommentIns(docInstruction, function, functionComment, dataStruct, language = language.name)
}

comments.addAll(methodCommentIns.filterNotNull())
}

return comments
}

/**
* Extracts the Kotlin documentation comments (KDoc) from the given code.
*
* @param code the Kotlin code from which to extract the KDoc comments
* @return a list of pairs, where each pair contains the line number and the extracted KDoc comment
*/
fun extractKdocComments(code: String): List<CodeComment> {
val pattern = Regex("""/\*\*[^*]*\*+([^/*][^*]*\*+)*/""")

val matches = pattern.findAll(code)

return matches.map { match ->
val commentContent = match.value.trimIndent()
val startLine = code.substring(0, match.range.first).count { it == '\n' } + 1
val stopLine = code.substring(0, match.range.last).count { it == '\n' } + 1
val startLinePosition = match.range.first - code.lastIndexOf('\n', match.range.first) - 1
val stopLinePosition = match.range.last - code.lastIndexOf('\n', match.range.last) - 1

val position = CodePosition(startLine, startLinePosition, stopLine, stopLinePosition)
val content = reIndentComment(commentContent)

CodeComment(content, position)
}.toList()
}

/**
* Re-indent the comment to remove the leading spaces.
*
* @param content the comment content to be re-indented
* @return the re-indented comment content
*/
private fun reIndentComment(content: String): String {
val lines = content.split("\n")
val indent = lines[1].takeWhile { it == ' ' }
val linesWithoutIndent = lines.map { it.removePrefix(indent) }

// except the first line, every line should have one leading space
val linesWithLeadingSpace = linesWithoutIndent.mapIndexed { index, line ->
if (index == 0) {
line
} else {
" $line"
}
}

return linesWithLeadingSpace.joinToString("\n")
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package cc.unitmesh.pick.builder.comment

import cc.unitmesh.core.SupportedLang
import cc.unitmesh.core.comment.CodeComment
import chapi.ast.kotlinast.KotlinAnalyser
import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test
Expand Down Expand Up @@ -35,7 +37,7 @@ class Group<T>(val name: String) {
@Test
fun `should extract KDoc comments when valid code provided`() {
// When
val result = JvmCommentBuilder("kotlin").extractKdocComments(kotlinCode)
val result = CodeComment.extractKdocComments(kotlinCode, SupportedLang.KOTLIN)

// Then
result.size shouldBe 3
Expand Down Expand Up @@ -64,7 +66,7 @@ class Group<T>(val name: String) {
val codeContainer = KotlinAnalyser().analysis(kotlinCode, "test.kt")

// When
val result = JvmCommentBuilder("kotlin").build(kotlinCode, codeContainer)
val result = JvmCommentBuilder(SupportedLang.KOTLIN).build(kotlinCode, codeContainer)

// Then
result.size shouldBe 3
Expand Down

0 comments on commit 61ef273

Please sign in to comment.