diff --git a/plugins/devkit/devkit-core/src/run/ProductInfo.kt b/plugins/devkit/devkit-core/src/run/ProductInfo.kt index 09ab825cdfcd..9a9172742114 100644 --- a/plugins/devkit/devkit-core/src/run/ProductInfo.kt +++ b/plugins/devkit/devkit-core/src/run/ProductInfo.kt @@ -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(productInfoJsonPath.readText()) } .onFailure { logger().error("error parsing '$productInfoJsonPath'", it) } diff --git a/plugins/devkit/intellij.devkit.gradle/resources/messages/DevKitGradleBundle.properties b/plugins/devkit/intellij.devkit.gradle/resources/messages/DevKitGradleBundle.properties index d9f2a9ca8700..2df3cefb47a2 100644 --- a/plugins/devkit/intellij.devkit.gradle/resources/messages/DevKitGradleBundle.properties +++ b/plugins/devkit/intellij.devkit.gradle/resources/messages/DevKitGradleBundle.properties @@ -1,6 +1,22 @@ module.wizard.gradle.presentable.name=IntelliJ Platform Plugin module.wizard.gradle.learn.title=Learn how to build plugins with Gradle -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 diff --git a/plugins/devkit/intellij.devkit.gradle/src/IntelliJPlatformAttachSourcesProvider.kt b/plugins/devkit/intellij.devkit.gradle/src/IntelliJPlatformAttachSourcesProvider.kt index 7aebf20680e8..34df3d813e4a 100644 --- a/plugins/devkit/intellij.devkit.gradle/src/IntelliJPlatformAttachSourcesProvider.kt +++ b/plugins/devkit/intellij.devkit.gradle/src/IntelliJPlatformAttachSourcesProvider.kt @@ -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): ActionCallback { - val executionResult = ActionCallback() + override fun perform(orderEntries: MutableList): 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): ActionCallback { - val externalProjectPath = CachedModuleDataFinder.getGradleModuleData(orderEntries.first().ownerModule)?.directoryToRunTask - ?: return ActionCallback.REJECTED + override fun perform(orderEntries: MutableList): 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) } + } }