Skip to content

ADFA-4419: Remote peer editor decoration API#1459

Open
Daniel-ADFA wants to merge 9 commits into
stagefrom
feat/ADFA-4419-remote-peer-editor-decoration
Open

ADFA-4419: Remote peer editor decoration API#1459
Daniel-ADFA wants to merge 9 commits into
stagefrom
feat/ADFA-4419-remote-peer-editor-decoration

Conversation

@Daniel-ADFA

@Daniel-ADFA Daniel-ADFA commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Host-side support for the pair-programming plugin (ADFA-4419): lets the external .cgp pair plugin render remote collaborators in the editor and drive shared editing entirely through plugin-api. The pair plugin itself ships separately; this PR adds only the host contract + bridge it talks to.

Daniel-ADFA and others added 8 commits May 28, 2026 23:42
Add IdeEditorService.addRemotePeerMarker/removeRemotePeerMarker/clearRemotePeerMarkers as default-implemented (backward-compatible) methods so a plugin can draw a remote collaborator's caret/badge inside the editor.

Backed by a new EditorDecorationManager + RemotePeerMarkerWindow (an EditorPopupWindow overlay that tracks scroll via FEATURE_SCROLL_AS_CONTENT) in the app module; EditorProviderImpl resolves the live editor via EditorHandlerActivity.getEditorForFile and marshals onto the main thread, clearing markers on dispose. IdeEditorServiceImpl exposes read-gated overrides and the parallel EditorProvider contract methods.

The new interface methods are default-implemented so this is an additive, non-breaking change for the generated plugin-api lib and existing implementers. Consumed by the Pair pair-programming plugin.
…orations

Rename EditorDecorationManager -> PeerPresenceOverlayManager and extract a
focused PeerPresenceProvider interface out of the broad EditorProvider, so the
pair-programming peer-cursor overlay (floating named badges) reads as a distinct
concern from the generic EditorDecorationProvider (additive color spans) added
in #1448 (ADFA-4436).

Host-internal only: no plugin-api contract changed and the merged rainbow-
brackets plugin is unaffected. Verified with :app:compileV8DebugKotlin.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@Daniel-ADFA Daniel-ADFA changed the title ADFA-4419: Remote peer editor decoration (pair programming) ADFA-4419: Remote peer editor decoration API Jun 28, 2026
@Daniel-ADFA

Daniel-ADFA commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

@claude review

@Daniel-ADFA Daniel-ADFA requested a review from a team June 28, 2026 23:52
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 834dd403-f836-4e8d-b14d-334d19e1728a

📥 Commits

Reviewing files that changed from the base of the PR and between ea2293a and 90dbab1.

📒 Files selected for processing (5)
  • app/src/main/java/com/itsaky/androidide/activities/editor/PeerPresenceOverlayManager.kt
  • app/src/main/java/com/itsaky/androidide/repositories/PluginRepositoryImpl.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeEditorServiceImpl.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt
💤 Files with no reviewable changes (1)
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeEditorServiceImpl.kt
🚧 Files skipped from review as they are similar to previous changes (4)
  • app/src/main/java/com/itsaky/androidide/repositories/PluginRepositoryImpl.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/PeerPresenceOverlayManager.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt

📝 Walkthrough
  • Added host-side support for remote peer presence in the editor via PeerPresenceOverlayManager/PeerCursorWindow, rendering floating caret/name badges per (file, peerId) and updating badge position with editor-aware line/column validation and clamping.
  • Integrated the peer presence overlay into EditorProviderImpl so peer cursor show/hide/clear operations add/remove/clear overlay markers and cleanup runs during provider disposal.
  • Introduced/extended the plugin-facing plugin-api contract in IdeServices with default no-op methods for IdeProjectService.openProject(projectDir) and IdeEditorService.showPeerCursor(...) (visual overlay only; does not mutate file contents).
  • Wired peer cursor operations through the plugin host stack (IdeEditorServiceImpl and PluginManager) by introducing a peer presence provider, enforcing FILESYSTEM_READ, and forwarding calls to the active editor provider.
  • Enhanced plugin installation robustness in PluginRepositoryImpl by verifying the plugin actually becomes available after load; on failure, deletes the copied artifact and throws with the recorded load error when possible.
  • Improved plugin load error handling in PluginManager by tracking per-plugin load failures (getLoadError(pluginId)), recording exceptions for later diagnostics, and rethrowing CancellationException to preserve coroutine cancellation semantics.
  • Implemented IdeProjectServiceImpl.openProject(projectDir) with permission checks, canonical containment validation under Environment.PROJECTS_DIR, path-policy enforcement, foreground-activity dependency via activityProvider, UI-thread activity recreation, and updates to lastOpenedProject.
  • Risk: the overlay-based peer cursor flow is separate from (and may conceptually overlap with) decoration-provider approaches, which could cause duplicated UX if both are enabled without coordination.
  • Risk: openProject(projectDir) performs privileged filesystem and activity operations; incorrect permission/path validation could impact security or stability.
  • Risk: overlay rendering depends on correct editor instance lookup and UI-thread lifecycle; timing/stale references could lead to misplacement or missed cleanup if not handled consistently.

Walkthrough

Adds peer-cursor presence rendering, an openProject plugin API and implementation, and plugin load-failure tracking with install-time verification.

Changes

Peer Cursor Overlay Feature

