/* * Copyright 2003-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.idea.java.debugger.breakpoints; import com.intellij.debugger.DebuggerManagerEx; import com.intellij.debugger.ui.breakpoints.Breakpoint; import com.intellij.debugger.ui.breakpoints.BreakpointWithHighlighter; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiField; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiMethod; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.xdebugger.XDebuggerManager; import com.intellij.xdebugger.XDebuggerUtil; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.breakpoints.XBreakpointListener; import jetbrains.mps.debugger.core.breakpoints.BreakpointsUiComponentEx; import jetbrains.mps.ide.editor.util.EditorComponentUtil; import jetbrains.mps.ide.project.ProjectHelper; import jetbrains.mps.idea.java.trace.GeneratedSourcePosition; import jetbrains.mps.nodeEditor.AdditionalPainter; import jetbrains.mps.nodeEditor.EditorComponent; import jetbrains.mps.openapi.editor.cells.EditorCell; import jetbrains.mps.smodel.ModelAccessHelper; import jetbrains.mps.util.Computable; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.mps.openapi.model.SNode; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; public class IdeaBreakpointsUiComponent extends BreakpointsUiComponentEx<Breakpoint, BreakpointWithHighlighter> implements ProjectComponent { private static final Logger LOG = LogManager.getLogger(IdeaBreakpointsUiComponent.class); private DebuggerManagerEx myDebuggerManager; private final XBreakpointListener myBreakpointListener = new MyBreakpointListener(); public IdeaBreakpointsUiComponent(Project project, FileEditorManager manager) { super(project, manager); } @Override public void projectOpened() { } @Override public void projectClosed() { } @Override public void initComponent() { super.init(); myDebuggerManager = DebuggerManagerEx.getInstanceEx(myProject); XDebuggerManager.getInstance(myProject).getBreakpointManager().addBreakpointListener(myBreakpointListener); } @Override public void disposeComponent() { super.dispose(); XDebuggerManager.getInstance(myProject).getBreakpointManager().removeBreakpointListener(myBreakpointListener); myDebuggerManager = null; } @NotNull @Override public String getComponentName() { return "Idea Breakpoints In MPS Component"; } @Override protected Set<BreakpointWithHighlighter> getBreakpointsForComponent(@NotNull final EditorComponent component) { final Set<BreakpointWithHighlighter> result = new HashSet<BreakpointWithHighlighter>(); final List<Breakpoint> breakpoints = myDebuggerManager.getBreakpointManager().getBreakpoints(); //XDebuggerManager.getInstance(myProject).getBreakpointManager() ProjectHelper.getModelAccess(myProject).runReadAction(new Runnable() { @Override public void run() { for (Breakpoint breakpoint : breakpoints) { if (breakpoint instanceof BreakpointWithHighlighter) { BreakpointWithHighlighter breakpointWithHighlighter = (BreakpointWithHighlighter) breakpoint; SNode node = BreakpointPainter.getNodeForBreakpoint(breakpointWithHighlighter); if (node != null && EditorComponentUtil.isNodeShownInTheComponent(component, node)) { result.add(breakpointWithHighlighter); } } } } }); return result; } @Override @NotNull protected List<EditorComponent> getComponentsForBreakpoint(@NotNull final BreakpointWithHighlighter breakpoint) { return new ModelAccessHelper(ProjectHelper.getModelAccess(myProject)).runReadAction(new Computable<List<EditorComponent>>() { public List<EditorComponent> compute() { SNode node = BreakpointPainter.getNodeForBreakpoint(breakpoint); if (node != null) { return EditorComponentUtil.findComponentForNode(node, myFileEditorManager); } return Collections.emptyList(); } }); } @Override protected BreakpointPainter createPainter(BreakpointWithHighlighter breakpoint) { return new BreakpointPainter(breakpoint); } @Override protected BreakpointIconRenderrer createRenderrer(BreakpointWithHighlighter breakpoint, EditorComponent component) { return new BreakpointIconRenderrer(breakpoint, component); } @Override protected void toggleBreakpoint(final SNode node) { boolean breakpointWasSet = false; GeneratedSourcePosition sourcePosition = GeneratedSourcePosition.fromNode(node); if (sourcePosition != null) { PsiFile psiFile = sourcePosition.getPsiFile(myProject); if (psiFile != null) { Document document = PsiDocumentManager.getInstance(myProject).getDocument(psiFile); if (document != null) { if (!hasMethodOrFieldsAtLine(document, psiFile, sourcePosition.getLineNumber())) { breakpointWasSet = toggleAtLine(psiFile, sourcePosition.getLineNumber()); } } } } if (!breakpointWasSet) { LOG.debug("Failed to create a breakpoint at this location"); } } private boolean toggleAtLine(PsiFile psiFile, int lineNumber) { XDebuggerUtil debuggerUtil = XDebuggerUtil.getInstance(); if (debuggerUtil.canPutBreakpointAt(myProject, psiFile.getVirtualFile(), lineNumber)) { debuggerUtil.toggleLineBreakpoint(myProject, psiFile.getVirtualFile(), lineNumber); return true; } return false; } private boolean hasMethodOrFieldsAtLine(@NotNull Document document, @NotNull PsiFile file, int lineNumber) { int lineStartOffset = document.getLineStartOffset(lineNumber); int lineEndOffset = document.getLineEndOffset(lineNumber); Class<? extends PsiElement>[] elementsLookingFor = new Class[]{PsiMethod.class, PsiField.class}; for (int i = lineStartOffset; i < lineEndOffset; ++i) { for (Class<? extends PsiElement> elementClass : elementsLookingFor) { PsiElement psiElement = PsiTreeUtil.findElementOfClassAtOffset(file, i, elementClass, true); if (psiElement != null && document.getLineNumber(psiElement.getTextOffset()) == lineNumber) { return true; } } } return false; } @Override protected EditorCell findDebuggableOrTraceableCell(EditorCell cell) { return findTraceableCell(cell); } private void clearAllEditors() { List<EditorComponent> allEditorComponents = EditorComponentUtil.getAllEditorComponents(myFileEditorManager, true); for (EditorComponent component : allEditorComponents) { component.getLeftEditorHighlighter().removeAllIconRenderers(BreakpointIconRenderrer.TYPE); List<AdditionalPainter> additionalPainters = component.getAdditionalPainters(); for (AdditionalPainter painter : additionalPainters) { if (painter instanceof BreakpointPainter) { component.removeAdditionalPainter(painter); } } } } private class MyBreakpointListener implements XBreakpointListener { @Override public void breakpointAdded(@NotNull XBreakpoint breakpoint) { breakpointChanged(breakpoint); } @Override public void breakpointRemoved(@NotNull XBreakpoint breakpoint) { breakpointChanged(breakpoint); } @Override public void breakpointChanged(@NotNull XBreakpoint breakpointNoUse) { clearAllEditors(); // XXX why do we remove/re-add all BPs here instead of managing them individually like BreakpointsUiComponent does? final List<Breakpoint> breakpoints = myDebuggerManager.getBreakpointManager().getBreakpoints(); for (Breakpoint breakpoint : breakpoints) { if (breakpoint instanceof BreakpointWithHighlighter) { BreakpointWithHighlighter breakpointWithHighlighter = (BreakpointWithHighlighter) breakpoint; addLocationBreakpoint(breakpointWithHighlighter); } } repaintBreakpoints(); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { List<EditorComponent> allEditorComponents = EditorComponentUtil.getAllEditorComponents(myFileEditorManager, true); for (EditorComponent component : allEditorComponents) { component.repaint(); } } }); } } }