From 3350c34a74844e21ea69077e01efff3bae64bdcd Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Tue, 23 Mar 2021 17:31:08 -0700 Subject: . --- html/shell/gap-buffer.mu.html | 844 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 844 insertions(+) create mode 100644 html/shell/gap-buffer.mu.html (limited to 'html/shell/gap-buffer.mu.html') diff --git a/html/shell/gap-buffer.mu.html b/html/shell/gap-buffer.mu.html new file mode 100644 index 00000000..10cc1f76 --- /dev/null +++ b/html/shell/gap-buffer.mu.html @@ -0,0 +1,844 @@ + + + + +Mu - shell/gap-buffer.mu + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/shell/gap-buffer.mu +
+  1 # primitive for editing a single word
+  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), max-word-size: 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, max-word-size
+ 15   var right/eax: (addr grapheme-stack) <- get self, right
+ 16   initialize-grapheme-stack right, max-word-size
+ 17 }
+ 18 
+ 19 # just for tests
+ 20 fn initialize-gap-buffer-with self: (addr gap-buffer), s: (addr array byte) {
+ 21   initialize-gap-buffer self, 0x10/max-word-size
+ 22   var stream-storage: (stream byte 0x10/max-word-size)
+ 23   var stream/ecx: (addr stream byte) <- address stream-storage
+ 24   write stream, s
+ 25   {
+ 26     var done?/eax: boolean <- stream-empty? stream
+ 27     compare done?, 0/false
+ 28     break-if-!=
+ 29     var g/eax: grapheme <- read-grapheme stream
+ 30     add-grapheme-at-gap self, g
+ 31     loop
+ 32   }
+ 33 }
+ 34 
+ 35 fn emit-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) {
+ 36   var self/esi: (addr gap-buffer) <- copy _self
+ 37   clear-stream out
+ 38   var left/eax: (addr grapheme-stack) <- get self, left
+ 39   emit-stack-from-bottom left, out
+ 40   var right/eax: (addr grapheme-stack) <- get self, right
+ 41   emit-stack-from-top right, out
+ 42 }
+ 43 
+ 44 # dump stack from bottom to top
+ 45 fn emit-stack-from-bottom _self: (addr grapheme-stack), out: (addr stream byte) {
+ 46   var self/esi: (addr grapheme-stack) <- copy _self
+ 47   var data-ah/edi: (addr handle array grapheme) <- get self, data
+ 48   var _data/eax: (addr array grapheme) <- lookup *data-ah
+ 49   var data/edi: (addr array grapheme) <- copy _data
+ 50   var top-addr/ecx: (addr int) <- get self, top
+ 51   var i/eax: int <- copy 0
+ 52   {
+ 53     compare i, *top-addr
+ 54     break-if->=
+ 55     var g/edx: (addr grapheme) <- index data, i
+ 56     write-grapheme out, *g
+ 57     i <- increment
+ 58     loop
+ 59   }
+ 60 }
+ 61 
+ 62 # dump stack from top to bottom
+ 63 fn emit-stack-from-top _self: (addr grapheme-stack), out: (addr stream byte) {
+ 64   var self/esi: (addr grapheme-stack) <- copy _self
+ 65   var data-ah/edi: (addr handle array grapheme) <- get self, data
+ 66   var _data/eax: (addr array grapheme) <- lookup *data-ah
+ 67   var data/edi: (addr array grapheme) <- copy _data
+ 68   var top-addr/ecx: (addr int) <- get self, top
+ 69   var i/eax: int <- copy *top-addr
+ 70   i <- decrement
+ 71   {
+ 72     compare i, 0
+ 73     break-if-<
+ 74     var g/edx: (addr grapheme) <- index data, i
+ 75     write-grapheme out, *g
+ 76     i <- decrement
+ 77     loop
+ 78   }
+ 79 }
+ 80 
+ 81 # We implicitly render everything editable in a single color, and assume the
+ 82 # cursor is a single other color.
+ 83 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 -> _/eax: int, _/ecx: int {
+ 84   var gap/esi: (addr gap-buffer) <- copy _gap
+ 85   var left/edx: (addr grapheme-stack) <- get gap, left
+ 86   var x2/eax: int <- copy 0
+ 87   var y2/ecx: int <- copy 0
+ 88   x2, y2 <- render-stack-from-bottom-wrapping-right-then-down screen, left, xmin, ymin, xmax, ymax, xmin, ymin
+ 89   var right/edx: (addr grapheme-stack) <- get gap, right
+ 90   x2, y2 <- render-stack-from-top-wrapping-right-then-down screen, right, xmin, ymin, xmax, ymax, x2, y2, render-cursor?
+ 91   # decide whether we still need to print a cursor
+ 92   var bg/ebx: int <- copy 0
+ 93   compare render-cursor?, 0/false
+ 94   {
+ 95     break-if-=
+ 96     # if the right side is empty, grapheme stack didn't print the cursor
+ 97     var empty?/eax: boolean <- grapheme-stack-empty? right
+ 98     compare empty?, 0/false
+ 99     break-if-=
+100     bg <- copy 7/cursor
+101   }
+102   # print a grapheme either way so that cursor position doesn't affect printed width
+103   var space/edx: grapheme <- copy 0x20
+104   x2, y2 <- render-grapheme screen, space, xmin, ymin, xmax, ymax, x2, y2, 3/fg=cyan, bg
+105   return x2, y2
+106 }
+107 
+108 fn render-gap-buffer screen: (addr screen), gap: (addr gap-buffer), x: int, y: int, render-cursor?: boolean -> _/eax: int {
+109   var _width/eax: int <- copy 0
+110   var _height/ecx: int <- copy 0
+111   _width, _height <- screen-size screen
+112   var width/edx: int <- copy _width
+113   var height/ebx: int <- copy _height
+114   var x2/eax: int <- copy 0
+115   var y2/ecx: int <- copy 0
+116   x2, y2 <- render-gap-buffer-wrapping-right-then-down screen, gap, x, y, width, height, render-cursor?
+117   return x2  # y2? yolo
+118 }
+119 
+120 fn gap-buffer-length _gap: (addr gap-buffer) -> _/eax: int {
+121   var gap/esi: (addr gap-buffer) <- copy _gap
+122   var left/eax: (addr grapheme-stack) <- get gap, left
+123   var tmp/eax: (addr int) <- get left, top
+124   var left-length/ecx: int <- copy *tmp
+125   var right/esi: (addr grapheme-stack) <- get gap, right
+126   tmp <- get right, top
+127   var result/eax: int <- copy *tmp
+128   result <- add left-length
+129   return result
+130 }
+131 
+132 fn add-grapheme-at-gap _self: (addr gap-buffer), g: grapheme {
+133   var self/esi: (addr gap-buffer) <- copy _self
+134   var left/eax: (addr grapheme-stack) <- get self, left
+135   push-grapheme-stack left, g
+136 }
+137 
+138 fn gap-to-start self: (addr gap-buffer) {
+139   {
+140     var curr/eax: grapheme <- gap-left self
+141     compare curr, -1
+142     loop-if-!=
+143   }
+144 }
+145 
+146 fn gap-to-end self: (addr gap-buffer) {
+147   {
+148     var curr/eax: grapheme <- gap-right self
+149     compare curr, -1
+150     loop-if-!=
+151   }
+152 }
+153 
+154 fn gap-at-start? _self: (addr gap-buffer) -> _/eax: boolean {
+155   var self/esi: (addr gap-buffer) <- copy _self
+156   var left/eax: (addr grapheme-stack) <- get self, left
+157   var result/eax: boolean <- grapheme-stack-empty? left
+158   return result
+159 }
+160 
+161 fn gap-at-end? _self: (addr gap-buffer) -> _/eax: boolean {
+162   var self/esi: (addr gap-buffer) <- copy _self
+163   var right/eax: (addr grapheme-stack) <- get self, right
+164   var result/eax: boolean <- grapheme-stack-empty? right
+165   return result
+166 }
+167 
+168 fn gap-right _self: (addr gap-buffer) -> _/eax: grapheme {
+169   var self/esi: (addr gap-buffer) <- copy _self
+170   var g/eax: grapheme <- copy 0
+171   var right/ecx: (addr grapheme-stack) <- get self, right
+172   g <- pop-grapheme-stack right
+173   compare g, -1
+174   {
+175     break-if-=
+176     var left/ecx: (addr grapheme-stack) <- get self, left
+177     push-grapheme-stack left, g
+178   }
+179   return g
+180 }
+181 
+182 fn gap-left _self: (addr gap-buffer) -> _/eax: grapheme {
+183   var self/esi: (addr gap-buffer) <- copy _self
+184   var g/eax: grapheme <- copy 0
+185   {
+186     var left/ecx: (addr grapheme-stack) <- get self, left
+187     g <- pop-grapheme-stack left
+188   }
+189   compare g, -1
+190   {
+191     break-if-=
+192     var right/ecx: (addr grapheme-stack) <- get self, right
+193     push-grapheme-stack right, g
+194   }
+195   return g
+196 }
+197 
+198 fn index-of-gap _self: (addr gap-buffer) -> _/eax: int {
+199   var self/eax: (addr gap-buffer) <- copy _self
+200   var left/eax: (addr grapheme-stack) <- get self, left
+201   var top-addr/eax: (addr int) <- get left, top
+202   var result/eax: int <- copy *top-addr
+203   return result
+204 }
+205 
+206 fn first-grapheme-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
+207   var self/esi: (addr gap-buffer) <- copy _self
+208   # try to read from left
+209   var left/eax: (addr grapheme-stack) <- get self, left
+210   var top-addr/ecx: (addr int) <- get left, top
+211   compare *top-addr, 0
+212   {
+213     break-if-<=
+214     var data-ah/eax: (addr handle array grapheme) <- get left, data
+215     var data/eax: (addr array grapheme) <- lookup *data-ah
+216     var result-addr/eax: (addr grapheme) <- index data, 0
+217     return *result-addr
+218   }
+219   # try to read from right
+220   var right/eax: (addr grapheme-stack) <- get self, right
+221   top-addr <- get right, top
+222   compare *top-addr, 0
+223   {
+224     break-if-<=
+225     var data-ah/eax: (addr handle array grapheme) <- get right, data
+226     var data/eax: (addr array grapheme) <- lookup *data-ah
+227     var top/ecx: int <- copy *top-addr
+228     top <- decrement
+229     var result-addr/eax: (addr grapheme) <- index data, top
+230     return *result-addr
+231   }
+232   # give up
+233   return -1
+234 }
+235 
+236 fn grapheme-before-cursor-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
+237   var self/esi: (addr gap-buffer) <- copy _self
+238   # try to read from left
+239   var left/ecx: (addr grapheme-stack) <- get self, left
+240   var top-addr/edx: (addr int) <- get left, top
+241   compare *top-addr, 0
+242   {
+243     break-if-<=
+244     var result/eax: grapheme <- pop-grapheme-stack left
+245     push-grapheme-stack left, result
+246     return result
+247   }
+248   # give up
+249   return -1
+250 }
+251 
+252 fn delete-before-gap _self: (addr gap-buffer) {
+253   var self/eax: (addr gap-buffer) <- copy _self
+254   var left/eax: (addr grapheme-stack) <- get self, left
+255   var dummy/eax: grapheme <- pop-grapheme-stack left
+256 }
+257 
+258 fn pop-after-gap _self: (addr gap-buffer) -> _/eax: grapheme {
+259   var self/eax: (addr gap-buffer) <- copy _self
+260   var right/eax: (addr grapheme-stack) <- get self, right
+261   var result/eax: grapheme <- pop-grapheme-stack right
+262   return result
+263 }
+264 
+265 fn gap-buffer-equal? _self: (addr gap-buffer), s: (addr array byte) -> _/eax: boolean {
+266   var self/esi: (addr gap-buffer) <- copy _self
+267   # complication: graphemes may be multiple bytes
+268   # so don't rely on length
+269   # instead turn the expected result into a stream and arrange to read from it in order
+270   var stream-storage: (stream byte 0x10/max-word-size)
+271   var expected-stream/ecx: (addr stream byte) <- address stream-storage
+272   write expected-stream, s
+273   # compare left
+274   var left/edx: (addr grapheme-stack) <- get self, left
+275   var result/eax: boolean <- prefix-match? left, expected-stream
+276   compare result, 0/false
+277   {
+278     break-if-!=
+279     return result
+280   }
+281   # compare right
+282   var right/edx: (addr grapheme-stack) <- get self, right
+283   result <- suffix-match? right, expected-stream
+284   compare result, 0/false
+285   {
+286     break-if-!=
+287     return result
+288   }
+289   # ensure there's nothing left over
+290   result <- stream-empty? expected-stream
+291   return result
+292 }
+293 
+294 fn test-gap-buffer-equal-from-end {
+295   var _g: gap-buffer
+296   var g/esi: (addr gap-buffer) <- address _g
+297   initialize-gap-buffer g, 0x10
+298   #
+299   var c/eax: grapheme <- copy 0x61/a
+300   add-grapheme-at-gap g, c
+301   add-grapheme-at-gap g, c
+302   add-grapheme-at-gap g, c
+303   # gap is at end (right is empty)
+304   var result/eax: boolean <- gap-buffer-equal? g, "aaa"
+305   check result, "F - test-gap-buffer-equal-from-end"
+306 }
+307 
+308 fn test-gap-buffer-equal-from-middle {
+309   var _g: gap-buffer
+310   var g/esi: (addr gap-buffer) <- address _g
+311   initialize-gap-buffer g, 0x10
+312   #
+313   var c/eax: grapheme <- copy 0x61/a
+314   add-grapheme-at-gap g, c
+315   add-grapheme-at-gap g, c
+316   add-grapheme-at-gap g, c
+317   var dummy/eax: grapheme <- gap-left g
+318   # gap is in the middle
+319   var result/eax: boolean <- gap-buffer-equal? g, "aaa"
+320   check result, "F - test-gap-buffer-equal-from-middle"
+321 }
+322 
+323 fn test-gap-buffer-equal-from-start {
+324   var _g: gap-buffer
+325   var g/esi: (addr gap-buffer) <- address _g
+326   initialize-gap-buffer g, 0x10
+327   #
+328   var c/eax: grapheme <- copy 0x61/a
+329   add-grapheme-at-gap g, c
+330   add-grapheme-at-gap g, c
+331   add-grapheme-at-gap g, c
+332   var dummy/eax: grapheme <- gap-left g
+333   dummy <- gap-left g
+334   dummy <- gap-left g
+335   # gap is at the start
+336   var result/eax: boolean <- gap-buffer-equal? g, "aaa"
+337   check result, "F - test-gap-buffer-equal-from-start"
+338 }
+339 
+340 fn test-gap-buffer-equal-fails {
+341   # g = "aaa"
+342   var _g: gap-buffer
+343   var g/esi: (addr gap-buffer) <- address _g
+344   initialize-gap-buffer g, 0x10
+345   var c/eax: grapheme <- copy 0x61/a
+346   add-grapheme-at-gap g, c
+347   add-grapheme-at-gap g, c
+348   add-grapheme-at-gap g, c
+349   #
+350   var result/eax: boolean <- gap-buffer-equal? g, "aa"
+351   check-not result, "F - test-gap-buffer-equal-fails"
+352 }
+353 
+354 fn gap-buffers-equal? self: (addr gap-buffer), g: (addr gap-buffer) -> _/eax: boolean {
+355   var tmp/eax: int <- gap-buffer-length self
+356   var len/ecx: int <- copy tmp
+357   var leng/eax: int <- gap-buffer-length g
+358   compare len, leng
+359   {
+360     break-if-=
+361     return 0/false
+362   }
+363   var i/edx: int <- copy 0
+364   {
+365     compare i, len
+366     break-if->=
+367     {
+368       var tmp/eax: grapheme <- gap-index self, i
+369       var curr/ecx: grapheme <- copy tmp
+370       var currg/eax: grapheme <- gap-index g, i
+371       compare curr, currg
+372       break-if-=
+373       return 0/false
+374     }
+375     i <- increment
+376     loop
+377   }
+378   return 1/true
+379 }
+380 
+381 fn gap-index _self: (addr gap-buffer), _n: int -> _/eax: grapheme {
+382   var self/esi: (addr gap-buffer) <- copy _self
+383   var n/ebx: int <- copy _n
+384   # if n < left->length, index into left
+385   var left/edi: (addr grapheme-stack) <- get self, left
+386   var left-len-a/edx: (addr int) <- get left, top
+387   compare n, *left-len-a
+388   {
+389     break-if->=
+390     var data-ah/eax: (addr handle array grapheme) <- get left, data
+391     var data/eax: (addr array grapheme) <- lookup *data-ah
+392     var result/eax: (addr grapheme) <- index data, n
+393     return *result
+394   }
+395   # shrink n
+396   n <- subtract *left-len-a
+397   # if n < right->length, index into right
+398   var right/edi: (addr grapheme-stack) <- get self, right
+399   var right-len-a/edx: (addr int) <- get right, top
+400   compare n, *right-len-a
+401   {
+402     break-if->=
+403     var data-ah/eax: (addr handle array grapheme) <- get right, data
+404     var data/eax: (addr array grapheme) <- lookup *data-ah
+405     # idx = right->len - n - 1
+406     var idx/ebx: int <- copy n
+407     idx <- subtract *right-len-a
+408     idx <- negate
+409     idx <- subtract 1
+410     var result/eax: (addr grapheme) <- index data, idx
+411     return *result
+412   }
+413   # error
+414   abort "gap-index: out of bounds"
+415   return 0
+416 }
+417 
+418 fn test-gap-buffers-equal? {
+419   var _a: gap-buffer
+420   var a/esi: (addr gap-buffer) <- address _a
+421   initialize-gap-buffer-with a, "abc"
+422   var _b: gap-buffer
+423   var b/edi: (addr gap-buffer) <- address _b
+424   initialize-gap-buffer-with b, "abc"
+425   var _c: gap-buffer
+426   var c/ebx: (addr gap-buffer) <- address _c
+427   initialize-gap-buffer-with c, "ab"
+428   var _d: gap-buffer
+429   var d/edx: (addr gap-buffer) <- address _d
+430   initialize-gap-buffer-with d, "abd"
+431   #
+432   var result/eax: boolean <- gap-buffers-equal? a, a
+433   check result, "F - test-gap-buffers-equal? - reflexive"
+434   result <- gap-buffers-equal? a, b
+435   check result, "F - test-gap-buffers-equal? - equal"
+436   # length not equal
+437   result <- gap-buffers-equal? a, c
+438   check-not result, "F - test-gap-buffers-equal? - not equal"
+439   # contents not equal
+440   result <- gap-buffers-equal? a, d
+441   check-not result, "F - test-gap-buffers-equal? - not equal 2"
+442   result <- gap-buffers-equal? d, a
+443   check-not result, "F - test-gap-buffers-equal? - not equal 3"
+444 }
+445 
+446 fn test-gap-buffer-index {
+447   var gap-storage: gap-buffer
+448   var gap/esi: (addr gap-buffer) <- address gap-storage
+449   initialize-gap-buffer-with gap, "abc"
+450   # gap is at end, all contents are in left
+451   var g/eax: grapheme <- gap-index gap, 0
+452   var x/ecx: int <- copy g
+453   check-ints-equal x, 0x61/a, "F - test-gap-index/left-1"
+454   var g/eax: grapheme <- gap-index gap, 1
+455   var x/ecx: int <- copy g
+456   check-ints-equal x, 0x62/b, "F - test-gap-index/left-2"
+457   var g/eax: grapheme <- gap-index gap, 2
+458   var x/ecx: int <- copy g
+459   check-ints-equal x, 0x63/c, "F - test-gap-index/left-3"
+460   # now check when everything is to the right
+461   gap-to-start gap
+462   rewind-gap-buffer gap
+463   var g/eax: grapheme <- gap-index gap, 0
+464   var x/ecx: int <- copy g
+465   check-ints-equal x, 0x61/a, "F - test-gap-index/right-1"
+466   var g/eax: grapheme <- gap-index gap, 1
+467   var x/ecx: int <- copy g
+468   check-ints-equal x, 0x62/b, "F - test-gap-index/right-2"
+469   var g/eax: grapheme <- gap-index gap, 2
+470   var x/ecx: int <- copy g
+471   check-ints-equal x, 0x63/c, "F - test-gap-index/right-3"
+472 }
+473 
+474 fn copy-gap-buffer _src-ah: (addr handle gap-buffer), _dest-ah: (addr handle gap-buffer) {
+475   # obtain src-a, dest-a
+476   var src-ah/eax: (addr handle gap-buffer) <- copy _src-ah
+477   var _src-a/eax: (addr gap-buffer) <- lookup *src-ah
+478   var src-a/esi: (addr gap-buffer) <- copy _src-a
+479   var dest-ah/eax: (addr handle gap-buffer) <- copy _dest-ah
+480   var _dest-a/eax: (addr gap-buffer) <- lookup *dest-ah
+481   var dest-a/edi: (addr gap-buffer) <- copy _dest-a
+482   # copy left grapheme-stack
+483   var src/ecx: (addr grapheme-stack) <- get src-a, left
+484   var dest/edx: (addr grapheme-stack) <- get dest-a, left
+485   copy-grapheme-stack src, dest
+486   # copy right grapheme-stack
+487   src <- get src-a, right
+488   dest <- get dest-a, right
+489   copy-grapheme-stack src, dest
+490 }
+491 
+492 fn gap-buffer-is-decimal-integer? _self: (addr gap-buffer) -> _/eax: boolean {
+493   var self/esi: (addr gap-buffer) <- copy _self
+494   var curr/ecx: (addr grapheme-stack) <- get self, left
+495   var result/eax: boolean <- grapheme-stack-is-decimal-integer? curr
+496   {
+497     compare result, 0/false
+498     break-if-=
+499     curr <- get self, right
+500     result <- grapheme-stack-is-decimal-integer? curr
+501   }
+502   return result
+503 }
+504 
+505 fn test-render-gap-buffer-without-cursor {
+506   # setup
+507   var gap-storage: gap-buffer
+508   var gap/esi: (addr gap-buffer) <- address gap-storage
+509   initialize-gap-buffer-with gap, "abc"
+510   # setup: screen
+511   var screen-on-stack: screen
+512   var screen/edi: (addr screen) <- address screen-on-stack
+513   initialize-screen screen, 5, 4
+514   #
+515   var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 0/no-cursor
+516   check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-without-cursor"
+517   check-ints-equal x, 4, "F - test-render-gap-buffer-without-cursor: result"
+518                                                                 # abc
+519   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "    ", "F - test-render-gap-buffer-without-cursor: bg"
+520 }
+521 
+522 fn test-render-gap-buffer-with-cursor-at-end {
+523   # setup
+524   var gap-storage: gap-buffer
+525   var gap/esi: (addr gap-buffer) <- address gap-storage
+526   initialize-gap-buffer-with gap, "abc"
+527   gap-to-end gap
+528   # setup: screen
+529   var screen-on-stack: screen
+530   var screen/edi: (addr screen) <- address screen-on-stack
+531   initialize-screen screen, 5, 4
+532   #
+533   var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
+534   check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-end"
+535   # we've drawn one extra grapheme for the cursor
+536   check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-end: result"
+537                                                                 # abc
+538   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "   |", "F - test-render-gap-buffer-with-cursor-at-end: bg"
+539 }
+540 
+541 fn test-render-gap-buffer-with-cursor-in-middle {
+542   # setup
+543   var gap-storage: gap-buffer
+544   var gap/esi: (addr gap-buffer) <- address gap-storage
+545   initialize-gap-buffer-with gap, "abc"
+546   gap-to-end gap
+547   var dummy/eax: grapheme <- gap-left gap
+548   # setup: screen
+549   var screen-on-stack: screen
+550   var screen/edi: (addr screen) <- address screen-on-stack
+551   initialize-screen screen, 5, 4
+552   #
+553   var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
+554   check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-in-middle"
+555   check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-in-middle: result"
+556                                                                 # abc
+557   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "  | ", "F - test-render-gap-buffer-with-cursor-in-middle: bg"
+558 }
+559 
+560 fn test-render-gap-buffer-with-cursor-at-start {
+561   var gap-storage: gap-buffer
+562   var gap/esi: (addr gap-buffer) <- address gap-storage
+563   initialize-gap-buffer-with gap, "abc"
+564   gap-to-start gap
+565   # setup: screen
+566   var screen-on-stack: screen
+567   var screen/edi: (addr screen) <- address screen-on-stack
+568   initialize-screen screen, 5, 4
+569   #
+570   var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
+571   check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-start"
+572   check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-start: result"
+573                                                                 # abc
+574   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|   ", "F - test-render-gap-buffer-with-cursor-at-start: bg"
+575 }
+576 
+577 ## some primitives for scanning through a gap buffer
+578 # don't modify the gap buffer while scanning
+579 # this includes moving the cursor around
+580 
+581 # restart scan without affecting gap-buffer contents
+582 fn rewind-gap-buffer _self: (addr gap-buffer) {
+583   var self/esi: (addr gap-buffer) <- copy _self
+584   var dest/eax: (addr int) <- get self, left-read-index
+585   copy-to *dest, 0
+586   dest <- get self, right-read-index
+587   copy-to *dest, 0
+588 }
+589 
+590 fn gap-buffer-scan-done? _self: (addr gap-buffer) -> _/eax: boolean {
+591   var self/esi: (addr gap-buffer) <- copy _self
+592   # more in left?
+593   var left/eax: (addr grapheme-stack) <- get self, left
+594   var left-size/eax: int <- grapheme-stack-length left
+595   var left-read-index/ecx: (addr int) <- get self, left-read-index
+596   compare *left-read-index, left-size
+597   {
+598     break-if->=
+599     return 0/false
+600   }
+601   # more in right?
+602   var right/eax: (addr grapheme-stack) <- get self, right
+603   var right-size/eax: int <- grapheme-stack-length right
+604   var right-read-index/ecx: (addr int) <- get self, right-read-index
+605   compare *right-read-index, right-size
+606   {
+607     break-if->=
+608     return 0/false
+609   }
+610   #
+611   return 1/true
+612 }
+613 
+614 fn peek-from-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
+615   var self/esi: (addr gap-buffer) <- copy _self
+616   # more in left?
+617   var left/ecx: (addr grapheme-stack) <- get self, left
+618   var left-size/eax: int <- grapheme-stack-length left
+619   var left-read-index-a/edx: (addr int) <- get self, left-read-index
+620   compare *left-read-index-a, left-size
+621   {
+622     break-if->=
+623     var left-data-ah/eax: (addr handle array grapheme) <- get left, data
+624     var left-data/eax: (addr array grapheme) <- lookup *left-data-ah
+625     var left-read-index/ecx: int <- copy *left-read-index-a
+626     var result/eax: (addr grapheme) <- index left-data, left-read-index
+627     return *result
+628   }
+629   # more in right?
+630   var right/ecx: (addr grapheme-stack) <- get self, right
+631   var _right-size/eax: int <- grapheme-stack-length right
+632   var right-size/ebx: int <- copy _right-size
+633   var right-read-index-a/edx: (addr int) <- get self, right-read-index
+634   compare *right-read-index-a, right-size
+635   {
+636     break-if->=
+637     # read the right from reverse
+638     var right-data-ah/eax: (addr handle array grapheme) <- get right, data
+639     var right-data/eax: (addr array grapheme) <- lookup *right-data-ah
+640     var right-read-index/ebx: int <- copy right-size
+641     right-read-index <- subtract *right-read-index-a
+642     right-read-index <- subtract 1
+643     var result/eax: (addr grapheme) <- index right-data, right-read-index
+644     return *result
+645   }
+646   # if we get here there's nothing left
+647   return 0/nul
+648 }
+649 
+650 fn read-from-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
+651   var self/esi: (addr gap-buffer) <- copy _self
+652   # more in left?
+653   var left/ecx: (addr grapheme-stack) <- get self, left
+654   var left-size/eax: int <- grapheme-stack-length left
+655   var left-read-index-a/edx: (addr int) <- get self, left-read-index
+656   compare *left-read-index-a, left-size
+657   {
+658     break-if->=
+659     var left-data-ah/eax: (addr handle array grapheme) <- get left, data
+660     var left-data/eax: (addr array grapheme) <- lookup *left-data-ah
+661     var left-read-index/ecx: int <- copy *left-read-index-a
+662     var result/eax: (addr grapheme) <- index left-data, left-read-index
+663     increment *left-read-index-a
+664     return *result
+665   }
+666   # more in right?
+667   var right/ecx: (addr grapheme-stack) <- get self, right
+668   var _right-size/eax: int <- grapheme-stack-length right
+669   var right-size/ebx: int <- copy _right-size
+670   var right-read-index-a/edx: (addr int) <- get self, right-read-index
+671   compare *right-read-index-a, right-size
+672   {
+673     break-if->=
+674     # read the right from reverse
+675     var right-data-ah/eax: (addr handle array grapheme) <- get right, data
+676     var right-data/eax: (addr array grapheme) <- lookup *right-data-ah
+677     var right-read-index/ebx: int <- copy right-size
+678     right-read-index <- subtract *right-read-index-a
+679     right-read-index <- subtract 1
+680     var result/eax: (addr grapheme) <- index right-data, right-read-index
+681     increment *right-read-index-a
+682     return *result
+683   }
+684   # if we get here there's nothing left
+685   return 0/nul
+686 }
+687 
+688 fn test-read-from-gap-buffer {
+689   var gap-storage: gap-buffer
+690   var gap/esi: (addr gap-buffer) <- address gap-storage
+691   initialize-gap-buffer-with gap, "abc"
+692   # gap is at end, all contents are in left
+693   var done?/eax: boolean <- gap-buffer-scan-done? gap
+694   check-not done?, "F - test-read-from-gap-buffer/left-1/done"
+695   var g/eax: grapheme <- read-from-gap-buffer gap
+696   var x/ecx: int <- copy g
+697   check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/left-1"
+698   var done?/eax: boolean <- gap-buffer-scan-done? gap
+699   check-not done?, "F - test-read-from-gap-buffer/left-2/done"
+700   var g/eax: grapheme <- read-from-gap-buffer gap
+701   var x/ecx: int <- copy g
+702   check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/left-2"
+703   var done?/eax: boolean <- gap-buffer-scan-done? gap
+704   check-not done?, "F - test-read-from-gap-buffer/left-3/done"
+705   var g/eax: grapheme <- read-from-gap-buffer gap
+706   var x/ecx: int <- copy g
+707   check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/left-3"
+708   var done?/eax: boolean <- gap-buffer-scan-done? gap
+709   check done?, "F - test-read-from-gap-buffer/left-4/done"
+710   var g/eax: grapheme <- read-from-gap-buffer gap
+711   var x/ecx: int <- copy g
+712   check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/left-4"
+713   # now check when everything is to the right
+714   gap-to-start gap
+715   rewind-gap-buffer gap
+716   var done?/eax: boolean <- gap-buffer-scan-done? gap
+717   check-not done?, "F - test-read-from-gap-buffer/right-1/done"
+718   var g/eax: grapheme <- read-from-gap-buffer gap
+719   var x/ecx: int <- copy g
+720   check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/right-1"
+721   var done?/eax: boolean <- gap-buffer-scan-done? gap
+722   check-not done?, "F - test-read-from-gap-buffer/right-2/done"
+723   var g/eax: grapheme <- read-from-gap-buffer gap
+724   var x/ecx: int <- copy g
+725   check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/right-2"
+726   var done?/eax: boolean <- gap-buffer-scan-done? gap
+727   check-not done?, "F - test-read-from-gap-buffer/right-3/done"
+728   var g/eax: grapheme <- read-from-gap-buffer gap
+729   var x/ecx: int <- copy g
+730   check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/right-3"
+731   var done?/eax: boolean <- gap-buffer-scan-done? gap
+732   check done?, "F - test-read-from-gap-buffer/right-4/done"
+733   var g/eax: grapheme <- read-from-gap-buffer gap
+734   var x/ecx: int <- copy g
+735   check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/right-4"
+736 }
+737 
+738 fn skip-whitespace-from-gap-buffer self: (addr gap-buffer) {
+739   var done?/eax: boolean <- gap-buffer-scan-done? self
+740   compare done?, 0/false
+741   break-if-!=
+742   var g/eax: grapheme <- peek-from-gap-buffer self
+743   {
+744     compare g, 0x20/space
+745     break-if-=
+746     compare g, 0xa/newline
+747     break-if-=
+748     return
+749   }
+750   g <- read-from-gap-buffer self
+751   loop
+752 }
+753 
+754 fn edit-gap-buffer self: (addr gap-buffer), key: grapheme {
+755   var g/edx: grapheme <- copy key
+756   {
+757     compare g, 8/backspace
+758     break-if-!=
+759     delete-before-gap self
+760     return
+761   }
+762   # arrow keys
+763   {
+764     compare g, 4/ctrl-d
+765     break-if-!=
+766     # ctrl-d: cursor down
+767     return
+768   }
+769   {
+770     compare g, 0x15/ctrl-u
+771     break-if-!=
+772     # ctrl-u: cursor up
+773     return
+774   }
+775   # default: insert character
+776   add-grapheme-at-gap self, g
+777 }
+778 
+779 fn cursor-on-final-line? self: (addr gap-buffer) -> _/eax: boolean {
+780   return 1/true
+781 }
+
+ + + -- cgit 1.4.1-2-gfad0