Skip to content

2026.2 - #103

Merged
hoxi merged 16 commits into
comod:masterfrom
hoxi:2026_2
Jul 27, 2026
Merged

2026.2#103
hoxi merged 16 commits into
comod:masterfrom
hoxi:2026_2

Conversation

@hoxi

@hoxi hoxi commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

hoxi added 16 commits July 27, 2026 19:52
Restructure the plugin into a modular architecture (shared/backend/
frontend) for JetBrains Remote Development split mode compatibility,
following the official IntelliJ Platform Modular Plugin guidelines.

Module structure:
- shared: GutterDataService, GitScopeSettings, @rpc interface, DTOs,
  Range/RangesBuilder
- backend: VCS operations, change collection, line status tracking,
  tool window, actions
- frontend: gutter rendering, editor interactions, popup UI,
  file-open data pickup
- root: plugin.xml with <content> module declarations only

Split mode compliance (per JetBrains docs):
- Module descriptors in src/main/resources/ (not META-INF)
- Backend XML declares dependencies on intellij.platform.backend,
  kernel.backend, shared
- Frontend XML declares dependencies on intellij.platform.frontend,
  shared
- Shared XML declares dependency on intellij.platform.rpc
- Content modules use required-if-available for strict loading
- splitMode=true and pluginInstallationTarget=BOTH at build level
- @rpc interface with RemoteApi<Unit> in shared, RemoteApiProvider
  in backend
- Frontend uses durable {} for automatic RPC reconnection
- rpc compiler plugin (fleet rpc-compiler-plugin 2.3.20-0.1) applied
  to all modules
- Backend RPC replays all current state on subscription
- Monolithic mode guard: frontend skips RPC, uses GutterDataService
  directly

RPC communication:
- GutterRpcApi defines
  getGutterUpdates(ProjectId): Flow<GutterUpdateEvent>
- GutterUpdateEvent is a sealed class:
  DataUpdated / DataCleared / AllCleared
- BackendGutterRpcImpl uses callbackFlow — registers listener before
  replay to avoid race condition
- FrontendGutterSubscriptions subscribes with durable {} in split
  mode only
- GutterFileDataDto, GutterRangeDto are @serializable DTOs

Dependency changes:
- Remove RxJava — replaced with CopyOnWriteArrayList listener
  pattern in MyModel and ViewService
- Remove Gson runtime — compileOnly (platform provides it)
- No external runtime dependencies in any module (classloader
  isolation safe)

Startup initialization:
- ViewService.initLater() selects saved tab with listener suppressed
  (tabInitializationInProgress), then calls setActiveModel() once
- MyTabContentListener checks isTabInitializationInProgress() to
  avoid double-firing
- GutterRenderingService registers fileOpened handler to pick up
  existing data on editor switch

Gutter popup improvements:
- Close popup on mouse wheel scroll and caret movement
- Keep popup open during prev/next change navigation
  (suppressCaretCancel flag)

Other fixes:
- Guard null changes in ToolWindowView.render()
- GutterDataService.getAllData() for RPC replay
- SharedReflection stripped to getGutterArea() only
- pluginInstallationTarget replaces deprecated splitModeTarget

Fixes comod#91
Add UtilRpcApi with NavigationCommand flow so Show in Project works
in both monolith and split mode. The backend emits navigation commands
via NavigationCommandService, and the frontend subscribes and activates
the Project tool window with focus.

Directory detection uses the tree node's FilePath to distinguish folder
nodes from file nodes, navigating to the folder itself rather than
picking an arbitrary child file.

Closes comod#100
Replace NavigationCommandService with UtilCommandService using a
sealed UtilCommand class (SelectInProject, OpenPreviewTab) for
extensibility.

Preview tab now opens via RPC on the frontend using
OpenFileDescriptor.setUsePreviewTab(true), fixing split mode where
the backend cannot open editor tabs directly.

Send file URLs instead of paths to ensure proper VirtualFile
resolution on the frontend in split mode.

Skip UISettings.getOpenInPreviewTabIfPossible() check in split mode
since that setting is a frontend concern not synced to backend.

Fix PlatformApiReflection for Kotlin @jvmoverloads synthetic
constructor (int defaultsMask + DefaultConstructorMarker params)
in CLion 2026.1+.
- Use fixed JBUI.scale(3) for hover expansion, matching
  LineStatusMarkerDrawUtil.getHoveredMarkerExtraWidth()
