https://github.com/akkartik/mu/blob/main/shell/trace.mu

       1 # A trace records the evolution of a computation.
       2 # An integral part of the Mu Shell is facilities for browsing traces.
       3
       4 type trace {
       5   curr-depth: int  # depth that will be assigned to next line appended
       6   data: (handle array trace-line)
       7   first-free: int
       8   first-full: int  # used only by check-trace-scan
       9
      10   # steady-state life cycle of a trace:
      11   #   reload loop:
      12   #     there are already some visible lines
      13   #     append a bunch of new trace lines to the trace
      14   #     render loop:
      15   #       rendering displays trace lines that match visible lines
      16   #       rendering computes cursor-line based on the cursor-y coordinate
      17   #       edit-trace updates cursor-y coordinate
      18   #       edit-trace might add/remove lines to visible
      19   visible: (handle array trace-line)
      20   recompute-visible?: boolean
      21   top-line-index: int  # index into data
      22   cursor-y: int  # row index on screen
      23   cursor-line-index: int  # index into data
      24 }
      25
      26 type trace-line {
      27   depth: int
      28   label: (handle array byte)
      29   data: (handle array byte)
      30   visible?: boolean
      31 }
      32
      33 ## generating traces
      34
      35 fn initialize-trace _self: (addr trace), capacity: int, visible-capacity: int {
      36   var self/esi: (addr trace) <- copy _self
      37   compare self, 0
      38   break-if-=
      39   var trace-ah/eax: (addr handle array trace-line) <- get self, data
      40   populate trace-ah, capacity
      41   var visible-ah/eax: (addr handle array trace-line) <- get self, visible
      42   populate visible-ah, visible-capacity
      43 }
      44
      45 fn clear-trace _self: (addr trace) {
      46   var self/eax: (addr trace) <- copy _self
      47   compare self, 0
      48   break-if-=
      49   var len/edx: (addr int) <- get self, first-free
      50   copy-to *len, 0
      51   # might leak memory; existing elements won't be used anymore
      52 }
      53
      54 fn has-errors? _self: (addr trace) -> _/eax: boolean {
      55   var self/eax: (addr trace) <- copy _self
      56   {
      57     compare self, 0
      58     break-if-!=
      59     return 0/false
      60   }
      61   var max/edx: (addr int) <- get self, first-free
      62   var trace-ah/eax: (addr handle array trace-line) <- get self, data
      63   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
      64   var trace/esi: (addr array trace-line) <- copy _trace
      65   var i/ecx: int <- copy 0
      66   {
      67     compare i, *max
      68     break-if->=
      69     var offset/eax: (offset trace-line) <- compute-offset trace, i
      70     var curr/eax: (addr trace-line) <- index trace, offset
      71     var curr-label-ah/eax: (addr handle array byte) <- get curr, label
      72     var curr-label/eax: (addr array byte) <- lookup *curr-label-ah
      73     var error?/eax: boolean <- string-equal? curr-label, "error"
      74     compare error?, 0/false
      75     {
      76       break-if-=
      77       return 1/true
      78     }
      79     i <- increment
      80     loop
      81   }
      82   return 0/false
      83 }
      84
      85 fn trace _self: (addr trace), label: (addr array byte), message: (addr stream byte) {
      86   var self/esi: (addr trace) <- copy _self
      87   compare self, 0
      88   break-if-=
      89   var data-ah/eax: (addr handle array trace-line) <- get self, data
      90   var data/eax: (addr array trace-line) <- lookup *data-ah
      91   var index-addr/edi: (addr int) <- get self, first-free
      92   var index/ecx: int <- copy *index-addr
      93   var offset/ecx: (offset trace-line) <- compute-offset data, index
      94   var dest/eax: (addr trace-line) <- index data, offset
      95   var depth/ecx: (addr int) <- get self, curr-depth
      96   rewind-stream message
      97   initialize-trace-line *depth, label, message, dest
      98   increment *index-addr
      99 }
     100
     101 fn trace-text self: (addr trace), label: (addr array byte), s: (addr array byte) {
     102   compare self, 0
     103   break-if-=
     104   var data-storage: (stream byte 0x100)
     105   var data/eax: (addr stream byte) <- address data-storage
     106   write data, s
     107   trace self, label, data
     108 }
     109
     110 fn error self: (addr trace), message: (addr array byte) {
     111   trace-text self, "error", message
     112 }
     113
     114 fn initialize-trace-line depth: int, label: (addr array byte), data: (addr stream byte), _out: (addr trace-line) {
     115   var out/edi: (addr trace-line) <- copy _out
     116   # depth
     117   var src/eax: int <- copy depth
     118   var dest/ecx: (addr int) <- get out, depth
     119   copy-to *dest, src
     120   # label
     121   var dest/eax: (addr handle array byte) <- get out, label
     122   copy-array-object label, dest
     123   # data
     124   var dest/eax: (addr handle array byte) <- get out, data
     125   stream-to-array data, dest
     126 }
     127
     128 fn trace-lower _self: (addr trace) {
     129   var self/esi: (addr trace) <- copy _self
     130   compare self, 0
     131   break-if-=
     132   var depth/eax: (addr int) <- get self, curr-depth
     133   increment *depth
     134 }
     135
     136 fn trace-higher _self: (addr trace) {
     137   var self/esi: (addr trace) <- copy _self
     138   compare self, 0
     139   break-if-=
     140   var depth/eax: (addr int) <- get self, curr-depth
     141   decrement *depth
     142 }
     143
     144 ## checking traces
     145
     146 fn check-trace-scans-to self: (addr trace), label: (addr array byte), data: (addr array byte), message: (addr array byte) {
     147   var tmp/eax: boolean <- trace-scans-to? self, label, data
     148   check tmp, message
     149 }
     150
     151 fn trace-scans-to? _self: (addr trace), label: (addr array byte), data: (addr array byte) -> _/eax: boolean {
     152   var self/esi: (addr trace) <- copy _self
     153   var start/eax: (addr int) <- get self, first-full
     154   var result/eax: boolean <- trace-contains? self, label, data, *start
     155   return result
     156 }
     157
     158 fn test-trace-scans-to {
     159   var t-storage: trace
     160   var t/esi: (addr trace) <- address t-storage
     161   initialize-trace t, 0x10, 0/visible  # we don't use trace UI
     162   #
     163   trace-text t, "label", "line 1"
     164   trace-text t, "label", "line 2"
     165   check-trace-scans-to t, "label", "line 1", "F - test-trace-scans-to/0"
     166   check-trace-scans-to t, "label", "line 2", "F - test-trace-scans-to/1"
     167   var tmp/eax: boolean <- trace-scans-to? t, "label", "line 1"
     168   check-not tmp, "F - test-trace-scans-to: fail on previously encountered lines"
     169   var tmp/eax: boolean <- trace-scans-to? t, "label", "line 3"
     170   check-not tmp, "F - test-trace-scans-to: fail on missing"
     171 }
     172
     173 # scan trace from start
     174 # resets previous scans
     175 fn check-trace-contains self: (addr trace), label: (addr array byte), data: (addr array byte), message: (addr array byte) {
     176   var tmp/eax: boolean <- trace-contains? self, label, data, 0
     177   check tmp, message
     178 }
     179
     180 fn test-trace-contains {
     181   var t-storage: trace
     182   var t/esi: (addr trace) <- address t-storage
     183   initialize-trace t, 0x10, 0/visible  # we don't use trace UI
     184   #
     185   trace-text t, "label", "line 1"
     186   trace-text t, "label", "line 2"
     187   check-trace-contains t, "label", "line 1", "F - test-trace-contains/0"
     188   check-trace-contains t, "label", "line 2", "F - test-trace-contains/1"
     189   check-trace-contains t, "label", "line 1", "F - test-trace-contains: find previously encountered lines"
     190   var tmp/eax: boolean <- trace-contains? t, "label", "line 3", 0/start
     191   check-not tmp, "F - test-trace-contains: fail on missing"
     192 }
     193
     194 # this is super-inefficient, string comparing every trace line
     195 # against every visible line on every render
     196 fn trace-contains? _self: (addr trace), label: (addr array byte), data: (addr array byte), start: int -> _/eax: boolean {
     197   var self/esi: (addr trace) <- copy _self
     198   var candidates-ah/eax: (addr handle array trace-line) <- get self, data
     199   var candidates/eax: (addr array trace-line) <- lookup *candidates-ah
     200   var i/ecx: int <- copy start
     201   var max/edx: (addr int) <- get self, first-free
     202   {
     203     compare i, *max
     204     break-if->=
     205     {
     206       var read-until-index/eax: (addr int) <- get self, first-full
     207       copy-to *read-until-index, i
     208     }
     209     {
     210       var curr-offset/ecx: (offset trace-line) <- compute-offset candidates, i
     211       var curr/ecx: (addr trace-line) <- index candidates, curr-offset
     212       # if curr->label does not match, return false
     213       var curr-label-ah/eax: (addr handle array byte) <- get curr, label
     214       var curr-label/eax: (addr array byte) <- lookup *curr-label-ah
     215       var match?/eax: boolean <- string-equal? curr-label, label
     216       compare match?, 0/false
     217       break-if-=
     218       # if curr->data does not match, return false
     219       var curr-data-ah/eax: (addr handle array byte) <- get curr, data
     220       var curr-data/eax: (addr array byte) <- lookup *curr-data-ah
     221       var match?/eax: boolean <- string-equal? curr-data, data
     222       compare match?, 0/false
     223       break-if-=
     224       return 1/true
     225     }
     226     i <- increment
     227     loop
     228   }
     229   return 0/false
     230 }
     231
     232 fn trace-lines-equal? _a: (addr trace-line), _b: (addr trace-line) -> _/eax: boolean {
     233   var a/esi: (addr trace-line) <- copy _a
     234   var b/edi: (addr trace-line) <- copy _b
     235   var a-depth/ecx: (addr int) <- get a, depth
     236   var b-depth/edx: (addr int) <- get b, depth
     237   var benchmark/eax: int <- copy *b-depth
     238   compare *a-depth, benchmark
     239   {
     240     break-if-=
     241     return 0/false
     242   }
     243   var a-label-ah/eax: (addr handle array byte) <- get a, label
     244   var _a-label/eax: (addr array byte) <- lookup *a-label-ah
     245   var a-label/ecx: (addr array byte) <- copy _a-label
     246   var b-label-ah/ebx: (addr handle array byte) <- get b, label
     247   var b-label/eax: (addr array byte) <- lookup *b-label-ah
     248   var label-match?/eax: boolean <- string-equal? a-label, b-label
     249   {
     250     compare label-match?, 0/false
     251     break-if-!=
     252     return 0/false
     253   }
     254   var a-data-ah/eax: (addr handle array byte) <- get a, data
     255   var _a-data/eax: (addr array byte) <- lookup *a-data-ah
     256   var a-data/ecx: (addr array byte) <- copy _a-data
     257   var b-data-ah/ebx: (addr handle array byte) <- get b, data
     258   var b-data/eax: (addr array byte) <- lookup *b-data-ah
     259   var data-match?/eax: boolean <- string-equal? a-data, b-data
     260   return data-match?
     261 }
     262
     263 fn dump-trace _self: (addr trace) {
     264   var already-hiding-lines?: boolean
     265   var y/ecx: int <- copy 0
     266   var self/esi: (addr trace) <- copy _self
     267   compare self, 0
     268   {
     269     break-if-!=
     270     return
     271   }
     272   var trace-ah/eax: (addr handle array trace-line) <- get self, data
     273   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
     274   var trace/edi: (addr array trace-line) <- copy _trace
     275   var i/edx: int <- copy 0
     276   var max-addr/ebx: (addr int) <- get self, first-free
     277   var max/ebx: int <- copy *max-addr
     278   $dump-trace:loop: {
     279     compare i, max
     280     break-if->=
     281     $dump-trace:iter: {
     282       var offset/ebx: (offset trace-line) <- compute-offset trace, i
     283       var curr/ebx: (addr trace-line) <- index trace, offset
     284       var curr-label-ah/eax: (addr handle array byte) <- get curr, label
     285       var curr-label/eax: (addr array byte) <- lookup *curr-label-ah
     286       y <- render-trace-line 0/screen, curr, 0, y, 0x80/width, 0x30/height, 7/fg, 0/bg
     287     }
     288     i <- increment
     289     loop
     290   }
     291 }
     292
     293 ## UI stuff
     294
     295 fn mark-lines-dirty _self: (addr trace) {
     296   var self/eax: (addr trace) <- copy _self
     297   var dest/edx: (addr boolean) <- get self, recompute-visible?
     298   copy-to *dest, 1/true
     299 }
     300
     301 fn mark-lines-clean _self: (addr trace) {
     302   var self/eax: (addr trace) <- copy _self
     303   var dest/edx: (addr boolean) <- get self, recompute-visible?
     304   copy-to *dest, 0/false
     305 }
     306
     307 fn render-trace screen: (addr screen), _self: (addr trace), xmin: int, ymin: int, xmax: int, ymax: int, show-cursor?: boolean -> _/ecx: int {
     308   var already-hiding-lines?: boolean
     309   var y/ecx: int <- copy ymin
     310   var self/esi: (addr trace) <- copy _self
     311   compare self, 0
     312   {
     313     break-if-!=
     314     return ymin
     315   }
     316   clamp-cursor-to-top self, y
     317   var trace-ah/eax: (addr handle array trace-line) <- get self, data
     318   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
     319   var trace/edi: (addr array trace-line) <- copy _trace
     320   var i/edx: int <- copy 0
     321   var max-addr/ebx: (addr int) <- get self, first-free
     322   var max/ebx: int <- copy *max-addr
     323   $render-trace:loop: {
     324     compare i, max
     325     break-if->=
     326     $render-trace:iter: {
     327       var offset/ebx: (offset trace-line) <- compute-offset trace, i
     328       var curr/ebx: (addr trace-line) <- index trace, offset
     329       var curr-label-ah/eax: (addr handle array byte) <- get curr, label
     330       var curr-label/eax: (addr array byte) <- lookup *curr-label-ah
     331       var bg/edi: int <- copy 0/black
     332       compare show-cursor?, 0/false
     333       {
     334         break-if-=
     335         var cursor-y/eax: (addr int) <- get self, cursor-y
     336         compare *cursor-y, y
     337         break-if-!=
     338         bg <- copy 7/cursor-line-bg
     339         var cursor-line-index/eax: (addr int) <- get self, cursor-line-index
     340         copy-to *cursor-line-index, i
     341       }
     342       # always display errors
     343       var error?/eax: boolean <- string-equal? curr-label, "error"
     344       {
     345         compare error?, 0/false
     346         break-if-=
     347         y <- render-trace-line screen, curr, xmin, y, xmax, ymax, 0xc/fg=trace-error, bg
     348         copy-to already-hiding-lines?, 0/false
     349         break $render-trace:iter
     350       }
     351       # display expanded lines
     352       var display?/eax: boolean <- should-render? self, curr
     353       {
     354         compare display?, 0/false
     355         break-if-=
     356         y <- render-trace-line screen, curr, xmin, y, xmax, ymax, 9/fg=blue, bg
     357         copy-to already-hiding-lines?, 0/false
     358         break $render-trace:iter
     359       }
     360       # ignore the rest
     361       compare already-hiding-lines?, 0/false
     362       {
     363         break-if-!=
     364         var x/eax: int <- copy xmin
     365         x, y <- draw-text-wrapping-right-then-down screen, "...", xmin, ymin, xmax, ymax, x, y, 9/fg=trace, bg
     366         y <- increment
     367         copy-to already-hiding-lines?, 1/true
     368       }
     369     }
     370     i <- increment
     371     loop
     372   }
     373   # prevent cursor from going too far down
     374   clamp-cursor-to-bottom self, y, screen, xmin, ymin, xmax, ymax
     375   mark-lines-clean self
     376   return y
     377 }
     378
     379 fn render-trace-line screen: (addr screen), _self: (addr trace-line), xmin: int, ymin: int, xmax: int, ymax: int, fg: int, bg: int -> _/ecx: int {
     380   var self/esi: (addr trace-line) <- copy _self
     381   var xsave/edx: int <- copy xmin
     382   var y/ecx: int <- copy ymin
     383   var label-ah/eax: (addr handle array byte) <- get self, label
     384   var _label/eax: (addr array byte) <- lookup *label-ah
     385   var label/ebx: (addr array byte) <- copy _label
     386   var error?/eax: boolean <- string-equal? label, "error"
     387   compare error?, 0/false
     388   {
     389     break-if-!=
     390     var x/eax: int <- copy xsave
     391     {
     392       var depth/edx: (addr int) <- get self, depth
     393       x, y <- draw-int32-decimal-wrapping-right-then-down screen, *depth, xmin, ymin, xmax, ymax, x, y, fg, bg
     394       x, y <- draw-text-wrapping-right-then-down screen, " ", xmin, ymin, xmax, ymax, x, y, fg, bg
     395       # don't show label in UI; it's just for tests
     396     }
     397     xsave <- copy x
     398   }
     399   var data-ah/eax: (addr handle array byte) <- get self, data
     400   var _data/eax: (addr array byte) <- lookup *data-ah
     401   var data/ebx: (addr array byte) <- copy _data
     402   var x/eax: int <- copy xsave
     403   x, y <- draw-text-wrapping-right-then-down screen, data, xmin, ymin, xmax, ymax, x, y, fg, bg
     404   y <- increment
     405   return y
     406 }
     407
     408 # this is super-inefficient, string comparing every trace line
     409 # against every visible line on every render
     410 fn should-render? _self: (addr trace), _line: (addr trace-line) -> _/eax: boolean {
     411   var self/esi: (addr trace) <- copy _self
     412   # if visible? is already cached, just return it
     413   var dest/edx: (addr boolean) <- get self, recompute-visible?
     414   compare *dest, 0/false
     415   {
     416     break-if-!=
     417     var line/eax: (addr trace-line) <- copy _line
     418     var result/eax: (addr boolean) <- get line, visible?
     419     return *result
     420   }
     421   # recompute
     422   var candidates-ah/eax: (addr handle array trace-line) <- get self, visible
     423   var candidates/eax: (addr array trace-line) <- lookup *candidates-ah
     424   var i/ecx: int <- copy 0
     425   var len/edx: int <- length candidates
     426   {
     427     compare i, len
     428     break-if->=
     429     {
     430       var curr-offset/ecx: (offset trace-line) <- compute-offset candidates, i
     431       var curr/ecx: (addr trace-line) <- index candidates, curr-offset
     432       var match?/eax: boolean <- trace-lines-equal? curr, _line
     433       compare match?, 0/false
     434       break-if-=
     435       var line/eax: (addr trace-line) <- copy _line
     436       var dest/eax: (addr boolean) <- get line, visible?
     437       copy-to *dest, 1/true
     438       return 1/true
     439     }
     440     i <- increment
     441     loop
     442   }
     443   var line/eax: (addr trace-line) <- copy _line
     444   var dest/eax: (addr boolean) <- get line, visible?
     445   copy-to *dest, 0/false
     446   return 0/false
     447 }
     448
     449 fn clamp-cursor-to-top _self: (addr trace), _y: int {
     450   var y/ecx: int <- copy _y
     451   var self/esi: (addr trace) <- copy _self
     452   var cursor-y/eax: (addr int) <- get self, cursor-y
     453   compare *cursor-y, y
     454   break-if->=
     455   copy-to *cursor-y, y
     456 }
     457
     458 # extremely hacky; consider deleting test-render-trace-empty-3 when you clean this up
     459 fn clamp-cursor-to-bottom _self: (addr trace), _y: int, screen: (addr screen), xmin: int, ymin: int, xmax: int, ymax: int {
     460   var y/ebx: int <- copy _y
     461   compare y, ymin
     462   {
     463     break-if->
     464     return
     465   }
     466   y <- decrement
     467   var self/esi: (addr trace) <- copy _self
     468   var cursor-y/eax: (addr int) <- get self, cursor-y
     469   compare *cursor-y, y
     470   break-if-<=
     471   copy-to *cursor-y, y
     472   # redraw cursor-line
     473   # TODO: ugly duplication
     474   var trace-ah/eax: (addr handle array trace-line) <- get self, data
     475   var trace/eax: (addr array trace-line) <- lookup *trace-ah
     476   var cursor-line-index-addr/ecx: (addr int) <- get self, cursor-line-index
     477   var cursor-line-index/ecx: int <- copy *cursor-line-index-addr
     478   var first-free/edx: (addr int) <- get self, first-free
     479   compare cursor-line-index, *first-free
     480   {
     481     break-if-<
     482     return
     483   }
     484   var cursor-offset/ecx: (offset trace-line) <- compute-offset trace, cursor-line-index
     485   var cursor-line/ecx: (addr trace-line) <- index trace, cursor-offset
     486   var display?/eax: boolean <- should-render? self, cursor-line
     487   {
     488     compare display?, 0/false
     489     break-if-=
     490     var dummy/ecx: int <- render-trace-line screen, cursor-line, xmin, y, xmax, ymax, 9/fg=blue, 7/cursor-line-bg
     491     return
     492   }
     493   var dummy1/eax: int <- copy 0
     494   var dummy2/ecx: int <- copy 0
     495   dummy1, dummy2 <- draw-text-wrapping-right-then-down screen, "...", xmin, ymin, xmax, ymax, xmin, y, 9/fg=trace, 7/cursor-line-bg
     496 }
     497
     498 fn test-render-trace-empty {
     499   var t-storage: trace
     500   var t/esi: (addr trace) <- address t-storage
     501   initialize-trace t, 0x10, 0x10
     502   # setup: screen
     503   var screen-on-stack: screen
     504   var screen/edi: (addr screen) <- address screen-on-stack
     505   initialize-screen screen, 5/width, 4/height
     506   #
     507   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 5/xmax, 4/ymax, 0/no-cursor
     508   #
     509   check-ints-equal y, 0, "F - test-render-trace-empty/cursor"
     510   check-screen-row screen,                                  0/y, "    ", "F - test-render-trace-empty"
     511   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "    ", "F - test-render-trace-empty/bg"
     512 }
     513
     514 fn test-render-trace-empty-2 {
     515   var t-storage: trace
     516   var t/esi: (addr trace) <- address t-storage
     517   initialize-trace t, 0x10, 0x10
     518   # setup: screen
     519   var screen-on-stack: screen
     520   var screen/edi: (addr screen) <- address screen-on-stack
     521   initialize-screen screen, 5/width, 4/height
     522   #
     523   var y/ecx: int <- render-trace screen, t, 0/xmin, 2/ymin, 5/xmax, 4/ymax, 0/no-cursor  # cursor below top row
     524   #
     525   check-ints-equal y, 2, "F - test-render-trace-empty-2/cursor"
     526   check-screen-row screen,                                  2/y, "    ", "F - test-render-trace-empty-2"
     527   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "    ", "F - test-render-trace-empty-2/bg"
     528 }
     529
     530 fn test-render-trace-empty-3 {
     531   var t-storage: trace
     532   var t/esi: (addr trace) <- address t-storage
     533   initialize-trace t, 0x10, 0x10
     534   # setup: screen
     535   var screen-on-stack: screen
     536   var screen/edi: (addr screen) <- address screen-on-stack
     537   initialize-screen screen, 5/width, 4/height
     538   #
     539   var y/ecx: int <- render-trace screen, t, 0/xmin, 2/ymin, 5/xmax, 4/ymax, 1/show-cursor  # try show cursor
     540   # still no cursor to show
     541   check-ints-equal y, 2, "F - test-render-trace-empty-3/cursor"
     542   check-screen-row screen,                                  1/y, "    ", "F - test-render-trace-empty-3/line-above-cursor"
     543   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "    ", "F - test-render-trace-empty-3/bg-for-line-above-cursor"
     544   check-screen-row screen,                                  2/y, "    ", "F - test-render-trace-empty-3"
     545   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "    ", "F - test-render-trace-empty-3/bg"
     546 }
     547
     548 fn test-render-trace-collapsed-by-default {
     549   var t-storage: trace
     550   var t/esi: (addr trace) <- address t-storage
     551   initialize-trace t, 0x10, 0x10
     552   trace-text t, "l", "data"
     553   # setup: screen
     554   var screen-on-stack: screen
     555   var screen/edi: (addr screen) <- address screen-on-stack
     556   initialize-screen screen, 5/width, 4/height
     557   #
     558   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 5/xmax, 4/ymax, 0/no-cursor
     559   #
     560   check-ints-equal y, 1, "F - test-render-trace-collapsed-by-default/cursor"
     561   check-screen-row screen, 0/y, "... ", "F - test-render-trace-collapsed-by-default"
     562 }
     563
     564 fn test-render-trace-error {
     565   var t-storage: trace
     566   var t/esi: (addr trace) <- address t-storage
     567   initialize-trace t, 0x10, 0x10
     568   error t, "error"
     569   # setup: screen
     570   var screen-on-stack: screen
     571   var screen/edi: (addr screen) <- address screen-on-stack
     572   initialize-screen screen, 0xa/width, 4/height
     573   #
     574   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 0/no-cursor
     575   #
     576   check-ints-equal y, 1, "F - test-render-trace-error/cursor"
     577   check-screen-row screen, 0/y, "error", "F - test-render-trace-error"
     578 }
     579
     580 fn test-render-trace-error-at-start {
     581   var t-storage: trace
     582   var t/esi: (addr trace) <- address t-storage
     583   initialize-trace t, 0x10, 0x10
     584   #
     585   error t, "error"
     586   trace-text t, "l", "data"
     587   # setup: screen
     588   var screen-on-stack: screen
     589   var screen/edi: (addr screen) <- address screen-on-stack
     590   initialize-screen screen, 0xa/width, 4/height
     591   #
     592   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 0/no-cursor
     593   #
     594   check-ints-equal y, 2, "F - test-render-trace-error-at-start/cursor"
     595   check-screen-row screen, 0/y, "error", "F - test-render-trace-error-at-start/0"
     596   check-screen-row screen, 1/y, "...  ", "F - test-render-trace-error-at-start/1"
     597 }
     598
     599 fn test-render-trace-error-at-end {
     600   var t-storage: trace
     601   var t/esi: (addr trace) <- address t-storage
     602   initialize-trace t, 0x10, 0x10
     603   #
     604   trace-text t, "l", "data"
     605   error t, "error"
     606   # setup: screen
     607   var screen-on-stack: screen
     608   var screen/edi: (addr screen) <- address screen-on-stack
     609   initialize-screen screen, 0xa/width, 4/height
     610   #
     611   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 0/no-cursor
     612   #
     613   check-ints-equal y, 2, "F - test-render-trace-error-at-end/cursor"
     614   check-screen-row screen, 0/y, "...  ", "F - test-render-trace-error-at-end/0"
     615   check-screen-row screen, 1/y, "error", "F - test-render-trace-error-at-end/1"
     616 }
     617
     618 fn test-render-trace-error-in-the-middle {
     619   var t-storage: trace
     620   var t/esi: (addr trace) <- address t-storage
     621   initialize-trace t, 0x10, 0x10
     622   #
     623   trace-text t, "l", "line 1"
     624   error t, "error"
     625   trace-text t, "l", "line 3"
     626   # setup: screen
     627   var screen-on-stack: screen
     628   var screen/edi: (addr screen) <- address screen-on-stack
     629   initialize-screen screen, 0xa/width, 4/height
     630   #
     631   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 0/no-cursor
     632   #
     633   check-ints-equal y, 3, "F - test-render-trace-error-in-the-middle/cursor"
     634   check-screen-row screen, 0/y, "...  ", "F - test-render-trace-error-in-the-middle/0"
     635   check-screen-row screen, 1/y, "error", "F - test-render-trace-error-in-the-middle/1"
     636   check-screen-row screen, 2/y, "...  ", "F - test-render-trace-error-in-the-middle/2"
     637 }
     638
     639 fn test-render-trace-cursor-in-single-line {
     640   var t-storage: trace
     641   var t/esi: (addr trace) <- address t-storage
     642   initialize-trace t, 0x10, 0x10
     643   #
     644   trace-text t, "l", "line 1"
     645   error t, "error"
     646   trace-text t, "l", "line 3"
     647   # setup: screen
     648   var screen-on-stack: screen
     649   var screen/edi: (addr screen) <- address screen-on-stack
     650   initialize-screen screen, 0xa/width, 4/height
     651   #
     652   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor
     653   #
     654   check-screen-row screen,                                  0/y, "...   ", "F - test-render-trace-cursor-in-single-line/0"
     655   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||   ", "F - test-render-trace-cursor-in-single-line/0/cursor"
     656   check-screen-row screen,                                  1/y, "error ", "F - test-render-trace-cursor-in-single-line/1"
     657   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-render-trace-cursor-in-single-line/1/cursor"
     658   check-screen-row screen,                                  2/y, "...   ", "F - test-render-trace-cursor-in-single-line/2"
     659   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-render-trace-cursor-in-single-line/2/cursor"
     660 }
     661
     662 fn render-trace-menu screen: (addr screen) {
     663   var width/eax: int <- copy 0
     664   var height/ecx: int <- copy 0
     665   width, height <- screen-size screen
     666   var y/ecx: int <- copy height
     667   y <- decrement
     668   set-cursor-position screen, 0/x, y
     669   draw-text-rightward-from-cursor screen, " ctrl-s ", width, 0/fg, 7/bg=grey
     670   draw-text-rightward-from-cursor screen, " run sandbox  ", width, 7/fg, 0/bg
     671   draw-text-rightward-from-cursor screen, " tab ", width, 0/fg, 3/bg=cyan
     672   draw-text-rightward-from-cursor screen, " move to sandbox  ", width, 7/fg, 0/bg
     673   draw-text-rightward-from-cursor screen, " j ", width, 0/fg, 7/bg=grey
     674   draw-text-rightward-from-cursor screen, " down  ", width, 7/fg, 0/bg
     675   draw-text-rightward-from-cursor screen, " k ", width, 0/fg, 7/bg=grey
     676   draw-text-rightward-from-cursor screen, " up  ", width, 7/fg, 0/bg
     677   draw-text-rightward-from-cursor screen, " enter ", width, 0/fg, 7/bg=grey
     678   draw-text-rightward-from-cursor screen, " expand  ", width, 7/fg, 0/bg
     679   draw-text-rightward-from-cursor screen, " backspace ", width, 0/fg, 7/bg=grey
     680   draw-text-rightward-from-cursor screen, " collapse  ", width, 7/fg, 0/bg
     681 }
     682
     683 fn edit-trace _self: (addr trace), key: grapheme {
     684   var self/esi: (addr trace) <- copy _self
     685   # cursor down
     686   {
     687     compare key, 0x6a/j
     688     break-if-!=
     689     var cursor-y/eax: (addr int) <- get self, cursor-y
     690     increment *cursor-y
     691     return
     692   }
     693   # cursor up
     694   {
     695     compare key, 0x6b/k
     696     break-if-!=
     697     var cursor-y/eax: (addr int) <- get self, cursor-y
     698     decrement *cursor-y
     699     return
     700   }
     701   # enter = expand
     702   {
     703     compare key, 0xa/newline
     704     break-if-!=
     705     expand self
     706     return
     707   }
     708   # backspace = collapse
     709   {
     710     compare key, 8/backspace
     711     break-if-!=
     712     collapse self
     713     return
     714   }
     715 }
     716
     717 fn expand _self: (addr trace) {
     718   var self/esi: (addr trace) <- copy _self
     719   var trace-ah/eax: (addr handle array trace-line) <- get self, data
     720   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
     721   var trace/edi: (addr array trace-line) <- copy _trace
     722   var cursor-line-index-addr/ecx: (addr int) <- get self, cursor-line-index
     723   var cursor-line-index/ecx: int <- copy *cursor-line-index-addr
     724   var cursor-line-offset/eax: (offset trace-line) <- compute-offset trace, cursor-line-index
     725   var cursor-line/edx: (addr trace-line) <- index trace, cursor-line-offset
     726   var cursor-line-visible?/eax: (addr boolean) <- get cursor-line, visible?
     727   var cursor-line-depth/ebx: (addr int) <- get cursor-line, depth
     728   var target-depth/ebx: int <- copy *cursor-line-depth
     729   # if cursor-line is already visible, increment target-depth
     730   compare *cursor-line-visible?, 0/false
     731   {
     732     break-if-=
     733     target-depth <- increment
     734   }
     735   # reveal the run of lines starting at cursor-line-index with depth target-depth
     736   var i/ecx: int <- copy cursor-line-index
     737   var max/edx: (addr int) <- get self, first-free
     738   {
     739     compare i, *max
     740     break-if->=
     741     var curr-line-offset/eax: (offset trace-line) <- compute-offset trace, i
     742     var curr-line/edx: (addr trace-line) <- index trace, curr-line-offset
     743     var curr-line-depth/eax: (addr int) <- get curr-line, depth
     744     compare *curr-line-depth, target-depth
     745     break-if-<
     746     {
     747       break-if-!=
     748       var curr-line-visible?/eax: (addr boolean) <- get curr-line, visible?
     749       copy-to *curr-line-visible?, 1/true
     750       reveal-trace-line self, curr-line
     751     }
     752     i <- increment
     753     loop
     754   }
     755 }
     756
     757 fn collapse _self: (addr trace) {
     758   var self/esi: (addr trace) <- copy _self
     759   var trace-ah/eax: (addr handle array trace-line) <- get self, data
     760   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
     761   var trace/edi: (addr array trace-line) <- copy _trace
     762   var cursor-line-index-addr/ecx: (addr int) <- get self, cursor-line-index
     763   var cursor-line-index/ecx: int <- copy *cursor-line-index-addr
     764   var cursor-line-offset/eax: (offset trace-line) <- compute-offset trace, cursor-line-index
     765   var cursor-line/edx: (addr trace-line) <- index trace, cursor-line-offset
     766   var cursor-line-visible?/eax: (addr boolean) <- get cursor-line, visible?
     767   # if cursor-line is not visible, do nothing
     768   compare *cursor-line-visible?, 0/false
     769   {
     770     break-if-!=
     771     return
     772   }
     773   # hide all lines between previous and next line with a lower depth
     774   var cursor-line-depth/ebx: (addr int) <- get cursor-line, depth
     775   var cursor-y/edx: (addr int) <- get self, cursor-y
     776   var target-depth/ebx: int <- copy *cursor-line-depth
     777   var i/ecx: int <- copy cursor-line-index
     778   $collapse:loop1: {
     779     compare i, 0
     780     break-if-<
     781     var curr-line-offset/eax: (offset trace-line) <- compute-offset trace, i
     782     var curr-line/eax: (addr trace-line) <- index trace, curr-line-offset
     783     {
     784       var curr-line-depth/eax: (addr int) <- get curr-line, depth
     785       compare *curr-line-depth, target-depth
     786       break-if-< $collapse:loop1
     787     }
     788     # if cursor-line is visible, decrement cursor-y
     789     {
     790       var curr-line-visible?/eax: (addr boolean) <- get curr-line, visible?
     791       compare *curr-line-visible?, 0/false
     792       break-if-=
     793       decrement *cursor-y
     794     }
     795     i <- decrement
     796     loop
     797   }
     798   i <- increment
     799   var max/edx: (addr int) <- get self, first-free
     800   $collapse:loop2: {
     801     compare i, *max
     802     break-if->=
     803     var curr-line-offset/eax: (offset trace-line) <- compute-offset trace, i
     804     var curr-line/edx: (addr trace-line) <- index trace, curr-line-offset
     805     var curr-line-depth/eax: (addr int) <- get curr-line, depth
     806     compare *curr-line-depth, target-depth
     807     break-if-<
     808     {
     809       hide-trace-line self, curr-line
     810       var curr-line-visible?/eax: (addr boolean) <- get curr-line, visible?
     811       copy-to *curr-line-visible?, 0/false
     812     }
     813     i <- increment
     814     loop
     815   }
     816 }
     817
     818 # the 'visible' array is not required to be in order
     819 # elements can also be deleted out of order
     820 # so it can have holes
     821 # however, lines in it always have visible? set
     822 # we'll use visible? being unset as a sign of emptiness
     823 fn reveal-trace-line _self: (addr trace), line: (addr trace-line) {
     824   var self/esi: (addr trace) <- copy _self
     825   var visible-ah/eax: (addr handle array trace-line) <- get self, visible
     826   var visible/eax: (addr array trace-line) <- lookup *visible-ah
     827   var i/ecx: int <- copy 0
     828   var len/edx: int <- length visible
     829   {
     830     compare i, len
     831     break-if->=
     832     var curr-offset/edx: (offset trace-line) <- compute-offset visible, i
     833     var curr/edx: (addr trace-line) <- index visible, curr-offset
     834     var curr-visible?/eax: (addr boolean) <- get curr, visible?
     835     compare *curr-visible?, 0/false
     836     {
     837       break-if-!=
     838       # empty slot found
     839       copy-object line, curr
     840       return
     841     }
     842     i <- increment
     843     loop
     844   }
     845   abort "too many visible lines; increase size of array trace.visible"
     846 }
     847
     848 fn hide-trace-line _self: (addr trace), line: (addr trace-line) {
     849   var self/esi: (addr trace) <- copy _self
     850   var visible-ah/eax: (addr handle array trace-line) <- get self, visible
     851   var visible/eax: (addr array trace-line) <- lookup *visible-ah
     852   var i/ecx: int <- copy 0
     853   var len/edx: int <- length visible
     854   {
     855     compare i, len
     856     break-if->=
     857     var curr-offset/edx: (offset trace-line) <- compute-offset visible, i
     858     var curr/edx: (addr trace-line) <- index visible, curr-offset
     859     var found?/eax: boolean <- trace-lines-equal? curr, line
     860     compare found?, 0/false
     861     {
     862       break-if-=
     863       clear-object curr
     864     }
     865     i <- increment
     866     loop
     867   }
     868 }
     869
     870 fn test-cursor-down-and-up-within-trace {
     871   var t-storage: trace
     872   var t/esi: (addr trace) <- address t-storage
     873   initialize-trace t, 0x10, 0x10
     874   #
     875   trace-text t, "l", "line 1"
     876   error t, "error"
     877   trace-text t, "l", "line 3"
     878   # setup: screen
     879   var screen-on-stack: screen
     880   var screen/edi: (addr screen) <- address screen-on-stack
     881   initialize-screen screen, 0xa/width, 4/height
     882   #
     883   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor
     884   #
     885   check-screen-row screen,                                  0/y, "...   ", "F - test-cursor-down-and-up-within-trace/pre-0"
     886   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||   ", "F - test-cursor-down-and-up-within-trace/pre-0/cursor"
     887   check-screen-row screen,                                  1/y, "error ", "F - test-cursor-down-and-up-within-trace/pre-1"
     888   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-cursor-down-and-up-within-trace/pre-1/cursor"
     889   check-screen-row screen,                                  2/y, "...   ", "F - test-cursor-down-and-up-within-trace/pre-2"
     890   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-cursor-down-and-up-within-trace/pre-2/cursor"
     891   # cursor down
     892   edit-trace t, 0x6a/j
     893   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor
     894   #
     895   check-screen-row screen,                                  0/y, "...   ", "F - test-cursor-down-and-up-within-trace/down-0"
     896   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "      ", "F - test-cursor-down-and-up-within-trace/down-0/cursor"
     897   check-screen-row screen,                                  1/y, "error ", "F - test-cursor-down-and-up-within-trace/down-1"
     898   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "||||| ", "F - test-cursor-down-and-up-within-trace/down-1/cursor"
     899   check-screen-row screen,                                  2/y, "...   ", "F - test-cursor-down-and-up-within-trace/down-2"
     900   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-cursor-down-and-up-within-trace/down-2/cursor"
     901   # cursor up
     902   edit-trace t, 0x6b/k
     903   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor
     904   #
     905   check-screen-row screen,                                  0/y, "...   ", "F - test-cursor-down-and-up-within-trace/up-0"
     906   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||   ", "F - test-cursor-down-and-up-within-trace/up-0/cursor"
     907   check-screen-row screen,                                  1/y, "error ", "F - test-cursor-down-and-up-within-trace/up-1"
     908   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-cursor-down-and-up-within-trace/up-1/cursor"
     909   check-screen-row screen,                                  2/y, "...   ", "F - test-cursor-down-and-up-within-trace/up-2"
     910   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-cursor-down-and-up-within-trace/up-2/cursor"
     911 }
     912
     913 fn test-cursor-down-past-bottom-of-trace {
     914   var t-storage: trace
     915   var t/esi: (addr trace) <- address t-storage
     916   initialize-trace t, 0x10, 0x10
     917   #
     918   trace-text t, "l", "line 1"
     919   error t, "error"
     920   trace-text t, "l", "line 3"
     921   # setup: screen
     922   var screen-on-stack: screen
     923   var screen/edi: (addr screen) <- address screen-on-stack
     924   initialize-screen screen, 0xa/width, 4/height
     925   #
     926   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor
     927   #
     928   check-screen-row screen,                                  0/y, "...   ", "F - test-cursor-down-past-bottom-of-trace/pre-0"
     929   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||   ", "F - test-cursor-down-past-bottom-of-trace/pre-0/cursor"
     930   check-screen-row screen,                                  1/y, "error ", "F - test-cursor-down-past-bottom-of-trace/pre-1"
     931   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-cursor-down-past-bottom-of-trace/pre-1/cursor"
     932   check-screen-row screen,                                  2/y, "...   ", "F - test-cursor-down-past-bottom-of-trace/pre-2"
     933   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-cursor-down-past-bottom-of-trace/pre-2/cursor"
     934   # cursor down several times
     935   edit-trace t, 0x6a/j
     936   edit-trace t, 0x6a/j
     937   edit-trace t, 0x6a/j
     938   edit-trace t, 0x6a/j
     939   edit-trace t, 0x6a/j
     940   # hack: we do need to render to make this test pass; we're mixing state management with rendering
     941   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor
     942   # cursor clamps at bottom
     943   check-screen-row screen,                                  0/y, "...   ", "F - test-cursor-down-past-bottom-of-trace/down-0"
     944   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "      ", "F - test-cursor-down-past-bottom-of-trace/down-0/cursor"
     945   check-screen-row screen,                                  1/y, "error ", "F - test-cursor-down-past-bottom-of-trace/down-1"
     946   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-cursor-down-past-bottom-of-trace/down-1/cursor"
     947   check-screen-row screen,                                  2/y, "...   ", "F - test-cursor-down-past-bottom-of-trace/down-2"
     948   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "|||   ", "F - test-cursor-down-past-bottom-of-trace/down-2/cursor"
     949 }
     950
     951 fn test-expand-within-trace {
     952   var t-storage: trace
     953   var t/esi: (addr trace) <- address t-storage
     954   initialize-trace t, 0x10, 0x10
     955   #
     956   trace-text t, "l", "line 1"
     957   trace-text t, "l", "line 2"
     958   # setup: screen
     959   var screen-on-stack: screen
     960   var screen/edi: (addr screen) <- address screen-on-stack
     961   initialize-screen screen, 0x10/width, 4/height
     962   #
     963   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
     964   #
     965   check-screen-row screen,                                  0/y, "...      ", "F - test-expand-within-trace/pre-0"
     966   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||      ", "F - test-expand-within-trace/pre-0/cursor"
     967   check-screen-row screen,                                  1/y, "         ", "F - test-expand-within-trace/pre-1"
     968   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "         ", "F - test-expand-within-trace/pre-1/cursor"
     969   # expand
     970   edit-trace t, 0xa/enter
     971   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
     972   #
     973   check-screen-row screen,                                  0/y, "0 line 1 ", "F - test-expand-within-trace/expand-0"
     974   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-expand-within-trace/expand-0/cursor"
     975   check-screen-row screen,                                  1/y, "0 line 2 ", "F - test-expand-within-trace/expand-1"
     976   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "         ", "F - test-expand-within-trace/expand-1/cursor"
     977   check-screen-row screen,                                  2/y, "         ", "F - test-expand-within-trace/expand-2"
     978   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "         ", "F - test-expand-within-trace/expand-2/cursor"
     979 }
     980
     981 fn test-trace-expand-skips-lower-depth {
     982   var t-storage: trace
     983   var t/esi: (addr trace) <- address t-storage
     984   initialize-trace t, 0x10, 0x10
     985   #
     986   trace-text t, "l", "line 1"
     987   trace-lower t
     988   trace-text t, "l", "line 2"
     989   # setup: screen
     990   var screen-on-stack: screen
     991   var screen/edi: (addr screen) <- address screen-on-stack
     992   initialize-screen screen, 0x10/width, 4/height
     993   #
     994   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
     995   #
     996   check-screen-row screen,                                  0/y, "...      ", "F - test-trace-expand-skips-lower-depth/pre-0"
     997   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||      ", "F - test-trace-expand-skips-lower-depth/pre-0/cursor"
     998   check-screen-row screen,                                  1/y, "         ", "F - test-trace-expand-skips-lower-depth/pre-1"
     999   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "         ", "F - test-trace-expand-skips-lower-depth/pre-1/cursor"
    1000   # expand
    1001   edit-trace t, 0xa/enter
    1002   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1003   #
    1004   check-screen-row screen,                                  0/y, "0 line 1 ", "F - test-trace-expand-skips-lower-depth/expand-0"
    1005   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-expand-skips-lower-depth/expand-0/cursor"
    1006   check-screen-row screen,                                  1/y, "...      ", "F - test-trace-expand-skips-lower-depth/expand-1"
    1007   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "         ", "F - test-trace-expand-skips-lower-depth/expand-1/cursor"
    1008   check-screen-row screen,                                  2/y, "         ", "F - test-trace-expand-skips-lower-depth/expand-2"
    1009   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "         ", "F - test-trace-expand-skips-lower-depth/expand-2/cursor"
    1010 }
    1011
    1012 fn test-trace-expand-continues-past-lower-depth {
    1013   var t-storage: trace
    1014   var t/esi: (addr trace) <- address t-storage
    1015   initialize-trace t, 0x10, 0x10
    1016   #
    1017   trace-text t, "l", "line 1"
    1018   trace-lower t
    1019   trace-text t, "l", "line 1.1"
    1020   trace-higher t
    1021   trace-text t, "l", "line 2"
    1022   # setup: screen
    1023   var screen-on-stack: screen
    1024   var screen/edi: (addr screen) <- address screen-on-stack
    1025   initialize-screen screen, 0x10/width, 4/height
    1026   #
    1027   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1028   #
    1029   check-screen-row screen,                                  0/y, "...      ", "F - test-trace-expand-continues-past-lower-depth/pre-0"
    1030   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||      ", "F - test-trace-expand-continues-past-lower-depth/pre-0/cursor"
    1031   check-screen-row screen,                                  1/y, "         ", "F - test-trace-expand-continues-past-lower-depth/pre-1"
    1032   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "         ", "F - test-trace-expand-continues-past-lower-depth/pre-1/cursor"
    1033   # expand
    1034   edit-trace t, 0xa/enter
    1035   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1036   #
    1037   check-screen-row screen,                                  0/y, "0 line 1 ", "F - test-trace-expand-continues-past-lower-depth/expand-0"
    1038   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-expand-continues-past-lower-depth/expand-0/cursor"
    1039   # TODO: might be too wasteful to show every place where lines are hidden
    1040   check-screen-row screen,                                  1/y, "...      ", "F - test-trace-expand-continues-past-lower-depth/expand-1"
    1041   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "         ", "F - test-trace-expand-continues-past-lower-depth/expand-1/cursor"
    1042   check-screen-row screen,                                  2/y, "0 line 2 ", "F - test-trace-expand-continues-past-lower-depth/expand-2"
    1043   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "         ", "F - test-trace-expand-continues-past-lower-depth/expand-2/cursor"
    1044 }
    1045
    1046 fn test-trace-expand-stops-at-higher-depth {
    1047   var t-storage: trace
    1048   var t/esi: (addr trace) <- address t-storage
    1049   initialize-trace t, 0x10, 0x10
    1050   #
    1051   trace-text t, "l", "line 1.1"
    1052   trace-lower t
    1053   trace-text t, "l", "line 1.1.1"
    1054   trace-higher t
    1055   trace-text t, "l", "line 1.2"
    1056   trace-higher t
    1057   trace-text t, "l", "line 2"
    1058   trace-lower t
    1059   trace-text t, "l", "line 2.1"
    1060   # setup: screen
    1061   var screen-on-stack: screen
    1062   var screen/edi: (addr screen) <- address screen-on-stack
    1063   initialize-screen screen, 0x10/width, 8/height
    1064   #
    1065   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
    1066   #
    1067   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-expand-stops-at-higher-depth/pre-0"
    1068   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-expand-stops-at-higher-depth/pre-0/cursor"
    1069   check-screen-row screen,                                  1/y, "           ", "F - test-trace-expand-stops-at-higher-depth/pre-1"
    1070   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-expand-stops-at-higher-depth/pre-1/cursor"
    1071   # expand
    1072   edit-trace t, 0xa/enter
    1073   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
    1074   #
    1075   check-screen-row screen,                                  0/y, "0 line 1.1 ", "F - test-trace-expand-stops-at-higher-depth/expand-0"
    1076   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||||| ", "F - test-trace-expand-stops-at-higher-depth/expand-0/cursor"
    1077   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-expand-stops-at-higher-depth/expand-1"
    1078   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-expand-stops-at-higher-depth/expand-1/cursor"
    1079   check-screen-row screen,                                  2/y, "0 line 1.2 ", "F - test-trace-expand-stops-at-higher-depth/expand-2"
    1080   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-expand-stops-at-higher-depth/expand-2/cursor"
    1081   check-screen-row screen,                                  3/y, "...        ", "F - test-trace-expand-stops-at-higher-depth/expand-3"
    1082   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-expand-stops-at-higher-depth/expand-3/cursor"
    1083   check-screen-row screen,                                  4/y, "           ", "F - test-trace-expand-stops-at-higher-depth/expand-4"
    1084   check-background-color-in-screen-row screen, 7/bg=cursor, 4/y, "           ", "F - test-trace-expand-stops-at-higher-depth/expand-4/cursor"
    1085 }
    1086
    1087 fn test-trace-expand-twice {
    1088   var t-storage: trace
    1089   var t/esi: (addr trace) <- address t-storage
    1090   initialize-trace t, 0x10, 0x10
    1091   #
    1092   trace-text t, "l", "line 1"
    1093   trace-lower t
    1094   trace-text t, "l", "line 1.1"
    1095   trace-higher t
    1096   trace-text t, "l", "line 2"
    1097   # setup: screen
    1098   var screen-on-stack: screen
    1099   var screen/edi: (addr screen) <- address screen-on-stack
    1100   initialize-screen screen, 0x10/width, 4/height
    1101   #
    1102   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1103   #
    1104   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-expand-twice/pre-0"
    1105   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-expand-twice/pre-0/cursor"
    1106   check-screen-row screen,                                  1/y, "           ", "F - test-trace-expand-twice/pre-1"
    1107   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-expand-twice/pre-1/cursor"
    1108   # expand
    1109   edit-trace t, 0xa/enter
    1110   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1111   #
    1112   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-expand-twice/expand-0"
    1113   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-expand-twice/expand-0/cursor"
    1114   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-expand-twice/expand-1"
    1115   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-expand-twice/expand-1/cursor"
    1116   check-screen-row screen,                                  2/y, "0 line 2   ", "F - test-trace-expand-twice/expand-2"
    1117   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-expand-twice/expand-2/cursor"
    1118   # cursor down
    1119   edit-trace t, 0x6a/j
    1120   # hack: we need to render here to make this test pass; we're mixing state management with rendering
    1121   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1122   #
    1123   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-expand-twice/down-0"
    1124   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-expand-twice/down-0/cursor"
    1125   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-expand-twice/down-1"
    1126   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "|||        ", "F - test-trace-expand-twice/down-1/cursor"
    1127   check-screen-row screen,                                  2/y, "0 line 2   ", "F - test-trace-expand-twice/down-2"
    1128   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-expand-twice/down-2/cursor"
    1129   # expand again
    1130   edit-trace t, 0xa/enter
    1131   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1132   #
    1133   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-expand-twice/expand2-0"
    1134   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-expand-twice/expand2-0/cursor"
    1135   check-screen-row screen,                                  1/y, "1 line 1.1 ", "F - test-trace-expand-twice/expand2-1"
    1136   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "|||||||||| ", "F - test-trace-expand-twice/expand2-1/cursor"
    1137   check-screen-row screen,                                  2/y, "0 line 2   ", "F - test-trace-expand-twice/expand2-2"
    1138   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-expand-twice/expand2-2/cursor"
    1139 }
    1140
    1141 fn test-trace-refresh-cursor {
    1142   var t-storage: trace
    1143   var t/esi: (addr trace) <- address t-storage
    1144   initialize-trace t, 0x10, 0x10
    1145   #
    1146   trace-text t, "l", "line 1"
    1147   trace-text t, "l", "line 2"
    1148   trace-text t, "l", "line 3"
    1149   # setup: screen
    1150   var screen-on-stack: screen
    1151   var screen/edi: (addr screen) <- address screen-on-stack
    1152   initialize-screen screen, 0x10/width, 4/height
    1153   #
    1154   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1155   #
    1156   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-refresh-cursor/pre-0"
    1157   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-refresh-cursor/pre-0/cursor"
    1158   check-screen-row screen,                                  1/y, "           ", "F - test-trace-refresh-cursor/pre-1"
    1159   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-refresh-cursor/pre-1/cursor"
    1160   # expand
    1161   edit-trace t, 0xa/enter
    1162   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1163   #
    1164   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-refresh-cursor/expand-0"
    1165   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-refresh-cursor/expand-0/cursor"
    1166   check-screen-row screen,                                  1/y, "0 line 2   ", "F - test-trace-refresh-cursor/expand-1"
    1167   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-refresh-cursor/expand-1/cursor"
    1168   check-screen-row screen,                                  2/y, "0 line 3   ", "F - test-trace-refresh-cursor/expand-2"
    1169   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-refresh-cursor/expand-2/cursor"
    1170   # cursor down
    1171   edit-trace t, 0x6a/j
    1172   edit-trace t, 0x6a/j
    1173   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1174   #
    1175   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-refresh-cursor/down-0"
    1176   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-refresh-cursor/down-0/cursor"
    1177   check-screen-row screen,                                  1/y, "0 line 2   ", "F - test-trace-refresh-cursor/down-1"
    1178   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-refresh-cursor/down-1/cursor"
    1179   check-screen-row screen,                                  2/y, "0 line 3   ", "F - test-trace-refresh-cursor/down-2"
    1180   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "||||||||   ", "F - test-trace-refresh-cursor/down-2/cursor"
    1181   # recreate trace
    1182   clear-trace t
    1183   trace-text t, "l", "line 1"
    1184   trace-text t, "l", "line 2"
    1185   trace-text t, "l", "line 3"
    1186   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1187   # cursor remains unchanged
    1188   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-refresh-cursor/refresh-0"
    1189   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-refresh-cursor/refresh-0/cursor"
    1190   check-screen-row screen,                                  1/y, "0 line 2   ", "F - test-trace-refresh-cursor/refresh-1"
    1191   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-refresh-cursor/refresh-1/cursor"
    1192   check-screen-row screen,                                  2/y, "0 line 3   ", "F - test-trace-refresh-cursor/refresh-2"
    1193   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "||||||||   ", "F - test-trace-refresh-cursor/refresh-2/cursor"
    1194 }
    1195
    1196 fn test-trace-preserve-cursor-on-refresh {
    1197   var t-storage: trace
    1198   var t/esi: (addr trace) <- address t-storage
    1199   initialize-trace t, 0x10, 0x10
    1200   #
    1201   trace-text t, "l", "line 1"
    1202   trace-text t, "l", "line 2"
    1203   trace-text t, "l", "line 3"
    1204   # setup: screen
    1205   var screen-on-stack: screen
    1206   var screen/edi: (addr screen) <- address screen-on-stack
    1207   initialize-screen screen, 0x10/width, 4/height
    1208   #
    1209   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1210   #
    1211   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-preserve-cursor-on-refresh/pre-0"
    1212   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-preserve-cursor-on-refresh/pre-0/cursor"
    1213   check-screen-row screen,                                  1/y, "           ", "F - test-trace-preserve-cursor-on-refresh/pre-1"
    1214   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-preserve-cursor-on-refresh/pre-1/cursor"
    1215   # expand
    1216   edit-trace t, 0xa/enter
    1217   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1218   #
    1219   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-preserve-cursor-on-refresh/expand-0"
    1220   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-preserve-cursor-on-refresh/expand-0/cursor"
    1221   check-screen-row screen,                                  1/y, "0 line 2   ", "F - test-trace-preserve-cursor-on-refresh/expand-1"
    1222   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-preserve-cursor-on-refresh/expand-1/cursor"
    1223   check-screen-row screen,                                  2/y, "0 line 3   ", "F - test-trace-preserve-cursor-on-refresh/expand-2"
    1224   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "              ", "F - test-trace-preserve-cursor-on-refresh/expand-2/cursor"
    1225   # cursor down
    1226   edit-trace t, 0x6a/j
    1227   edit-trace t, 0x6a/j
    1228   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1229   #
    1230   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-preserve-cursor-on-refresh/down-0"
    1231   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-preserve-cursor-on-refresh/down-0/cursor"
    1232   check-screen-row screen,                                  1/y, "0 line 2   ", "F - test-trace-preserve-cursor-on-refresh/down-1"
    1233   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-preserve-cursor-on-refresh/down-1/cursor"
    1234   check-screen-row screen,                                  2/y, "0 line 3   ", "F - test-trace-preserve-cursor-on-refresh/down-2"
    1235   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "||||||||   ", "F - test-trace-preserve-cursor-on-refresh/down-2/cursor"
    1236   # recreate trace with slightly different lines
    1237   clear-trace t
    1238   trace-text t, "l", "line 4"
    1239   trace-text t, "l", "line 5"
    1240   trace-text t, "l", "line 3"  # cursor line is unchanged
    1241   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1242   # cursor remains unchanged
    1243   check-screen-row screen,                                  0/y, "0 line 4   ", "F - test-trace-preserve-cursor-on-refresh/refresh-0"
    1244   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-preserve-cursor-on-refresh/refresh-0/cursor"
    1245   check-screen-row screen,                                  1/y, "0 line 5   ", "F - test-trace-preserve-cursor-on-refresh/refresh-1"
    1246   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-preserve-cursor-on-refresh/refresh-1/cursor"
    1247   check-screen-row screen,                                  2/y, "0 line 3   ", "F - test-trace-preserve-cursor-on-refresh/refresh-2"
    1248   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "||||||||   ", "F - test-trace-preserve-cursor-on-refresh/refresh-2/cursor"
    1249 }
    1250
    1251 fn test-trace-keep-cursor-visible-on-refresh {
    1252   var t-storage: trace
    1253   var t/esi: (addr trace) <- address t-storage
    1254   initialize-trace t, 0x10, 0x10
    1255   #
    1256   trace-text t, "l", "line 1"
    1257   trace-text t, "l", "line 2"
    1258   trace-text t, "l", "line 3"
    1259   # setup: screen
    1260   var screen-on-stack: screen
    1261   var screen/edi: (addr screen) <- address screen-on-stack
    1262   initialize-screen screen, 0x10/width, 4/height
    1263   #
    1264   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1265   #
    1266   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-keep-cursor-visible-on-refresh/pre-0"
    1267   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-keep-cursor-visible-on-refresh/pre-0/cursor"
    1268   check-screen-row screen,                                  1/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/pre-1"
    1269   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/pre-1/cursor"
    1270   # expand
    1271   edit-trace t, 0xa/enter
    1272   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1273   #
    1274   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-keep-cursor-visible-on-refresh/expand-0"
    1275   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-keep-cursor-visible-on-refresh/expand-0/cursor"
    1276   check-screen-row screen,                                  1/y, "0 line 2   ", "F - test-trace-keep-cursor-visible-on-refresh/expand-1"
    1277   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/expand-1/cursor"
    1278   check-screen-row screen,                                  2/y, "0 line 3   ", "F - test-trace-keep-cursor-visible-on-refresh/expand-2"
    1279   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "              ", "F - test-trace-keep-cursor-visible-on-refresh/expand-2/cursor"
    1280   # cursor down
    1281   edit-trace t, 0x6a/j
    1282   edit-trace t, 0x6a/j
    1283   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1284   #
    1285   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-keep-cursor-visible-on-refresh/down-0"
    1286   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/down-0/cursor"
    1287   check-screen-row screen,                                  1/y, "0 line 2   ", "F - test-trace-keep-cursor-visible-on-refresh/down-1"
    1288   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/down-1/cursor"
    1289   check-screen-row screen,                                  2/y, "0 line 3   ", "F - test-trace-keep-cursor-visible-on-refresh/down-2"
    1290   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "||||||||   ", "F - test-trace-keep-cursor-visible-on-refresh/down-2/cursor"
    1291   # recreate trace with entirely different lines
    1292   clear-trace t
    1293   trace-text t, "l", "line 4"
    1294   trace-text t, "l", "line 5"
    1295   trace-text t, "l", "line 6"
    1296   mark-lines-dirty t
    1297   clear-screen screen
    1298   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1299   # trace collapses, and cursor bumps up
    1300   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-0"
    1301   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-0/cursor"
    1302   check-screen-row screen,                                  1/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-1"
    1303   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-1/cursor"
    1304   check-screen-row screen,                                  2/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-2"
    1305   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-2/cursor"
    1306 }
    1307
    1308 fn test-trace-collapse-at-top {
    1309   var t-storage: trace
    1310   var t/esi: (addr trace) <- address t-storage
    1311   initialize-trace t, 0x10, 0x10
    1312   #
    1313   trace-text t, "l", "line 1"
    1314   trace-lower t
    1315   trace-text t, "l", "line 1.1"
    1316   trace-higher t
    1317   trace-text t, "l", "line 2"
    1318   # setup: screen
    1319   var screen-on-stack: screen
    1320   var screen/edi: (addr screen) <- address screen-on-stack
    1321   initialize-screen screen, 0x10/width, 4/height
    1322   #
    1323   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1324   #
    1325   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-at-top/pre-0"
    1326   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-at-top/pre-0/cursor"
    1327   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-at-top/pre-1"
    1328   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-at-top/pre-1/cursor"
    1329   # expand
    1330   edit-trace t, 0xa/enter
    1331   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1332   #
    1333   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-collapse-at-top/expand-0"
    1334   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-collapse-at-top/expand-0/cursor"
    1335   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-collapse-at-top/expand-1"
    1336   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-at-top/expand-1/cursor"
    1337   check-screen-row screen,                                  2/y, "0 line 2   ", "F - test-trace-collapse-at-top/expand-2"
    1338   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-collapse-at-top/expand-2/cursor"
    1339   # collapse
    1340   edit-trace t, 8/backspace
    1341   # hack: we need to render here to make this test pass; we're mixing state management with rendering
    1342   clear-screen screen
    1343   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1344   #
    1345   check-ints-equal y, 1, "F - test-trace-collapse-at-top/post-0/y"
    1346   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-at-top/post-0"
    1347   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-at-top/post-0/cursor"
    1348   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-at-top/post-1"
    1349   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-at-top/post-1/cursor"
    1350 }
    1351
    1352 fn test-trace-collapse {
    1353   var t-storage: trace
    1354   var t/esi: (addr trace) <- address t-storage
    1355   initialize-trace t, 0x10, 0x10
    1356   #
    1357   trace-text t, "l", "line 1"
    1358   trace-text t, "l", "line 2"
    1359   # setup: screen
    1360   var screen-on-stack: screen
    1361   var screen/edi: (addr screen) <- address screen-on-stack
    1362   initialize-screen screen, 0x10/width, 4/height
    1363   #
    1364   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1365   #
    1366   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse/pre-0"
    1367   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse/pre-0/cursor"
    1368   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse/pre-1"
    1369   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse/pre-1/cursor"
    1370   # expand
    1371   edit-trace t, 0xa/enter
    1372   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1373   #
    1374   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-collapse/expand-0"
    1375   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-collapse/expand-0/cursor"
    1376   check-screen-row screen,                                  1/y, "0 line 2   ", "F - test-trace-collapse/expand-1"
    1377   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse/expand-1/cursor"
    1378   # cursor down
    1379   edit-trace t, 0x6a/j
    1380   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1381   # collapse
    1382   edit-trace t, 8/backspace
    1383   clear-screen screen
    1384   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1385   #
    1386   check-ints-equal y, 1, "F - test-trace-collapse/post-0/y"
    1387   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse/post-0"
    1388   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse/post-0/cursor"
    1389   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse/post-1"
    1390   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse/post-1/cursor"
    1391 }
    1392
    1393 fn test-trace-collapse-skips-invisible-lines {
    1394   var t-storage: trace
    1395   var t/esi: (addr trace) <- address t-storage
    1396   initialize-trace t, 0x10, 0x10
    1397   #
    1398   trace-text t, "l", "line 1"
    1399   trace-lower t
    1400   trace-text t, "l", "line 1.1"
    1401   trace-higher t
    1402   trace-text t, "l", "line 2"
    1403   # setup: screen
    1404   var screen-on-stack: screen
    1405   var screen/edi: (addr screen) <- address screen-on-stack
    1406   initialize-screen screen, 0x10/width, 4/height
    1407   #
    1408   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1409   #
    1410   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-skips-invisible-lines/pre-0"
    1411   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-skips-invisible-lines/pre-0/cursor"
    1412   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-skips-invisible-lines/pre-1"
    1413   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-skips-invisible-lines/pre-1/cursor"
    1414   # expand
    1415   edit-trace t, 0xa/enter
    1416   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1417   # two visible lines with an invisible line in between
    1418   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-collapse-skips-invisible-lines/expand-0"
    1419   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-collapse-skips-invisible-lines/expand-0/cursor"
    1420   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-collapse-skips-invisible-lines/expand-1"
    1421   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-skips-invisible-lines/expand-1/cursor"
    1422   check-screen-row screen,                                  2/y, "0 line 2   ", "F - test-trace-collapse-skips-invisible-lines/expand-2"
    1423   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-collapse-skips-invisible-lines/expand-2/cursor"
    1424   # cursor down to second visible line
    1425   edit-trace t, 0x6a/j
    1426   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1427   edit-trace t, 0x6a/j
    1428   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1429   # collapse
    1430   edit-trace t, 8/backspace
    1431   clear-screen screen
    1432   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1433   #
    1434   check-ints-equal y, 1, "F - test-trace-collapse-skips-invisible-lines/post-0/y"
    1435   var cursor-y/eax: (addr int) <- get t, cursor-y
    1436   check-ints-equal *cursor-y, 0, "F - test-trace-collapse-skips-invisible-lines/post-0/cursor-y"
    1437   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-skips-invisible-lines/post-0"
    1438   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-skips-invisible-lines/post-0/cursor"
    1439   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-skips-invisible-lines/post-1"
    1440   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-skips-invisible-lines/post-1/cursor"
    1441 }
    1442
    1443 fn test-trace-collapse-two-levels {
    1444   var t-storage: trace
    1445   var t/esi: (addr trace) <- address t-storage
    1446   initialize-trace t, 0x10, 0x10
    1447   #
    1448   trace-text t, "l", "line 1"
    1449   trace-lower t
    1450   trace-text t, "l", "line 1.1"
    1451   trace-higher t
    1452   trace-text t, "l", "line 2"
    1453   # setup: screen
    1454   var screen-on-stack: screen
    1455   var screen/edi: (addr screen) <- address screen-on-stack
    1456   initialize-screen screen, 0x10/width, 4/height
    1457   #
    1458   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1459   #
    1460   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-two-levels/pre-0"
    1461   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-two-levels/pre-0/cursor"
    1462   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-two-levels/pre-1"
    1463   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-two-levels/pre-1/cursor"
    1464   # expand
    1465   edit-trace t, 0xa/enter
    1466   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1467   # two visible lines with an invisible line in between
    1468   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-collapse-two-levels/expand-0"
    1469   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-collapse-two-levels/expand-0/cursor"
    1470   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-collapse-two-levels/expand-1"
    1471   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-two-levels/expand-1/cursor"
    1472   check-screen-row screen,                                  2/y, "0 line 2   ", "F - test-trace-collapse-two-levels/expand-2"
    1473   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-collapse-two-levels/expand-2/cursor"
    1474   # cursor down to ellipses
    1475   edit-trace t, 0x6a/j
    1476   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1477   # expand
    1478   edit-trace t, 0xa/enter
    1479   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1480   # two visible lines with an invisible line in between
    1481   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-collapse-two-levels/expand2-0"
    1482   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-collapse-two-levels/expand2-0/cursor"
    1483   check-screen-row screen,                                  1/y, "1 line 1.1 ", "F - test-trace-collapse-two-levels/expand2-1"
    1484   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "|||||||||| ", "F - test-trace-collapse-two-levels/expand2-1/cursor"
    1485   check-screen-row screen,                                  2/y, "0 line 2   ", "F - test-trace-collapse-two-levels/expand2-2"
    1486   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-collapse-two-levels/expand2-2/cursor"
    1487   # cursor down to second visible line
    1488   edit-trace t, 0x6a/j
    1489   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1490   # collapse
    1491   edit-trace t, 8/backspace
    1492   clear-screen screen
    1493   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
    1494   #
    1495   check-ints-equal y, 1, "F - test-trace-collapse-two-levels/post-0/y"
    1496   var cursor-y/eax: (addr int) <- get t, cursor-y
    1497   check-ints-equal *cursor-y, 0, "F - test-trace-collapse-two-levels/post-0/cursor-y"
    1498   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-two-levels/post-0"
    1499   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-two-levels/post-0/cursor"
    1500   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-two-levels/post-1"
    1501   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-two-levels/post-1/cursor"
    1502 }
    1503
    1504 fn test-trace-collapse-nested-level {
    1505   var t-storage: trace
    1506   var t/esi: (addr trace) <- address t-storage
    1507   initialize-trace t, 0x10, 0x10
    1508   #
    1509   trace-text t, "l", "line 1"
    1510   trace-lower t
    1511   trace-text t, "l", "line 1.1"
    1512   trace-higher t
    1513   trace-text t, "l", "line 2"
    1514   trace-lower t
    1515   trace-text t, "l", "line 2.1"
    1516   trace-text t, "l", "line 2.2"
    1517   trace-higher t
    1518   # setup: screen
    1519   var screen-on-stack: screen
    1520   var screen/edi: (addr screen) <- address screen-on-stack
    1521   initialize-screen screen, 0x10/width, 8/height
    1522   #
    1523   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
    1524   #
    1525   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-nested-level/pre-0"
    1526   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-nested-level/pre-0/cursor"
    1527   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-nested-level/pre-1"
    1528   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-nested-level/pre-1/cursor"
    1529   # expand
    1530   edit-trace t, 0xa/enter
    1531   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
    1532   # two visible lines with an invisible line in between
    1533   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-collapse-nested-level/expand-0"
    1534   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-collapse-nested-level/expand-0/cursor"
    1535   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-collapse-nested-level/expand-1"
    1536   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-nested-level/expand-1/cursor"
    1537   check-screen-row screen,                                  2/y, "0 line 2   ", "F - test-trace-collapse-nested-level/expand-2"
    1538   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-collapse-nested-level/expand-2/cursor"
    1539   check-screen-row screen,                                  3/y, "...        ", "F - test-trace-collapse-nested-level/expand-3"
    1540   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-collapse-nested-level/expand-3/cursor"
    1541   # cursor down to bottom
    1542   edit-trace t, 0x6a/j
    1543   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
    1544   edit-trace t, 0x6a/j
    1545   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
    1546   edit-trace t, 0x6a/j
    1547   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
    1548   # expand
    1549   edit-trace t, 0xa/enter
    1550   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
    1551   # two visible lines with an invisible line in between
    1552   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-collapse-nested-level/expand2-0"
    1553   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-collapse-nested-level/expand2-0/cursor"
    1554   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-collapse-nested-level/expand2-1"
    1555   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-nested-level/expand2-1/cursor"
    1556   check-screen-row screen,                                  2/y, "0 line 2   ", "F - test-trace-collapse-nested-level/expand2-2"
    1557   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-collapse-nested-level/expand2-2/cursor"
    1558   check-screen-row screen,                                  3/y, "1 line 2.1 ", "F - test-trace-collapse-nested-level/expand2-3"
    1559   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "|||||||||| ", "F - test-trace-collapse-nested-level/expand2-3/cursor"
    1560   check-screen-row screen,                                  4/y, "1 line 2.2 ", "F - test-trace-collapse-nested-level/expand2-4"
    1561   check-background-color-in-screen-row screen, 7/bg=cursor, 4/y, "           ", "F - test-trace-collapse-nested-level/expand2-4/cursor"
    1562   # collapse
    1563   edit-trace t, 8/backspace
    1564   clear-screen screen
    1565   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
    1566   #
    1567   check-ints-equal y, 4, "F - test-trace-collapse-nested-level/post-0/y"
    1568   var cursor-y/eax: (addr int) <- get t, cursor-y
    1569   check-ints-equal *cursor-y, 2, "F - test-trace-collapse-nested-level/post-0/cursor-y"
    1570   check-screen-row screen,                                  0/y, "0 line 1   ", "F - test-trace-collapse-nested-level/post-0"
    1571   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-collapse-nested-level/post-0/cursor"
    1572   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-collapse-nested-level/post-1"
    1573   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-nested-level/post-1/cursor"
    1574   check-screen-row screen,                                  2/y, "0 line 2   ", "F - test-trace-collapse-nested-level/post-2"
    1575   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "||||||||   ", "F - test-trace-collapse-nested-level/post-2/cursor"
    1576   check-screen-row screen,                                  3/y, "...        ", "F - test-trace-collapse-nested-level/post-3"
    1577   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-collapse-nested-level/post-3/cursor"
    1578 }