Skip to content

Commit

Permalink
feat(script): add support for 'mavenId()' in 'class'
Browse files Browse the repository at this point in the history
  • Loading branch information
tangcent committed Aug 12, 2024
1 parent 5fddde4 commit 6f431a4
Show file tree
Hide file tree
Showing 7 changed files with 319 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/co.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
./gradlew check --stacktrace
./gradlew codeCoverageReport
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: build/reports/jacoco/report.xml
Expand Down
2 changes: 1 addition & 1 deletion idea-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ intellij {
type.set("IC")
pluginName.set("easy-yapi")
sandboxDir.set("idea-sandbox")
plugins.set(listOf("java"))
plugins.set(listOf("java", "maven", "gradle"))
}

tasks {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import com.itangcent.common.utils.mapToTypedArray
import com.itangcent.http.RequestUtils
import com.itangcent.idea.plugin.api.MethodInferHelper
import com.itangcent.idea.plugin.format.Json5Formatter
import com.itangcent.idea.utils.MavenHelper
import com.itangcent.idea.utils.MavenIdData
import com.itangcent.intellij.config.rule.*
import com.itangcent.intellij.context.ActionContext
import com.itangcent.intellij.extend.notReentrant
Expand Down Expand Up @@ -470,6 +472,10 @@ abstract class ScriptRuleParser : AbstractRuleParser() {
}
}

override fun mavenId(): MavenIdData? {
return actionContext.callInReadUI { MavenHelper.getMavenId(psiClass) }
}

override fun toString(): String {
return name()
}
Expand Down Expand Up @@ -1069,6 +1075,7 @@ abstract class ScriptRuleParser : AbstractRuleParser() {
*/
abstract fun implements(): Array<ScriptClassContext>?

abstract fun mavenId(): MavenIdData?
}

/**
Expand Down Expand Up @@ -1231,6 +1238,11 @@ abstract class ScriptRuleParser : AbstractRuleParser() {
}
}

override fun mavenId(): MavenIdData? {
val psiClass = getResource() as? PsiClass ?: return null
return actionContext.callInReadUI { MavenHelper.getMavenId(psiClass) }
}

override fun toString(): String {
return name()
}
Expand Down Expand Up @@ -1439,6 +1451,11 @@ abstract class ScriptRuleParser : AbstractRuleParser() {
}
}

override fun mavenId(): MavenIdData? {
val psiClass = getResource() as? PsiClass ?: return null
return actionContext.callInReadUI { MavenHelper.getMavenId(psiClass) }
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ScriptClassContext) return false
Expand Down
163 changes: 163 additions & 0 deletions idea-plugin/src/main/kotlin/com/itangcent/idea/utils/MavenHelper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package com.itangcent.idea.utils

import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.psi.PsiClass
import com.itangcent.annotation.script.ScriptTypeName
import com.itangcent.common.utils.safe
import org.jetbrains.idea.maven.project.MavenProjectsManager
import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataCache


/**
* Utility object for obtaining Maven identifiers of a given PsiClass through Maven or Gradle.
*
* @author tangcent
* @date 2024/08/11
*/
object MavenHelper {

/**
* Attempts to retrieve the Maven ID of a PsiClass using Maven or Gradle.
*
* @param psiClass the PsiClass for which the Maven ID is to be retrieved.
* @return MavenIdData if found, null otherwise.
*/
fun getMavenId(psiClass: PsiClass): MavenIdData? {
return safe {
getMavenIdByMaven(psiClass)
} ?: safe {
getMavenIdByGradle(psiClass)
}
}

/**
* Retrieves the Maven ID of a PsiClass using Maven.
*
* @param psiClass the PsiClass for which the Maven ID is to be retrieved.
* @return MavenIdData if found, null otherwise.
*/
private fun getMavenIdByMaven(psiClass: PsiClass): MavenIdData? {
val project = psiClass.project
val module = ModuleUtilCore.findModuleForPsiElement(psiClass)
?: return null

val mavenProjectsManager = MavenProjectsManager.getInstance(project)
val mavenProject = mavenProjectsManager.findProject(module) ?: return null
val mavenId = mavenProject.mavenId
return MavenIdData(
groupId = mavenId.groupId!!,
artifactId = mavenId.artifactId!!,
version = mavenId.version!!
)
}

/**
* Retrieves the Maven ID of a PsiClass using Gradle.
*
* @param psiClass the PsiClass for which the Maven ID is to be retrieved.
* @return MavenIdData if found, null otherwise.
*/
private fun getMavenIdByGradle(psiClass: PsiClass): MavenIdData? {
val project = psiClass.project
val projectPath = project.basePath ?: return null
val externalProject = ExternalProjectDataCache.getInstance(project).getRootExternalProject(projectPath)
?: return null

return MavenIdData(
groupId = externalProject.group,
artifactId = externalProject.name,
version = externalProject.version
)
}
}

