IDEA-305434 Refactoring: Extract inlay Run-To-Cursor logic from XDebuggerManagerImpl

GitOrigin-RevId: d7fa5a223a73f0b9c8f69f47ea142945f466d158
This commit is contained in:
Alexey Merkulov
2023-09-01 19:16:51 +00:00
committed by intellij-monorepo-bot
parent ff203c76f7
commit b312ae476f
2 changed files with 170 additions and 179 deletions
@@ -0,0 +1,161 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.xdebugger.impl
import com.intellij.codeInsight.daemon.impl.IntentionsUIImpl
import com.intellij.codeInsight.hint.HintManager
import com.intellij.codeInsight.hint.HintManagerImpl
import com.intellij.codeInsight.hint.PriorityQuestionAction
import com.intellij.codeInsight.hint.QuestionAction
import com.intellij.openapi.actionSystem.*
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl
import com.intellij.openapi.actionSystem.impl.ToolbarUtils.createImmediatelyUpdatedToolbar
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorKind
import com.intellij.openapi.editor.LogicalPosition
import com.intellij.openapi.editor.event.EditorMouseEvent
import com.intellij.openapi.editor.event.EditorMouseListener
import com.intellij.openapi.editor.event.EditorMouseMotionListener
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.registry.Registry
import com.intellij.ui.HintHint
import com.intellij.ui.LightweightHint
import com.intellij.ui.components.panels.NonOpaquePanel
import com.intellij.util.ui.JBDimension
import com.intellij.util.ui.JBUI
import com.intellij.xdebugger.XDebuggerManager
import com.intellij.xdebugger.impl.actions.XDebuggerActions
import java.awt.Point
import java.lang.ref.WeakReference
import javax.swing.JComponent
import javax.swing.JPanel
import javax.swing.SwingUtilities
internal class InlayRunToCursorEditorListener(private val project: Project) : EditorMouseMotionListener, EditorMouseListener {
private var currentHint = WeakReference<RunToCursorHint?>(null)
private var currentEditor = WeakReference<Editor?>(null)
private var currentLineNumber = -1
override fun mouseMoved(e: EditorMouseEvent) {
if (!Registry.`is`("debugger.inlayRunToCursor")) {
IntentionsUIImpl.DISABLE_INTENTION_BULB[project] = false
return
}
val wasShown = showInlayRunToCursor(e)
if (!wasShown) {
hideHint()
}
}
private fun reset(hint: RunToCursorHint) {
if (hint != currentHint.get()) {
return
}
currentHint.clear()
currentEditor = WeakReference(null)
currentLineNumber = -1
}
private fun hideHint() {
val hint = currentHint.get()
hint?.hide()
}
private fun showInlayRunToCursor(e: EditorMouseEvent): Boolean {
val editor = e.editor
if (editor.getEditorKind() != EditorKind.MAIN_EDITOR) {
return false
}
if (editor.getScrollingModel().getHorizontalScrollOffset() != 0) {
return false
}
val session = XDebuggerManager.getInstance(project).getCurrentSession() as XDebugSessionImpl?
if (session == null || !session.isPaused || session.isReadOnly) {
IntentionsUIImpl.DISABLE_INTENTION_BULB[project] = false
return false
}
IntentionsUIImpl.DISABLE_INTENTION_BULB[project] = true
val lineNumber = XDebuggerManagerImpl.getLineNumber(e)
if (lineNumber < 0) {
return false
}
if (currentEditor.get() === editor && currentLineNumber == lineNumber) {
return true
}
currentEditor = WeakReference(editor)
currentLineNumber = lineNumber
var firstNonSpaceSymbol = editor.getDocument().getLineStartOffset(lineNumber)
val charsSequence = editor.getDocument().charsSequence
while (true) {
if (firstNonSpaceSymbol >= charsSequence.length) {
return false //end of file
}
val c = charsSequence[firstNonSpaceSymbol]
if (c == '\n') {
return false // empty line
}
if (!Character.isWhitespace(c)) {
break
}
firstNonSpaceSymbol++
}
val firstNonSpacePos = editor.offsetToXY(firstNonSpaceSymbol)
if (firstNonSpacePos.x < JBUI.scale(16)) {
return false
}
val lineY = editor.logicalPositionToXY(LogicalPosition(lineNumber, 0)).y
val position = SwingUtilities.convertPoint(editor.getContentComponent(), Point(-JBUI.scale(6), lineY),
editor.getComponent().rootPane.layeredPane)
val group = DefaultActionGroup()
val pausePosition = session.currentPosition
if (pausePosition != null && pausePosition.getFile() == editor.virtualFile && pausePosition.getLine() == lineNumber) {
group.add(ActionManager.getInstance().getAction(XDebuggerActions.RESUME))
}
else {
group.add(ActionManager.getInstance().getAction(IdeActions.ACTION_RUN_TO_CURSOR))
}
val caretLine = editor.getCaretModel().logicalPosition.line
if (editor.getSettings().isShowIntentionBulb() && caretLine == lineNumber && firstNonSpacePos.x >= JBUI.scale(4 + 22 * 2)) {
group.add(ActionManager.getInstance().getAction(IdeActions.ACTION_SHOW_INTENTION_ACTIONS))
}
val toolbarImpl = createImmediatelyUpdatedToolbar(group, ActionPlaces.EDITOR_HINT, editor.getComponent(), true) {} as ActionToolbarImpl
toolbarImpl.setLayoutPolicy(ActionToolbar.NOWRAP_LAYOUT_POLICY)
val sideButtonOffset = 2
toolbarImpl.setActionButtonBorder(JBUI.Borders.empty(0, sideButtonOffset))
toolbarImpl.setBorder(null)
toolbarImpl.setOpaque(false)
toolbarImpl.setAdditionalDataProvider { dataId: String? ->
if (XDebuggerUtilImpl.LINE_NUMBER.`is`(dataId)) {
return@setAdditionalDataProvider lineNumber
}
null
}
val justPanel: JPanel = NonOpaquePanel()
justPanel.preferredSize = JBDimension((2 * sideButtonOffset + 22) * group.childrenCount, 22)
justPanel.add(toolbarImpl.component)
val hint = RunToCursorHint(justPanel, this)
currentHint = WeakReference(hint)
val questionAction: QuestionAction = object : PriorityQuestionAction {
override fun execute(): Boolean {
return true
}
override fun getPriority(): Int {
return PriorityQuestionAction.INTENTION_BULB_PRIORITY
}
}
val offset = editor.getCaretModel().offset
HintManagerImpl.getInstanceImpl().showQuestionHint(editor, position, offset, offset, hint, questionAction, HintManager.RIGHT)
return true
}
private class RunToCursorHint(component: JComponent, private val listener: InlayRunToCursorEditorListener) : LightweightHint(component) {
override fun show(parentComponent: JComponent, x: Int, y: Int, focusBackComponent: JComponent, hintHint: HintHint) {
super.show(parentComponent, x, y, focusBackComponent, HintHint(parentComponent, Point(x, y)))
}
override fun hide(ok: Boolean) {
listener.reset(this)
super.hide(ok)
}
}
}
@@ -2,8 +2,9 @@
package com.intellij.xdebugger.impl;
import com.intellij.AppTopics;
import com.intellij.codeInsight.daemon.impl.IntentionsUIImpl;
import com.intellij.codeInsight.hint.*;
import com.intellij.codeInsight.hint.LineTooltipRenderer;
import com.intellij.codeInsight.hint.TooltipController;
import com.intellij.codeInsight.hint.TooltipGroup;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.executors.DefaultDebugExecutor;
@@ -23,8 +24,6 @@ import com.intellij.notification.NotificationGroupManager;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionUtil;
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl;
import com.intellij.openapi.actionSystem.impl.ToolbarUtils;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
@@ -46,15 +45,14 @@ import com.intellij.openapi.wm.IdeGlassPaneUtil;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.ui.ExperimentalUI;
import com.intellij.ui.HintHint;
import com.intellij.ui.LightweightHint;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.util.DocumentUtil;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.ui.JBDimension;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import com.intellij.xdebugger.*;
import com.intellij.xdebugger.XDebugProcess;
import com.intellij.xdebugger.XDebugProcessStarter;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerManager;
import com.intellij.xdebugger.breakpoints.XBreakpoint;
import com.intellij.xdebugger.breakpoints.XBreakpointListener;
import com.intellij.xdebugger.impl.actions.XDebuggerActions;
@@ -75,7 +73,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
@@ -182,7 +179,7 @@ public final class XDebuggerManagerImpl extends XDebuggerManager implements Pers
eventMulticaster.addEditorMouseListener(listener, this);
eventMulticaster.addEditorMouseMotionListener(bpPromoter, this);
if (ExperimentalUI.isNewUI()) {
InlayRunToCursorEditorListener newRunToCursorListener = new InlayRunToCursorEditorListener();
InlayRunToCursorEditorListener newRunToCursorListener = new InlayRunToCursorEditorListener(myProject);
eventMulticaster.addEditorMouseMotionListener(newRunToCursorListener, this);
eventMulticaster.addEditorMouseListener(newRunToCursorListener, this);
}
@@ -481,173 +478,6 @@ public final class XDebuggerManagerImpl extends XDebuggerManager implements Pers
}
}
private static final class RunToCursorHint extends LightweightHint {
private final InlayRunToCursorEditorListener listener;
RunToCursorHint(@NotNull JComponent component, InlayRunToCursorEditorListener listener) {
super(component);
this.listener = listener;
}
@Override
public void show(@NotNull JComponent parentComponent, int x, int y, JComponent focusBackComponent, @NotNull HintHint hintHint) {
super.show(parentComponent, x, y, focusBackComponent, new HintHint(parentComponent, new Point(x, y)));
}
@Override
public void hide(boolean ok) {
listener.reset(this);
super.hide(ok);
}
}
private final class InlayRunToCursorEditorListener implements EditorMouseMotionListener, EditorMouseListener {
private WeakReference<RunToCursorHint> myCurrentHint = new WeakReference<>(null);
private WeakReference<Editor> myEditor = new WeakReference<>(null);
private int myLineNumber = -1;
@Override
public void mouseMoved(@NotNull EditorMouseEvent e) {
if (!Registry.is("debugger.inlayRunToCursor")) {
IntentionsUIImpl.DISABLE_INTENTION_BULB.set(myProject, false);
return;
}
boolean wasShown = showInlayRunToCursor(e);
if (!wasShown) {
hideHint();
}
}
void reset(@NotNull RunToCursorHint hint) {
if (hint != myCurrentHint.get()) {
return;
}
myCurrentHint.clear();
myEditor = new WeakReference<>(null);
myLineNumber = -1;
}
private void hideHint() {
RunToCursorHint hint = myCurrentHint.get();
if (hint != null) {
hint.hide();
}
}
private boolean showInlayRunToCursor(@NotNull EditorMouseEvent e) {
Editor editor = e.getEditor();
if (editor.getEditorKind() != EditorKind.MAIN_EDITOR) {
return false;
}
if (editor.getScrollingModel().getHorizontalScrollOffset() != 0) {
return false;
}
XDebugSessionImpl session = getCurrentSession();
if (session == null || !session.isPaused() || session.isReadOnly()) {
IntentionsUIImpl.DISABLE_INTENTION_BULB.set(myProject, false);
return false;
}
IntentionsUIImpl.DISABLE_INTENTION_BULB.set(myProject, true);
int lineNumber = getLineNumber(e);
if (lineNumber < 0) {
return false;
}
if (myEditor.get() == editor && myLineNumber == lineNumber) {
return true;
}
myEditor = new WeakReference<>(editor);
myLineNumber = lineNumber;
int firstNonSpaceSymbol = editor.getDocument().getLineStartOffset(lineNumber);
CharSequence charsSequence = editor.getDocument().getCharsSequence();
while (true) {
if (firstNonSpaceSymbol >= charsSequence.length()) {
return false; //end of file
}
char c = charsSequence.charAt(firstNonSpaceSymbol);
if (c == '\n') {
return false; // empty line
}
if (!Character.isWhitespace(c)) {
break;
}
firstNonSpaceSymbol++;
}
Point firstNonSpacePos = editor.offsetToXY(firstNonSpaceSymbol);
if (firstNonSpacePos.x < JBUI.scale(16)) {
return false;
}
int lineY = editor.logicalPositionToXY(new LogicalPosition(lineNumber, 0)).y;
Point position = SwingUtilities.convertPoint(editor.getContentComponent(), new Point(-JBUI.scale(6), lineY),
editor.getComponent().getRootPane().getLayeredPane());
DefaultActionGroup group = new DefaultActionGroup();
XSourcePosition pausePosition = session.getCurrentPosition();
if (pausePosition != null && pausePosition.getFile().equals(editor.getVirtualFile()) && pausePosition.getLine() == lineNumber) {
group.add(ActionManager.getInstance().getAction(XDebuggerActions.RESUME));
}
else {
group.add(ActionManager.getInstance().getAction(IdeActions.ACTION_RUN_TO_CURSOR));
}
int caretLine = editor.getCaretModel().getLogicalPosition().line;
if (editor.getSettings().isShowIntentionBulb() && caretLine == lineNumber && firstNonSpacePos.x >= JBUI.scale(4 + 22*2)) {
group.add(ActionManager.getInstance().getAction(IdeActions.ACTION_SHOW_INTENTION_ACTIONS));
}
ActionToolbarImpl toolbarImpl = (ActionToolbarImpl)ToolbarUtils.INSTANCE.createImmediatelyUpdatedToolbar(group, ActionPlaces.EDITOR_HINT, editor.getComponent(), true, (t) -> Unit.INSTANCE);
toolbarImpl.setLayoutPolicy(ActionToolbar.NOWRAP_LAYOUT_POLICY);
int sideButtonOffset = 2;
toolbarImpl.setActionButtonBorder(JBUI.Borders.empty(0, sideButtonOffset));
toolbarImpl.setBorder(null);
toolbarImpl.setOpaque(false);
toolbarImpl.setAdditionalDataProvider((dataId) -> {
if (XDebuggerUtilImpl.LINE_NUMBER.is(dataId)) {
return lineNumber;
}
return null;
});
JPanel justPanel = new NonOpaquePanel();
justPanel.setPreferredSize(new JBDimension((2 * sideButtonOffset + 22) * group.getChildrenCount(),22));
justPanel.add(toolbarImpl.getComponent());
RunToCursorHint hint = new RunToCursorHint(justPanel, this);
myCurrentHint = new WeakReference<>(hint);
QuestionAction questionAction = new PriorityQuestionAction() {
@Override
public boolean execute() {
return true;
}
@Override
public int getPriority() {
return INTENTION_BULB_PRIORITY;
}
};
int offset = editor.getCaretModel().getOffset();
HintManagerImpl.getInstanceImpl().showQuestionHint(editor, position, offset, offset, hint, questionAction, HintManager.RIGHT);
return true;
}
}
private final class GutterUiRunToCursorEditorListener implements EditorMouseMotionListener, EditorMouseListener {
RangeHighlighter myCurrentHighlighter;
@@ -730,7 +560,7 @@ public final class XDebuggerManagerImpl extends XDebuggerManager implements Pers
}
}
private static int getLineNumber(@NotNull EditorMouseEvent event) {
static int getLineNumber(@NotNull EditorMouseEvent event) {
Editor editor = event.getEditor();
if (event.getVisualPosition().line >= ((EditorImpl)editor).getVisibleLineCount()) {
return -1;