-
Notifications
You must be signed in to change notification settings - Fork 45
Introduce custom JavaDoc tags #565 #585
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
e88bd65
Introduce custom plugin's JavaDoc tags #565
onewhl 43c4cf0
Render UtBot custom JavaDoc tags correctly #565
onewhl 760594f
Add an option to generate summaries using custom JavaDoc tags #565
onewhl 050550f
Fill value of utbot.iterates tag #565
onewhl ca86976
Collect info about Invoke, Iterate, and Return sections #565
onewhl d77e739
Review fixes
onewhl 6ae1157
Add unit tests for summaries with custom JavaDoc tags #565
onewhl 100e3df
Fix after rebasing
onewhl 8d4063b
Add summary tests for MinStack #565
onewhl 6d01afe
Fix broken tests
onewhl d03ad76
Add <pre> tag only in case when custom javadoc tags are not used
onewhl 74c5822
Use a full exception name instead of simple name to build inline link…
onewhl 7e4aaf9
Minor refactoring
onewhl 92eb15d
Minor refactoring: avoid code duplication
onewhl cf9b60f
Add DocCustomTagStatement and CgCustomTagStatement
onewhl 9c14eb0
Refactored code to avoid code duplication
onewhl 6dce850
Fix tests: add full name for classes
onewhl 7800d33
Add JUnit extension to control USE_CUSTOM_TAGS setting
onewhl d003962
Move useCustomJavaDocTags to UtSettings, make useFuzzing true
onewhl f8e6764
Remove unused import and fix broken tests
onewhl 321bf0d
Fix broken tests
onewhl a52fb20
Add comments, remove unused method
onewhl 83f4e71
Review fixes: fixed formatting, removed redundant types
onewhl abe7ca6
Review fixes: fixed formatting, removed useless overriding methods
onewhl 475cae5
Review fixes: extracted method, polished code
onewhl 99b06ad
fix after rebasing
onewhl 21a7035
fix after rebasing
onewhl 4951b6f
review fixes
onewhl bd616a9
fix rendering after updating to idea 2022.1. now we don't need to gen…
onewhl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Render UtBot custom JavaDoc tags correctly #565
- Loading branch information
commit 43c4cf0913bc4b4d928e62847b66bb939b6bd204
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/javadoc/UtDocumentationProvider.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package org.utbot.intellij.plugin.javadoc | ||
|
|
||
| import com.intellij.codeInsight.javadoc.JavaDocExternalFilter | ||
| import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator | ||
| import com.intellij.lang.java.JavaDocumentationProvider | ||
| import com.intellij.psi.PsiDocCommentBase | ||
| import com.intellij.psi.PsiJavaDocumentedElement | ||
| import com.intellij.psi.javadoc.PsiDocComment | ||
|
|
||
| /** | ||
| * To render UtBot custom JavaDoc tags correctly, we need to override the way it generates HTML tags for comments. | ||
| * We get JavaDoc info generated by IJ platform and include sections related to UTBot, | ||
| * each section relates to the specific JavaDoc tag. | ||
| * It renders text, code, and links. | ||
| */ | ||
| class UtDocumentationProvider : JavaDocumentationProvider() { | ||
| override fun generateRenderedDoc(comment: PsiDocCommentBase): String { | ||
| var target = comment.owner | ||
| if (target == null) target = comment | ||
| val docComment: PsiDocComment? | ||
| var finalJavaDoc = "" | ||
| if (target is PsiJavaDocumentedElement) { | ||
| docComment = target.docComment | ||
| if (docComment != null) { | ||
| val baseJavaDocInfoGenerator = JavaDocInfoGenerator(target.project, target) | ||
| val baseJavaDocInfo = baseJavaDocInfoGenerator.generateRenderedDocInfo() | ||
| val utJavaDocInfoGenerator = UtJavaDocInfoGenerator() | ||
| val javaDocInfoWithUtSections = | ||
| utJavaDocInfoGenerator.addUtBotSpecificSectionsToJavaDoc(baseJavaDocInfo, docComment) | ||
| finalJavaDoc = JavaDocExternalFilter.filterInternalDocInfo(javaDocInfoWithUtSections)!! | ||
| } | ||
| } | ||
| return finalJavaDoc | ||
| } | ||
| } | ||
201 changes: 201 additions & 0 deletions
201
utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/javadoc/UtJavaDocInfoGenerator.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| package org.utbot.intellij.plugin.javadoc | ||
|
|
||
| import com.intellij.codeInsight.documentation.DocumentationManagerUtil | ||
| import com.intellij.codeInsight.javadoc.JavaDocUtil | ||
| import com.intellij.lang.documentation.DocumentationMarkup | ||
| import com.intellij.openapi.project.DumbService | ||
| import com.intellij.openapi.project.IndexNotReadyException | ||
| import com.intellij.openapi.util.text.StringUtil | ||
| import com.intellij.psi.* | ||
| import com.intellij.psi.javadoc.PsiDocComment | ||
| import com.intellij.psi.javadoc.PsiDocTag | ||
| import com.intellij.psi.javadoc.PsiDocToken | ||
| import com.intellij.psi.javadoc.PsiInlineDocTag | ||
| import mu.KotlinLogging | ||
|
|
||
| private const val LINK_TAG = "link" | ||
| private const val LINKPLAIN_TAG = "linkplain" | ||
| private const val LITERAL_TAG = "literal" | ||
| private const val CODE_TAG = "code" | ||
| private const val SYSTEM_PROPERTY_TAG = "systemProperty" | ||
| private const val MESSAGE_SEPARATOR = ":" | ||
|
|
||
| private val logger = KotlinLogging.logger {} | ||
|
|
||
| class UtJavaDocInfoGenerator { | ||
| /** | ||
| * Generates UtBot specific sections to include them to rendered JavaDoc comment. | ||
| */ | ||
| fun addUtBotSpecificSectionsToJavaDoc(javadoc: String?, comment: PsiDocComment): String { | ||
| val builder: StringBuilder = StringBuilder(javadoc) | ||
| generateUtTagSection(builder, comment, UtCustomJavaDocTagProvider.UtCustomTag.ClassUnderTest) | ||
| generateUtTagSection(builder, comment, UtCustomJavaDocTagProvider.UtCustomTag.MethodUnderTest) | ||
| generateUtTagSection(builder, comment, UtCustomJavaDocTagProvider.UtCustomTag.Invokes) | ||
| generateUtTagSection(builder, comment, UtCustomJavaDocTagProvider.UtCustomTag.Executes) | ||
| generateUtTagSection(builder, comment, UtCustomJavaDocTagProvider.UtCustomTag.ExpectedResult) | ||
| generateUtTagSection(builder, comment, UtCustomJavaDocTagProvider.UtCustomTag.ActualResult) | ||
| generateUtTagSection(builder, comment, UtCustomJavaDocTagProvider.UtCustomTag.ReturnsFrom) | ||
| generateUtTagSection(builder, comment, UtCustomJavaDocTagProvider.UtCustomTag.ThrowsException) | ||
| return builder.toString() | ||
| } | ||
|
|
||
| /** | ||
| * Searches for UtBot tag in the comment and generates a related section for it. | ||
| */ | ||
| private fun generateUtTagSection( | ||
| builder: StringBuilder, | ||
| comment: PsiDocComment?, | ||
|
onewhl marked this conversation as resolved.
Outdated
|
||
| utTag: UtCustomJavaDocTagProvider.UtCustomTag | ||
| ) { | ||
| if (comment != null) { | ||
| val tag = comment.findTagByName(utTag.name) ?: return | ||
| startHeaderSection(builder, utTag.getMessage())?.append("<p>") | ||
| val tmp = StringBuilder() | ||
| generateValue(tmp, tag.dataElements) | ||
| builder.append(tmp.toString().trim { it <= ' ' }) | ||
|
onewhl marked this conversation as resolved.
Outdated
|
||
| builder.append(DocumentationMarkup.SECTION_END) | ||
| } | ||
| } | ||
|
|
||
| private fun startHeaderSection(builder: StringBuilder, message: String): StringBuilder? { | ||
| return builder.append(DocumentationMarkup.SECTION_HEADER_START) | ||
| .append(message) | ||
| .append(MESSAGE_SEPARATOR) | ||
| .append(DocumentationMarkup.SECTION_SEPARATOR) | ||
| } | ||
|
|
||
| /** | ||
| * Generates info depending on tag's value type. | ||
| */ | ||
| private fun generateValue(builder: StringBuilder, elements: Array<PsiElement>) { | ||
| var offset = if (elements.isNotEmpty()) { | ||
| elements[0].textOffset + elements[0].text.length | ||
| } else 0 | ||
|
onewhl marked this conversation as resolved.
Outdated
|
||
|
|
||
| for (i in elements.indices) { | ||
| if (elements[i].textOffset > offset) builder.append(' ') | ||
| offset = elements[i].textOffset + elements[i].text.length | ||
| val element = elements[i] | ||
| if (element is PsiInlineDocTag) { | ||
| when (element.name) { | ||
| LITERAL_TAG -> generateLiteralValue(builder, element) | ||
| CODE_TAG, SYSTEM_PROPERTY_TAG -> generateCodeValue(element, builder) | ||
| LINK_TAG -> generateLinkValue(element, builder, false) | ||
| LINKPLAIN_TAG -> generateLinkValue(element, builder, true) | ||
| } | ||
| } else { | ||
| appendPlainText(builder, element.text) | ||
| } | ||
| } | ||
|
onewhl marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| private fun appendPlainText(builder: StringBuilder, text: String) { | ||
| builder.append(StringUtil.replaceUnicodeEscapeSequences(text)) | ||
| } | ||
|
|
||
| private fun collectElementText(builder: StringBuilder, element: PsiElement) { | ||
| element.accept(object : PsiRecursiveElementWalkingVisitor() { | ||
| override fun visitElement(element: PsiElement) { | ||
| super.visitElement(element) | ||
| if (element is PsiWhiteSpace || | ||
| element is PsiJavaToken || | ||
| element is PsiDocToken && element.tokenType !== JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS | ||
|
onewhl marked this conversation as resolved.
Outdated
|
||
| ) { | ||
| builder.append(element.text) | ||
| } | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| private fun generateCodeValue(tag: PsiInlineDocTag, builder: StringBuilder) { | ||
| builder.append("<code>") | ||
| val pos = builder.length | ||
| generateLiteralValue(builder, tag) | ||
| builder.append("</code>") | ||
| if (builder[pos] == '\n') builder.insert( | ||
| pos, | ||
| ' ' | ||
| ) // line break immediately after opening tag is ignored by JEditorPane | ||
|
onewhl marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| private fun generateLiteralValue(builder: StringBuilder, tag: PsiDocTag) { | ||
| val tmpBuilder = StringBuilder() | ||
| val children = tag.children | ||
| for (i in 2 until children.size - 1) { // process all children except tag opening/closing elements | ||
| val child = children[i] | ||
| if (child is PsiDocToken && child.tokenType === JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) continue | ||
| var elementText = child.text | ||
| if (child is PsiWhiteSpace) { | ||
| val pos = elementText.lastIndexOf('\n') | ||
| if (pos >= 0) elementText = elementText.substring(0, pos + 1) // skip whitespace before leading asterisk | ||
| } | ||
| appendPlainText(tmpBuilder, StringUtil.escapeXmlEntities(elementText)) | ||
| } | ||
| builder.append(StringUtil.trimLeading(tmpBuilder)) | ||
| } | ||
|
|
||
| private fun generateLinkValue(tag: PsiInlineDocTag, builder: StringBuilder, plainLink: Boolean) { | ||
| val tagElements = tag.dataElements | ||
| val linkText: String = createLinkText(tagElements) | ||
|
onewhl marked this conversation as resolved.
Outdated
|
||
| if (linkText.isNotEmpty()) { | ||
|
onewhl marked this conversation as resolved.
Outdated
|
||
| val index = JavaDocUtil.extractReference(linkText) | ||
| val referenceText = linkText.substring(0, index).trim { it <= ' ' } | ||
| val label = StringUtil.nullize(linkText.substring(index).trim { it <= ' ' }) | ||
|
onewhl marked this conversation as resolved.
Outdated
|
||
| generateLink(builder, referenceText, label, tagElements[0], plainLink) | ||
| } | ||
| } | ||
|
|
||
| private fun createLinkText(tagElements: Array<PsiElement>): String { | ||
| var offset = if (tagElements.isNotEmpty()) { | ||
|
onewhl marked this conversation as resolved.
Outdated
|
||
| tagElements[0].textOffset + tagElements[0].text.length | ||
| } else { | ||
| 0 | ||
| } | ||
|
|
||
| val builder = StringBuilder() | ||
| for (i in tagElements.indices) { | ||
| val tagElement = tagElements[i] | ||
| if (tagElement.textOffset > offset) builder.append(' ') | ||
| offset = tagElement.textOffset + tagElement.text.length | ||
| collectElementText(builder, tagElement) | ||
| if (i < tagElements.size - 1) { | ||
| builder.append(' ') | ||
| } | ||
| } | ||
| return builder.toString().trim { it <= ' ' } | ||
| } | ||
|
|
||
| private fun generateLink( | ||
| builder: StringBuilder, | ||
| refText: String?, | ||
| label: String?, | ||
| context: PsiElement, | ||
| plainLink: Boolean | ||
| ) { | ||
| var linkLabel = label | ||
| if (label == null) { | ||
| val manager = context.manager | ||
| linkLabel = JavaDocUtil.getLabelText(manager.project, manager, refText, context) | ||
| } | ||
|
onewhl marked this conversation as resolved.
Outdated
|
||
|
|
||
| var target: PsiElement? = null | ||
| try { | ||
| if (refText != null) { | ||
| target = JavaDocUtil.findReferenceTarget(context.manager, refText, context) | ||
| } | ||
| } catch (e: IndexNotReadyException) { | ||
| logger.info(e) { "Failed to find a reference while generating JavaDoc comment. Details: ${e.message}" } | ||
|
onewhl marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| if (target == null && DumbService.isDumb(context.project)) { | ||
| builder.append(linkLabel) | ||
| } else if (target == null) { | ||
| builder.append("<font color=red>").append(linkLabel).append("</font>") | ||
| } else { | ||
| val referenceText = JavaDocUtil.getReferenceText(target.project, target) | ||
| if (referenceText != null) { | ||
| DocumentationManagerUtil.createHyperlink(builder, target, referenceText, linkLabel, plainLink) | ||
| } | ||
|
onewhl marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.