Skip to content

FindReplaceOverlay: share search settings per editor stack#1

Open
vlakn wants to merge 1 commit into
masterfrom
findreplaceoverlay_same_setting_across_stack
Open

FindReplaceOverlay: share search settings per editor stack#1
vlakn wants to merge 1 commit into
masterfrom
findreplaceoverlay_same_setting_across_stack

Conversation

@vlakn

@vlakn vlakn commented Jun 25, 2026

Copy link
Copy Markdown

Editors in the same tab group now share a common find/replace state. Switching between tabs in a stack keeps the overlay in sync: it auto-opens on the newly activated editor and auto-closes when another stack-mate closes it.

Independent stacks (e.g. side-by-side editors) maintain their own separate settings.

Aufzeichnung 2026-06-25 104918

Fixes: eclipse-platform#2055

Editors in the same tab group now share a common find/replace
state. Switching between tabs in a stack keeps the overlay in sync: it
auto-opens on the newly activated editor and auto-closes when
another stack-mate closes it.

Independent stacks (e.g. side-by-side editors) maintain their
own separate settings.

Fixes: eclipse-platform#2055
@r-mennig

r-mennig commented Jun 25, 2026

Copy link
Copy Markdown

I've found some issues with the current implementation in regard to marker resets / selection changes:

BugSearch CursorJumps CursorJumps_Replace TabSwitch

