From 76883f94133b78cde4e674575d5c718edc0f4563 Mon Sep 17 00:00:00 2001 From: Nikita Katkov Date: Wed, 5 Aug 2026 15:05:57 +0200 Subject: [PATCH] [remote development tooling] IJPL-231068 Sync split mode inspections state with master branch to offer devs who do not use nightly builds the same tooling that the safe push checks use GitOrigin-RevId: be32cc21e0147ac93fefaef9dfd802483f3d98aa --- plugins/devkit/devkit-core/BUILD.bazel | 2 + .../devkit-core/intellij.devkit.core.iml | 1 + .../preview.xml | 11 + .../resources/intellij.devkit.core.xml | 17 +- .../messages/DevKitBundle.properties | 15 +- ...estrictions.json => ApiRestrictions.json5} | 627 +++++++++--------- .../DevKitSplitModeInspectionExclusions.json | 22 - ...orepoFeaturesWithoutSplitModeSupport.json5 | 51 ++ .../PredefinedModuleKinds.json | 190 ------ .../SplitModeProjectBaselineVersion.json5 | 10 + .../SplitModeQodanaAnalysisScope.json | 405 +---------- .../PluginDescriptorDomFileDescription.java | 8 +- ...endOrBackendRuntimeDependencyInspection.kt | 13 +- ...MonorepoFeaturesWithoutSplitModeSupport.kt | 156 +++++ .../src/inspections/remotedev/OWNERSHIP | 1 + .../remotedev/SplitModeApiUsageInspection.kt | 21 +- .../SplitModeDependencyQuickFixes.kt | 35 +- .../SplitModeImplicitModuleKindInspection.kt | 12 +- .../SplitModeInspectionExclusions.kt | 233 ++----- .../remotedev/SplitModeInspectionUtil.kt | 3 +- .../SplitModeMixedDependenciesInspection.kt | 13 +- .../SplitModeXmlApiUsageInspection.kt | 22 +- .../analysis/SplitModeAnalysisModel.kt | 6 + .../SplitModeApiRestrictionsService.kt | 204 ++++-- .../SplitModeDescriptorDependencyAnalyzer.kt | 30 +- .../analysis/SplitModeModuleKindIcons.kt | 86 ++- .../analysis/SplitModeModuleKindResolver.kt | 44 +- .../DescriptorSplitModeInlayHintsProvider.kt | 147 ++++ ...ncludingContentModuleLineMarkerProvider.kt | 20 +- plugins/devkit/devkit-java-tests/BUILD.bazel | 2 + .../intellij.devkit.java.tests.iml | 1 + .../PluginDescriptorSplitModeIconsTest.kt | 131 ++-- ...rBackendRuntimeDependencyInspectionTest.kt | 136 ---- .../SplitModeApiRestrictionsSourceModeTest.kt | 25 +- .../SplitModeDependencyQuickFixesTest.kt | 10 +- ...ModeExcludedModulesScopeConsistencyTest.kt | 55 ++ ...SplitModeInspectionExclusionsReloadTest.kt | 60 +- ...PredefinedModuleKindsAdditionalFileTest.kt | 9 +- .../SplitModeXmlApiUsageInspectionTest.kt | 75 +++ ...scriptorSplitModeInlayHintsProviderTest.kt | 256 +++++++ .../SplitModeApiUsageInspectionTest.kt | 155 +++-- .../devkit/intellij.devkit.mcp/BUILD.bazel | 6 + .../intellij.devkit.mcp.iml | 3 + .../resources/intellij.devkit.mcp.xml | 14 +- .../src/DevKitMcpToolset.kt | 112 +++- ...vKitSplitModeCompatibilityInspectionApi.kt | 208 ++++++ .../src/DevKitSplitModeRecognitionApi.kt | 37 +- 47 files changed, 2179 insertions(+), 1521 deletions(-) create mode 100644 plugins/devkit/devkit-core/resources/inlayProviders/devkit.plugin.descriptor.split.mode.kind/preview.xml rename plugins/devkit/devkit-core/resources/remotedevInspectionData/{ApiRestrictions.json => ApiRestrictions.json5} (59%) delete mode 100644 plugins/devkit/devkit-core/resources/remotedevInspectionData/DevKitSplitModeInspectionExclusions.json create mode 100644 plugins/devkit/devkit-core/resources/remotedevInspectionData/MonorepoFeaturesWithoutSplitModeSupport.json5 delete mode 100644 plugins/devkit/devkit-core/resources/remotedevInspectionData/PredefinedModuleKinds.json create mode 100644 plugins/devkit/devkit-core/resources/remotedevInspectionData/SplitModeProjectBaselineVersion.json5 create mode 100644 plugins/devkit/devkit-core/src/inspections/remotedev/MonorepoFeaturesWithoutSplitModeSupport.kt create mode 100644 plugins/devkit/devkit-core/src/inspections/remotedev/OWNERSHIP create mode 100644 plugins/devkit/devkit-core/src/navigation/DescriptorSplitModeInlayHintsProvider.kt create mode 100644 plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeExcludedModulesScopeConsistencyTest.kt create mode 100644 plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/navigation/DescriptorSplitModeInlayHintsProviderTest.kt create mode 100644 plugins/devkit/intellij.devkit.mcp/src/DevKitSplitModeCompatibilityInspectionApi.kt diff --git a/plugins/devkit/devkit-core/BUILD.bazel b/plugins/devkit/devkit-core/BUILD.bazel index 5bca9b6a0b06..6299560fdd56 100644 --- a/plugins/devkit/devkit-core/BUILD.bazel +++ b/plugins/devkit/devkit-core/BUILD.bazel @@ -123,6 +123,7 @@ jvm_library( "//libraries/jackson/jackson", "//libraries/jackson/databind", "//libraries/kotlinx/collections-immutable:libraries-kotlinx-collections-immutable", + "//platform/workspace/jps", ], ) @@ -231,6 +232,7 @@ jvm_library( "//libraries/jackson/jackson:jackson_test_lib", "//libraries/jackson/databind:databind_test_lib", "//libraries/kotlinx/collections-immutable:libraries-kotlinx-collections-immutable_test_lib", + "//platform/workspace/jps:jps_test_lib", ], ) ### auto-generated section `build intellij.devkit.core` end diff --git a/plugins/devkit/devkit-core/intellij.devkit.core.iml b/plugins/devkit/devkit-core/intellij.devkit.core.iml index 28848725a3ce..40b2220cbccf 100644 --- a/plugins/devkit/devkit-core/intellij.devkit.core.iml +++ b/plugins/devkit/devkit-core/intellij.devkit.core.iml @@ -123,5 +123,6 @@ + \ No newline at end of file diff --git a/plugins/devkit/devkit-core/resources/inlayProviders/devkit.plugin.descriptor.split.mode.kind/preview.xml b/plugins/devkit/devkit-core/resources/inlayProviders/devkit.plugin.descriptor.split.mode.kind/preview.xml new file mode 100644 index 000000000000..689f14978d56 --- /dev/null +++ b/plugins/devkit/devkit-core/resources/inlayProviders/devkit.plugin.descriptor.split.mode.kind/preview.xml @@ -0,0 +1,11 @@ + + + + + com.example.backend.plugin + JetBrains + + + + + diff --git a/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml b/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml index 2d5c22baee3a..882a742484bf 100644 --- a/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml +++ b/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml @@ -159,15 +159,17 @@ + description="Enables deferred accurate split-mode icon resolution for DevKit descriptor and directory icons."/> + + diff --git a/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties b/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties index c104133b02b3..c6124de30335 100644 --- a/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties +++ b/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties @@ -753,6 +753,17 @@ inspections.meta.information.json.schema.display.name=IntelliJ Inspections Metai inlay.threading.name=IJ Platform threading # suppress inspection "GrazieInspection" inlay.threading.description=IntelliJ Platform threading and read/write lock related annotations, e.g., @RequiresEdt, @RequiresReadLock.

