What should the output of TRACE look like?

Rebol's only real historical debugging tool was TRACE. So before we talk about single-step-debugging, let's talk about what it does...and what it can and can't do.

For a very simplified example, let's say your code looked like this:

>> foo: func [x] [add x bar 10]
>> bar: func [y] [either y = 10 [20] [y + 1]]

>> foo 30
== 50

With TRACE ON you would get the following output.

>> foo 30
  1: foo : function! [x]
  2: 3n
     --> foo
      1: add : action! [value1 value2]
      2: x : 30
      3: bar : function! [y]
      4: 1n
         --> bar
          1: either : native! [condition true-branch false-branch /only]
          2: y : 10
          3: = : op! [value1 value2]
          4: 10
             --> =
         <-- = == true
          5: [20]
          6: [y + 1]
             --> either
              1: 20
         <-- either == 20
     <-- bar == 20
         --> add
     <-- add == 50
 <-- foo == 50
 == 50

(If you're wondering what 2: 3n and 4: 1n is, those are supposed to be 2: 30 and 4: 10, respectively. R3-Alpha had quite a number of memory bugs, and running a trace over large amounts of code was a good way to see them. One of the earliest tasks in Ren-C was in fact to have TRACE on for the entirety of boot, without tripping any asserts or address sanitizer problems.)

Another example:

>> do [x: 10 y: add 20 30]
 1: do : native! [value /args arg /next var]
 2: [x: 10 y: add 20 30]
    --> do
     1: x:
     2: 10
     3: y:
     4: add : action! [value1 value2]
     5: 20
     6: 30
        --> add
    <-- add == 50
<-- do == 50
== 50

What we see here is that in terms of the "what am I about to run", you get one unit of information. If the one unit of thing turns out to be a FUNCTION!, then you get a level of indentation.

Rebol doesn't really have a way to simulate the evaluator's logic to know how much of an expression is going to be consumed in advance, in order to print that full expression out. Hence the "one unit". The only reliable way to know where an expression is going to end is to run it...which means the trace output would come after the expression is run.

So take into account the evaluation order is such that if you say foo bar x then x evaluates, then bar evaluates, then foo evaluates. So the only way you're going to see output that says the whole expression foo bar x is if you wait until all the sub-expressions have evaluated (and suppress their output) and then show the full expression.

This suggests the difference between "step-over" and "step-into", where "step-over" generates no output until the stack level you started from has finished, at which point you get the full expression. I'm not sure how this pertains to trace, or what the relationship between trace and step debugging is.

I've done some light experimentation with alternatives, and wonder if people have an intuition of what they would like to see.

Never used this trace, but having studied your example I can see the use of it, it demonstrates how DO evaluates.

I see how each element of the same block is at the same indentation level , though at first I didn't see that. I was automatically looking for a tree even though I know emitting text only at each instruction doesn't lend itself to that.

An alternative explanatory view, that shows a tree would be nice, especially now that there are more enfixed operations. If one could indent something after it has been emitted, then perhaps we don't need to wait until the expression is complete to see the structure evolve. In the console, this may mean rewriting the screen, a GUI would probably be more suitable.