ok....
Before I start: ideas on how to implement are a general direction. If you go through the code, and come up with alternative ideas, then please discuss them.
Also if you have other ideas what to add/improve.
- Yes: watches = "Liste der überwachten Ausdrücke".
what do you mean with "instance class"
Basically any structured data: classes, records / data with fields.
Such data - displayed in a single line - is hard to read. It may be fine for a TPoint with just x,y.
But a TForm with dozens of fields - it would be easier to read with each field in a line of its own. => And if a field happens to be another record, recursively open it up.
That would mean to display values in some sort of tree-view, with expandable notes.
You may have noted the crude workaround. One selected watch can be displayed multiline using the "magnify glass" from the toolbar.
A full expanded view (but with even more details) is available in the "debug inspector" (which is like the object inspector from the form designer).
In the debug inspector, when double clicking Form1.Fcomponents it will display you the list object. It will fetch more data from the debugger.
You can see, if you extend and combine the concepts, a lot of comfort function could be added. A collapse/expand tree-view could be used for watches, locals, hints and fetch more data if required. The user could travel deeper into classes. Though that's at the far end.
To see for yourself, open a TFOrm (or other component) in the debug inspector. Click the "instance" (use class of instance) button. Double click fields (especially those for other embedded objects).
About "class of instance": The debugger show any object according to its declaration.
"Sender: TObject" is showing only the fields declared in TObject. That is pretty useless, as sender usually is some object with a lot of other fields.
"class of instance" will find out what Sender is, maybe a TButton. THen it will display all the fields TButton has. Essentially it does TButton(Sender).
There are more ideas, for comfort watches. But I think the above is a good first step
One part of the task is to find a suitable component for the task. The current listview does not have the capacity. That part is less to do with the debugger, and more with GUI design.
The other is retrieving the data. And this will probably get you into the actual debugger code.
Lets start.
There are 2 main ways to get watches (or rather evaluated-expressions)
1) Creating a watch
2) DebugBoss.Evaluate(FExpression, @EvaluateCallback, Opts) see "debug inspector" (inspectdlg.pas), "eval dialog" (evaluatedlg.pp) and hint (hidden in the IDE code, search in files for "DebugBoss.Evaluate")
DebugBoss.Evaluate get the current thread/stack value. If they are changed by the user (stack window / thread window) then they need to be called again)
There are different detail-levels of expression eval in the debuggers (backend)
TDBGEvaluateFlag =
(defNoTypeInfo, // No Typeinfo object will be returned
defSimpleTypeInfo, // Returns: Kind (skSimple, skClass, ..); TypeName (but does make no attempt to avoid an alias)
defFullTypeInfo, // Get all typeinfo, resolve all anchestors
defClassAutoCast // Find real class of instance, and use, instead of declared class of variable
);
They can be set on watches, or passed to DebugBoss.Evaluate
defClassAutoCast => already explained above.
The other 3 are not defiend very well.... Sadly. And they may need some work done....
When a watch is evaluated, the result comes back in TWatchValue (components\debuggerintf\dbgintfdebuggerbase.pp)
However not all fields may be populated.
The reason for that is primarily historical: Getting data from GDB is time consuming. But a slow debugger is a pain (worst case you have 20 watches, you need the last watch, and the 19 before that take 30 seconds to evaluate. Bummer)
Getting details on the type, cost extra time. => Info is only retrieved when needed.
A better way would be "on request" in the sense, retrieval starts when you access the property. But for simplicity retrieval depends on the flag passed in.
SO WatchValue.TypeInfo will only be available if requested. And WatchValue.TypeInfo.Members (info on each field of an object) is only available at defFullTypeInfo.
FpDebug still follows that. But I am not sure if it has the exact same subset of info for each of the flags.
So for your work that means:
Depending on what needs to be displayed, you need to evaluate (or re-evaluate) with the correct flags.
You need to test what different debuggers return. The may not deliver the info at all, which must be handled too.
Debugger backends may have the info but not report it in the most practical manner. Or wrongly interpret the flags.
If so, then you may need to dive into the backends (I will help) and work it out.
Most of it should work though, because "debug inspector" works with all backends.
Ok so far so good. The above is plenty to explore. "debug inspector" and the "magnify" in watches-window may be a point to start studying.
Then when you get started you may think about how to store additional requests.
If a user unfolds Form1.FComponents, then "Form1.FComponents" must be evaluated as a watch.
You can use DebugBoss.Evaluate, but that has disadvantages.
- the result is not kept. If the user switches stackframe, and switches back, the data needs to be retrieved again.
- for that reason the data will not be avail in "debug history" either.
So creating a new watch may be an option. But it needs to be know, that it is displayed in the tree, and not top-level.
For the backend, it still needs to get into the current flat list of watches.
But the TIdeWatch could have an "Parent: TObject" and other linking fields.
Ok, I hope I did not forget to many things. I probably did skip a few....
but once you get familiar with the different classes used by the IDE and debugger, it will start to make more sense. (I remember when I looked at them for the first time around 2007/2008. I took a while....)
So the aim here is Get to know the classes involved. Then you will recognize them, when you start looking at the back-end.
One last note. It is a bit off topic. But may be interesting to know. Depending on what changes will become necessary, decisions may take the next bit into account....
* DisplayFormat
For some obscure reason (no idea why) "procedure TIdeWatch.DoDisplayFormatChanged;" => the watch is considered changed. It is sent to the debugger, and the debugger returns the same value with the new format.
(I am not sure if that is fixed in some places)...
So worst case: You retrieved "MyInteger" as 127. You set DisplayFormat to hex, and the debugger backend as to evaluate it again... Really the front end should know how to display an integer in different formats.
Part of the reason, again "speed" => classes come as text only, the fronted does not get the fields (unless it passes a flag). So the frontend can not reformat it.
But integer.....
As I said off-topic. But keep in mind stuff. Its not a black and white choice how to solve this. Lots to consider.
Ok so for so good.