From 063e189d5d86266be481552b7d83ec9a5fa7b6a1 Mon Sep 17 00:00:00 2001 From: Kate Botsman Date: Fri, 15 Aug 2025 18:55:27 +0200 Subject: [PATCH] IJPL-201747 Better docs for Experimental RPC APi GitOrigin-RevId: 230dab513f61ba3323b2dc9df27b8de2e59095c5 --- .../src/com/intellij/ide/rpc/DataContextId.kt | 40 ++++++++++++++- .../src/com/intellij/ide/rpc/DocumentId.kt | 28 ++++++++++- .../src/com/intellij/ide/ui/colors/ColorId.kt | 17 ++++++- .../src/com/intellij/ide/ui/icons/IconId.kt | 39 ++++++++++++++- .../src/com/intellij/ide/vfs/VirtualFileId.kt | 33 ++++++++++++- .../intellij/openapi/editor/impl/EditorId.kt | 49 ++++++++++++++++--- platform/project/shared/src/ProjectId.kt | 46 ++++++++++++++--- 7 files changed, 231 insertions(+), 21 deletions(-) diff --git a/platform/platform-impl/rpc/src/com/intellij/ide/rpc/DataContextId.kt b/platform/platform-impl/rpc/src/com/intellij/ide/rpc/DataContextId.kt index 85da701e15f6..5a3740aec820 100644 --- a/platform/platform-impl/rpc/src/com/intellij/ide/rpc/DataContextId.kt +++ b/platform/platform-impl/rpc/src/com/intellij/ide/rpc/DataContextId.kt @@ -8,7 +8,25 @@ import kotlinx.serialization.Transient import org.jetbrains.annotations.ApiStatus /** - * Converts an [DataContext] instance into a [DataContextId] which can be used in RPC calls and stored in Rhizome. + * Converts a [DataContext] instance into a [DataContextId] which can be used in RPC calls and stored in Rhizome. + * + * **WARNING: Use with extreme care, and preferably avoid using this function at all.** + * + * In the monolith version of the IDE, this essentially stores a reference to the original DataContext object. + * In Remote Development scenarios, this function attempts to serialize the entire DataContext for RPC transmission, + * but **not all elements of DataContext can be properly sent/retrieved** using this mechanism. + * Many DataContext values may: + * - Not be serializable + * - Reference local IDE components that don't exist on the remote side + * - Contain complex objects that can't be safely transmitted + * + * **Recommended approach:** Instead of using this function, manually send only the specific data you need + * via RPC. This ensures better reliability, performance, and maintainability. + * + * **For custom data types:** If you need to serialize custom DataContext elements, implement + * [com.intellij.ide.CustomDataContextSerializer] for your specific data types. + * + * @return A [DataContextId] that can be used in RPC calls, but may not contain all original DataContext elements */ @ApiStatus.Experimental fun DataContext.rpcId(): DataContextId { @@ -20,6 +38,26 @@ fun DataContext.rpcId(): DataContextId { /** * Retrieves the [DataContext] associated with the given [DataContextId]. + * + * **WARNING: Use with extreme care, and preferably avoid using this function at all.** + * + * In the monolith version of the IDE, this method essentially does nothing - it just reuses the original + * object that was passed to [rpcId]. However, in distributed scenarios (Remote Development), this function + * attempts to deserialize a DataContext from RPC data, but **not all elements of DataContext can be properly + * sent/retrieved** using this mechanism. The retrieved DataContext may: + * - Be missing important data that couldn't be serialized + * - Contain stale or invalid references + * - Not reflect the current state of the IDE + * - Fail to deserialize complex objects properly + * + * **Recommended approach:** Instead of using this function, manually send only the specific data you need + * via RPC and work with that data directly. This ensures better reliability, performance, and maintainability. + * + * **For custom data types:** If you need to deserialize custom DataContext elements, implement + * [com.intellij.ide.CustomDataContextSerializer] for your specific data types. + * + * @return The deserialized [DataContext] if available, or null if deserialization fails or no context is available. + * Note that even a non-null result may not contain all expected DataContext elements. */ @ApiStatus.Experimental fun DataContextId.dataContext(): DataContext? { diff --git a/platform/platform-impl/rpc/src/com/intellij/ide/rpc/DocumentId.kt b/platform/platform-impl/rpc/src/com/intellij/ide/rpc/DocumentId.kt index 14c1e11d8a86..c4e301aaa743 100644 --- a/platform/platform-impl/rpc/src/com/intellij/ide/rpc/DocumentId.kt +++ b/platform/platform-impl/rpc/src/com/intellij/ide/rpc/DocumentId.kt @@ -11,7 +11,19 @@ import org.jetbrains.annotations.ApiStatus private val LOG = fileLogger() /** - * Converts an [Document] instance into a [DocumentId] which can be used in RPC calls and stored in Rhizome. + * Converts a [Document] instance into a [DocumentId] which can be used in RPC calls and stored in Rhizome. + * + * **WARNING: This API is experimental and should be used with care.** + * + * In the monolith version of the IDE, this essentially stores a reference to the original document object. + * In Remote Development scenarios, the document is serialized for transmission to the frontend. + * + * Important limitations: + * - **Won't work for documents created manually on the frontend** - only documents that exist in the backend + * IDE instance can be properly serialized and retrieved + * - Document state may not be fully synchronized with the backend's one + * + * @return A [DocumentId] that can be used in RPC calls */ @ApiStatus.Experimental fun Document.rpcId(): DocumentId { @@ -22,6 +34,20 @@ fun Document.rpcId(): DocumentId { /** * Retrieves the [Document] associated with the given [DocumentId]. + * + * **WARNING: This API is experimental and should be used with care.** + * + * In the monolith version of the IDE, this method essentially does nothing - it just reuses the original + * document object that was passed to [rpcId]. However, in distributed scenarios (Remote Development), this + * function attempts to deserialize a Document from RPC data. + * + * Important limitations: + * - **Won't work for documents created manually on the frontend** - only documents that originated from + * the backend IDE instance can be properly retrieved + * - Document state may not be fully synchronized with the frontend's one + * - May return null if deserialization fails or the document is no longer available + * + * @return The [Document] if available, or null if deserialization fails or the document cannot be found */ @ApiStatus.Experimental fun DocumentId.document(): Document? { diff --git a/platform/platform-impl/rpc/src/com/intellij/ide/ui/colors/ColorId.kt b/platform/platform-impl/rpc/src/com/intellij/ide/ui/colors/ColorId.kt index f2643ad2cbe5..b50afb861dd6 100644 --- a/platform/platform-impl/rpc/src/com/intellij/ide/ui/colors/ColorId.kt +++ b/platform/platform-impl/rpc/src/com/intellij/ide/ui/colors/ColorId.kt @@ -14,7 +14,14 @@ import java.awt.Color private val LOG = fileLogger() /** - * Converts an [Color] instance into a [ColorId] which can be used in RPC calls and stored in Rhizome. + * Converts a [Color] instance into a [ColorId] which can be used in RPC calls and stored in Rhizome. + * + * **WARNING: This API is experimental and should be used with care.** + * + * In the monolith version of the IDE, this essentially stores a reference to the original Color object. + * In Remote Development scenarios, the Color is serialized for transmission to the frontend. + * + * @return A [ColorId] that can be used in RPC calls */ @ApiStatus.Experimental fun Color.rpcId(): ColorId { @@ -26,6 +33,14 @@ fun Color.rpcId(): ColorId { /** * Retrieves the [Color] associated with the given [ColorId]. + * + * **WARNING: This API is experimental and should be used with care.** + * + * In the monolith version of the IDE, this method essentially does nothing - it just reuses the original + * Color object that was passed to [rpcId]. However, in distributed scenarios (Remote Development), this + * function attempts to deserialize a Color from RPC data. + * + * @return The [Color] if available, or [JBColor.BLACK] if deserialization fails */ @ApiStatus.Experimental fun ColorId.color(): Color { diff --git a/platform/platform-impl/rpc/src/com/intellij/ide/ui/icons/IconId.kt b/platform/platform-impl/rpc/src/com/intellij/ide/ui/icons/IconId.kt index 6f8ed4facf35..8e73a98cf923 100644 --- a/platform/platform-impl/rpc/src/com/intellij/ide/ui/icons/IconId.kt +++ b/platform/platform-impl/rpc/src/com/intellij/ide/ui/icons/IconId.kt @@ -14,7 +14,22 @@ import javax.swing.Icon private val LOG = fileLogger() /** - * Converts an [Icon] instance into a [IconId] which can be used in RPC calls and stored in Rhizome. + * Converts an [Icon] instance into an [IconId] which can be used in RPC calls and stored in Rhizome. + * + * **WARNING: This API is experimental and should be used with care.** + * + * In the monolith version of the IDE, this essentially stores a reference to the original icon object. + * In Remote Development scenarios, the icon is serialized for transmission to the frontend. + * + * Important limitations: + * - **Might not work for custom icon classes** - serialization may fail for custom Icon implementations + * - **Recommended approach:** Use default icon classes provided by the platform API + * instead of custom implementations for better compatibility + * - Custom icons may not serialize/deserialize properly across RPC boundaries + * + + * @return An [IconId] that can be used in RPC calls + * @throws Exception if the icon cannot be serialized (use [rpcIdOrNull] for safer handling) */ @ApiStatus.Experimental fun Icon.rpcId(): IconId { @@ -24,6 +39,15 @@ fun Icon.rpcId(): IconId { return IconId(serializedIcon, icon) } +/** + * Converts an [Icon] instance into an [IconId] which can be used in RPC calls and stored in Rhizome. + * + * **WARNING: This API is experimental and should be used with care.** + * + * This is a safer version of [rpcId] that returns null instead of throwing an exception when serialization fails. + * + * @return An [IconId] that can be used in RPC calls, or null if serialization fails + */ @ApiStatus.Experimental fun Icon.rpcIdOrNull(): IconId? { val icon = this @@ -40,6 +64,19 @@ fun Icon.rpcIdOrNull(): IconId? { /** * Retrieves the [Icon] associated with the given [IconId]. + * + * **WARNING: This API is experimental and should be used with care.** + * + * In the monolith version of the IDE, this method essentially does nothing - it just reuses the original + * icon object that was passed to [rpcId]. However, in distributed scenarios (Remote Development), this + * function attempts to deserialize an Icon from RPC data. + * + * Important limitations: + * - **Might not work for custom icon classes** - deserialization may fail for custom Icon implementations + * - **Recommended approach:** Use default icon classes provided by the platform API for better compatibility and reliability + * - Falls back to [AllIcons.Empty] if deserialization fails + * + * @return The [Icon] if available, or [AllIcons.Empty] if deserialization fails */ @ApiStatus.Experimental fun IconId.icon(): Icon { diff --git a/platform/platform-impl/rpc/src/com/intellij/ide/vfs/VirtualFileId.kt b/platform/platform-impl/rpc/src/com/intellij/ide/vfs/VirtualFileId.kt index a2f607d23a95..acefec5358aa 100644 --- a/platform/platform-impl/rpc/src/com/intellij/ide/vfs/VirtualFileId.kt +++ b/platform/platform-impl/rpc/src/com/intellij/ide/vfs/VirtualFileId.kt @@ -13,7 +13,21 @@ import org.jetbrains.annotations.ApiStatus private val LOG = fileLogger() /** - * Converts an [VirtualFile] instance into a [VirtualFileId] which can be used in RPC calls and stored in Rhizome. + * Converts a [VirtualFile] instance into a [VirtualFileId] which can be used in RPC calls and stored in Rhizome. + * + * **WARNING: This API is experimental and should be used with care.** + * + * In the monolith version of the IDE, this essentially stores a reference to the original VirtualFile object. + * In Remote Development scenarios, the VirtualFile is serialized for transmission to the frontend. + * + * Important limitations: + * - **Won't work for files created manually on the frontend** - only files that exist in the backend + * IDE instance can be properly serialized and retrieved + * - VirtualFile references may become stale or invalid across RPC boundaries + * - File system state may change between serialization and deserialization + * - Some VirtualFile implementations may not serialize properly + * + * @return A [VirtualFileId] that can be used in RPC calls */ @ApiStatus.Experimental fun VirtualFile.rpcId(): VirtualFileId { @@ -24,6 +38,23 @@ fun VirtualFile.rpcId(): VirtualFileId { /** * Retrieves the [VirtualFile] associated with the given [VirtualFileId]. + * + * **WARNING: This API is experimental and should be used with care.** + * + * In the monolith version of the IDE, this method essentially does nothing - it just reuses the original + * VirtualFile object that was passed to [rpcId]. However, in distributed scenarios (Remote Development), this + * function attempts to deserialize a VirtualFile from RPC data. + * + * Important limitations: + * - **Won't work for files created manually on the frontend** - only files that originated from + * the backend IDE instance can be properly retrieved + * - VirtualFile references may become stale or invalid after RPC transmission + * - File system state may have changed since serialization + * - The file may have been deleted, moved, or modified + * - Some VirtualFile implementations may not deserialize properly + * - May return null if deserialization fails or the file is no longer available + * + * @return The [VirtualFile] if available, or null if deserialization fails or the file cannot be found */ @ApiStatus.Experimental fun VirtualFileId.virtualFile(): VirtualFile? { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorId.kt b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorId.kt index 1aa4f6c17cbd..5f5ea1ceb8d8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorId.kt +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorId.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.openapi.editor.impl import com.intellij.openapi.editor.Editor @@ -53,10 +53,19 @@ val KERNEL_EDITOR_ID_KEY = Key.create("EditorImpl.KERNEL_EDITOR_ID") /** * Provides [EditorId] for the given [Editor]. - * This [EditorId] can be used for RPC calls between frontend and backend + * This [EditorId] can be used for RPC calls between frontend and backend. + * + * **WARNING: This API is experimental and should be used with care.** + * + * This function retrieves the unique identifier assigned to an Editor instance, returning null if not found. + * Important considerations: + * - Returns null for editors that have not been properly initialized with an EditorId + * - In Remote Development scenarios, ensures editor identity is maintained across frontend/backend boundaries + * - Safe to use for RPC transmission as EditorId is serializable + * - Safer alternative to [editorId] that doesn't throw exceptions * * @return The [EditorId] instance associated with the provided [Editor], - * or null if [Editor]'s implementation didn't assign id to it. + * or null if [Editor]'s implementation didn't assign id to it */ @ApiStatus.Experimental fun Editor.editorIdOrNull(): EditorId? { @@ -65,7 +74,15 @@ fun Editor.editorIdOrNull(): EditorId? { /** * Provides [EditorId] for the given [Editor]. - * This [EditorId] can be used for RPC calls between frontend and backend + * This [EditorId] can be used for RPC calls between frontend and backend. + * + * **WARNING: This API is experimental and should be used with care.** + * + * This function retrieves the unique identifier assigned to an Editor instance. Important considerations: + * - Only works for editors that have been properly initialized with an EditorId + * - In Remote Development scenarios, ensures editor identity is maintained across frontend/backend boundaries + * - Safe to use for RPC transmission as EditorId is serializable + * - Throws an exception if the editor doesn't have an assigned ID * * @return The [EditorId] instance associated with the provided [Editor] * @throws IllegalStateException if [Editor]'s implementation didn't assign [EditorId] to it @@ -78,8 +95,16 @@ fun Editor.editorId(): EditorId { /** * Provides [Editor] for the given [EditorId]. * + * **WARNING: This API is experimental and should be used with care.** + * + * This function attempts to locate an Editor instance by its unique identifier. Important considerations: + * - **Won't work for editors created on the frontend** - only editors that exist on the backend + * can be found using this method + * - Only finds editors that are currently open and have been assigned the corresponding EditorId + * - May return null if the editor was closed or disposed after the EditorId was obtained + * * @return The [Editor] instance associated with the provided [EditorId], - * or null if there is no editor with the given [EditorId]. + * or null if there is no editor with the given [EditorId] */ @ApiStatus.Experimental fun EditorId.findEditorOrNull(): Editor? { @@ -89,9 +114,17 @@ fun EditorId.findEditorOrNull(): Editor? { /** * Provides [Editor] for the given [EditorId]. * - * @return The [Editor] instance associated with the provided [EditorId], - * or null if there is no editor with the given [EditorId]. - * @throws IllegalStateException if there is no editor with the given [EditorId]. + * **WARNING: This API is experimental and should be used with care.** + * + * This function attempts to locate an Editor instance by its unique identifier and throws an exception if not found. + * Important considerations: + * - **Won't work for editors created on the frontend** - only editors that exist on the backend + * can be found using this method + * - Only finds editors that are currently open and have been assigned the corresponding EditorId + * - Throws an exception if the editor was closed or disposed after the EditorId was obtained + * + * @return The [Editor] instance associated with the provided [EditorId] + * @throws IllegalStateException if there is no editor with the given [EditorId] */ @ApiStatus.Experimental fun EditorId.findEditor(): Editor { diff --git a/platform/project/shared/src/ProjectId.kt b/platform/project/shared/src/ProjectId.kt index 869b6b6148c3..34de315b2f07 100644 --- a/platform/project/shared/src/ProjectId.kt +++ b/platform/project/shared/src/ProjectId.kt @@ -91,10 +91,19 @@ fun setNewProjectId(project: Project, newProjectId: ProjectId) { /** * Provides the [ProjectId] for the given [Project]. - * This [ProjectId] can be used for RPC calls between frontend and backend + * This [ProjectId] can be used for RPC calls between frontend and backend. + * + * **WARNING: This API is experimental and should be used with care.** + * + * This function retrieves the unique identifier assigned to a Project instance, returning null if not found. + * Important considerations: + * - Returns null for projects that have not been properly registered with a ProjectId + * - In Remote Development scenarios, ensures project identity is maintained across frontend/backend boundaries + * - Safe to use for RPC transmission as ProjectId is serializable + * - Safer alternative to [projectId] that doesn't throw exceptions * * @return The [ProjectId] instance associated with the provided [Project], - * or null if [Project]'s implementation didn't assign id to it. + * or null if [Project]'s implementation didn't assign id to it */ @ApiStatus.Experimental fun Project.projectIdOrNull(): ProjectId? { @@ -103,10 +112,17 @@ fun Project.projectIdOrNull(): ProjectId? { /** * Provides the [ProjectId] for the given [Project]. - * This [ProjectId] can be used for RPC calls between frontend and backend + * This [ProjectId] can be used for RPC calls between frontend and backend. * - * @return The [ProjectId] instance associated with the provided [Project], - * @throws IllegalStateException if [Project]'s implementation didn't assign id to it. + * **WARNING: This API is experimental and should be used with care.** + * + * This function retrieves the unique identifier assigned to a Project instance. Important considerations: + * - Only works for projects that have been properly registered with a ProjectId + * - In Remote Development scenarios, ensures project identity is maintained across frontend/backend boundaries + * - Safe to use for RPC transmission as ProjectId is serializable + * + * @return The [ProjectId] instance associated with the provided [Project] + * @throws IllegalStateException if [Project]'s implementation didn't assign id to it */ @ApiStatus.Experimental fun Project.projectId(): ProjectId { @@ -125,8 +141,15 @@ fun Project.projectIdOrNullWithLogError(log: Logger): ProjectId? { /** * Provides [Project] for the given [ProjectId]. * + * **WARNING: This API is experimental and should be used with care.** + * + * This function attempts to locate a Project instance by its unique identifier. Important considerations: + * - Only finds projects that are currently open and registered in the IDE + * - In Remote Development scenarios, only searches for projects on the current (backend) side + * - May return null if the project was closed or unregistered after the ProjectId was obtained + * * @return The [Project] instance associated with the provided [ProjectId], - * or null if there is no project with the given [ProjectId]. + * or null if there is no project with the given [ProjectId] */ @ApiStatus.Experimental fun ProjectId.findProjectOrNull(): Project? { @@ -151,8 +174,15 @@ fun ProjectId.findProjectOrNullWithLogError(log: Logger): Project? { /** * Provides [Project] for the given [ProjectId]. * - * @return The [Project] instance associated with the provided [ProjectId], - * @throws IllegalStateException if there is no project with the given [ProjectId]. + * **WARNING: This API is experimental and should be used with care.** + * + * This function attempts to locate a Project instance by its unique identifier and throws an exception if not found. + * Important considerations: + * - Only finds projects that are currently open and registered in the IDE + * - Throws an exception if the project was closed or unregistered after the ProjectId was obtained + * + * @return The [Project] instance associated with the provided [ProjectId] + * @throws IllegalStateException if there is no project with the given [ProjectId] */ @ApiStatus.Experimental fun ProjectId.findProject(): Project {