Perhaps apart from attributes and the significant changes in the area of RTTI in Delphi 2010, there really isn’t much in the way of “HEADLINE NEWS” in this release. But that doesn’t mean there isn’t lots to appreciate, some of it dangerously subtle.
“Dangerous” because unless someone draws your attention to it, you might not even notice it.
As they occur to me I shall discuss some of these, usually with some explanation of why I think they are great, rather than just listing off the feature itself. I shall also present some ideas that these new features have given me for yet further improvements in some areas.
So first up in this occasional series….
I used the term “gutter-ball” recently when talking about this feature, so perhaps I’d better explain what they are before getting into dragging and dropping them.
“Gutter balls” are what I call those decorations that pop-up in the “gutter” alongside your code editor. Breakpoints (the red dots), bookmarks (the numbered squares), the execution point indicator (when stepping through code in the debugger) etc etc.
“Balls” is a bit of a misnomer because many of these decorations aren’t actually round, like a ball, but why let strict linguistic accuracy get in the way of a good nick-name?
Anyway, in Delphi 2010 many of these decorations can now be freely dragged and dropped. This to me a “a big deal”.
I often use conditions on breakpoints, be that a pass count or some explicit condition expression to control when/if a breakpoint is active.
during a debugging session I’ll often find myself honing in on a bug, removing breakpoints and adding new ones as I get closer and close to where I suspect there to be a problem. Often the conditions on those new breakpoints are the same – or very similar – to those that I have removed, and I have to remember to setup those conditions on those new breakpoints each time.
Now I can simply drag an existing breakpoint, complete with conditions, and simply drop it where I now wish it to be. This may not save me a lot of actual time in absolute terms but it sure is going to make my debugging life easier.
The Execution Point
This is a potential minefield, but also a potential God-send.
Just as with breakpoints, when debugging and “stopped” on some line of code you can now drag the execution point and drop it somewhere else. When you resume execution or step, your code will execute from that new point.
WOAH! Hold on a minute, isn’t that, like… DANGEROUS!?
Absolutely. You could make a real mess of things by moving the execution point too far. In particular if you move it from within one function body to inside another, then your stack is almost certainly going to become corrupt, not to mention the fact that the new function body will not have been invoked with the necessary parameter assignments to registers etc etc so what happens when you resume execution is pretty unpredictable (some shortcut to restore the execution point to the “real” current execution point might be useful for those times when a thoughtless drag and a reckless drop create such a situation inadvertently).
You really do not want to move the execution point too far. But sometimes it is undeniably useful to be able to move it just a little.
Let us imagine you are debugging… you find yourself stopped on a line of code that you suddenly realise might be responsible for all your problems. Perhaps you forgot some trivial parameter check and are about to – incorrectly – call some function with a NIL parameter. Or perhaps you realised that your code just called a function with the wrong value in some variable passed as a parameter.
You know that you need to fix that, and you’ll come back to it shortly, but for now you’d like to see if that change would actually fix your problem. If only you could prevent the code from calling that function, or fix the value of that variable and call that function again with the correct parameter value – just as the changes in either case you are going to make would result in.
Well now you can.
Simply pick up the execution point and advance it FORWARD, beyond that function call, or edit the variable value (via the Expression Evaluator as usual – Ctrl+F7) and move the execution point BACKWARD, to repeat the call.
With Delphi’s lightning fast compilation performance, maybe it’s not such a big deal to have to halt the program, make the change, recompile and then re-test. But perhaps the application in question can’t simply or safely be Ctrl-F2‘d and has to be allowed to proceed through some lengthy shutdown procedure, and/or maybe recreating the problem to get to this point in the code to test it is non-trivial.
Sometimes a “quick fix” is just what the debugger ordered.
This is not such a big deal – bookmarks were already very easily moved from one location to another simply by redeclaring the new location of the bookmark via the appropriate keyboard shortcut (Ctrl+Shift+<0..9>).
However, this might provide the inspiration for extending these newly movable gutter-balls even further… beyond their current limitations.
I will say however that a distinct improvement in this areas is the visual representation of bookmarks – these now much more clearly identify the number of the bookmark associated with them.
The biggest limitation is that you can’t drag/drop between different tabs in the editor. If whilst debugging I step-in to a routine in another unit and realise that I now want my breakpoint in that routine I cannot drag it and drop it from one unit to the other.
Dragging and dropping the execution point between units is not a great idea – as mentioned, even moving it between different functions in the same unit is asking for trouble as it is, so let’s not worry about those.
Another problem is that when the execution point is on a line with a breakpoint you can’t get at the breakpoint to drag it! Any drag operation picks up the execution point, not the breakpoint “behind” it! If you want to move the breakpoint this creates a rather messy situation where you have to drag the execution point out of the way, drag/drop the breakpoint, then drag the execution point back to where it was.
Exceeding The Limitations – Breakpoints
For breakpoints there are two ways I can think of to get around the inability to drag/drop between units, and one of those would also fix the “execution point hides my breakpoint” problem:
- When dragging, if the mouse moves over an editor tab then switch the editor to that tab (you will notice when trying to drag breakpoints that “tabs” themselves are already “active” dropspots when dragging, in a way that the tab strip itself is not).
This doesn’t really need any explanation. The next idea will need a little more detailed exploration though I think…
- Provide some way of identifying breakpoints, perhaps by simply assigning some number to them, as with bookmarks. Ctrl+Alt+Shift+<0..9> might be available for this. Other key combinations could work but for the purposes of exploring the suggestion below, let’s assume this combination for now.
Pressing Ctrl+Alt+Shift+<0..9> where no breakpoint is currently assigned to that number should drop a new breakpoint at the current location or, if there are existing un-numbered breakpoints defined, pop-up a list of those breakpoints (“anchored” in the gutter, and keyboard navigable by default) to allow you to choose one to move to that location and assign the appropriate number to it.
Pressing Ctrl+Alt+Shift+<0..9> for a breakpoint that is already assigned to that number would simply move the appropriate breakpoint to the current location.
Breakpoint number assignments could of course be managed more directly via the existing Breakpoints list window in the IDE.
Pressing Ctrl+Alt+<0..9> (i.e. without the Shift modifier) for a breakpoint that is already assigned to that number would take you to the current location of that breakpoint, acting like a bookmark (I often find myself dropping a bookmark alongside a breakpoint).
10 may seem a rather limiting number for the total of such identified breakpoints, but in my experience if you have that many active breakpoints then in my view you aren’t debugging very effectively. A potentially contentious view, but I have yet to encounter a situation, even in highly complex problems, where more than 4 or 5 breakpoints are actively useful, and usually it’s only 2 or 3.
In fact, I think this “numbered breakpoint” idea has merit beyond being a work around for moving breakpoints between units.
Above and Beyond- Further Debugger Enhancements
With the ability to modify variable values via Expression Evaluation, and the ability to easily move the execution point around, we are pretty much as close to being able to modify our code “on the fly” during a debugging session as it’s likely to be possible to get with a fully compiled runtime/debugging environment.
A couple of fairly minor additions would make it even easier to effect that sort of behaviour and further enhance the debugging tools imho.
- Provide a Skip Over debugger shortcut key/toolbar command. The existing Step Over – F8 – allows you to execute the line of code at the current execution point and resume debugging at the next source line. Skip Over – Shift+F8 perhaps?– would allow you to advance the execution point over that same line of code without executing it.
- A Step Out command would also be useful. This would execute the code up to the end of the current function or procedure. i.e. equivalent to dropping a breakpoint at the end of the the procedure, running to that breakpoint, then removing the breakpoint – something I find myself doing a LOT when I find myself in a RTL function that I really am not interested in, when debugging with Debug DCU’s. [Update: Already present since at least Delphi 7! I don’t know how I’ve not noticed that before! It uses Shift+F8 already, so a “Skip Over” would need to use something else. Thanks to msohn and windwings for pointing that out]
- Provide a Skip Out command. This would work like Step Out but would jump to the end of the current function/procedure without executing the code between there and the current execution point. Equivalent to an EXIT call being made at the current execution point (for that reason, perhaps it should instead Skip to any finally block if the current execution point is within a try/finally… I don’t know if that’s possible for the debugger to determine however).
- Make the Expression Evaluator window a modeless, dockable panel and/or allow in-place editing of watchlist or local variable values.