/**
* Data class representing Maven ID information.
*/
@ScriptTypeName("MavenId")
class MavenIdData(
val groupId: String,
val artifactId: String,
val version: String
) {

/**
* Generates a Maven dependency snippet
*/
fun maven(): String {
return """
<dependency>
<groupId>$groupId</groupId>
<artifactId>$artifactId</artifactId>
<version>$version</version>
</dependency>
""".trimIndent()
}

/**
* Generates a Gradle implementation dependency snippet
*/
fun gradle(): String {
return """
implementation group: '$groupId', name: '$artifactId', version: '$version'
""".trimIndent()
}

/**
* Generates a Gradle implementation dependency snippet in short form.
*/
fun gradleShort(): String {
return """
implementation '$groupId:$artifactId:$version'
""".trimIndent()
}

/**
* Generates a Gradle implementation dependency snippet in Kotlin DSL.
*/
fun gradleKotlin(): String {
return """
implementation("$groupId:$artifactId:$version")
""".trimIndent()
}

/**
* Generates an SBT dependency snippet.
*/
fun sbt(): String {
return """
libraryDependencies += "$groupId" % "$artifactId" % "$version"
""".trimIndent()
}

/**
* Generates an Ivy dependency snippet.
*/
fun ivy(): String {
return """
<dependency org="$groupId" name="$artifactId" rev="$version" />
""".trimIndent()
}

override fun toString(): String {
return "$groupId:$artifactId:$version"
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is MavenIdData) return false

if (groupId != other.groupId) return false
if (artifactId != other.artifactId) return false
if (version != other.version) return false

return true
}

override fun hashCode(): Int {
var result = groupId.hashCode()
result = 31 * result + artifactId.hashCode()
result = 31 * result + version.hashCode()
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,10 @@ abstract class ScriptClassContextBaseTest : PluginContextLightCodeInsightFixture
assertTrue(modelPsiClass.asClassContext().implements()!!.isEmpty())
}

fun testMavenId() {
assertNull(objectPsiClass.asClassContext().mavenId())
}

//endregion

//region tests of ScriptFieldContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ object ETHUtils {