Comment on lines +366 to +369
IWorkbenchPartSite site = targetPart.getSite();
if (site == null) {
return null;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

getSite() sollte zu diesem Zeitpunkt nicht mehr null sein dürfen, da die Initialisierung vom Part schon abgeschlossen sein muss

Comment on lines +383 to +385
if (isRestoringSettings) {
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Das hier finde ich nicht so schön gelöst.

Dieser isRestoringSettings-Guard ist ja nach meinem Verständnis nur notwendig ist, weil dieser in applyStackSettings von searchBar.setText(...) sonst nochmal aufgerufen werden würde (hier wäre übrigens ein Kommentar hilfreich, ohne zu Debuggen ist sonst kaum zu erkennen, wofür das gedacht ist).

Image

Den Check dafür würde ich daher nicht hier, sondern im ModifyListener der Searchbar einbauen (mit Kommentar).

Am besten wäre es natürlich den Textinhalt der Searchbar wiederherzustellen, ohne dabei ein Update auszulösen. @HeikoKlare Hast du noch Ideen, was man hier machen könnte?

Comment on lines +616 to +631
private void openForStack() {
if (!containerControl.isVisible()) {
containerControl.setVisible(true);
bindListeners();
restoreOverlaySettings();
setOverlayOpenInStack(true);
registerStackMemberActivationListener();
}
assignIDs();
containerControl.layout();
containerControl.moveAbove(null);
updatePlacementAndVisibility();
updateContentAssistAvailability();

searchBar.setFocus();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Der Code ist fast identisch mit der open()-Methode -> als open(boolean forStack) kombinieren

Comment on lines +587 to +596
private void silentClose() {
if (containerControl.isDisposed() || !containerControl.getVisible()) {
return;
}
setOverlayOpenInStack(false);
storeOverlaySettings();
findReplaceLogic.activate(SearchOptions.GLOBAL);
unbindListeners();
containerControl.setVisible(false);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Der Code ist fast identisch mit der close()-Methode -> als close(boolean silently) kombinieren

Comment on lines +1332 to +1359
@Override
public void partDeactivated(IWorkbenchPartReference partRef) {
/* unused */ }

@Override
public void partOpened(IWorkbenchPartReference partRef) {
/* unused */ }

@Override
public void partClosed(IWorkbenchPartReference partRef) {
/* unused */ }

@Override
public void partHidden(IWorkbenchPartReference partRef) {
/* unused */ }

@Override
public void partVisible(IWorkbenchPartReference partRef) {
/* unused */ }

@Override
public void partBroughtToTop(IWorkbenchPartReference partRef) {
/* unused */ }

@Override
public void partInputChanged(IWorkbenchPartReference partRef) {
/* unused */ }
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Evtl. könnte hier ein separates Issue erzeugt werden, um eine generische PartListenerAdapter-Klasse zu definieren.

@fedejeanne Was meinst du? Ich sehe keinen Grund, warum ein Verwender hier immer auf alle Part-Events reagieren sollte

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Man kann einfach die leere Methoden löschen, IPartListener2 hat bereits leere default-Methoden

* always show the same search term and options, while independent editor stacks
* (e.g. side-by-side layouts) can carry different, unrelated search sessions.
*/
public class OverlaySearchSettings {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Das würde sich als Record meiner Meinung nach schöner abbilden lassen und ein HashCode/Equals gäbs dann auch noch gratis dazu :)


private static final WeakHashMap<MPartStack, OverlaySearchSettings> STACK_SETTINGS = new WeakHashMap<>();

private static final WeakHashMap<IWorkbenchPart, FindReplaceOverlay> PART_TO_OVERLAY = new WeakHashMap<>();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Diese Felder würde ich nicht im Overlay selbst halten, sondern in eine Registry-Klasse auslagern

settings.setReplaceBarOpen(false);

Accessor actionAccessor= new Accessor(getFindReplaceAction(), FindReplaceAction.class);
FindReplaceOverlay overlay= (FindReplaceOverlay) actionAccessor.get("overlay");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Wenn du noch eine createForPart()-Methode ergänzen würdest, bei der die initialen Settings übergeben werden können, könnten die Reflection-Calls hier vermutlich vermieden werden

shellToUse = fShell;
}
overlay = new FindReplaceOverlay(shellToUse, fWorkbenchPart, fTarget);
overlay = FindReplaceOverlay.getOrCreateForPart(shellToUse, fWorkbenchPart, fTarget);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Jetzt habe ich schon ziemlich viele Einzelkommentare gemacht, aber meiner Meinung nach könnte der Codefluss ab hier schon anders sein.

Design-Idee:
Die Action frägt bei einer FindReplaceOverlayRegistry an, ob zu einem bestimmten PartStack schon ein Overlay offen ist oder mal offen war. Wenn ja, wird die existierende Instanz zurückgegeben oder ein neue Instanz mit den gespeicherten Settings gebaut.

Wird eine solche Instanz jetzt aktiv vom Nutzer geschlossen (oder andersweitig disposed, z.B. durch Schließen des Editor-Tabs), werden die derzeitigen Settings für den PartStack gespeichert (falls dieser dann noch exisitiert).

Aus meiner Sicht gibts keinen Grund, dann noch bei jeder einzelnen Modifikation immer wieder die Settings zu speichern oder übersehe ich da etwas?

@fedejeanne fedejeanne 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.

Ich habe nicht getestet, nur den Code überflogen. Ich glaube mit den Kommentaren von Robin gibt es einiges zu tun bis zum nächsten Review.

Eine Sache nur: wir sollten uns abstimmen, ob wir in vi-eclipse auf Deutsch oder auf Englisch reviewen. Diese Orga ist public ;-)

.withToolTipText(FindReplaceMessages.FindReplaceOverlay_upSearchButton_toolTip)
.withOperation(() -> performSearch(false))
.withShortcuts(KeyboardShortcuts.SEARCH_BACKWARD).build();
.withOperation(() -> performSearch(false)).withShortcuts(KeyboardShortcuts.SEARCH_BACKWARD).build();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Auto-formattierung aktiv? Wenn möglich, bitte solche unnötige Änderungen vermeiden.

Comment on lines +1332 to +1359
@Override
public void partDeactivated(IWorkbenchPartReference partRef) {
/* unused */ }

@Override
public void partOpened(IWorkbenchPartReference partRef) {
/* unused */ }

@Override
public void partClosed(IWorkbenchPartReference partRef) {
/* unused */ }

@Override
public void partHidden(IWorkbenchPartReference partRef) {
/* unused */ }

@Override
public void partVisible(IWorkbenchPartReference partRef) {
/* unused */ }

@Override
public void partBroughtToTop(IWorkbenchPartReference partRef) {
/* unused */ }

@Override
public void partInputChanged(IWorkbenchPartReference partRef) {
/* unused */ }
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Man kann einfach die leere Methoden löschen, IPartListener2 hat bereits leere default-Methoden

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.

Find/Replace overlay: remember parameters from earlier

3 participants