diff --git a/html/101screen.subx.html b/html/101screen.subx.html index 775988d0..53bdb2a7 100644 --- a/html/101screen.subx.html +++ b/html/101screen.subx.html @@ -1,29 +1,29 @@ - + - + Mu - 101screen.subx - - + + - + - - + + +https://github.com/akkartik/mu/blob/main/512array.mu +
+  1 # Inserting and deleting in arrays.
+  2 #
+  3 # The primitives here just do the work of making space and compacting.
+  4 
+  5 fn slide-up _a: (addr array int), start: int, end: int, target: int {
+  6   var a/esi: (addr array int) <- copy _a
+  7   var src-idx/ecx: int <- copy start
+  8   var dest-idx/edx: int <- copy target
+  9   # if start == target, nothing to do
+ 10   {
+ 11     compare src-idx, dest-idx
+ 12     break-if-!=
+ 13     return
+ 14   }
+ 15   # if start < target, abort
+ 16   {
+ 17     compare src-idx, dest-idx
+ 18     break-if->
+ 19     abort "slide-up: target > start; use slide-down instead"
+ 20   }
+ 21   # perform the copy
+ 22   {
+ 23     compare src-idx, end
+ 24     break-if->=
+ 25     var dest/ebx: (addr int) <- index a, dest-idx
+ 26     var src/eax: (addr int) <- index a, src-idx
+ 27     var val/eax: int <- copy *src
+ 28     copy-to *dest, val
+ 29     src-idx <- increment
+ 30     dest-idx <- increment
+ 31     loop
+ 32   }
+ 33 }
+ 34 
+ 35 fn slide-down _a: (addr array int), start: int, end: int, target: int {
+ 36   var a/esi: (addr array int) <- copy _a
+ 37   var src-idx/ecx: int <- copy end
+ 38   src-idx <- decrement
+ 39   var dest-idx/edx: int <- copy target
+ 40   dest-idx <- add end
+ 41   dest-idx <- subtract start
+ 42   dest-idx <- decrement
+ 43   # if start == target, nothing to do
+ 44   {
+ 45     compare src-idx, dest-idx
+ 46     break-if-!=
+ 47     return
+ 48   }
+ 49   # if start > target, abort
+ 50   {
+ 51     compare src-idx, dest-idx
+ 52     break-if-<
+ 53     abort "slide-down: target < start; use slide-down instead"
+ 54   }
+ 55   # perform the copy
+ 56   {
+ 57     compare src-idx, start
+ 58     break-if-<
+ 59     var dest/ebx: (addr int) <- index a, dest-idx
+ 60     var src/eax: (addr int) <- index a, src-idx
+ 61     var val/eax: int <- copy *src
+ 62     copy-to *dest, val
+ 63     src-idx <- decrement
+ 64     dest-idx <- decrement
+ 65     loop
+ 66   }
+ 67 }
+ 68 
+ 69 fn test-slide-up {
+ 70   check-slide-up "0 1 2 3 0", 1/start 1/end, 0/target, "0 1 2 3 0", "F - test-slide-up/empty-interval"
+ 71   check-slide-up "0 1 2 3 0", 1/start 2/end, 0/target, "1 1 2 3 0", "F - test-slide-up/single-non-overlapping"
+ 72   check-slide-up "0 0 0 1 2 3 0", 3/start 6/end, 0/target, "1 2 3 1 2 3 0", "F - test-slide-up/multiple-non-overlapping"
+ 73   check-slide-up "0 1 2 3 0", 1/start 4/end, 0/target, "1 2 3 3 0", "F - test-slide-up/overlapping"
+ 74 }
+ 75 
+ 76 fn test-slide-down {
+ 77   check-slide-down "0 1 2 3 0", 1/start 1/end, 4/target, "0 1 2 3 0", "F - test-slide-down/empty-interval"
+ 78   check-slide-down "0 1 2 3 0", 1/start 2/end, 4/target, "0 1 2 3 1", "F - test-slide-down/single-non-overlapping"
+ 79   check-slide-down "0 1 2 3 0 0 0", 1/start 4/end, 4/target, "0 1 2 3 1 2 3", "F - test-slide-down/multiple-non-overlapping"
+ 80   check-slide-down "0 1 2 3 0", 1/start 4/end, 2/target, "0 1 1 2 3", "F - test-slide-down/overlapping"
+ 81 }
+ 82 
+ 83 # Return the index that val is at.
+ 84 # If not found, return len-1.
+ 85 # That way the result is always a valid index to pass into slide-down.
+ 86 fn find-slide-down-slot-in-array _a: (addr array int), _val: int -> _/ecx: int {
+ 87   var a/esi: (addr array int) <- copy _a
+ 88   var val/ebx: int <- copy _val
+ 89   var max/edx: int <- length a
+ 90   max <- decrement
+ 91   var i/ecx: int <- copy 0
+ 92   {
+ 93     compare i, max
+ 94     break-if->=
+ 95     var curr/eax: (addr int) <- index a, i
+ 96     compare *curr, val
+ 97     break-if-=
+ 98     i <- increment
+ 99     loop
+100   }
+101   return i
+102 }
+103 
+104 # helpers for tests
+105 fn check-slide-up before: (addr array byte), start: int, end: int, target: int, after: (addr array byte), msg: (addr array byte) {
+106   var arr-h: (handle array int)
+107   var arr-ah/eax: (addr handle array int) <- address arr-h
+108   parse-array-of-decimal-ints before, arr-ah
+109   var arr/eax: (addr array int) <- lookup *arr-ah
+110   slide-up arr, start, end, target
+111   check-array-equal arr, after, msg
+112 }
+113 
+114 fn check-slide-down before: (addr array byte), start: int, end: int, target: int, after: (addr array byte), msg: (addr array byte) {
+115   var arr-h: (handle array int)
+116   var arr-ah/eax: (addr handle array int) <- address arr-h
+117   parse-array-of-decimal-ints before, arr-ah
+118   var arr/eax: (addr array int) <- lookup *arr-ah
+119   slide-down arr, start, end, target
+120   check-array-equal arr, after, msg
+121 }
+
+ + + diff --git a/html/513grapheme-stack.mu.html b/html/513grapheme-stack.mu.html new file mode 100644 index 00000000..2b7ce0c3 --- /dev/null +++ b/html/513grapheme-stack.mu.html @@ -0,0 +1,624 @@ + + + + +Mu - 513grapheme-stack.mu + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/513grapheme-stack.mu +
+  1 # grapheme stacks are the smallest unit of editable text
+  2 
+  3 type grapheme-stack {
+  4   data: (handle array grapheme)
+  5   top: int
+  6 }
+  7 
+  8 fn initialize-grapheme-stack _self: (addr grapheme-stack), n: int {
+  9   var self/esi: (addr grapheme-stack) <- copy _self
+ 10   var d/edi: (addr handle array grapheme) <- get self, data
+ 11   populate d, n
+ 12   var top/eax: (addr int) <- get self, top
+ 13   copy-to *top, 0
+ 14 }
+ 15 
+ 16 fn clear-grapheme-stack _self: (addr grapheme-stack) {
+ 17   var self/esi: (addr grapheme-stack) <- copy _self
+ 18   var top/eax: (addr int) <- get self, top
+ 19   copy-to *top, 0
+ 20 }
+ 21 
+ 22 fn grapheme-stack-empty? _self: (addr grapheme-stack) -> _/eax: boolean {
+ 23   var self/esi: (addr grapheme-stack) <- copy _self
+ 24   var top/eax: (addr int) <- get self, top
+ 25   compare *top, 0
+ 26   {
+ 27     break-if-!=
+ 28     return 1/true
+ 29   }
+ 30   return 0/false
+ 31 }
+ 32 
+ 33 fn grapheme-stack-length _self: (addr grapheme-stack) -> _/eax: int {
+ 34   var self/esi: (addr grapheme-stack) <- copy _self
+ 35   var top/eax: (addr int) <- get self, top
+ 36   return *top
+ 37 }
+ 38 
+ 39 fn push-grapheme-stack _self: (addr grapheme-stack), _val: grapheme {
+ 40   var self/esi: (addr grapheme-stack) <- copy _self
+ 41   var top-addr/ecx: (addr int) <- get self, top
+ 42   var data-ah/edx: (addr handle array grapheme) <- get self, data
+ 43   var data/eax: (addr array grapheme) <- lookup *data-ah
+ 44   var top/edx: int <- copy *top-addr
+ 45   var dest-addr/edx: (addr grapheme) <- index data, top
+ 46   var val/eax: grapheme <- copy _val
+ 47   copy-to *dest-addr, val
+ 48   add-to *top-addr, 1
+ 49 }
+ 50 
+ 51 fn pop-grapheme-stack _self: (addr grapheme-stack) -> _/eax: grapheme {
+ 52   var self/esi: (addr grapheme-stack) <- copy _self
+ 53   var top-addr/ecx: (addr int) <- get self, top
+ 54   {
+ 55     compare *top-addr, 0
+ 56     break-if->
+ 57     return -1
+ 58   }
+ 59   subtract-from *top-addr, 1
+ 60   var data-ah/edx: (addr handle array grapheme) <- get self, data
+ 61   var data/eax: (addr array grapheme) <- lookup *data-ah
+ 62   var top/edx: int <- copy *top-addr
+ 63   var result-addr/eax: (addr grapheme) <- index data, top
+ 64   return *result-addr
+ 65 }
+ 66 
+ 67 fn copy-grapheme-stack _src: (addr grapheme-stack), dest: (addr grapheme-stack) {
+ 68   var src/esi: (addr grapheme-stack) <- copy _src
+ 69   var data-ah/edi: (addr handle array grapheme) <- get src, data
+ 70   var _data/eax: (addr array grapheme) <- lookup *data-ah
+ 71   var data/edi: (addr array grapheme) <- copy _data
+ 72   var top-addr/ecx: (addr int) <- get src, top
+ 73   var i/eax: int <- copy 0
+ 74   {
+ 75     compare i, *top-addr
+ 76     break-if->=
+ 77     var g/edx: (addr grapheme) <- index data, i
+ 78     push-grapheme-stack dest, *g
+ 79     i <- increment
+ 80     loop
+ 81   }
+ 82 }
+ 83 
+ 84 # dump stack to screen from bottom to top
+ 85 # hardcoded colors:
+ 86 #   matching paren
+ 87 fn render-stack-from-bottom-wrapping-right-then-down screen: (addr screen), _self: (addr grapheme-stack), xmin: int, ymin: int, xmax: int, ymax: int, _x: int, _y: int, highlight-matching-open-paren?: boolean, open-paren-depth: int, color: int, background-color: int -> _/eax: int, _/ecx: int {
+ 88   var self/esi: (addr grapheme-stack) <- copy _self
+ 89   var matching-open-paren-index/edx: int <- get-matching-open-paren-index self, highlight-matching-open-paren?, open-paren-depth
+ 90   var data-ah/edi: (addr handle array grapheme) <- get self, data
+ 91   var _data/eax: (addr array grapheme) <- lookup *data-ah
+ 92   var data/edi: (addr array grapheme) <- copy _data
+ 93   var x/eax: int <- copy _x
+ 94   var y/ecx: int <- copy _y
+ 95   var top-addr/esi: (addr int) <- get self, top
+ 96   var i/ebx: int <- copy 0
+ 97   {
+ 98     compare i, *top-addr
+ 99     break-if->=
+100     {
+101       var g/esi: (addr grapheme) <- index data, i
+102       var fg: int
+103       {
+104         var tmp/eax: int <- copy color
+105         copy-to fg, tmp
+106       }
+107       {
+108         compare i, matching-open-paren-index
+109         break-if-!=
+110         copy-to fg, 0xf/highlight
+111       }
+112       x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, fg, background-color
+113     }
+114     i <- increment
+115     loop
+116   }
+117   return x, y
+118 }
+119 
+120 # helper for small words
+121 fn render-stack-from-bottom screen: (addr screen), self: (addr grapheme-stack), x: int, y: int, highlight-matching-open-paren?: boolean, open-paren-depth: int -> _/eax: int {
+122   var _width/eax: int <- copy 0
+123   var _height/ecx: int <- copy 0
+124   _width, _height <- screen-size screen
+125   var width/edx: int <- copy _width
+126   var height/ebx: int <- copy _height
+127   var x2/eax: int <- copy 0
+128   var y2/ecx: int <- copy 0
+129   x2, y2 <- render-stack-from-bottom-wrapping-right-then-down screen, self, x, y, width, height, x, y, highlight-matching-open-paren?, open-paren-depth, 3/fg=cyan, 0xc5/bg=blue-bg
+130   return x2  # y2? yolo
+131 }
+132 
+133 # dump stack to screen from top to bottom
+134 # optionally render a 'cursor' with the top grapheme
+135 # hard-coded colors:
+136 #   matching paren
+137 #   cursor
+138 fn render-stack-from-top-wrapping-right-then-down screen: (addr screen), _self: (addr grapheme-stack), xmin: int, ymin: int, xmax: int, ymax: int, _x: int, _y: int, render-cursor?: boolean, color: int, background-color: int -> _/eax: int, _/ecx: int {
+139   var self/esi: (addr grapheme-stack) <- copy _self
+140   var matching-close-paren-index/edx: int <- get-matching-close-paren-index self, render-cursor?
+141   var data-ah/eax: (addr handle array grapheme) <- get self, data
+142   var _data/eax: (addr array grapheme) <- lookup *data-ah
+143   var data/edi: (addr array grapheme) <- copy _data
+144   var x/eax: int <- copy _x
+145   var y/ecx: int <- copy _y
+146   var top-addr/ebx: (addr int) <- get self, top
+147   var i/ebx: int <- copy *top-addr
+148   i <- decrement
+149   # if render-cursor?, peel off first iteration
+150   {
+151     compare render-cursor?, 0/false
+152     break-if-=
+153     compare i, 0
+154     break-if-<
+155     var g/esi: (addr grapheme) <- index data, i
+156     x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, background-color, color
+157     i <- decrement
+158   }
+159   # remaining iterations
+160   {
+161     compare i, 0
+162     break-if-<
+163     # highlight matching paren if needed
+164     var fg: int
+165     {
+166       var tmp/eax: int <- copy color
+167       copy-to fg, tmp
+168     }
+169     compare i, matching-close-paren-index
+170     {
+171       break-if-!=
+172       copy-to fg, 0xf/highlight
+173     }
+174     #
+175     var g/esi: (addr grapheme) <- index data, i
+176     x, y <- render-grapheme screen, *g, xmin, ymin, xmax, ymax, x, y, fg, background-color
+177     i <- decrement
+178     loop
+179   }
+180   return x, y
+181 }
+182 
+183 # helper for small words
+184 fn render-stack-from-top screen: (addr screen), self: (addr grapheme-stack), x: int, y: int, render-cursor?: boolean -> _/eax: int {
+185   var _width/eax: int <- copy 0
+186   var _height/ecx: int <- copy 0
+187   _width, _height <- screen-size screen
+188   var width/edx: int <- copy _width
+189   var height/ebx: int <- copy _height
+190   var x2/eax: int <- copy 0
+191   var y2/ecx: int <- copy 0
+192   x2, y2 <- render-stack-from-top-wrapping-right-then-down screen, self, x, y, width, height, x, y, render-cursor?, 3/fg=cyan, 0xc5/bg=blue-bg
+193   return x2  # y2? yolo
+194 }
+195 
+196 fn test-render-grapheme-stack {
+197   # setup: gs = "abc"
+198   var gs-storage: grapheme-stack
+199   var gs/edi: (addr grapheme-stack) <- address gs-storage
+200   initialize-grapheme-stack gs, 5
+201   var g/eax: grapheme <- copy 0x61/a
+202   push-grapheme-stack gs, g
+203   g <- copy 0x62/b
+204   push-grapheme-stack gs, g
+205   g <- copy 0x63/c
+206   push-grapheme-stack gs, g
+207   # setup: screen
+208   var screen-on-stack: screen
+209   var screen/esi: (addr screen) <- address screen-on-stack
+210   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+211   #
+212   var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 0/y, 0/no-highlight-matching-open-paren, 0/open-paren-depth
+213   check-screen-row screen, 0/y, "abc ", "F - test-render-grapheme-stack from bottom"
+214   check-ints-equal x, 3, "F - test-render-grapheme-stack from bottom: result"
+215   check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, "   ", "F - test-render-grapheme-stack from bottom: bg"
+216   #
+217   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 1/y, 0/cursor=false
+218   check-screen-row screen, 1/y, "cba ", "F - test-render-grapheme-stack from top without cursor"
+219   check-ints-equal x, 3, "F - test-render-grapheme-stack from top without cursor: result"
+220   check-background-color-in-screen-row screen, 3/bg=reverse, 1/y, "   ", "F - test-render-grapheme-stack from top without cursor: bg"
+221   #
+222   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
+223   check-screen-row screen, 2/y, "cba ", "F - test-render-grapheme-stack from top with cursor"
+224   check-ints-equal x, 3, "F - test-render-grapheme-stack from top with cursor: result"
+225   check-background-color-in-screen-row screen, 3/bg=reverse, 2/y, "|   ", "F - test-render-grapheme-stack from top with cursor: bg"
+226 }
+227 
+228 fn test-render-grapheme-stack-while-highlighting-matching-close-paren {
+229   # setup: gs = "(b)"
+230   var gs-storage: grapheme-stack
+231   var gs/edi: (addr grapheme-stack) <- address gs-storage
+232   initialize-grapheme-stack gs, 5
+233   var g/eax: grapheme <- copy 0x29/close-paren
+234   push-grapheme-stack gs, g
+235   g <- copy 0x62/b
+236   push-grapheme-stack gs, g
+237   g <- copy 0x28/open-paren
+238   push-grapheme-stack gs, g
+239   # setup: screen
+240   var screen-on-stack: screen
+241   var screen/esi: (addr screen) <- address screen-on-stack
+242   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+243   #
+244   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
+245   check-screen-row                      screen,               2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren"
+246   check-background-color-in-screen-row  screen, 3/bg=reverse,  2/y, "|   ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: cursor"
+247   check-screen-row-in-color             screen, 0xf/fg=white, 2/y, "  ) ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren: matching paren"
+248 }
+249 
+250 fn test-render-grapheme-stack-while-highlighting-matching-close-paren-2 {
+251   # setup: gs = "(a (b)) c"
+252   var gs-storage: grapheme-stack
+253   var gs/edi: (addr grapheme-stack) <- address gs-storage
+254   initialize-grapheme-stack gs, 0x10
+255   var g/eax: grapheme <- copy 0x63/c
+256   push-grapheme-stack gs, g
+257   g <- copy 0x20/space
+258   push-grapheme-stack gs, g
+259   g <- copy 0x29/close-paren
+260   push-grapheme-stack gs, g
+261   g <- copy 0x29/close-paren
+262   push-grapheme-stack gs, g
+263   g <- copy 0x62/b
+264   push-grapheme-stack gs, g
+265   g <- copy 0x28/open-paren
+266   push-grapheme-stack gs, g
+267   g <- copy 0x20/space
+268   push-grapheme-stack gs, g
+269   g <- copy 0x61/a
+270   push-grapheme-stack gs, g
+271   g <- copy 0x28/open-paren
+272   push-grapheme-stack gs, g
+273   # setup: screen
+274   var screen-on-stack: screen
+275   var screen/esi: (addr screen) <- address screen-on-stack
+276   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+277   #
+278   var x/eax: int <- render-stack-from-top screen, gs, 0/x, 2/y, 1/cursor=true
+279   check-screen-row                      screen,               2/y, "(a (b)) c ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2"
+280   check-background-color-in-screen-row  screen, 3/bg=reverse,  2/y, "|         ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: cursor"
+281   check-screen-row-in-color             screen, 0xf/fg=white, 2/y, "      )   ", "F - test-render-grapheme-stack-while-highlighting-matching-close-paren-2: matching paren"
+282 }
+283 
+284 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end {
+285   # setup: gs = "(b)"
+286   var gs-storage: grapheme-stack
+287   var gs/edi: (addr grapheme-stack) <- address gs-storage
+288   initialize-grapheme-stack gs, 5
+289   var g/eax: grapheme <- copy 0x28/open-paren
+290   push-grapheme-stack gs, g
+291   g <- copy 0x62/b
+292   push-grapheme-stack gs, g
+293   g <- copy 0x29/close-paren
+294   push-grapheme-stack gs, g
+295   # setup: screen
+296   var screen-on-stack: screen
+297   var screen/esi: (addr screen) <- address screen-on-stack
+298   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+299   #
+300   var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth
+301   check-screen-row          screen,               2/y, "(b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end"
+302   check-screen-row-in-color screen, 0xf/fg=white, 2/y, "(   ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end: matching paren"
+303 }
+304 
+305 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2 {
+306   # setup: gs = "a((b))"
+307   var gs-storage: grapheme-stack
+308   var gs/edi: (addr grapheme-stack) <- address gs-storage
+309   initialize-grapheme-stack gs, 0x10
+310   var g/eax: grapheme <- copy 0x61/a
+311   push-grapheme-stack gs, g
+312   g <- copy 0x28/open-paren
+313   push-grapheme-stack gs, g
+314   g <- copy 0x28/open-paren
+315   push-grapheme-stack gs, g
+316   g <- copy 0x62/b
+317   push-grapheme-stack gs, g
+318   g <- copy 0x29/close-paren
+319   push-grapheme-stack gs, g
+320   g <- copy 0x29/close-paren
+321   push-grapheme-stack gs, g
+322   # setup: screen
+323   var screen-on-stack: screen
+324   var screen/esi: (addr screen) <- address screen-on-stack
+325   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+326   #
+327   var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 1/open-paren-depth
+328   check-screen-row          screen,               2/y, "a((b)) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2"
+329   check-screen-row-in-color screen, 0xf/fg=white, 2/y, " (     ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-with-close-paren-at-end-2: matching paren"
+330 }
+331 
+332 fn test-render-grapheme-stack-while-highlighting-matching-open-paren {
+333   # setup: gs = "(b"
+334   var gs-storage: grapheme-stack
+335   var gs/edi: (addr grapheme-stack) <- address gs-storage
+336   initialize-grapheme-stack gs, 5
+337   var g/eax: grapheme <- copy 0x28/open-paren
+338   push-grapheme-stack gs, g
+339   g <- copy 0x62/b
+340   push-grapheme-stack gs, g
+341   # setup: screen
+342   var screen-on-stack: screen
+343   var screen/esi: (addr screen) <- address screen-on-stack
+344   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+345   #
+346   var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth
+347   check-screen-row          screen,               2/y, "(b ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren"
+348   check-screen-row-in-color screen, 0xf/fg=white, 2/y, "(  ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren: matching paren"
+349 }
+350 
+351 fn test-render-grapheme-stack-while-highlighting-matching-open-paren-2 {
+352   # setup: gs = "a((b)"
+353   var gs-storage: grapheme-stack
+354   var gs/edi: (addr grapheme-stack) <- address gs-storage
+355   initialize-grapheme-stack gs, 0x10
+356   var g/eax: grapheme <- copy 0x61/a
+357   push-grapheme-stack gs, g
+358   g <- copy 0x28/open-paren
+359   push-grapheme-stack gs, g
+360   g <- copy 0x28/open-paren
+361   push-grapheme-stack gs, g
+362   g <- copy 0x62/b
+363   push-grapheme-stack gs, g
+364   g <- copy 0x29/close-paren
+365   push-grapheme-stack gs, g
+366   # setup: screen
+367   var screen-on-stack: screen
+368   var screen/esi: (addr screen) <- address screen-on-stack
+369   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+370   #
+371   var x/eax: int <- render-stack-from-bottom screen, gs, 0/x, 2/y, 1/highlight-matching-open-paren, 0/open-paren-depth
+372   check-screen-row          screen,               2/y, "a((b) ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2"
+373   check-screen-row-in-color screen, 0xf/fg=white, 2/y, " (    ", "F - test-render-grapheme-stack-while-highlighting-matching-open-paren-2: matching paren"
+374 }
+375 
+376 # return the index of the matching close-paren of the grapheme at cursor (top of stack)
+377 # or top index if there's no matching close-paren
+378 fn get-matching-close-paren-index _self: (addr grapheme-stack), render-cursor?: boolean -> _/edx: int {
+379   var self/esi: (addr grapheme-stack) <- copy _self
+380   var top-addr/edx: (addr int) <- get self, top
+381   # if not rendering cursor, return
+382   compare render-cursor?, 0/false
+383   {
+384     break-if-!=
+385     return *top-addr
+386   }
+387   var data-ah/eax: (addr handle array grapheme) <- get self, data
+388   var data/eax: (addr array grapheme) <- lookup *data-ah
+389   var i/ecx: int <- copy *top-addr
+390   # if stack is empty, return
+391   compare i, 0
+392   {
+393     break-if->
+394     return *top-addr
+395   }
+396   # if cursor is not '(' return
+397   i <- decrement
+398   var g/esi: (addr grapheme) <- index data, i
+399   compare *g, 0x28/open-paren
+400   {
+401     break-if-=
+402     return *top-addr
+403   }
+404   # otherwise scan to matching paren
+405   var paren-count/ebx: int <- copy 1
+406   i <- decrement
+407   {
+408     compare i, 0
+409     break-if-<
+410     var g/esi: (addr grapheme) <- index data, i
+411     compare *g, 0x28/open-paren
+412     {
+413       break-if-!=
+414       paren-count <- increment
+415     }
+416     compare *g, 0x29/close-paren
+417     {
+418       break-if-!=
+419       compare paren-count, 1
+420       {
+421         break-if-!=
+422         return i
+423       }
+424       paren-count <- decrement
+425     }
+426     i <- decrement
+427     loop
+428   }
+429   return *top-addr
+430 }
+431 
+432 # return the index of the first open-paren at the given depth
+433 # or top index if there's no matching close-paren
+434 fn get-matching-open-paren-index _self: (addr grapheme-stack), control: boolean, depth: int -> _/edx: int {
+435   var self/esi: (addr grapheme-stack) <- copy _self
+436   var top-addr/edx: (addr int) <- get self, top
+437   # if not rendering cursor, return
+438   compare control, 0/false
+439   {
+440     break-if-!=
+441     return *top-addr
+442   }
+443   var data-ah/eax: (addr handle array grapheme) <- get self, data
+444   var data/eax: (addr array grapheme) <- lookup *data-ah
+445   var i/ecx: int <- copy *top-addr
+446   # if stack is empty, return
+447   compare i, 0
+448   {
+449     break-if->
+450     return *top-addr
+451   }
+452   # scan to matching open paren
+453   var paren-count/ebx: int <- copy 0
+454   i <- decrement
+455   {
+456     compare i, 0
+457     break-if-<
+458     var g/esi: (addr grapheme) <- index data, i
+459     compare *g, 0x29/close-paren
+460     {
+461       break-if-!=
+462       paren-count <- increment
+463     }
+464     compare *g, 0x28/open-paren
+465     {
+466       break-if-!=
+467       compare paren-count, depth
+468       {
+469         break-if-!=
+470         return i
+471       }
+472       paren-count <- decrement
+473     }
+474     i <- decrement
+475     loop
+476   }
+477   return *top-addr
+478 }
+479 
+480 # compare from bottom
+481 # beware: modifies 'stream', which must be disposed of after a false result
+482 fn prefix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
+483   var self/esi: (addr grapheme-stack) <- copy _self
+484   var data-ah/edi: (addr handle array grapheme) <- get self, data
+485   var _data/eax: (addr array grapheme) <- lookup *data-ah
+486   var data/edi: (addr array grapheme) <- copy _data
+487   var top-addr/ecx: (addr int) <- get self, top
+488   var i/ebx: int <- copy 0
+489   {
+490     compare i, *top-addr
+491     break-if->=
+492     # if curr != expected, return false
+493     {
+494       var curr-a/edx: (addr grapheme) <- index data, i
+495       var expected/eax: grapheme <- read-grapheme s
+496       {
+497         compare expected, *curr-a
+498         break-if-=
+499         return 0/false
+500       }
+501     }
+502     i <- increment
+503     loop
+504   }
+505   return 1   # true
+506 }
+507 
+508 # compare from bottom
+509 # beware: modifies 'stream', which must be disposed of after a false result
+510 fn suffix-match? _self: (addr grapheme-stack), s: (addr stream byte) -> _/eax: boolean {
+511   var self/esi: (addr grapheme-stack) <- copy _self
+512   var data-ah/edi: (addr handle array grapheme) <- get self, data
+513   var _data/eax: (addr array grapheme) <- lookup *data-ah
+514   var data/edi: (addr array grapheme) <- copy _data
+515   var top-addr/eax: (addr int) <- get self, top
+516   var i/ebx: int <- copy *top-addr
+517   i <- decrement
+518   {
+519     compare i, 0
+520     break-if-<
+521     {
+522       var curr-a/edx: (addr grapheme) <- index data, i
+523       var expected/eax: grapheme <- read-grapheme s
+524       # if curr != expected, return false
+525       {
+526         compare expected, *curr-a
+527         break-if-=
+528         return 0/false
+529       }
+530     }
+531     i <- decrement
+532     loop
+533   }
+534   return 1   # true
+535 }
+536 
+537 fn grapheme-stack-is-decimal-integer? _self: (addr grapheme-stack) -> _/eax: boolean {
+538   var self/esi: (addr grapheme-stack) <- copy _self
+539   var data-ah/eax: (addr handle array grapheme) <- get self, data
+540   var _data/eax: (addr array grapheme) <- lookup *data-ah
+541   var data/edx: (addr array grapheme) <- copy _data
+542   var top-addr/ecx: (addr int) <- get self, top
+543   var i/ebx: int <- copy 0
+544   var result/eax: boolean <- copy 1/true
+545   $grapheme-stack-is-integer?:loop: {
+546     compare i, *top-addr
+547     break-if->=
+548     var g/edx: (addr grapheme) <- index data, i
+549     result <- decimal-digit? *g
+550     compare result, 0/false
+551     break-if-=
+552     i <- increment
+553     loop
+554   }
+555   return result
+556 }
+
+ + + diff --git a/html/514gap-buffer.mu.html b/html/514gap-buffer.mu.html new file mode 100644 index 00000000..f0cac176 --- /dev/null +++ b/html/514gap-buffer.mu.html @@ -0,0 +1,1538 @@ + + + + +Mu - 514gap-buffer.mu + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/514gap-buffer.mu +
+   1 # primitive for editing text
+   2 
+   3 type gap-buffer {
+   4   left: grapheme-stack
+   5   right: grapheme-stack
+   6   # some fields for scanning incrementally through a gap-buffer
+   7   left-read-index: int
+   8   right-read-index: int
+   9 }
+  10 
+  11 fn initialize-gap-buffer _self: (addr gap-buffer), capacity: int {
+  12   var self/esi: (addr gap-buffer) <- copy _self
+  13   var left/eax: (addr grapheme-stack) <- get self, left
+  14   initialize-grapheme-stack left, capacity
+  15   var right/eax: (addr grapheme-stack) <- get self, right
+  16   initialize-grapheme-stack right, capacity
+  17 }
+  18 
+  19 fn clear-gap-buffer _self: (addr gap-buffer) {
+  20   var self/esi: (addr gap-buffer) <- copy _self
+  21   var left/eax: (addr grapheme-stack) <- get self, left
+  22   clear-grapheme-stack left
+  23   var right/eax: (addr grapheme-stack) <- get self, right
+  24   clear-grapheme-stack right
+  25 }
+  26 
+  27 fn gap-buffer-empty? _self: (addr gap-buffer) -> _/eax: boolean {
+  28   var self/esi: (addr gap-buffer) <- copy _self
+  29   # if !empty?(left) return false
+  30   {
+  31     var left/eax: (addr grapheme-stack) <- get self, left
+  32     var result/eax: boolean <- grapheme-stack-empty? left
+  33     compare result, 0/false
+  34     break-if-!=
+  35     return 0/false
+  36   }
+  37   # return empty?(right)
+  38   var left/eax: (addr grapheme-stack) <- get self, left
+  39   var result/eax: boolean <- grapheme-stack-empty? left
+  40   return result
+  41 }
+  42 
+  43 fn gap-buffer-capacity _gap: (addr gap-buffer) -> _/edx: int {
+  44   var gap/esi: (addr gap-buffer) <- copy _gap
+  45   var left/eax: (addr grapheme-stack) <- get gap, left
+  46   var left-data-ah/eax: (addr handle array grapheme) <- get left, data
+  47   var left-data/eax: (addr array grapheme) <- lookup *left-data-ah
+  48   var result/eax: int <- length left-data
+  49   return result
+  50 }
+  51 
+  52 # just for tests
+  53 fn initialize-gap-buffer-with self: (addr gap-buffer), keys: (addr array byte) {
+  54   initialize-gap-buffer self, 0x40/capacity
+  55   var input-stream-storage: (stream byte 0x40/capacity)
+  56   var input-stream/ecx: (addr stream byte) <- address input-stream-storage
+  57   write input-stream, keys
+  58   {
+  59     var done?/eax: boolean <- stream-empty? input-stream
+  60     compare done?, 0/false
+  61     break-if-!=
+  62     var g/eax: grapheme <- read-grapheme input-stream
+  63     add-grapheme-at-gap self, g
+  64     loop
+  65   }
+  66 }
+  67 
+  68 fn load-gap-buffer-from-stream self: (addr gap-buffer), in: (addr stream byte) {
+  69   rewind-stream in
+  70   {
+  71     var done?/eax: boolean <- stream-empty? in
+  72     compare done?, 0/false
+  73     break-if-!=
+  74     var key/eax: byte <- read-byte in
+  75     compare key, 0/null
+  76     break-if-=
+  77     var g/eax: grapheme <- copy key
+  78     edit-gap-buffer self, g
+  79     loop
+  80   }
+  81 }
+  82 
+  83 fn emit-gap-buffer self: (addr gap-buffer), out: (addr stream byte) {
+  84   clear-stream out
+  85   append-gap-buffer self, out
+  86 }
+  87 
+  88 fn append-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) {
+  89   var self/esi: (addr gap-buffer) <- copy _self
+  90   var left/eax: (addr grapheme-stack) <- get self, left
+  91   emit-stack-from-bottom left, out
+  92   var right/eax: (addr grapheme-stack) <- get self, right
+  93   emit-stack-from-top right, out
+  94 }
+  95 
+  96 # dump stack from bottom to top
+  97 fn emit-stack-from-bottom _self: (addr grapheme-stack), out: (addr stream byte) {
+  98   var self/esi: (addr grapheme-stack) <- copy _self
+  99   var data-ah/edi: (addr handle array grapheme) <- get self, data
+ 100   var _data/eax: (addr array grapheme) <- lookup *data-ah
+ 101   var data/edi: (addr array grapheme) <- copy _data
+ 102   var top-addr/ecx: (addr int) <- get self, top
+ 103   var i/eax: int <- copy 0
+ 104   {
+ 105     compare i, *top-addr
+ 106     break-if->=
+ 107     var g/edx: (addr grapheme) <- index data, i
+ 108     write-grapheme out, *g
+ 109     i <- increment
+ 110     loop
+ 111   }
+ 112 }
+ 113 
+ 114 # dump stack from top to bottom
+ 115 fn emit-stack-from-top _self: (addr grapheme-stack), out: (addr stream byte) {
+ 116   var self/esi: (addr grapheme-stack) <- copy _self
+ 117   var data-ah/edi: (addr handle array grapheme) <- get self, data
+ 118   var _data/eax: (addr array grapheme) <- lookup *data-ah
+ 119   var data/edi: (addr array grapheme) <- copy _data
+ 120   var top-addr/ecx: (addr int) <- get self, top
+ 121   var i/eax: int <- copy *top-addr
+ 122   i <- decrement
+ 123   {
+ 124     compare i, 0
+ 125     break-if-<
+ 126     var g/edx: (addr grapheme) <- index data, i
+ 127     write-grapheme out, *g
+ 128     i <- decrement
+ 129     loop
+ 130   }
+ 131 }
+ 132 
+ 133 fn word-at-gap _self: (addr gap-buffer), out: (addr stream byte) {
+ 134   var self/esi: (addr gap-buffer) <- copy _self
+ 135   clear-stream out
+ 136   {
+ 137     var g/eax: grapheme <- grapheme-at-gap self
+ 138     var at-word?/eax: boolean <- is-ascii-word-grapheme? g
+ 139     compare at-word?, 0/false
+ 140     break-if-!=
+ 141     return
+ 142   }
+ 143   var left/ecx: (addr grapheme-stack) <- get self, left
+ 144   var left-index/eax: int <- top-most-word left
+ 145   emit-stack-from-index left, left-index, out
+ 146   var right/ecx: (addr grapheme-stack) <- get self, right
+ 147   var right-index/eax: int <- top-most-word right
+ 148   emit-stack-to-index right, right-index, out
+ 149 }
+ 150 
+ 151 fn test-word-at-gap-single-word-with-gap-at-end {
+ 152   var _g: gap-buffer
+ 153   var g/esi: (addr gap-buffer) <- address _g
+ 154   initialize-gap-buffer-with g, "abc"
+ 155   # gap is at end (right is empty)
+ 156   var out-storage: (stream byte 0x10)
+ 157   var out/eax: (addr stream byte) <- address out-storage
+ 158   word-at-gap g, out
+ 159   check-stream-equal out, "abc", "F - test-word-at-gap-single-word-with-gap-at-end"
+ 160 }
+ 161 
+ 162 fn test-word-at-gap-single-word-with-gap-at-start {
+ 163   var _g: gap-buffer
+ 164   var g/esi: (addr gap-buffer) <- address _g
+ 165   initialize-gap-buffer-with g, "abc"
+ 166   gap-to-start g
+ 167   #
+ 168   var out-storage: (stream byte 0x10)
+ 169   var out/eax: (addr stream byte) <- address out-storage
+ 170   word-at-gap g, out
+ 171   check-stream-equal out, "abc", "F - test-word-at-gap-single-word-with-gap-at-start"
+ 172 }
+ 173 
+ 174 fn test-word-at-gap-multiple-words-with-gap-at-non-word-grapheme-at-end {
+ 175   var _g: gap-buffer
+ 176   var g/esi: (addr gap-buffer) <- address _g
+ 177   initialize-gap-buffer-with g, "abc "
+ 178   # gap is at end (right is empty)
+ 179   var out-storage: (stream byte 0x10)
+ 180   var out/eax: (addr stream byte) <- address out-storage
+ 181   word-at-gap g, out
+ 182   check-stream-equal out, "", "F - test-word-at-gap-multiple-words-with-gap-at-non-word-grapheme-at-end"
+ 183 }
+ 184 
+ 185 fn test-word-at-gap-multiple-words-with-gap-at-non-word-grapheme-at-start {
+ 186   var _g: gap-buffer
+ 187   var g/esi: (addr gap-buffer) <- address _g
+ 188   initialize-gap-buffer-with g, " abc"
+ 189   gap-to-start g
+ 190   #
+ 191   var out-storage: (stream byte 0x10)
+ 192   var out/eax: (addr stream byte) <- address out-storage
+ 193   word-at-gap g, out
+ 194   check-stream-equal out, "", "F - test-word-at-gap-multiple-words-with-gap-at-non-word-grapheme-at-start"
+ 195 }
+ 196 
+ 197 fn test-word-at-gap-multiple-words-with-gap-at-end {
+ 198   var _g: gap-buffer
+ 199   var g/esi: (addr gap-buffer) <- address _g
+ 200   initialize-gap-buffer-with g, "a bc d"
+ 201   # gap is at end (right is empty)
+ 202   var out-storage: (stream byte 0x10)
+ 203   var out/eax: (addr stream byte) <- address out-storage
+ 204   word-at-gap g, out
+ 205   check-stream-equal out, "d", "F - test-word-at-gap-multiple-words-with-gap-at-end"
+ 206 }
+ 207 
+ 208 fn test-word-at-gap-multiple-words-with-gap-at-initial-word {
+ 209   var _g: gap-buffer
+ 210   var g/esi: (addr gap-buffer) <- address _g
+ 211   initialize-gap-buffer-with g, "a bc d"
+ 212   gap-to-start g
+ 213   #
+ 214   var out-storage: (stream byte 0x10)
+ 215   var out/eax: (addr stream byte) <- address out-storage
+ 216   word-at-gap g, out
+ 217   check-stream-equal out, "a", "F - test-word-at-gap-multiple-words-with-gap-at-initial-word"
+ 218 }
+ 219 
+ 220 fn test-word-at-gap-multiple-words-with-gap-at-final-word {
+ 221   var _g: gap-buffer
+ 222   var g/esi: (addr gap-buffer) <- address _g
+ 223   initialize-gap-buffer-with g, "a bc d"
+ 224   var dummy/eax: grapheme <- gap-left g
+ 225   # gap is at final word
+ 226   var out-storage: (stream byte 0x10)
+ 227   var out/eax: (addr stream byte) <- address out-storage
+ 228   word-at-gap g, out
+ 229   check-stream-equal out, "d", "F - test-word-at-gap-multiple-words-with-gap-at-final-word"
+ 230 }
+ 231 
+ 232 fn test-word-at-gap-multiple-words-with-gap-at-final-non-word {
+ 233   var _g: gap-buffer
+ 234   var g/esi: (addr gap-buffer) <- address _g
+ 235   initialize-gap-buffer-with g, "abc "
+ 236   var dummy/eax: grapheme <- gap-left g
+ 237   # gap is at final word
+ 238   var out-storage: (stream byte 0x10)
+ 239   var out/eax: (addr stream byte) <- address out-storage
+ 240   word-at-gap g, out
+ 241   check-stream-equal out, "", "F - test-word-at-gap-multiple-words-with-gap-at-final-non-word"
+ 242 }
+ 243 
+ 244 fn grapheme-at-gap _self: (addr gap-buffer) -> _/eax: grapheme {
+ 245   # send top of right most of the time
+ 246   var self/esi: (addr gap-buffer) <- copy _self
+ 247   var right/edi: (addr grapheme-stack) <- get self, right
+ 248   var data-ah/eax: (addr handle array grapheme) <- get right, data
+ 249   var data/eax: (addr array grapheme) <- lookup *data-ah
+ 250   var top-addr/ecx: (addr int) <- get right, top
+ 251   {
+ 252     compare *top-addr, 0
+ 253     break-if-<=
+ 254     var top/ecx: int <- copy *top-addr
+ 255     top <- decrement
+ 256     var result/eax: (addr grapheme) <- index data, top
+ 257     return *result
+ 258   }
+ 259   # send top of left only if right is empty
+ 260   var left/edi: (addr grapheme-stack) <- get self, left
+ 261   var data-ah/eax: (addr handle array grapheme) <- get left, data
+ 262   var data/eax: (addr array grapheme) <- lookup *data-ah
+ 263   var top-addr/ecx: (addr int) <- get left, top
+ 264   {
+ 265     compare *top-addr, 0
+ 266     break-if-<=
+ 267     var top/ecx: int <- copy *top-addr
+ 268     top <- decrement
+ 269     var result/eax: (addr grapheme) <- index data, top
+ 270     return *result
+ 271   }
+ 272   # send null if everything is empty
+ 273   return 0
+ 274 }
+ 275 
+ 276 fn top-most-word _self: (addr grapheme-stack) -> _/eax: int {
+ 277   var self/esi: (addr grapheme-stack) <- copy _self
+ 278   var data-ah/edi: (addr handle array grapheme) <- get self, data
+ 279   var _data/eax: (addr array grapheme) <- lookup *data-ah
+ 280   var data/edi: (addr array grapheme) <- copy _data
+ 281   var top-addr/ecx: (addr int) <- get self, top
+ 282   var i/ebx: int <- copy *top-addr
+ 283   i <- decrement
+ 284   {
+ 285     compare i, 0
+ 286     break-if-<
+ 287     var g/edx: (addr grapheme) <- index data, i
+ 288     var is-word?/eax: boolean <- is-ascii-word-grapheme? *g
+ 289     compare is-word?, 0/false
+ 290     break-if-=
+ 291     i <- decrement
+ 292     loop
+ 293   }
+ 294   i <- increment
+ 295   return i
+ 296 }
+ 297 
+ 298 fn emit-stack-from-index _self: (addr grapheme-stack), start: int, out: (addr stream byte) {
+ 299   var self/esi: (addr grapheme-stack) <- copy _self
+ 300   var data-ah/edi: (addr handle array grapheme) <- get self, data
+ 301   var _data/eax: (addr array grapheme) <- lookup *data-ah
+ 302   var data/edi: (addr array grapheme) <- copy _data
+ 303   var top-addr/ecx: (addr int) <- get self, top
+ 304   var i/eax: int <- copy start
+ 305   {
+ 306     compare i, *top-addr
+ 307     break-if->=
+ 308     var g/edx: (addr grapheme) <- index data, i
+ 309     write-grapheme out, *g
+ 310     i <- increment
+ 311     loop
+ 312   }
+ 313 }
+ 314 
+ 315 fn emit-stack-to-index _self: (addr grapheme-stack), end: int, out: (addr stream byte) {
+ 316   var self/esi: (addr grapheme-stack) <- copy _self
+ 317   var data-ah/edi: (addr handle array grapheme) <- get self, data
+ 318   var _data/eax: (addr array grapheme) <- lookup *data-ah
+ 319   var data/edi: (addr array grapheme) <- copy _data
+ 320   var top-addr/ecx: (addr int) <- get self, top
+ 321   var i/eax: int <- copy *top-addr
+ 322   i <- decrement
+ 323   {
+ 324     compare i, 0
+ 325     break-if-<
+ 326     compare i, end
+ 327     break-if-<
+ 328     var g/edx: (addr grapheme) <- index data, i
+ 329     write-grapheme out, *g
+ 330     i <- decrement
+ 331     loop
+ 332   }
+ 333 }
+ 334 
+ 335 fn is-ascii-word-grapheme? g: grapheme -> _/eax: boolean {
+ 336   compare g, 0x21/!
+ 337   {
+ 338     break-if-!=
+ 339     return 1/true
+ 340   }
+ 341   compare g, 0x30/0
+ 342   {
+ 343     break-if->=
+ 344     return 0/false
+ 345   }
+ 346   compare g, 0x39/9
+ 347   {
+ 348     break-if->
+ 349     return 1/true
+ 350   }
+ 351   compare g, 0x3f/?
+ 352   {
+ 353     break-if-!=
+ 354     return 1/true
+ 355   }
+ 356   compare g, 0x41/A
+ 357   {
+ 358     break-if->=
+ 359     return 0/false
+ 360   }
+ 361   compare g, 0x5a/Z
+ 362   {
+ 363     break-if->
+ 364     return 1/true
+ 365   }
+ 366   compare g, 0x5f/_
+ 367   {
+ 368     break-if-!=
+ 369     return 1/true
+ 370   }
+ 371   compare g, 0x61/a
+ 372   {
+ 373     break-if->=
+ 374     return 0/false
+ 375   }
+ 376   compare g, 0x7a/z
+ 377   {
+ 378     break-if->
+ 379     return 1/true
+ 380   }
+ 381   return 0/false
+ 382 }
+ 383 
+ 384 # We implicitly render everything editable in a single color, and assume the
+ 385 # cursor is a single other color.
+ 386 fn render-gap-buffer-wrapping-right-then-down screen: (addr screen), _gap: (addr gap-buffer), xmin: int, ymin: int, xmax: int, ymax: int, render-cursor?: boolean, color: int, background-color: int -> _/eax: int, _/ecx: int {
+ 387   var gap/esi: (addr gap-buffer) <- copy _gap
+ 388   var left/edx: (addr grapheme-stack) <- get gap, left
+ 389   var highlight-matching-open-paren?/ebx: boolean <- copy 0/false
+ 390   var matching-open-paren-depth/edi: int <- copy 0
+ 391   highlight-matching-open-paren?, matching-open-paren-depth <- highlight-matching-open-paren? gap, render-cursor?
+ 392   var x2/eax: int <- copy 0
+ 393   var y2/ecx: int <- copy 0
+ 394   x2, y2 <- render-stack-from-bottom-wrapping-right-then-down screen, left, xmin, ymin, xmax, ymax, xmin, ymin, highlight-matching-open-paren?, matching-open-paren-depth, color, background-color
+ 395   var right/edx: (addr grapheme-stack) <- get gap, right
+ 396   x2, y2 <- render-stack-from-top-wrapping-right-then-down screen, right, xmin, ymin, xmax, ymax, x2, y2, render-cursor?, color, background-color
+ 397   # decide whether we still need to print a cursor
+ 398   var fg/edi: int <- copy color
+ 399   var bg/ebx: int <- copy background-color
+ 400   compare render-cursor?, 0/false
+ 401   {
+ 402     break-if-=
+ 403     # if the right side is empty, grapheme stack didn't print the cursor
+ 404     var empty?/eax: boolean <- grapheme-stack-empty? right
+ 405     compare empty?, 0/false
+ 406     break-if-=
+ 407     # swap foreground and background
+ 408     fg <- copy background-color
+ 409     bg <- copy color
+ 410   }
+ 411   # print a grapheme either way so that cursor position doesn't affect printed width
+ 412   var space/edx: grapheme <- copy 0x20
+ 413   x2, y2 <- render-grapheme screen, space, xmin, ymin, xmax, ymax, x2, y2, fg, bg
+ 414   return x2, y2
+ 415 }
+ 416 
+ 417 fn render-gap-buffer screen: (addr screen), gap: (addr gap-buffer), x: int, y: int, render-cursor?: boolean, color: int, background-color: int -> _/eax: int {
+ 418   var _width/eax: int <- copy 0
+ 419   var _height/ecx: int <- copy 0
+ 420   _width, _height <- screen-size screen
+ 421   var width/edx: int <- copy _width
+ 422   var height/ebx: int <- copy _height
+ 423   var x2/eax: int <- copy 0
+ 424   var y2/ecx: int <- copy 0
+ 425   x2, y2 <- render-gap-buffer-wrapping-right-then-down screen, gap, x, y, width, height, render-cursor?, color, background-color
+ 426   return x2  # y2? yolo
+ 427 }
+ 428 
+ 429 fn gap-buffer-length _gap: (addr gap-buffer) -> _/eax: int {
+ 430   var gap/esi: (addr gap-buffer) <- copy _gap
+ 431   var left/eax: (addr grapheme-stack) <- get gap, left
+ 432   var tmp/eax: (addr int) <- get left, top
+ 433   var left-length/ecx: int <- copy *tmp
+ 434   var right/esi: (addr grapheme-stack) <- get gap, right
+ 435   tmp <- get right, top
+ 436   var result/eax: int <- copy *tmp
+ 437   result <- add left-length
+ 438   return result
+ 439 }
+ 440 
+ 441 fn add-grapheme-at-gap _self: (addr gap-buffer), g: grapheme {
+ 442   var self/esi: (addr gap-buffer) <- copy _self
+ 443   var left/eax: (addr grapheme-stack) <- get self, left
+ 444   push-grapheme-stack left, g
+ 445 }
+ 446 
+ 447 fn add-code-point-at-gap self: (addr gap-buffer), c: code-point {
+ 448   var g/eax: grapheme <- copy c
+ 449   add-grapheme-at-gap self, g
+ 450 }
+ 451 
+ 452 fn gap-to-start self: (addr gap-buffer) {
+ 453   {
+ 454     var curr/eax: grapheme <- gap-left self
+ 455     compare curr, -1
+ 456     loop-if-!=
+ 457   }
+ 458 }
+ 459 
+ 460 fn gap-to-end self: (addr gap-buffer) {
+ 461   {
+ 462     var curr/eax: grapheme <- gap-right self
+ 463     compare curr, -1
+ 464     loop-if-!=
+ 465   }
+ 466 }
+ 467 
+ 468 fn gap-at-start? _self: (addr gap-buffer) -> _/eax: boolean {
+ 469   var self/esi: (addr gap-buffer) <- copy _self
+ 470   var left/eax: (addr grapheme-stack) <- get self, left
+ 471   var result/eax: boolean <- grapheme-stack-empty? left
+ 472   return result
+ 473 }
+ 474 
+ 475 fn gap-at-end? _self: (addr gap-buffer) -> _/eax: boolean {
+ 476   var self/esi: (addr gap-buffer) <- copy _self
+ 477   var right/eax: (addr grapheme-stack) <- get self, right
+ 478   var result/eax: boolean <- grapheme-stack-empty? right
+ 479   return result
+ 480 }
+ 481 
+ 482 fn gap-right _self: (addr gap-buffer) -> _/eax: grapheme {
+ 483   var self/esi: (addr gap-buffer) <- copy _self
+ 484   var g/eax: grapheme <- copy 0
+ 485   var right/ecx: (addr grapheme-stack) <- get self, right
+ 486   g <- pop-grapheme-stack right
+ 487   compare g, -1
+ 488   {
+ 489     break-if-=
+ 490     var left/ecx: (addr grapheme-stack) <- get self, left
+ 491     push-grapheme-stack left, g
+ 492   }
+ 493   return g
+ 494 }
+ 495 
+ 496 fn gap-left _self: (addr gap-buffer) -> _/eax: grapheme {
+ 497   var self/esi: (addr gap-buffer) <- copy _self
+ 498   var g/eax: grapheme <- copy 0
+ 499   {
+ 500     var left/ecx: (addr grapheme-stack) <- get self, left
+ 501     g <- pop-grapheme-stack left
+ 502   }
+ 503   compare g, -1
+ 504   {
+ 505     break-if-=
+ 506     var right/ecx: (addr grapheme-stack) <- get self, right
+ 507     push-grapheme-stack right, g
+ 508   }
+ 509   return g
+ 510 }
+ 511 
+ 512 fn index-of-gap _self: (addr gap-buffer) -> _/eax: int {
+ 513   var self/eax: (addr gap-buffer) <- copy _self
+ 514   var left/eax: (addr grapheme-stack) <- get self, left
+ 515   var top-addr/eax: (addr int) <- get left, top
+ 516   var result/eax: int <- copy *top-addr
+ 517   return result
+ 518 }
+ 519 
+ 520 fn first-grapheme-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
+ 521   var self/esi: (addr gap-buffer) <- copy _self
+ 522   # try to read from left
+ 523   var left/eax: (addr grapheme-stack) <- get self, left
+ 524   var top-addr/ecx: (addr int) <- get left, top
+ 525   compare *top-addr, 0
+ 526   {
+ 527     break-if-<=
+ 528     var data-ah/eax: (addr handle array grapheme) <- get left, data
+ 529     var data/eax: (addr array grapheme) <- lookup *data-ah
+ 530     var result-addr/eax: (addr grapheme) <- index data, 0
+ 531     return *result-addr
+ 532   }
+ 533   # try to read from right
+ 534   var right/eax: (addr grapheme-stack) <- get self, right
+ 535   top-addr <- get right, top
+ 536   compare *top-addr, 0
+ 537   {
+ 538     break-if-<=
+ 539     var data-ah/eax: (addr handle array grapheme) <- get right, data
+ 540     var data/eax: (addr array grapheme) <- lookup *data-ah
+ 541     var top/ecx: int <- copy *top-addr
+ 542     top <- decrement
+ 543     var result-addr/eax: (addr grapheme) <- index data, top
+ 544     return *result-addr
+ 545   }
+ 546   # give up
+ 547   return -1
+ 548 }
+ 549 
+ 550 fn grapheme-before-cursor-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
+ 551   var self/esi: (addr gap-buffer) <- copy _self
+ 552   # try to read from left
+ 553   var left/ecx: (addr grapheme-stack) <- get self, left
+ 554   var top-addr/edx: (addr int) <- get left, top
+ 555   compare *top-addr, 0
+ 556   {
+ 557     break-if-<=
+ 558     var result/eax: grapheme <- pop-grapheme-stack left
+ 559     push-grapheme-stack left, result
+ 560     return result
+ 561   }
+ 562   # give up
+ 563   return -1
+ 564 }
+ 565 
+ 566 fn delete-before-gap _self: (addr gap-buffer) {
+ 567   var self/eax: (addr gap-buffer) <- copy _self
+ 568   var left/eax: (addr grapheme-stack) <- get self, left
+ 569   var dummy/eax: grapheme <- pop-grapheme-stack left
+ 570 }
+ 571 
+ 572 fn pop-after-gap _self: (addr gap-buffer) -> _/eax: grapheme {
+ 573   var self/eax: (addr gap-buffer) <- copy _self
+ 574   var right/eax: (addr grapheme-stack) <- get self, right
+ 575   var result/eax: grapheme <- pop-grapheme-stack right
+ 576   return result
+ 577 }
+ 578 
+ 579 fn gap-buffer-equal? _self: (addr gap-buffer), s: (addr array byte) -> _/eax: boolean {
+ 580   var self/esi: (addr gap-buffer) <- copy _self
+ 581   # complication: graphemes may be multiple bytes
+ 582   # so don't rely on length
+ 583   # instead turn the expected result into a stream and arrange to read from it in order
+ 584   var stream-storage: (stream byte 0x10/capacity)
+ 585   var expected-stream/ecx: (addr stream byte) <- address stream-storage
+ 586   write expected-stream, s
+ 587   # compare left
+ 588   var left/edx: (addr grapheme-stack) <- get self, left
+ 589   var result/eax: boolean <- prefix-match? left, expected-stream
+ 590   compare result, 0/false
+ 591   {
+ 592     break-if-!=
+ 593     return result
+ 594   }
+ 595   # compare right
+ 596   var right/edx: (addr grapheme-stack) <- get self, right
+ 597   result <- suffix-match? right, expected-stream
+ 598   compare result, 0/false
+ 599   {
+ 600     break-if-!=
+ 601     return result
+ 602   }
+ 603   # ensure there's nothing left over
+ 604   result <- stream-empty? expected-stream
+ 605   return result
+ 606 }
+ 607 
+ 608 fn test-gap-buffer-equal-from-end {
+ 609   var _g: gap-buffer
+ 610   var g/esi: (addr gap-buffer) <- address _g
+ 611   initialize-gap-buffer g, 0x10
+ 612   #
+ 613   add-code-point-at-gap g, 0x61/a
+ 614   add-code-point-at-gap g, 0x61/a
+ 615   add-code-point-at-gap g, 0x61/a
+ 616   # gap is at end (right is empty)
+ 617   var result/eax: boolean <- gap-buffer-equal? g, "aaa"
+ 618   check result, "F - test-gap-buffer-equal-from-end"
+ 619 }
+ 620 
+ 621 fn test-gap-buffer-equal-from-middle {
+ 622   var _g: gap-buffer
+ 623   var g/esi: (addr gap-buffer) <- address _g
+ 624   initialize-gap-buffer g, 0x10
+ 625   #
+ 626   add-code-point-at-gap g, 0x61/a
+ 627   add-code-point-at-gap g, 0x61/a
+ 628   add-code-point-at-gap g, 0x61/a
+ 629   var dummy/eax: grapheme <- gap-left g
+ 630   # gap is in the middle
+ 631   var result/eax: boolean <- gap-buffer-equal? g, "aaa"
+ 632   check result, "F - test-gap-buffer-equal-from-middle"
+ 633 }
+ 634 
+ 635 fn test-gap-buffer-equal-from-start {
+ 636   var _g: gap-buffer
+ 637   var g/esi: (addr gap-buffer) <- address _g
+ 638   initialize-gap-buffer g, 0x10
+ 639   #
+ 640   add-code-point-at-gap g, 0x61/a
+ 641   add-code-point-at-gap g, 0x61/a
+ 642   add-code-point-at-gap g, 0x61/a
+ 643   var dummy/eax: grapheme <- gap-left g
+ 644   dummy <- gap-left g
+ 645   dummy <- gap-left g
+ 646   # gap is at the start
+ 647   var result/eax: boolean <- gap-buffer-equal? g, "aaa"
+ 648   check result, "F - test-gap-buffer-equal-from-start"
+ 649 }
+ 650 
+ 651 fn test-gap-buffer-equal-fails {
+ 652   # g = "aaa"
+ 653   var _g: gap-buffer
+ 654   var g/esi: (addr gap-buffer) <- address _g
+ 655   initialize-gap-buffer g, 0x10
+ 656   add-code-point-at-gap g, 0x61/a
+ 657   add-code-point-at-gap g, 0x61/a
+ 658   add-code-point-at-gap g, 0x61/a
+ 659   #
+ 660   var result/eax: boolean <- gap-buffer-equal? g, "aa"
+ 661   check-not result, "F - test-gap-buffer-equal-fails"
+ 662 }
+ 663 
+ 664 fn gap-buffers-equal? self: (addr gap-buffer), g: (addr gap-buffer) -> _/eax: boolean {
+ 665   var tmp/eax: int <- gap-buffer-length self
+ 666   var len/ecx: int <- copy tmp
+ 667   var leng/eax: int <- gap-buffer-length g
+ 668   compare len, leng
+ 669   {
+ 670     break-if-=
+ 671     return 0/false
+ 672   }
+ 673   var i/edx: int <- copy 0
+ 674   {
+ 675     compare i, len
+ 676     break-if->=
+ 677     {
+ 678       var tmp/eax: grapheme <- gap-index self, i
+ 679       var curr/ecx: grapheme <- copy tmp
+ 680       var currg/eax: grapheme <- gap-index g, i
+ 681       compare curr, currg
+ 682       break-if-=
+ 683       return 0/false
+ 684     }
+ 685     i <- increment
+ 686     loop
+ 687   }
+ 688   return 1/true
+ 689 }
+ 690 
+ 691 fn gap-index _self: (addr gap-buffer), _n: int -> _/eax: grapheme {
+ 692   var self/esi: (addr gap-buffer) <- copy _self
+ 693   var n/ebx: int <- copy _n
+ 694   # if n < left->length, index into left
+ 695   var left/edi: (addr grapheme-stack) <- get self, left
+ 696   var left-len-a/edx: (addr int) <- get left, top
+ 697   compare n, *left-len-a
+ 698   {
+ 699     break-if->=
+ 700     var data-ah/eax: (addr handle array grapheme) <- get left, data
+ 701     var data/eax: (addr array grapheme) <- lookup *data-ah
+ 702     var result/eax: (addr grapheme) <- index data, n
+ 703     return *result
+ 704   }
+ 705   # shrink n
+ 706   n <- subtract *left-len-a
+ 707   # if n < right->length, index into right
+ 708   var right/edi: (addr grapheme-stack) <- get self, right
+ 709   var right-len-a/edx: (addr int) <- get right, top
+ 710   compare n, *right-len-a
+ 711   {
+ 712     break-if->=
+ 713     var data-ah/eax: (addr handle array grapheme) <- get right, data
+ 714     var data/eax: (addr array grapheme) <- lookup *data-ah
+ 715     # idx = right->len - n - 1
+ 716     var idx/ebx: int <- copy n
+ 717     idx <- subtract *right-len-a
+ 718     idx <- negate
+ 719     idx <- subtract 1
+ 720     var result/eax: (addr grapheme) <- index data, idx
+ 721     return *result
+ 722   }
+ 723   # error
+ 724   abort "gap-index: out of bounds"
+ 725   return 0
+ 726 }
+ 727 
+ 728 fn test-gap-buffers-equal? {
+ 729   var _a: gap-buffer
+ 730   var a/esi: (addr gap-buffer) <- address _a
+ 731   initialize-gap-buffer-with a, "abc"
+ 732   var _b: gap-buffer
+ 733   var b/edi: (addr gap-buffer) <- address _b
+ 734   initialize-gap-buffer-with b, "abc"
+ 735   var _c: gap-buffer
+ 736   var c/ebx: (addr gap-buffer) <- address _c
+ 737   initialize-gap-buffer-with c, "ab"
+ 738   var _d: gap-buffer
+ 739   var d/edx: (addr gap-buffer) <- address _d
+ 740   initialize-gap-buffer-with d, "abd"
+ 741   #
+ 742   var result/eax: boolean <- gap-buffers-equal? a, a
+ 743   check result, "F - test-gap-buffers-equal? - reflexive"
+ 744   result <- gap-buffers-equal? a, b
+ 745   check result, "F - test-gap-buffers-equal? - equal"
+ 746   # length not equal
+ 747   result <- gap-buffers-equal? a, c
+ 748   check-not result, "F - test-gap-buffers-equal? - not equal"
+ 749   # contents not equal
+ 750   result <- gap-buffers-equal? a, d
+ 751   check-not result, "F - test-gap-buffers-equal? - not equal 2"
+ 752   result <- gap-buffers-equal? d, a
+ 753   check-not result, "F - test-gap-buffers-equal? - not equal 3"
+ 754 }
+ 755 
+ 756 fn test-gap-buffer-index {
+ 757   var gap-storage: gap-buffer
+ 758   var gap/esi: (addr gap-buffer) <- address gap-storage
+ 759   initialize-gap-buffer-with gap, "abc"
+ 760   # gap is at end, all contents are in left
+ 761   var g/eax: grapheme <- gap-index gap, 0
+ 762   var x/ecx: int <- copy g
+ 763   check-ints-equal x, 0x61/a, "F - test-gap-index/left-1"
+ 764   var g/eax: grapheme <- gap-index gap, 1
+ 765   var x/ecx: int <- copy g
+ 766   check-ints-equal x, 0x62/b, "F - test-gap-index/left-2"
+ 767   var g/eax: grapheme <- gap-index gap, 2
+ 768   var x/ecx: int <- copy g
+ 769   check-ints-equal x, 0x63/c, "F - test-gap-index/left-3"
+ 770   # now check when everything is to the right
+ 771   gap-to-start gap
+ 772   rewind-gap-buffer gap
+ 773   var g/eax: grapheme <- gap-index gap, 0
+ 774   var x/ecx: int <- copy g
+ 775   check-ints-equal x, 0x61/a, "F - test-gap-index/right-1"
+ 776   var g/eax: grapheme <- gap-index gap, 1
+ 777   var x/ecx: int <- copy g
+ 778   check-ints-equal x, 0x62/b, "F - test-gap-index/right-2"
+ 779   var g/eax: grapheme <- gap-index gap, 2
+ 780   var x/ecx: int <- copy g
+ 781   check-ints-equal x, 0x63/c, "F - test-gap-index/right-3"
+ 782 }
+ 783 
+ 784 fn copy-gap-buffer _src-ah: (addr handle gap-buffer), _dest-ah: (addr handle gap-buffer) {
+ 785   # obtain src-a, dest-a
+ 786   var src-ah/eax: (addr handle gap-buffer) <- copy _src-ah
+ 787   var _src-a/eax: (addr gap-buffer) <- lookup *src-ah
+ 788   var src-a/esi: (addr gap-buffer) <- copy _src-a
+ 789   var dest-ah/eax: (addr handle gap-buffer) <- copy _dest-ah
+ 790   var _dest-a/eax: (addr gap-buffer) <- lookup *dest-ah
+ 791   var dest-a/edi: (addr gap-buffer) <- copy _dest-a
+ 792   # copy left grapheme-stack
+ 793   var src/ecx: (addr grapheme-stack) <- get src-a, left
+ 794   var dest/edx: (addr grapheme-stack) <- get dest-a, left
+ 795   copy-grapheme-stack src, dest
+ 796   # copy right grapheme-stack
+ 797   src <- get src-a, right
+ 798   dest <- get dest-a, right
+ 799   copy-grapheme-stack src, dest
+ 800 }
+ 801 
+ 802 fn gap-buffer-is-decimal-integer? _self: (addr gap-buffer) -> _/eax: boolean {
+ 803   var self/esi: (addr gap-buffer) <- copy _self
+ 804   var curr/ecx: (addr grapheme-stack) <- get self, left
+ 805   var result/eax: boolean <- grapheme-stack-is-decimal-integer? curr
+ 806   {
+ 807     compare result, 0/false
+ 808     break-if-=
+ 809     curr <- get self, right
+ 810     result <- grapheme-stack-is-decimal-integer? curr
+ 811   }
+ 812   return result
+ 813 }
+ 814 
+ 815 fn test-render-gap-buffer-without-cursor {
+ 816   # setup
+ 817   var gap-storage: gap-buffer
+ 818   var gap/esi: (addr gap-buffer) <- address gap-storage
+ 819   initialize-gap-buffer-with gap, "abc"
+ 820   # setup: screen
+ 821   var screen-on-stack: screen
+ 822   var screen/edi: (addr screen) <- address screen-on-stack
+ 823   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+ 824   #
+ 825   var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 0/no-cursor, 3/fg, 0xc5/bg=blue-bg
+ 826   check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-without-cursor"
+ 827   check-ints-equal x, 4, "F - test-render-gap-buffer-without-cursor: result"
+ 828                                                                 # abc
+ 829   check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, "    ", "F - test-render-gap-buffer-without-cursor: bg"
+ 830 }
+ 831 
+ 832 fn test-render-gap-buffer-with-cursor-at-end {
+ 833   # setup
+ 834   var gap-storage: gap-buffer
+ 835   var gap/esi: (addr gap-buffer) <- address gap-storage
+ 836   initialize-gap-buffer-with gap, "abc"
+ 837   gap-to-end gap
+ 838   # setup: screen
+ 839   var screen-on-stack: screen
+ 840   var screen/edi: (addr screen) <- address screen-on-stack
+ 841   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+ 842   #
+ 843   var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg
+ 844   check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-end"
+ 845   # we've drawn one extra grapheme for the cursor
+ 846   check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-end: result"
+ 847                                                                 # abc
+ 848   check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, "   |", "F - test-render-gap-buffer-with-cursor-at-end: bg"
+ 849 }
+ 850 
+ 851 fn test-render-gap-buffer-with-cursor-in-middle {
+ 852   # setup
+ 853   var gap-storage: gap-buffer
+ 854   var gap/esi: (addr gap-buffer) <- address gap-storage
+ 855   initialize-gap-buffer-with gap, "abc"
+ 856   gap-to-end gap
+ 857   var dummy/eax: grapheme <- gap-left gap
+ 858   # setup: screen
+ 859   var screen-on-stack: screen
+ 860   var screen/edi: (addr screen) <- address screen-on-stack
+ 861   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+ 862   #
+ 863   var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg
+ 864   check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-in-middle"
+ 865   check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-in-middle: result"
+ 866                                                                 # abc
+ 867   check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, "  | ", "F - test-render-gap-buffer-with-cursor-in-middle: bg"
+ 868 }
+ 869 
+ 870 fn test-render-gap-buffer-with-cursor-at-start {
+ 871   var gap-storage: gap-buffer
+ 872   var gap/esi: (addr gap-buffer) <- address gap-storage
+ 873   initialize-gap-buffer-with gap, "abc"
+ 874   gap-to-start gap
+ 875   # setup: screen
+ 876   var screen-on-stack: screen
+ 877   var screen/edi: (addr screen) <- address screen-on-stack
+ 878   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+ 879   #
+ 880   var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg
+ 881   check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-start"
+ 882   check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-start: result"
+ 883                                                                 # abc
+ 884   check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, "|   ", "F - test-render-gap-buffer-with-cursor-at-start: bg"
+ 885 }
+ 886 
+ 887 fn test-render-gap-buffer-highlight-matching-close-paren {
+ 888   var gap-storage: gap-buffer
+ 889   var gap/esi: (addr gap-buffer) <- address gap-storage
+ 890   initialize-gap-buffer-with gap, "(a)"
+ 891   gap-to-start gap
+ 892   # setup: screen
+ 893   var screen-on-stack: screen
+ 894   var screen/edi: (addr screen) <- address screen-on-stack
+ 895   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+ 896   #
+ 897   var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg
+ 898   check-screen-row                     screen, 0/y,                   "(a) ", "F - test-render-gap-buffer-highlight-matching-close-paren"
+ 899   check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-close-paren: result"
+ 900   check-background-color-in-screen-row screen, 3/bg=reverse,      0/y, "|   ", "F - test-render-gap-buffer-highlight-matching-close-paren: cursor"
+ 901   check-screen-row-in-color            screen, 0xf/fg=highlight, 0/y, "  ) ", "F - test-render-gap-buffer-highlight-matching-close-paren: matching paren"
+ 902 }
+ 903 
+ 904 fn test-render-gap-buffer-highlight-matching-open-paren {
+ 905   var gap-storage: gap-buffer
+ 906   var gap/esi: (addr gap-buffer) <- address gap-storage
+ 907   initialize-gap-buffer-with gap, "(a)"
+ 908   gap-to-end gap
+ 909   var dummy/eax: grapheme <- gap-left gap
+ 910   # setup: screen
+ 911   var screen-on-stack: screen
+ 912   var screen/edi: (addr screen) <- address screen-on-stack
+ 913   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+ 914   #
+ 915   var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg
+ 916   check-screen-row                     screen, 0/y,                   "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren"
+ 917   check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren: result"
+ 918   check-background-color-in-screen-row screen, 3/bg=reverse,      0/y, "  | ", "F - test-render-gap-buffer-highlight-matching-open-paren: cursor"
+ 919   check-screen-row-in-color            screen, 0xf/fg=highlight, 0/y, "(   ", "F - test-render-gap-buffer-highlight-matching-open-paren: matching paren"
+ 920 }
+ 921 
+ 922 fn test-render-gap-buffer-highlight-matching-open-paren-of-end {
+ 923   var gap-storage: gap-buffer
+ 924   var gap/esi: (addr gap-buffer) <- address gap-storage
+ 925   initialize-gap-buffer-with gap, "(a)"
+ 926   gap-to-end gap
+ 927   # setup: screen
+ 928   var screen-on-stack: screen
+ 929   var screen/edi: (addr screen) <- address screen-on-stack
+ 930   initialize-screen screen, 5, 4, 0/no-pixel-graphics
+ 931   #
+ 932   var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg
+ 933   check-screen-row                     screen, 0/y,                   "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end"
+ 934   check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: result"
+ 935   check-background-color-in-screen-row screen, 3/bg=reverse,      0/y, "   |", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: cursor"
+ 936   check-screen-row-in-color            screen, 0xf/fg=highlight, 0/y, "(   ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: matching paren"
+ 937 }
+ 938 
+ 939 # should I highlight a matching open paren? And if so, at what depth from top of left?
+ 940 # basically there are two cases to disambiguate here:
+ 941 #   Usually the cursor is at top of right. Highlight first '(' at depth 0 from top of left.
+ 942 #   If right is empty, match the ')' _before_ cursor. Highlight first '(' at depth _1_ from top of left.
+ 943 fn highlight-matching-open-paren? _gap: (addr gap-buffer), render-cursor?: boolean -> _/ebx: boolean, _/edi: int {
+ 944   # if not rendering cursor, return
+ 945   compare render-cursor?, 0/false
+ 946   {
+ 947     break-if-!=
+ 948     return 0/false, 0
+ 949   }
+ 950   var gap/esi: (addr gap-buffer) <- copy _gap
+ 951   var stack/edi: (addr grapheme-stack) <- get gap, right
+ 952   var top-addr/eax: (addr int) <- get stack, top
+ 953   var top-index/ecx: int <- copy *top-addr
+ 954   compare top-index, 0
+ 955   {
+ 956     break-if->
+ 957     # if cursor at end, return (char before cursor == ')', 1)
+ 958     stack <- get gap, left
+ 959     top-addr <- get stack, top
+ 960     top-index <- copy *top-addr
+ 961     compare top-index, 0
+ 962     {
+ 963       break-if->
+ 964       return 0/false, 0
+ 965     }
+ 966     top-index <- decrement
+ 967     var data-ah/eax: (addr handle array grapheme) <- get stack, data
+ 968     var data/eax: (addr array grapheme) <- lookup *data-ah
+ 969     var g/eax: (addr grapheme) <- index data, top-index
+ 970     compare *g, 0x29/close-paren
+ 971     {
+ 972       break-if-=
+ 973       return 0/false, 0
+ 974     }
+ 975     return 1/true, 1
+ 976   }
+ 977   # cursor is not at end; return (char at cursor == ')')
+ 978   top-index <- decrement
+ 979   var data-ah/eax: (addr handle array grapheme) <- get stack, data
+ 980   var data/eax: (addr array grapheme) <- lookup *data-ah
+ 981   var g/eax: (addr grapheme) <- index data, top-index
+ 982   compare *g, 0x29/close-paren
+ 983   {
+ 984     break-if-=
+ 985     return 0/false, 0
+ 986   }
+ 987   return 1/true, 0
+ 988 }
+ 989 
+ 990 fn test-highlight-matching-open-paren {
+ 991   var gap-storage: gap-buffer
+ 992   var gap/esi: (addr gap-buffer) <- address gap-storage
+ 993   initialize-gap-buffer-with gap, "(a)"
+ 994   gap-to-end gap
+ 995   var highlight-matching-open-paren?/ebx: boolean <- copy 0/false
+ 996   var open-paren-depth/edi: int <- copy 0
+ 997   highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 0/no-cursor
+ 998   check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: no cursor"
+ 999   highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor
+1000   check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: at end immediately after ')'"
+1001   check-ints-equal open-paren-depth, 1, "F - test-highlight-matching-open-paren: depth at end immediately after ')'"
+1002   var dummy/eax: grapheme <- gap-left gap
+1003   highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor
+1004   check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: on ')'"
+1005   dummy <- gap-left gap
+1006   highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor
+1007   check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: not on ')'"
+1008 }
+1009 
+1010 ## some primitives for scanning through a gap buffer
+1011 # don't modify the gap buffer while scanning
+1012 # this includes moving the cursor around
+1013 
+1014 # restart scan without affecting gap-buffer contents
+1015 fn rewind-gap-buffer _self: (addr gap-buffer) {
+1016   var self/esi: (addr gap-buffer) <- copy _self
+1017   var dest/eax: (addr int) <- get self, left-read-index
+1018   copy-to *dest, 0
+1019   dest <- get self, right-read-index
+1020   copy-to *dest, 0
+1021 }
+1022 
+1023 fn gap-buffer-scan-done? _self: (addr gap-buffer) -> _/eax: boolean {
+1024   var self/esi: (addr gap-buffer) <- copy _self
+1025   # more in left?
+1026   var left/eax: (addr grapheme-stack) <- get self, left
+1027   var left-size/eax: int <- grapheme-stack-length left
+1028   var left-read-index/ecx: (addr int) <- get self, left-read-index
+1029   compare *left-read-index, left-size
+1030   {
+1031     break-if->=
+1032     return 0/false
+1033   }
+1034   # more in right?
+1035   var right/eax: (addr grapheme-stack) <- get self, right
+1036   var right-size/eax: int <- grapheme-stack-length right
+1037   var right-read-index/ecx: (addr int) <- get self, right-read-index
+1038   compare *right-read-index, right-size
+1039   {
+1040     break-if->=
+1041     return 0/false
+1042   }
+1043   #
+1044   return 1/true
+1045 }
+1046 
+1047 fn peek-from-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
+1048   var self/esi: (addr gap-buffer) <- copy _self
+1049   # more in left?
+1050   var left/ecx: (addr grapheme-stack) <- get self, left
+1051   var left-size/eax: int <- grapheme-stack-length left
+1052   var left-read-index-a/edx: (addr int) <- get self, left-read-index
+1053   compare *left-read-index-a, left-size
+1054   {
+1055     break-if->=
+1056     var left-data-ah/eax: (addr handle array grapheme) <- get left, data
+1057     var left-data/eax: (addr array grapheme) <- lookup *left-data-ah
+1058     var left-read-index/ecx: int <- copy *left-read-index-a
+1059     var result/eax: (addr grapheme) <- index left-data, left-read-index
+1060     return *result
+1061   }
+1062   # more in right?
+1063   var right/ecx: (addr grapheme-stack) <- get self, right
+1064   var _right-size/eax: int <- grapheme-stack-length right
+1065   var right-size/ebx: int <- copy _right-size
+1066   var right-read-index-a/edx: (addr int) <- get self, right-read-index
+1067   compare *right-read-index-a, right-size
+1068   {
+1069     break-if->=
+1070     # read the right from reverse
+1071     var right-data-ah/eax: (addr handle array grapheme) <- get right, data
+1072     var right-data/eax: (addr array grapheme) <- lookup *right-data-ah
+1073     var right-read-index/ebx: int <- copy right-size
+1074     right-read-index <- subtract *right-read-index-a
+1075     right-read-index <- subtract 1
+1076     var result/eax: (addr grapheme) <- index right-data, right-read-index
+1077     return *result
+1078   }
+1079   # if we get here there's nothing left
+1080   return 0/nul
+1081 }
+1082 
+1083 fn read-from-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
+1084   var self/esi: (addr gap-buffer) <- copy _self
+1085   # more in left?
+1086   var left/ecx: (addr grapheme-stack) <- get self, left
+1087   var left-size/eax: int <- grapheme-stack-length left
+1088   var left-read-index-a/edx: (addr int) <- get self, left-read-index
+1089   compare *left-read-index-a, left-size
+1090   {
+1091     break-if->=
+1092     var left-data-ah/eax: (addr handle array grapheme) <- get left, data
+1093     var left-data/eax: (addr array grapheme) <- lookup *left-data-ah
+1094     var left-read-index/ecx: int <- copy *left-read-index-a
+1095     var result/eax: (addr grapheme) <- index left-data, left-read-index
+1096     increment *left-read-index-a
+1097     return *result
+1098   }
+1099   # more in right?
+1100   var right/ecx: (addr grapheme-stack) <- get self, right
+1101   var _right-size/eax: int <- grapheme-stack-length right
+1102   var right-size/ebx: int <- copy _right-size
+1103   var right-read-index-a/edx: (addr int) <- get self, right-read-index
+1104   compare *right-read-index-a, right-size
+1105   {
+1106     break-if->=
+1107     # read the right from reverse
+1108     var right-data-ah/eax: (addr handle array grapheme) <- get right, data
+1109     var right-data/eax: (addr array grapheme) <- lookup *right-data-ah
+1110     var right-read-index/ebx: int <- copy right-size
+1111     right-read-index <- subtract *right-read-index-a
+1112     right-read-index <- subtract 1
+1113     var result/eax: (addr grapheme) <- index right-data, right-read-index
+1114     increment *right-read-index-a
+1115     return *result
+1116   }
+1117   # if we get here there's nothing left
+1118   return 0/nul
+1119 }
+1120 
+1121 fn put-back-from-gap-buffer _self: (addr gap-buffer) {
+1122   var self/esi: (addr gap-buffer) <- copy _self
+1123   # more in right?
+1124   var right/eax: (addr grapheme-stack) <- get self, right
+1125   var right-size/eax: int <- grapheme-stack-length right
+1126   var right-read-index-a/eax: (addr int) <- get self, right-read-index
+1127   compare *right-read-index-a, 0
+1128   {
+1129     break-if-<=
+1130     decrement *right-read-index-a
+1131     return
+1132   }
+1133   # more in left?
+1134   var left/eax: (addr grapheme-stack) <- get self, left
+1135   var left-size/eax: int <- grapheme-stack-length left
+1136   var left-read-index-a/eax: (addr int) <- get self, left-read-index
+1137   decrement *left-read-index-a
+1138 }
+1139 
+1140 fn test-read-from-gap-buffer {
+1141   var gap-storage: gap-buffer
+1142   var gap/esi: (addr gap-buffer) <- address gap-storage
+1143   initialize-gap-buffer-with gap, "abc"
+1144   # gap is at end, all contents are in left
+1145   var done?/eax: boolean <- gap-buffer-scan-done? gap
+1146   check-not done?, "F - test-read-from-gap-buffer/left-1/done"
+1147   var g/eax: grapheme <- read-from-gap-buffer gap
+1148   var x/ecx: int <- copy g
+1149   check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/left-1"
+1150   var done?/eax: boolean <- gap-buffer-scan-done? gap
+1151   check-not done?, "F - test-read-from-gap-buffer/left-2/done"
+1152   var g/eax: grapheme <- read-from-gap-buffer gap
+1153   var x/ecx: int <- copy g
+1154   check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/left-2"
+1155   var done?/eax: boolean <- gap-buffer-scan-done? gap
+1156   check-not done?, "F - test-read-from-gap-buffer/left-3/done"
+1157   var g/eax: grapheme <- read-from-gap-buffer gap
+1158   var x/ecx: int <- copy g
+1159   check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/left-3"
+1160   var done?/eax: boolean <- gap-buffer-scan-done? gap
+1161   check done?, "F - test-read-from-gap-buffer/left-4/done"
+1162   var g/eax: grapheme <- read-from-gap-buffer gap
+1163   var x/ecx: int <- copy g
+1164   check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/left-4"
+1165   # now check when everything is to the right
+1166   gap-to-start gap
+1167   rewind-gap-buffer gap
+1168   var done?/eax: boolean <- gap-buffer-scan-done? gap
+1169   check-not done?, "F - test-read-from-gap-buffer/right-1/done"
+1170   var g/eax: grapheme <- read-from-gap-buffer gap
+1171   var x/ecx: int <- copy g
+1172   check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/right-1"
+1173   var done?/eax: boolean <- gap-buffer-scan-done? gap
+1174   check-not done?, "F - test-read-from-gap-buffer/right-2/done"
+1175   var g/eax: grapheme <- read-from-gap-buffer gap
+1176   var x/ecx: int <- copy g
+1177   check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/right-2"
+1178   var done?/eax: boolean <- gap-buffer-scan-done? gap
+1179   check-not done?, "F - test-read-from-gap-buffer/right-3/done"
+1180   var g/eax: grapheme <- read-from-gap-buffer gap
+1181   var x/ecx: int <- copy g
+1182   check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/right-3"
+1183   var done?/eax: boolean <- gap-buffer-scan-done? gap
+1184   check done?, "F - test-read-from-gap-buffer/right-4/done"
+1185   var g/eax: grapheme <- read-from-gap-buffer gap
+1186   var x/ecx: int <- copy g
+1187   check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/right-4"
+1188 }
+1189 
+1190 fn skip-spaces-from-gap-buffer self: (addr gap-buffer) {
+1191   var done?/eax: boolean <- gap-buffer-scan-done? self
+1192   compare done?, 0/false
+1193   break-if-!=
+1194   var g/eax: grapheme <- peek-from-gap-buffer self
+1195   {
+1196     compare g, 0x20/space
+1197     break-if-=
+1198     return
+1199   }
+1200   g <- read-from-gap-buffer self
+1201   loop
+1202 }
+1203 
+1204 fn edit-gap-buffer self: (addr gap-buffer), key: grapheme {
+1205   var g/edx: grapheme <- copy key
+1206   {
+1207     compare g, 8/backspace
+1208     break-if-!=
+1209     delete-before-gap self
+1210     return
+1211   }
+1212   {
+1213     compare g, 0x80/left-arrow
+1214     break-if-!=
+1215     var dummy/eax: grapheme <- gap-left self
+1216     return
+1217   }
+1218   {
+1219     compare g, 0x83/right-arrow
+1220     break-if-!=
+1221     var dummy/eax: grapheme <- gap-right self
+1222     return
+1223   }
+1224   {
+1225     compare g, 6/ctrl-f
+1226     break-if-!=
+1227     gap-to-start-of-next-word self
+1228     return
+1229   }
+1230   {
+1231     compare g, 2/ctrl-b
+1232     break-if-!=
+1233     gap-to-end-of-previous-word self
+1234     return
+1235   }
+1236   {
+1237     compare g, 1/ctrl-a
+1238     break-if-!=
+1239     gap-to-previous-start-of-line self
+1240     return
+1241   }
+1242   {
+1243     compare g, 5/ctrl-e
+1244     break-if-!=
+1245     gap-to-next-end-of-line self
+1246     return
+1247   }
+1248   {
+1249     compare g, 0x81/down-arrow
+1250     break-if-!=
+1251     gap-down self
+1252     return
+1253   }
+1254   {
+1255     compare g, 0x82/up-arrow
+1256     break-if-!=
+1257     gap-up self
+1258     return
+1259   }
+1260   {
+1261     compare g, 0x15/ctrl-u
+1262     break-if-!=
+1263     clear-gap-buffer self
+1264     return
+1265   }
+1266   {
+1267     compare g, 9/tab
+1268     break-if-!=
+1269     # tab = 2 spaces
+1270     add-code-point-at-gap self, 0x20/space
+1271     add-code-point-at-gap self, 0x20/space
+1272     return
+1273   }
+1274   # default: insert character
+1275   add-grapheme-at-gap self, g
+1276 }
+1277 
+1278 fn gap-to-start-of-next-word self: (addr gap-buffer) {
+1279   var curr/eax: grapheme <- copy 0
+1280   # skip to next space
+1281   {
+1282     curr <- gap-right self
+1283     compare curr, -1
+1284     break-if-=
+1285     compare curr, 0x20/space
+1286     break-if-=
+1287     compare curr, 0xa/newline
+1288     break-if-=
+1289     loop
+1290   }
+1291   # skip past spaces
+1292   {
+1293     curr <- gap-right self
+1294     compare curr, -1
+1295     break-if-=
+1296     compare curr, 0x20/space
+1297     loop-if-=
+1298     compare curr, 0xa/space
+1299     loop-if-=
+1300     curr <- gap-left self
+1301     break
+1302   }
+1303 }
+1304 
+1305 fn gap-to-end-of-previous-word self: (addr gap-buffer) {
+1306   var curr/eax: grapheme <- copy 0
+1307   # skip to previous space
+1308   {
+1309     curr <- gap-left self
+1310     compare curr, -1
+1311     break-if-=
+1312     compare curr, 0x20/space
+1313     break-if-=
+1314     compare curr, 0xa/newline
+1315     break-if-=
+1316     loop
+1317   }
+1318   # skip past all spaces but one
+1319   {
+1320     curr <- gap-left self
+1321     compare curr, -1
+1322     break-if-=
+1323     compare curr, 0x20/space
+1324     loop-if-=
+1325     compare curr, 0xa/space
+1326     loop-if-=
+1327     curr <- gap-right self
+1328     break
+1329   }
+1330 }
+1331 
+1332 fn gap-to-previous-start-of-line self: (addr gap-buffer) {
+1333   # skip past immediate newline
+1334   var dummy/eax: grapheme <- gap-left self
+1335   # skip to previous newline
+1336   {
+1337     dummy <- gap-left self
+1338     {
+1339       compare dummy, -1
+1340       break-if-!=
+1341       return
+1342     }
+1343     {
+1344       compare dummy, 0xa/newline
+1345       break-if-!=
+1346       dummy <- gap-right self
+1347       return
+1348     }
+1349     loop
+1350   }
+1351 }
+1352 
+1353 fn gap-to-next-end-of-line self: (addr gap-buffer) {
+1354   # skip past immediate newline
+1355   var dummy/eax: grapheme <- gap-right self
+1356   # skip to next newline
+1357   {
+1358     dummy <- gap-right self
+1359     {
+1360       compare dummy, -1
+1361       break-if-!=
+1362       return
+1363     }
+1364     {
+1365       compare dummy, 0xa/newline
+1366       break-if-!=
+1367       dummy <- gap-left self
+1368       return
+1369     }
+1370     loop
+1371   }
+1372 }
+1373 
+1374 fn gap-up self: (addr gap-buffer) {
+1375   # compute column
+1376   var col/edx: int <- count-columns-to-start-of-line self
+1377   #
+1378   gap-to-previous-start-of-line self
+1379   # skip ahead by up to col on previous line
+1380   var i/ecx: int <- copy 0
+1381   {
+1382     compare i, col
+1383     break-if->=
+1384     var curr/eax: grapheme <- gap-right self
+1385     {
+1386       compare curr, -1
+1387       break-if-!=
+1388       return
+1389     }
+1390     compare curr, 0xa/newline
+1391     {
+1392       break-if-!=
+1393       curr <- gap-left self
+1394       return
+1395     }
+1396     i <- increment
+1397     loop
+1398   }
+1399 }
+1400 
+1401 fn gap-down self: (addr gap-buffer) {
+1402   # compute column
+1403   var col/edx: int <- count-columns-to-start-of-line self
+1404   # skip to start of next line
+1405   gap-to-end-of-line self
+1406   var dummy/eax: grapheme <- gap-right self
+1407   # skip ahead by up to col on previous line
+1408   var i/ecx: int <- copy 0
+1409   {
+1410     compare i, col
+1411     break-if->=
+1412     var curr/eax: grapheme <- gap-right self
+1413     {
+1414       compare curr, -1
+1415       break-if-!=
+1416       return
+1417     }
+1418     compare curr, 0xa/newline
+1419     {
+1420       break-if-!=
+1421       curr <- gap-left self
+1422       return
+1423     }
+1424     i <- increment
+1425     loop
+1426   }
+1427 }
+1428 
+1429 fn count-columns-to-start-of-line self: (addr gap-buffer) -> _/edx: int {
+1430   var count/edx: int <- copy 0
+1431   var dummy/eax: grapheme <- copy 0
+1432   # skip to previous newline
+1433   {
+1434     dummy <- gap-left self
+1435     {
+1436       compare dummy, -1
+1437       break-if-!=
+1438       return count
+1439     }
+1440     {
+1441       compare dummy, 0xa/newline
+1442       break-if-!=
+1443       dummy <- gap-right self
+1444       return count
+1445     }
+1446     count <- increment
+1447     loop
+1448   }
+1449   return count
+1450 }
+1451 
+1452 fn gap-to-end-of-line self: (addr gap-buffer) {
+1453   var dummy/eax: grapheme <- copy 0
+1454   # skip to next newline
+1455   {
+1456     dummy <- gap-right self
+1457     {
+1458       compare dummy, -1
+1459       break-if-!=
+1460       return
+1461     }
+1462     {
+1463       compare dummy, 0xa/newline
+1464       break-if-!=
+1465       dummy <- gap-left self
+1466       return
+1467     }
+1468     loop
+1469   }
+1470 }
+
+ + + diff --git a/html/apps/color-game.mu.html b/html/apps/color-game.mu.html index 07fac07f..7838733a 100644 --- a/html/apps/color-game.mu.html +++ b/html/apps/color-game.mu.html @@ -1,36 +1,35 @@ - + - + Mu - apps/color-game.mu - - + + - + - -