tweak editor popup handlers for RemDev

1. Use groups instead of imperative popup handlers
2. Provide editor-local id for editor popup action
3. Encode EditorMouseEvent into that local id
4. Avoid breaking API changes, stick to `ContextMenuPopupHandler`

Fixes
GTW-7427 Git blame actions are missing
GTW-6380 No context menu options in consoles inside LUXed windows

GitOrigin-RevId: d9e109dac3268ab94e6bc64e4e156f8506872739
This commit is contained in:
Gregory.Shrago
2024-02-19 20:59:23 +04:00
committed by intellij-monorepo-bot
parent eefe73501c
commit f6aa793f4d
8 changed files with 171 additions and 65 deletions

View File

@@ -22,6 +22,7 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.remoting.ActionRemoteBehavior
import com.intellij.openapi.actionSystem.remoting.ActionRemoteBehaviorSpecification
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.impl.EditorImpl
@@ -66,7 +67,9 @@ class ShowSettingsWithAddedPattern : AnAction() {
}
}
class ShowParameterHintsSettings : AnAction(), ActionRemoteBehaviorSpecification.Frontend {
class ShowParameterHintsSettings : AnAction(), ActionRemoteBehaviorSpecification {
override fun getBehavior(): ActionRemoteBehavior = ActionRemoteBehavior.BackendOnly
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
@@ -263,7 +266,9 @@ private fun InlayParameterHintsProvider.hasDisabledOptionHintInfo(element: PsiEl
}
class ToggleInlineHintsAction : AnAction(), ActionRemoteBehaviorSpecification.Frontend {
class ToggleInlineHintsAction : AnAction(), ActionRemoteBehaviorSpecification {
override fun getBehavior(): ActionRemoteBehavior = ActionRemoteBehavior.BackendOnly
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT

View File

@@ -6,11 +6,19 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.actionSystem.remoting.ActionRemoteBehavior;
import com.intellij.openapi.actionSystem.remoting.ActionRemoteBehaviorSpecification;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
public final class ToggleCompletionHintsAction extends ToggleAction implements ActionRemoteBehaviorSpecification.Frontend {
public final class ToggleCompletionHintsAction extends ToggleAction implements ActionRemoteBehaviorSpecification {
@NotNull
@Override
public ActionRemoteBehavior getBehavior() {
return ActionRemoteBehavior.BackendOnly;
}
@Override
public @NotNull ActionUpdateThread getActionUpdateThread() {
return ActionUpdateThread.BGT;

View File

@@ -10,6 +10,7 @@ import com.intellij.injected.editor.DocumentWindow;
import com.intellij.injected.editor.EditorWindow;
import com.intellij.injected.editor.MarkupModelWindow;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.colors.EditorColorsManager;
@@ -823,6 +824,11 @@ final class EditorWindowImpl extends UserDataHolderBase implements EditorWindow,
myDelegate.installPopupHandler(popupHandler);
}
@Override
public @Nullable ActionGroup getPopupActionGroup(@NotNull EditorMouseEvent event) {
return myDelegate.getPopupActionGroup(event);
}
@Override
public void setCustomCursor(@NotNull Object requestor, @Nullable Cursor cursor) {
myDelegate.setCustomCursor(requestor, cursor);

View File

@@ -74,7 +74,6 @@ file.not.found=File not found: {0}
guarded.block.modification.attempt.error.message=Unable to perform an action since it changes read-only fragments of the current document
guarded.block.modification.attempt.error.title=Guarded Block Modification Attempt
close.editor.annotations.action.name=Close Annotations
editor.annotations.action.group.name=Annotations
move.cursor.command.name=Move Cursor
paste.command.name=Paste
move.selection.command.name=Move Selection

View File

@@ -20,6 +20,7 @@ import com.intellij.ide.CutProvider;
import com.intellij.ide.DeleteProvider;
import com.intellij.ide.PasteProvider;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.*;
@@ -343,6 +344,10 @@ public interface EditorEx extends Editor {
*/
void uninstallPopupHandler(@NotNull EditorPopupHandler popupHandler);
default @Nullable ActionGroup getPopupActionGroup(@NotNull EditorMouseEvent event) {
return null;
}
/**
* If {@code cursor} parameter value is not {@code null}, sets custom cursor to {@link #getContentComponent() editor's content component},
* otherwise restores default editor cursor management logic ({@code requestor} parameter value should be the same in both setting and

View File

@@ -34,6 +34,8 @@ import com.intellij.lang.Language;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionUtil;
import com.intellij.openapi.actionSystem.remoting.ActionRemoteBehavior;
import com.intellij.openapi.actionSystem.remoting.ActionRemoteBehaviorSpecification;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
@@ -43,6 +45,7 @@ import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorFontType;
import com.intellij.openapi.editor.event.CaretEvent;
import com.intellij.openapi.editor.event.CaretListener;
import com.intellij.openapi.editor.event.EditorMouseEvent;
import com.intellij.openapi.editor.event.EditorMouseEventArea;
import com.intellij.openapi.editor.ex.*;
import com.intellij.openapi.editor.ex.util.EditorUIUtil;
@@ -2458,11 +2461,17 @@ final class EditorGutterComponentImpl extends EditorGutterComponentEx implements
updateSize();
}
private final class CloseAnnotationsAction extends DumbAwareAction {
private final class CloseAnnotationsAction extends DumbAwareAction implements ActionRemoteBehaviorSpecification {
CloseAnnotationsAction() {
super(EditorBundle.messagePointer("close.editor.annotations.action.name"));
}
@NotNull
@Override
public ActionRemoteBehavior getBehavior() {
return ActionRemoteBehavior.BackendOnly;
}
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
closeAllAnnotations();
@@ -2572,74 +2581,68 @@ final class EditorGutterComponentImpl extends EditorGutterComponentEx implements
if (info != null) {
logGutterIconClick(info.renderer);
}
myLastActionableClick = new ClickInfo(logicalLineAtCursor, info == null ? point : info.iconCenterPosition);
final ActionManager actionManager = ActionManager.getInstance();
if (myEditor.getMouseEventArea(e) == EditorMouseEventArea.ANNOTATIONS_AREA) {
final List<AnAction> addActions = getTextAnnotationPopupActions(logicalLineAtCursor);
if (!addActions.isEmpty()) {
e.consume();
DefaultActionGroup actionGroup = DefaultActionGroup.createPopupGroup(
EditorBundle.messagePointer("editor.annotations.action.group.name"));
for (AnAction addAction : addActions) {
actionGroup.add(addAction);
}
JPopupMenu menu = actionManager.createActionPopupMenu(ActionPlaces.EDITOR_ANNOTATIONS_AREA_POPUP, actionGroup).getComponent();
menu.show(this, e.getX(), e.getY());
}
ClickInfo clickInfo = new ClickInfo(logicalLineAtCursor, info == null ? point : info.iconCenterPosition);
EditorMouseEventArea editorArea = myEditor.getMouseEventArea(e);
myLastActionableClick = clickInfo;
AnAction rightButtonAction = info == null ? null : info.renderer.getRightButtonClickAction();
if (rightButtonAction != null) {
e.consume();
performAction(rightButtonAction, e, ActionPlaces.EDITOR_GUTTER_POPUP, myEditor.getDataContext(), info);
return;
}
EditorMouseEvent editorMouseEvent = new EditorMouseEvent(
myEditor, e, editorArea, 0, new LogicalPosition(logicalLineAtCursor, 0),
new VisualPosition(0, 0), true, null, null, null);
ActionGroup group = info != null ? info.renderer.getPopupMenuActions() :
getPopupActionGroup(editorMouseEvent);
if (group == null) {
// nothing
}
else if (!checkDumbAware(group)) {
notifyNotDumbAware();
}
else {
if (info != null) {
AnAction rightButtonAction = info.renderer.getRightButtonClickAction();
if (rightButtonAction != null) {
e.consume();
performAction(rightButtonAction, e, ActionPlaces.EDITOR_GUTTER_POPUP, myEditor.getDataContext(), info);
}
else {
ActionGroup actionGroup = info.renderer.getPopupMenuActions();
if (actionGroup != null) {
e.consume();
if (checkDumbAware(actionGroup)) {
addLoadingIconForGutterMark(info);
actionManager.createActionPopupMenu(ActionPlaces.EDITOR_GUTTER_POPUP, actionGroup)
.getComponent()
.show(this, e.getX(), e.getY());
}
else {
notifyNotDumbAware();
}
}
}
if (info != null) addLoadingIconForGutterMark(info);
String place = editorArea == EditorMouseEventArea.ANNOTATIONS_AREA ?
ActionPlaces.EDITOR_ANNOTATIONS_AREA_POPUP : ActionPlaces.EDITOR_GUTTER_POPUP;
showGutterContextMenu(group, e, place);
}
}
@Nullable ActionGroup getPopupActionGroup(@NotNull EditorMouseEvent event) {
List<AnAction> actions;
if (event.getArea() == EditorMouseEventArea.ANNOTATIONS_AREA) {
actions = getTextAnnotationPopupActions(event.getLogicalPosition().line);
}
else {
actions = new ArrayList<>();
if (ExperimentalUI.isNewUI() &&
event.getArea() == EditorMouseEventArea.LINE_NUMBERS_AREA &&
getClientProperty("line.number.hover.icon.context.menu") instanceof ActionGroup g) {
actions.add(g);
}
else {
List<AnAction> actions = new ArrayList<>();
if (ExperimentalUI.isNewUI() &&
myEditor.getMouseEventArea(e) == EditorMouseEventArea.LINE_NUMBERS_AREA &&
getClientProperty("line.number.hover.icon.context.menu") instanceof ActionGroup g) {
actions.add(g);
}
if (myCustomGutterPopupGroup != null) {
if (myCustomGutterPopupGroup != null) {
actions.add(Separator.getInstance());
actions.add(myCustomGutterPopupGroup);
}
else if (myShowDefaultGutterPopup) {
ActionGroup g = (ActionGroup)CustomActionsSchema.getInstance().getCorrectedAction(IdeActions.GROUP_EDITOR_GUTTER);
if (g != null) {
actions.add(Separator.getInstance());
actions.add(myCustomGutterPopupGroup);
}
else if (myShowDefaultGutterPopup) {
ActionGroup g = (ActionGroup)CustomActionsSchema.getInstance().getCorrectedAction(IdeActions.GROUP_EDITOR_GUTTER);
if (g != null) {
actions.add(Separator.getInstance());
actions.add(g);
}
}
if (!actions.isEmpty()) {
showGutterContextMenu(new DefaultActionGroup(actions), e);
actions.add(g);
}
}
}
if (actions.isEmpty()) return null;
return new EditorMousePopupActionGroup(actions, event);
}
private static final String EDITOR_GUTTER_CONTEXT_MENU_KEY = "editor.gutter.context.menu";
private void showGutterContextMenu(@NotNull ActionGroup group, MouseEvent e) {
private void showGutterContextMenu(@NotNull ActionGroup group, MouseEvent e, String place) {
e.consume();
ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.EDITOR_GUTTER_POPUP, group);
ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(place, group);
putClientProperty(EDITOR_GUTTER_CONTEXT_MENU_KEY, popupMenu);
popupMenu.getComponent().addPopupMenuListener(new PopupMenuListenerAdapter() {
@Override

View File

@@ -2,6 +2,7 @@
package com.intellij.openapi.editor.impl;
import com.intellij.application.options.EditorFontsConstants;
import com.intellij.codeWithMe.ClientId;
import com.intellij.diagnostic.Dumpable;
import com.intellij.ide.*;
import com.intellij.ide.dnd.DnDManager;
@@ -36,7 +37,6 @@ import com.intellij.openapi.editor.ex.util.EmptyEditorHighlighter;
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.editor.highlighter.HighlighterClient;
import com.intellij.openapi.editor.impl.event.MarkupModelListener;
import com.intellij.openapi.editor.impl.stickyLines.StickyLinesPanel;
import com.intellij.openapi.editor.impl.stickyLines.StickyLinesManager;
import com.intellij.openapi.editor.impl.stickyLines.StickyLinesPanel;
import com.intellij.openapi.editor.impl.view.EditorView;
@@ -858,6 +858,22 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
myPopupHandlers.remove(popupHandler);
}
@Override
public @Nullable ActionGroup getPopupActionGroup(@NotNull EditorMouseEvent event) {
if (event.getArea() == EditorMouseEventArea.EDITING_AREA) {
for (int i = myPopupHandlers.size() - 1; i >= 0; i--) {
EditorPopupHandler handler = myPopupHandlers.get(i);
ActionGroup group = handler instanceof ContextMenuPopupHandler o ? o.getActionGroup(event) : null;
if (group instanceof DefaultActionGroup o && o.getChildrenCount() == 0) return group;
if (group != null) return new EditorMousePopupActionGroup(group, event);
}
return null;
}
else {
return myGutterComponent.getPopupActionGroup(event);
}
}
private @Nullable Cursor getCustomCursor() {
return ContainerUtil.getFirstItem(myCustomCursors.values());
}
@@ -5320,9 +5336,27 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
private void invokePopupIfNeeded(@NotNull EditorMouseEvent event) {
if (myPopupHandlers.isEmpty()) return;
if (event.getArea() == EditorMouseEventArea.EDITING_AREA && event.getMouseEvent().isPopupTrigger() && !event.isConsumed()) {
for (int i = myPopupHandlers.size() - 1; i >= 0; i--) {
if (myPopupHandlers.get(i).handlePopup(event)) break;
if (ContainerUtil.all(myPopupHandlers, o -> o instanceof ContextMenuPopupHandler)) {
ActionGroup group = getPopupActionGroup(event);
if (group == null) return;
if (group instanceof DefaultActionGroup o && o.getChildrenCount() == 0) return;
new ContextMenuPopupHandler.Simple(group).handlePopup(event);
}
else {
String message = "Non-ContextMenuPopupHandler popup handler detected: " +
ContainerUtil.map(myPopupHandlers, o -> o.getClass().getName());
if (ClientId.isCurrentlyUnderLocalId()) {
LOG.warn(message);
}
else {
LOG.error(message);
}
for (int i = myPopupHandlers.size() - 1; i >= 0; i--) {
if (myPopupHandlers.get(i).handlePopup(event)) break;
}
}
}
}
@@ -5369,7 +5403,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
private final class DefaultPopupHandler extends ContextMenuPopupHandler {
@Override
public @Nullable ActionGroup getActionGroup(@NotNull EditorMouseEvent event) {
public @Nullable ActionGroup getActionGroup(@NotNull EditorMouseEvent event) { //TODO ! renderer, collapsed-host
String contextMenuGroupId = myState.getContextMenuGroupId();
Inlay<?> inlay = event.getInlay();
if (inlay != null) {

View File

@@ -0,0 +1,46 @@
// Copyright 2000-2024 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.actionSystem.*;
import com.intellij.openapi.editor.event.EditorMouseEvent;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author gregsh
*/
@ApiStatus.Internal
public class EditorMousePopupActionGroup extends DefaultActionGroup {
private final EditorMouseEvent myEvent;
private final boolean myResetChildPopupFlag;
public EditorMousePopupActionGroup(@NotNull List<AnAction> actions, @NotNull EditorMouseEvent event) {
addAll(actions);
myEvent = event;
myResetChildPopupFlag = false;
}
public EditorMousePopupActionGroup(@NotNull ActionGroup group, @NotNull EditorMouseEvent event) {
add(group);
myEvent = event;
myResetChildPopupFlag = true;
}
public @NotNull EditorMouseEvent getEvent() {
return myEvent;
}
@Override
public AnAction @NotNull [] getChildren(@Nullable AnActionEvent e) {
AnAction[] children = super.getChildren(e);
if (e != null && myResetChildPopupFlag) {
Presentation presentation = e.getUpdateSession().presentation(children[0]);
presentation.setPopupGroup(false);
}
return children;
}
}