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