- Add editor font zoom scaling to fallback gutter width calculation
  via EditorImpl.getScale(), matching scaleWithEditor() in the IDE
- Change renderer position from LEFT to CUSTOM to match the IDE's
  LineStatusGutterMarkerRenderer and avoid unintended left free
  painters area expansion
- Use marker width as corner arc radius (pill shape) to match the
  IDE's RectanglePainter2D rendering style
…iew-tab setting

Open files through FileEditorManagerEx.openFile (the same per-client path Project
View uses): full editor init, IDE + scope gutters, and no duplicate tab in split
mode. Respect the 'Enable preview tab' setting in both modes: read UISettings
directly in monolith; in split mode use the value the frontend pushes over a new
setPreviewTabEnabled RPC, since the backend UISettings is not synced there.
Move the build to JBR 25 for 2026.2 compatibility:
- JDK 25 toolchain and JVM 25 target across all projects, including the root
  aggregator, so it can consume the JVM-25 modules
- bump IntelliJ Platform Gradle Plugin 2.16.0 -> 2.18.1; rename custom run tasks
  and migrate off the deprecated 'by registering' syntax (Gradle 9.6)
- Gradle wrapper 9.5.1 -> 9.6.1, platform 2026.1.2 -> 2026.1.4, gson -> 2.14.0
- raise plugin since-build floor to 261 (2026.1)
2026.2 splits the VCS APIs into finer content modules that are no longer
pulled in transitively via the Git4Idea plugin. Declare them explicitly on
the backend (vcs.impl, vcs.impl.shared, vcs.dvcs[.impl][.shared],
vcs.log.impl) so the existing code compiles unchanged, and bump
platformVersion 2026.1.4 -> 2026.2.
Add actions (bindable in Keymap, no default shortcuts) to review a scope
without the mouse:
- Next/Previous Change: step through changes; cross into the adjacent changed
  file at a file's ends; wrap at the global ends
- Next/Previous Changed File: cycle to the first change of the adjacent file
- Show Diff: open the scope diff for the current file as an editor tab, reusing
  the existing tab (rebuilding when the scope base revision changed); Close Diff
  closes the active diff tab

Navigation and diff run on the backend (authoritative scope change set) via the
canonical open path, so files open with full init and gutters, honor the
preview-tab setting, and highlight in the Git Scope tree — working in both
monolith and split/remote mode. Extract shared open logic into FileOpener and
the scope diff producer into ScopeDiff. Document the review workflow and the
preview-tab tip in the README.
The actions already appear under the Git Scope group in Keymap, so the prefix
is redundant. Rename the review actions (Next/Previous Change, Next/Previous
Changed File, Show Diff, Close Diff) and the Toggle Head action, and update the
README tables to match.
Remove the FileEditorOpenRequest / openInPreviewTab reflection from
PlatformApiReflection. It was superseded by FileOpener calling the
FileEditorManagerEx.openFile(FileEditorOpenRequest) API directly, and the
reflective openInPreviewTab method had no remaining callers.

Also ignore the .kotlin build-session cache.
Single- and double-click handlers used getSelectedChanges(), which for a
directory node aggregates all changes beneath it — so clicking a folder opened
its first file. Resolve the actual clicked/selected node instead and only open
when it is a file/change leaf. For a directory, double-click now toggles
expand/collapse (the platform's double-click handler reports the event as
handled, suppressing the tree's default toggle, so we do it explicitly).
The custom Close Diff action didn't work reliably in split mode (the keystroke
was consumed as editor input rather than routed to the action), and it is
redundant: the standard Close Tab / Close Editor shortcut closes a diff tab and
always works. Remove the action, its registration, and the README references.
Change navigation traversed only scope changes. Local changes (the IDE's own
gutter markers, which we exclude from our scope painting) were computed in the
gutter pipeline purely for exclusion and then discarded, so navigation skipped
them.

Publish those local ranges via GutterFileData and traverse the union of scope
and local changes: the file list is now the union of both change sets, and the
per-file stop lines are the de-duplicated union of scope and local marker start
lines. Open files reuse the cached gutter ranges (exact match with the visible
markers); unopened files compute both range sets on demand.
@hoxi hoxi self-assigned this Jul 27, 2026
@hoxi
hoxi merged commit a255d8c into comod:master Jul 27, 2026
1 check passed
@hoxi
hoxi deleted the 2026_2 branch July 27, 2026 22:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant