Skip to content

Commit f8941b9

Browse files
authored
Older versions of IDE support (#798)
Support older versions of IDE
1 parent 32d7a7e commit f8941b9

File tree

9 files changed

+251
-20
lines changed

9 files changed

+251
-20
lines changed

.github/workflows/build-and-run-tests-from-branch.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ jobs:
5757
fail-fast: false
5858
matrix: ${{ fromJson(needs.prepare-tests-matrix.outputs.matrix) }}
5959
runs-on: ubuntu-20.04
60-
container: unittestbot/java-env:java11-zulu-jdk-fx-gradle7.4.2-kotlinc1.7.0
60+
container: unittestbot/java-env:java11-zulu-jdk-gradle7.4.2-kotlinc1.7.0
6161
steps:
6262
- name: Print environment variables
6363
run: printenv
@@ -111,7 +111,7 @@ jobs:
111111
matrix:
112112
project: [utbot-api, utbot-cli, utbot-core, utbot-framework-api, utbot-fuzzers, utbot-gradle, utbot-instrumentation, utbot-instrumentation-tests, utbot-intellij, utbot-junit-contest, utbot-sample, utbot-summary, utbot-summary-tests]
113113
runs-on: ubuntu-20.04
114-
container: unittestbot/java-env:java11-zulu-jdk-fx-gradle7.4.2-kotlinc1.7.0
114+
container: unittestbot/java-env:java11-zulu-jdk-gradle7.4.2-kotlinc1.7.0
115115
steps:
116116
- name: Print environment variables
117117
run: printenv

.github/workflows/build-and-run-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
needs: build-and-run-tests
2424
if: ${{ github.event_name == 'push' }}
2525
runs-on: ubuntu-20.04
26-
container: unittestbot/java-env:java11-zulu-jdk-fx-gradle7.4.2-kotlinc1.7.0
26+
container: unittestbot/java-env:java11-zulu-jdk-gradle7.4.2-kotlinc1.7.0
2727
steps:
2828
- name: Print environment variables
2929
run: printenv

.github/workflows/publish-plugin-and-cli-from-branch.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ env:
2828
jobs:
2929
publish_plugin_and_cli:
3030
runs-on: ubuntu-20.04
31-
container: unittestbot/java-env:java11-zulu-jdk-fx-gradle7.4.2-kotlinc1.7.0
31+
container: unittestbot/java-env:java11-zulu-jdk-gradle7.4.2-kotlinc1.7.0
3232

3333
steps:
3434
- name: Print environment variables

.github/workflows/run-chosen-tests-from-branch.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ env:
3434
jobs:
3535
run-chosen-tests:
3636
runs-on: ubuntu-20.04
37-
container: unittestbot/java-env:java11-zulu-jdk-fx-gradle7.4.2-kotlinc1.7.0
37+
container: unittestbot/java-env:java11-zulu-jdk-gradle7.4.2-kotlinc1.7.0
3838

3939
steps:
4040
- name: Print environment variables

gradle.properties

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,5 @@ shadow_jar_version=7.1.2
4848

4949
org.gradle.daemon=false
5050
org.gradle.parallel=false
51-
org.gradle.jvmargs="-XX:MaxHeapSize=3072m"
52-
kotlin.compiler.execution.strategy=in-process
53-
54-
org.gradle.jvmargs=-Xmx6144m
51+
org.gradle.jvmargs="-XX:MaxHeapSize=6144m"
52+
kotlin.compiler.execution.strategy=in-process

utbot-intellij/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ dependencies {
4444

4545
// See https://github.com/JetBrains/gradle-intellij-plugin/
4646
intellij {
47-
version = "2022.1"
47+
version = "2021.2"
4848
type = "IC"
4949
plugins = ['java',
5050
// TODO: SAT-1539 - specify version of android plugin to be supported by our kotlin version.

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/javadoc/UtDocumentationProvider.kt

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,43 @@
11
package org.utbot.intellij.plugin.javadoc
22

33
import com.intellij.codeInsight.javadoc.JavaDocExternalFilter
4-
import com.intellij.codeInsight.javadoc.JavaDocInfoGeneratorFactory
4+
import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator
55
import com.intellij.lang.java.JavaDocumentationProvider
66
import com.intellij.psi.PsiDocCommentBase
77
import com.intellij.psi.PsiJavaDocumentedElement
88

99
/**
1010
* To render UtBot custom JavaDoc tags messages, we need to override basic behaviour of [JavaDocumentationProvider].
11-
* The IJ platform knows only custom tag names, so we need to add their messages in rendered comments to make it look nice.
1211
*/
1312
class UtDocumentationProvider : JavaDocumentationProvider() {
13+
1414
override fun generateRenderedDoc(comment: PsiDocCommentBase): String? {
1515
val target = comment.owner ?: comment
1616

1717
if (target !is PsiJavaDocumentedElement) {
1818
return ""
1919
}
2020

21-
val baseJavaDocInfoGenerator = JavaDocInfoGeneratorFactory.getBuilder(target.getProject())
22-
.setPsiElement(target)
23-
.setIsGenerationForRenderedDoc(true)
24-
.create()
21+
val docComment = target.docComment ?: return ""
22+
23+
val baseJavaDocInfoGenerator = JavaDocInfoGenerator(target.project, target)
24+
25+
// get JavaDoc comment rendered by the platform.
26+
val baseJavaDocInfo = baseJavaDocInfoGenerator.generateRenderedDocInfo()
2527

26-
val finalDocContent = replaceTagNamesWithMessages(baseJavaDocInfoGenerator.generateRenderedDocInfo())
28+
// add UTBot sections with custom tags.
29+
val utJavaDocInfoGenerator = UtJavaDocInfoGenerator()
30+
val javaDocInfoWithUtSections =
31+
utJavaDocInfoGenerator.addUtBotSpecificSectionsToJavaDoc(baseJavaDocInfo, docComment)
2732

28-
return JavaDocExternalFilter.filterInternalDocInfo(finalDocContent)
33+
return JavaDocExternalFilter.filterInternalDocInfo(javaDocInfoWithUtSections)
2934
}
3035

3136
/**
3237
* Replaces names of plugin's custom JavaDoc tags with their messages in the comment generated by the IJ platform.
33-
* Example: utbot.MethodUnderTest -> Method under test.
38+
* Example: utbot.methodUnderTest -> Method under test.
39+
*
40+
* Use it to update comment built by the IJ platform after updating to 2022.2.
3441
*/
3542
private fun replaceTagNamesWithMessages(comment: String?) =
3643
comment?.let {
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
package org.utbot.intellij.plugin.javadoc
2+
3+
import com.intellij.codeInsight.documentation.DocumentationManagerUtil
4+
import com.intellij.codeInsight.javadoc.JavaDocUtil
5+
import com.intellij.lang.documentation.DocumentationMarkup
6+
import com.intellij.openapi.project.DumbService
7+
import com.intellij.openapi.project.IndexNotReadyException
8+
import com.intellij.openapi.util.text.StringUtil
9+
import com.intellij.psi.*
10+
import com.intellij.psi.javadoc.PsiDocComment
11+
import com.intellij.psi.javadoc.PsiDocTag
12+
import com.intellij.psi.javadoc.PsiDocToken
13+
import com.intellij.psi.javadoc.PsiInlineDocTag
14+
import mu.KotlinLogging
15+
16+
private const val LINK_TAG = "link"
17+
private const val LINKPLAIN_TAG = "linkplain"
18+
private const val LITERAL_TAG = "literal"
19+
private const val CODE_TAG = "code"
20+
private const val SYSTEM_PROPERTY_TAG = "systemProperty"
21+
private const val MESSAGE_SEPARATOR = ":"
22+
private const val PARAGRAPH_TAG = "<p>"
23+
private const val CODE_TAG_START = "<code>"
24+
private const val CODE_TAG_END = "</code>"
25+
26+
private val logger = KotlinLogging.logger {}
27+
28+
/**
29+
* Generates UtBot specific sections to include them to rendered JavaDoc comment.
30+
*
31+
* Methods responsible for value generation were taken from IJ platform class (they are private and couldn't be used outside).
32+
*
33+
* See [com.intellij.codeInsight.javadoc.JavaDocInfoGenerator].
34+
*
35+
* It wouldn't be needed to generate rendered doc on our own after updating to the IJ platform 2022.2,
36+
* so delete it after updating and use basic [com.intellij.codeInsight.javadoc.JavaDocInfoGenerator].
37+
*/
38+
class UtJavaDocInfoGenerator {
39+
fun addUtBotSpecificSectionsToJavaDoc(javadoc: String?, comment: PsiDocComment): String {
40+
val builder = if (javadoc == null) {
41+
StringBuilder()
42+
} else {
43+
StringBuilder(javadoc)
44+
}
45+
46+
val docTagProvider = UtCustomJavaDocTagProvider()
47+
docTagProvider.supportedTags.forEach {
48+
generateUtTagSection(builder, comment, it)
49+
}
50+
return builder.toString()
51+
}
52+
53+
/**
54+
* Searches for UtBot tag in the comment and generates a related section for it.
55+
*/
56+
private fun generateUtTagSection(
57+
builder: StringBuilder,
58+
comment: PsiDocComment,
59+
utTag: UtCustomJavaDocTagProvider.UtCustomTagInfo
60+
) {
61+
val tag = comment.findTagByName(utTag.name) ?: return
62+
startHeaderSection(builder, utTag.getMessage()).append(PARAGRAPH_TAG)
63+
val sectionContent = buildString {
64+
generateValue(this, tag.dataElements)
65+
trim()
66+
}
67+
68+
builder.append(sectionContent)
69+
builder.append(DocumentationMarkup.SECTION_END)
70+
}
71+
72+
private fun startHeaderSection(builder: StringBuilder, message: String): StringBuilder =
73+
builder.append(DocumentationMarkup.SECTION_HEADER_START)
74+
.append(message)
75+
.append(MESSAGE_SEPARATOR)
76+
.append(DocumentationMarkup.SECTION_SEPARATOR)
77+
78+
/**
79+
* Generates info depending on tag's value type.
80+
*/
81+
private fun generateValue(builder: StringBuilder, elements: Array<PsiElement>) {
82+
if (elements.isEmpty()) {
83+
return
84+
}
85+
86+
var offset = elements[0].textOffset + elements[0].text.length
87+
88+
for (element in elements) {
89+
with(element) {
90+
if (textOffset > offset) {
91+
builder.append(' ')
92+
}
93+
94+
offset = textOffset + text.length
95+
96+
if (element is PsiInlineDocTag) {
97+
when (element.name) {
98+
LITERAL_TAG -> generateLiteralValue(builder, element)
99+
CODE_TAG, SYSTEM_PROPERTY_TAG -> generateCodeValue(element, builder)
100+
LINK_TAG -> generateLinkValue(element, builder, false)
101+
LINKPLAIN_TAG -> generateLinkValue(element, builder, true)
102+
}
103+
} else {
104+
appendPlainText(builder, text)
105+
}
106+
}
107+
}
108+
}
109+
110+
private fun appendPlainText(builder: StringBuilder, text: String) {
111+
builder.append(StringUtil.replaceUnicodeEscapeSequences(text))
112+
}
113+
114+
private fun collectElementText(builder: StringBuilder, element: PsiElement) {
115+
element.accept(object : PsiRecursiveElementWalkingVisitor() {
116+
override fun visitElement(element: PsiElement) {
117+
super.visitElement(element)
118+
if (element is PsiWhiteSpace ||
119+
element is PsiJavaToken ||
120+
element is PsiDocToken && element.tokenType !== JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS
121+
) {
122+
builder.append(element.text)
123+
}
124+
}
125+
})
126+
}
127+
128+
private fun generateCodeValue(tag: PsiInlineDocTag, builder: StringBuilder) {
129+
builder.append(CODE_TAG_START)
130+
val pos = builder.length
131+
generateLiteralValue(builder, tag)
132+
builder.append(CODE_TAG_END)
133+
if (builder[pos] == '\n') {
134+
builder.insert(
135+
pos,
136+
' '
137+
) // line break immediately after opening tag is ignored by JEditorPane
138+
}
139+
}
140+
141+
private fun generateLiteralValue(builder: StringBuilder, tag: PsiDocTag) {
142+
val literalValue = buildString {
143+
val children = tag.children
144+
for (i in 2 until children.size - 1) { // process all children except tag opening/closing elements
145+
val child = children[i]
146+
if (child is PsiDocToken && child.tokenType === JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) {
147+
continue
148+
}
149+
150+
var elementText = child.text
151+
if (child is PsiWhiteSpace) {
152+
val pos = elementText.lastIndexOf('\n')
153+
if (pos >= 0) {
154+
elementText = elementText.substring(0, pos + 1) // skip whitespace before leading asterisk
155+
}
156+
}
157+
appendPlainText(this, StringUtil.escapeXmlEntities(elementText))
158+
}
159+
}
160+
builder.append(StringUtil.trimLeading(literalValue))
161+
}
162+
163+
private fun generateLinkValue(tag: PsiInlineDocTag, builder: StringBuilder, plainLink: Boolean) {
164+
val tagElements = tag.dataElements
165+
val linkText = createLinkText(tagElements)
166+
if (linkText.isNotEmpty()) {
167+
val index = JavaDocUtil.extractReference(linkText)
168+
val referenceText = linkText.substring(0, index).trim()
169+
val label = StringUtil.nullize(linkText.substring(index).trim())
170+
generateLink(builder, referenceText, label, tagElements[0], plainLink)
171+
}
172+
}
173+
174+
private fun createLinkText(tagElements: Array<PsiElement>): String {
175+
var offset = if (tagElements.isNotEmpty()) {
176+
tagElements[0].textOffset + tagElements[0].text.length
177+
} else {
178+
0
179+
}
180+
181+
return buildString {
182+
for (i in tagElements.indices) {
183+
val tagElement = tagElements[i]
184+
if (tagElement.textOffset > offset) {
185+
this.append(' ')
186+
}
187+
offset = tagElement.textOffset + tagElement.text.length
188+
collectElementText(this, tagElement)
189+
if (i < tagElements.lastIndex) {
190+
this.append(' ')
191+
}
192+
}
193+
}.trim()
194+
}
195+
196+
private fun generateLink(
197+
builder: StringBuilder,
198+
refText: String?,
199+
label: String?,
200+
context: PsiElement,
201+
plainLink: Boolean
202+
) {
203+
val linkLabel = label ?: context.manager.let {
204+
JavaDocUtil.getLabelText(it.project, it, refText, context)
205+
}
206+
207+
var target: PsiElement? = null
208+
try {
209+
if (refText != null) {
210+
target = JavaDocUtil.findReferenceTarget(context.manager, refText, context)
211+
}
212+
} catch (e: IndexNotReadyException) {
213+
logger.info(e) { "Failed to find a reference while generating JavaDoc comment. Details: ${e.message}" }
214+
}
215+
216+
if (target == null && DumbService.isDumb(context.project)) {
217+
builder.append(linkLabel)
218+
} else if (target == null) {
219+
builder.append("<font color=red>").append(linkLabel).append("</font>")
220+
} else {
221+
JavaDocUtil.getReferenceText(target.project, target)?.let {
222+
DocumentationManagerUtil.createHyperlink(builder, target, it, linkLabel, plainLink)
223+
}
224+
}
225+
}
226+
}

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/RunConfigurationHelper.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import com.intellij.psi.PsiClass
2020
import com.intellij.psi.PsiDocumentManager
2121
import com.intellij.psi.PsiElement
2222
import com.intellij.psi.PsiFile
23-
import com.intellij.psi.util.childrenOfType
23+
import org.jetbrains.plugins.groovy.lang.psi.util.childrenOfType
2424
import com.intellij.testFramework.MapDataContext
2525
import mu.KotlinLogging
2626
import org.utbot.intellij.plugin.models.GenerateTestsModel

0 commit comments

Comments
 (0)