[devkit] IDEA-336017 Attach IntelliJ Platform dependency sources

GitOrigin-RevId: 543d07f4d54ad50b9497ca148032ddc526f9292c
This commit is contained in:
Jakub Chrzanowski
2024-02-22 21:34:16 +00:00
committed by intellij-monorepo-bot
parent 42b017d658
commit c5a80136b6
7 changed files with 204 additions and 54 deletions

View File

@@ -36,14 +36,15 @@ internal class LibraryCoordinatesState : BaseState() {
private fun getKnownIntellijLibrariesCoordinates(): List<Pair<String, String>> {
val result = mutableListOf<Pair<String, String>>()
for ((groupId, artifactId) in IntelliJPlatformProduct.values().asSequence().mapNotNull { product ->
getMavenCoordinatesOfProduct(product)
}) {
// original coordinates of the product.
result.add(groupId to artifactId)
// coordinates used in the 'gradle-intellij-plugin' to specify IDE dependency
result.add("com.jetbrains" to artifactId)
IntelliJPlatformProduct.entries.forEach {
it.mavenCoordinates?.split(":")?.let { (groupId,artifactId) ->
// original coordinates of the product.
result.add(groupId to artifactId)
// coordinates used in the 'gradle-intellij-plugin' 1.x to specify IDE dependency; obsolete in IntelliJ Platform Gradle Plugin 2.x
result.add("com.jetbrains" to artifactId)
}
}
return result
}
@@ -59,29 +60,3 @@ private fun createDefaultState(): LibrariesWithIntellijClassesState {
}
return result
}
private fun getMavenCoordinatesOfProduct(product: IntelliJPlatformProduct): Pair<String, String>? {
return when (product) {
IntelliJPlatformProduct.IDEA -> "com.jetbrains.intellij.idea" to "ideaIU"
IntelliJPlatformProduct.IDEA_IC -> "com.jetbrains.intellij.idea" to "ideaIC"
IntelliJPlatformProduct.CLION -> "com.jetbrains.intellij.clion" to "clion"
IntelliJPlatformProduct.PYCHARM -> "com.jetbrains.intellij.pycharm" to "pycharmPY"
IntelliJPlatformProduct.PYCHARM_PC -> "com.jetbrains.intellij.pycharm" to "pycharmPC"
IntelliJPlatformProduct.RIDER -> "com.jetbrains.intellij.rider" to "riderRD"
IntelliJPlatformProduct.GOIDE -> "com.jetbrains.intellij.goland" to "goland"
IntelliJPlatformProduct.RUBYMINE,
IntelliJPlatformProduct.DATASPELL,
IntelliJPlatformProduct.PYCHARM_EDU,
IntelliJPlatformProduct.PHPSTORM,
IntelliJPlatformProduct.WEBSTORM,
IntelliJPlatformProduct.APPCODE,
IntelliJPlatformProduct.MOBILE_IDE,
IntelliJPlatformProduct.DBE,
IntelliJPlatformProduct.ANDROID_STUDIO,
IntelliJPlatformProduct.CWM_GUEST,
IntelliJPlatformProduct.JETBRAINS_CLIENT,
IntelliJPlatformProduct.GATEWAY,
IntelliJPlatformProduct.IDEA_IE -> null
}
}

View File

@@ -2,38 +2,45 @@
package org.jetbrains.idea.devkit.projectRoots;
import com.intellij.util.PlatformUtils;
import com.intellij.util.containers.ContainerUtil;
import kotlin.Pair;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Objects;
public enum IntelliJPlatformProduct {
IDEA("IU", "IntelliJ IDEA", null),
IDEA_IC("IC", "IntelliJ IDEA Community Edition", PlatformUtils.IDEA_CE_PREFIX),
IDEA_IE("IE", "IntelliJ IDEA Educational Edition", PlatformUtils.IDEA_EDU_PREFIX),
RUBYMINE("RM", "RubyMine", PlatformUtils.RUBY_PREFIX),
PYCHARM("PY", "PyCharm", PlatformUtils.PYCHARM_PREFIX),
PYCHARM_PC("PC", "PyCharm Community Edition", PlatformUtils.PYCHARM_CE_PREFIX),
DATASPELL("DS", "DataSpell", PlatformUtils.DATASPELL_PREFIX),
PYCHARM_EDU("PE", "PyCharm Educational Edition", PlatformUtils.PYCHARM_EDU_PREFIX),
PHPSTORM("PS", "PhpStorm", PlatformUtils.PHP_PREFIX),
WEBSTORM("WS", "WebStorm", PlatformUtils.WEB_PREFIX),
APPCODE("OC", "AppCode", PlatformUtils.APPCODE_PREFIX),
CLION("CL", "CLion", PlatformUtils.CLION_PREFIX),
MOBILE_IDE("MI", "Mobile IDE", PlatformUtils.MOBILE_IDE_PREFIX),
DBE("DB", "DataGrip", PlatformUtils.DBE_PREFIX),
RIDER("RD", "Rider", PlatformUtils.RIDER_PREFIX),
GOIDE("GO", "GoLand", PlatformUtils.GOIDE_PREFIX),
ANDROID_STUDIO("AI", "Android Studio", "AndroidStudio"),
IDEA("IU", "IntelliJ IDEA", null, "com.jetbrains.intellij.idea:ideaIU"),
IDEA_IC("IC", "IntelliJ IDEA Community Edition", PlatformUtils.IDEA_CE_PREFIX, "com.jetbrains.intellij.idea:ideaIC"),
IDEA_IE("IE", "IntelliJ IDEA Educational Edition", PlatformUtils.IDEA_EDU_PREFIX, null),
RUBYMINE("RM", "RubyMine", PlatformUtils.RUBY_PREFIX, null),
PYCHARM("PY", "PyCharm", PlatformUtils.PYCHARM_PREFIX, "com.jetbrains.intellij.pycharm:pycharmPY"),
PYCHARM_PC("PC", "PyCharm Community Edition", PlatformUtils.PYCHARM_CE_PREFIX, "com.jetbrains.intellij.pycharm:pycharmPC"),
DATASPELL("DS", "DataSpell", PlatformUtils.DATASPELL_PREFIX, null),
PYCHARM_EDU("PE", "PyCharm Educational Edition", PlatformUtils.PYCHARM_EDU_PREFIX, null),
PHPSTORM("PS", "PhpStorm", PlatformUtils.PHP_PREFIX, "com.jetbrains.intellij.phpstorm:phpstorm"),
WEBSTORM("WS", "WebStorm", PlatformUtils.WEB_PREFIX, null),
APPCODE("OC", "AppCode", PlatformUtils.APPCODE_PREFIX, null),
CLION("CL", "CLion", PlatformUtils.CLION_PREFIX, "com.jetbrains.intellij.clion:clion"),
MOBILE_IDE("MI", "Mobile IDE", PlatformUtils.MOBILE_IDE_PREFIX, null),
DBE("DB", "DataGrip", PlatformUtils.DBE_PREFIX, null),
RIDER("RD", "Rider", PlatformUtils.RIDER_PREFIX, "com.jetbrains.intellij.rider:riderRD"),
GOIDE("GO", "GoLand", PlatformUtils.GOIDE_PREFIX, "com.jetbrains.intellij.goland:goland"),
ANDROID_STUDIO("AI", "Android Studio", "AndroidStudio", null),
/**
* @deprecated Code With Me Guest is an old name for JetBrains Client
*/
@Deprecated
CWM_GUEST("CWMG", "Code With Me Guest", PlatformUtils.CWM_GUEST_PREFIX),
JETBRAINS_CLIENT("JBC", "JetBrains Client", PlatformUtils.JETBRAINS_CLIENT_PREFIX),
GATEWAY("GW", "Gateway", PlatformUtils.GATEWAY_PREFIX);
CWM_GUEST("CWMG", "Code With Me Guest", PlatformUtils.CWM_GUEST_PREFIX, null),
JETBRAINS_CLIENT("JBC", "JetBrains Client", PlatformUtils.JETBRAINS_CLIENT_PREFIX, null),
GATEWAY("GW", "Gateway", PlatformUtils.GATEWAY_PREFIX, "com.jetbrains.intellij.gateway:gateway"),
FLEET_BACKEND("FLIJ", "Fleet Backend", PlatformUtils.FLEET_PREFIX, "com.jetbrains.intellij.fleetBackend:fleetBackend");
private final String myProductCode;
private final String myName;
private final String myPlatformPrefix;
private final String myMavenCoordinates;
public @NonNls String getName() {
return myName;
@@ -43,10 +50,18 @@ public enum IntelliJPlatformProduct {
return myPlatformPrefix;
}
IntelliJPlatformProduct(@NonNls String productCode,@NonNls String name, @NonNls String platformPrefix) {
/**
* Provides Maven coordinates which can be used for resolving artifact from the IntelliJ Repository.
*/
public @Nullable String getMavenCoordinates() {
return myMavenCoordinates;
}
IntelliJPlatformProduct(@NonNls String productCode, @NonNls String name, @NonNls String platformPrefix, @Nullable String mavenCoordinates) {
myProductCode = productCode;
myName = name;
myPlatformPrefix = platformPrefix;
myMavenCoordinates = mavenCoordinates;
}
public static IntelliJPlatformProduct fromBuildNumber(String buildNumber) {
@@ -57,4 +72,8 @@ public enum IntelliJPlatformProduct {
}
return IDEA;
}
public static @Nullable IntelliJPlatformProduct fromMavenCoordinates(String groupId, String artifactId) {
return ContainerUtil.find(values(), product -> Objects.equals(product.getMavenCoordinates(), groupId + ":" + artifactId));
}
}

View File

@@ -0,0 +1,16 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.idea.devkit.projectRoots;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
import static org.jetbrains.idea.devkit.projectRoots.IntelliJPlatformProduct.fromMavenCoordinates;
public class IntelliJPlatformProductTest extends JavaCodeInsightFixtureTestCase {
public void testFromMavenCoordinates() {
assertSame(fromMavenCoordinates("com.jetbrains.intellij.idea", "ideaIU"), IntelliJPlatformProduct.IDEA);
assertSame(fromMavenCoordinates("com.jetbrains.intellij.idea", "ideaIC"), IntelliJPlatformProduct.IDEA_IC);
assertSame(fromMavenCoordinates("com.jetbrains.intellij.phpstorm", "phpstorm"), IntelliJPlatformProduct.PHPSTORM);
assertSame(fromMavenCoordinates("foo", "bar"), null);
}
}

View File

@@ -27,5 +27,6 @@
<orderEntry type="module" module-name="intellij.platform.core.ui" />
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
<orderEntry type="module" module-name="intellij.java.testFramework" scope="TEST" />
<orderEntry type="module" module-name="intellij.java" />
</component>
</module>

View File

@@ -9,6 +9,7 @@
</extensions>
<extensions defaultExtensionNs="com.intellij">
<attachSourcesProvider implementation="org.jetbrains.idea.devkit.gradle.IntelliJPlatformAttachSourcesProvider"/>
<consoleFilterProvider implementation="org.jetbrains.idea.devkit.gradle.GradlePluginConsoleFilterProvider"/>
</extensions>
</idea-plugin>

View File

@@ -1,2 +1,6 @@
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

View File

@@ -0,0 +1,134 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.idea.devkit.gradle
import com.intellij.codeInsight.AttachSourcesProvider
import com.intellij.codeInsight.AttachSourcesProvider.AttachSourcesAction
import com.intellij.jarFinder.InternetAttachSourceProvider
import com.intellij.java.library.MavenCoordinates
import com.intellij.java.library.getMavenCoordinates
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
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.plugins.gradle.util.GradleDependencySourceDownloader
import java.io.File
/**
* 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.
* To handle such a case, IntelliJ IDEA Community sources are attached.
*/
class IntelliJPlatformAttachSourcesProvider : AttachSourcesProvider {
override fun getActions(orderEntries: MutableList<out LibraryOrderEntry>, psiFile: PsiFile): List<AttachSourcesAction> {
// Search for a product that matches any of the entry coordinates. Return both product and coordinates, to refer to the same version.
val (product, libraryCoordinates) = orderEntries.mapNotNull {
it.library?.getMavenCoordinates()
}.firstNotNullOfOrNull { coordinates ->
val product = IntelliJPlatformProduct.fromMavenCoordinates(coordinates.groupId, coordinates.artifactId)
if (product == null) {
return@firstNotNullOfOrNull null
}
product to coordinates
} ?: return emptyList()
// 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
val isLspApi = with(psiFile.virtualFile.path.substringAfter('!')) {
when {
startsWith("/com/intellij/platform/lsp/impl/") -> false
startsWith("/com/intellij/platform/lsp/") -> true
else -> false
} && product == IntelliJPlatformProduct.IDEA // LSP API sources are provided only with IU
}
val action = when {
isLspApi -> createAttachLSPSourcesAction(psiFile)
else -> createAttachPlatformSourcesAction(psiFile, product, libraryCoordinates)
}
return action?.let { listOf(it) } ?: emptyList()
}
/**
* Resolve and attach IntelliJ IDEA Community or PyCharm Community sources to the IntelliJ Platform dependency in requested version.
*/
private fun createAttachPlatformSourcesAction(
psiFile: PsiFile,
product: IntelliJPlatformProduct,
libraryCoordinates: MavenCoordinates,
): AttachSourcesAction? {
val productCoordinates =
when (product) {
IntelliJPlatformProduct.PYCHARM, IntelliJPlatformProduct.PYCHARM_PC -> IntelliJPlatformProduct.PYCHARM_PC
else -> IntelliJPlatformProduct.IDEA_IC
}.mavenCoordinates ?: return null
return object : AttachSourcesAction {
override fun getName() = DevKitGradleBundle.message("attachSources.action.name")
override fun getBusyText() = DevKitGradleBundle.message("attachSources.action.busyText")
override fun perform(orderEntries: MutableList<out LibraryOrderEntry>): ActionCallback {
val externalProjectPath = orderEntries.first().ownerModule.let {
ExternalSystemApiUtil.getExternalRootProjectPath(it)
} ?: return ActionCallback.REJECTED
val executionResult = ActionCallback()
val project = psiFile.project
val sourceArtifactNotation = "$productCoordinates:${libraryCoordinates.version}:sources"
GradleDependencySourceDownloader.downloadSources(project, name, sourceArtifactNotation, externalProjectPath).whenComplete { path, error ->
if (error != null) {
executionResult.setRejected()
}
else {
attachSources(path, orderEntries) {
executionResult.setDone()
}
}
}
return executionResult
}
}
}
/**
* 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.
*/
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
return object : AttachSourcesAction {
override fun getName() = DevKitGradleBundle.message("attachLSPSources.action.name")
override fun getBusyText() = DevKitGradleBundle.message("attachLSPSources.action.busyText")
override fun perform(orderEntries: MutableList<out LibraryOrderEntry>): ActionCallback {
val executionResult = ActionCallback()
attachSources(sources.toNioPath().toFile(), orderEntries) {
executionResult.setDone()
}
return executionResult
}
}
}
/**
* Attaches sources jar to the specified libraries and executes the provided block of code.
*/
private fun attachSources(path: File, orderEntries: MutableList<out LibraryOrderEntry>, block: () -> Unit) {
ApplicationManager.getApplication().invokeLater {
InternetAttachSourceProvider.attachSourceJar(path, orderEntries.mapNotNull { it.library })
block()
}
}
}