[devkit] IJPL-158473 attach bundled plugin sources archives located inside the IntelliJ Platform directory

(cherry picked from commit 40481ca23e1ac81d3b3e99b732cdaa49f0fef0a9)

IJ-CR-140104

GitOrigin-RevId: 954f4059a02959254c428bc563467acd1d0f778b
This commit is contained in:
Jakub Chrzanowski
2024-07-17 02:47:52 +02:00
committed by intellij-monorepo-bot
parent 5e46db7296
commit bb40a5e935
3 changed files with 98 additions and 54 deletions

View File

@@ -6,7 +6,6 @@ import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.util.SystemInfo
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.exists
@@ -19,11 +18,10 @@ private val json = Json { ignoreUnknownKeys = true }
*/
fun loadProductInfo(ideaJdkHome: String): ProductInfo? {
val ideHomePath = Path.of(ideaJdkHome)
val productInfoJsonPath = when {
SystemInfo.isMac -> ideHomePath.resolve(ApplicationEx.PRODUCT_INFO_FILE_NAME_MAC)
else -> ideHomePath.resolve(ApplicationEx.PRODUCT_INFO_FILE_NAME)
}
if (Files.notExists(productInfoJsonPath)) return null
val productInfoJsonPath = listOf(
ApplicationEx.PRODUCT_INFO_FILE_NAME_MAC,
ApplicationEx.PRODUCT_INFO_FILE_NAME,
).firstNotNullOfOrNull { ideHomePath.resolve(it).takeIf(Path::exists) } ?: return null
return runCatching { json.decodeFromString<ProductInfo>(productInfoJsonPath.readText()) }
.onFailure { logger<ProductInfo>().error("error parsing '$productInfoJsonPath'", it) }

View File

@@ -1,6 +1,22 @@
module.wizard.gradle.presentable.name=IntelliJ Platform Plugin
module.wizard.gradle.learn.title=Learn how to <a>build plugins with Gradle</a>
attachSources.action.name=Download IntelliJ Platform sources
attachSources.action.busyText=Downloading IntelliJ Platform sources\u2026
attachLSPSources.action.name=Attach IntelliJ Platform LSP-API sources
attachLSPSources.action.busyText=Attaching IntelliJ Platform LSP-API sources\u2026
attachSources.intellijPlatform.action.name=Download IntelliJ Platform sources
attachSources.intellijPlatform.action.busyText=Downloading IntelliJ Platform sources\u2026
attachSources.lsp.action.name=Attach IntelliJ Platform LSP-API sources
attachSources.lsp.action.busyText=Attaching IntelliJ Platform LSP-API sources\u2026
attachSources.css.action.name=Attach CSS plugin sources
attachSources.css.action.busyText=Attaching CSS plugin sources\u2026
attachSources.database.action.name=Attach Database plugin sources
attachSources.database.action.busyText=Attaching Database plugin sources\u2026
attachSources.java.action.name=Attach Java plugin sources
attachSources.java.action.busyText=Attaching Java plugin sources\u2026
attachSources.javaee.action.name=Attach JavaEE plugin sources
attachSources.javaee.action.busyText=Attaching JavaEE plugin sources\u2026
attachSources.persistence.action.name=Attach Persistence plugin sources
attachSources.persistence.action.busyText=Attaching Persistence plugin sources\u2026
attachSources.spring.action.name=Attach Spring plugin sources
attachSources.spring.action.busyText=Attaching Spring plugin sources\u2026
attachSources.springBoot.action.name=Attach SpringBoot plugin sources
attachSources.springBoot.action.busyText=Attaching SpringBoot plugin sources\u2026
attachSources.tomcat.action.name=Attach Tomcat plugin sources
attachSources.tomcat.action.busyText=Attaching Tomcat plugin sources\u2026

View File

@@ -10,7 +10,6 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.util.ActionCallback
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.findFile
import com.intellij.psi.PsiFile
import org.jetbrains.idea.devkit.projectRoots.IntelliJPlatformProduct
import org.jetbrains.idea.devkit.run.ProductInfo
@@ -18,9 +17,27 @@ import org.jetbrains.idea.devkit.run.loadProductInfo
import org.jetbrains.plugins.gradle.execution.build.CachedModuleDataFinder
import org.jetbrains.plugins.gradle.util.GradleDependencySourceDownloader
import java.io.File
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.exists
import kotlin.io.path.pathString
internal enum class PluginSourceArchives(
val pluginId: String,
val archiveName: String,
val messageKey: String,
) {
CSS("com.intellij.css", "src_css-api.zip", "css"),
DATABASE("com.intellij.database", "src_database-openapi.zip", "database"),
JAVA("com.intellij.java", "src_jam-openapi.zip", "java"),
JAVAEE("com.intellij.javaee", "src_javaee-openapi.zip", "javaee"),
PERSISTENCE("com.intellij.persistence", "src_persistence-openapi.zip", "persistence"),
SPRING("com.intellij.spring", "src_spring-openapi.zip", "spring"),
SPRING_BOOT("com.intellij.spring.boot", "src_spring-boot-openapi.zip", "springBoot"),
TOMCAT("Tomcat", "src_tomcat.zip", "tomcat"),
LSP("LSP", "src_lsp-openapi.zip", "lsp"),
}
/**
* Attaches sources to the IntelliJ Platform dependencies in projects using IntelliJ Platform Gradle Plugin 2.x.
* Some IDEs, like IntelliJ IDEA Ultimate or PhpStorm, don't provide sources for artifacts published to IntelliJ Repository.
@@ -91,7 +108,8 @@ internal class IntelliJPlatformAttachSourcesProvider : AttachSourcesProvider {
else -> IntelliJPlatformProduct.IDEA_IC
}.mavenCoordinates ?: return null
// We're checking if the compiled class belongs to the `/com/intellij/platform/lsp/` package, but not `.../impl` as it's not part of the API
// When targeting IntelliJ IDEA Ultimate, it is possible to attach LSP module sources.
// If the compiled class belongs to `com/intellij/platform/lsp/`, suggest attaching the relevant ZIP archive with LSP sources.
val isLspApi = with(psiFile.virtualFile.path.substringAfter('!')) {
when {
startsWith("/com/intellij/platform/lsp/impl/") -> false
@@ -102,7 +120,7 @@ internal class IntelliJPlatformAttachSourcesProvider : AttachSourcesProvider {
return when {
// We're handing LSP API class, but IU is lower than 242 -> attach a standalone sources file
isLspApi && majorVersion < 242 -> createAttachLSPSourcesAction(psiFile)
isLspApi && majorVersion < 242 -> createAttachSourcesArchiveAction(psiFile, PluginSourceArchives.LSP)
// Create the actual IntelliJ Platform sources attaching action
else -> createAttachPlatformSourcesAction(psiFile, productCoordinates, version)
@@ -133,32 +151,37 @@ internal class IntelliJPlatformAttachSourcesProvider : AttachSourcesProvider {
val product = IntelliJPlatformProduct.fromProductCode(productInfo.productCode) ?: return null
val version = coordinates.version.substringBefore('+')
return resolveIntelliJPlatformAction(psiFile, product, version)
val pluginSourceArchive = PluginSourceArchives.entries.firstOrNull { it.pluginId == coordinates.artifactId }
return createAttachSourcesArchiveAction(psiFile, pluginSourceArchive)
?: resolveIntelliJPlatformAction(psiFile, product, version)
}
/**
* When targeting IntelliJ IDEA Ultimate, it is possible to attach LSP module sources.
* If the compiled class belongs to `com/intellij/platform/lsp/`, suggest attaching relevant ZIP archive with LSP sources.
* Attach the provided sources archive.
*
* @param psiFile The PSI file that represents the currently handled class.
* @param pluginSourceArchive Plugin sources archive metadata.
*/
private fun createAttachLSPSourcesAction(psiFile: PsiFile): AttachSourcesAction? {
val jarFile = VfsUtilCore.getVirtualFileForJar(psiFile.virtualFile) ?: return null
val sources = jarFile.parent.findFile("src/src_lsp-openapi.zip") ?: return null
private fun createAttachSourcesArchiveAction(psiFile: PsiFile, pluginSourceArchive: PluginSourceArchives?): AttachSourcesAction? {
if (pluginSourceArchive == null) {
return null
}
return object : AttachSourcesAction {
override fun getName() = DevKitGradleBundle.message("attachLSPSources.action.name")
return resolveSourcesArchive(psiFile, pluginSourceArchive.archiveName)?.let {
object : AttachSourcesAction {
override fun getName() = DevKitGradleBundle.message("attachSources.${pluginSourceArchive.messageKey}.action.name")
override fun getBusyText() = DevKitGradleBundle.message("attachLSPSources.action.busyText")
override fun getBusyText() = DevKitGradleBundle.message("attachSources.${pluginSourceArchive.messageKey}.action.busyText")
override fun perform(orderEntries: MutableList<out LibraryOrderEntry>): ActionCallback {
val executionResult = ActionCallback()
override fun perform(orderEntries: MutableList<out LibraryOrderEntry>): ActionCallback {
val executionResult = ActionCallback()
attachSources(sources.toNioPath().toFile(), orderEntries) {
executionResult.setDone()
attachSources(it.toFile(), orderEntries) {
executionResult.setDone()
}
return executionResult
}
return executionResult
}
}
}
@@ -170,39 +193,36 @@ internal class IntelliJPlatformAttachSourcesProvider : AttachSourcesProvider {
* @param productCoordinates The Maven coordinates of the IntelliJ Platform whose sources we load.
* @param version The version of the product.
*/
private fun createAttachPlatformSourcesAction(
psiFile: PsiFile,
productCoordinates: String,
version: String,
) = object : AttachSourcesAction {
override fun getName() = DevKitGradleBundle.message("attachSources.action.name")
private fun createAttachPlatformSourcesAction(psiFile: PsiFile, productCoordinates: String, version: String) =
object : AttachSourcesAction {
override fun getName() = DevKitGradleBundle.message("attachSources.intellijPlatform.action.name")
override fun getBusyText() = DevKitGradleBundle.message("attachSources.action.busyText")
override fun getBusyText() = DevKitGradleBundle.message("attachSources.intellijPlatform.action.busyText")
override fun perform(orderEntries: MutableList<out LibraryOrderEntry>): ActionCallback {
val externalProjectPath = CachedModuleDataFinder.getGradleModuleData(orderEntries.first().ownerModule)?.directoryToRunTask
?: return ActionCallback.REJECTED
override fun perform(orderEntries: MutableList<out LibraryOrderEntry>): ActionCallback {
val externalProjectPath = CachedModuleDataFinder.getGradleModuleData(orderEntries.first().ownerModule)?.directoryToRunTask
?: return ActionCallback.REJECTED
val executionResult = ActionCallback()
val project = psiFile.project
val sourceArtifactNotation = "$productCoordinates:$version:sources"
val executionResult = ActionCallback()
val project = psiFile.project
val sourceArtifactNotation = "$productCoordinates:$version:sources"
GradleDependencySourceDownloader
.downloadSources(project, name, sourceArtifactNotation, externalProjectPath)
.whenComplete { path, error ->
if (error != null) {
executionResult.setRejected()
}
else {
attachSources(path, orderEntries) {
executionResult.setDone()
GradleDependencySourceDownloader
.downloadSources(project, name, sourceArtifactNotation, externalProjectPath)
.whenComplete { path, error ->
if (error != null) {
executionResult.setRejected()
}
else {
attachSources(path, orderEntries) {
executionResult.setDone()
}
}
}
}
return executionResult
return executionResult
}
}
}
/**
* Attaches sources jar to the specified libraries and executes the provided block of code.
@@ -218,9 +238,19 @@ internal class IntelliJPlatformAttachSourcesProvider : AttachSourcesProvider {
* Resolve the [ProductInfo] of the current IntelliJ Platform.
*/
private fun resolveProductInfo(psiFile: PsiFile): ProductInfo? {
val jarPath = Path(psiFile.virtualFile.path)
val jarPath = Path(psiFile.virtualFile.path.substringBefore('!'))
return generateSequence(jarPath) { it.parent }
.takeWhile { it != it.root }
.firstNotNullOfOrNull { loadProductInfo(it.pathString) }
}
/**
* Resolve the [ProductInfo] of the current IntelliJ Platform.
*/
private fun resolveSourcesArchive(psiFile: PsiFile, archiveName: String): Path? {
val path = VfsUtilCore.getVirtualFileForJar(psiFile.virtualFile)?.toNioPath() ?: return null
return generateSequence(path) { it.parent }
.takeWhile { it != it.root }
.firstNotNullOfOrNull { it.resolve("lib/src/$archiveName").takeIf(Path::exists) }
}
}