fun <T> testCETH(original: T, copy: T.() -> T) {
assertNotNull(original, "original should not be null")
original!!
// Create a copy using the data class generated `copy` method
val copied = original.copy()
assertEquals(original, copied)
assertEquals(original.hashCode(), copied.hashCode())
assertEquals(original.toString(), copied.toString())

// Use reflection to fetch all properties
Settings::class.memberProperties
original!!::class.memberProperties
.filterIsInstance<KMutableProperty<*>>()
.forEach { property ->
val newCopied = original.copy()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package com.itangcent.idea.utils

import com.intellij.openapi.module.Module
import com.intellij.psi.PsiClass
import com.itangcent.idea.plugin.settings.ETHUtils
import com.itangcent.testFramework.PluginContextLightCodeInsightFixtureTestCase
import org.jetbrains.idea.maven.model.MavenId
import org.jetbrains.idea.maven.project.MavenProject
import org.jetbrains.idea.maven.project.MavenProjectsManager
import org.jetbrains.plugins.gradle.model.ExternalProject
import org.jetbrains.plugins.gradle.service.project.data.ExternalProjectDataCache
import org.mockito.Mockito.mockStatic
import org.mockito.kotlin.any
import org.mockito.kotlin.mock

/**
* Test class for [MavenHelper].
*
* @author tangcent
* @date 2024/08/11
*/
class MavenHelperTest : PluginContextLightCodeInsightFixtureTestCase() {

private lateinit var userCtrlPsiClass: PsiClass

override fun beforeBind() {
super.beforeBind()
userCtrlPsiClass = loadClass("api/UserCtrl.java")!!
}

fun testGetMavenIdByMaven() {
mockStatic(MavenProjectsManager::class.java).use { mavenProjectsManagerMock ->
val mavenProject = mock<MavenProject> {
on(it.mavenId)
.thenReturn(MavenId("com.itangcent", "intellij-idea-test", "1.0.0"))
}
val mavenProjectsManager = mock<MavenProjectsManager> {
on(it.findProject(any<Module>()))
.thenReturn(mavenProject)
}

mavenProjectsManagerMock.`when`<MavenProjectsManager> {
MavenProjectsManager.getInstance(project)
}.thenReturn(mavenProjectsManager)

val mavenId = MavenHelper.getMavenId(userCtrlPsiClass)

assertNotNull(mavenId)
assertEquals("com.itangcent", mavenId!!.groupId)
assertEquals("intellij-idea-test", mavenId.artifactId)
assertEquals("1.0.0", mavenId.version)
}
}

fun testGetMavenIdByGradle() {
mockStatic(ExternalProjectDataCache::class.java).use { externalProjectDataCacheMock ->
val externalProject = mock<ExternalProject> {
on(it.name)
.thenReturn("intellij-idea-test")
on(it.group)
.thenReturn("com.itangcent")
on(it.version)
.thenReturn("1.0.0")
}
val externalProjectDataCache = mock<ExternalProjectDataCache> {
on(it.getRootExternalProject(any()))
.thenReturn(externalProject)
}

externalProjectDataCacheMock.`when`<ExternalProjectDataCache> {
ExternalProjectDataCache.getInstance(project)
}.thenReturn(externalProjectDataCache)

val mavenIdData = MavenHelper.getMavenId(userCtrlPsiClass)

assertNotNull(mavenIdData)
assertEquals("com.itangcent", mavenIdData!!.groupId)
assertEquals("intellij-idea-test", mavenIdData.artifactId)
assertEquals("1.0.0", mavenIdData.version)
}
}

fun testMavenIdData() {
val mavenIdData = MavenIdData("com.itangcent", "intellij-idea-test", "1.0.0")
assertEquals("com.itangcent", mavenIdData.groupId)
assertEquals("intellij-idea-test", mavenIdData.artifactId)
assertEquals("1.0.0", mavenIdData.version)

assertEquals(
"""
<dependency>
<groupId>com.itangcent</groupId>
<artifactId>intellij-idea-test</artifactId>
<version>1.0.0</version>
</dependency>
""".trimIndent(), mavenIdData.maven()
)

assertEquals(
"""
implementation group: 'com.itangcent', name: 'intellij-idea-test', version: '1.0.0'
""".trimIndent(), mavenIdData.gradle()
)

assertEquals(
"""
implementation 'com.itangcent:intellij-idea-test:1.0.0'
""".trimIndent(), mavenIdData.gradleShort()
)

assertEquals(
"""
implementation("com.itangcent:intellij-idea-test:1.0.0")
""".trimIndent(), mavenIdData.gradleKotlin()
)

assertEquals(
"""
<dependency org="com.itangcent" name="intellij-idea-test" rev="1.0.0" />
""".trimIndent(), mavenIdData.ivy()
)

assertEquals(
"""
libraryDependencies += "com.itangcent" % "intellij-idea-test" % "1.0.0"
""".trimIndent(), mavenIdData.sbt()
)

assertEquals("com.itangcent:intellij-idea-test:1.0.0", mavenIdData.toString())
ETHUtils.testCETH(mavenIdData) { MavenIdData("com.itangcent", "intellij-idea-test", "1.0.0") }
}
}

0 comments on commit 6f431a4

Please sign in to comment.