/**
* Copyright (c) 2005-2013 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;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListResourceBundle;
import java.util.Map;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IBreakpointListener;
import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.MarkerAnnotation;
import org.python.pydev.debug.ui.actions.AbstractBreakpointRulerAction;
import org.python.pydev.editor.PyEdit;
import org.python.pydev.shared_core.callbacks.ICallbackListener;
import org.python.pydev.shared_ui.editor.BaseEditor;
import org.python.pydev.shared_ui.editor.IPyEditListener;
import org.python.pydev.shared_ui.editor.IPyEditListener4;
import org.python.pydev.shared_ui.utils.PyMarkerUtils;
/**
* This class is used to keep the annotations related to the debugger in sync with external editors
* (if we're not dealing with an external editor, this class won't actually do anything)
*
* @author Fabio
*/
public class PyEditBreakpointSync implements IPyEditListener, IPyEditListener4 {
public static class PyEditBreakpointSyncImpl implements IBreakpointListener, IPyEditListener, IPyEditListener4 {
private PyEdit edit;
public PyEditBreakpointSyncImpl(PyEdit edit) {
this.edit = edit;
}
// breakpoints listening ---------------------------------------------------------------------------------------
// breakpoints listening ---------------------------------------------------------------------------------------
// breakpoints listening ---------------------------------------------------------------------------------------
@Override
public void breakpointAdded(IBreakpoint breakpoint) {
updateAnnotations();
}
@Override
public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
}
@Override
public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
updateAnnotations();
}
// pyedit listening --------------------------------------------------------------------------------------------
// pyedit listening --------------------------------------------------------------------------------------------
// pyedit listening --------------------------------------------------------------------------------------------
@Override
public void onCreateActions(ListResourceBundle resources, BaseEditor baseEditor, IProgressMonitor monitor) {
}
@Override
public void onDispose(BaseEditor baseEditor, IProgressMonitor monitor) {
if (this.edit != null) {
this.edit = null;
IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager();
breakpointManager.removeBreakpointListener(this);
}
}
@Override
public void onEditorCreated(BaseEditor baseEditor) {
}
@Override
public void onSave(BaseEditor baseEditor, IProgressMonitor monitor) {
updateAnnotations();
}
/**
* When the document is set, this class will start listening for the breakpoint manager, so that any changes in it
* will update the debug annotations.
*/
@Override
public void onSetDocument(IDocument document, BaseEditor baseEditor, IProgressMonitor monitor) {
PyEdit edit = (PyEdit) baseEditor;
if (this.edit != null) {
this.edit = null;
IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager();
breakpointManager.removeBreakpointListener(this);
}
if (AbstractBreakpointRulerAction.isExternalFileEditor(edit)) {
this.edit = edit;
IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager();
breakpointManager.addBreakpointListener(this);
}
//initial update (the others will be on changes)
updateAnnotations();
}
// update annotations ----------------------------------------------------------------------------------------------
// update annotations ----------------------------------------------------------------------------------------------
// update annotations ----------------------------------------------------------------------------------------------
@SuppressWarnings("unchecked")
private void updateAnnotations() {
if (edit == null) {
return;
}
IDocumentProvider provider = edit.getDocumentProvider();
if (provider == null) {
return;
}
IAnnotationModel model = provider.getAnnotationModel(edit.getEditorInput());
if (model == null) {
return;
}
IAnnotationModelExtension modelExtension = (IAnnotationModelExtension) model;
List<Annotation> existing = new ArrayList<Annotation>();
Iterator<Annotation> it = model.getAnnotationIterator();
if (it == null) {
return;
}
while (it.hasNext()) {
existing.add(it.next());
}
IDocument doc = edit.getDocument();
IResource resource = PyMarkerUtils.getResourceForTextEditor(edit);
IEditorInput externalFileEditorInput = AbstractBreakpointRulerAction.getExternalFileEditorInput(edit);
List<IMarker> markers = AbstractBreakpointRulerAction.getMarkersFromEditorResource(resource, doc,
externalFileEditorInput, 0, false, model);
Map<Annotation, Position> annotationsToAdd = new HashMap<Annotation, Position>();
for (IMarker m : markers) {
Position pos = PyMarkerUtils.getMarkerPosition(doc, m, model);
MarkerAnnotation newAnnotation = new MarkerAnnotation(m);
annotationsToAdd.put(newAnnotation, pos);
}
//update all in a single step
modelExtension.replaceAnnotations(existing.toArray(new Annotation[0]), annotationsToAdd);
}
}
@Override
public void onSave(BaseEditor baseEditor, IProgressMonitor monitor) {
}
@Override
public void onCreateActions(ListResourceBundle resources, BaseEditor baseEditor, IProgressMonitor monitor) {
}
@Override
public void onDispose(BaseEditor baseEditor, IProgressMonitor monitor) {
}
@Override
public void onSetDocument(IDocument document, BaseEditor baseEditor, IProgressMonitor monitor) {
}
@Override
public void onEditorCreated(final BaseEditor baseEditor) {
final PyEdit edit = (PyEdit) baseEditor;
Map<String, Object> cache = edit.getCache();
//Register listener that'll keep the breakpoints in sync for external files
String key = "PyEditBreakpointSync.PyEditBreakpointSyncImpl";
PyEditBreakpointSyncImpl syncImpl = (PyEditBreakpointSyncImpl) cache.get(key);
if (syncImpl == null) {
syncImpl = new PyEditBreakpointSyncImpl(edit);
edit.addPyeditListener(syncImpl);
cache.put(key, syncImpl);
}
//Register the adapter for IToggleBreakpointsTarget
edit.onGetAdapter.registerListener(new ICallbackListener<Class<?>>() {
@Override
public Object call(Class<?> obj) {
if (IToggleBreakpointsTarget.class == obj) {
Map<String, Object> cache = edit.getCache();
String key = "PyEditBreakpointSync.ToggleBreakpointsTarget";
Object object = cache.get(key);
if (object == null) {
object = new PyToggleBreakpointsTarget();
cache.put(key, object);
}
return object;
}
return null;
}
});
}
}