/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package org.python.pydev.debug.ui.actions; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IBreakpointManager; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.jface.action.Action; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.IVerticalRulerInfo; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IPathEditorInput; import org.eclipse.ui.IURIEditorInput; import org.eclipse.ui.texteditor.AbstractMarkerAnnotationModel; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.ui.texteditor.IUpdate; import org.eclipse.ui.texteditor.MarkerUtilities; import org.python.pydev.core.log.Log; import org.python.pydev.debug.core.PydevDebugPlugin; import org.python.pydev.debug.model.PyBreakpoint; import org.python.pydev.editor.PyEdit; import org.python.pydev.editorinput.PydevFileEditorInput; import com.aptana.shared_core.io.FileUtils; import com.aptana.shared_core.structure.Tuple; /** * Some things similar to: org.eclipse.ui.texteditor.MarkerRulerAction */ public abstract class AbstractBreakpointRulerAction extends Action implements IUpdate { protected IVerticalRulerInfo fInfo; protected ITextEditor fTextEditor; private IBreakpoint fBreakpoint; protected IBreakpoint getBreakpoint() { return fBreakpoint; } protected void setBreakpoint(IBreakpoint breakpoint) { fBreakpoint = breakpoint; } protected ITextEditor getTextEditor() { return fTextEditor; } protected void setTextEditor(ITextEditor textEditor) { fTextEditor = textEditor; } protected IVerticalRulerInfo getInfo() { return fInfo; } protected void setInfo(IVerticalRulerInfo info) { fInfo = info; } /** * @return the breakpoint in the line the user clicked last or null if there is no such breakpoint. */ protected IBreakpoint getBreakpointFromLastLineOfActivityInCurrentEditor() { List<IBreakpoint> breakpoints = getBreakpointsFromCurrentFile(true); int size = breakpoints.size(); if (size == 0) { return null; } else if (size == 1) { return breakpoints.get(0); } else if (size > 1) { Log.log("Did not expect more than one breakpoint in the current line. Returning first."); return breakpoints.get(0); } else { Log.log("Unexpected condition!"); return null; } } /** * @return the document of the editor's input */ protected IDocument getDocument() { IDocumentProvider provider = fTextEditor.getDocumentProvider(); return provider.getDocument(fTextEditor.getEditorInput()); } protected IResource getResourceForDebugMarkers() { return getResourceForDebugMarkers(fTextEditor); } public boolean isExternalFileEditor() { return isExternalFileEditor(fTextEditor); } public IEditorInput getExternalFileEditorInput() { return getExternalFileEditorInput(fTextEditor); } // utilities ------------------------------------------------------------------------------------------------------- // utilities ------------------------------------------------------------------------------------------------------- // utilities ------------------------------------------------------------------------------------------------------- // utilities ------------------------------------------------------------------------------------------------------- // utilities ------------------------------------------------------------------------------------------------------- /** * @return whether we're in an external editor or not. */ public static boolean isExternalFileEditor(ITextEditor editor) { IEditorInput externalFileEditorInput = getExternalFileEditorInput(editor); if (externalFileEditorInput != null) { return true; } return false; } /** * @return the IEditorInput if we're dealing with an external file (or null otherwise) */ public static IEditorInput getExternalFileEditorInput(ITextEditor editor) { IEditorInput input = editor.getEditorInput(); //only return not null if it's an external file (IFileEditorInput marks a workspace file, not external file) if (input instanceof IFileEditorInput) { return null; } if (input instanceof IPathEditorInput) { //PydevFileEditorInput would enter here return input; } try { if (input instanceof IURIEditorInput) { return input; } } catch (Throwable e) { //IURIEditorInput not added until eclipse 3.3 } //Note that IStorageEditorInput is not handled for external files (files from zip) return input; } /** * @return true if the given marker is from an external file, and the editor in which this action is being executed * is in this same editor. */ protected static boolean isInSameExternalEditor(IMarker marker, IEditorInput externalFileEditorInput) throws CoreException { if (marker == null || externalFileEditorInput == null) { return false; } String attribute = (String) marker.getAttribute(PyBreakpoint.PY_BREAK_EXTERNAL_PATH_ID); if (attribute != null) { File file = PydevFileEditorInput.getFile(externalFileEditorInput); if (file == null) { return false; } if (attribute.equals(FileUtils.getFileAbsolutePath(file))) { return true; } } return false; } /** * @return the position for a marker. */ public static Position getMarkerPosition(IDocument document, IMarker marker, IAnnotationModel model) { if (model instanceof AbstractMarkerAnnotationModel) { return ((AbstractMarkerAnnotationModel) model).getMarkerPosition(marker); } else { int start = MarkerUtilities.getCharStart(marker); int end = MarkerUtilities.getCharEnd(marker); if (start > end) { end = start + end; start = end - start; end = end - start; } if (start == -1 && end == -1) { // marker line number is 1-based int line = MarkerUtilities.getLineNumber(marker); if (line > 0 && document != null) { try { start = document.getLineOffset(line - 1); end = start; } catch (BadLocationException x) { } } } if (start > -1 && end > -1) { return new Position(start, end - start); } } return null; } /** * @return the resource for which to create the marker or <code>null</code> * * If the editor maps to a workspace file, it will return that file. Otherwise, it will return the * workspace root (so, markers from external files will be created in the workspace root). */ public static IResource getResourceForDebugMarkers(ITextEditor textEditor) { IEditorInput input = textEditor.getEditorInput(); IResource resource = (IResource) input.getAdapter(IFile.class); if (resource == null) { resource = (IResource) input.getAdapter(IResource.class); } if (resource == null) { resource = ResourcesPlugin.getWorkspace().getRoot(); } return resource; } public static List<IMarker> getMarkersFromCurrentFile(PyEdit edit, int line) { return getMarkersFromEditorResource(getResourceForDebugMarkers(edit), edit.getDocument(), getExternalFileEditorInput(edit), line, true, edit.getAnnotationModel()); } protected List<IBreakpoint> getBreakpointsFromCurrentFile(boolean onlyIncludeLastLineActivity) { List<Tuple<IMarker, IBreakpoint>> markersAndBreakpointsFromEditorResource = getMarkersAndBreakpointsFromEditorResource( getResourceForDebugMarkers(), getDocument(), getExternalFileEditorInput(), getInfo() .getLineOfLastMouseButtonActivity(), onlyIncludeLastLineActivity, getAnnotationModel()); int size = markersAndBreakpointsFromEditorResource.size(); ArrayList<IBreakpoint> r = new ArrayList<IBreakpoint>(size); for (int i = 0; i < size; i++) { r.add(markersAndBreakpointsFromEditorResource.get(i).o2); } return r; } private IAnnotationModel getAnnotationModel() { if (fTextEditor == null) { return null; } final IDocumentProvider documentProvider = fTextEditor.getDocumentProvider(); if (documentProvider == null) { return null; } return documentProvider.getAnnotationModel(fTextEditor.getEditorInput()); } /** * @return all the breakpoint markers from the current file or only the ones which match the last line of * activity (i.e.: the one the user clicked). */ protected List<IMarker> getMarkersFromCurrentFile(boolean onlyIncludeLastLineActivity) { return getMarkersFromEditorResource(getResourceForDebugMarkers(), getDocument(), getExternalFileEditorInput(), getInfo().getLineOfLastMouseButtonActivity(), onlyIncludeLastLineActivity, getAnnotationModel()); } public static List<IMarker> getMarkersFromEditorResource(IResource resource, IDocument document, IEditorInput externalFileEditorInput, int lastLineActivity, boolean onlyIncludeLastLineActivity, IAnnotationModel annotationModel) { List<Tuple<IMarker, IBreakpoint>> markersAndBreakpointsFromEditorResource = getMarkersAndBreakpointsFromEditorResource( resource, document, externalFileEditorInput, lastLineActivity, onlyIncludeLastLineActivity, annotationModel); int size = markersAndBreakpointsFromEditorResource.size(); ArrayList<IMarker> r = new ArrayList<IMarker>(size); for (int i = 0; i < size; i++) { r.add(markersAndBreakpointsFromEditorResource.get(i).o1); } return r; } protected static boolean includesRulerLine(Position position, IDocument document, int line) { if (position != null) { try { int markerLine = document.getLineOfOffset(position.getOffset()); if (line == markerLine) { return true; } } catch (BadLocationException x) { } } return false; } /** * @param resource may be the file open in the editor or the workspace root (if it is an external file) * @param document is the document opened in the editor * @param externalFileEditorInput is not-null if this is an external file * @param info is the vertical ruler info (only used if this is not an external file) * @param onlyIncludeLastLineActivity if only the markers that are in the last mouse-click should be included * * @return the markers that correspond to the markers from the current editor. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static List<Tuple<IMarker, IBreakpoint>> getMarkersAndBreakpointsFromEditorResource(IResource resource, IDocument document, IEditorInput externalFileEditorInput, int lastLineActivity, boolean onlyIncludeLastLineActivity, IAnnotationModel annotationModel) { List<Tuple<IMarker, IBreakpoint>> breakpoints = new ArrayList<Tuple<IMarker, IBreakpoint>>(); try { List<IMarker> markers = new ArrayList<IMarker>(); boolean isExternalFile = false; markers.addAll(Arrays.asList(resource.findMarkers(PyBreakpoint.PY_BREAK_MARKER, true, IResource.DEPTH_INFINITE))); markers.addAll(Arrays.asList(resource.findMarkers(PyBreakpoint.PY_CONDITIONAL_BREAK_MARKER, true, IResource.DEPTH_INFINITE))); if (!(resource instanceof IFile)) { //it was created from an external file isExternalFile = true; } IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager(); for (IMarker marker : markers) { if (marker == null) { continue; } IBreakpoint breakpoint = breakpointManager.getBreakpoint(marker); if (breakpoint != null && breakpointManager.isRegistered(breakpoint)) { Position pos = getMarkerPosition(document, marker, annotationModel); if (!isExternalFile) { if (!onlyIncludeLastLineActivity) { breakpoints.add(new Tuple(marker, breakpoint)); } else if (includesRulerLine(pos, document, lastLineActivity)) { breakpoints.add(new Tuple(marker, breakpoint)); } } else { if (isInSameExternalEditor(marker, externalFileEditorInput)) { if (!onlyIncludeLastLineActivity) { breakpoints.add(new Tuple(marker, breakpoint)); } else if (includesRulerLine(pos, document, lastLineActivity)) { breakpoints.add(new Tuple(marker, breakpoint)); } } } } } } catch (CoreException x) { PydevDebugPlugin.log(IStatus.ERROR, "Unexpected getMarkers error (recovered properly)", x); } return breakpoints; } }