The debugger will not yet be aware of these breakpoints. This functionality will be implemented in the next tutorial. In case your editor already supports setting breakpoints, you may skip this tutorial.
In eclipse there exist different kinds of breakpoints: line breakpoints (stop execution at a certain line), function breakpoints (stop whenever function foo() is called), watchpoints (stop whenever a variable is accessed/changed) and some more. Not all of them make sense for a dedicated language. In this tutorial we will have a closer look at line breakpoints only.
Debug Framework Tutorials
For a list of all debug related tutorials see Debug Framework Tutorials Overview.
Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.
Step 1: Breakpoint definition
Breakpoints are defined by the breakpoints extension point. To make such breakpoints persistent, eclipse uses markers. So each breakpoint is backed by a marker which is bound to a workspace resource.
Create a new org.eclipse.core.resource.markers extension in com.codeandme.debugger.textinterpreter.debugger/plugin.xml. The marker ID should be a single name without any plugin name qualifier before it, so set it to "textLineBreakpointMarker". The full marker name is automatically prepended with the Plug-in ID. Our marker should be a subtype of org.eclipse.debug.core.lineBreakpointMarker and persistable. Set these attributes by adding subnodes to your marker definition (see screenshot below).
Afterwards we create an org.eclipse.debug.core.breakpoints extension which refers to the marker type just created before.
The implementation is straight forward. We only add a constructor to attach a breakpoint to a resource:
public class TextLineBreakpoint extends LineBreakpoint { /** * Default constructor needed by the Eclipse debug framework. Do not remove! */ public TextLineBreakpoint() { } public TextLineBreakpoint(final IResource resource, final int lineNumber) throws CoreException { this(resource, lineNumber, true); } protected TextLineBreakpoint(final IResource resource, final int lineNumber, final boolean persistent) throws CoreException { IWorkspaceRunnable runnable = new IWorkspaceRunnable() { @Override public void run(IProgressMonitor monitor) throws CoreException { IMarker marker = resource.createMarker(Activator.PLUGIN_ID + ".textLineBreakpointMarker"); setMarker(marker); marker.setAttribute(IBreakpoint.ENABLED, true); marker.setAttribute(IBreakpoint.PERSISTED, persistent); marker.setAttribute(IMarker.LINE_NUMBER, lineNumber); marker.setAttribute(IBreakpoint.ID, getModelIdentifier()); marker.setAttribute(IMarker.MESSAGE, "Line Breakpoint: " + resource.getName() + " [line: " + lineNumber + "]"); } }; run(getMarkerRule(resource), runnable); } @Override public String getModelIdentifier() { return TextDebugModelPresentation.ID; } }Make sure to keep the default constructor, as it is needed by the debug framework to restore breakpoints.
As you can see, we used a new class here: TextDebugModelPresentation. It is needed to embrace all parts of the debug model for a certain debugger type. For now it is an empty implementation of IDebugModelPresentation, only providing its ID as a static constant. Do not forget to register it as an extension in com.codeandme.debugger.textinterpreter.debugger/plugin.xml.
Step 2: Add breakpoint support to editors
Users want to set their breakpoints in the code editor. For that our editor of choice needs to implement the IToggleBreakpointsTarget interface. Unfortunately the default text editor we use does not support this interface. Therefore we need to create an adapter for the editor. The adapter pattern is out of scope of this tutorial. If you need more information on how to create adapters you may read this article.
The adapter factory creates instances of TextLineBreakpointTarget:
public class TextLineBreakpointTarget implements IToggleBreakpointsTarget { @Override public boolean canToggleLineBreakpoints(IWorkbenchPart part, ISelection selection) { return true; } @Override public void toggleLineBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException { ITextEditor textEditor = getEditor(part); if (textEditor != null) { IResource resource = (IResource) textEditor.getEditorInput().getAdapter(IResource.class); ITextSelection textSelection = (ITextSelection) selection; int lineNumber = textSelection.getStartLine(); IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(TextDebugModelPresentation.ID); for (int i = 0; i < breakpoints.length; i++) { IBreakpoint breakpoint = breakpoints[i]; if (resource.equals(breakpoint.getMarker().getResource())) { if (((ILineBreakpoint) breakpoint).getLineNumber() == (lineNumber + 1)) { // existing breakpoint; delete breakpoint.delete(); return; } } } // new breakpoint; create TextLineBreakpoint lineBreakpoint = new TextLineBreakpoint(resource, lineNumber + 1); DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(lineBreakpoint); } } }Similar to the step and resume commands in the previous tutorials we have to implement some canToggleBreakpoint() methods. In our case we allow for LineBreakpoints only. The toggling itself is simple:
- get the active editor (line 10)
- get the current line number from the selection (lines 13, 14)
- look for an existing breakpoint at that line (lines 15-25)
- optionally delete an existing breakpoint or create a new one (lines 21/29)
Step 3: Toggle breakpoints from the ruler context menu
To add an item to the context menu of the ruler we need to use actions. Open the plugin.xml of the debugger project and add a new org.eclipse.ui.popupMenus extension. Add a new viewerContribution to it. Set id to "textEditor.rulerActions" and targetID to "#TextRulerContext". Now add a new action with id set to "pda.editor.ruler.toggleBreakpointAction". The implementation is rather boring as it only resolves the line number of the mouse click and then uses our IToggleBreakpointsTarget from before to toggle a line breakpoint.
Toggling breakpoints now works from the ruler context menu. Note that a double click still sets bookmarks and does not toggle breakpoints.
Conclusion
We are now able to set breakpoints in the UI. But our debugger does not use them right now. In the next tutorial we will implement debugger support for breakpoints.
Very nice tutorials! Exactly what i need for my project at the moment.
ReplyDeleteReally helpful tutorial. Unfortunately when I tried to use it in my own editor, then breakpoints were not visible (they appears in Breakpoints view, but not on the ruler). What could be the reason of it ?
ReplyDeletepiksowski, may be it's too late for answer...
ReplyDeleteFind in your plugin.xml extension point org.eclipse.core.resources.markers. Then change id from textLineBreakpointMarker to com.codeandme.textinterpreter.debugger.textLineBreakpointMarker (the same id that value in markerType of extension points org.eclipse.debug.core.breakpoints).
Hope it helps to anybody...
The source code for this project is no longer exist on the svn (404). Do you think you can provide it somewhere ? Thx.
ReplyDeleteI am moving my tutorials to github step by step. However this process is quite slow. Get in touch with me via email an I send you the code directly
Delete