Experimental, enabled in IDEA project only. +inlay.split.mode.module.kind.name=Plugin descriptor split-mode kind +inlay.split.mode.module.kind.description=Shows whether a plugin descriptor is a frontend, backend, shared, or monolith plugin/content module/XML descriptor. +inlay.split.mode.module.kind.presentation={0} {1} +inlay.split.mode.module.kind.tooltip=Split mode compatibility: {0} can be installed into {1}. Reason: {2} +inlay.split.mode.module.kind.compatibility.frontend=frontend IDE +inlay.split.mode.module.kind.compatibility.backend=backend IDE +inlay.split.mode.module.kind.compatibility.shared=both frontend and backend IDE +inlay.split.mode.module.kind.compatibility.monolith=monolith IDE +inlay.split.mode.module.kind.content.module=content module +inlay.split.mode.module.kind.plugin=plugin +inlay.split.mode.module.kind.xml.descriptor=XML descriptor inspections.static.initialization.in.extensions.name=Static initialization in extension point implementations inspections.static.initialization.in.extensions.message=Extension point implementations must not use static initialization @@ -836,8 +847,8 @@ inspection.message.return.value.without.path.annotation.where.expected=Return va inspection.remote.dev.missing.runtime.dependency.name=Missing frontend or backend runtime dependency inspection.remote.dev.missing.runtime.dependency.message=The name of the current module ''{0}'' ends with ''{1}'', but the dependency on ''{2}'' is missing inspection.remote.dev.missing.runtime.dependency.fix.add=Add the ''{0}'' dependency -inspection.remote.dev.add.to.split.mode.exclusions.fix.name=Add this violation to the exclusions list -inspection.remote.dev.add.to.split.mode.exclusions.command.name=Add This Violation to the Exclusions List +inspection.remote.dev.increase.split.mode.baseline.fix.name=Bypass incremental Split Mode safe-push tests +inspection.remote.dev.increase.split.mode.baseline.command.name=Increase Split Mode Compatibility Baseline Version inspection.split.mode.mixed.dependencies.display.name=Split mode compatibility inspection inspection.split.mode.implicit.module.kind.display.name=Implicit split mode module kind inspection inspection.remote.dev.mixed.dependencies.message=This module effectively depends on frontend-only and backend-only modules simultaneously. It will not get loaded in Split Mode. diff --git a/plugins/devkit/devkit-core/resources/remotedevInspectionData/ApiRestrictions.json b/plugins/devkit/devkit-core/resources/remotedevInspectionData/ApiRestrictions.json5 similarity index 59% rename from plugins/devkit/devkit-core/resources/remotedevInspectionData/ApiRestrictions.json rename to plugins/devkit/devkit-core/resources/remotedevInspectionData/ApiRestrictions.json5 index 35c684b23be9..63be7ca29d9f 100644 --- a/plugins/devkit/devkit-core/resources/remotedevInspectionData/ApiRestrictions.json +++ b/plugins/devkit/devkit-core/resources/remotedevInspectionData/ApiRestrictions.json5 @@ -1,317 +1,342 @@ +// Split-mode API restrictions consumed by DevKit inspections. +// JSON5 comments, single-quoted strings, unquoted field names, and trailing commas are supported. +// +// Entry structure: +// { +// "apiName": "fully.qualified.ApiName", // Required, unique API identifier used by code inspections. +// "extensionPointNames": ["com.intellij.extensionPoint"], // Optional. XML extension point names mapped to this API restriction. +// "targetModules": ["frontend", "backend", "shared"], // Required, non-empty. Allowed values: "frontend", "backend", "shared", "monolith". +// "restrictionKind": "genericPlatformApi", // Required. Allowed values: "genericPlatformApi", "ui". +// "hint": "Extra guidance shown by the inspection" // Optional. Added to the text user/agent sees when an error is reported by inspections +// } +// +// APIs with "restrictionKind": "ui" are forbidden in modules with the backendWithNonUiApiPermit policy, others are allowed. These modules are rider and clion specific backend modules [ - {"apiName": "com.intellij.openapi.wm.ToolWindowFactory", "extensionPointNames": ["com.intellij.toolWindow"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.wm.ToolWindow", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.editor.toolbar.floating.FloatingToolbarProvider", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.ui.DialogWrapper", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.ui.popup.JBPopupFactory", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.fileEditor.FileEditorManagerListener", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.fileEditor.FileEditorProvider", "extensionPointNames": ["com.intellij.fileEditorProvider"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.editor.event.DocumentListener", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.editor.NonWriteAccessTypedHandler", "extensionPointNames": ["com.intellij.nonWriteAccessTypedHandler"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.fileEditor.FileEditorManager.getFocusedEditor", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.execution.services.ServiceViewContributor", "extensionPointNames": ["com.intellij.serviceViewContributor"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.ui.tree.AsyncTreeModel", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.ui.jcef.JBCefClient", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.notification.NotificationGroup", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.jetbrains.rdclient.daemon.FrontendHighlighterSuppressionHandler", "extensionPointNames": ["com.intellij.rdclient.highlighterSuppressionHandler"], "targetModules": ["frontend"]}, - {"apiName": "com.jetbrains.rdclient.actions.base.BackendDelegatingActionCustomization", "extensionPointNames": ["com.intellij.rdclient.actionCustomization"], "targetModules": ["frontend"]}, - {"apiName": "com.jetbrains.rdclient.editorActions.cwm.FrontendEditorActionHandlerStrategyCustomizer", "extensionPointNames": ["com.intellij.rdclient.editorActionHandlerStrategyCustomizer"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.wm.StatusBarWidgetFactory", "extensionPointNames": ["com.intellij.statusBarWidgetFactory"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.application.options.editor.EditorOptionsPageCustomizer", "extensionPointNames": ["com.intellij.generalEditorOptionsCustomizer"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.codeInsight.folding.CollapseBlockHandler", "extensionPointNames": ["com.intellij.codeInsight.folding.collapseBlockHandler"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.editor.LineWrapPositionStrategy", "extensionPointNames": ["com.intellij.lang.lineWrapStrategy"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.editor.actionSystem.EditorActionHandler", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.editor.event.CaretListener", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.editor.event.EditorMouseMotionListener", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.editor.event.EditorMouseListener", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.editor.event.SelectionListener", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.editor.event.VisibleAreaListener", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.editor.ex.FoldingListener", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.ui.popup.JBPopupListener", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.actionSystem.CommonDataKeys.EDITOR", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.actionSystem.PlatformDataKeys.TOOL_WINDOW", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.actionSystem.PlatformCoreDataKeys.SELECTED_ITEM", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.actionSystem.PlatformCoreDataKeys.SELECTED_ITEMS", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.actionSystem.PlatformCoreDataKeys.CONTEXT_COMPONENT", "extensionPointNames": [], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.codeInsight.lookup.impl.LookupDisplayStrategy", "extensionPointNames": ["com.intellij.lookup.displayStrategy"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.codeInsight.lookup.LookupBottomPanelProvider", "extensionPointNames": ["com.intellij.lookup.bottomPanelProvider"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.codeInsight.lookup.LookupBottomPanelAdvertiserCustomizer", "extensionPointNames": ["com.intellij.lookup.bottomPanelAdvertiserCustomizer"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.codeInsight.lookup.LookupMayHaveCustomPreviewProvider", "extensionPointNames": ["com.intellij.lookup.may.have.custom.preview.provider"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.codeInsight.completion.TemplateParameterTraversalPolicy", "extensionPointNames": ["com.intellij.templateParameterTraversalPolicy"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.options.UnnamedConfigurable", "extensionPointNames": ["com.intellij.codeCompletionConfigurable", "com.intellij.generalEditorOptionsExtension", "com.intellij.inlineCompletionConfigurable", "com.intellij.mlCodeCompletionConfigurable"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.profile.codeInspection.ui.ErrorOptionsProvider", "extensionPointNames": ["com.intellij.errorOptionsProvider"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.application.options.editor.CodeFoldingOptionsProvider", "extensionPointNames": ["com.intellij.codeFoldingOptionsProvider"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.application.options.editor.AutoImportOptionsProvider", "extensionPointNames": ["com.intellij.autoImportOptionsProvider"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.application.options.editor.EditorOptionsProvider", "extensionPointNames": ["com.intellij.editorOptionsProvider"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.application.options.editor.InlineDocsAdditionalConfigurable", "extensionPointNames": ["com.intellij.editorAppearanceInlineDocHandler"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.openapi.roots.ui.configuration.LibrarySettingsProvider", "extensionPointNames": ["com.intellij.librarySettingsProvider"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.profile.codeInspection.ui.InspectionProfileActionProvider", "extensionPointNames": ["com.intellij.inspectionProfileActionProvider"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.profile.codeInspection.ui.InspectionTreeAdvertiser", "extensionPointNames": ["com.intellij.inspectionTreeAdvertiser"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.analysis.problemsView.toolWindow.ProblemsViewPanelProvider", "extensionPointNames": ["com.intellij.problemsViewPanelProvider"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.analysis.problemsView.toolWindow.HighlightingProblemFactory", "extensionPointNames": ["com.intellij.problemsViewHighlightingProblemFactory"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.find.impl.livePreview.EditorSearchAreaProvider", "extensionPointNames": ["com.intellij.editorSearchAreaProvider"], "targetModules": ["frontend"]}, - {"apiName": "com.intellij.codeInsight.intention.impl.IntentionPopupProvider", "extensionPointNames": ["com.intellij.intentionPopupProvider"], "targetModules": ["frontend"]}, + // frontend ui apis + {"apiName": "com.intellij.openapi.wm.ToolWindowFactory", "extensionPointNames": ["com.intellij.toolWindow"], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.wm.ToolWindow", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.editor.toolbar.floating.FloatingToolbarProvider", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.ui.DialogWrapper", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.ui.popup.JBPopupFactory", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.ui.tree.AsyncTreeModel", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.ui.jcef.JBCefClient", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.wm.StatusBarWidgetFactory", "extensionPointNames": ["com.intellij.statusBarWidgetFactory"], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.fileEditor.FileEditorManagerListener", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.fileEditor.FileEditorProvider", "extensionPointNames": ["com.intellij.fileEditorProvider"], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.fileEditor.FileEditorManager.getFocusedEditor", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.execution.services.ServiceViewContributor", "extensionPointNames": ["com.intellij.serviceViewContributor"], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.editor.event.CaretListener", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.editor.event.EditorMouseMotionListener", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.editor.event.EditorMouseListener", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.editor.event.SelectionListener", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.editor.event.VisibleAreaListener", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.ui.popup.JBPopupListener", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.editor.ex.FoldingListener", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.analysis.problemsView.toolWindow.ProblemsViewPanelProvider", "extensionPointNames": ["com.intellij.problemsViewPanelProvider"], "targetModules": ["frontend"], "restrictionKind": "ui"}, + {"apiName": "com.intellij.openapi.options.Configurable", "extensionPointNames": ["com.intellij.applicationConfigurable", "com.intellij.projectConfigurable"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + + + // frontend generic apis + {"apiName": "com.intellij.openapi.editor.event.DocumentListener", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.editor.NonWriteAccessTypedHandler", "extensionPointNames": ["com.intellij.nonWriteAccessTypedHandler"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.notification.NotificationGroup", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.jetbrains.rdclient.daemon.FrontendHighlighterSuppressionHandler", "extensionPointNames": ["com.intellij.rdclient.highlighterSuppressionHandler"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.jetbrains.rdclient.actions.base.BackendDelegatingActionCustomization", "extensionPointNames": ["com.intellij.rdclient.actionCustomization"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.jetbrains.rdclient.editorActions.cwm.FrontendEditorActionHandlerStrategyCustomizer", "extensionPointNames": ["com.intellij.rdclient.editorActionHandlerStrategyCustomizer"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.application.options.editor.EditorOptionsPageCustomizer", "extensionPointNames": ["com.intellij.generalEditorOptionsCustomizer"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.folding.CollapseBlockHandler", "extensionPointNames": ["com.intellij.codeInsight.folding.collapseBlockHandler"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.editor.actionSystem.EditorActionHandler", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.actionSystem.CommonDataKeys.EDITOR", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.actionSystem.PlatformDataKeys.TOOL_WINDOW", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.actionSystem.PlatformCoreDataKeys.SELECTED_ITEM", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.actionSystem.PlatformCoreDataKeys.SELECTED_ITEMS", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.actionSystem.PlatformCoreDataKeys.CONTEXT_COMPONENT", "extensionPointNames": [], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.lookup.impl.LookupDisplayStrategy", "extensionPointNames": ["com.intellij.lookup.displayStrategy"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.lookup.LookupBottomPanelProvider", "extensionPointNames": ["com.intellij.lookup.bottomPanelProvider"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.lookup.LookupBottomPanelAdvertiserCustomizer", "extensionPointNames": ["com.intellij.lookup.bottomPanelAdvertiserCustomizer"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.lookup.LookupMayHaveCustomPreviewProvider", "extensionPointNames": ["com.intellij.lookup.may.have.custom.preview.provider"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.completion.TemplateParameterTraversalPolicy", "extensionPointNames": ["com.intellij.templateParameterTraversalPolicy"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.options.UnnamedConfigurable", "extensionPointNames": ["com.intellij.codeCompletionConfigurable", "com.intellij.generalEditorOptionsExtension", "com.intellij.inlineCompletionConfigurable", "com.intellij.mlCodeCompletionConfigurable"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.profile.codeInspection.ui.ErrorOptionsProvider", "extensionPointNames": ["com.intellij.errorOptionsProvider"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.application.options.editor.CodeFoldingOptionsProvider", "extensionPointNames": ["com.intellij.codeFoldingOptionsProvider"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.application.options.editor.AutoImportOptionsProvider", "extensionPointNames": ["com.intellij.autoImportOptionsProvider"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.application.options.editor.EditorOptionsProvider", "extensionPointNames": ["com.intellij.editorOptionsProvider"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.application.options.editor.InlineDocsAdditionalConfigurable", "extensionPointNames": ["com.intellij.editorAppearanceInlineDocHandler"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.roots.ui.configuration.LibrarySettingsProvider", "extensionPointNames": ["com.intellij.librarySettingsProvider"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.profile.codeInspection.ui.InspectionProfileActionProvider", "extensionPointNames": ["com.intellij.inspectionProfileActionProvider"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.profile.codeInspection.ui.InspectionTreeAdvertiser", "extensionPointNames": ["com.intellij.inspectionTreeAdvertiser"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.analysis.problemsView.toolWindow.HighlightingProblemFactory", "extensionPointNames": ["com.intellij.problemsViewHighlightingProblemFactory"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.find.impl.livePreview.EditorSearchAreaProvider", "extensionPointNames": ["com.intellij.editorSearchAreaProvider"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.intention.impl.IntentionPopupProvider", "extensionPointNames": ["com.intellij.intentionPopupProvider"], "targetModules": ["frontend"], "restrictionKind": "genericPlatformApi"}, - {"apiName": "com.intellij.openapi.vfs.VirtualFileManager", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.vfs.VfsUtil", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.psi.stubs.StubIndex", "extensionPointNames": ["com.intellij.stubIndex"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.psi.stubs.StubIndexExtension", "extensionPointNames": ["com.intellij.stubIndex"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.util.indexing.IndexExtension", "extensionPointNames": ["com.intellij.fileBasedIndex"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.util.indexing.FileBasedIndexExtension", "extensionPointNames": ["com.intellij.fileBasedIndex"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.configurations.ConfigurationType", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.psi.search.searches.ReferencesSearch", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.model.search.SearchService", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.util.Query", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.hints.InlayHintsProvider", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.hints.declarative.InlayHintsProvider", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.daemon.LineMarkerProvider", "extensionPointNames": ["com.intellij.codeInsight.lineMarkerProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInspection.LocalInspectionTool", "extensionPointNames": ["com.intellij.localInspection"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInspection.incorrectFormatting.IncorrectFormattingResultHandlerProvider", "extensionPointNames": ["com.intellij.resultHandlerProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.psi.PsiReferenceContributor", "extensionPointNames": ["com.intellij.psi.referenceContributor"], "targetModules": ["backend"]}, - {"apiName": "com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.actions.RunConfigurationProducer", "extensionPointNames": ["com.intellij.runConfigurationProducer"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.structureView.StructureViewBuilder", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.vfs.AsyncFileListener", "extensionPointNames": ["com.intellij.vfs.asyncListener", "com.intellij.vfs.asyncListenerBackgroundable"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.psi.ResolveScopeProvider", "extensionPointNames": ["com.intellij.resolveScopeProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.vfs.VirtualFileListener", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.platform.backend.workspace.WorkspaceModelChangeListener", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.project.ProjectManagerListener", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.process.ProcessHandler", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.actionSystem.CommonDataKeys.SYMBOLS", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.actionSystem.PlatformCoreDataKeys.MODULE", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.actionSystem.LangDataKeys.MODULE_CONTEXT", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.actionSystem.LangDataKeys.MODIFIABLE_MODULE_MODEL", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.completion.CompletionPreselectionBehaviourProvider", "extensionPointNames": ["com.intellij.completion.preselectionBehaviourProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.completion.ml.ContextFeatureProvider", "extensionPointNames": ["com.intellij.completion.ml.contextFeatures"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.internal.ml.completion.RankingModelProvider", "extensionPointNames": ["com.intellij.completion.ml.model"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.completion.ml.ElementFeatureProvider", "extensionPointNames": ["com.intellij.completion.ml.elementFeatures"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.completion.PlainTextSymbolCompletionContributor", "extensionPointNames": ["com.intellij.completion.plainTextSymbol"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.lang.WordCompletionElementFilter", "extensionPointNames": ["com.intellij.codeInsight.wordCompletionFilter"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.completion.ml.MLFeatureProvider", "extensionPointNames": ["com.intellij.internal.ml.featureProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.modcompletion.ModCompletionItemProvider", "extensionPointNames": ["com.intellij.modcompletion.completionItemProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.modcompletion.ModCompletionItemFilter", "extensionPointNames": ["com.intellij.modcompletion.completionItemFilter"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.completion.command.CommandCompletionFactory", "extensionPointNames": ["com.intellij.codeInsight.completion.command.factory"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.completion.command.CommandProvider", "extensionPointNames": ["com.intellij.codeInsight.completion.command.provider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.completion.command.commands.IntentionCommandSkipper", "extensionPointNames": ["com.intellij.codeInsight.completion.intention.skipper"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.completion.command.commands.ErrorFixCommandProvider", "extensionPointNames": ["com.intellij.codeInsight.completion.error.intention"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.completion.command.commands.IntentionCommandOffsetProvider", "extensionPointNames": ["com.intellij.codeInsight.completion.intention.offset.provider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.lang.refactoring.RefactoringSupportProvider", "extensionPointNames": ["com.intellij.lang.refactoringSupport"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.lang.refactoring.NamesValidator", "extensionPointNames": ["com.intellij.lang.namesValidator"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.rename.RenameHandler", "extensionPointNames": ["com.intellij.renameHandler"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.rename.RenamePsiElementProcessorBase", "extensionPointNames": ["com.intellij.renamePsiElementProcessor"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.psi.impl.file.UpdateAddedFileProcessor", "extensionPointNames": ["com.intellij.updateAddedFileProcessor"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.rename.naming.AutomaticRenamerFactory", "extensionPointNames": ["com.intellij.automaticRenamerFactory"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.rename.NameSuggestionProvider", "extensionPointNames": ["com.intellij.nameSuggestionProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.rename.RenamerFactory", "extensionPointNames": ["com.intellij.renamerFactory"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.RefactoringHelper", "extensionPointNames": ["com.intellij.refactoring.helper"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.lang.refactoring.InlineActionHandler", "extensionPointNames": ["com.intellij.inlineActionHandler"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.lang.refactoring.InlineHandler", "extensionPointNames": ["com.intellij.refactoring.inlineHandler"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.rename.ResolveSnapshotProvider", "extensionPointNames": ["com.intellij.rename.inplace.resolveSnapshotProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.rename.RenameInputValidator", "extensionPointNames": ["com.intellij.renameInputValidator"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.listeners.RefactoringElementListenerProvider", "extensionPointNames": ["com.intellij.refactoring.elementListenerProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.find.FindInProjectSearchEngine", "extensionPointNames": ["com.intellij.findInProjectSearchEngine"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.QualifiedNameProvider", "extensionPointNames": ["com.intellij.qualifiedNameProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.VirtualFileQualifiedNameProvider", "extensionPointNames": ["com.intellij.virtualFileQualifiedNameProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.roots.TestSourcesFilter", "extensionPointNames": ["com.intellij.testSourcesFilter"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.classMembers.ClassMembersRefactoringSupport", "extensionPointNames": ["com.intellij.lang.refactoringSupport.classMembersRefactoringSupport"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.copy.CopyHandlerDelegate", "extensionPointNames": ["com.intellij.refactoring.copyHandler"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.move.MoveHandlerDelegate", "extensionPointNames": ["com.intellij.refactoring.moveHandler"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.introduceParameterObject.IntroduceParameterObjectDelegate", "extensionPointNames": ["com.intellij.refactoring.introduceParameterObject"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.memberPushDown.PushDownDelegate", "extensionPointNames": ["com.intellij.refactoring.pushDown"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.move.moveClassesOrPackages.MoveDirectoryWithClassesHelper", "extensionPointNames": ["com.intellij.refactoring.moveDirectoryWithClassesHelper"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.safeDelete.SafeDeleteProcessorDelegate", "extensionPointNames": ["com.intellij.refactoring.safeDeleteProcessor"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.RefactoringActionHandler", "extensionPointNames": ["com.intellij.refactoring.extractIncludeHandler"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.changeSignature.ChangeSignatureUsageProcessor", "extensionPointNames": ["com.intellij.refactoring.changeSignatureUsageProcessor"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.move.moveFilesOrDirectories.MoveFileHandler", "extensionPointNames": ["com.intellij.moveFileHandler"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.rename.symbol.SymbolRenameTargetFactory", "extensionPointNames": ["com.intellij.rename.symbolRenameTargetFactory"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.actions.RenameFileActionProvider", "extensionPointNames": ["com.intellij.renameFileActionProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.changeSignature.inplace.LanguageChangeSignatureDetector", "extensionPointNames": ["com.intellij.changeSignatureDetector"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.suggested.SuggestedRefactoringSupport", "extensionPointNames": ["com.intellij.suggestedRefactoringSupport"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.changeSignature.ChangeSignatureUsageProvider", "extensionPointNames": ["com.intellij.changeSignature.usageProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeHighlighting.TextEditorHighlightingPassFactoryRegistrar", "extensionPointNames": ["com.intellij.highlightingPassFactory"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.RunConfigurationProducerSuppressor", "extensionPointNames": ["com.intellij.runConfigurationProducerSuppressor"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.psi.search.IndexPatternProvider", "extensionPointNames": ["com.intellij.indexPatternProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInspection.reference.RefGraphAnnotator", "extensionPointNames": ["com.intellij.refGraphAnnotator"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.daemon.impl.HighlightVisitor", "extensionPointNames": ["com.intellij.highlightVisitor"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.daemon.impl.analysis.ErrorQuickFixProvider", "extensionPointNames": ["com.intellij.errorQuickFixProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.paths.PathReferenceProvider", "extensionPointNames": ["com.intellij.pathReferenceProvider", "com.intellij.anchorReferenceProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.paths.DynamicContextProvider", "extensionPointNames": ["com.intellij.dynamicContextProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider", "extensionPointNames": ["com.intellij.codeInsight.unresolvedReferenceQuickFixProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.lang.ExternalAnnotatorsFilter", "extensionPointNames": ["com.intellij.daemon.externalAnnotatorsFilter"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.daemon.ChangeLocalityDetector", "extensionPointNames": ["com.intellij.daemon.changeLocalityDetector"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.daemon.ImplicitUsageProvider", "extensionPointNames": ["com.intellij.implicitUsageProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.lang.injection.ConcatenationAwareInjector", "extensionPointNames": ["com.intellij.concatenationAwareInjector"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.psi.injection.ReferenceInjector", "extensionPointNames": ["com.intellij.referenceInjector"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.lang.annotation.ExternalAnnotator", "extensionPointNames": ["com.intellij.externalAnnotator"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.lang.findUsages.FindUsagesProvider", "extensionPointNames": ["com.intellij.lang.findUsagesProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.lang.cacheBuilder.WordsScanner", "extensionPointNames": ["com.intellij.cacheBuilder"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.model.search.Searcher", "extensionPointNames": ["com.intellij.searcher"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.psi.search.SearchScopeProvider", "extensionPointNames": ["com.intellij.searchScopesProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.util.gotoByName.GotoActionAliasMatcher", "extensionPointNames": ["com.intellij.gotoActionAliasMatcher"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.navigation.ChooseByNameContributor", "extensionPointNames": ["com.intellij.gotoClassContributor", "com.intellij.gotoSymbolContributor", "com.intellij.gotoFileContributor"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.navigation.GotoRelatedProvider", "extensionPointNames": ["com.intellij.gotoRelatedProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.navigation.AnonymousElementProvider", "extensionPointNames": ["com.intellij.anonymousElementProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywhereContributorFactory", "extensionPointNames": ["com.intellij.searchEverywhereContributor"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.searcheverywhere.SEResultsEqualityProvider", "extensionPointNames": ["com.intellij.searchEverywhereResultsEqualityProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywhereMlService", "extensionPointNames": ["com.intellij.searchEverywhereMlService"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywhereMlContributorReplacement", "extensionPointNames": ["com.intellij.searchEverywhereMlContributorReplacement"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywhereSpellingCorrectorFactory", "extensionPointNames": ["com.intellij.searchEverywhereSpellingCorrector"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywhereEssentialContributorMarker", "extensionPointNames": ["com.intellij.searchEverywhereEssentialContributorsMarker"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.searcheverywhere.remote.RemoteSearchEverywhereConverterSupplier", "extensionPointNames": ["com.intellij.searchEverywhereRemoteConverter"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywhereReorderingService", "extensionPointNames": ["com.intellij.searchEverywhereReorderingService"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywherePreviewPrimaryUsageFinder", "extensionPointNames": ["com.intellij.searchEverywherePreviewPrimaryUsageFinder"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.SearchEverywhereManagerFactory", "extensionPointNames": ["com.intellij.searchEverywhere.manager"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.runAnything.activity.RunAnythingProvider", "extensionPointNames": ["com.intellij.runAnything.executionProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.runAnything.handlers.RunAnythingCommandHandler", "extensionPointNames": ["com.intellij.runAnything.commandHandler"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.runAnything.groups.RunAnythingHelpGroup", "extensionPointNames": ["com.intellij.runAnything.helpGroup"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.runAnything.commands.RunAnythingCommandCustomizer", "extensionPointNames": ["com.intellij.runAnything.commandCustomizer"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.highlighting.HighlightUsagesHandlerFactory", "extensionPointNames": ["com.intellij.highlightUsagesHandlerFactory"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.usages.UsageTargetProvider", "extensionPointNames": ["com.intellij.usageTargetProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.usages.UsageToPsiElementProvider", "extensionPointNames": ["com.intellij.usageToPsiElementProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.find.impl.FindInProjectExtension", "extensionPointNames": ["com.intellij.findInProjectExtension"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.model.psi.PsiSymbolDeclarationProvider", "extensionPointNames": ["com.intellij.psi.declarationProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.navigation.actions.TypeDeclarationProvider", "extensionPointNames": ["com.intellij.typeDeclarationProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.navigation.GotoTargetRendererProvider", "extensionPointNames": ["com.intellij.gotoTargetRendererProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.navigation.GotoTargetPresentationProvider", "extensionPointNames": ["com.intellij.gotoTargetPresentationProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.find.usages.symbol.SymbolSearchTargetFactory", "extensionPointNames": ["com.intellij.lang.symbolSearchTarget"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.find.findUsages.FindUsagesHandlerFactory", "extensionPointNames": ["com.intellij.findUsagesHandlerFactory"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.find.findUsages.CustomUsageSearcher", "extensionPointNames": ["com.intellij.customUsageSearcher"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.platform.ide.navigation.NavigationHandler", "extensionPointNames": ["com.intellij.navigation.navigationHandler"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.impl.RunConfigurationTemplateProvider", "extensionPointNames": ["com.intellij.runConfigurationTemplateProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.configurations.RunConfigurationsSettings", "extensionPointNames": ["com.intellij.runConfigurationsSettings"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.runners.ProgramRunner", "extensionPointNames": ["com.intellij.programRunner"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.task.ProjectTaskRunner", "extensionPointNames": ["com.intellij.projectTaskRunner"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.Executor", "extensionPointNames": ["com.intellij.executor"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.runToolbar.RunToolbarProcess", "extensionPointNames": ["com.intellij.runToolbarProcess"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.ExecutionTargetProvider", "extensionPointNames": ["com.intellij.executionTargetProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.BeforeRunTaskProvider", "extensionPointNames": ["com.intellij.stepsBeforeRunProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.impl.RunConfigurationBeforeRunProviderDelegate", "extensionPointNames": ["com.intellij.runConfigurationBeforeRunProviderDelegate"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.target.RunConfigurationTargetEnvironmentAdjuster$Factory", "extensionPointNames": ["com.intellij.runConfigurationTargetEnvironmentAdjusterFactory"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.target.TargetEnvironmentType", "extensionPointNames": ["com.intellij.executionTargetType"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.target.LanguageRuntimeType", "extensionPointNames": ["com.intellij.executionTargetLanguageRuntimeType"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.filters.ConsoleFilterProvider", "extensionPointNames": ["com.intellij.consoleFilterProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.filters.ConsoleInputFilterProvider", "extensionPointNames": ["com.intellij.consoleInputFilterProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.actions.ConsoleActionsPostProcessor", "extensionPointNames": ["com.intellij.consoleActionsPostProcessor"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.junit.RuntimeConfigurationProducer", "extensionPointNames": ["com.intellij.configurationProducer"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.lineMarker.RunLineMarkerContributor", "extensionPointNames": ["com.intellij.runLineMarkerContributor"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.actions.MultipleRunLocationsProvider", "extensionPointNames": ["com.intellij.multipleRunLocationsProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.dashboard.RunDashboardCustomizer", "extensionPointNames": ["com.intellij.runDashboardCustomizer"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.dashboard.LegacyRunDashboardServiceSubstitutor", "extensionPointNames": ["com.intellij.legacyRunDashboardServiceSubstitutor"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.dashboard.RunDashboardDefaultTypesProvider", "extensionPointNames": ["com.intellij.runDashboardDefaultTypesProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.navigation.MethodNavigationOffsetProvider", "extensionPointNames": ["com.intellij.methodNavigationOffsetProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.testIntegration.TestFinder", "extensionPointNames": ["com.intellij.testFinder"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.testIntegration.TestLocationProvider", "extensionPointNames": ["com.intellij.testSrcLocator"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.testIntegration.TestCreator", "extensionPointNames": ["com.intellij.testCreator"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.hierarchy.HierarchyProvider", "extensionPointNames": ["com.intellij.callHierarchyProvider", "com.intellij.methodHierarchyProvider", "com.intellij.typeHierarchyProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.TargetElementUtilExtender", "extensionPointNames": ["com.intellij.targetElementUtilExtender"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.TargetElementEvaluator", "extensionPointNames": ["com.intellij.targetElementEvaluator"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.psi.impl.cache.impl.id.IdIndexer", "extensionPointNames": ["com.intellij.idIndexer"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.daemon.ProblemHighlightFilter", "extensionPointNames": ["com.intellij.problemHighlightFilter"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.filters.Filter", "extensionPointNames": ["com.intellij.analyzeStacktraceFilter"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.unscramble.StacktraceTabContentProvider", "extensionPointNames": ["com.intellij.analyzeStacktraceRunContentProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.actions.SearchEverywhereClassifier", "extensionPointNames": ["com.intellij.searchEverywhereClassifier"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.util.gotoByName.GotoFileCustomizer", "extensionPointNames": ["com.intellij.gotoFileCustomizer"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.util.fuzzyMatching.GotoFileFuzzyMatcherFactory", "extensionPointNames": ["com.intellij.gotoFileFuzzyMatcherFactory"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.slicer.SliceLanguageSupportProvider", "extensionPointNames": ["com.intellij.lang.sliceProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.console.ConsoleHistoryModelProvider", "extensionPointNames": ["com.intellij.consoleHistoryModelProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.execution.update.RunningApplicationUpdaterProvider", "extensionPointNames": ["com.intellij.runningApplicationUpdaterProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.hint.ImplementationViewSessionFactory", "extensionPointNames": ["com.intellij.implementationViewSessionFactory"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.hint.ImplementationViewDocumentFactory", "extensionPointNames": ["com.intellij.implementationViewDocumentFactory"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.platform.backend.documentation.DocumentationTargetProvider", "extensionPointNames": ["com.intellij.platform.backend.documentation.targetProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.platform.backend.documentation.SymbolDocumentationTargetProvider", "extensionPointNames": ["com.intellij.platform.backend.documentation.symbolTargetProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.platform.backend.documentation.PsiDocumentationTargetProvider", "extensionPointNames": ["com.intellij.platform.backend.documentation.psiTargetProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.platform.backend.documentation.LookupElementDocumentationTargetProvider", "extensionPointNames": ["com.intellij.platform.backend.documentation.lookupElementTargetProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.platform.backend.documentation.InlineDocumentationProvider", "extensionPointNames": ["com.intellij.platform.backend.documentation.inlineDocumentationProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.platform.backend.documentation.DocumentationLinkHandler", "extensionPointNames": ["com.intellij.platform.backend.documentation.linkHandler"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.refactoring.safeDelete.api.SafeDeleteTargetProvider", "extensionPointNames": ["com.intellij.safeDeleteTargetProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.navigation.DirectNavigationProvider", "extensionPointNames": ["com.intellij.lang.directNavigationProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.codeInsight.navigation.SymbolTypeProvider", "extensionPointNames": ["com.intellij.lang.symbolTypeProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.application.ApplicationListener", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.project.ProjectListener", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.project.ProjectCloseListener", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.openapi.project.VetoableProjectManagerListener", "extensionPointNames": [], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.projectView.TreeStructureProvider", "extensionPointNames": ["com.intellij.treeStructureProvider"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.projectView.impl.AbstractProjectViewPane", "extensionPointNames": ["com.intellij.projectViewPane"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.projectView.impl.ProjectViewPaneSelectionHelper", "extensionPointNames": ["com.intellij.projectViewPaneSelectionHelper"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.projectView.ProjectViewNodeDecorator", "extensionPointNames": ["com.intellij.projectViewNodeDecorator"], "targetModules": ["backend"]}, - {"apiName": "com.intellij.ide.projectView.ProjectViewNestingRulesProvider", "extensionPointNames": ["com.intellij.projectViewNestingRulesProvider"], "targetModules": ["backend"]}, + // backend generic apis + {"apiName": "com.intellij.openapi.vfs.VirtualFileManager", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.vfs.VfsUtil", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.roots.FileIndex", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.roots.ModuleFileIndex", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.roots.ProjectFileIndex", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.stubs.StubIndex", "extensionPointNames": ["com.intellij.stubIndex"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.stubs.StubIndexExtension", "extensionPointNames": ["com.intellij.stubIndex"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.util.indexing.IndexExtension", "extensionPointNames": ["com.intellij.fileBasedIndex"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.util.indexing.FileBasedIndexExtension", "extensionPointNames": ["com.intellij.fileBasedIndex"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.configurations.ConfigurationType", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.search.searches.ReferencesSearch", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.model.search.SearchService", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.util.Query", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.hints.InlayHintsProvider", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.hints.declarative.InlayHintsProvider", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.daemon.LineMarkerProvider", "extensionPointNames": ["com.intellij.codeInsight.lineMarkerProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInspection.LocalInspectionTool", "extensionPointNames": ["com.intellij.localInspection"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInspection.incorrectFormatting.IncorrectFormattingResultHandlerProvider", "extensionPointNames": ["com.intellij.resultHandlerProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.PsiReferenceContributor", "extensionPointNames": ["com.intellij.psi.referenceContributor"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.actions.RunConfigurationProducer", "extensionPointNames": ["com.intellij.runConfigurationProducer"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.structureView.StructureViewBuilder", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.vfs.AsyncFileListener", "extensionPointNames": ["com.intellij.vfs.asyncListener", "com.intellij.vfs.asyncListenerBackgroundable"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.ResolveScopeProvider", "extensionPointNames": ["com.intellij.resolveScopeProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.vfs.VirtualFileListener", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.platform.backend.workspace.WorkspaceModelChangeListener", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.project.ProjectManagerListener", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.process.ProcessHandler", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.actionSystem.CommonDataKeys.SYMBOLS", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.actionSystem.PlatformCoreDataKeys.MODULE", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.actionSystem.LangDataKeys.MODULE_CONTEXT", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.actionSystem.LangDataKeys.MODIFIABLE_MODULE_MODEL", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.completion.CompletionPreselectionBehaviourProvider", "extensionPointNames": ["com.intellij.completion.preselectionBehaviourProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.completion.ml.ContextFeatureProvider", "extensionPointNames": ["com.intellij.completion.ml.contextFeatures"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.internal.ml.completion.RankingModelProvider", "extensionPointNames": ["com.intellij.completion.ml.model"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.completion.ml.ElementFeatureProvider", "extensionPointNames": ["com.intellij.completion.ml.elementFeatures"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.completion.PlainTextSymbolCompletionContributor", "extensionPointNames": ["com.intellij.completion.plainTextSymbol"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.WordCompletionElementFilter", "extensionPointNames": ["com.intellij.codeInsight.wordCompletionFilter"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.completion.ml.MLFeatureProvider", "extensionPointNames": ["com.intellij.internal.ml.featureProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.modcompletion.ModCompletionItemProvider", "extensionPointNames": ["com.intellij.modcompletion.completionItemProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.modcompletion.ModCompletionItemFilter", "extensionPointNames": ["com.intellij.modcompletion.completionItemFilter"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.completion.command.CommandCompletionFactory", "extensionPointNames": ["com.intellij.codeInsight.completion.command.factory"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.completion.command.CommandProvider", "extensionPointNames": ["com.intellij.codeInsight.completion.command.provider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.completion.command.commands.IntentionCommandSkipper", "extensionPointNames": ["com.intellij.codeInsight.completion.intention.skipper"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.completion.command.commands.ErrorFixCommandProvider", "extensionPointNames": ["com.intellij.codeInsight.completion.error.intention"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.completion.command.commands.IntentionCommandOffsetProvider", "extensionPointNames": ["com.intellij.codeInsight.completion.intention.offset.provider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.refactoring.RefactoringSupportProvider", "extensionPointNames": ["com.intellij.lang.refactoringSupport"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.refactoring.NamesValidator", "extensionPointNames": ["com.intellij.lang.namesValidator"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.rename.RenameHandler", "extensionPointNames": ["com.intellij.renameHandler"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.rename.RenamePsiElementProcessorBase", "extensionPointNames": ["com.intellij.renamePsiElementProcessor"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.impl.file.UpdateAddedFileProcessor", "extensionPointNames": ["com.intellij.updateAddedFileProcessor"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.rename.naming.AutomaticRenamerFactory", "extensionPointNames": ["com.intellij.automaticRenamerFactory"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.rename.NameSuggestionProvider", "extensionPointNames": ["com.intellij.nameSuggestionProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.rename.RenamerFactory", "extensionPointNames": ["com.intellij.renamerFactory"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.RefactoringHelper", "extensionPointNames": ["com.intellij.refactoring.helper"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.refactoring.InlineActionHandler", "extensionPointNames": ["com.intellij.inlineActionHandler"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.refactoring.InlineHandler", "extensionPointNames": ["com.intellij.refactoring.inlineHandler"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.rename.ResolveSnapshotProvider", "extensionPointNames": ["com.intellij.rename.inplace.resolveSnapshotProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.rename.RenameInputValidator", "extensionPointNames": ["com.intellij.renameInputValidator"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.listeners.RefactoringElementListenerProvider", "extensionPointNames": ["com.intellij.refactoring.elementListenerProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.find.FindInProjectSearchEngine", "extensionPointNames": ["com.intellij.findInProjectSearchEngine"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.QualifiedNameProvider", "extensionPointNames": ["com.intellij.qualifiedNameProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.VirtualFileQualifiedNameProvider", "extensionPointNames": ["com.intellij.virtualFileQualifiedNameProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.roots.TestSourcesFilter", "extensionPointNames": ["com.intellij.testSourcesFilter"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.classMembers.ClassMembersRefactoringSupport", "extensionPointNames": ["com.intellij.lang.refactoringSupport.classMembersRefactoringSupport"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.copy.CopyHandlerDelegate", "extensionPointNames": ["com.intellij.refactoring.copyHandler"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.move.MoveHandlerDelegate", "extensionPointNames": ["com.intellij.refactoring.moveHandler"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.introduceParameterObject.IntroduceParameterObjectDelegate", "extensionPointNames": ["com.intellij.refactoring.introduceParameterObject"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.memberPushDown.PushDownDelegate", "extensionPointNames": ["com.intellij.refactoring.pushDown"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.move.moveClassesOrPackages.MoveDirectoryWithClassesHelper", "extensionPointNames": ["com.intellij.refactoring.moveDirectoryWithClassesHelper"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.safeDelete.SafeDeleteProcessorDelegate", "extensionPointNames": ["com.intellij.refactoring.safeDeleteProcessor"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.RefactoringActionHandler", "extensionPointNames": ["com.intellij.refactoring.extractIncludeHandler"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.changeSignature.ChangeSignatureUsageProcessor", "extensionPointNames": ["com.intellij.refactoring.changeSignatureUsageProcessor"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.move.moveFilesOrDirectories.MoveFileHandler", "extensionPointNames": ["com.intellij.moveFileHandler"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.rename.symbol.SymbolRenameTargetFactory", "extensionPointNames": ["com.intellij.rename.symbolRenameTargetFactory"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.actions.RenameFileActionProvider", "extensionPointNames": ["com.intellij.renameFileActionProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.changeSignature.inplace.LanguageChangeSignatureDetector", "extensionPointNames": ["com.intellij.changeSignatureDetector"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.suggested.SuggestedRefactoringSupport", "extensionPointNames": ["com.intellij.suggestedRefactoringSupport"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.changeSignature.ChangeSignatureUsageProvider", "extensionPointNames": ["com.intellij.changeSignature.usageProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeHighlighting.TextEditorHighlightingPassFactoryRegistrar", "extensionPointNames": ["com.intellij.highlightingPassFactory"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.RunConfigurationProducerSuppressor", "extensionPointNames": ["com.intellij.runConfigurationProducerSuppressor"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.search.IndexPatternProvider", "extensionPointNames": ["com.intellij.indexPatternProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInspection.reference.RefGraphAnnotator", "extensionPointNames": ["com.intellij.refGraphAnnotator"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.daemon.impl.HighlightVisitor", "extensionPointNames": ["com.intellij.highlightVisitor"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.daemon.impl.analysis.ErrorQuickFixProvider", "extensionPointNames": ["com.intellij.errorQuickFixProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.paths.PathReferenceProvider", "extensionPointNames": ["com.intellij.pathReferenceProvider", "com.intellij.anchorReferenceProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.paths.DynamicContextProvider", "extensionPointNames": ["com.intellij.dynamicContextProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider", "extensionPointNames": ["com.intellij.codeInsight.unresolvedReferenceQuickFixProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.ExternalAnnotatorsFilter", "extensionPointNames": ["com.intellij.daemon.externalAnnotatorsFilter"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.daemon.ChangeLocalityDetector", "extensionPointNames": ["com.intellij.daemon.changeLocalityDetector"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.daemon.ImplicitUsageProvider", "extensionPointNames": ["com.intellij.implicitUsageProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.injection.ConcatenationAwareInjector", "extensionPointNames": ["com.intellij.concatenationAwareInjector"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.injection.ReferenceInjector", "extensionPointNames": ["com.intellij.referenceInjector"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.annotation.ExternalAnnotator", "extensionPointNames": ["com.intellij.externalAnnotator"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.findUsages.FindUsagesProvider", "extensionPointNames": ["com.intellij.lang.findUsagesProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.cacheBuilder.WordsScanner", "extensionPointNames": ["com.intellij.cacheBuilder"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.model.search.Searcher", "extensionPointNames": ["com.intellij.searcher"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.search.SearchScopeProvider", "extensionPointNames": ["com.intellij.searchScopesProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.util.gotoByName.GotoActionAliasMatcher", "extensionPointNames": ["com.intellij.gotoActionAliasMatcher"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.navigation.ChooseByNameContributor", "extensionPointNames": ["com.intellij.gotoClassContributor", "com.intellij.gotoSymbolContributor", "com.intellij.gotoFileContributor"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.navigation.GotoRelatedProvider", "extensionPointNames": ["com.intellij.gotoRelatedProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.navigation.AnonymousElementProvider", "extensionPointNames": ["com.intellij.anonymousElementProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywhereContributorFactory", "extensionPointNames": ["com.intellij.searchEverywhereContributor"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.searcheverywhere.SEResultsEqualityProvider", "extensionPointNames": ["com.intellij.searchEverywhereResultsEqualityProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywhereMlService", "extensionPointNames": ["com.intellij.searchEverywhereMlService"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywhereMlContributorReplacement", "extensionPointNames": ["com.intellij.searchEverywhereMlContributorReplacement"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywhereSpellingCorrectorFactory", "extensionPointNames": ["com.intellij.searchEverywhereSpellingCorrector"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywhereEssentialContributorMarker", "extensionPointNames": ["com.intellij.searchEverywhereEssentialContributorsMarker"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.searcheverywhere.remote.RemoteSearchEverywhereConverterSupplier", "extensionPointNames": ["com.intellij.searchEverywhereRemoteConverter"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywhereReorderingService", "extensionPointNames": ["com.intellij.searchEverywhereReorderingService"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.searcheverywhere.SearchEverywherePreviewPrimaryUsageFinder", "extensionPointNames": ["com.intellij.searchEverywherePreviewPrimaryUsageFinder"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.SearchEverywhereManagerFactory", "extensionPointNames": ["com.intellij.searchEverywhere.manager"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.runAnything.activity.RunAnythingProvider", "extensionPointNames": ["com.intellij.runAnything.executionProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.runAnything.handlers.RunAnythingCommandHandler", "extensionPointNames": ["com.intellij.runAnything.commandHandler"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.runAnything.groups.RunAnythingHelpGroup", "extensionPointNames": ["com.intellij.runAnything.helpGroup"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.runAnything.commands.RunAnythingCommandCustomizer", "extensionPointNames": ["com.intellij.runAnything.commandCustomizer"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.highlighting.HighlightUsagesHandlerFactory", "extensionPointNames": ["com.intellij.highlightUsagesHandlerFactory"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.usages.UsageTargetProvider", "extensionPointNames": ["com.intellij.usageTargetProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.usages.UsageToPsiElementProvider", "extensionPointNames": ["com.intellij.usageToPsiElementProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.find.impl.FindInProjectExtension", "extensionPointNames": ["com.intellij.findInProjectExtension"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.model.psi.PsiSymbolDeclarationProvider", "extensionPointNames": ["com.intellij.psi.declarationProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.navigation.actions.TypeDeclarationProvider", "extensionPointNames": ["com.intellij.typeDeclarationProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.navigation.GotoTargetRendererProvider", "extensionPointNames": ["com.intellij.gotoTargetRendererProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.navigation.GotoTargetPresentationProvider", "extensionPointNames": ["com.intellij.gotoTargetPresentationProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.find.usages.symbol.SymbolSearchTargetFactory", "extensionPointNames": ["com.intellij.lang.symbolSearchTarget"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.find.findUsages.FindUsagesHandlerFactory", "extensionPointNames": ["com.intellij.findUsagesHandlerFactory"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.find.findUsages.CustomUsageSearcher", "extensionPointNames": ["com.intellij.customUsageSearcher"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.platform.ide.navigation.NavigationHandler", "extensionPointNames": ["com.intellij.navigation.navigationHandler"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.impl.RunConfigurationTemplateProvider", "extensionPointNames": ["com.intellij.runConfigurationTemplateProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.configurations.RunConfigurationsSettings", "extensionPointNames": ["com.intellij.runConfigurationsSettings"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.runners.ProgramRunner", "extensionPointNames": ["com.intellij.programRunner"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.task.ProjectTaskRunner", "extensionPointNames": ["com.intellij.projectTaskRunner"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.Executor", "extensionPointNames": ["com.intellij.executor"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.runToolbar.RunToolbarProcess", "extensionPointNames": ["com.intellij.runToolbarProcess"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.ExecutionTargetProvider", "extensionPointNames": ["com.intellij.executionTargetProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.BeforeRunTaskProvider", "extensionPointNames": ["com.intellij.stepsBeforeRunProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.impl.RunConfigurationBeforeRunProviderDelegate", "extensionPointNames": ["com.intellij.runConfigurationBeforeRunProviderDelegate"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.target.RunConfigurationTargetEnvironmentAdjuster$Factory", "extensionPointNames": ["com.intellij.runConfigurationTargetEnvironmentAdjusterFactory"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.target.TargetEnvironmentType", "extensionPointNames": ["com.intellij.executionTargetType"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.target.LanguageRuntimeType", "extensionPointNames": ["com.intellij.executionTargetLanguageRuntimeType"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.filters.ConsoleFilterProvider", "extensionPointNames": ["com.intellij.consoleFilterProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.filters.ConsoleInputFilterProvider", "extensionPointNames": ["com.intellij.consoleInputFilterProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.actions.ConsoleActionsPostProcessor", "extensionPointNames": ["com.intellij.consoleActionsPostProcessor"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.junit.RuntimeConfigurationProducer", "extensionPointNames": ["com.intellij.configurationProducer"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.lineMarker.RunLineMarkerContributor", "extensionPointNames": ["com.intellij.runLineMarkerContributor"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.actions.MultipleRunLocationsProvider", "extensionPointNames": ["com.intellij.multipleRunLocationsProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.dashboard.RunDashboardCustomizer", "extensionPointNames": ["com.intellij.runDashboardCustomizer"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.dashboard.LegacyRunDashboardServiceSubstitutor", "extensionPointNames": ["com.intellij.legacyRunDashboardServiceSubstitutor"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.dashboard.RunDashboardDefaultTypesProvider", "extensionPointNames": ["com.intellij.runDashboardDefaultTypesProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.navigation.MethodNavigationOffsetProvider", "extensionPointNames": ["com.intellij.methodNavigationOffsetProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.testIntegration.TestFinder", "extensionPointNames": ["com.intellij.testFinder"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.testIntegration.TestLocationProvider", "extensionPointNames": ["com.intellij.testSrcLocator"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.testIntegration.TestCreator", "extensionPointNames": ["com.intellij.testCreator"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.hierarchy.HierarchyProvider", "extensionPointNames": ["com.intellij.callHierarchyProvider", "com.intellij.methodHierarchyProvider", "com.intellij.typeHierarchyProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.TargetElementUtilExtender", "extensionPointNames": ["com.intellij.targetElementUtilExtender"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.TargetElementEvaluator", "extensionPointNames": ["com.intellij.targetElementEvaluator"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.impl.cache.impl.id.IdIndexer", "extensionPointNames": ["com.intellij.idIndexer"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.daemon.ProblemHighlightFilter", "extensionPointNames": ["com.intellij.problemHighlightFilter"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.filters.Filter", "extensionPointNames": ["com.intellij.analyzeStacktraceFilter"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.unscramble.StacktraceTabContentProvider", "extensionPointNames": ["com.intellij.analyzeStacktraceRunContentProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.actions.SearchEverywhereClassifier", "extensionPointNames": ["com.intellij.searchEverywhereClassifier"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.util.gotoByName.GotoFileCustomizer", "extensionPointNames": ["com.intellij.gotoFileCustomizer"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.util.fuzzyMatching.GotoFileFuzzyMatcherFactory", "extensionPointNames": ["com.intellij.gotoFileFuzzyMatcherFactory"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.slicer.SliceLanguageSupportProvider", "extensionPointNames": ["com.intellij.lang.sliceProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.console.ConsoleHistoryModelProvider", "extensionPointNames": ["com.intellij.consoleHistoryModelProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.execution.update.RunningApplicationUpdaterProvider", "extensionPointNames": ["com.intellij.runningApplicationUpdaterProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.hint.ImplementationViewSessionFactory", "extensionPointNames": ["com.intellij.implementationViewSessionFactory"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.hint.ImplementationViewDocumentFactory", "extensionPointNames": ["com.intellij.implementationViewDocumentFactory"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.platform.backend.documentation.DocumentationTargetProvider", "extensionPointNames": ["com.intellij.platform.backend.documentation.targetProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.platform.backend.documentation.SymbolDocumentationTargetProvider", "extensionPointNames": ["com.intellij.platform.backend.documentation.symbolTargetProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.platform.backend.documentation.PsiDocumentationTargetProvider", "extensionPointNames": ["com.intellij.platform.backend.documentation.psiTargetProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.platform.backend.documentation.LookupElementDocumentationTargetProvider", "extensionPointNames": ["com.intellij.platform.backend.documentation.lookupElementTargetProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.platform.backend.documentation.InlineDocumentationProvider", "extensionPointNames": ["com.intellij.platform.backend.documentation.inlineDocumentationProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.platform.backend.documentation.DocumentationLinkHandler", "extensionPointNames": ["com.intellij.platform.backend.documentation.linkHandler"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.refactoring.safeDelete.api.SafeDeleteTargetProvider", "extensionPointNames": ["com.intellij.safeDeleteTargetProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.navigation.DirectNavigationProvider", "extensionPointNames": ["com.intellij.lang.directNavigationProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.navigation.SymbolTypeProvider", "extensionPointNames": ["com.intellij.lang.symbolTypeProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.application.ApplicationListener", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.project.ProjectListener", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.project.ProjectCloseListener", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.project.VetoableProjectManagerListener", "extensionPointNames": [], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.projectView.TreeStructureProvider", "extensionPointNames": ["com.intellij.treeStructureProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.projectView.impl.AbstractProjectViewPane", "extensionPointNames": ["com.intellij.projectViewPane"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.projectView.impl.ProjectViewPaneSelectionHelper", "extensionPointNames": ["com.intellij.projectViewPaneSelectionHelper"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.projectView.ProjectViewNodeDecorator", "extensionPointNames": ["com.intellij.projectViewNodeDecorator"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.projectView.ProjectViewNestingRulesProvider", "extensionPointNames": ["com.intellij.projectViewNestingRulesProvider"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.intention.IntentionAction", "extensionPointNames": ["com.intellij.intentionAction"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.editor.actions.FlipCommaIntention.Flipper", "extensionPointNames": ["com.intellij.flipCommaIntention.flipper"], "targetModules": ["backend"], "restrictionKind": "genericPlatformApi"}, - {"apiName": "com.intellij.lang.ParserDefinition", "extensionPointNames": ["com.intellij.lang.parserDefinition"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.psi.FileViewProviderFactory", "extensionPointNames": ["com.intellij.lang.fileViewProviderFactory"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.lang.ASTFactory", "extensionPointNames": ["com.intellij.lang.ast.factory", "lang.ast.factory"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.lang.PsiParser", "extensionPointNames": [], "targetModules": ["shared"]}, - {"apiName": "com.intellij.lexer.Lexer", "extensionPointNames": [], "targetModules": ["shared"]}, - {"apiName": "com.intellij.psi.LanguageInjector", "extensionPointNames": ["com.intellij.languageInjector"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.lang.injection.MultiHostInjector", "extensionPointNames": ["com.intellij.multiHostInjector"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.psi.ElementDescriptionProvider", "extensionPointNames": ["com.intellij.elementDescriptionProvider"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.psi.ElementManipulator", "extensionPointNames": ["com.intellij.lang.elementManipulator"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.openapi.editor.bidi.BidiRegionsSeparator", "extensionPointNames": ["com.intellij.bidiRegionsSeparator"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.openapi.editor.actions.WordBoundaryFilter", "extensionPointNames": ["com.intellij.wordBoundaryFilter"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.openapi.options.colors.ColorAndFontDescriptorsProvider", "extensionPointNames": ["com.intellij.colorAndFontDescriptorProvider"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.application.options.GeneralCodeStyleOptionsProvider", "extensionPointNames": ["com.intellij.generalCodeStyleOptionsProvider"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.psi.codeStyle.FileTypeIndentOptionsProvider", "extensionPointNames": ["com.intellij.fileTypeIndentOptionsProvider"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider", "extensionPointNames": ["com.intellij.langCodeStyleSettingsProvider"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.psi.codeStyle.LanguageCodeStyleSettingsContributor", "extensionPointNames": ["com.intellij.langCodeStyleSettingsContributor"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.psi.codeStyle.PredefinedCodeStyle", "extensionPointNames": ["com.intellij.predefinedCodeStyle"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.psi.templateLanguages.MultipleLangCommentProvider", "extensionPointNames": ["com.intellij.multiLangCommenter"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.psi.codeStyle.arrangement.Rearranger", "extensionPointNames": ["com.intellij.lang.rearranger"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.SelectionQuotingTypedHandler.UnquotingFilter", "extensionPointNames": ["com.intellij.selectionUnquotingFilter"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.openapi.editor.actions.FlipCommaIntention.Flipper", "extensionPointNames": ["com.intellij.flipCommaIntention.flipper"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.lang.Commenter", "extensionPointNames": ["com.intellij.lang.commenter"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.QuoteHandler", "extensionPointNames": ["com.intellij.quoteHandler", "com.intellij.lang.quoteHandler"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.TypedHandlerDelegate", "extensionPointNames": ["com.intellij.typedHandler"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.moveUpDown.StatementUpDownMover", "extensionPointNames": ["com.intellij.statementUpDownMover"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.formatting.InjectedFormattingOptionsProvider", "extensionPointNames": ["com.intellij.formatting.injectedOptions"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.lang.folding.FoldingBuilder", "extensionPointNames": ["com.intellij.lang.foldingBuilder"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.openapi.editor.IndentStrategy", "extensionPointNames": ["com.intellij.lang.indentStrategy"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.psi.codeStyle.lineIndent.LineIndentProvider", "extensionPointNames": ["com.intellij.lineIndentProvider"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.ExtendWordSelectionHandler", "extensionPointNames": ["com.intellij.extendWordSelectionHandler"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.moveLeftRight.MoveElementLeftRightHandler", "extensionPointNames": ["com.intellij.moveLeftRightHandler"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.JoinLinesHandlerDelegate", "extensionPointNames": ["com.intellij.joinLinesHandler"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.BackspaceModeOverride", "extensionPointNames": ["com.intellij.editor.backspaceModeOverride"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.TypingActionsExtension", "extensionPointNames": ["com.intellij.typingActionsExtension"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.BackspaceHandlerDelegate", "extensionPointNames": ["com.intellij.backspaceHandlerDelegate"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.CodeBlockProvider", "extensionPointNames": ["com.intellij.codeBlockProvider"], "targetModules": ["shared"]}, - {"apiName": "com.intellij.codeInsight.highlighting.CodeBlockSupportHandler", "extensionPointNames": ["com.intellij.codeBlockSupportHandler"], "targetModules": ["shared"]}, + // shared generic apis + {"apiName": "com.intellij.lang.ParserDefinition", "extensionPointNames": ["com.intellij.lang.parserDefinition"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.FileViewProviderFactory", "extensionPointNames": ["com.intellij.lang.fileViewProviderFactory"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.ASTFactory", "extensionPointNames": ["com.intellij.lang.ast.factory", "lang.ast.factory"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.PsiParser", "extensionPointNames": [], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lexer.Lexer", "extensionPointNames": [], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.LanguageInjector", "extensionPointNames": ["com.intellij.languageInjector"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.injection.MultiHostInjector", "extensionPointNames": ["com.intellij.multiHostInjector"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.ElementManipulator", "extensionPointNames": ["com.intellij.lang.elementManipulator"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.editor.bidi.BidiRegionsSeparator", "extensionPointNames": ["com.intellij.bidiRegionsSeparator"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.editor.actions.WordBoundaryFilter", "extensionPointNames": ["com.intellij.wordBoundaryFilter"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.editor.LineWrapPositionStrategy", "extensionPointNames": ["com.intellij.lang.lineWrapStrategy"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.options.colors.ColorAndFontDescriptorsProvider", "extensionPointNames": ["com.intellij.colorAndFontDescriptorProvider"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.application.options.GeneralCodeStyleOptionsProvider", "extensionPointNames": ["com.intellij.generalCodeStyleOptionsProvider"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.codeStyle.FileTypeIndentOptionsProvider", "extensionPointNames": ["com.intellij.fileTypeIndentOptionsProvider"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider", "extensionPointNames": ["com.intellij.langCodeStyleSettingsProvider"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.codeStyle.LanguageCodeStyleSettingsContributor", "extensionPointNames": ["com.intellij.langCodeStyleSettingsContributor"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.codeStyle.PredefinedCodeStyle", "extensionPointNames": ["com.intellij.predefinedCodeStyle"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.templateLanguages.MultipleLangCommentProvider", "extensionPointNames": ["com.intellij.multiLangCommenter"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.codeStyle.arrangement.Rearranger", "extensionPointNames": ["com.intellij.lang.rearranger"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.SelectionQuotingTypedHandler.UnquotingFilter", "extensionPointNames": ["com.intellij.selectionUnquotingFilter"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.Commenter", "extensionPointNames": ["com.intellij.lang.commenter"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.QuoteHandler", "extensionPointNames": ["com.intellij.quoteHandler", "com.intellij.lang.quoteHandler"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.TypedHandlerDelegate", "extensionPointNames": ["com.intellij.typedHandler"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.moveUpDown.StatementUpDownMover", "extensionPointNames": ["com.intellij.statementUpDownMover"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.formatting.InjectedFormattingOptionsProvider", "extensionPointNames": ["com.intellij.formatting.injectedOptions"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.folding.FoldingBuilder", "extensionPointNames": ["com.intellij.lang.foldingBuilder"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.openapi.editor.IndentStrategy", "extensionPointNames": ["com.intellij.lang.indentStrategy"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.codeStyle.lineIndent.LineIndentProvider", "extensionPointNames": ["com.intellij.lineIndentProvider"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.ExtendWordSelectionHandler", "extensionPointNames": ["com.intellij.extendWordSelectionHandler"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.moveLeftRight.MoveElementLeftRightHandler", "extensionPointNames": ["com.intellij.moveLeftRightHandler"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.JoinLinesHandlerDelegate", "extensionPointNames": ["com.intellij.joinLinesHandler"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.BackspaceModeOverride", "extensionPointNames": ["com.intellij.editor.backspaceModeOverride"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.TypingActionsExtension", "extensionPointNames": ["com.intellij.typingActionsExtension"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.BackspaceHandlerDelegate", "extensionPointNames": ["com.intellij.backspaceHandlerDelegate"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.CodeBlockProvider", "extensionPointNames": ["com.intellij.codeBlockProvider"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.highlighting.CodeBlockSupportHandler", "extensionPointNames": ["com.intellij.codeBlockSupportHandler"], "targetModules": ["shared"], "restrictionKind": "genericPlatformApi"}, - {"apiName": "com.intellij.codeInsight.editorActions.CopyPastePostProcessor", "extensionPointNames": ["com.intellij.copyPastePostProcessor"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.CopyPastePreProcessor", "extensionPointNames": ["com.intellij.copyPastePreProcessor"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.PreserveIndentOnPasteBean", "extensionPointNames": ["com.intellij.preserveIndentOnPaste"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.psi.impl.source.codeStyle.PreFormatProcessor", "extensionPointNames": ["com.intellij.preFormatProcessor"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.psi.impl.source.codeStyle.PostFormatProcessor", "extensionPointNames": ["com.intellij.postFormatProcessor"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.lang.CustomAutoFormatSyntaxErrorsVerifier", "extensionPointNames": ["com.intellij.lang.formatter.syntaxErrorsVerifier"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.formatting.service.FormattingService", "extensionPointNames": ["com.intellij.formattingService"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.ide.PasteProvider", "extensionPointNames": ["com.intellij.customPasteProvider"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.formatting.FormattingModelBuilder", "extensionPointNames": ["com.intellij.lang.formatter"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.lang.LanguageFormattingRestriction", "extensionPointNames": ["com.intellij.lang.formatter.restriction"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.psi.formatter.WhiteSpaceFormattingStrategy", "extensionPointNames": ["com.intellij.lang.whiteSpaceFormattingStrategy"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.enter.EnterBetweenBracesDelegate", "extensionPointNames": ["com.intellij.enterBetweenBracesDelegate"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegate", "extensionPointNames": ["com.intellij.enterHandlerDelegate"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegateAdapter", "extensionPointNames": ["com.intellij.enterHandlerDelegate"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.codeInsight.editorActions.smartEnter.SmartEnterProcessor", "extensionPointNames": ["com.intellij.lang.smartEnterProcessor"], "targetModules": ["backend", "shared"]}, - {"apiName": "com.intellij.codeInsight.highlighting.BraceMatcher", "extensionPointNames": ["com.intellij.braceMatcher", "com.intellij.lang.braceMatcher"], "targetModules": ["backend", "shared"]}, + // backend or shared generic apis - ideally shared (better UX), but depending on implementation may require resolve, so backend is also allowed + {"apiName": "com.intellij.codeInsight.editorActions.CopyPastePostProcessor", "extensionPointNames": ["com.intellij.copyPastePostProcessor"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.CopyPastePreProcessor", "extensionPointNames": ["com.intellij.copyPastePreProcessor"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.PreserveIndentOnPasteBean", "extensionPointNames": ["com.intellij.preserveIndentOnPaste"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.impl.source.codeStyle.PreFormatProcessor", "extensionPointNames": ["com.intellij.preFormatProcessor"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.impl.source.codeStyle.PostFormatProcessor", "extensionPointNames": ["com.intellij.postFormatProcessor"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.CustomAutoFormatSyntaxErrorsVerifier", "extensionPointNames": ["com.intellij.lang.formatter.syntaxErrorsVerifier"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.formatting.service.FormattingService", "extensionPointNames": ["com.intellij.formattingService"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.ide.PasteProvider", "extensionPointNames": ["com.intellij.customPasteProvider"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.formatting.FormattingModelBuilder", "extensionPointNames": ["com.intellij.lang.formatter"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.lang.LanguageFormattingRestriction", "extensionPointNames": ["com.intellij.lang.formatter.restriction"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.psi.formatter.WhiteSpaceFormattingStrategy", "extensionPointNames": ["com.intellij.lang.whiteSpaceFormattingStrategy"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.enter.EnterBetweenBracesDelegate", "extensionPointNames": ["com.intellij.enterBetweenBracesDelegate"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegate", "extensionPointNames": ["com.intellij.enterHandlerDelegate"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.enter.EnterHandlerDelegateAdapter", "extensionPointNames": ["com.intellij.enterHandlerDelegate"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.editorActions.smartEnter.SmartEnterProcessor", "extensionPointNames": ["com.intellij.lang.smartEnterProcessor"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, + {"apiName": "com.intellij.codeInsight.highlighting.BraceMatcher", "extensionPointNames": ["com.intellij.braceMatcher", "com.intellij.lang.braceMatcher"], "targetModules": ["backend", "shared"], "restrictionKind": "genericPlatformApi"}, - {"apiName": "com.intellij.openapi.util.registry.RegistryValueListener", "extensionPointNames": [], "targetModules": ["frontend", "backend"], "hint": "Registry values are different on frontend and backend, prefer listening to them explicitly in desired IDE part"}, - {"apiName": "com.intellij.ide.plugins.DynamicPluginListener", "extensionPointNames": [], "targetModules": ["frontend", "backend"], "hint": "Plugin lists are different on frontend and backend, prefer listening to them explicitly in desired IDE part"}, - {"apiName": "com.intellij.openapi.options.Configurable", "extensionPointNames": ["com.intellij.applicationConfigurable", "com.intellij.projectConfigurable"], "targetModules": ["frontend", "backend"]}, - {"apiName": "com.intellij.lang.annotation.Annotator", "extensionPointNames": ["com.intellij.annotator"], "targetModules": ["frontend", "backend"], "hint": "Resolve-dependent annotators belong to the backend, while lightweight ones belong to frontend"}, - {"apiName": "com.intellij.lang.annotation.ContributedReferencesAnnotator", "extensionPointNames": ["com.intellij.contributedReferencesAnnotator"], "targetModules": ["frontend", "backend"], "hint": "Resolve-dependent annotators belong to the backend, while lightweight ones belong to frontend"}, + // frontend or backend generic apis - suggested to be explicitly registered in a dedicated module type to make sure the developer understands where the code is going to be executed. + // example: shared plugin loading listener with logic touching VFS will not effectively work in frontend, so either there must be two different implementations, or a single registered where the necessary resources (VFS) exist + {"apiName": "com.intellij.openapi.util.registry.RegistryValueListener", "extensionPointNames": [], "targetModules": ["frontend", "backend"], "restrictionKind": "genericPlatformApi", "hint": "Registry values are different on frontend and backend, prefer listening to them explicitly in desired IDE part"}, + {"apiName": "com.intellij.ide.plugins.DynamicPluginListener", "extensionPointNames": [], "targetModules": ["frontend", "backend"], "restrictionKind": "genericPlatformApi", "hint": "Plugin lists are different on frontend and backend, prefer listening to them explicitly in desired IDE part"}, + {"apiName": "com.intellij.lang.annotation.Annotator", "extensionPointNames": ["com.intellij.annotator"], "targetModules": ["frontend", "backend"], "restrictionKind": "genericPlatformApi", "hint": "Resolve-dependent annotators belong to the backend, while lightweight ones belong to frontend"}, + {"apiName": "com.intellij.lang.annotation.ContributedReferencesAnnotator", "extensionPointNames": ["com.intellij.contributedReferencesAnnotator"], "targetModules": ["frontend", "backend"], "restrictionKind": "genericPlatformApi", "hint": "Resolve-dependent annotators belong to the backend, while lightweight ones belong to frontend"}, - {"apiName": "com.intellij.psi.codeStyle.arrangement.std.ArrangementUiComponent.Factory", "extensionPointNames": ["com.intellij.rearranger.ui"], "targetModules": ["frontend", "shared"], "hint": "Language supporting extensions belong to shared, if the language supports injections. Otherwise frontend."}, - {"apiName": "com.intellij.openapi.options.colors.ColorSettingsPage", "extensionPointNames": ["com.intellij.colorSettingsPage"], "targetModules": ["frontend", "shared"], "hint": "Language supporting extensions belong to shared, if the language supports injections. Otherwise frontend."}, - {"apiName": "com.intellij.application.options.colors.ColorAndFontPanelFactory", "extensionPointNames": ["com.intellij.colorAndFontPanelFactory"], "targetModules": ["frontend", "shared"], "hint": "Language supporting extensions belong to shared, if the language supports injections. Otherwise frontend."}, - {"apiName": "com.intellij.psi.codeStyle.CodeStyleSettingsProvider", "extensionPointNames": ["com.intellij.codeStyleSettingsProvider"], "targetModules": ["frontend", "shared"], "hint": "Language supporting extensions belong to shared, if the language supports injections. Otherwise frontend."}, - {"apiName": "com.intellij.openapi.fileTypes.SyntaxHighlighter", "extensionPointNames": ["com.intellij.syntaxHighlighter", "com.intellij.lang.syntaxHighlighter"], "targetModules": ["frontend", "shared"], "hint": "Language supporting extensions belong to shared, if the language supports injections. Otherwise frontend."}, - {"apiName": "com.intellij.openapi.fileTypes.SyntaxHighlighterFactory", "extensionPointNames": ["com.intellij.lang.syntaxHighlighterFactory"], "targetModules": ["frontend", "shared"], "hint": "Language supporting extensions belong to shared, if the language supports injections. Otherwise frontend."}, - {"apiName": "com.intellij.openapi.fileTypes.EditorHighlighterProvider", "extensionPointNames": ["com.intellij.editorHighlighterProvider"], "targetModules": ["frontend", "shared"], "hint": "Language supporting extensions belong to shared, if the language supports injections. Otherwise frontend."} -] \ No newline at end of file + // frontend or shared generic apis + {"apiName": "com.intellij.openapi.options.colors.ColorSettingsPage", "extensionPointNames": ["com.intellij.colorSettingsPage"], "targetModules": ["frontend", "shared"], "restrictionKind": "genericPlatformApi", "hint": "Language supporting extensions belong to shared, if the language supports injections. Otherwise frontend."}, + {"apiName": "com.intellij.application.options.colors.ColorAndFontPanelFactory", "extensionPointNames": ["com.intellij.colorAndFontPanelFactory"], "targetModules": ["frontend", "shared"], "restrictionKind": "genericPlatformApi", "hint": "Language supporting extensions belong to shared, if the language supports injections. Otherwise frontend."}, + {"apiName": "com.intellij.psi.codeStyle.CodeStyleSettingsProvider", "extensionPointNames": ["com.intellij.codeStyleSettingsProvider"], "targetModules": ["frontend", "shared"], "restrictionKind": "genericPlatformApi", "hint": "Language supporting extensions belong to shared, if the language supports injections. Otherwise frontend."}, + {"apiName": "com.intellij.openapi.fileTypes.SyntaxHighlighter", "extensionPointNames": ["com.intellij.syntaxHighlighter", "com.intellij.lang.syntaxHighlighter"], "targetModules": ["frontend", "shared"], "restrictionKind": "genericPlatformApi", "hint": "Language supporting extensions belong to shared, if the language supports injections. Otherwise frontend."}, + {"apiName": "com.intellij.openapi.fileTypes.SyntaxHighlighterFactory", "extensionPointNames": ["com.intellij.lang.syntaxHighlighterFactory"], "targetModules": ["frontend", "shared"], "restrictionKind": "genericPlatformApi", "hint": "Language supporting extensions belong to shared, if the language supports injections. Otherwise frontend."}, + {"apiName": "com.intellij.openapi.fileTypes.EditorHighlighterProvider", "extensionPointNames": ["com.intellij.editorHighlighterProvider"], "targetModules": ["frontend", "shared"], "restrictionKind": "genericPlatformApi", "hint": "Language supporting extensions belong to shared, if the language supports injections. Otherwise frontend."} +] diff --git a/plugins/devkit/devkit-core/resources/remotedevInspectionData/DevKitSplitModeInspectionExclusions.json b/plugins/devkit/devkit-core/resources/remotedevInspectionData/DevKitSplitModeInspectionExclusions.json deleted file mode 100644 index 245718b55be2..000000000000 --- a/plugins/devkit/devkit-core/resources/remotedevInspectionData/DevKitSplitModeInspectionExclusions.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "exclusions": [ - { - "inspection": "SplitModeApiUsage", - "file": "community/python/python-type-engine/src/com/intellij/python/typeEngine/PyTypeEngineStatusBarWidget.kt", - "line": 49, - "reason": "PY-90092: Python LSP/type-engine tools were moved from PyCharm Pro into PyCharm Community. The type engine status bar widget is monolith-only UI that has not been split into frontend/backend yet." - }, - { - "inspection": "SplitModeApiUsage", - "file": "community/python/python-type-engine/src/com/intellij/python/typeEngine/PyTypeEngineStatusBarWidget.kt", - "line": 132, - "reason": "PY-90092: JBPopupFactory usage in the monolith-only Python type engine status bar widget (not yet split for remote dev)." - }, - { - "inspection": "SplitModeXmlApiUsage", - "file": "community/python/python-type-engine/resources/intellij.python.typeEngine.xml", - "line": 42, - "reason": "PY-90092: statusBarWidgetFactory registration for the monolith-only Python type engine widget (not yet split for remote dev)." - } - ] -} diff --git a/plugins/devkit/devkit-core/resources/remotedevInspectionData/MonorepoFeaturesWithoutSplitModeSupport.json5 b/plugins/devkit/devkit-core/resources/remotedevInspectionData/MonorepoFeaturesWithoutSplitModeSupport.json5 new file mode 100644 index 000000000000..01f0ab6276c7 --- /dev/null +++ b/plugins/devkit/devkit-core/resources/remotedevInspectionData/MonorepoFeaturesWithoutSplitModeSupport.json5 @@ -0,0 +1,51 @@ +// Monorepo features that are not inspected by DevKit split-mode inspections yet. +// This file is the source of truth for SplitModeExcludedModules.xml IDE scopes. +// Use pathPatterns for repository paths and moduleNames for IntelliJ module names.\ + +// After making changes to this file run the SplitModeExcludedModulesScopeConsistencyTest +// and apply changes to the config files generated from the MonorepoFeaturesWithoutSplitModeSupport.json5 +{ + pathPatterns: [ + "plugins/llm//*", + "plugins/llm-experimental//*", + "plugins/llm-installer//*", + "plugins/llm-sandbox//*", + "community/python//*", + "language-server//*" + ], + + moduleNames: [ + "intellij.platform.resources", + "intellij.platform.ui.jcef", + "intellij.platform.util", + "intellij.platform.projectModel", + "intellij.platform.projectModel.impl", + "intellij.platform.refactoring", + "intellij.platform.split", + "intellij.platform.split.connection", + "intellij.platform.ide.bootstrap", + "intellij.platform.ide.core", + "intellij.platform.ide.core.impl", + "intellij.platform.ide.impl", + "intellij.platform.indexing", + "intellij.platform.indexing.impl", + "intellij.platform.lang", + "intellij.platform.lang.core", + "intellij.platform.lang.impl", + "intellij.platform.editor.ex", + "intellij.platform.editor.ui", + "intellij.platform.frontend.split", + "intellij.platform.frontend.split.base", + "intellij.platform.ide", + "intellij.platform.analysis", + "intellij.platform.analysis.impl", + "intellij.platform.backend.split", + "intellij.platform.codeStyle", + "intellij.platform.codeStyle.impl", + "intellij.platform.commercial", + "intellij.platform.commercial.license", + "intellij.platform.completion.common", + "intellij.platform.core", + "intellij.platform.core.impl", + ], +} diff --git a/plugins/devkit/devkit-core/resources/remotedevInspectionData/PredefinedModuleKinds.json b/plugins/devkit/devkit-core/resources/remotedevInspectionData/PredefinedModuleKinds.json deleted file mode 100644 index fbc56e470b3a..000000000000 --- a/plugins/devkit/devkit-core/resources/remotedevInspectionData/PredefinedModuleKinds.json +++ /dev/null @@ -1,190 +0,0 @@ -[ - {"moduleName": "intellij.platform.monolith", "moduleKind": "monolith"}, - - {"moduleName": "intellij.platform.frontend", "moduleKind": "frontend"}, - {"moduleName": "intellij.platform.frontend.split", "moduleKind": "frontend"}, - {"moduleName": "intellij.platform.plugins.frontend.split", "moduleKind": "frontend"}, - {"moduleName": "com.intellij.jetbrains.client", "moduleKind": "frontend"}, - {"moduleName": "com.intellij.jetbrains.client.git", "moduleKind": "frontend"}, - {"moduleName": "intellij.cwm.frontend", "moduleKind": "frontend"}, - {"moduleName": "intellij.cwm.connection.frontend.split", "moduleKind": "frontend"}, - - {"moduleName": "intellij.platform.backend", "moduleKind": "backend"}, - {"moduleName": "intellij.platform.backend.split", "moduleKind": "backend"}, - {"moduleName": "intellij.platform.kernel.backend", "moduleKind": "backend"}, - {"moduleName": "com.jetbrains.remoteDevelopment", "moduleKind": "backend"}, - {"moduleName": "intellij.cwm", "moduleKind": "backend"}, - {"moduleName": "intellij.cwm.plugin", "moduleKind": "backend"}, - {"moduleName": "intellij.cwm.plugin.provisioner", "moduleKind": "backend"}, - {"moduleName": "intellij.cwm.connection.backend", "moduleKind": "backend"}, - {"moduleName": "intellij.cwm.connection.backend.license", "moduleKind": "backend"}, - {"moduleName": "intellij.cwm.vcs", "moduleKind": "backend"}, - {"moduleName": "intellij.cwm.enabler", "moduleKind": "backend"}, - {"moduleName": "intellij.cwm.enabler.core", "moduleKind": "backend"}, - {"moduleName": "intellij.codeWithMe.abstract.main", "moduleKind": "backend"}, - {"moduleName": "intellij.codeWithMe.idea.community.main", "moduleKind": "backend"}, - {"moduleName": "intellij.codeWithMe.idea.ultimate.main.android", "moduleKind": "backend"}, - {"moduleName": "intellij.codeWithMe.datagrip.main", "moduleKind": "backend"}, - {"moduleName": "intellij.codeWithMe.pycharm.community.main", "moduleKind": "backend"}, - {"moduleName": "intellij.codeWithMe.appcode.main", "moduleKind": "backend"}, - - {"moduleName": "intellij.cwm.connection", "moduleKind": "shared"}, - {"moduleName": "intellij.cwm.plugin.common", "moduleKind": "shared"}, - {"moduleName": "intellij.rd.client.base", "moduleKind": "shared"}, - {"moduleName": "intellij.rd.client", "moduleKind": "shared"}, - {"moduleName": "intellij.driver.client", "moduleKind": "shared"}, - {"moduleName": "intellij.driver.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.driver.model", "moduleKind": "shared"}, - {"moduleName": "intellij.driver.sdk", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.analysis", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.analysis.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.boot", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.bootstrap", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.bootstrap.coroutine", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.bootstrap.dev", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.buildScripts", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.buildScripts.api", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.buildScripts.bazel", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.buildScripts.codeOptimizer", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.buildScripts.devBuildServer", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.buildScripts.downloader", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.buildScripts.licenses", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.buildScripts.productDsl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.buildScripts.usages", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.codeStyle", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.codeStyle.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.concurrency", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.configurationStore.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.core", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.core.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.core.nio.fs", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.core.ui", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.diagnostic", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.diagnostic.freezeAnalyzer", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.diagnostic.startUpPerformanceReporter", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.diagnostic.telemetry", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.diagnostic.telemetry.agent.extension", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.diagnostic.telemetry.exporters", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.diagnostic.telemetry.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.diagnostic.telemetry.rt", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.editor", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.editor.ex", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.editor.ui", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.eel", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.eel.codegen", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.eel.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.eel.impl.base", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.eel.nioFs", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.eel.nioFs.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.eel.provider", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.eel.tcp", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.eelHelper", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.extensions", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.foldings", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.icons", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.icons.api", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.icons.api.rendering", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.icons.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.icons.impl.intellij", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.bootstrap", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.bootstrap.eel", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.bootstrap.kernel", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.codeinsight.inline", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.concurrency", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.core", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.core.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.core.plugins", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.internal", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.rpc", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.ui", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.util.io", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.util.io.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.ide.util.netty", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.kernel", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.kernel.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.lang", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.lang.core", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.lang.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.locking.impl", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.project", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.runtime.repository", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.base", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.base.multiplatform", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.classLoader", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.commonsLangV2Shim", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.coroutines", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.diff", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.ex", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.http", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.immutableKeyValueStore", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.immutableKeyValueStore.benchmark", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.io.storages", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.jdom", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.multiplatform", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.nanoxml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.progress", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.rt", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.rt.java8", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.text.matching", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.trove", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.troveCompileOnly", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.ui", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.xmlDom", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.util.zip", "moduleKind": "shared"}, - {"moduleName": "intellij.remoteDev.util", "moduleKind": "shared"}, - - {"pluginId": "com.intellij.modules.ultimate", "moduleKind": "shared"}, - {"pluginId": "com.intellij.modules.lang", "moduleKind": "shared"}, - - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/PlatformLangPlugin.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/PlatformLangComponents.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/idea/PriorityEditorLangActions.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/idea/PlatformActions.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/PlatformExecutionActions.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/idea/ExecutionActions.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/idea/LangActions.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/idea/UsageViewActions.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/idea/ProblemsViewActions.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/Core.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/CoreImpl.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/Analysis.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/AnalysisImpl.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/WorkspaceModel.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/WorkspaceModelImpl.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/ProjectModel.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/ProjectModelImpl.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/ProjectModelPlatform.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/Indexing.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/Editor.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/EditorEx.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/IdeCore.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/PlatformExtensionPoints.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/MinimapExtensionPoints.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/Refactoring.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/LangExtensionPoints.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/PlatformExtensions.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/PlatformExecutionExtensions.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/LangExtensions.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/ProjectModelLangExtensions.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/SearchEverywhereCodeInsightContributors.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/OpenTelemetryExtensions.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/WorkspaceModelExtensions.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/intellij.platform.feedback.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/IJent.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/intellij.platform.syntax.psi.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/CompletionExtensionPoints.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/RefactoringExtensionPoints.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/RefactoringLangExtensionPoints.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/FormatterExtensionPoints.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/EditorExtensionPoints.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/CodeStyle.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/META-INF/RemoteDevUtil.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/intellij.platform.project.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/intellij.platform.ide.progress.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.platform.resources", "descriptorRelativePath": "src/intellij.platform.experiment.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.rd.client.base", "descriptorRelativePath": "resources/intellij.rd.client.base.xml", "moduleKind": "shared"}, - {"moduleName": "intellij.rd.client", "descriptorRelativePath": "resources/intellij.rd.client.xml", "moduleKind": "shared"} -] diff --git a/plugins/devkit/devkit-core/resources/remotedevInspectionData/SplitModeProjectBaselineVersion.json5 b/plugins/devkit/devkit-core/resources/remotedevInspectionData/SplitModeProjectBaselineVersion.json5 new file mode 100644 index 000000000000..f55ceb797af7 --- /dev/null +++ b/plugins/devkit/devkit-core/resources/remotedevInspectionData/SplitModeProjectBaselineVersion.json5 @@ -0,0 +1,10 @@ +// The file carries the current project baseline version for Split Mode Compatibility safe-push checks. +// Increase currentProjectBaselineVersion only when incremental Split Mode Compatibility tests incorrectly +// report pre-existing violations after large refactorings. +// Changing this value is a legal bypass: the affected safe push may skip the incremental Split Mode +// Compatibility tests for that run, allowing the refactoring to proceed while the baseline catches up. +// Do not use it for real new violations. Fix those instead; see Split Mode documentation IJPL-A-632. +// Contact the RemDev team in #ij-remote-dev if unsure. +{ + "currentProjectBaselineVersion": 12 +} diff --git a/plugins/devkit/devkit-core/resources/remotedevInspectionData/SplitModeQodanaAnalysisScope.json b/plugins/devkit/devkit-core/resources/remotedevInspectionData/SplitModeQodanaAnalysisScope.json index 0e33b5e6a800..a7be3960de15 100644 --- a/plugins/devkit/devkit-core/resources/remotedevInspectionData/SplitModeQodanaAnalysisScope.json +++ b/plugins/devkit/devkit-core/resources/remotedevInspectionData/SplitModeQodanaAnalysisScope.json @@ -1,406 +1,3 @@ { - "moduleNames": [ - "intellij.ml.llm", - "intellij.ml.llm.activation", - "intellij.ml.llm.activation.config", - "intellij.ml.llm.activation.data.mock", - "intellij.ml.llm.activation.data.model", - "intellij.ml.llm.activation.data.repos", - "intellij.ml.llm.activation.facade", - "intellij.ml.llm.activation.facade.impl", - "intellij.ml.llm.activation.ij", - "intellij.ml.llm.activation.ij.daemon", - "intellij.ml.llm.activation.ij.grazie", - "intellij.ml.llm.activation.platform", - "intellij.ml.llm.activation.platform.tests", - "intellij.ml.llm.activation.presentation", - "intellij.ml.llm.activation.presentation.tests", - "intellij.ml.llm.activation.state.impl", - "intellij.ml.llm.activation.state.impl.tests", - "intellij.ml.llm.activation.state.manager", - "intellij.ml.llm.activation.state.model", - "intellij.ml.llm.activation.tests.runtime", - "intellij.ml.llm.activation.util", - "intellij.ml.llm.activation.util.tests", - "intellij.ml.llm.agents", - "intellij.ml.llm.agents.acp", - "intellij.ml.llm.agents.acp.embeddedMcp", - "intellij.ml.llm.agents.acp.embeddedMcp.tests", - "intellij.ml.llm.agents.acp.json", - "intellij.ml.llm.agents.acp.tests", - "intellij.ml.llm.agents.acp.view.tests", - "intellij.ml.llm.agents.contrib", - "intellij.ml.llm.agents.contrib.contract", - "intellij.ml.llm.agents.contrib.ide.datagrip", - "intellij.ml.llm.agents.contrib.ide.idea", - "intellij.ml.llm.agents.contrib.ide.pycharm", - "intellij.ml.llm.agents.contrib.ide.rider", - "intellij.ml.llm.agents.contrib.ide.rubymine", - "intellij.ml.llm.agents.contrib.ide.rubymine.tests", - "intellij.ml.llm.agents.contrib.shared.vcs", - "intellij.ml.llm.agents.contrib.tests", - "intellij.ml.llm.agents.frontend", - "intellij.ml.llm.agents.frontend.tests", - "intellij.ml.llm.agents.impl", - "intellij.ml.llm.agents.impl.tests", - "intellij.ml.llm.agents.shared", - "intellij.ml.llm.agents.skills", - "intellij.ml.llm.agents.skills.tests", - "intellij.ml.llm.android", - "intellij.ml.llm.android.tests", - "intellij.ml.llm.apiTestsWithJava", - "intellij.ml.llm.aui.events", - "intellij.ml.llm.aui.events.tests", - "intellij.ml.llm.automation", - "intellij.ml.llm.build", - "intellij.ml.llm.chat", - "intellij.ml.llm.chat.completion", - "intellij.ml.llm.chat.completion.tests", - "intellij.ml.llm.chat.frontend", - "intellij.ml.llm.chat.history.migration", - "intellij.ml.llm.chat.shared", - "intellij.ml.llm.chat.tests", - "intellij.ml.llm.chat.testsWithKotlin", - "intellij.ml.llm.chatInputLanguage", - "intellij.ml.llm.chatInputLanguage.grazie", - "intellij.ml.llm.chatInputLanguage.markdown", - "intellij.ml.llm.chatInputLanguage.tests", - "intellij.ml.llm.chatInputLanguage.testsWithJava", - "intellij.ml.llm.chatInputLanguage.testsWithKotlin", - "intellij.ml.llm.cloudEnabledTests", - "intellij.ml.llm.cloudEnabledTests.withJava", - "intellij.ml.llm.cloudEnabledTests.withMcp", - "intellij.ml.llm.codeEngine", - "intellij.ml.llm.codeEngine.tests", - "intellij.ml.llm.codeGeneration", - "intellij.ml.llm.codeGeneration.tests", - "intellij.ml.llm.completion", - "intellij.ml.llm.completion.cloud.testFramework", - "intellij.ml.llm.completion.latest", - "intellij.ml.llm.completion.nextEdits.proxy", - "intellij.ml.llm.completion.testFramework", - "intellij.ml.llm.completion.tests", - "intellij.ml.llm.content", - "intellij.ml.llm.context", - "intellij.ml.llm.context.airules", - "intellij.ml.llm.context.airules.tests", - "intellij.ml.llm.context.ide", - "intellij.ml.llm.context.llm", - "intellij.ml.llm.context.runtime", - "intellij.ml.llm.context.runtime.jvm", - "intellij.ml.llm.context.runtime.jvm.tests", - "intellij.ml.llm.contextRankerFeatures", - "intellij.ml.llm.core", - "intellij.ml.llm.core.grazieClient", - "intellij.ml.llm.core.httpClient", - "intellij.ml.llm.core.tests", - "intellij.ml.llm.core.tests.runtime", - "intellij.ml.llm.cpp", - "intellij.ml.llm.cpp.cmake", - "intellij.ml.llm.cpp.common", - "intellij.ml.llm.cpp.completion", - "intellij.ml.llm.cpp.nextEdits.backend", - "intellij.ml.llm.cpp.tests", - "intellij.ml.llm.cpp.testsClassic", - "intellij.ml.llm.css", - "intellij.ml.llm.css.completion", - "intellij.ml.llm.css.less.completion", - "intellij.ml.llm.css.nextEdits.backend", - "intellij.ml.llm.css.postcss.completion", - "intellij.ml.llm.css.sass.completion", - "intellij.ml.llm.css.tests", - "intellij.ml.llm.devkit", - "intellij.ml.llm.devkit.tests", - "intellij.ml.llm.diagram.frontend", - "intellij.ml.llm.domains", - "intellij.ml.llm.domains.ide", - "intellij.ml.llm.editor.backend", - "intellij.ml.llm.editor.common", - "intellij.ml.llm.editor.frontend", - "intellij.ml.llm.embeddings", - "intellij.ml.llm.embeddings.latest", - "intellij.ml.llm.embeddings.tests", - "intellij.ml.llm.erb.completion", - "intellij.ml.llm.erb.completion.tests", - "intellij.ml.llm.experimental", - "intellij.ml.llm.experimental.aidiff", - "intellij.ml.llm.experimental.aidiff.tests", - "intellij.ml.llm.experimental.build", - "intellij.ml.llm.experimental.insights", - "intellij.ml.llm.experimental.insights.java", - "intellij.ml.llm.experimental.insights.kotlin", - "intellij.ml.llm.experimental.insights.provenance", - "intellij.ml.llm.experimental.insights.python", - "intellij.ml.llm.experimental.insights.tests", - "intellij.ml.llm.experimental.recap", - "intellij.ml.llm.experimental.recap.tests", - "intellij.ml.llm.experiments", - "intellij.ml.llm.experiments.platformAb", - "intellij.ml.llm.experiments.platformAb.tests", - "intellij.ml.llm.experiments.tests", - "intellij.ml.llm.figma", - "intellij.ml.llm.git.nextEdits.backend", - "intellij.ml.llm.github", - "intellij.ml.llm.github.latest", - "intellij.ml.llm.gitlab", - "intellij.ml.llm.go", - "intellij.ml.llm.go.completion", - "intellij.ml.llm.go.embeddings", - "intellij.ml.llm.go.inlinePromptDetector", - "intellij.ml.llm.go.inlinePromptDetector.tests", - "intellij.ml.llm.go.nextEdits.backend", - "intellij.ml.llm.go.tests", - "intellij.ml.llm.gradle", - "intellij.ml.llm.groovy.inlinePromptDetector", - "intellij.ml.llm.html.completion", - "intellij.ml.llm.html.nextEdits.backend", - "intellij.ml.llm.html.tests", - "intellij.ml.llm.httpClient", - "intellij.ml.llm.idea.product.modules", - "intellij.ml.llm.ideolog", - "intellij.ml.llm.inlinePromptDetector", - "intellij.ml.llm.inlinePromptDetector.tests", - "intellij.ml.llm.integration.testFramework", - "intellij.ml.llm.integration.testFramework.tests", - "intellij.ml.llm.integration.tests", - "intellij.ml.llm.java", - "intellij.ml.llm.java.completion", - "intellij.ml.llm.java.domains", - "intellij.ml.llm.java.embeddings", - "intellij.ml.llm.java.embeddings.tests", - "intellij.ml.llm.java.inlinePromptDetector", - "intellij.ml.llm.java.inlinePromptDetector.tests", - "intellij.ml.llm.java.nextEdits.backend", - "intellij.ml.llm.java.tests", - "intellij.ml.llm.javaee", - "intellij.ml.llm.javaee.jpa", - "intellij.ml.llm.javaee.spring", - "intellij.ml.llm.javaee.spring.completion", - "intellij.ml.llm.javaee.spring.data", - "intellij.ml.llm.javaee.spring.mvc", - "intellij.ml.llm.javaee.spring.security", - "intellij.ml.llm.javascript", - "intellij.ml.llm.javascript.astro", - "intellij.ml.llm.javascript.astro.completion", - "intellij.ml.llm.javascript.astro.nextEdits.backend", - "intellij.ml.llm.javascript.completion", - "intellij.ml.llm.javascript.embeddings", - "intellij.ml.llm.javascript.inlinePromptDetector", - "intellij.ml.llm.javascript.latest", - "intellij.ml.llm.javascript.nextEdits.backend", - "intellij.ml.llm.javascript.nextEdits.ultimate.backend", - "intellij.ml.llm.javascript.svelte", - "intellij.ml.llm.javascript.svelte.completion", - "intellij.ml.llm.javascript.svelte.nextEdits.backend", - "intellij.ml.llm.javascript.tests", - "intellij.ml.llm.javascript.vue", - "intellij.ml.llm.javascript.vue.completion", - "intellij.ml.llm.javascript.vue.nextEdits.backend", - "intellij.ml.llm.json.completion", - "intellij.ml.llm.json.nextEdits.backend", - "intellij.ml.llm.json.tests", - "intellij.ml.llm.jupyter.common", - "intellij.ml.llm.jupyter.common.nextEdits.backend", - "intellij.ml.llm.jupyter.common.nextEdits.frontend", - "intellij.ml.llm.jupyter.kotlin", - "intellij.ml.llm.jupyter.kotlin.completion", - "intellij.ml.llm.jupyter.python", - "intellij.ml.llm.jupyter.python.completion", - "intellij.ml.llm.jupyter.python.nextEdits.backend", - "intellij.ml.llm.jupyter.python.tests", - "intellij.ml.llm.jupyter.tests", - "intellij.ml.llm.kotlin", - "intellij.ml.llm.kotlin.completion", - "intellij.ml.llm.kotlin.embeddings", - "intellij.ml.llm.kotlin.inlinePromptDetector", - "intellij.ml.llm.kotlin.j2k", - "intellij.ml.llm.kotlin.nextEdits.backend", - "intellij.ml.llm.kotlin.tests", - "intellij.ml.llm.libraries.grazie", - "intellij.ml.llm.libraries.grazie.cloud", - "intellij.ml.llm.libraries.jcp", - "intellij.ml.llm.libraries.junie.a2a", - "intellij.ml.llm.libraries.molecule", - "intellij.ml.llm.licensetool", - "intellij.ml.llm.localization", - "intellij.ml.llm.logicalStructure", - "intellij.ml.llm.markdown.completion", - "intellij.ml.llm.markdown.completion.tests", - "intellij.ml.llm.markdown.nextEdits.backend", - "intellij.ml.llm.maven", - "intellij.ml.llm.mcp", - "intellij.ml.llm.mcp.embedded", - "intellij.ml.llm.mcp.json", - "intellij.ml.llm.mcp.perforce", - "intellij.ml.llm.mcp.tests", - "intellij.ml.llm.mdx.nextEdits.backend", - "intellij.ml.llm.microservices", - "intellij.ml.llm.nextEdits.acp.backend", - "intellij.ml.llm.nextEdits.agents.backend", - "intellij.ml.llm.nextEdits.backend", - "intellij.ml.llm.nextEdits.common", - "intellij.ml.llm.nextEdits.frontend", - "intellij.ml.llm.nextEdits.monolith", - "intellij.ml.llm.nextEdits.testFramework", - "intellij.ml.llm.nextEdits.tests", - "intellij.ml.llm.performanceTesting", - "intellij.ml.llm.performanceTesting.embeddings", - "intellij.ml.llm.performanceTesting.embeddings.commands", - "intellij.ml.llm.php", - "intellij.ml.llm.php.completion", - "intellij.ml.llm.php.embeddings", - "intellij.ml.llm.php.inlinePromptDetector", - "intellij.ml.llm.php.inlinePromptDetector.tests", - "intellij.ml.llm.php.mcp", - "intellij.ml.llm.php.mcp.tests", - "intellij.ml.llm.php.nextEdits.backend", - "intellij.ml.llm.php.tests", - "intellij.ml.llm.platform", - "intellij.ml.llm.playground", - "intellij.ml.llm.playground.latest", - "intellij.ml.llm.privacy", - "intellij.ml.llm.privacy.filter", - "intellij.ml.llm.privacy.shared", - "intellij.ml.llm.privacy.shared.tests", - "intellij.ml.llm.privacy.tests", - "intellij.ml.llm.properties.completion", - "intellij.ml.llm.properties.completion.tests", - "intellij.ml.llm.provider", - "intellij.ml.llm.provider.anthropic", - "intellij.ml.llm.provider.google", - "intellij.ml.llm.provider.lmstudio", - "intellij.ml.llm.provider.ollama", - "intellij.ml.llm.provider.ollama.tests", - "intellij.ml.llm.provider.openai", - "intellij.ml.llm.provider.openai.tests", - "intellij.ml.llm.python", - "intellij.ml.llm.python.completion", - "intellij.ml.llm.python.domains", - "intellij.ml.llm.python.embeddings", - "intellij.ml.llm.python.inlinePromptDetector", - "intellij.ml.llm.python.nextEdits.backend", - "intellij.ml.llm.python.tests", - "intellij.ml.llm.python.ultimate", - "intellij.ml.llm.qodana", - "intellij.ml.llm.qodana.agents", - "intellij.ml.llm.qodana.agents.tests", - "intellij.ml.llm.qodana.common", - "intellij.ml.llm.qodana.inspectionKts", - "intellij.ml.llm.qodana.inspectionKts.tests", - "intellij.ml.llm.qodana.inspectionsGenerator", - "intellij.ml.llm.rbs.completion", - "intellij.ml.llm.rbs.completion.tests", - "intellij.ml.llm.rider", - "intellij.ml.llm.rider.cpp", - "intellij.ml.llm.rider.cpp.completion", - "intellij.ml.llm.rider.cpp.nextEdits.backend", - "intellij.ml.llm.rider.csharp", - "intellij.ml.llm.rider.csharp.completion", - "intellij.ml.llm.rider.csharp.inlinePromptDetector", - "intellij.ml.llm.rider.csharp.nextEdits.backend", - "intellij.ml.llm.rider.csharp.razor", - "intellij.ml.llm.rider.dotTools", - "intellij.ml.llm.rider.fsharp", - "intellij.ml.llm.rider.fsharp.completion", - "intellij.ml.llm.rider.godot.completion", - "intellij.ml.llm.rider.hlsl.completion", - "intellij.ml.llm.rider.shaderlab.completion", - "intellij.ml.llm.rider.unrealengine.completion", - "intellij.ml.llm.ruby", - "intellij.ml.llm.ruby.completion", - "intellij.ml.llm.ruby.embeddings", - "intellij.ml.llm.ruby.embeddings.tests", - "intellij.ml.llm.ruby.inlinePromptDetector", - "intellij.ml.llm.ruby.inlinePromptDetector.tests", - "intellij.ml.llm.ruby.mcp", - "intellij.ml.llm.ruby.mcp.tests", - "intellij.ml.llm.ruby.nextEdits.backend", - "intellij.ml.llm.ruby.test", - "intellij.ml.llm.rust", - "intellij.ml.llm.rust.completion", - "intellij.ml.llm.rust.nextEdits.backend", - "intellij.ml.llm.rust.test", - "intellij.ml.llm.sandbox", - "intellij.ml.llm.sandbox.build", - "intellij.ml.llm.sandbox.selfReviewDaemon", - "intellij.ml.llm.sandbox.spec", - "intellij.ml.llm.sandbox.spec.mcp", - "intellij.ml.llm.sandbox.spec.wysiwyg", - "intellij.ml.llm.searchEverywhere", - "intellij.ml.llm.searchEverywhere.embeddings", - "intellij.ml.llm.searchEverywhere.embeddings.datagrip", - "intellij.ml.llm.searchEverywhere.embeddings.radler", - "intellij.ml.llm.searchEverywhere.embeddings.rider", - "intellij.ml.llm.searchEverywhere.embeddings.rider.tests", - "intellij.ml.llm.searchEverywhere.tests", - "intellij.ml.llm.sh", - "intellij.ml.llm.smartChat", - "intellij.ml.llm.smartChat.embeddings", - "intellij.ml.llm.space", - "intellij.ml.llm.space.latest", - "intellij.ml.llm.sql", - "intellij.ml.llm.sql.completion", - "intellij.ml.llm.sql.completion.tests", - "intellij.ml.llm.sql.datagrip", - "intellij.ml.llm.sql.embeddings", - "intellij.ml.llm.sql.inlinePromptDetector", - "intellij.ml.llm.sql.inlinePromptDetector.tests", - "intellij.ml.llm.sql.tests", - "intellij.ml.llm.starter", - "intellij.ml.llm.starter.tests", - "intellij.ml.llm.tasks", - "intellij.ml.llm.telemetry", - "intellij.ml.llm.terminal", - "intellij.ml.llm.terminal.completion", - "intellij.ml.llm.terminal.completion.tests", - "intellij.ml.llm.terminal.context", - "intellij.ml.llm.terminal.context.tests", - "intellij.ml.llm.terminal.tests", - "intellij.ml.llm.terraform.completion", - "intellij.ml.llm.tests", - "intellij.ml.llm.testsWithKotlinAndJava", - "intellij.ml.llm.testsWithPython", - "intellij.ml.llm.text.completion", - "intellij.ml.llm.textmate", - "intellij.ml.llm.textmate.tests", - "intellij.ml.llm.tokenizer", - "intellij.ml.llm.tokenizer.tests", - "intellij.ml.llm.uiTestGeneration", - "intellij.ml.llm.vcs", - "intellij.ml.llm.vcs.completion", - "intellij.ml.llm.vcs.completion.tests", - "intellij.ml.llm.vcs.core", - "intellij.ml.llm.vcs.latest", - "intellij.ml.llm.vcs.markdown", - "intellij.ml.llm.vcs.tests", - "intellij.ml.llm.voice", - "intellij.ml.llm.xdebugger.completion", - "intellij.ml.llm.xdebugger.completion.tests", - "intellij.ml.llm.xml.completion", - "intellij.ml.llm.xml.nextEdits.backend", - "intellij.ml.llm.xml.tests", - "intellij.ml.llm.yaml.completion", - "intellij.ml.llm.yaml.inlinePromptDetector", - "intellij.ml.llm.yaml.inlinePromptDetector.tests", - "intellij.ml.llm.yaml.javaee.spring.completion", - "intellij.ml.llm.yaml.json.completion", - "intellij.ml.llm.yaml.nextEdits.backend", - "intellij.ml.llm.yaml.tests", - "intellij.platform.recentFiles", - "intellij.platform.recentFiles.backend", - "intellij.platform.recentFiles.frontend", - "intellij.platform.recentFiles.plugin", - "intellij.swagger", - "intellij.swagger.codegen", - "intellij.swagger.core", - "intellij.swagger.core.tests", - "intellij.swagger.endpoints", - "intellij.swagger.endpoints.tests", - "intellij.swagger.gradle", - "intellij.swagger.java", - "intellij.swagger.maven", - "intellij.hibernate.debugger", - "intellij.hibernate.debugger.core", - "intellij.hibernate.debugger.ide" - ] + "moduleNames": [] } diff --git a/plugins/devkit/devkit-core/src/dom/impl/PluginDescriptorDomFileDescription.java b/plugins/devkit/devkit-core/src/dom/impl/PluginDescriptorDomFileDescription.java index d586ebdc95ac..cdeae327a79c 100644 --- a/plugins/devkit/devkit-core/src/dom/impl/PluginDescriptorDomFileDescription.java +++ b/plugins/devkit/devkit-core/src/dom/impl/PluginDescriptorDomFileDescription.java @@ -25,11 +25,9 @@ final class PluginDescriptorDomFileDescription extends DomFileDescription = listOf( + ".idea/scopes/$SPLIT_MODE_EXCLUDED_MODULES_SCOPE_FILE_NAME", + ".idea.bazel/scopes/$SPLIT_MODE_EXCLUDED_MODULES_SCOPE_FILE_NAME", +) + +internal fun createSplitModeExcludedModulesScopeXml( + resourceReader: SplitModeInspectionResourceReader, + readMode: SplitModeInspectionResourceReadMode, +): String { + val configurationText = requireNotNull( + resourceReader.readText(MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RESOURCE_PATH, readMode) + ) { + "Cannot load $MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH" + } + return createSplitModeExcludedModulesScopeXml(configurationText) +} + +internal fun createSplitModeExcludedModulesScopeXml(json5Text: String): String { + val configuration = parseMonorepoFeaturesWithoutSplitModeSupport(json5Text) + val scopePattern = createSplitModeExcludedModulesScopePattern(configuration) + return """ + + + + """.trimIndent() + "\n" +} + +private fun parseMonorepoFeaturesWithoutSplitModeSupport(json5Text: String): MonorepoFeaturesWithoutSplitModeSupportFile { + val normalizedJson = try { + json5.readTree(json5Text).toString() + } + catch (e: JsonProcessingException) { + throw IllegalArgumentException("Cannot parse $MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH", e) + } + catch (e: IOException) { + throw IllegalArgumentException("Cannot parse $MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH", e) + } + + val configuration = try { + json.decodeFromString(normalizedJson) + } + catch (e: SerializationException) { + throw IllegalArgumentException("Cannot parse $MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH", e) + } + catch (e: IllegalArgumentException) { + throw IllegalArgumentException("Cannot parse $MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH", e) + } + + validateEntries("pathPatterns", configuration.pathPatterns) { entry -> + require(!entry.startsWith("file:")) { + "pathPatterns entries must not include the generated 'file:' prefix: $entry" + } + } + validateEntries("moduleNames", configuration.moduleNames) { entry -> + require('/' !in entry && '[' !in entry && ']' !in entry) { + "moduleNames entries must be raw module names: $entry" + } + } + return configuration +} + +private fun createSplitModeExcludedModulesScopePattern(configuration: MonorepoFeaturesWithoutSplitModeSupportFile): String { + val pathPatterns = configuration.pathPatterns.map { "file:$it" } + val modulePatterns = configuration.moduleNames.map { "file[$it]:*/" } + val patterns = pathPatterns + modulePatterns + require(patterns.isNotEmpty()) { + "$MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH must define at least one pathPatterns or moduleNames entry" + } + require(patterns.size == patterns.toSet().size) { + "$MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH contains entries that generate duplicate scope patterns" + } + return patterns.joinToString("||") +} + +private fun validateEntries( + fieldName: String, + entries: List, + validateEntry: (String) -> Unit, +) { + val duplicates = entries.groupingBy { it }.eachCount().filterValues { it > 1 }.keys + require(duplicates.isEmpty()) { + "$MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH contains duplicate $fieldName entries: ${duplicates.joinToString()}" + } + + for (entry in entries) { + require(entry.isNotBlank()) { + "$MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH contains blank $fieldName entry" + } + require(entry == entry.trim()) { + "$MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH contains $fieldName entry with leading or trailing whitespace: $entry" + } + require("||" !in entry) { + "$MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH entries must not contain scope pattern separators: $entry" + } + validateEntry(entry) + } +} + +private fun String.escapeXmlAttributeValue(): String { + return buildString(length) { + for (character in this@escapeXmlAttributeValue) { + when (character) { + '&' -> append("&") + '"' -> append(""") + '<' -> append("<") + '>' -> append(">") + else -> append(character) + } + } + } +} + +@Serializable +private data class MonorepoFeaturesWithoutSplitModeSupportFile( + @SerialName("pathPatterns") + val pathPatterns: List = emptyList(), + + @SerialName("moduleNames") + val moduleNames: List = emptyList(), +) + +private val json5: ObjectMapper = JsonMapper.builder() + .enable( + JsonReadFeature.ALLOW_JAVA_COMMENTS, + JsonReadFeature.ALLOW_SINGLE_QUOTES, + JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES, + JsonReadFeature.ALLOW_TRAILING_COMMA, + ) + .build() + +private val json = Json { + ignoreUnknownKeys = true + isLenient = true +} diff --git a/plugins/devkit/devkit-core/src/inspections/remotedev/OWNERSHIP b/plugins/devkit/devkit-core/src/inspections/remotedev/OWNERSHIP new file mode 100644 index 000000000000..ae0e0a60ad1c --- /dev/null +++ b/plugins/devkit/devkit-core/src/inspections/remotedev/OWNERSHIP @@ -0,0 +1 @@ +owner 'RD Split Kernel' \ No newline at end of file diff --git a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeApiUsageInspection.kt b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeApiUsageInspection.kt index 33a1814122d2..33101d897113 100644 --- a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeApiUsageInspection.kt +++ b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeApiUsageInspection.kt @@ -17,7 +17,7 @@ import org.jetbrains.annotations.VisibleForTesting import org.jetbrains.idea.devkit.inspections.DevKitUastInspectionBase import org.jetbrains.idea.devkit.inspections.remotedev.SplitModeInspectionUtil.buildModuleKindMismatchMessage import org.jetbrains.idea.devkit.inspections.remotedev.SplitModeInspectionUtil.buildModuleKindMismatchTooltipMessage -import org.jetbrains.idea.devkit.inspections.remotedev.analysis.SplitModeModuleKindResolver.doesApiKindMatchExpectedModuleKind +import org.jetbrains.idea.devkit.inspections.remotedev.analysis.SplitModeModuleKindResolver.isApiUsageAllowed import org.jetbrains.idea.devkit.inspections.remotedev.analysis.ModuleAnalysis import org.jetbrains.idea.devkit.inspections.remotedev.analysis.SplitModeApiRestrictionsService import org.jetbrains.idea.devkit.inspections.remotedev.analysis.SplitModeQodanaInspectionScopeLimiter @@ -26,7 +26,6 @@ import org.jetbrains.uast.UCallExpression import org.jetbrains.uast.UClass import org.jetbrains.uast.UElement import org.jetbrains.uast.UExpression -import org.jetbrains.uast.UField import org.jetbrains.uast.UMethod import org.jetbrains.uast.UQualifiedReferenceExpression import org.jetbrains.uast.USimpleNameReferenceExpression @@ -38,7 +37,7 @@ import org.jetbrains.uast.visitor.AbstractUastVisitor @VisibleForTesting @ApiStatus.Internal -class SplitModeApiUsageInspection : DevKitUastInspectionBase(UClass::class.java, UField::class.java, UMethod::class.java) { +class SplitModeApiUsageInspection : DevKitUastInspectionBase(UClass::class.java, UMethod::class.java) { override fun isAllowed(holder: ProblemsHolder): Boolean { return super.isAllowed(holder) @@ -115,29 +114,25 @@ class SplitModeApiUsageInspection : DevKitUastInspectionBase(UClass::class.java, val resolvedApi = resolveApiUsage(expression) ?: return val sourcePsi = expression.sourcePsi ?: return val restrictionsService = SplitModeApiRestrictionsService.getInstance(sourcePsi.project) - val expectedModuleKind = restrictionsService.getCodeApiKind(resolvedApi.qualifiedName, resolvedApi.owner) ?: return + val apiRestriction = restrictionsService.getCodeApiRestriction(resolvedApi.qualifiedName, resolvedApi.owner) ?: return + val expectedModuleKind = apiRestriction.targetModuleKind val currentModuleType = currentModuleAnalysis.resolvedModuleKind - if (!doesApiKindMatchExpectedModuleKind(currentModuleType, expectedModuleKind)) { + if (!isApiUsageAllowed(currentModuleAnalysis, apiRestriction)) { val hint = restrictionsService.getCodeApiHint(resolvedApi.qualifiedName) val message = buildModuleKindMismatchMessage(resolvedApi.qualifiedName, expectedModuleKind, currentModuleType, hint) val tooltipMessage = buildModuleKindMismatchTooltipMessage(resolvedApi.qualifiedName, expectedModuleKind, currentModuleType, hint) val module = ModuleUtilCore.findModuleForPsiElement(sourcePsi) ?: return - if (SplitModeInspectionExclusionsService.getInstance(sourcePsi.project).isExcluded(sourcePsi, SPLIT_MODE_API_USAGE_SHORT_NAME)) { - return - } val regularFixes = SplitModeDependencyQuickFixes.createMismatchFixes(module, null, expectedModuleKind) - val suppressionFix = - SplitModeInspectionExclusionsService.getInstance(sourcePsi.project).createSuppressionFixIfApplicable(sourcePsi, - SPLIT_MODE_API_USAGE_SHORT_NAME) - val fixes = if (suppressionFix != null) regularFixes + suppressionFix else regularFixes + val suppressionFixes = SplitModeInspectionExclusionsService.getInstance(sourcePsi.project).createCommonSuppressionQuickFixes() + val fixes = regularFixes + suppressionFixes descriptors.add( manager.createProblemDescriptor( sourcePsi, null, message, - ProblemHighlightType.WEAK_WARNING, + ProblemHighlightType.WARNING, tooltipMessage, isOnTheFly, *fixes, diff --git a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeDependencyQuickFixes.kt b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeDependencyQuickFixes.kt index 849934867a5d..d64331f077dd 100644 --- a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeDependencyQuickFixes.kt +++ b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeDependencyQuickFixes.kt @@ -1,6 +1,7 @@ // Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.idea.devkit.inspections.remotedev +import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.ProblemDescriptor @@ -188,18 +189,29 @@ internal object SplitModeDependencyQuickFixes { override fun startInWriteAction(): Boolean = IntentionPreviewUtils.isIntentionPreviewActive() + override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo { + if (previewDescriptor.psiElement.containingFile !is XmlFile) { + return IntentionPreviewInfo.EMPTY + } + return super.generatePreview(project, previewDescriptor) + } + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val ideaPlugin = findQuickFixTargetDescriptor(descriptor) ?: return val module = findTargetModule(descriptor) val xmlFile = ideaPlugin.xmlElement?.containingFile ?: return - if (!IntentionPreviewUtils.prepareElementForWrite(xmlFile)) return if (IntentionPreviewUtils.isIntentionPreviewActive()) { - applyDependencyFixInPreview(ideaPlugin, module, desiredModuleKind) + if (IntentionPreviewUtils.isPreviewElement(xmlFile)) { + applyDependencyFixInPreview(ideaPlugin, module, desiredModuleKind) + } return } - val commandName: @NlsContexts.Command String = message("inspection.remote.dev.make.only.kind.dependencies.fix.progress.title", desiredModuleKind.id) + if (!IntentionPreviewUtils.prepareElementForWrite(xmlFile)) return + + val commandName: @NlsContexts.Command String = + message("inspection.remote.dev.make.only.kind.dependencies.fix.progress.title", desiredModuleKind.id) runWithModalProgressBlocking(project, commandName) { applyDependencyFix(project, ideaPlugin, module, desiredModuleKind, commandName) @@ -223,18 +235,29 @@ internal object SplitModeDependencyQuickFixes { override fun startInWriteAction(): Boolean = IntentionPreviewUtils.isIntentionPreviewActive() + override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo { + if (previewDescriptor.psiElement.containingFile !is XmlFile) { + return IntentionPreviewInfo.EMPTY + } + return super.generatePreview(project, previewDescriptor) + } + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val ideaPlugin = findQuickFixTargetDescriptor(descriptor) ?: return val module = findTargetModule(descriptor) val xmlFile = ideaPlugin.xmlElement?.containingFile ?: return - if (!IntentionPreviewUtils.prepareElementForWrite(xmlFile)) return if (IntentionPreviewUtils.isIntentionPreviewActive()) { - applyDependencyFixInPreview(ideaPlugin, module, desiredModuleKind) + if (IntentionPreviewUtils.isPreviewElement(xmlFile)) { + applyDependencyFixInPreview(ideaPlugin, module, desiredModuleKind) + } return } - val commandName: @NlsContexts.Command String = message("inspection.remote.dev.make.only.kind.dependencies.fix.progress.title", desiredModuleKind.id) + if (!IntentionPreviewUtils.prepareElementForWrite(xmlFile)) return + + val commandName: @NlsContexts.Command String = + message("inspection.remote.dev.make.only.kind.dependencies.fix.progress.title", desiredModuleKind.id) runWithModalProgressBlocking(project, commandName) { applyDependencyFix(project, ideaPlugin, module, desiredModuleKind, commandName) diff --git a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeImplicitModuleKindInspection.kt b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeImplicitModuleKindInspection.kt index 4f45ae10d714..c555bf4a8a4c 100644 --- a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeImplicitModuleKindInspection.kt +++ b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeImplicitModuleKindInspection.kt @@ -28,11 +28,8 @@ internal class SplitModeImplicitModuleKindInspection : DevKitPluginXmlInspection val module = element.module ?: return val currentXmlFile = holder.fileElement.file - val xmlElement = element.xmlElement ?: return + if (element.xmlElement == null) return val exclusionsService = SplitModeInspectionExclusionsService.getInstance(currentXmlFile.project) - if (exclusionsService.isExcluded(xmlElement, SPLIT_MODE_IMPLICIT_MODULE_KIND_SHORT_NAME)) { - return - } val moduleAnalysis = SplitModeModuleKindResolver.getOrComputeModuleAnalysis(module, currentXmlFile) if (!SplitModeInspectionUtil.isImplicitFrontendOrBackendMainPluginXml(currentXmlFile, moduleAnalysis)) { @@ -41,11 +38,8 @@ internal class SplitModeImplicitModuleKindInspection : DevKitPluginXmlInspection val regularFixes = SplitModeDependencyQuickFixes.createAddExplicitDependenciesFixes(module, element, moduleAnalysis.resolvedModuleKind.kind) - val suppressionFix = exclusionsService.createSuppressionFixIfApplicable( - xmlElement, - SPLIT_MODE_IMPLICIT_MODULE_KIND_SHORT_NAME, - ) - val quickFixes = if (suppressionFix != null) regularFixes + suppressionFix else regularFixes + val suppressionFixes = exclusionsService.createCommonSuppressionQuickFixes() + val quickFixes = regularFixes + suppressionFixes holder.createProblem( element, ProblemHighlightType.WEAK_WARNING, diff --git a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeInspectionExclusions.kt b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeInspectionExclusions.kt index b04fda16d873..90708497040c 100644 --- a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeInspectionExclusions.kt +++ b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeInspectionExclusions.kt @@ -3,7 +3,6 @@ package org.jetbrains.idea.devkit.inspections.remotedev -import com.intellij.codeInsight.intention.FileModifier.SafeTypeForPreview import com.intellij.codeInsight.intention.LowPriorityAction import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.ProblemDescriptor @@ -12,7 +11,6 @@ import com.intellij.openapi.components.Service import com.intellij.openapi.components.service import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.logger -import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.fileEditor.OpenFileDescriptor import com.intellij.openapi.project.IntelliJProjectUtil import com.intellij.openapi.project.Project @@ -21,9 +19,8 @@ import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.writeText -import com.intellij.psi.PsiDocumentManager -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile +import com.fasterxml.jackson.core.json.JsonReadFeature +import com.fasterxml.jackson.databind.json.JsonMapper import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.SerializationException @@ -31,24 +28,9 @@ import kotlinx.serialization.json.Json import org.jetbrains.idea.devkit.DevKitBundle.message import java.io.IOException -internal const val EXCLUSIONS_FILE_NAME: String = "DevKitSplitModeInspectionExclusions.json" -internal const val EXCLUSIONS_RESOURCE_PATH: String = "remotedevInspectionData/$EXCLUSIONS_FILE_NAME" -internal const val EXCLUSIONS_RELATIVE_PATH: String = - "community/plugins/devkit/devkit-core/resources/$EXCLUSIONS_RESOURCE_PATH" - -internal const val SPLIT_MODE_API_USAGE_SHORT_NAME: String = "SplitModeApiUsage" -internal const val SPLIT_MODE_XML_API_USAGE_SHORT_NAME: String = "SplitModeXmlApiUsage" -internal const val SPLIT_MODE_MIXED_DEPENDENCIES_SHORT_NAME: String = "SplitModeMixedDependencies" -internal const val SPLIT_MODE_IMPLICIT_MODULE_KIND_SHORT_NAME: String = "SplitModeImplicitModuleKind" -internal const val MISSING_RUNTIME_DEPENDENCY_SHORT_NAME: String = "MissingFrontendOrBackendRuntimeDependency" - -@SafeTypeForPreview -internal data class SplitModeInspectionExclusionProblem( - val inspection: String, - val file: String, - val line: Int, - val reason: String = "", -) +internal const val PROJECT_BASELINE_VERSION_FILE_NAME: String = "SplitModeProjectBaselineVersion.json5" +internal const val PROJECT_BASELINE_VERSION_RELATIVE_PATH: String = + "community/plugins/devkit/devkit-core/resources/remotedevInspectionData/$PROJECT_BASELINE_VERSION_FILE_NAME" @Service(Service.Level.PROJECT) internal class SplitModeInspectionExclusionsService(private val project: Project) { @@ -56,55 +38,22 @@ internal class SplitModeInspectionExclusionsService(private val project: Project fun getInstance(project: Project): SplitModeInspectionExclusionsService = project.service() } - private val resourceReader = SplitModeInspectionResourceReader.getInstance(project) - - private val exclusionsResource = object : SplitModeInspectionReloadableResource( - resourceReader = resourceReader, - resourcePath = EXCLUSIONS_RESOURCE_PATH, - readMode = SplitModeInspectionResourceReadMode.PROJECT_ONLY, - ) { - override fun parse(text: String): SplitModeInspectionExclusionsSnapshot { - return SplitModeInspectionExclusionsSnapshot(parseExclusionsFile(text).exclusions) - } - - override fun getDefaultValue(): SplitModeInspectionExclusionsSnapshot { - return SplitModeInspectionExclusionsSnapshot() - } + fun createCommonSuppressionQuickFixes(): Array { + if (!isBaselineFixAvailable()) return LocalQuickFix.EMPTY_ARRAY + return arrayOf(IncreaseSplitModeProjectBaselineVersionFix()) } - fun createSuppressionFixIfApplicable( - element: PsiElement, - inspectionShortName: String, - ): LocalQuickFix? { - if (!isExclusionFixAvailable()) return null - val problem = createProblem(inspectionShortName, element) ?: return null - return AddToSplitModeInspectionExclusionsFix(problem) + fun increaseProjectBaselineVersion(): VirtualFile? { + val baselineVersionFile = findOrCreateProjectBaselineVersionFile() ?: return null + val currentFile = parseProjectBaselineVersionFile(VfsUtilCore.loadText(baselineVersionFile)) + val updatedFile = currentFile.copy( + currentProjectBaselineVersion = currentFile.currentProjectBaselineVersion + 1, + ) + baselineVersionFile.writeText(createProjectBaselineVersionJson5(updatedFile)) + return baselineVersionFile } - fun isExcluded(element: PsiElement, inspectionShortName: String): Boolean { - val problem = createProblem(inspectionShortName, element) ?: return false - return isExcluded(problem) - } - - fun isExcluded(problem: SplitModeInspectionExclusionProblem): Boolean { - return getSnapshot().exclusions.any { it.matches(problem) } - } - - fun appendExclusion(problem: SplitModeInspectionExclusionProblem): VirtualFile? { - val exclusionsFile = findOrCreateExclusionsFile() ?: return null - val currentFile = SplitModeInspectionExclusionsFile(getSnapshot().exclusions) - val newEntry = problem.toEntry() - if (currentFile.exclusions.any { it.matches(newEntry) }) { - return exclusionsFile - } - - val updatedFile = currentFile.copy(exclusions = currentFile.exclusions + newEntry) - exclusionsFile.writeText(json.encodeToString(updatedFile) + "\n") - exclusionsResource.invalidate() - return exclusionsFile - } - - private fun isExclusionFixAvailable(): Boolean { + private fun isBaselineFixAvailable(): Boolean { return IntelliJProjectUtil.isIntelliJPlatformProject(project) && getProjectRoot() != null } @@ -112,152 +61,98 @@ internal class SplitModeInspectionExclusionsService(private val project: Project return project.guessProjectDir() } - private fun getProjectRelativePath(file: PsiFile): String? { - val virtualFile = file.originalFile.virtualFile ?: file.virtualFile ?: return null - val projectRoot = getProjectRoot() ?: return null - return VfsUtilCore.getRelativePath(virtualFile, projectRoot, '/') - } - - private fun createProblem( - inspectionShortName: String, - element: PsiElement, - ): SplitModeInspectionExclusionProblem? { - val file = element.containingFile ?: return null - val filePath = getProjectRelativePath(file) ?: return null - val line = getLineNumber(file, element) ?: return null - return SplitModeInspectionExclusionProblem( - inspection = inspectionShortName, - file = filePath, - line = line, - reason = "", - ) - } - - private fun getLineNumber(file: PsiFile, element: PsiElement): Int? { - val document = PsiDocumentManager.getInstance(file.project).getDocument(file) - ?: file.viewProvider.document - ?: return null - return document.getLineNumber(element.textRange.startOffset) + 1 - } - - private fun getSnapshot(): SplitModeInspectionExclusionsSnapshot { - return exclusionsResource.getValue() - } - - private fun findOrCreateExclusionsFile(): VirtualFile? { + private fun findOrCreateProjectBaselineVersionFile(): VirtualFile? { val projectRoot = getProjectRoot() ?: return null return try { val parentDirectory = VfsUtil.createDirectoryIfMissing(projectRoot, EXCLUSIONS_DIRECTORY_RELATIVE_PATH) - parentDirectory.findChild(EXCLUSIONS_FILE_NAME) - ?: parentDirectory.createChildData(this, EXCLUSIONS_FILE_NAME).also { file -> - file.writeText(INITIAL_EXCLUSIONS_JSON) + parentDirectory.findChild(PROJECT_BASELINE_VERSION_FILE_NAME) + ?: parentDirectory.createChildData(this, PROJECT_BASELINE_VERSION_FILE_NAME).also { file -> + file.writeText(createProjectBaselineVersionJson5(SplitModeProjectBaselineVersionFile(currentProjectBaselineVersion = 0))) } } catch (e: IOException) { - LOG.warn("Cannot create $EXCLUSIONS_RELATIVE_PATH", e) + LOG.warn("Cannot create $PROJECT_BASELINE_VERSION_RELATIVE_PATH", e) null } } - private fun parseExclusionsFile(text: String): SplitModeInspectionExclusionsFile { + private fun parseProjectBaselineVersionFile(text: String): SplitModeProjectBaselineVersionFile { if (text.isBlank()) { - return SplitModeInspectionExclusionsFile() + return SplitModeProjectBaselineVersionFile(currentProjectBaselineVersion = 0) } return try { - json.decodeFromString(text) + val normalizedJson = json5.readTree(text).toString() + json.decodeFromString(normalizedJson) } catch (e: SerializationException) { - LOG.warn("Cannot parse $EXCLUSIONS_RELATIVE_PATH", e) - SplitModeInspectionExclusionsFile() + LOG.warn("Cannot parse $PROJECT_BASELINE_VERSION_RELATIVE_PATH", e) + SplitModeProjectBaselineVersionFile(currentProjectBaselineVersion = 0) + } + catch (e: IOException) { + LOG.warn("Cannot parse $PROJECT_BASELINE_VERSION_RELATIVE_PATH", e) + SplitModeProjectBaselineVersionFile(currentProjectBaselineVersion = 0) } catch (e: IllegalArgumentException) { - LOG.warn("Cannot parse $EXCLUSIONS_RELATIVE_PATH", e) - SplitModeInspectionExclusionsFile() + LOG.warn("Cannot parse $PROJECT_BASELINE_VERSION_RELATIVE_PATH", e) + SplitModeProjectBaselineVersionFile(currentProjectBaselineVersion = 0) } } - - private fun SplitModeInspectionExclusionEntry.matches(problem: SplitModeInspectionExclusionProblem): Boolean { - if (inspection != problem.inspection) return false - if (file != problem.file) return false - if (line != problem.line) return false - return true - } - - private fun SplitModeInspectionExclusionEntry.matches(other: SplitModeInspectionExclusionEntry): Boolean { - if (inspection != other.inspection) return false - if (file != other.file) return false - if (line != other.line) return false - return true - } - - private fun SplitModeInspectionExclusionProblem.toEntry(): SplitModeInspectionExclusionEntry { - return SplitModeInspectionExclusionEntry( - inspection = inspection, - file = file, - line = line, - reason = reason, - ) - } } -private class AddToSplitModeInspectionExclusionsFix( - private val problem: SplitModeInspectionExclusionProblem, -) : LocalQuickFix, LowPriorityAction { +private class IncreaseSplitModeProjectBaselineVersionFix : LocalQuickFix, LowPriorityAction { override fun getName(): String = familyName override fun getFamilyName(): String { - return message("inspection.remote.dev.add.to.split.mode.exclusions.fix.name") + return message("inspection.remote.dev.increase.split.mode.baseline.fix.name") } override fun startInWriteAction(): Boolean = false override fun applyFix(project: Project, descriptor: ProblemDescriptor) { - val exclusionsFile = WriteCommandAction.writeCommandAction(project) - .withName(message("inspection.remote.dev.add.to.split.mode.exclusions.command.name")) + val baselineVersionFile = WriteCommandAction.writeCommandAction(project) + .withName(message("inspection.remote.dev.increase.split.mode.baseline.command.name")) .compute { - SplitModeInspectionExclusionsService.getInstance(project).appendExclusion(problem) + SplitModeInspectionExclusionsService.getInstance(project).increaseProjectBaselineVersion() } - if (exclusionsFile == null) { + if (baselineVersionFile == null) { return } - val document = FileDocumentManager.getInstance().getDocument(exclusionsFile) - val lastLine = maxOf((document?.lineCount ?: 1) - 1, 0) - OpenFileDescriptor(project, exclusionsFile, lastLine, 0).navigate(true) + OpenFileDescriptor(project, baselineVersionFile, 0, 0).navigate(true) } } -private data class SplitModeInspectionExclusionsSnapshot( - val exclusions: List = emptyList(), -) - @Serializable -internal data class SplitModeInspectionExclusionsFile( - @SerialName("exclusions") - val exclusions: List = emptyList(), -) - -@Serializable -internal data class SplitModeInspectionExclusionEntry( - @SerialName("inspection") - val inspection: String, - - @SerialName("file") - val file: String = "", - - @SerialName("line") - val line: Int = -1, - - @SerialName("reason") - val reason: String? = null, +private data class SplitModeProjectBaselineVersionFile( + @SerialName("currentProjectBaselineVersion") + val currentProjectBaselineVersion: Int, ) private val LOG: Logger = logger() private const val EXCLUSIONS_DIRECTORY_RELATIVE_PATH: String = "community/plugins/devkit/devkit-core/resources/remotedevInspectionData" -private const val INITIAL_EXCLUSIONS_JSON: String = "{ \"exclusions\": [] }\n" +private val json5 = JsonMapper.builder() + .enable( + JsonReadFeature.ALLOW_JAVA_COMMENTS, + JsonReadFeature.ALLOW_SINGLE_QUOTES, + JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES, + JsonReadFeature.ALLOW_TRAILING_COMMA, + ) + .build() + +private fun createProjectBaselineVersionJson5(file: SplitModeProjectBaselineVersionFile): String { + return """ + // The file carries the current project baseline version for Split Mode Compatibility safe-push checks. + // Increase currentProjectBaselineVersion only when incremental Split Mode Compatibility tests incorrectly + // report pre-existing violations after large refactorings. + // Changing this value is a legal bypass: the affected safe push may skip the incremental Split Mode + // Compatibility tests for that run, allowing the refactoring to proceed while the baseline catches up. + // Do not use it for real new violations. Fix those instead; see Split Mode documentation IJPL-A-632. + // Contact the RemDev team in #ij-remote-dev if unsure. + """.trimIndent() + "\n" + json.encodeToString(file) + "\n" +} private val json = Json { ignoreUnknownKeys = true diff --git a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeInspectionUtil.kt b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeInspectionUtil.kt index d8d3b865685b..d0c544366deb 100644 --- a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeInspectionUtil.kt +++ b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeInspectionUtil.kt @@ -21,6 +21,7 @@ import org.jetbrains.annotations.Nls import org.jetbrains.idea.devkit.DevKitBundle import org.jetbrains.idea.devkit.dom.ContentDescriptor.ModuleDescriptor import org.jetbrains.idea.devkit.dom.index.PluginIdDependenciesIndex +import org.jetbrains.idea.devkit.inspections.remotedev.analysis.ApiUsagePolicy import org.jetbrains.idea.devkit.inspections.remotedev.analysis.ModuleAnalysis import org.jetbrains.idea.devkit.inspections.remotedev.analysis.ResolvedModuleKind import org.jetbrains.idea.devkit.inspections.remotedev.analysis.SplitModeAnalysisFlags @@ -187,7 +188,7 @@ internal object SplitModeInspectionUtil { restrictionsService.getPredefinedModuleKind(module, ideaPlugin = ideaPlugin) } - return predefinedModuleKind != null + return predefinedModuleKind != null && predefinedModuleKind.apiUsagePolicy == ApiUsagePolicy.DEFAULT } fun shouldSuppressForSingleModuleExternalPlugin(file: PsiFile): Boolean { diff --git a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeMixedDependenciesInspection.kt b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeMixedDependenciesInspection.kt index aa5200fe5362..5098809561a2 100644 --- a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeMixedDependenciesInspection.kt +++ b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeMixedDependenciesInspection.kt @@ -27,20 +27,13 @@ internal class SplitModeMixedDependenciesInspection : DevKitPluginXmlInspectionB val module = element.module ?: return val currentXmlFile = holder.fileElement.file - val xmlElement = element.xmlElement ?: return - if (SplitModeInspectionExclusionsService.getInstance(currentXmlFile.project) - .isExcluded(xmlElement, SPLIT_MODE_MIXED_DEPENDENCIES_SHORT_NAME)) { - return - } + if (element.xmlElement == null) return val moduleAnalysis = SplitModeModuleKindResolver.getOrComputeModuleAnalysis(module, currentXmlFile) if (moduleAnalysis.resolvedModuleKind.kind == SplitModeApiRestrictionsService.ModuleKind.MIXED) { val regularFixes = SplitModeDependencyQuickFixes.createMixedModuleFixes(module, element) - val suppressionFix = SplitModeInspectionExclusionsService.getInstance(currentXmlFile.project).createSuppressionFixIfApplicable( - xmlElement, - SPLIT_MODE_MIXED_DEPENDENCIES_SHORT_NAME, - ) - val quickFixes = if (suppressionFix != null) regularFixes + suppressionFix else regularFixes + val suppressionFixes = SplitModeInspectionExclusionsService.getInstance(currentXmlFile.project).createCommonSuppressionQuickFixes() + val quickFixes = regularFixes + suppressionFixes val mixedDependenciesMessage = buildMixedModuleDependenciesMessage(moduleAnalysis.resolvedModuleKind.reasoning) holder.createProblem( element, diff --git a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeXmlApiUsageInspection.kt b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeXmlApiUsageInspection.kt index bf23a38ce2bb..c519a01525de 100644 --- a/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeXmlApiUsageInspection.kt +++ b/plugins/devkit/devkit-core/src/inspections/remotedev/SplitModeXmlApiUsageInspection.kt @@ -11,7 +11,7 @@ import org.jetbrains.idea.devkit.inspections.remotedev.SplitModeInspectionUtil.b import org.jetbrains.idea.devkit.inspections.remotedev.analysis.SplitModeApiRestrictionsService import org.jetbrains.idea.devkit.inspections.remotedev.analysis.SplitModeQodanaInspectionScopeLimiter import org.jetbrains.idea.devkit.inspections.remotedev.analysis.SplitModeModuleKindResolver -import org.jetbrains.idea.devkit.inspections.remotedev.analysis.SplitModeModuleKindResolver.doesApiKindMatchExpectedModuleKind +import org.jetbrains.idea.devkit.inspections.remotedev.analysis.SplitModeModuleKindResolver.isApiUsageAllowed import org.jetbrains.idea.devkit.util.DescriptorUtil internal class SplitModeXmlApiUsageInspection : DevKitPluginXmlInspectionBase() { @@ -19,7 +19,8 @@ internal class SplitModeXmlApiUsageInspection : DevKitPluginXmlInspectionBase() override fun isAllowed(holder: DomElementAnnotationHolder): Boolean { return super.isAllowed(holder) && SplitModeInspectionUtil.isAllowedForSplitModeInspection(holder.fileElement.file) - && SplitModeQodanaInspectionScopeLimiter.getInstance(holder.fileElement.file.project).shouldInspectFileInQodanaMode(holder.fileElement.file) + && SplitModeQodanaInspectionScopeLimiter.getInstance(holder.fileElement.file.project) + .shouldInspectFileInQodanaMode(holder.fileElement.file) } override fun checkDomElement(element: DomElement, holder: DomElementAnnotationHolder, helper: DomHighlightingHelper) { @@ -30,25 +31,20 @@ internal class SplitModeXmlApiUsageInspection : DevKitPluginXmlInspectionBase() val module = element.module ?: return val currentXmlFile = holder.fileElement.file val restrictionsService = SplitModeApiRestrictionsService.getInstance(currentXmlFile.project) - val expectedModuleKind = restrictionsService.getExtensionPointKind(extensionPointName) ?: return + val apiRestriction = restrictionsService.getExtensionPointRestriction(extensionPointName) ?: return + val expectedModuleKind = apiRestriction.targetModuleKind val moduleAnalysis = SplitModeModuleKindResolver.getOrComputeModuleAnalysis(module, currentXmlFile) if (SplitModeInspectionUtil.shouldReportSinglePluginLevelErrorInsteadOfManyNestedErrors(currentXmlFile, moduleAnalysis)) return val actualModuleKind = moduleAnalysis.resolvedModuleKind - if (doesApiKindMatchExpectedModuleKind(actualModuleKind, expectedModuleKind)) return + if (isApiUsageAllowed(moduleAnalysis, apiRestriction)) return val currentlyOpenedDescriptor = DescriptorUtil.getIdeaPlugin(holder.fileElement.file) val hint = restrictionsService.getExtensionPointHint(extensionPointName) - val xmlElement = element.xmlElement ?: return - if (SplitModeInspectionExclusionsService.getInstance(currentXmlFile.project).isExcluded(xmlElement, SPLIT_MODE_XML_API_USAGE_SHORT_NAME)) { - return - } + if (element.xmlElement == null) return val regularFixes = SplitModeDependencyQuickFixes.createMismatchFixes(module, currentlyOpenedDescriptor, expectedModuleKind) - val suppressionFix = SplitModeInspectionExclusionsService.getInstance(currentXmlFile.project).createSuppressionFixIfApplicable( - xmlElement, - SPLIT_MODE_XML_API_USAGE_SHORT_NAME, - ) - val fixes = if (suppressionFix != null) regularFixes + suppressionFix else regularFixes + val suppressionFixes = SplitModeInspectionExclusionsService.getInstance(currentXmlFile.project).createCommonSuppressionQuickFixes() + val fixes = regularFixes + suppressionFixes holder.createProblem( element, buildModuleKindMismatchMessage(extensionPointName, expectedModuleKind, actualModuleKind, hint), diff --git a/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeAnalysisModel.kt b/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeAnalysisModel.kt index 9d9573392214..7c7d25d29bc4 100644 --- a/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeAnalysisModel.kt +++ b/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeAnalysisModel.kt @@ -6,8 +6,14 @@ import com.intellij.openapi.util.NlsSafe internal data class ModuleAnalysis( val resolvedModuleKind: ResolvedModuleKind, val evidence: ModuleKindEvidence = ModuleKindEvidence(), + val apiUsagePolicy: ApiUsagePolicy = ApiUsagePolicy.DEFAULT, ) +internal enum class ApiUsagePolicy(val id: String) { + DEFAULT("default"), + BACKEND_WITH_NON_UI_API_PERMIT("backendWithNonUiApiPermit"), +} + internal data class ModuleKindEvidence( val hasOwnFrontendEvidence: Boolean = false, val hasOwnBackendEvidence: Boolean = false, diff --git a/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeApiRestrictionsService.kt b/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeApiRestrictionsService.kt index b6d07b3ab43c..4ff80c335bfd 100644 --- a/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeApiRestrictionsService.kt +++ b/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeApiRestrictionsService.kt @@ -14,6 +14,9 @@ import com.intellij.openapi.util.NlsSafe import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.psi.PsiModifierListOwner import com.intellij.psi.xml.XmlFile +import com.fasterxml.jackson.core.json.JsonReadFeature +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.json.JsonMapper import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -45,8 +48,8 @@ class SplitModeApiRestrictionsService( companion object { private val LOG: Logger = logger() - private const val API_RESTRICTIONS_RESOURCE_PATH = "remotedevInspectionData/ApiRestrictions.json" - private const val PREDEFINED_MODULE_KINDS_RESOURCE_PATH = "remotedevInspectionData/PredefinedModuleKinds.json" + private const val API_RESTRICTIONS_RESOURCE_PATH = "remotedevInspectionData/ApiRestrictions.json5" + private const val PREDEFINED_MODULE_KINDS_RESOURCE_PATH = "remotedevInspectionData/PredefinedModuleKinds.json5" private const val BACKEND_API_ANNOTATION = "com.intellij.util.remdev.BackendApi" private const val FRONTEND_API_ANNOTATION = "com.intellij.util.remdev.FrontendApi" @@ -96,10 +99,29 @@ class SplitModeApiRestrictionsService( } } + enum class ApiRestrictionKind(val id: String) { + GENERIC_PLATFORM_API("genericPlatformApi"), + UI("ui"), + } + + private enum class PredefinedModuleKindEntryKind(val id: String) { + MODULE_ID("moduleId"), + PLUGIN_ID("pluginId"), + XML_DESCRIPTOR_FILE("xmlDescriptorFile"), + } + private val json = Json { ignoreUnknownKeys = true isLenient = true } + private val json5: ObjectMapper = JsonMapper.builder() + .enable( + JsonReadFeature.ALLOW_JAVA_COMMENTS, + JsonReadFeature.ALLOW_SINGLE_QUOTES, + JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES, + JsonReadFeature.ALLOW_TRAILING_COMMA, + ) + .build() private val resourceReader = SplitModeInspectionResourceReader.getInstance(project) private val backgroundLoadScheduled = AtomicBoolean(false) @@ -135,10 +157,18 @@ class SplitModeApiRestrictionsService( } fun getCodeApiKind(apiName: String, apiOwner: PsiModifierListOwner?): ModuleKind? { - return getAnnotatedApiKind(apiOwner) ?: getRestrictionsSnapshot().codeRestrictions[apiName] + return getCodeApiRestriction(apiName, apiOwner)?.targetModuleKind } - fun getExtensionPointKind(extensionPointName: String): ModuleKind? { + internal fun getCodeApiRestriction(apiName: String, apiOwner: PsiModifierListOwner?): ApiRestrictionMatch? { + val annotatedApiKind = getAnnotatedApiKind(apiOwner) + if (annotatedApiKind != null) { + return ApiRestrictionMatch(annotatedApiKind, ApiRestrictionKind.GENERIC_PLATFORM_API) + } + return getRestrictionsSnapshot().codeRestrictions[apiName] + } + + internal fun getExtensionPointRestriction(extensionPointName: String): ApiRestrictionMatch? { val restrictions = getRestrictionsSnapshot() val apiName = restrictions.extensionPointToApiName[extensionPointName] ?: return null return restrictions.codeRestrictions[apiName] @@ -156,11 +186,11 @@ class SplitModeApiRestrictionsService( fun getPredefinedDependencyKind(dependencyId: String): ModuleKind? { val predefinedModuleKinds = getRestrictionsSnapshot().predefinedModuleKinds - val moduleKind = predefinedModuleKinds.moduleNames[dependencyId] - if (moduleKind != null) { - return moduleKind + val moduleInfo = predefinedModuleKinds.moduleIds[dependencyId] + if (moduleInfo != null) { + return moduleInfo.moduleKind } - return predefinedModuleKinds.pluginIds[dependencyId] + return predefinedModuleKinds.pluginIds[dependencyId]?.moduleKind } internal fun getPredefinedModuleKind( @@ -172,10 +202,11 @@ class SplitModeApiRestrictionsService( val descriptorRelativePath = if (descriptorFile == null) null else computeDescriptorRelativePath(module, descriptorFile) if (descriptorRelativePath != null) { val selector = DescriptorPathSelector(module.name, descriptorRelativePath) - val moduleKind = lookup.descriptorPaths[selector] - if (moduleKind != null) { + val moduleInfo = lookup.descriptorPaths[selector] + if (moduleInfo != null) { return PredefinedModuleKindMatch( - moduleKind = moduleKind, + moduleKind = moduleInfo.moduleKind, + apiUsagePolicy = moduleInfo.apiUsagePolicy, cacheKey = "descriptor|${module.name}|$descriptorRelativePath", reasoning = "Predefined module kind for descriptor '$descriptorRelativePath' in module '${module.name}'", ) @@ -185,10 +216,11 @@ class SplitModeApiRestrictionsService( if (ideaPlugin != null) { val pluginSelectorIds = collectPluginSelectorIds(ideaPlugin) for (pluginSelectorId in pluginSelectorIds) { - val moduleKind = lookup.pluginIds[pluginSelectorId] - if (moduleKind != null) { + val moduleInfo = lookup.pluginIds[pluginSelectorId] + if (moduleInfo != null) { return PredefinedModuleKindMatch( - moduleKind = moduleKind, + moduleKind = moduleInfo.moduleKind, + apiUsagePolicy = moduleInfo.apiUsagePolicy, cacheKey = "plugin|$pluginSelectorId", reasoning = "Predefined module kind for plugin/module id '$pluginSelectorId'", ) @@ -196,9 +228,10 @@ class SplitModeApiRestrictionsService( } } - val moduleKind = lookup.moduleNames[module.name] ?: return null + val moduleInfo = lookup.moduleIds[module.name] ?: return null return PredefinedModuleKindMatch( - moduleKind = moduleKind, + moduleKind = moduleInfo.moduleKind, + apiUsagePolicy = moduleInfo.apiUsagePolicy, cacheKey = "module|${module.name}", reasoning = "Predefined module kind for module '${module.name}'", ) @@ -293,7 +326,8 @@ class SplitModeApiRestrictionsService( readMode = readMode, ) { override fun parse(text: String): RestrictionsLookup { - return buildApiRestrictionsLookup(json.decodeFromString>(text)) + val normalizedJson = json5.readTree(text).toString() + return buildApiRestrictionsLookup(json.decodeFromString>(normalizedJson)) } override fun getDefaultValue(): RestrictionsLookup { @@ -311,7 +345,8 @@ class SplitModeApiRestrictionsService( readMode = readMode, ) { override fun parse(text: String): PredefinedModuleKindsLookup { - return buildPredefinedModuleKindsLookup(json.decodeFromString>(text)) + val normalizedJson = json5.readTree(text).toString() + return buildPredefinedModuleKindsLookup(json.decodeFromString>(normalizedJson)) } override fun getDefaultValue(): PredefinedModuleKindsLookup { @@ -321,13 +356,14 @@ class SplitModeApiRestrictionsService( } private fun buildApiRestrictionsLookup(data: List): RestrictionsLookup { - val codeRestrictions = mutableMapOf() + val codeRestrictions = mutableMapOf() val extensionPointToApiName = mutableMapOf() val apiHints = mutableMapOf() for (restriction in data) { val targetModuleKind = toTargetModuleKind(restriction) - val existingCodeRestriction = codeRestrictions.putIfAbsent(restriction.apiName, targetModuleKind) + val apiRestriction = ApiRestrictionMatch(targetModuleKind, parseApiRestrictionKind(restriction.restrictionKind)) + val existingCodeRestriction = codeRestrictions.putIfAbsent(restriction.apiName, apiRestriction) check(existingCodeRestriction == null) { "Duplicate API restriction for '${restriction.apiName}'" } @@ -337,7 +373,7 @@ class SplitModeApiRestrictionsService( for (extensionPointName in restriction.extensionPointNames) { val existingApiName = extensionPointToApiName[extensionPointName] val existingExtensionPointRestriction = if (existingApiName == null) null else codeRestrictions[existingApiName] - check(existingExtensionPointRestriction == null || existingExtensionPointRestriction == targetModuleKind) { + check(existingExtensionPointRestriction == null || existingExtensionPointRestriction == apiRestriction) { "Conflicting extension point restriction for '$extensionPointName'" } if (existingApiName == null) { @@ -354,50 +390,47 @@ class SplitModeApiRestrictionsService( } private fun buildPredefinedModuleKindsLookup(data: List): PredefinedModuleKindsLookup { - val moduleNames = mutableMapOf() - val pluginIds = mutableMapOf() - val descriptorPaths = mutableMapOf() + val moduleIds = mutableMapOf() + val pluginIds = mutableMapOf() + val descriptorPaths = mutableMapOf() - for ((moduleName, pluginId, descriptorRelativePath, moduleKindId) in data) { + for ((entryKindId, entryId, moduleName, descriptorRelativePath, moduleKindId, apiUsagePolicyId) in data) { val moduleKind = parsePredefinedModuleKind(moduleKindId) - val hasModuleName = !moduleName.isNullOrBlank() - val hasPluginId = !pluginId.isNullOrBlank() - - check(hasModuleName != hasPluginId) { - "Predefined module kind must specify exactly one of moduleName or pluginId" - } - check(descriptorRelativePath == null || hasModuleName) { - "descriptorRelativePath is only supported together with moduleName" - } - check(descriptorRelativePath == null || descriptorRelativePath.isNotBlank()) { - "descriptorRelativePath must not be blank" - } - - if (descriptorRelativePath != null) { - val selector = DescriptorPathSelector(moduleName!!, descriptorRelativePath) - val previousModuleKind = descriptorPaths.putIfAbsent(selector, moduleKind) - check(previousModuleKind == null) { - "Duplicate predefined module kind for descriptor '${selector.descriptorRelativePath}' in module '${selector.moduleName}'" + val moduleInfo = PredefinedModuleKindInfo(moduleKind, parseApiUsagePolicy(apiUsagePolicyId)) + when (parsePredefinedModuleKindEntryKind(entryKindId)) { + PredefinedModuleKindEntryKind.MODULE_ID -> { + check(moduleName == null) { "Predefined moduleId entry must not specify moduleName" } + check(descriptorRelativePath == null) { "Predefined moduleId entry must not specify descriptorRelativePath" } + check(!entryId.isNullOrBlank()) { "Predefined moduleId entry must specify non-blank id" } + val previousModuleKind = moduleIds.putIfAbsent(entryId, moduleInfo) + check(previousModuleKind == null) { + "Duplicate predefined module kind for module id '$entryId'" + } } - continue - } - - if (hasModuleName) { - val previousModuleKind = moduleNames.putIfAbsent(moduleName, moduleKind) - check(previousModuleKind == null) { - "Duplicate predefined module kind for module '$moduleName'" + PredefinedModuleKindEntryKind.PLUGIN_ID -> { + check(moduleName == null) { "Predefined pluginId entry must not specify moduleName" } + check(descriptorRelativePath == null) { "Predefined pluginId entry must not specify descriptorRelativePath" } + check(!entryId.isNullOrBlank()) { "Predefined pluginId entry must specify non-blank id" } + val previousModuleKind = pluginIds.putIfAbsent(entryId, moduleInfo) + check(previousModuleKind == null) { + "Duplicate predefined module kind for plugin/module id '$entryId'" + } } - } - else { - val previousModuleKind = pluginIds.putIfAbsent(pluginId!!, moduleKind) - check(previousModuleKind == null) { - "Duplicate predefined module kind for plugin/module id '$pluginId'" + PredefinedModuleKindEntryKind.XML_DESCRIPTOR_FILE -> { + check(entryId == null) { "Predefined xmlDescriptorFile entry must not specify id" } + check(!moduleName.isNullOrBlank()) { "Predefined xmlDescriptorFile entry must specify non-blank moduleName" } + check(!descriptorRelativePath.isNullOrBlank()) { "Predefined xmlDescriptorFile entry must specify non-blank descriptorRelativePath" } + val selector = DescriptorPathSelector(moduleName, descriptorRelativePath) + val previousModuleKind = descriptorPaths.putIfAbsent(selector, moduleInfo) + check(previousModuleKind == null) { + "Duplicate predefined module kind for descriptor '${selector.descriptorRelativePath}' in module '${selector.moduleName}'" + } } } } return PredefinedModuleKindsLookup( - moduleNames = moduleNames, + moduleIds = moduleIds, pluginIds = pluginIds, descriptorPaths = descriptorPaths, ) @@ -426,6 +459,27 @@ class SplitModeApiRestrictionsService( return moduleKind } + private fun parseApiRestrictionKind(restrictionKindId: String): ApiRestrictionKind { + return ApiRestrictionKind.entries.firstOrNull { it.id.equals(restrictionKindId, ignoreCase = true) } + ?: error("Unknown split-mode API restriction kind '$restrictionKindId'") + } + + private fun parsePredefinedModuleKindEntryKind(entryKindId: String): PredefinedModuleKindEntryKind { + return PredefinedModuleKindEntryKind.entries.firstOrNull { it.id == entryKindId } + ?: error("Unknown predefined module kind entry kind '$entryKindId'") + } + + private fun parseApiUsagePolicy(apiUsagePolicyId: String?): ApiUsagePolicy { + if (apiUsagePolicyId == null) { + return ApiUsagePolicy.DEFAULT + } + return when (apiUsagePolicyId) { + ApiUsagePolicy.DEFAULT.id -> ApiUsagePolicy.DEFAULT + ApiUsagePolicy.BACKEND_WITH_NON_UI_API_PERMIT.id -> ApiUsagePolicy.BACKEND_WITH_NON_UI_API_PERMIT + else -> error("Unknown split-mode API usage policy '$apiUsagePolicyId'") + } + } + private fun parsePredefinedModuleKind(moduleKindId: String): ModuleKind { val moduleKind = parseModuleKindId(moduleKindId) check(moduleKind != ModuleKind.MIXED) { "'mixed' is not supported in predefined module kinds" } @@ -467,13 +521,13 @@ class SplitModeApiRestrictionsService( } private data class RestrictionsLookup( - val codeRestrictions: Map = emptyMap(), + val codeRestrictions: Map = emptyMap(), val extensionPointToApiName: Map = emptyMap(), val apiHints: Map = emptyMap(), ) private data class RestrictionsSnapshot( - val codeRestrictions: Map = emptyMap(), + val codeRestrictions: Map = emptyMap(), val extensionPointToApiName: Map = emptyMap(), val apiHints: Map = emptyMap(), val predefinedModuleKinds: PredefinedModuleKindsLookup = PredefinedModuleKindsLookup(), @@ -481,25 +535,36 @@ class SplitModeApiRestrictionsService( internal data class PredefinedModuleKindMatch( val moduleKind: ModuleKind, + val apiUsagePolicy: ApiUsagePolicy, val cacheKey: String, val reasoning: @NlsSafe String, ) + internal data class ApiRestrictionMatch( + val targetModuleKind: ModuleKind, + val restrictionKind: ApiRestrictionKind, + ) + private data class DescriptorPathSelector( val moduleName: String, val descriptorRelativePath: String, ) private data class PredefinedModuleKindsLookup( - val moduleNames: Map = emptyMap(), - val pluginIds: Map = emptyMap(), - val descriptorPaths: Map = emptyMap(), + val moduleIds: Map = emptyMap(), + val pluginIds: Map = emptyMap(), + val descriptorPaths: Map = emptyMap(), ) { fun size(): Int { - return moduleNames.size + pluginIds.size + descriptorPaths.size + return moduleIds.size + pluginIds.size + descriptorPaths.size } } + private data class PredefinedModuleKindInfo( + val moduleKind: ModuleKind, + val apiUsagePolicy: ApiUsagePolicy, + ) + @Serializable private data class ApiRestriction( @SerialName("apiName") @@ -511,22 +576,31 @@ class SplitModeApiRestrictionsService( @SerialName("targetModules") val targetModules: List, + @SerialName("restrictionKind") + val restrictionKind: String = ApiRestrictionKind.GENERIC_PLATFORM_API.id, + @SerialName("hint") val hint: @Nls String? = null, ) @Serializable private data class PredefinedModuleKind( + @SerialName("kind") + val kind: String, + + @SerialName("id") + val id: String? = null, + @SerialName("moduleName") val moduleName: String? = null, - @SerialName("pluginId") - val pluginId: String? = null, - @SerialName("descriptorRelativePath") val descriptorRelativePath: String? = null, @SerialName("moduleKind") val moduleKind: String, + + @SerialName("apiUsagePolicy") + val apiUsagePolicy: String? = null, ) } diff --git a/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeDescriptorDependencyAnalyzer.kt b/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeDescriptorDependencyAnalyzer.kt index 99d7c0ad7259..7aa5a7dbe1a3 100644 --- a/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeDescriptorDependencyAnalyzer.kt +++ b/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeDescriptorDependencyAnalyzer.kt @@ -87,7 +87,9 @@ internal object SplitModeDescriptorDependencyAnalyzer { val descriptorLocation = getDescriptorLocation(ideaPlugin) val accumulator = DependencyFactsAccumulator(descriptorLocation) val directDependencies = collectDirectDependencies(ideaPlugin).toList() - for ((dependencyName, dependencyDescriptors) in directDependencies) { + for (dependency in directDependencies) { + val dependencyName = dependency.name + val dependencyDescriptors = dependency.resolveDescriptors() var hasPredefinedDependencyFacts = false for (dependencyDescriptor in dependencyDescriptors) { val predefinedDependencyFacts = getPredefinedDependencyFacts(dependencyDescriptor) ?: continue @@ -118,7 +120,9 @@ internal object SplitModeDescriptorDependencyAnalyzer { return accumulator.toDependencyFacts() } - for ((dependencyName, dependencyDescriptors) in directDependencies) { + for (dependency in directDependencies) { + val dependencyName = dependency.name + val dependencyDescriptors = dependency.resolveDescriptors() for (dependencyDescriptor in dependencyDescriptors) { val dependencyFacts = getOrComputeOwnDescriptorDependencyFacts(dependencyDescriptor, analysisStates) accumulator.recordTransitiveDependencies(dependencyName, dependencyFacts) @@ -162,7 +166,9 @@ internal object SplitModeDescriptorDependencyAnalyzer { continue } val dependencyName = dependency.rawText ?: dependency.stringValue ?: continue - yield(DirectDependency(dependencyName, resolvePluginDependencyDescriptors(ideaPlugin, dependencyName))) + yield(DirectDependency(dependencyName) { + resolvePluginDependencyDescriptors(ideaPlugin, dependencyName) + }) } val pluginDependencies = ideaPlugin.dependencies @@ -172,12 +178,16 @@ internal object SplitModeDescriptorDependencyAnalyzer { for (moduleDescriptor in pluginDependencies.moduleEntry) { val dependencyName = moduleDescriptor.name.stringValue ?: continue - yield(DirectDependency(dependencyName, listOfNotNull(moduleDescriptor.name.value))) + yield(DirectDependency(dependencyName) { + listOfNotNull(moduleDescriptor.name.value) + }) } for (pluginDescriptor in pluginDependencies.plugin) { val dependencyName = pluginDescriptor.id.stringValue ?: continue - yield(DirectDependency(dependencyName, resolvePluginDependencyDescriptors(ideaPlugin, dependencyName))) + yield(DirectDependency(dependencyName) { + resolvePluginDependencyDescriptors(ideaPlugin, dependencyName) + }) } } .distinctBy { it.name } @@ -378,10 +388,14 @@ private class DependencyFactsAccumulator( } } -private data class DirectDependency( +private class DirectDependency( val name: String, - val descriptors: List, -) + descriptorResolver: () -> List, +) { + private val descriptors by lazy(LazyThreadSafetyMode.NONE) { descriptorResolver() } + + fun resolveDescriptors(): List = descriptors +} private data class ContentModuleDependency( val moduleName: String, diff --git a/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeModuleKindIcons.kt b/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeModuleKindIcons.kt index ac2c9d1ef6bd..4fd6db3b588f 100644 --- a/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeModuleKindIcons.kt +++ b/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeModuleKindIcons.kt @@ -5,25 +5,33 @@ import com.intellij.devkit.core.icons.DevkitCoreIcons.BackendModule import com.intellij.devkit.core.icons.DevkitCoreIcons.FrontendModule import com.intellij.devkit.core.icons.DevkitCoreIcons.SharedModule import com.intellij.icons.AllIcons +import com.intellij.lang.xml.XMLLanguage +import com.intellij.openapi.application.smartReadAction import com.intellij.openapi.project.DumbService +import com.intellij.openapi.project.Project import com.intellij.openapi.util.NlsSafe -import com.intellij.openapi.util.registry.Registry +import com.intellij.openapi.util.registry.RegistryManager +import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiFile +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import com.intellij.psi.xml.XmlFile +import com.intellij.ui.IconDeferrer import org.jetbrains.annotations.ApiStatus +import org.jetbrains.idea.devkit.util.DescriptorUtil import javax.swing.Icon @ApiStatus.Internal object SplitModeModuleKindIcons { - @JvmStatic - fun isCustomIconsEnabled(): Boolean { - return Registry.`is`("devkit.split.mode.custom.icons", false) - } - @JvmStatic fun getDescriptorIcon(descriptorFile: PsiFile): Icon? { - if (!isCustomIconsEnabled() || DumbService.isDumb(descriptorFile.project)) return null - val kind = recognizeSplitModeModuleKind(descriptorFile)?.kind ?: return null - return getKindIcon(kind, descriptorFile.name) + val fastIcon = getFastDescriptorIcon(descriptorFile) ?: return null + if (!isAccurateIconsEnabled() || DumbService.isDumb(descriptorFile.project)) return fastIcon + + val virtualFile = descriptorFile.virtualFile ?: return fastIcon + return IconDeferrer.getInstance().deferAsync(fastIcon, DescriptorIconDeferredKey(descriptorFile.project, virtualFile)) { + getAccurateDescriptorIcon(descriptorFile, fastIcon) + } } @JvmStatic @@ -43,4 +51,64 @@ object SplitModeModuleKindIcons { private fun followsSharedModuleNamingConvention(moduleName: @NlsSafe String): Boolean { return moduleName.endsWith(".shared.xml") || moduleName.endsWith(".common.xml") || moduleName.contains(".frontback") } + + private fun isAccurateIconsEnabled(): Boolean { + return RegistryManager.getInstance().`is`("devkit.split.mode.custom.icons") + } + + private fun getFastDescriptorIcon(descriptorFile: PsiFile): Icon? { + val kind = getOrComputeModuleKindFast(descriptorFile) ?: return null + return getKindIcon(kind, descriptorFile.name) + } + + private suspend fun getAccurateDescriptorIcon(descriptorFile: PsiFile, fastIcon: Icon): Icon { + return smartReadAction(descriptorFile.project) { + val kind = recognizeSplitModeModuleKind(descriptorFile)?.kind + kind?.let { getKindIcon(it, descriptorFile.name) } ?: fastIcon + } + } + + private fun recognizeLightweightModuleKind(descriptorFile: PsiFile): SplitModeApiRestrictionsService.ModuleKind? { + val explicitDependencyKinds = collectExplicitDependencyKinds(descriptorFile) + return when { + explicitDependencyKinds.size > 1 -> SplitModeApiRestrictionsService.ModuleKind.MIXED + explicitDependencyKinds.size == 1 -> explicitDependencyKinds.single() + else -> inferModuleKindFromFileName(descriptorFile.name) + } + } + + private fun getOrComputeModuleKindFast(descriptorFile: PsiFile): SplitModeApiRestrictionsService.ModuleKind? { + return CachedValuesManager.getCachedValue(descriptorFile) { + CachedValueProvider.Result.create( + recognizeLightweightModuleKind(descriptorFile), + descriptorFile.manager.modificationTracker.forLanguage(XMLLanguage.INSTANCE), + ) + } + } + + private fun collectExplicitDependencyKinds(descriptorFile: PsiFile): Set { + val xmlFile = descriptorFile as? XmlFile ?: return emptySet() + val ideaPlugin = DescriptorUtil.getIdeaPlugin(xmlFile) ?: return emptySet() + val explicitDependencyKinds = LinkedHashSet() + for (dependencyName in SplitModeDescriptorDependencyAnalyzer.collectDirectDependencyNames(ideaPlugin)) { + val dependencyKind = recognizeExplicitDependencyKind(dependencyName) ?: continue + explicitDependencyKinds.add(dependencyKind) + } + return explicitDependencyKinds + } + + private fun inferModuleKindFromFileName(descriptorFileName: @NlsSafe String): SplitModeApiRestrictionsService.ModuleKind? { + return when { + descriptorFileName == "plugin.xml" -> SplitModeApiRestrictionsService.ModuleKind.SHARED + descriptorFileName.endsWith(".frontend.xml") || descriptorFileName.endsWith(".frontend.split.xml") -> SplitModeApiRestrictionsService.ModuleKind.FRONTEND + descriptorFileName.endsWith(".backend.xml") || descriptorFileName.endsWith(".backend.split.xml") -> SplitModeApiRestrictionsService.ModuleKind.BACKEND + followsSharedModuleNamingConvention(descriptorFileName) -> SplitModeApiRestrictionsService.ModuleKind.SHARED + else -> null + } + } } + +private data class DescriptorIconDeferredKey( + val project: Project, + val virtualFile: VirtualFile, +) diff --git a/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeModuleKindResolver.kt b/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeModuleKindResolver.kt index a556b2356976..89a0c400c980 100644 --- a/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeModuleKindResolver.kt +++ b/plugins/devkit/devkit-core/src/inspections/remotedev/analysis/SplitModeModuleKindResolver.kt @@ -1,15 +1,27 @@ // Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.idea.devkit.inspections.remotedev.analysis +import com.intellij.lang.xml.XMLLanguage import com.intellij.openapi.diagnostic.debug import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.roots.ProjectRootModificationTracker +import com.intellij.openapi.util.Key +import com.intellij.psi.util.CachedValue +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.xml.XmlFile import org.jetbrains.idea.devkit.inspections.remotedev.SplitModeInspectionUtil.findDependingContentModuleEntriesInFile import org.jetbrains.idea.devkit.module.PluginModuleType import org.jetbrains.idea.devkit.util.DescriptorUtil +private val MODULE_ID_ANALYSIS_KEY = + Key.create>("SplitModeModuleKindResolver.moduleIdAnalysis") + +private val MODULE_DESCRIPTOR_ANALYSIS_KEY = + Key.create>("SplitModeModuleKindResolver.moduleDescriptorAnalysis") + internal object SplitModeModuleKindResolver { private val LOG = logger() @@ -20,17 +32,47 @@ internal object SplitModeModuleKindResolver { return expectedKind.accepts(actualApiUsageModuleKind.kind) } + fun isApiUsageAllowed( + actualModuleAnalysis: ModuleAnalysis, + apiRestriction: SplitModeApiRestrictionsService.ApiRestrictionMatch, + ): Boolean { + val actualModuleKind = actualModuleAnalysis.resolvedModuleKind + return doesApiKindMatchExpectedModuleKind(actualModuleKind, apiRestriction.targetModuleKind) + || actualModuleKind.kind == SplitModeApiRestrictionsService.ModuleKind.BACKEND + && actualModuleAnalysis.apiUsagePolicy == ApiUsagePolicy.BACKEND_WITH_NON_UI_API_PERMIT + && apiRestriction.restrictionKind == SplitModeApiRestrictionsService.ApiRestrictionKind.GENERIC_PLATFORM_API + } + fun getOrComputeModuleAnalysis(module: Module, descriptorFile: XmlFile? = null): ModuleAnalysis { val xmlDescriptor = descriptorFile ?: PluginModuleType.getContentModuleDescriptorXml(module) ?: PluginModuleType.getPluginXml(module) if (xmlDescriptor == null) { return ModuleAnalysis(ResolvedModuleKind(SplitModeApiRestrictionsService.ModuleKind.SHARED, "")) } + val cacheKey = if (descriptorFile == null) MODULE_ID_ANALYSIS_KEY else MODULE_DESCRIPTOR_ANALYSIS_KEY + return CachedValuesManager.getManager(module.project).getCachedValue(xmlDescriptor, cacheKey, { + CachedValueProvider.Result.create( + computeModuleAnalysis(module, descriptorFile), + ProjectRootModificationTracker.getInstance(module.project), + xmlDescriptor.manager.modificationTracker.forLanguage(XMLLanguage.INSTANCE), + ) + }, false) + } + + private fun computeModuleAnalysis(module: Module, descriptorFile: XmlFile? = null): ModuleAnalysis { + val xmlDescriptor = descriptorFile ?: PluginModuleType.getContentModuleDescriptorXml(module) ?: PluginModuleType.getPluginXml(module) + if (xmlDescriptor == null) { + return ModuleAnalysis(ResolvedModuleKind(SplitModeApiRestrictionsService.ModuleKind.SHARED, "")) + } + val parsedXmlDescriptor = DescriptorUtil.getIdeaPlugin(xmlDescriptor) val predefinedModuleKind = SplitModeApiRestrictionsService.getInstance(module.project).getPredefinedModuleKind(module, xmlDescriptor, parsedXmlDescriptor) if (predefinedModuleKind != null) { - return ModuleAnalysis(ResolvedModuleKind(predefinedModuleKind.moduleKind, predefinedModuleKind.reasoning)) + return ModuleAnalysis( + resolvedModuleKind = ResolvedModuleKind(predefinedModuleKind.moduleKind, predefinedModuleKind.reasoning), + apiUsagePolicy = predefinedModuleKind.apiUsagePolicy, + ) } if (parsedXmlDescriptor == null) { return ModuleAnalysis(ResolvedModuleKind(SplitModeApiRestrictionsService.ModuleKind.SHARED, "")) diff --git a/plugins/devkit/devkit-core/src/navigation/DescriptorSplitModeInlayHintsProvider.kt b/plugins/devkit/devkit-core/src/navigation/DescriptorSplitModeInlayHintsProvider.kt new file mode 100644 index 000000000000..1b81e5ee1632 --- /dev/null +++ b/plugins/devkit/devkit-core/src/navigation/DescriptorSplitModeInlayHintsProvider.kt @@ -0,0 +1,147 @@ +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.idea.devkit.navigation + +import com.intellij.codeInsight.hints.declarative.HintFormat +import com.intellij.codeInsight.hints.declarative.InlayHintsCollector +import com.intellij.codeInsight.hints.declarative.InlayHintsProvider +import com.intellij.codeInsight.hints.declarative.InlayTreeSink +import com.intellij.codeInsight.hints.declarative.InlineInlayPosition +import com.intellij.codeInsight.hints.declarative.OwnBypassCollector +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.registry.Registry +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.PsiFile +import com.intellij.psi.xml.XmlFile +import com.intellij.psi.xml.XmlToken +import com.intellij.psi.xml.XmlTokenType +import com.intellij.xml.util.XmlUtil +import org.jetbrains.idea.devkit.DevKitBundle +import org.jetbrains.idea.devkit.inspections.remotedev.SplitModeInspectionUtil.findDependingContentModuleEntriesInFile +import org.jetbrains.idea.devkit.inspections.remotedev.analysis.SplitModeApiRestrictionsService.ModuleKind +import org.jetbrains.idea.devkit.inspections.remotedev.analysis.recognizeSplitModeModuleKind +import org.jetbrains.idea.devkit.module.PluginModuleType + +internal class DescriptorSplitModeInlayHintsProvider : InlayHintsProvider { + + override fun createCollector(file: PsiFile, editor: Editor): InlayHintsCollector? { + if (!isDescriptorSplitModeInlayEnabled()) return null + val xmlFile = file as? XmlFile ?: return null + if (findIdeaPluginRootTagNameToken(xmlFile) == null) return null + return DescriptorSplitModeInlayHintsCollector() + } + + private class DescriptorSplitModeInlayHintsCollector : OwnBypassCollector { + override fun collectHintsForFile(file: PsiFile, sink: InlayTreeSink) { + val xmlFile = file as? XmlFile ?: return + val tagNameToken = findIdeaPluginRootTagNameToken(xmlFile) ?: return + val inlayInfo = getDescriptorSplitModeInlayInfo(xmlFile) ?: return + + sink.addPresentation( + InlineInlayPosition(tagNameToken.textRange.endOffset, true), + tooltip = inlayInfo.tooltip, + hintFormat = HintFormat.default, + ) { + text(inlayInfo.text) + } + } + + override fun shouldSuggestToggling(project: Project, editor: Editor, file: PsiFile): Boolean { + return isDescriptorSplitModeInlayEnabled() && file is XmlFile && findIdeaPluginRootTagNameToken(file) != null + } + } +} + +internal data class DescriptorSplitModeInlayInfo( + val text: String, + val tooltip: String, +) + +internal fun getDescriptorSplitModeInlayInfo(xmlFile: XmlFile): DescriptorSplitModeInlayInfo? { + val recognizedModuleKind = recognizeSplitModeModuleKind(xmlFile) ?: return null + val descriptorKindMessageKey = getDescriptorKindMessageKey(xmlFile) + val descriptorKind = DevKitBundle.message(descriptorKindMessageKey) + return DescriptorSplitModeInlayInfo( + DevKitBundle.message( + "inlay.split.mode.module.kind.presentation", + getPresentableModuleKind(recognizedModuleKind.kind), + descriptorKind, + ), + DevKitBundle.message( + "inlay.split.mode.module.kind.tooltip", + descriptorKind, + getCompatibilityTarget(recognizedModuleKind.kind), + normalizeTooltipReasoning(recognizedModuleKind.reasoning), + ), + ) +} + +private fun normalizeTooltipReasoning(reasoning: String): String { + return reasoning.lineSequence().map(String::trim).filter(String::isNotEmpty).joinToString(" ") +} + +private fun isDescriptorSplitModeInlayEnabled(): Boolean { + return Registry.`is`("devkit.split.mode.inlay.plugin.descriptor.kind") +} + +private fun getPresentableModuleKind(kind: ModuleKind): String { + val presentableId = when (kind) { + ModuleKind.FRONTEND -> ModuleKind.FRONTEND.id + ModuleKind.BACKEND -> ModuleKind.BACKEND.id + ModuleKind.SHARED -> ModuleKind.SHARED.id + ModuleKind.MONOLITH, + ModuleKind.MIXED, + -> ModuleKind.MONOLITH.id + is ModuleKind.Composite -> kind.id + } + return StringUtil.capitalize(presentableId) +} + +private fun getCompatibilityTarget(kind: ModuleKind): String { + val messageKey = when (kind) { + ModuleKind.FRONTEND -> "inlay.split.mode.module.kind.compatibility.frontend" + ModuleKind.BACKEND -> "inlay.split.mode.module.kind.compatibility.backend" + ModuleKind.SHARED -> "inlay.split.mode.module.kind.compatibility.shared" + ModuleKind.MONOLITH, + ModuleKind.MIXED, + -> "inlay.split.mode.module.kind.compatibility.monolith" + is ModuleKind.Composite -> "inlay.split.mode.module.kind.compatibility.shared" + } + return DevKitBundle.message(messageKey) +} + +private fun getDescriptorKindMessageKey(xmlFile: XmlFile): String { + if (xmlFile.name == "plugin.xml") { + return "inlay.split.mode.module.kind.plugin" + } + if (isContentModuleDescriptor(xmlFile)) { + return "inlay.split.mode.module.kind.content.module" + } + return "inlay.split.mode.module.kind.xml.descriptor" +} + +private fun isContentModuleDescriptor(xmlFile: XmlFile): Boolean { + val module = ModuleUtilCore.findModuleForPsiElement(xmlFile) + if (PluginModuleType.getContentModuleDescriptorXml(module)?.virtualFile == xmlFile.virtualFile) { + return true + } + if (module != null && xmlFile.name in computePossibleContentModuleDescriptorNames(module.name)) { + return true + } + return findDependingContentModuleEntriesInFile(xmlFile).any() +} + +private fun computePossibleContentModuleDescriptorNames(moduleName: String): List { + return listOf( + "$moduleName.xml", + "${moduleName.replace(".main", "")}.xml", + "${moduleName.replace("_", ".").replace(".main", "")}.xml", + ) +} + +private fun findIdeaPluginRootTagNameToken(xmlFile: XmlFile): XmlToken? { + val rootTag = xmlFile.rootTag ?: return null + val tagNameToken = XmlUtil.getTokenOfType(rootTag, XmlTokenType.XML_NAME) ?: return null + return tagNameToken.takeIf(::isIdeaPluginElementNameLeaf) +} diff --git a/plugins/devkit/devkit-core/src/navigation/DescriptorsIncludingContentModuleLineMarkerProvider.kt b/plugins/devkit/devkit-core/src/navigation/DescriptorsIncludingContentModuleLineMarkerProvider.kt index 26862b1b18b8..9400b6ffbe6c 100644 --- a/plugins/devkit/devkit-core/src/navigation/DescriptorsIncludingContentModuleLineMarkerProvider.kt +++ b/plugins/devkit/devkit-core/src/navigation/DescriptorsIncludingContentModuleLineMarkerProvider.kt @@ -47,15 +47,6 @@ internal class DescriptorsIncludingContentModuleLineMarkerProvider : DevkitRelat } } - private fun isIdeaPluginElementNameLeaf(leaf: PsiElement): Boolean { - if (leaf !is XmlToken) return false - if (leaf.getNode().elementType != XmlTokenType.XML_NAME) return false - val prev = PsiTreeUtil.getPrevSiblingOfType(leaf, XmlToken::class.java) - if (prev == null || prev.node.elementType !== XmlTokenType.XML_START_TAG_START) return false - if (!leaf.textMatches("idea-plugin")) return false - return DomUtil.getDomElement(leaf) is IdeaPlugin - } - private fun findDependingContentModuleEntries(element: PsiElement): List { val xmlFile = element.containingFile as? XmlFile ?: return emptyList() return findDependingContentModuleEntriesInFile(xmlFile).toList() @@ -109,4 +100,13 @@ internal class DescriptorsIncludingContentModuleLineMarkerProvider : DevkitRelat return moduleDescriptor.loading.value?.value ?: "optional" } } -} \ No newline at end of file +} + +internal fun isIdeaPluginElementNameLeaf(leaf: PsiElement): Boolean { + if (leaf !is XmlToken) return false + if (leaf.getNode().elementType != XmlTokenType.XML_NAME) return false + val prev = PsiTreeUtil.getPrevSiblingOfType(leaf, XmlToken::class.java) + if (prev == null || prev.node.elementType !== XmlTokenType.XML_START_TAG_START) return false + if (!leaf.textMatches("idea-plugin")) return false + return DomUtil.getDomElement(leaf) is IdeaPlugin +} diff --git a/plugins/devkit/devkit-java-tests/BUILD.bazel b/plugins/devkit/devkit-java-tests/BUILD.bazel index cd19877bcd46..597850b02102 100644 --- a/plugins/devkit/devkit-java-tests/BUILD.bazel +++ b/plugins/devkit/devkit-java-tests/BUILD.bazel @@ -130,6 +130,8 @@ jvm_library( "//java/compiler:compiler-tests_test_lib", "//java/testFramework", "//java/testFramework:testFramework_test_lib", + "//plugins/junit", + "//plugins/junit:junit_test_lib", "//xml/dom-tests", "//xml/dom-tests:dom-tests_test_lib", "//plugins/properties:properties-backend", diff --git a/plugins/devkit/devkit-java-tests/intellij.devkit.java.tests.iml b/plugins/devkit/devkit-java-tests/intellij.devkit.java.tests.iml index c670a24091db..bef533ff9cdb 100644 --- a/plugins/devkit/devkit-java-tests/intellij.devkit.java.tests.iml +++ b/plugins/devkit/devkit-java-tests/intellij.devkit.java.tests.iml @@ -44,6 +44,7 @@ + diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/descriptor/PluginDescriptorSplitModeIconsTest.kt b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/descriptor/PluginDescriptorSplitModeIconsTest.kt index c6498be00711..c25ba738a6b7 100644 --- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/descriptor/PluginDescriptorSplitModeIconsTest.kt +++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/descriptor/PluginDescriptorSplitModeIconsTest.kt @@ -2,11 +2,11 @@ package org.jetbrains.idea.devkit.descriptor import com.intellij.devkit.core.icons.DevkitCoreIcons +import com.intellij.ide.IconProvider import com.intellij.icons.AllIcons.Nodes.Plugin import com.intellij.openapi.application.invokeAndWaitIfNeeded import com.intellij.openapi.application.runReadActionBlocking import com.intellij.openapi.project.IntelliJProjectUtil -import com.intellij.openapi.util.registry.Registry import com.intellij.psi.PsiManager import com.intellij.testFramework.IndexingTestUtil import com.intellij.testFramework.PsiTestUtil @@ -38,45 +38,85 @@ internal class PluginDescriptorSplitModeIconsTest { @Test @RegistryKey(key = "devkit.plugin.directory.icons", value = "true") - fun `descriptor and directory icons follow split mode kind`() { - Registry.get("devkit.split.mode.custom.icons").setValue(true) + @RegistryKey(key = "devkit.split.mode.custom.icons", value = "false") + fun `descriptor and directory icons use fast split mode kind when accurate icons are disabled`() { IntelliJProjectUtil.markAsIntelliJPlatformProject(project, true) try { - createModuleDescriptor("module.shared", "module.shared.xml", """ - - - """.trimIndent()) - createSharedPluginDescriptor(""" - - - """.trimIndent()) - createFrontendPluginDescriptor(""" - - - - - - """.trimIndent()) - createModuleDescriptor("module.backend", "module.backend.xml", """ - - - - - - """.trimIndent()) - IndexingTestUtil.waitUntilIndexesAreReady(project) + createSplitModeIconFixtures() assertFileIcon("shared.plugin/resources/META-INF/plugin.xml", Plugin) assertFileIcon("module.shared/resources/module.shared.xml", DevkitCoreIcons.SharedModule) assertFileIcon("frontend.plugin/resources/META-INF/plugin.xml", DevkitCoreIcons.FrontendModule) + assertFileIcon( + "intellij.transitive.frontend/resources/intellij.transitive.frontend.xml", + DevkitCoreIcons.FrontendModule, + ) assertFileIcon("module.backend/resources/module.backend.xml", DevkitCoreIcons.BackendModule) + assertFileIcon("transitive.consumer/resources/META-INF/plugin.xml", Plugin) + assertDirectoryIcon("frontend.plugin", DevkitCoreIcons.FrontendModule) + assertDirectoryIcon("transitive.consumer", Plugin) } finally { IntelliJProjectUtil.markAsIntelliJPlatformProject(project, false) - Registry.get("devkit.split.mode.custom.icons").setValue(false) } } + @Test + @RegistryKey(key = "devkit.plugin.directory.icons", value = "true") + @RegistryKey(key = "devkit.split.mode.custom.icons", value = "true") + fun `descriptor and directory icons resolve accurate split mode kind when enabled`() { + IntelliJProjectUtil.markAsIntelliJPlatformProject(project, true) + try { + createSplitModeIconFixtures() + + assertFileIcon("transitive.consumer/resources/META-INF/plugin.xml", DevkitCoreIcons.FrontendModule) + assertDirectoryIcon("transitive.consumer", DevkitCoreIcons.FrontendModule) + } + finally { + IntelliJProjectUtil.markAsIntelliJPlatformProject(project, false) + } + } + + private fun createSplitModeIconFixtures() { + createModuleDescriptor("module.shared", "module.shared.xml", """ + + + """.trimIndent()) + createSharedPluginDescriptor(""" + + + """.trimIndent()) + createFrontendPluginDescriptor(""" + + + + + + """.trimIndent()) + createModuleDescriptor("intellij.transitive.frontend", "intellij.transitive.frontend.xml", """ + + + + + + """.trimIndent()) + createModuleDescriptor("module.backend", "module.backend.xml", """ + + + + + + """.trimIndent()) + createPluginDescriptor("transitive.consumer", """ + + + + + + """.trimIndent()) + IndexingTestUtil.waitUntilIndexesAreReady(project) + } + private fun createModuleDescriptor(moduleName: String, descriptorName: String, descriptorText: String) { val moduleRoot = projectModel.baseProjectDir.newVirtualDirectory(moduleName) val module = PsiTestUtil.addModule(project, PluginModuleType.getInstance(), moduleName, moduleRoot) @@ -86,18 +126,14 @@ internal class PluginDescriptorSplitModeIconsTest { } private fun createFrontendPluginDescriptor(descriptorText: String) { - val moduleName = "frontend.plugin" - val moduleRoot = projectModel.baseProjectDir.newVirtualDirectory(moduleName) - val module = PsiTestUtil.addModule(project, PluginModuleType.getInstance(), moduleName, moduleRoot) - val resourcesRoot = projectModel.baseProjectDir.newVirtualDirectory("$moduleName/resources") - PsiTestUtil.addSourceRoot(module, resourcesRoot, JavaResourceRootType.RESOURCE) - val descriptorFile = - projectModel.baseProjectDir.newVirtualFile("$moduleName/resources/META-INF/plugin.xml", descriptorText.toByteArray()) - PluginBuildConfiguration.getInstance(module)!!.setPluginXmlFromVirtualFile(descriptorFile) + createPluginDescriptor("frontend.plugin", descriptorText) } private fun createSharedPluginDescriptor(descriptorText: String) { - val moduleName = "shared.plugin" + createPluginDescriptor("shared.plugin", descriptorText) + } + + private fun createPluginDescriptor(moduleName: String, descriptorText: String) { val moduleRoot = projectModel.baseProjectDir.newVirtualDirectory(moduleName) val module = PsiTestUtil.addModule(project, PluginModuleType.getInstance(), moduleName, moduleRoot) val resourcesRoot = projectModel.baseProjectDir.newVirtualDirectory("$moduleName/resources") @@ -109,13 +145,28 @@ internal class PluginDescriptorSplitModeIconsTest { private fun assertFileIcon(relativePath: String, expectedIcon: Icon) { assertIcon(expectedIcon) { - PsiManager.getInstance(project).findFile(projectModel.baseProjectDir.virtualFileRoot.findFileByRelativePath(relativePath)!!)!! + val file = PsiManager.getInstance(project).findFile(projectModel.baseProjectDir.virtualFileRoot.findFileByRelativePath(relativePath)!!)!! + PsiIconUtil.getIconFromProviders(file, 0) } } - private fun assertIcon(expectedIcon: Icon, psiElementProvider: () -> com.intellij.psi.PsiElement) { + private fun assertDirectoryIcon(relativePath: String, expectedIcon: Icon) { + assertIcon(expectedIcon) { + val directory = PsiManager.getInstance(project) + .findDirectory(projectModel.baseProjectDir.virtualFileRoot.findFileByRelativePath(relativePath)!!)!! + getScaffoldingDirectoryIconProvider().getIcon(directory, 0) + } + } + + private fun getScaffoldingDirectoryIconProvider(): IconProvider { + return IconProvider.EXTENSION_POINT_NAME.extensionList.single { + it.javaClass.name == "com.intellij.devkit.scaffolding.ScaffoldingDirectoryIconProvider" + } + } + + private fun assertIcon(expectedIcon: Icon, iconProvider: () -> Icon?) { val iconFromProviders = runReadActionBlocking { - PsiIconUtil.getIconFromProviders(psiElementProvider(), 0) + iconProvider() } checkNotNull(iconFromProviders) awaitDeferredIcons(iconFromProviders) @@ -130,13 +181,13 @@ internal class PluginDescriptorSplitModeIconsTest { is DeferredIconImpl<*> -> runBlocking { icon.awaitEvaluation() } - is RetrievableIcon -> awaitDeferredIcons(icon.retrieveIcon()) is CompositeIcon -> { for (i in 0 until icon.iconCount) { val nestedIcon = icon.getIcon(i) ?: continue awaitDeferredIcons(nestedIcon) } } + is RetrievableIcon -> awaitDeferredIcons(icon.retrieveIcon()) } } } diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/MissingFrontendOrBackendRuntimeDependencyInspectionTest.kt b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/MissingFrontendOrBackendRuntimeDependencyInspectionTest.kt index 4389faf6b390..1c70394f903a 100644 --- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/MissingFrontendOrBackendRuntimeDependencyInspectionTest.kt +++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/MissingFrontendOrBackendRuntimeDependencyInspectionTest.kt @@ -1,18 +1,12 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.idea.devkit.inspections -import com.intellij.openapi.command.WriteCommandAction -import com.intellij.openapi.fileEditor.FileEditorManager -import com.intellij.openapi.project.IntelliJProjectUtil -import com.intellij.openapi.vfs.VfsUtil -import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.psi.PsiFile import com.intellij.testFramework.PsiTestUtil import com.intellij.testFramework.fixtures.CodeInsightTestFixture import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase import org.intellij.lang.annotations.Language -import org.jetbrains.idea.devkit.inspections.remotedev.EXCLUSIONS_RELATIVE_PATH import org.jetbrains.idea.devkit.inspections.remotedev.MissingFrontendOrBackendRuntimeDependencyInspection import org.jetbrains.idea.devkit.module.PluginModuleType @@ -49,48 +43,6 @@ internal class MissingFrontendOrBackendRuntimeDependencyInspectionTest : JavaCod fun testShouldNotCrashOnCyclicDependencyInFrontend() = testShouldNotCrashOnCyclicDependency("frontend") fun testShouldNotCrashOnCyclicDependencyInBackend() = testShouldNotCrashOnCyclicDependency("backend") - fun testShouldNotReportExcludedFrontendDependency() { - myFixture.addSplitModeExclusionsFile(splitModeExclusionsJson( - missingRuntimeDependencyExclusion("intellij.test.feature.frontend.xml", 2) - )) - - val testedFile = myFixture.addXmlFile("intellij.test.feature.frontend.xml", """ - - - - - - """.trimIndent() - ) - testHighlighting(testedFile) - } - - fun testShouldReportWhenExclusionsFileIsEmpty() { - myFixture.addSplitModeExclusionsFile("") - testReportDependency("frontend") - } - - fun testExclusionsInvalidatedAfterJsonChange() { - val exclusionsFile = myFixture.addSplitModeExclusionsFile("""{ "exclusions": [] }""") - val testedFile = myFixture.addXmlFile("intellij.test.feature.frontend.xml", """ - - - - - - """.trimIndent() - ) - assertTrue(hasMissingFrontendRuntimeDependencyWarning(testedFile)) - - WriteCommandAction.runWriteCommandAction(project) { - VfsUtil.saveText(exclusionsFile.virtualFile, splitModeExclusionsJson( - missingRuntimeDependencyExclusion("intellij.test.feature.frontend.xml", 2) - )) - } - - assertFalse(hasMissingFrontendRuntimeDependencyWarning(testedFile)) - } - private fun testReportDependency(frontendOrBackend: String) { val testedFile = myFixture.addXmlFile("intellij.test.feature.$frontendOrBackend.xml", """ @@ -212,13 +164,6 @@ internal class MissingFrontendOrBackendRuntimeDependencyInspectionTest : JavaCod private fun testHighlighting(testedFile: PsiFile) { myFixture.testHighlighting(true, true, true, testedFile.virtualFile) } - - private fun hasMissingFrontendRuntimeDependencyWarning(testedFile: PsiFile): Boolean { - val expectedDescription = "The name of the current module 'intellij.test.feature.frontend' ends with '.frontend', " + - "but the dependency on 'intellij.platform.frontend' is missing" - myFixture.configureFromExistingVirtualFile(testedFile.virtualFile) - return myFixture.doHighlighting().any { it.description == expectedDescription } - } } internal class MissingFrontendOrBackendRuntimeDependencyFixTest : JavaCodeInsightFixtureTestCase() { @@ -236,57 +181,6 @@ internal class MissingFrontendOrBackendRuntimeDependencyFixTest : JavaCodeInsigh fun testAddingBackendDependencyWhenDependenciesElementDoesNotExist() = testAddingDependencyWhenDependenciesElementDoesNotExist("backend") - fun testAddToExclusionsFixIsHiddenOutsideIntelliJProject() { - IntelliJProjectUtil.markAsIntelliJPlatformProject(project, false) - - val testedFile = myFixture.addXmlFile("intellij.test.feature.frontend.xml", """ - plugin> - - - """.trimIndent() - ) - myFixture.configureFromExistingVirtualFile(testedFile.virtualFile) - - val splitModeFixes = myFixture.getAllQuickFixes().map { it.text } - assertFalse(splitModeFixes.contains(addToExclusionsFixName())) - } - - fun testAddToExclusionsFixIsLastAndAppendsEntry() { - IntelliJProjectUtil.markAsIntelliJPlatformProject(project, true) - - val testedFile = myFixture.addXmlFile("intellij.test.feature.frontend.xml", """ - plugin> - - - """.trimIndent() - ) - myFixture.configureFromExistingVirtualFile(testedFile.virtualFile) - - val dependencyFixName = addRuntimeDependencyFixName("frontend") - val exclusionsFixName = addToExclusionsFixName() - val splitModeFixes = myFixture.getAllQuickFixes() - .map { it.text } - .filter { it == dependencyFixName || it == exclusionsFixName } - assertSameElements(splitModeFixes, dependencyFixName, exclusionsFixName) - assertEquals(exclusionsFixName, splitModeFixes.last()) - - val intention = myFixture.findSingleIntention(exclusionsFixName) - myFixture.launchAction(intention) - - val exclusionsFile = myFixture.findFileInTempDir(EXCLUSIONS_RELATIVE_PATH) - val exclusionsText = VfsUtilCore.loadText(exclusionsFile) - assertTrue(exclusionsText.contains("\"inspection\": \"MissingFrontendOrBackendRuntimeDependency\"")) - assertTrue(exclusionsText.contains("\"file\": \"intellij.test.feature.frontend.xml\"")) - assertTrue(exclusionsText.contains("\"line\": 1")) - assertTrue(exclusionsText.contains("\"reason\": \"\"")) - - val fileEditorManager = FileEditorManager.getInstance(project) - assertEquals(exclusionsFile, fileEditorManager.selectedEditor?.file) - val selectedTextEditor = fileEditorManager.selectedTextEditor - assertNotNull(selectedTextEditor) - assertEquals(selectedTextEditor!!.document.lineCount - 1, selectedTextEditor.caretModel.logicalPosition.line) - } - private fun testAddingDependency(frontendOrBackend: String) { doTest( frontendOrBackend, @@ -328,42 +222,12 @@ internal class MissingFrontendOrBackendRuntimeDependencyFixTest : JavaCodeInsigh assertTrue(fileText.contains("")) assertTrue(fileText.contains("intellij.platform.$frontendOrBackend")) } - } private fun CodeInsightTestFixture.addXmlFile(relativePath: String, @Language("XML") fileText: String): PsiFile { return this.addFileToProject(relativePath, fileText) } -private fun CodeInsightTestFixture.addSplitModeExclusionsFile(@Language("JSON") fileText: String): PsiFile { - return addFileToProject(EXCLUSIONS_RELATIVE_PATH, fileText) -} - -private fun splitModeExclusionsJson(entry: String): String { - return """ - { - "exclusions": [ - $entry - ] - } - """.trimIndent() -} - -private fun missingRuntimeDependencyExclusion(file: String, line: Int, reason: String = "Documented test exclusion"): String { - return """ - { - "inspection": "MissingFrontendOrBackendRuntimeDependency", - "file": "$file", - "line": $line, - "reason": "$reason" - } - """.trimIndent() -} - private fun addRuntimeDependencyFixName(frontendOrBackend: String): String { return "Make module 'intellij.test.feature.$frontendOrBackend' work in '$frontendOrBackend' only" } - -private fun addToExclusionsFixName(): String { - return "Add this violation to the exclusions list" -} \ No newline at end of file diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeApiRestrictionsSourceModeTest.kt b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeApiRestrictionsSourceModeTest.kt index f841d38bff3c..ce7b398e5436 100644 --- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeApiRestrictionsSourceModeTest.kt +++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeApiRestrictionsSourceModeTest.kt @@ -7,7 +7,7 @@ import org.jetbrains.idea.devkit.inspections.remotedev.analysis.SplitModeApiRest import org.junit.Assert private const val API_RESTRICTIONS_PROJECT_RELATIVE_PATH = - "community/plugins/devkit/devkit-core/resources/remotedevInspectionData/ApiRestrictions.json" + "community/plugins/devkit/devkit-core/resources/remotedevInspectionData/ApiRestrictions.json5" internal class SplitModeApiRestrictionsSourceModeTest : BasePlatformTestCase() { fun testProjectApiRestrictionsOverrideBundledListWhenProjectModeEnabled() { @@ -36,6 +36,29 @@ internal class SplitModeApiRestrictionsSourceModeTest : BasePlatformTestCase() { } } + fun testProjectApiRestrictionKindCanBeRead() { + withApiRestrictionsSourceMode( + "project", + """ + // Project-mode restrictions may use JSON5 comments. + [ + {apiName: 'custom.split.mode.ui.api', targetModules: ['frontend'], restrictionKind: 'ui'}, + /* Generic platform APIs may be explicitly marked as non-UI restrictions. */ + {apiName: 'custom.split.mode.generic.api', targetModules: ['frontend'], restrictionKind: 'genericPlatformApi'}, + ] + """.trimIndent(), + ) { service -> + Assert.assertEquals( + SplitModeApiRestrictionsService.ApiRestrictionKind.UI, + service.getCodeApiRestriction("custom.split.mode.ui.api", null)?.restrictionKind, + ) + Assert.assertEquals( + SplitModeApiRestrictionsService.ApiRestrictionKind.GENERIC_PLATFORM_API, + service.getCodeApiRestriction("custom.split.mode.generic.api", null)?.restrictionKind, + ) + } + } + fun testProjectApiRestrictionsReloadAfterProjectFileChange() { withApiRestrictionsSourceMode( "project", diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeDependencyQuickFixesTest.kt b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeDependencyQuickFixesTest.kt index 31abc060a4c9..7f3a2798dd29 100644 --- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeDependencyQuickFixesTest.kt +++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeDependencyQuickFixesTest.kt @@ -55,8 +55,12 @@ internal class SplitModeDependencyQuickFixesTest : JavaCodeInsightFixtureTestCas ) myFixture.configureFromExistingVirtualFile(pluginXml.virtualFile) - launchActionAndWait("Make module 'unique.module.name.quick.fix.1' work in 'frontend' only") { - getModuleDependencyNames(pluginXml).contains("intellij.platform.frontend") + val intention = myFixture.findSingleIntention("Make module 'unique.module.name.quick.fix.1' work in 'frontend' only") + myFixture.checkPreviewAndLaunchAction(intention) + timeoutRunBlocking { + waitUntil("Quick fix was not applied", 5.seconds) { + getModuleDependencyNames(pluginXml).contains("intellij.platform.frontend") + } } val result = myFixture.file.text @@ -293,7 +297,7 @@ internal class SplitModeDependencyQuickFixesTest : JavaCodeInsightFixtureTestCas - /> + /> """.trimIndent() diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeExcludedModulesScopeConsistencyTest.kt b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeExcludedModulesScopeConsistencyTest.kt new file mode 100644 index 000000000000..20ba813aa0f7 --- /dev/null +++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeExcludedModulesScopeConsistencyTest.kt @@ -0,0 +1,55 @@ +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.idea.devkit.inspections + +import com.intellij.openapi.application.PathManager +import com.intellij.testFramework.junit5.TestApplication +import com.intellij.testFramework.junit5.fixture.projectFixture +import org.jetbrains.idea.devkit.inspections.remotedev.MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH +import org.jetbrains.idea.devkit.inspections.remotedev.SPLIT_MODE_EXCLUDED_MODULES_SCOPE_RELATIVE_PATHS +import org.jetbrains.idea.devkit.inspections.remotedev.SplitModeInspectionResourceReadMode +import org.jetbrains.idea.devkit.inspections.remotedev.SplitModeInspectionResourceReader +import org.jetbrains.idea.devkit.inspections.remotedev.createSplitModeExcludedModulesScopeXml +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import kotlin.io.path.exists +import kotlin.io.path.readText +import kotlin.io.path.writeText + +private const val UPDATE_SPLIT_MODE_EXCLUDED_MODULES_SCOPE_PROPERTY = "devkit.split.mode.excluded.modules.scope.update" + +@TestApplication +internal class SplitModeExcludedModulesScopeConsistencyTest { + companion object { + private val projectFixture = projectFixture() + } + + private val project get() = projectFixture.get() + + @Test + fun `SplitModeExcludedModules scopes are generated from monorepo features without split mode support`() { + val projectRoot = getMonorepoRoot() + val expectedXml = createSplitModeExcludedModulesScopeXml( + SplitModeInspectionResourceReader.getInstance(project), + SplitModeInspectionResourceReadMode.BUNDLED_ONLY, + ) + + for (relativePath in SPLIT_MODE_EXCLUDED_MODULES_SCOPE_RELATIVE_PATHS) { + val scopeFile = projectRoot.resolve(relativePath) + if (java.lang.Boolean.getBoolean(UPDATE_SPLIT_MODE_EXCLUDED_MODULES_SCOPE_PROPERTY)) { + scopeFile.writeText(expectedXml) + } + + val actualXml = scopeFile.readText() + assertEquals( + expectedXml, + actualXml, + "$relativePath must be generated from $MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH", + ) + } + } + + private fun getMonorepoRoot() = sequenceOf(PathManager.getHomeDir(), PathManager.getHomeDir().parent) + .filterNotNull() + .firstOrNull { it.resolve(MONOREPO_FEATURES_WITHOUT_SPLIT_MODE_SUPPORT_RELATIVE_PATH).exists() } + ?: PathManager.getHomeDir() +} diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeInspectionExclusionsReloadTest.kt b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeInspectionExclusionsReloadTest.kt index 2a4ad44faea9..6b82f35bf96e 100644 --- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeInspectionExclusionsReloadTest.kt +++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeInspectionExclusionsReloadTest.kt @@ -1,50 +1,62 @@ // Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.idea.devkit.inspections +import com.intellij.openapi.application.runWriteAction +import com.intellij.openapi.project.IntelliJProjectUtil +import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.testFramework.fixtures.BasePlatformTestCase -import org.jetbrains.idea.devkit.inspections.remotedev.EXCLUSIONS_RELATIVE_PATH -import org.jetbrains.idea.devkit.inspections.remotedev.SPLIT_MODE_API_USAGE_SHORT_NAME -import org.jetbrains.idea.devkit.inspections.remotedev.SplitModeInspectionExclusionProblem +import org.jetbrains.idea.devkit.inspections.remotedev.PROJECT_BASELINE_VERSION_RELATIVE_PATH import org.jetbrains.idea.devkit.inspections.remotedev.SplitModeInspectionExclusionsService import org.junit.Assert internal class SplitModeInspectionExclusionsReloadTest : BasePlatformTestCase() { - fun testExclusionsReloadAfterProjectFileChange() { - val exclusionsFile = createProjectResourceFile( + fun testQuickFixesContainOnlyBaselineByDefault() { + IntelliJProjectUtil.markAsIntelliJPlatformProject(project, true) + val fixes = SplitModeInspectionExclusionsService.getInstance(project).createCommonSuppressionQuickFixes() + + Assert.assertEquals( + listOf("Bypass incremental Split Mode safe-push tests"), + fixes.map { it.familyName }, + ) + } + + fun testBaselineVersionIncreasesExistingProjectFile() { + val baselineVersionFile = createProjectResourceFile( project, - EXCLUSIONS_RELATIVE_PATH, + PROJECT_BASELINE_VERSION_RELATIVE_PATH, """ + // Keep this comment. { - "exclusions": [ - {"inspection": "$SPLIT_MODE_API_USAGE_SHORT_NAME", "file": "src/example.kt", "line": 7} - ] + "currentProjectBaselineVersion": 41 } """.trimIndent(), ) - val service = SplitModeInspectionExclusionsService.getInstance(project) - val problem = SplitModeInspectionExclusionProblem( - inspection = SPLIT_MODE_API_USAGE_SHORT_NAME, - file = "src/example.kt", - line = 7, - ) try { - Assert.assertTrue(service.isExcluded(problem)) + val updatedFile = runWriteAction { + SplitModeInspectionExclusionsService.getInstance(project).increaseProjectBaselineVersion() + } - createProjectResourceFile( - project, - EXCLUSIONS_RELATIVE_PATH, + Assert.assertEquals(baselineVersionFile, updatedFile) + Assert.assertEquals( """ + // The file carries the current project baseline version for Split Mode Compatibility safe-push checks. + // Increase currentProjectBaselineVersion only when incremental Split Mode Compatibility tests incorrectly + // report pre-existing violations after large refactorings. + // Changing this value is a legal bypass: the affected safe push may skip the incremental Split Mode + // Compatibility tests for that run, allowing the refactoring to proceed while the baseline catches up. + // Do not use it for real new violations. Fix those instead; see Split Mode documentation IJPL-A-632. + // Contact the RemDev team in #ij-remote-dev if unsure. { - "exclusions": [] + "currentProjectBaselineVersion": 42 } - """.trimIndent(), - ) - Assert.assertFalse(service.isExcluded(problem)) + """.trimIndent(), + VfsUtilCore.loadText(baselineVersionFile), + ) } finally { - deleteProjectResourceFile(project, exclusionsFile) + deleteProjectResourceFile(project, baselineVersionFile) } } } diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModePredefinedModuleKindsAdditionalFileTest.kt b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModePredefinedModuleKindsAdditionalFileTest.kt index fc020a961913..2eb81c8d0606 100644 --- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModePredefinedModuleKindsAdditionalFileTest.kt +++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModePredefinedModuleKindsAdditionalFileTest.kt @@ -7,7 +7,7 @@ import org.jetbrains.idea.devkit.inspections.remotedev.analysis.SplitModeApiRest import org.junit.Assert private const val PREDEFINED_MODULE_KINDS_PROJECT_RELATIVE_PATH = - "community/plugins/devkit/devkit-core/resources/remotedevInspectionData/PredefinedModuleKinds.json" + "community/plugins/devkit/devkit-core/resources/remotedevInspectionData/PredefinedModuleKinds.json5" internal class SplitModePredefinedModuleKindsSourceModeTest : BasePlatformTestCase() { fun testProjectPredefinedModuleKindsOverrideBundledListWhenProjectModeEnabled() { @@ -15,7 +15,8 @@ internal class SplitModePredefinedModuleKindsSourceModeTest : BasePlatformTestCa "project", """ [ - {"moduleName": "custom.split.mode.frontend", "moduleKind": "frontend"} + // JSON5 project overrides are accepted. + {kind: 'moduleId', id: 'custom.split.mode.frontend', moduleKind: 'frontend'}, ] """.trimIndent(), ) { service -> @@ -49,7 +50,7 @@ internal class SplitModePredefinedModuleKindsSourceModeTest : BasePlatformTestCa "project", """ [ - {"moduleName": "custom.split.mode.frontend", "moduleKind": "frontend"} + {kind: 'moduleId', id: 'custom.split.mode.frontend', moduleKind: 'frontend'}, ] """.trimIndent(), ) { service -> @@ -65,7 +66,7 @@ internal class SplitModePredefinedModuleKindsSourceModeTest : BasePlatformTestCa PREDEFINED_MODULE_KINDS_PROJECT_RELATIVE_PATH, """ [ - {"moduleName": "custom.split.mode.backend", "moduleKind": "backend"} + {kind: 'moduleId', id: 'custom.split.mode.backend', moduleKind: 'backend'}, ] """.trimIndent(), ) diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeXmlApiUsageInspectionTest.kt b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeXmlApiUsageInspectionTest.kt index 97d32f8680cd..0535a4f653d0 100644 --- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeXmlApiUsageInspectionTest.kt +++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/SplitModeXmlApiUsageInspectionTest.kt @@ -1053,6 +1053,81 @@ No frontend or backend dependencies were found for descriptor 'unique.module.nam .setValue(true, testRootDisposable) } + fun testNonUiExtensionsInBackendWithNonUiApiPermitModule() { + val pluginXml = addModuleWithXmlDescriptor( + moduleName = "intellij.clion.radler.core", + descriptorRelativePathToResourcesDirectory = "META-INF/plugin.xml", + """ + + + + + + + """.trimIndent() + ) + myFixture.configureFromExistingVirtualFile(pluginXml.virtualFile) + + myFixture.checkHighlighting() + } + + fun testFrontendApiExtensionInOrdinaryBackendModule() { + val pluginXml = addModuleWithXmlDescriptor( + moduleName = "unique.module.name.64", + descriptorRelativePathToResourcesDirectory = "META-INF/plugin.xml", + """ + + + + + + <rdclient.actionCustomization/> + + + """.trimIndent() + ) + myFixture.configureFromExistingVirtualFile(pluginXml.virtualFile) + + myFixture.checkHighlighting() + } + + fun testUiExtensionInBackendWithNonUiApiPermitModule() { + val pluginXml = addModuleWithXmlDescriptor( + moduleName = "intellij.clion.radler.core", + descriptorRelativePathToResourcesDirectory = "META-INF/plugin.xml", + """ + + + <toolWindow/> + + + """.trimIndent() + ) + myFixture.configureFromExistingVirtualFile(pluginXml.virtualFile) + + myFixture.checkHighlighting() + } + + fun testBackendWithNonUiApiPermitModuleIsBackendDependencyEvidence() { + val pluginXml = addModuleWithXmlDescriptor( + moduleName = "unique.module.name.65", + descriptorRelativePathToResourcesDirectory = "META-INF/plugin.xml", + """ + + + + + + <lang.parserDefinition/> + + + """.trimIndent() + ) + myFixture.configureFromExistingVirtualFile(pluginXml.virtualFile) + + myFixture.checkHighlighting() + } + fun testSharedExtensionInBackendModule() { val pluginXml = addModuleWithXmlDescriptor( moduleName = "unique.module.name.48", diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/navigation/DescriptorSplitModeInlayHintsProviderTest.kt b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/navigation/DescriptorSplitModeInlayHintsProviderTest.kt new file mode 100644 index 000000000000..b44c73242886 --- /dev/null +++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/navigation/DescriptorSplitModeInlayHintsProviderTest.kt @@ -0,0 +1,256 @@ +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.idea.devkit.navigation + +import com.intellij.codeInsight.hints.declarative.InlayProviderPassInfo +import com.intellij.codeInsight.hints.declarative.impl.DeclarativeInlayHintsPass +import com.intellij.codeInsight.hints.declarative.impl.inlayRenderer.DeclarativeInlayRenderer +import com.intellij.codeInsight.hints.declarative.impl.views.InlayPresentationList +import com.intellij.codeInsight.hints.declarative.impl.views.TextInlayPresentationEntry +import com.intellij.codeInsight.multiverse.codeInsightContext +import com.intellij.openapi.actionSystem.ex.ActionUtil +import com.intellij.openapi.progress.EmptyProgressIndicator +import com.intellij.openapi.util.registry.Registry +import com.intellij.psi.xml.XmlFile +import com.intellij.testFramework.PsiTestUtil +import com.intellij.testFramework.TestDataPath +import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase +import org.intellij.lang.annotations.Language +import org.jetbrains.idea.devkit.DevkitJavaTestsUtil +import org.jetbrains.idea.devkit.module.PluginModuleType + +@TestDataPath($$"$CONTENT_ROOT/testData/navigation/descriptorsIncludingContentModule") +internal class DescriptorSplitModeInlayHintsProviderTest : JavaCodeInsightFixtureTestCase() { + + override fun getBasePath() = DevkitJavaTestsUtil.TESTDATA_PATH + "navigation/descriptorsIncludingContentModule" + + fun testFrontendContentModuleInlay() { + createModuleWithModuleDescriptor("declaring.module", "declaring.module.xml", """ + + + + + + """.trimIndent() + ) + + createModuleWithModuleDescriptor("test.module", "test.module.xml", """ + + + + + + """.trimIndent() + ) + + testDescriptorInlay( + descriptorPath = "test.module/test.module.xml", + expectedInlayText = "Frontend content module", + expectedTooltip = expectedTooltip( + descriptorKind = "content module", + compatibilityTarget = "frontend IDE", + reason = "Frontend dependency 'intellij.platform.frontend' from descriptor 'test.module.xml' in module 'test.module'", + ), + ) + } + + fun testSharedContentModuleInlay() { + createModuleWithModuleDescriptor("declaring.module", "declaring.module.xml", """ + + + + + + """.trimIndent() + ) + + createModuleWithModuleDescriptor("test.module", "test.module.xml", """ + + + + """.trimIndent() + ) + + testDescriptorInlay( + descriptorPath = "test.module/test.module.xml", + expectedInlayText = "Shared content module", + expectedTooltip = expectedTooltip( + descriptorKind = "content module", + compatibilityTarget = "both frontend and backend IDE", + reason = "No frontend or backend dependencies were found for descriptor 'test.module.xml' in module 'test.module'", + ), + ) + } + + fun testBackendPluginInlay() { + createModuleWithModuleDescriptor("root.plugin", "plugin.xml", """ + + + + + + """.trimIndent() + ) + + testDescriptorInlay( + descriptorPath = "root.plugin/plugin.xml", + expectedInlayText = "Backend plugin", + expectedTooltip = expectedTooltip( + descriptorKind = "plugin", + compatibilityTarget = "backend IDE", + reason = "Backend dependency 'intellij.platform.backend' from descriptor 'plugin.xml' in module 'root.plugin'", + ), + ) + } + + fun testMixedContentModuleInlayUsesMonolithPresentation() { + createModuleWithModuleDescriptor("declaring.module", "declaring.module.xml", """ + + + + + + """.trimIndent() + ) + + createModuleWithModuleDescriptor("test.module", "test.module.xml", """ + + + + + + + """.trimIndent() + ) + + testDescriptorInlay( + descriptorPath = "test.module/test.module.xml", + expectedInlayText = "Monolith content module", + expectedTooltip = expectedTooltip( + descriptorKind = "content module", + compatibilityTarget = "monolith IDE", + reason = """ + Frontend dependency 'intellij.platform.frontend' from descriptor 'test.module.xml' in module 'test.module' + + Backend dependency 'intellij.platform.backend' from descriptor 'test.module.xml' in module 'test.module' + """.trimIndent(), + ), + ) + } + + fun testMonolithPluginInlay() { + createModuleWithModuleDescriptor("root.plugin", "plugin.xml", """ + + + + + + """.trimIndent() + ) + + testDescriptorInlay( + descriptorPath = "root.plugin/plugin.xml", + expectedInlayText = "Monolith plugin", + expectedTooltip = expectedTooltip( + descriptorKind = "plugin", + compatibilityTarget = "monolith IDE", + reason = "Monolith dependency 'intellij.platform.monolith' from descriptor 'plugin.xml' in module 'root.plugin'", + ), + ) + } + + fun testNonPluginXmlDescriptorInlay() { + createModuleWithModuleDescriptor("root.plugin", "root.plugin.optional.xml", """ + + + + + + """.trimIndent() + ) + + testDescriptorInlay( + descriptorPath = "root.plugin/root.plugin.optional.xml", + expectedInlayText = "Backend XML descriptor", + expectedTooltip = expectedTooltip( + descriptorKind = "XML descriptor", + compatibilityTarget = "backend IDE", + reason = "Backend dependency 'intellij.platform.backend' from descriptor 'root.plugin.optional.xml' in module 'root.plugin'", + ), + ) + } + + fun testRegistryDisablesInlay() { + Registry.get("devkit.split.mode.inlay.plugin.descriptor.kind").setValue(false, testRootDisposable) + + createModuleWithModuleDescriptor("root.plugin", "plugin.xml", """ + + + + + + """.trimIndent() + ) + + myFixture.configureFromTempProjectFile("root.plugin/plugin.xml") + + runInlayPass() + + assertEmpty(myFixture.editor.inlayModel.getInlineElementsInRange(0, myFixture.editor.document.textLength)) + } + + private fun createModuleWithModuleDescriptor( + moduleName: String, + moduleDescriptorName: String, + @Language("XML") moduleDescriptorText: String, + ) { + PsiTestUtil.addModule( + project, + PluginModuleType.getInstance(), + moduleName, + myFixture.tempDirFixture.findOrCreateDir(moduleName) + ) + myFixture.addFileToProject("$moduleName/$moduleDescriptorName", moduleDescriptorText) + } + + private fun testDescriptorInlay( + descriptorPath: String, + expectedInlayText: String, + expectedTooltip: String, + ) { + val xmlFile = myFixture.configureFromTempProjectFile(descriptorPath) as XmlFile + + runInlayPass() + + val inlay = myFixture.editor.inlayModel + .getInlineElementsInRange(0, myFixture.editor.document.textLength, DeclarativeInlayRenderer::class.java) + .single() + assertEquals(expectedInlayText, inlay.renderer.presentationLists.single().getText()) + assertEquals(expectedTooltip, inlay.renderer.toInlayData().single().tooltip) + + val inlayInfo = getDescriptorSplitModeInlayInfo(xmlFile) + assertNotNull(inlayInfo) + assertEquals(expectedInlayText, inlayInfo!!.text) + assertEquals(expectedTooltip, inlayInfo.tooltip) + } + + private fun expectedTooltip(descriptorKind: String, compatibilityTarget: String, reason: String): String { + val normalizedReason = reason.lineSequence().map(String::trim).filter(String::isNotEmpty).joinToString(" ") + return "Split mode compatibility: $descriptorKind can be installed into $compatibilityTarget. Reason: $normalizedReason" + } + + private fun runInlayPass() { + val providerInfo = InlayProviderPassInfo(DescriptorSplitModeInlayHintsProvider(), "test.inlay.provider", emptyMap()) + val pass = ActionUtil.underModalProgress(project, "") { + DeclarativeInlayHintsPass(myFixture.file, myFixture.editor, listOf(providerInfo), isPreview = false) + } + pass.setContext(myFixture.file.codeInsightContext) + ActionUtil.underModalProgress(project, "") { + pass.doCollectInformation(EmptyProgressIndicator()) + } + pass.applyInformationToEditor() + } + + private fun InlayPresentationList.getText(): String { + return getEntries().joinToString(separator = "") { (it as TextInlayPresentationEntry).text } + } +} diff --git a/plugins/devkit/devkit-kotlin-fir-tests/testSrc/org/jetbrains/idea/devkit/k2/inspections/remotedev/SplitModeApiUsageInspectionTest.kt b/plugins/devkit/devkit-kotlin-fir-tests/testSrc/org/jetbrains/idea/devkit/k2/inspections/remotedev/SplitModeApiUsageInspectionTest.kt index dcd6b2a69549..66849d7ef601 100644 --- a/plugins/devkit/devkit-kotlin-fir-tests/testSrc/org/jetbrains/idea/devkit/k2/inspections/remotedev/SplitModeApiUsageInspectionTest.kt +++ b/plugins/devkit/devkit-kotlin-fir-tests/testSrc/org/jetbrains/idea/devkit/k2/inspections/remotedev/SplitModeApiUsageInspectionTest.kt @@ -112,6 +112,36 @@ class SplitModeApiUsageInspectionTest : LightJavaCodeInsightFixtureTestCase() { """.trimIndent() ) + myFixture.addClass( + """ + package com.intellij.openapi.roots; + + public interface FileIndex {} + """.trimIndent() + ) + + myFixture.addClass( + """ + package com.intellij.openapi.roots; + + import com.intellij.openapi.project.Project; + + public interface ProjectFileIndex extends FileIndex { + static ProjectFileIndex getInstance(Project project) { + return null; + } + } + """.trimIndent() + ) + + myFixture.addClass( + """ + package com.intellij.openapi.roots; + + public interface ModuleFileIndex extends FileIndex {} + """.trimIndent() + ) + myFixture.addClass( """ package com.intellij.openapi.options; @@ -237,22 +267,22 @@ class SplitModeApiUsageInspectionTest : LightJavaCodeInsightFixtureTestCase() { import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.fileEditor.FileEditorManager; - class CustomToolWindowFactory: ToolWindowFactory {} +Backend dependency 'intellij.platform.backend' from descriptor 'plugin.xml' in module 'light_idea_test_case'">ToolWindowFactory {} class BackendService { fun doStuff() { // no warning here expected VirtualFileManager.getInstance() - FileEditorManager.getInstance().getFocusedEditor() +Backend dependency 'intellij.platform.backend' from descriptor 'plugin.xml' in module 'light_idea_test_case'">FileEditorManager.getInstance().getFocusedEditor() } } """.trimIndent() @@ -306,11 +336,11 @@ Backend dependency 'intellij.platform.backend' from descriptor 'plugin.xml' in m class SharedLangModule { fun test() { - VirtualFileManager.getInstance() +Predefined module kind for plugin/module id 'com.intellij.modules.lang'">VirtualFileManager.getInstance() } } """.trimIndent() @@ -344,11 +374,11 @@ Predefined module kind for plugin/module id 'com.intellij.modules.lang'">Virtual class FrontendService { fun doStuff() { - VirtualFileManager.getInstance() +Frontend dependency 'intellij.platform.frontend' from descriptor 'plugin.xml' in module 'light_idea_test_case'">VirtualFileManager.getInstance() } } """.trimIndent() @@ -357,7 +387,60 @@ Frontend dependency 'intellij.platform.frontend' from descriptor 'plugin.xml' in myFixture.checkHighlighting() } - fun testConfigurableApiInBackendModule() { + fun testFileIndexApiInFrontendModule() { + configurePluginXml( + """ + + + + + + """.trimIndent() + ) + + myFixture.configureByText( + "FrontendFileIndexService.kt", """ + package com.example.frontend + + import com.intellij.openapi.project.Project + import com.intellij.openapi.roots.FileIndex + import com.intellij.openapi.roots.ModuleFileIndex + import com.intellij.openapi.roots.ProjectFileIndex + + class FrontendFileIndexService(private val project: Project) { + fun doStuff() { + ProjectFileIndex.getInstance(project) + } + } + + class FrontendFileIndex : FileIndex + + class FrontendProjectFileIndex : ProjectFileIndex + + class FrontendModuleFileIndex : ModuleFileIndex + """.trimIndent() + ) + + myFixture.checkHighlighting() + } + + fun testFrontendOrBackendApiInBackendModule() { configurePluginXml( """ @@ -369,12 +452,12 @@ Frontend dependency 'intellij.platform.frontend' from descriptor 'plugin.xml' in ) myFixture.configureByText( - "BackendConfigurable.kt", """ + "BackendDynamicPluginListener.kt", """ package com.example.backend - import com.intellij.openapi.options.Configurable + import com.intellij.ide.plugins.DynamicPluginListener - class BackendConfigurable : Configurable + class BackendDynamicPluginListener : DynamicPluginListener """.trimIndent() ) @@ -398,11 +481,11 @@ Frontend dependency 'intellij.platform.frontend' from descriptor 'plugin.xml' in import com.intellij.openapi.fileEditor.FileEditorManagerListener - class BackendFileEditorListener : FileEditorManagerListener +Backend dependency 'intellij.platform.backend' from descriptor 'plugin.xml' in module 'light_idea_test_case'">FileEditorManagerListener """.trimIndent() ) @@ -426,11 +509,11 @@ Backend dependency 'intellij.platform.backend' from descriptor 'plugin.xml' in m import com.intellij.lang.ParserDefinition - class FrontendParserDefinition : ParserDefinition +Frontend dependency 'intellij.platform.frontend' from descriptor 'plugin.xml' in module 'light_idea_test_case'">ParserDefinition """.trimIndent() ) @@ -454,11 +537,11 @@ Frontend dependency 'intellij.platform.frontend' from descriptor 'plugin.xml' in import com.intellij.openapi.project.ProjectManagerListener - class FrontendProjectManagerListener : ProjectManagerListener +Frontend dependency 'intellij.platform.frontend' from descriptor 'plugin.xml' in module 'light_idea_test_case'">ProjectManagerListener """.trimIndent() ) @@ -490,11 +573,11 @@ Frontend dependency 'intellij.platform.frontend' from descriptor 'plugin.xml' in class FrontendService { fun doStuff() { - VirtualFileManager.getInstance() +Frontend dependency 'intellij.platform.frontend' from descriptor 'light_idea_test_case.xml' in module 'light_idea_test_case'">VirtualFileManager.getInstance() } } """.trimIndent() @@ -522,11 +605,11 @@ Frontend dependency 'intellij.platform.frontend' from descriptor 'light_idea_tes class FrontendService { fun doStuff() { - AnnotatedBackendApi.getInstance() +Frontend dependency 'intellij.platform.frontend' from descriptor 'plugin.xml' in module 'light_idea_test_case'">AnnotatedBackendApi.getInstance() } } """.trimIndent() @@ -554,11 +637,11 @@ Frontend dependency 'intellij.platform.frontend' from descriptor 'plugin.xml' in class BackendService { fun doStuff() { - AnnotatedFrontendApi.getInstance() +Backend dependency 'intellij.platform.backend' from descriptor 'plugin.xml' in module 'light_idea_test_case'">AnnotatedFrontendApi.getInstance() } } """.trimIndent() @@ -589,19 +672,19 @@ Backend dependency 'intellij.platform.backend' from descriptor 'plugin.xml' in m // both warnings are expected in a shared module class SharedService { fun testFrontendApi() { - class MyToolWindow: ToolWindowFactory {} +No frontend or backend dependencies were found for module 'light_idea_test_case'">ToolWindowFactory {} } fun testBackendApi() { - VirtualFileManager.getInstance() +No frontend or backend dependencies were found for module 'light_idea_test_case'">VirtualFileManager.getInstance() } } """.trimIndent() @@ -627,13 +710,13 @@ No frontend or backend dependencies were found for module 'light_idea_test_case' import com.intellij.ide.plugins.DynamicPluginListener - class SharedDynamicPluginListener : DynamicPluginListener +No frontend or backend dependencies were found for module 'light_idea_test_case'">DynamicPluginListener """.trimIndent() ) @@ -732,7 +815,9 @@ No frontend or backend dependencies were found for module 'light_idea_test_case' """.trimIndent() ) - launchActionAndWait("Make module 'light_idea_test_case' work in 'frontend' only") { + val intention = myFixture.findSingleIntention("Make module 'light_idea_test_case' work in 'frontend' only") + assertNull(myFixture.getIntentionPreviewText(intention)) + launchActionAndWait(intention) { getModuleDependencyNames().contains("intellij.platform.frontend") } @@ -854,7 +939,8 @@ No frontend or backend dependencies were found for module 'light_idea_test_case' ) addCurrentModuleDependencies("intellij.platform.frontend") - launchActionAndWait("Make module 'light_idea_test_case' work in 'backend' only") { + val intention = myFixture.findSingleIntention("Make module 'light_idea_test_case' work in 'backend' only") + launchActionAndWait(intention) { !getModuleDependencyNames().contains("intellij.platform.frontend") } @@ -879,11 +965,6 @@ No frontend or backend dependencies were found for module 'light_idea_test_case' } } - private fun launchActionAndWait(intentionText: String, condition: () -> Boolean) { - val intention = myFixture.findSingleIntention(intentionText) - launchActionAndWait(intention, condition) - } - private fun launchActionAndWait(intention: IntentionAction, condition: () -> Boolean) { myFixture.launchAction(intention) timeoutRunBlocking { diff --git a/plugins/devkit/intellij.devkit.mcp/BUILD.bazel b/plugins/devkit/intellij.devkit.mcp/BUILD.bazel index 1e6f73955803..afcd9607052c 100644 --- a/plugins/devkit/intellij.devkit.mcp/BUILD.bazel +++ b/plugins/devkit/intellij.devkit.mcp/BUILD.bazel @@ -28,6 +28,9 @@ jvm_library( "//platform/lang-core", "//platform/util", "//java/java-psi-api:psi", + "//platform/analysis-api:analysis", + "//platform/analysis-impl", + "//platform/projectModel-api:projectModel", "//libraries/kotlinx/serialization/json", "//libraries/kotlinx/serialization/core", "//platform/progress/shared:ide-progress", @@ -53,6 +56,9 @@ jvm_library( "//platform/lang-core:lang-core_test_lib", "//platform/util:util_test_lib", "//java/java-psi-api:psi_test_lib", + "//platform/analysis-api:analysis_test_lib", + "//platform/analysis-impl:analysis-impl_test_lib", + "//platform/projectModel-api:projectModel_test_lib", "//libraries/kotlinx/serialization/json:json_test_lib", "//libraries/kotlinx/serialization/core:core_test_lib", "//platform/progress/shared:ide-progress_test_lib", diff --git a/plugins/devkit/intellij.devkit.mcp/intellij.devkit.mcp.iml b/plugins/devkit/intellij.devkit.mcp/intellij.devkit.mcp.iml index e22095f8a645..143cab4a32d2 100644 --- a/plugins/devkit/intellij.devkit.mcp/intellij.devkit.mcp.iml +++ b/plugins/devkit/intellij.devkit.mcp/intellij.devkit.mcp.iml @@ -38,6 +38,9 @@ + + + diff --git a/plugins/devkit/intellij.devkit.mcp/resources/intellij.devkit.mcp.xml b/plugins/devkit/intellij.devkit.mcp/resources/intellij.devkit.mcp.xml index 251609bb9f84..09d0823cb3f6 100644 --- a/plugins/devkit/intellij.devkit.mcp/resources/intellij.devkit.mcp.xml +++ b/plugins/devkit/intellij.devkit.mcp/resources/intellij.devkit.mcp.xml @@ -1,13 +1,21 @@ + - - + - + + + + + + + + + diff --git a/plugins/devkit/intellij.devkit.mcp/src/DevKitMcpToolset.kt b/plugins/devkit/intellij.devkit.mcp/src/DevKitMcpToolset.kt index f5c51d696620..0265eba2a894 100644 --- a/plugins/devkit/intellij.devkit.mcp/src/DevKitMcpToolset.kt +++ b/plugins/devkit/intellij.devkit.mcp/src/DevKitMcpToolset.kt @@ -31,6 +31,8 @@ import java.util.Collections import java.util.EnumSet import kotlin.time.Duration.Companion.milliseconds +private const val DEFAULT_SPLIT_MODE_COMPATIBILITY_ANALYSIS_TIMEOUT_MILLISECONDS: Int = 5 * 60 * 1000 + class DevKitMcpToolset : McpToolset { @McpTool @McpDescription( @@ -94,6 +96,41 @@ class DevKitMcpToolset : McpToolset { return recognizeSplitModeModuleKindForPath(descriptorPath) } + @McpTool + @McpDescription( + """ + |Recognizes the Split Mode API kind for a fully qualified API name using the same DevKit restriction data + |as the remdev inspections. + |Returns the target module kind when the API is listed in DevKit split-mode restrictions. + """ + ) + @Suppress("unused", "FunctionName") + suspend fun recognize_split_mode_api_kind( + @McpDescription("Fully qualified code API name, for example `com.intellij.openapi.ui.DialogWrapper`.") + apiName: String, + ): RecognizeSplitModeApiKindResult { + return recognizeSplitModeApiKind(apiName) + } + + @McpTool + @McpDescription( + """ + |Collects Split Mode compatibility issues for all supported files under the given directory. + |Runs only the DevKit split-mode compatibility inspection whitelist and returns every found issue. + |Whitelisted inspections: SplitModeApiUsage, SplitModeXmlApiUsage, SplitModeMixedDependencies, + |SplitModeImplicitModuleKind, MissingFrontendOrBackendRuntimeDependency. + """ + ) + @Suppress("unused", "FunctionName") + suspend fun collect_split_mode_compatibility_issues( + @McpDescription(Constants.RELATIVE_PATH_IN_PROJECT_DESCRIPTION) + directoryPath: String, + @McpDescription("Analysis timeout in milliseconds.") + analysisTimeout: Int = DEFAULT_SPLIT_MODE_COMPATIBILITY_ANALYSIS_TIMEOUT_MILLISECONDS, + ): SplitModeCompatibilityIssuesResult { + return collectSplitModeCompatibilityIssues(directoryPath, analysisTimeout) + } + @McpTool @McpDescription(""" |Analyzes the usage of the Read/Write lock for the method under the caret. @@ -169,12 +206,14 @@ class DevKitMcpToolset : McpToolset { mapper: (ConstraintType) -> T, ): Pair>>, Boolean> { val project = currentCoroutineContext().project - val file = VirtualFileManager.getInstance().findFileByNioPath(project.resolveInProject(filePath)) ?: throw McpExpectedError("Virtual file not found") + val file = VirtualFileManager.getInstance().findFileByNioPath(project.resolveInProject(filePath)) + ?: throw McpExpectedError("Virtual file not found") val pointer = readAction { val document = FileDocumentManager.getInstance().getDocument(file) val psiTree = PsiManager.getInstance(project).findFile(file) ?: throw McpExpectedError("PsiFile not found") val offset = document?.getLineStartOffset(line - 1)?.plus(column - 1) ?: throw McpExpectedError("Invalid line/column") - val method = LockReqPsiOps.forLanguage(psiTree.language).extractTargetElement(psiTree, offset) ?: throw McpExpectedError("No method found at the specified position") + val method = LockReqPsiOps.forLanguage(psiTree.language).extractTargetElement(psiTree, offset) + ?: throw McpExpectedError("No method found at the specified position") SmartPointerManager.createPointer(method) } @@ -183,15 +222,18 @@ class DevKitMcpToolset : McpToolset { val result = withTimeoutOrNull(timeout.milliseconds) { @Suppress("HardCodedStringLiteral") withBackgroundProgress(project, "Analyzing Locking Requirements usage for AI", true) { - LockReqAnalyzerParallelBFS().analyzeMethodStreaming(pointer, AnalysisConfig.forProject(project, requirementSet), project, object : LockReqConsumer { - override fun onPath(path: ExecutionPath) { - val lockType = mapper(path.lockRequirement.constraintType) - val locations = path.methodChain.map { - SourceLocation(it.containingClassName ?: "", it.methodName) - } - list.add(lockType to locations) - } - }) + LockReqAnalyzerParallelBFS().analyzeMethodStreaming(pointer, + AnalysisConfig.forProject(project, requirementSet), + project, + object : LockReqConsumer { + override fun onPath(path: ExecutionPath) { + val lockType = mapper(path.lockRequirement.constraintType) + val locations = path.methodChain.map { + SourceLocation(it.containingClassName ?: "", it.methodName) + } + list.add(lockType to locations) + } + }) } } val timeout = result == null @@ -199,7 +241,7 @@ class DevKitMcpToolset : McpToolset { } enum class LockType { - READ_ASSERTION, WRITE_ASSERTION, WRITE_INTENT_ASSERTION, NO_READ_ASSERTION + READ_ASSERTION, WRITE_ASSERTION, WRITE_INTENT_ASSERTION, NO_READ_ASSERTION } enum class ThreadType { @@ -250,4 +292,50 @@ class DevKitMcpToolset : McpToolset { @property:McpDescription("Reasoning produced by the same DevKit module-kind analysis used by the remdev inspections.") val reasoning: String, ) + + @Serializable + data class RecognizeSplitModeApiKindResult( + @property:McpDescription("Fully qualified code API name that was recognized.") + val apiName: String, + @property:McpDescription("Target Split Mode module kind for this API, or null when no restriction matches.") + val kindId: String?, + @property:McpDescription("Optional hint from the DevKit split-mode restrictions data.") + val hint: String?, + ) + + @Serializable + data class SplitModeCompatibilityIssuesResult( + @property:McpDescription("Directory path that was analyzed, relative to the project root when provided that way.") + val directoryPath: String, + @property:McpDescription("Split Mode inspection short names used for this analysis.") + val inspectionIds: List, + @property:McpDescription("Number of supported source files inspected under the directory.") + val inspectedFileCount: Int, + @property:McpDescription("All found Split Mode compatibility issues from the whitelisted inspections.") + val issues: List, + @property:McpDescription(Constants.TIMED_OUT_DESCRIPTION) + val timedOut: Boolean, + ) + + @Serializable + data class SplitModeCompatibilityIssue( + @property:McpDescription("Inspection short name that reported the issue.") + val inspectionId: String, + @property:McpDescription("Problem highlight type reported by the inspection.") + val severity: String, + @property:McpDescription("Problem file path, relative to the project root when possible.") + val filePath: String, + @property:McpDescription("1-based start line, if available.") + val line: Int?, + @property:McpDescription("1-based start column, if available.") + val column: Int?, + @property:McpDescription("1-based end line, if available.") + val endLine: Int?, + @property:McpDescription("1-based end column, if available.") + val endColumn: Int?, + @property:McpDescription("Rendered inspection problem description.") + val description: String, + @property:McpDescription("Quick-fix family names available in batch mode.") + val quickFixes: List, + ) } diff --git a/plugins/devkit/intellij.devkit.mcp/src/DevKitSplitModeCompatibilityInspectionApi.kt b/plugins/devkit/intellij.devkit.mcp/src/DevKitSplitModeCompatibilityInspectionApi.kt new file mode 100644 index 000000000000..b752b87579e5 --- /dev/null +++ b/plugins/devkit/intellij.devkit.mcp/src/DevKitSplitModeCompatibilityInspectionApi.kt @@ -0,0 +1,208 @@ +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.mcp + +import com.intellij.codeInspection.InspectionEngine +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemDescriptorBase +import com.intellij.codeInspection.ProblemDescriptorUtil +import com.intellij.codeInspection.ex.LocalInspectionToolWrapper +import com.intellij.mcpserver.mcpFail +import com.intellij.mcpserver.project +import com.intellij.mcpserver.util.resolveInProject +import com.intellij.openapi.application.smartReadAction +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.progress.EmptyProgressIndicator +import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.progress.checkCanceled +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.ProjectFileIndex +import com.intellij.openapi.util.TextRange +import com.intellij.openapi.vfs.VfsUtilCore +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.openapi.vfs.VirtualFileVisitor +import com.intellij.platform.ide.progress.withBackgroundProgress +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiManager +import com.intellij.util.PairProcessor +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.withTimeoutOrNull +import kotlin.time.Duration.Companion.milliseconds + +private val SPLIT_MODE_COMPATIBILITY_INSPECTION_IDS = listOf( + "SplitModeApiUsage", + "SplitModeXmlApiUsage", + "SplitModeMixedDependencies", + "SplitModeImplicitModuleKind", + "MissingFrontendOrBackendRuntimeDependency", +) + +private val SPLIT_MODE_COMPATIBILITY_FILE_EXTENSIONS = setOf("java", "kt", "kts", "xml") + +internal suspend fun collectSplitModeCompatibilityIssues( + directoryPath: String, + analysisTimeout: Int, +): DevKitMcpToolset.SplitModeCompatibilityIssuesResult { + val project = currentCoroutineContext().project + val resolvedDirectoryPath = project.resolveInProject(directoryPath) + val directory = VirtualFileManager.getInstance().findFileByNioPath(resolvedDirectoryPath) + ?: VirtualFileManager.getInstance().refreshAndFindFileByNioPath(resolvedDirectoryPath) + ?: mcpFail("Directory not found: $directoryPath") + if (!directory.isDirectory) { + mcpFail("Not a directory: $directoryPath") + } + + loadSplitModeRestrictions(project) + + var result = SplitModeCompatibilityInspectionRunResult(emptyList(), 0) + val timedOut = withTimeoutOrNull(analysisTimeout.milliseconds) { + @Suppress("HardCodedStringLiteral") + withBackgroundProgress(project, "Collecting Split Mode compatibility issues", true) { + val inspectionFiles = smartReadAction(project) { + collectInspectionFiles(directory, project) + } + + val issues = mutableListOf() + var inspectedFileCount = 0 + for (virtualFile in inspectionFiles) { + checkCanceled() + val fileIssues = smartReadAction(project) { + val psiManager = PsiManager.getInstance(project) + val psiFile = psiManager.findFile(virtualFile) ?: return@smartReadAction null + val tools = getSplitModeCompatibilityInspectionTools(project) + inspectSplitModeCompatibilityFile(project, psiFile, tools) + } ?: continue + inspectedFileCount++ + issues += fileIssues + } + result = SplitModeCompatibilityInspectionRunResult(issues, inspectedFileCount) + } + } == null + + return DevKitMcpToolset.SplitModeCompatibilityIssuesResult( + directoryPath = directoryPath, + inspectionIds = SPLIT_MODE_COMPATIBILITY_INSPECTION_IDS, + inspectedFileCount = result.inspectedFileCount, + issues = result.issues.sortedWith(compareBy( + { it.filePath }, + { it.line ?: Int.MAX_VALUE }, + { it.column ?: Int.MAX_VALUE }, + { it.inspectionId }, + { it.description }, + )), + timedOut = timedOut, + ) +} + +private fun getSplitModeCompatibilityInspectionTools(project: Project): List { + return SPLIT_MODE_COMPATIBILITY_INSPECTION_IDS.map { inspectionId -> + val wrapper = LocalInspectionToolWrapper.findTool2RunInBatch(project, null, inspectionId) + ?: mcpFail("Inspection not found: $inspectionId") + val localWrapper = wrapper as? LocalInspectionToolWrapper + ?: mcpFail("Inspection is not a local inspection: $inspectionId") + localWrapper.createCopy() + } +} + +private fun collectInspectionFiles(root: VirtualFile, project: Project): List { + val projectFileIndex = ProjectFileIndex.getInstance(project) + val files = mutableListOf() + VfsUtilCore.visitChildrenRecursively(root, object : VirtualFileVisitor() { + override fun visitFile(file: VirtualFile): Boolean { + ProgressManager.checkCanceled() + if (projectFileIndex.isUnderIgnored(file)) { + return false + } + if (file.isDirectory) { + return true + } + + val extension = file.extension?.lowercase() + if (extension in SPLIT_MODE_COMPATIBILITY_FILE_EXTENSIONS && projectFileIndex.getModuleForFile(file, true) != null) { + files += file + } + return true + } + }) + return files +} + +private fun inspectSplitModeCompatibilityFile( + project: Project, + psiFile: PsiFile, + tools: List, +): List { + val descriptorsByTool = InspectionEngine.inspectEx( + tools, + psiFile, + psiFile.textRange, + psiFile.textRange, + false, + true, + true, + EmptyProgressIndicator(), + PairProcessor.alwaysTrue(), + ) + + return descriptorsByTool.flatMap { (tool, descriptors) -> + descriptors.map { descriptor -> + descriptor.toSplitModeCompatibilityIssue(project, tool.shortName) + } + } +} + +private fun ProblemDescriptor.toSplitModeCompatibilityIssue( + project: Project, + inspectionId: String, +): DevKitMcpToolset.SplitModeCompatibilityIssue { + val textRange = (this as? ProblemDescriptorBase)?.textRange ?: psiElement?.textRange + val virtualFile = (this as? ProblemDescriptorBase)?.containingFile ?: psiElement?.containingFile?.virtualFile + val position = computePosition(virtualFile, textRange) + return DevKitMcpToolset.SplitModeCompatibilityIssue( + inspectionId = inspectionId, + severity = highlightType.name, + filePath = virtualFile?.toProjectRelativePath(project) ?: "", + line = position?.line, + column = position?.column, + endLine = position?.endLine, + endColumn = position?.endColumn, + description = ProblemDescriptorUtil.renderDescriptionMessage(this, psiElement, false), + quickFixes = fixes?.map { it.familyName }?.distinct().orEmpty(), + ) +} + +private fun computePosition( + virtualFile: VirtualFile?, + textRange: TextRange?, +): SourcePosition? { + if (virtualFile == null || textRange == null) { + return null + } + val document = FileDocumentManager.getInstance().getDocument(virtualFile) ?: return null + val startOffset = textRange.startOffset.coerceIn(0, document.textLength) + val endOffset = textRange.endOffset.coerceIn(startOffset, document.textLength) + return SourcePosition( + line = document.getLineNumber(startOffset) + 1, + column = startOffset - document.getLineStartOffset(document.getLineNumber(startOffset)) + 1, + endLine = document.getLineNumber(endOffset) + 1, + endColumn = endOffset - document.getLineStartOffset(document.getLineNumber(endOffset)) + 1, + ) +} + +private fun VirtualFile.toProjectRelativePath(project: Project): String { + val basePath = project.basePath ?: return path + val relativePath = path.removePrefix(basePath).removePrefix("/") + return relativePath.ifEmpty { path } +} + +private data class SourcePosition( + val line: Int, + val column: Int, + val endLine: Int, + val endColumn: Int, +) + +private data class SplitModeCompatibilityInspectionRunResult( + val issues: List, + val inspectedFileCount: Int, +) diff --git a/plugins/devkit/intellij.devkit.mcp/src/DevKitSplitModeRecognitionApi.kt b/plugins/devkit/intellij.devkit.mcp/src/DevKitSplitModeRecognitionApi.kt index 53187ddd02eb..b2ff98dfc088 100644 --- a/plugins/devkit/intellij.devkit.mcp/src/DevKitSplitModeRecognitionApi.kt +++ b/plugins/devkit/intellij.devkit.mcp/src/DevKitSplitModeRecognitionApi.kt @@ -5,6 +5,7 @@ import com.intellij.mcpserver.mcpFail import com.intellij.mcpserver.project import com.intellij.mcpserver.util.resolveInProject import com.intellij.openapi.application.readAction +import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.psi.PsiManager import kotlinx.coroutines.currentCoroutineContext @@ -19,22 +20,16 @@ internal suspend fun recognizeSplitModeModuleKindForPath(descriptorPath: String) val project = currentCoroutineContext().project val resolvedDescriptorPath = project.resolveInProject(descriptorPath) val descriptorVirtualFile = VirtualFileManager.getInstance().findFileByNioPath(resolvedDescriptorPath) - ?: VirtualFileManager.getInstance().refreshAndFindFileByNioPath(resolvedDescriptorPath) - ?: mcpFail("File not found: $descriptorPath") + ?: VirtualFileManager.getInstance().refreshAndFindFileByNioPath(resolvedDescriptorPath) + ?: mcpFail("File not found: $descriptorPath") - val service = SplitModeApiRestrictionsService.getInstance(project) - service.scheduleLoadRestrictions() - withTimeoutOrNull(5.seconds) { - while (!service.isLoaded()) { - delay(50.milliseconds) - } - } ?: mcpFail("Timed out waiting for split-mode module restrictions to load") + loadSplitModeRestrictions(project) val recognizedKind = readAction { val psiFile = PsiManager.getInstance(project).findFile(descriptorVirtualFile) ?: mcpFail("Psi file not found: $descriptorPath") recognizeSplitModeModuleKind(psiFile) - ?: mcpFail("Descriptor is not an IntelliJ plugin XML file or no IntelliJ module was found: $descriptorPath") + ?: mcpFail("Descriptor is not an IntelliJ plugin XML file or no IntelliJ module was found: $descriptorPath") } return DevKitMcpToolset.RecognizeIjModuleKindResult( @@ -44,3 +39,25 @@ internal suspend fun recognizeSplitModeModuleKindForPath(descriptorPath: String) reasoning = recognizedKind.reasoning, ) } + +internal suspend fun recognizeSplitModeApiKind(apiName: String): DevKitMcpToolset.RecognizeSplitModeApiKindResult { + val project = currentCoroutineContext().project + val service = loadSplitModeRestrictions(project) + val apiKind = service.getCodeApiKind(apiName, null) + return DevKitMcpToolset.RecognizeSplitModeApiKindResult( + apiName = apiName, + kindId = apiKind?.id, + hint = service.getCodeApiHint(apiName), + ) +} + +internal suspend fun loadSplitModeRestrictions(project: Project): SplitModeApiRestrictionsService { + val service = SplitModeApiRestrictionsService.getInstance(project) + service.scheduleLoadRestrictions() + withTimeoutOrNull(5.seconds) { + while (!service.isLoaded()) { + delay(50.milliseconds) + } + } ?: mcpFail("Timed out waiting for split-mode module restrictions to load") + return service +}