Skip to content

Find/Replace overlay: make the overlay the active part while focused - #4293

Open
HeikoKlare wants to merge 1 commit into
eclipse-platform:masterfrom
HeikoKlare:findreplace-overlay-own-part-context
Open

Find/Replace overlay: make the overlay the active part while focused#4293
HeikoKlare wants to merge 1 commit into
eclipse-platform:masterfrom
HeikoKlare:findreplace-overlay-own-part-context

Conversation

@HeikoKlare

Copy link
Copy Markdown
Contributor

Important

This is based on and contains #4292, which contributes regression tests for exactly the command-related behavior changed here, and it should be merged after it. Until #4292 is merged, its commit shows up in this pull request as well; only the second commit belongs to this change.

Rationale

The Find/Replace overlay is drawn inside the widget tree of the editor it searches in, so focusing one of its input fields does not change the active workbench part. The editor stays active, and its key bindings and command handlers therefore compete for every keystroke with the field the user is typing into. Getting that wrong is very visible: undo applying to the document instead of to the search field, Select All selecting the whole file, Delete editing the document.

So far this was handled by reflectively calling the private AbstractTextEditor#setActionActivation(false) while an input field has focus, and by nulling out the editor's global action handlers for multi-page editors. Both are workarounds rather than a mechanism: they reach into private API, they only work for editors derived from AbstractTextEditor, and they cover the six retargetable actions rather than the full set of commands that conflict with typing into a text field.

That coupling is also what keeps the overlay tied to one kind of editor. It is currently offered only for StatusTextEditor, and FindReplaceOverlay branches on that type in two further places.

Concept

Instead of suppressing the editor's commands one at a time, the overlay is given an IEclipseContext of its own, which it activates while one of its input fields has focus and in which it publishes its own id as the active part id.

Two independent conditions decide whether one of the editor's handlers wins a command, and the editor's commands are spread over both. Its key binding scopes and part-level handlers are only reachable through the editor's part context, and activating a context that is a sibling of that context takes them off the active chain. Its retargetable actions are not registered there at all but in the window context, guarded by an expression over the active part id, which publishing the overlay's own id makes evaluate to false.

Together this leaves no editor handler in the command resolution path, so command enablement no longer depends on the editor type, on a list of commands to suppress, or on reflection. Keys that the platform does not otherwise handle reach the native text widget, and the workbench's own default handlers for cut, copy, paste and select all act on the focused input field, so those work from the Edit menu again. The overlay's own key binding scopes are activated in that same context, which also gives them their lifetime.

What this enables

With command enablement independent of the editor type, the remaining instanceof StatusTextEditor checks can be removed in follow-up changes, which is what would let the overlay be used with editors other than text editors:

Neither is addressed here. This change removes the reason those checks exist.

Alternatives

A number of architecturally quite different approaches were considered, and several were built as proofs of concept and measured against each other, among them suppressing the editor's commands individually, giving the overlay its own shell, and modelling it as a real workbench part. The approach proposed here turned out to be both the simplest and the most flexible: it needs no command list, no reflection, no knowledge of the editor, and it leaves the workbench's notion of the active part, the active editor and the selection untouched, so nothing else in the IDE observes a change while the overlay has focus.

Because the reasoning behind that comparison is not recoverable from the code, the insights and the decision are recorded as an architecture decision record in docs/adr/0001-find-replace-overlay-key-handling.md, including the alternatives, what was measured about them, and the consequences of the chosen one.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Test Results

   858 files  ±0     858 suites  ±0   51m 22s ⏱️ - 11m 48s
 8 216 tests ±0   7 973 ✅ ±0  243 💤 ±0  0 ❌ ±0 
20 550 runs  ±0  19 880 ✅ ±0  670 💤 ±0  0 ❌ ±0 

Results for commit 57ffb15. ± Comparison against base commit c9b498b.

♻️ This comment has been updated with latest results.

Copilot AI 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.

Pull request overview

This PR changes how the Find/Replace overlay integrates with Eclipse’s command/keybinding resolution so that, while a Find/Replace input field is focused, the overlay effectively becomes the active participant in the handler/context chain (without becoming an actual workbench part), preventing editor handlers/keybindings from competing with text entry. It also adds end-to-end regression coverage and records the architectural rationale as an ADR.

Changes:

  • Introduce FindReplaceOverlayContextSupport to activate an overlay-owned IEclipseContext (shadowing activePartId) while an overlay field is focused, and to manage overlay keybinding contexts.
  • Remove the previous reflection-based / action-bar suppression mechanisms from FindReplaceOverlayCommandSupport, delegating focus-driven context behavior to the new context support.
  • Add an end-to-end editor-based UI test bundle contribution (via plugin.xml) and a new ADR documenting the decision and alternatives.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/WorkbenchTextEditorTestSuite.java Adds the new end-to-end overlay-in-editor test to the suite.
tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/internal/findandreplace/overlay/TestTextEditorInput.java New in-memory IEditorInput to support a minimal test editor.
tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/internal/findandreplace/overlay/TestTextEditor.java New minimal StatusTextEditor with in-memory document provider and text editor keybinding scope.
tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/internal/findandreplace/overlay/FindReplaceOverlayInEditorTest.java New end-to-end test validating command routing between overlay fields and host editor.
tests/org.eclipse.ui.workbench.texteditor.tests/plugin.xml Contributes the minimal test editor via the editors extension point.
tests/org.eclipse.ui.workbench.texteditor.tests/META-INF/MANIFEST.MF Marks the test bundle as a singleton to ensure plugin.xml is read.
tests/org.eclipse.ui.workbench.texteditor.tests/build.properties Includes plugin.xml in the built test bundle.
docs/adr/0001-find-replace-overlay-key-handling.md ADR documenting the chosen context-based approach and alternatives/measurements.
bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/overlay/FindReplaceOverlayContextSupport.java New overlay-owned E4 context + activePartId shadowing + keybinding context switching.
bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/overlay/FindReplaceOverlayCommandSupport.java Removes reflection/workarounds; delegates focus-driven context behavior to the new context support.
bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/overlay/FindReplaceOverlay.java Hooks command support disposal to overlay container disposal; removes focus tracking via IFocusService.
bundles/org.eclipse.ui.workbench.texteditor/plugin.xml Adjusts overlay context parent to avoid inheriting the editor scope.
bundles/org.eclipse.ui.workbench.texteditor/META-INF/MANIFEST.MF Adds required E4 bundles for the new context/model usage.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@HeikoKlare
HeikoKlare force-pushed the findreplace-overlay-own-part-context branch from bd16768 to 4cce1ed Compare August 28, 2026 18:20
@HeikoKlare
HeikoKlare marked this pull request as ready for review August 28, 2026 18:55
@HeikoKlare
HeikoKlare force-pushed the findreplace-overlay-own-part-context branch 5 times, most recently from 7319808 to 2dabbef Compare September 1, 2026 14:16
The overlay's control is parented into the editor's widget tree, so focusing an
input field does not change the active part. The editor's key bindings and
command handlers therefore stayed in effect and consumed keys meant for the
input fields, which was worked around by reflectively disabling the editor's
action activation and by nulling out its global action handlers. That workaround
reached into private API, applied only to AbstractTextEditor, and covered only
the six retargetable actions rather than the full set of conflicting commands.

Instead of suppressing the editor's commands one by one, the overlay now takes
the editor out of the resolution path while an input field has focus. Both
conditions that decide whether one of the editor's handlers wins have to be
addressed, because the editor's commands are spread over both: its key binding
scopes and part-level handlers are reachable through the editor's part context,
while its retargetable actions live in the window context and are guarded by an
expression over the active part id. Activating a context that is a sibling of
the editor's part context removes the former, and declaring that context's own
id as the active part id makes the latter evaluate to false.

The context is placed below the window context rather than below the application
context, so that window-scoped commands and services remain available. Only the
active part id is overridden, not the active part itself, so the overlay and
anything invoked from it still operate on the editor, and so do contributions
keyed on the active part, the active editor or the selection.

With no editor handler left in the resolution path, keys the platform does not
otherwise handle reach the native text widget, and the workbench-wide default
handlers for cut, copy, paste and select all act on the focused input field, so
those also work from the Edit menu again.

The overlay's key binding scopes are activated in that same context rather than
at the workbench context service. Scopes are collected along the chain between
the active leaf and the root, so a scope activated there is active exactly while
the context is the active leaf: the overlay's shared scope is activated once and
never deactivated, and only the per-field scope is switched as focus moves
between the input fields. Both kinds of context are consequently owned by one
class, leaving the command support with handler activation and shortcut hints.

Since the overlay reports itself as the active part, that is also what scopes its
own command handlers, which are activated once at the workbench. The active part
id changes at exactly the moments focus enters and leaves an input field, and it
says which overlay is focused, so the overlays of different editors no longer
need to be told apart by inspecting the focus control's widget hierarchy and no
focus tracking has to be registered for the input fields at all.

The overlay's shared scope no longer declares the text editor scope as its
parent. Parent scopes are resolved when building the set a binding lookup runs
against, so that parent would have reintroduced the editor's bindings regardless
of the context topology, and it also tied the overlay to text editors.

The active leaf is handed back when an input field loses focus only while the
overlay still holds it. Losing the focus to another part is not such a case: the
workbench activates the part under the mouse before the focus leaves the field, so
that part already owns the leaf by then and has to keep it. Handing it to the
editor anyway would raise the editor's key binding scope beside the one the part
coming up brings, and while both are up every stroke the two scopes have in common
is an unresolvable binding conflict, as observed for the zoom commands the console
and the text editor both bind.

None of this changes what the overlay owes its users, so the existing end-to-end
tests pass unchanged before and after. The one behaviour not covered by them is
that leaving the overlay for another part reports no binding conflict, which could
not be reproduced outside a running IDE: it needs the other part to hold its key
binding scope across the switch, which a part whose scope follows the active leaf
does not do, so every fixture tried settles on a single scope and never conflicts.
It is described in the architecture decision record instead of being pinned by a
test that would pass either way.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
@HeikoKlare
HeikoKlare force-pushed the findreplace-overlay-own-part-context branch from 2dabbef to 57ffb15 Compare September 2, 2026 08:44
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.

2 participants