package jetbrains.mps.debugger.core.breakpoints; /*Generated by MPS */ import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; import jetbrains.mps.nodeEditor.LeftMarginMouseListener; import com.intellij.util.messages.MessageBusConnection; import jetbrains.mps.util.containers.MultiMap; import jetbrains.mps.nodeEditor.highlighter.EditorComponentCreateListener; import jetbrains.mps.RuntimeFlags; import jetbrains.mps.nodeEditor.EditorComponent; import jetbrains.mps.ide.editor.util.EditorComponentUtil; import org.jetbrains.annotations.NotNull; import java.util.Set; import java.util.List; import org.jetbrains.mps.openapi.model.SNode; import jetbrains.mps.openapi.editor.cells.EditorCell; import jetbrains.mps.textgen.trace.TraceInfo; import org.jetbrains.mps.util.Condition; import org.jetbrains.annotations.Nullable; import jetbrains.mps.internal.collections.runtime.SetSequence; import com.intellij.openapi.application.ApplicationManager; import jetbrains.mps.internal.collections.runtime.ListSequence; import java.awt.event.MouseEvent; import jetbrains.mps.smodel.ModelAccess; /** * * * @param <B> breakpoint type * @param <L> location breakpoint type */ public abstract class BreakpointsUiComponentEx<B, L extends B> { protected final FileEditorManager myFileEditorManager; protected final Project myProject; private final LeftMarginMouseListener myMouseListener = new BreakpointsUiComponentEx.MyLeftMarginMouseListener(); private final BreakpointsUiComponentEx.MyEditorComponentCreateListener myEditorComponentCreationHandler = new BreakpointsUiComponentEx.MyEditorComponentCreateListener(); private MessageBusConnection myMessageBusConnection; private final MultiMap<L, BreakpointIconRenderrerEx<L>> myBreakpointRender = new MultiMap<L, BreakpointIconRenderrerEx<L>>(); public BreakpointsUiComponentEx(Project project, FileEditorManager manager) { myProject = project; myFileEditorManager = manager; } public void init() { myMessageBusConnection = myProject.getMessageBus().connect(); myMessageBusConnection.subscribe(EditorComponentCreateListener.EDITOR_COMPONENT_CREATION, myEditorComponentCreationHandler); if (RuntimeFlags.isTestMode()) { return; } for (EditorComponent editor : EditorComponentUtil.getAllEditorComponents(myFileEditorManager, true)) { editorComponentCreated(editor); } } public void dispose() { myMessageBusConnection.disconnect(); myBreakpointRender.clear(); } @NotNull protected abstract Set<L> getBreakpointsForComponent(@NotNull EditorComponent component); @NotNull protected abstract List<EditorComponent> getComponentsForBreakpoint(@NotNull L breakpoint); protected abstract BreakpointPainterEx<L> createPainter(L breakpoint); protected abstract BreakpointIconRenderrerEx<L> createRenderrer(L breakpoint, EditorComponent component); protected abstract void toggleBreakpoint(SNode node); protected abstract EditorCell findDebuggableOrTraceableCell(EditorCell cell); public void toggleBreakpoint(EditorCell cell) { EditorCell debuggableCell = findDebuggableOrTraceableCell(cell); if (debuggableCell != null) { toggleBreakpoint(debuggableCell.getSNode()); } } public boolean isDebuggable(EditorCell cell) { return findDebuggableOrTraceableCell(cell) != null; } protected EditorCell findTraceableCell(EditorCell foundCell) { EditorCell cell = foundCell; while (cell != null) { SNode node = cell.getSNode(); if (TraceInfo.hasTrace(node)) { break; } cell = cell.getParent(); } return cell; } private SNode findDebuggableNode(final EditorComponent editorComponent, int x, final int y) { EditorCell foundCell = editorComponent.getRootCell().findNearestLeafOnLine(x, y, new Condition<EditorCell>() { @Override public boolean met(EditorCell object) { EditorCell debuggableOrTraceableCell = findDebuggableOrTraceableCell(object); if (debuggableOrTraceableCell == null) { return false; } // todo do we need to know about ui? EditorCell iconAnchorCell = BreakpointIconRenderrerEx.getBreakpointIconAnchorCell(editorComponent.findNodeCell(debuggableOrTraceableCell.getSNode())); // ignoring mouse clicks to any other rows except one containing "BreakpointIconAnchorCell" // (this cell will be marked with breakpoint icon in LeftEditorHighlighter) return iconAnchorCell != null && (y >= iconAnchorCell.getY() && iconAnchorCell.getBaseline() >= y); } }); if (foundCell == null) { return null; } EditorCell cell = findDebuggableOrTraceableCell(foundCell); if (cell == null) { return null; } return cell.getSNode(); } protected void editorComponentCreated(@Nullable EditorComponent editorComponent) { if (editorComponent == null) { return; } if (!(editorComponent.getLeftMarginPressListeners().contains(myMouseListener))) { editorComponent.addLeftMarginPressListener(myMouseListener); } Set<L> breakpointsForRoot = getBreakpointsForComponent(editorComponent); for (L breakpoint : SetSequence.fromSet(breakpointsForRoot)) { editorComponent.addAdditionalPainter(createPainter(breakpoint)); BreakpointIconRenderrerEx<L> r = createRenderrer(breakpoint, editorComponent); myBreakpointRender.putValue(breakpoint, r); editorComponent.getLeftEditorHighlighter().addIconRenderer(r); } editorComponent.repaintExternalComponent(); } protected void editorComponentDisposed(@Nullable EditorComponent editorComponent) { if (editorComponent == null) { return; } editorComponent.removeLeftMarginPressListener(myMouseListener); Set<L> breakpointsForRoot = getBreakpointsForComponent(editorComponent); for (L breakpoint : SetSequence.fromSet(breakpointsForRoot)) { editorComponent.removeAdditionalPainterByItem(breakpoint); editorComponent.getLeftEditorHighlighter().removeAllIconRenderers(myBreakpointRender.get(breakpoint)); } // stale renderers may persist in myBreakpointRenderer, I have no means to identify EditorComponents to use them as additional key along with breakpoint itself // I don't care as these stale renders are only for equals() match in removeAllIconsRenderers(). } protected void addLocationBreakpoint(L breakpoint) { for (EditorComponent editorComponent : getComponentsForBreakpoint(breakpoint)) { editorComponent.addAdditionalPainter(createPainter(breakpoint)); BreakpointIconRenderrerEx<L> r = createRenderrer(breakpoint, editorComponent); myBreakpointRender.putValue(breakpoint, r); editorComponent.getLeftEditorHighlighter().addIconRenderer(r); editorComponent.repaintExternalComponent(); } } protected void removeLocationBreakpoint(L breakpoint) { for (EditorComponent editorComponent : getComponentsForBreakpoint(breakpoint)) { editorComponent.removeAdditionalPainterByItem(breakpoint); editorComponent.getLeftEditorHighlighter().removeAllIconRenderers(myBreakpointRender.get(breakpoint)); editorComponent.repaintExternalComponent(); } myBreakpointRender.remove(breakpoint); } public void repaintBreakpoints() { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { List<EditorComponent> allEditorComponents = EditorComponentUtil.getAllEditorComponents(myFileEditorManager, true); for (EditorComponent component : ListSequence.fromList(allEditorComponents)) { component.repaintExternalComponent(); } } }); } private class MyLeftMarginMouseListener implements LeftMarginMouseListener { private MyLeftMarginMouseListener() { } @Override public void mousePressed(MouseEvent e, EditorComponent editorComponent) { } @Override public void mouseReleased(MouseEvent e, EditorComponent editorComponent) { } @Override public void mouseClicked(final MouseEvent e, final EditorComponent editorComponent) { if (e.getButton() == MouseEvent.BUTTON1) { ModelAccess.instance().runReadAction(new Runnable() { @Override public void run() { SNode node = findDebuggableNode(editorComponent, e.getX(), e.getY()); if (node != null) { toggleBreakpoint(node); } } }); } } } private class MyEditorComponentCreateListener implements EditorComponentCreateListener { private MyEditorComponentCreateListener() { } @Override public void editorComponentCreated(@NotNull EditorComponent editorComponent) { BreakpointsUiComponentEx.this.editorComponentCreated(editorComponent); } @Override public void editorComponentDisposed(@NotNull EditorComponent editorComponent) { BreakpointsUiComponentEx.this.editorComponentDisposed(editorComponent); } } }