Layer / File(s) Summary
Plugin API contracts
plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/services/IdeServices.kt
Adds default openProject(projectDir) to IdeProjectService and showPeerCursor(...) to IdeEditorService with KDoc describing semantics and permissions.
Editor service delegation
plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeEditorServiceImpl.kt, plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt
Adds PeerPresenceProvider, extends EditorProvider with peer-cursor methods, implements read-gated delegation in IdeEditorServiceImpl, and forwards those calls through PluginManager when an editor provider is wired.
Overlay manager and window
app/src/main/java/com/itsaky/androidide/activities/editor/PeerPresenceOverlayManager.kt
Implements the peer presence registry, marker lifecycle, popup badge rendering, arrow handling, positioning, and text contrast selection.
Editor provider wiring
app/src/main/java/com/itsaky/androidide/app/EditorProviderImpl.kt
Adds the overlay manager to EditorProviderImpl, delegates peer-cursor operations to it, and clears overlays during dispose().

openProject Plugin API Implementation

Layer / File(s) Summary
Project opening implementation
plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt
Adds activityProvider to IdeProjectServiceImpl and implements openProject with permission checks, canonical project-dir validation, activity lookup, state updates, and UI-thread recreation.
Plugin context wiring
plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt
Passes activityProvider into IdeProjectServiceImpl from both plugin context creation paths.

Plugin Load Error Tracking

Layer / File(s) Summary
Load failure recording
plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt
Adds loadFailures storage, cancellation-aware parallel loading, recordLoadFailure, and getLoadError for per-plugin failure messages.
Install-time verification
app/src/main/java/com/itsaky/androidide/repositories/PluginRepositoryImpl.kt
Checks that the loaded plugin exists after install, deletes the copied artifact when it does not, and throws with the recorded load error when available.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • jatezzz
  • itsaky-adfa

Poem

🐇 Hop through the editor, a badge in the light,
Peer names now appear and the colors feel right.
Projects can open with paths kept in line,
And load failures whisper when plugins don’t shine.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.21% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the new remote peer editor decoration API.
Description check ✅ Passed The description is directly related to the host-side plugin API and bridge changes in the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ADFA-4419-remote-peer-editor-decoration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@app/src/main/java/com/itsaky/androidide/activities/editor/PeerPresenceOverlayManager.kt`:
- Around line 122-125: The clamping logic in PeerPresenceOverlayManager’s
position calculation does not handle the exact-fit case because when maxX is 0
it falls back to rawX, allowing the overlay to render off-screen; update the x
computation so the clamp applies whenever the editor width is known, including
when label.measuredWidth equals boundEditor.width, and keep the existing
rawX.coerceIn(0, maxX) behavior for all non-negative maxX values.

In
`@app/src/main/java/com/itsaky/androidide/repositories/PluginRepositoryImpl.kt`:
- Around line 147-154: The failure path in installPluginFromFile() leaves a
broken upgraded plugin on disk after the old version has already been removed,
so adjust the replace flow to avoid half-installed upgrades. Use the existing
load check around manager.loadPlugins()/manager.getPlugin(pluginId) to either
delay uninstalling the current plugin until the new package is proven loadable,
or restore the previous package on failure. In the failure branch that throws
IllegalStateException, also delete the copied finalFile so future
PluginRepositoryImpl loads do not keep retrying the bad artifact.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt`:
- Around line 279-284: The load path in PluginManager.loadPlugins is swallowing
coroutine cancellation by catching all Exception around loadPlugin, so a
cancelled job is treated like a plugin failure instead of stopping execution.
Narrow the catch in the loadPlugin/result block to let CancellationException
pass through unchanged, and only wrap genuine plugin load errors in
Result.failure before calling recordLoadFailure.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeEditorServiceImpl.kt`:
- Around line 283-292: `hidePeerCursor` and `clearPeerCursors` in
`IdeEditorServiceImpl` are unnecessarily gated by `ensureFileAccessible(file)`,
which blocks overlay cleanup when the file/tab is no longer open. Remove the
accessibility check from these cleanup-only paths while keeping `requireRead()`
and the downstream `editorProvider.hidePeerCursor(...)` /
`editorProvider.clearPeerCursors(...)` calls so peer overlays can still be
dismissed by file key.

In
`@plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt`:
- Around line 97-105: Use the validated canonical project path in
IdeProjectServiceImpl.openProject and apply the existing PathValidator before
switching projects. The current flow checks containment with
isUnderProjectsDir(projectDir) but later persists the original path, so update
the openProject logic to reuse the validated canonical target and run it through
PathValidator like getProjectByPath does before calling the project
switch/selection code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e0348b09-b91a-4370-9cb1-9c72e245fff6

📥 Commits

Reviewing files that changed from the base of the PR and between c14733b and ea2293a.

📒 Files selected for processing (7)
  • app/src/main/java/com/itsaky/androidide/activities/editor/PeerPresenceOverlayManager.kt
  • app/src/main/java/com/itsaky/androidide/app/EditorProviderImpl.kt
  • app/src/main/java/com/itsaky/androidide/repositories/PluginRepositoryImpl.kt
  • plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/services/IdeServices.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/core/PluginManager.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeEditorServiceImpl.kt
  • plugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/services/IdeProjectServiceImpl.kt

- PeerPresenceOverlayManager: clamp peer badge on exact-fit width (maxX >= 0)
- PluginManager.loadPlugins: rethrow CancellationException instead of recording
  cancellation as a plugin load failure
- IdeEditorServiceImpl: don't gate hidePeerCursor/clearPeerCursors on file
  accessibility, so overlay cleanup still works after a tab closes
- IdeProjectServiceImpl.openProject: use the validated canonical path and run it
  through PathValidator before switching projects
- PluginRepositoryImpl: delete the broken artifact when an upgraded plugin fails
  to load, so loadPlugins() doesn't keep retrying it
- Drop PairTrace / [HOST] dev-trace Log.d (kept warn/error diagnostics)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants