1 # primitive for editing text
6 # some fields for scanning incrementally through a gap-buffer
11 fn initialize-gap-buffer _self: (addr gap-buffer), capacity: int {
12 var self/esi: (addr gap-buffer) <- copy _self
13 var left/eax: (addr grapheme-stack) <- get self, left
14 initialize-grapheme-stack left, capacity
15 var right/eax: (addr grapheme-stack) <- get self, right
16 initialize-grapheme-stack right, capacity
19 fn clear-gap-buffer _self: (addr gap-buffer) {
20 var self/esi: (addr gap-buffer) <- copy _self
21 var left/eax: (addr grapheme-stack) <- get self, left
22 clear-grapheme-stack left
23 var right/eax: (addr grapheme-stack) <- get self, right
24 clear-grapheme-stack right
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
31 var left/eax: (addr grapheme-stack) <- get self, left
32 var result/eax: boolean <- grapheme-stack-empty? left
33 compare result, 0/false
37 # return empty?(right)
38 var left/eax: (addr grapheme-stack) <- get self, left
39 var result/eax: boolean <- grapheme-stack-empty? left
43 fn gap-buffer-capacity _gap: (addr gap-buffer) -> _/edx: int {
44 var gap/esi: (addr gap-buffer) <- copy _gap
45 var left/eax: (addr grapheme-stack) <- get gap, left
46 var left-data-ah/eax: (addr handle array code-point-utf8) <- get left, data
47 var left-data/eax: (addr array code-point-utf8) <- lookup *left-data-ah
48 var result/eax: int <- length left-data
52 fn initialize-gap-buffer-with self: (addr gap-buffer), keys: (addr array byte) {
53 initialize-gap-buffer self, 0x40/capacity
54 var input-stream-storage: (stream byte 0x40/capacity)
55 var input-stream/ecx: (addr stream byte) <- address input-stream-storage
56 write input-stream, keys
58 var done?/eax: boolean <- stream-empty? input-stream
59 compare done?, 0/false
61 var g/eax: code-point-utf8 <- read-code-point-utf8 input-stream
62 add-code-point-utf8-at-gap self, g
67 fn load-gap-buffer-from-stream self: (addr gap-buffer), in: (addr stream byte) {
70 var done?/eax: boolean <- stream-empty? in
71 compare done?, 0/false
73 var key/eax: byte <- read-byte in
76 var g/eax: code-point-utf8 <- copy key
77 edit-gap-buffer self, g
82 fn emit-gap-buffer self: (addr gap-buffer), out: (addr stream byte) {
84 append-gap-buffer self, out
87 fn append-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) {
88 var self/esi: (addr gap-buffer) <- copy _self
89 var left/eax: (addr grapheme-stack) <- get self, left
90 emit-stack-from-bottom left, out
91 var right/eax: (addr grapheme-stack) <- get self, right
92 emit-stack-from-top right, out
95 # dump stack from bottom to top
96 fn emit-stack-from-bottom _self: (addr grapheme-stack), out: (addr stream byte) {
97 var self/esi: (addr grapheme-stack) <- copy _self
98 var data-ah/edi: (addr handle array code-point-utf8) <- get self, data
99 var _data/eax: (addr array code-point-utf8) <- lookup *data-ah
100 var data/edi: (addr array code-point-utf8) <- copy _data
101 var top-addr/ecx: (addr int) <- get self, top
102 var i/eax: int <- copy 0
106 var g/edx: (addr code-point-utf8) <- index data, i
107 write-code-point-utf8 out, *g
113 # dump stack from top to bottom
114 fn emit-stack-from-top _self: (addr grapheme-stack), out: (addr stream byte) {
115 var self/esi: (addr grapheme-stack) <- copy _self
116 var data-ah/edi: (addr handle array code-point-utf8) <- get self, data
117 var _data/eax: (addr array code-point-utf8) <- lookup *data-ah
118 var data/edi: (addr array code-point-utf8) <- copy _data
119 var top-addr/ecx: (addr int) <- get self, top
120 var i/eax: int <- copy *top-addr
125 var g/edx: (addr code-point-utf8) <- index data, i
126 write-code-point-utf8 out, *g
132 fn word-at-gap _self: (addr gap-buffer), out: (addr stream byte) {
133 var self/esi: (addr gap-buffer) <- copy _self
136 var g/eax: code-point-utf8 <- code-point-utf8-at-gap self
137 var at-word?/eax: boolean <- is-ascii-word-code-point-utf8? g
138 compare at-word?, 0/false
142 var left/ecx: (addr grapheme-stack) <- get self, left
143 var left-index/eax: int <- top-most-word left
144 emit-stack-from-index left, left-index, out
145 var right/ecx: (addr grapheme-stack) <- get self, right
146 var right-index/eax: int <- top-most-word right
147 emit-stack-to-index right, right-index, out
150 fn test-word-at-gap-single-word-with-gap-at-end {
152 var g/esi: (addr gap-buffer) <- address _g
153 initialize-gap-buffer-with g, "abc"
154 # gap is at end (right is empty)
155 var out-storage: (stream byte 0x10)
156 var out/eax: (addr stream byte) <- address out-storage
158 check-stream-equal out, "abc", "F - test-word-at-gap-single-word-with-gap-at-end"
161 fn test-word-at-gap-single-word-with-gap-at-start {
163 var g/esi: (addr gap-buffer) <- address _g
164 initialize-gap-buffer-with g, "abc"
167 var out-storage: (stream byte 0x10)
168 var out/eax: (addr stream byte) <- address out-storage
170 check-stream-equal out, "abc", "F - test-word-at-gap-single-word-with-gap-at-start"
173 fn test-word-at-gap-multiple-words-with-gap-at-non-word-code-point-utf8-at-end {
175 var g/esi: (addr gap-buffer) <- address _g
176 initialize-gap-buffer-with g, "abc "
177 # gap is at end (right is empty)
178 var out-storage: (stream byte 0x10)
179 var out/eax: (addr stream byte) <- address out-storage
181 check-stream-equal out, "", "F - test-word-at-gap-multiple-words-with-gap-at-non-word-code-point-utf8-at-end"
184 fn test-word-at-gap-multiple-words-with-gap-at-non-word-code-point-utf8-at-start {
186 var g/esi: (addr gap-buffer) <- address _g
187 initialize-gap-buffer-with g, " abc"
190 var out-storage: (stream byte 0x10)
191 var out/eax: (addr stream byte) <- address out-storage
193 check-stream-equal out, "", "F - test-word-at-gap-multiple-words-with-gap-at-non-word-code-point-utf8-at-start"
196 fn test-word-at-gap-multiple-words-with-gap-at-end {
198 var g/esi: (addr gap-buffer) <- address _g
199 initialize-gap-buffer-with g, "a bc d"
200 # gap is at end (right is empty)
201 var out-storage: (stream byte 0x10)
202 var out/eax: (addr stream byte) <- address out-storage
204 check-stream-equal out, "d", "F - test-word-at-gap-multiple-words-with-gap-at-end"
207 fn test-word-at-gap-multiple-words-with-gap-at-initial-word {
209 var g/esi: (addr gap-buffer) <- address _g
210 initialize-gap-buffer-with g, "a bc d"
213 var out-storage: (stream byte 0x10)
214 var out/eax: (addr stream byte) <- address out-storage
216 check-stream-equal out, "a", "F - test-word-at-gap-multiple-words-with-gap-at-initial-word"
219 fn test-word-at-gap-multiple-words-with-gap-at-final-word {
221 var g/esi: (addr gap-buffer) <- address _g
222 initialize-gap-buffer-with g, "a bc d"
223 var dummy/eax: code-point-utf8 <- gap-left g
224 # gap is at final word
225 var out-storage: (stream byte 0x10)
226 var out/eax: (addr stream byte) <- address out-storage
228 check-stream-equal out, "d", "F - test-word-at-gap-multiple-words-with-gap-at-final-word"
231 fn test-word-at-gap-multiple-words-with-gap-at-final-non-word {
233 var g/esi: (addr gap-buffer) <- address _g
234 initialize-gap-buffer-with g, "abc "
235 var dummy/eax: code-point-utf8 <- gap-left g
236 # gap is at final word
237 var out-storage: (stream byte 0x10)
238 var out/eax: (addr stream byte) <- address out-storage
240 check-stream-equal out, "", "F - test-word-at-gap-multiple-words-with-gap-at-final-non-word"
243 fn code-point-utf8-at-gap _self: (addr gap-buffer) -> _/eax: code-point-utf8 {
244 # send top of right most of the time
245 var self/esi: (addr gap-buffer) <- copy _self
246 var right/edi: (addr grapheme-stack) <- get self, right
247 var data-ah/eax: (addr handle array code-point-utf8) <- get right, data
248 var data/eax: (addr array code-point-utf8) <- lookup *data-ah
249 var top-addr/ecx: (addr int) <- get right, top
253 var top/ecx: int <- copy *top-addr
255 var result/eax: (addr code-point-utf8) <- index data, top
258 # send top of left only if right is empty
259 var left/edi: (addr grapheme-stack) <- get self, left
260 var data-ah/eax: (addr handle array code-point-utf8) <- get left, data
261 var data/eax: (addr array code-point-utf8) <- lookup *data-ah
262 var top-addr/ecx: (addr int) <- get left, top
266 var top/ecx: int <- copy *top-addr
268 var result/eax: (addr code-point-utf8) <- index data, top
271 # send null if everything is empty
275 fn top-most-word _self: (addr grapheme-stack) -> _/eax: int {
276 var self/esi: (addr grapheme-stack) <- copy _self
277 var data-ah/edi: (addr handle array code-point-utf8) <- get self, data
278 var _data/eax: (addr array code-point-utf8) <- lookup *data-ah
279 var data/edi: (addr array code-point-utf8) <- copy _data
280 var top-addr/ecx: (addr int) <- get self, top
281 var i/ebx: int <- copy *top-addr
286 var g/edx: (addr code-point-utf8) <- index data, i
287 var is-word?/eax: boolean <- is-ascii-word-code-point-utf8? *g
288 compare is-word?, 0/false
297 fn emit-stack-from-index _self: (addr grapheme-stack), start: int, out: (addr stream byte) {
298 var self/esi: (addr grapheme-stack) <- copy _self
299 var data-ah/edi: (addr handle array code-point-utf8) <- get self, data
300 var _data/eax: (addr array code-point-utf8) <- lookup *data-ah
301 var data/edi: (addr array code-point-utf8) <- copy _data
302 var top-addr/ecx: (addr int) <- get self, top
303 var i/eax: int <- copy start
307 var g/edx: (addr code-point-utf8) <- index data, i
308 write-code-point-utf8 out, *g
314 fn emit-stack-to-index _self: (addr grapheme-stack), end: int, out: (addr stream byte) {
315 var self/esi: (addr grapheme-stack) <- copy _self
316 var data-ah/edi: (addr handle array code-point-utf8) <- get self, data
317 var _data/eax: (addr array code-point-utf8) <- lookup *data-ah
318 var data/edi: (addr array code-point-utf8) <- copy _data
319 var top-addr/ecx: (addr int) <- get self, top
320 var i/eax: int <- copy *top-addr
327 var g/edx: (addr code-point-utf8) <- index data, i
328 write-code-point-utf8 out, *g
334 fn is-ascii-word-code-point-utf8? g: code-point-utf8 -> _/eax: boolean {
383 # We implicitly render everything editable in a single color, and assume the
384 # cursor is a single other color.
385 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 {
386 var gap/esi: (addr gap-buffer) <- copy _gap
387 var left/edx: (addr grapheme-stack) <- get gap, left
388 var highlight-matching-open-paren?/ebx: boolean <- copy 0/false
389 var matching-open-paren-depth/edi: int <- copy 0
390 highlight-matching-open-paren?, matching-open-paren-depth <- highlight-matching-open-paren? gap, render-cursor?
391 var x2/eax: int <- copy 0
392 var y2/ecx: int <- copy 0
393 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
394 var right/edx: (addr grapheme-stack) <- get gap, right
395 x2, y2 <- render-stack-from-top-wrapping-right-then-down screen, right, xmin, ymin, xmax, ymax, x2, y2, render-cursor?, color, background-color
396 # decide whether we still need to print a cursor
397 var fg/edi: int <- copy color
398 var bg/ebx: int <- copy background-color
399 compare render-cursor?, 0/false
402 # if the right side is empty, code-point-utf8 stack didn't print the cursor
403 var empty?/eax: boolean <- grapheme-stack-empty? right
404 compare empty?, 0/false
406 # swap foreground and background
407 fg <- copy background-color
410 # print a code-point-utf8 either way so that cursor position doesn't affect printed width
411 var space/edx: code-point <- copy 0x20
412 x2, y2 <- render-code-point screen, space, xmin, ymin, xmax, ymax, x2, y2, fg, bg
416 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 {
417 var _width/eax: int <- copy 0
418 var _height/ecx: int <- copy 0
419 _width, _height <- screen-size screen
420 var width/edx: int <- copy _width
421 var height/ebx: int <- copy _height
422 var x2/eax: int <- copy 0
423 var y2/ecx: int <- copy 0
424 x2, y2 <- render-gap-buffer-wrapping-right-then-down screen, gap, x, y, width, height, render-cursor?, color, background-color
428 fn gap-buffer-length _gap: (addr gap-buffer) -> _/eax: int {
429 var gap/esi: (addr gap-buffer) <- copy _gap
430 var left/eax: (addr grapheme-stack) <- get gap, left
431 var tmp/eax: (addr int) <- get left, top
432 var left-length/ecx: int <- copy *tmp
433 var right/esi: (addr grapheme-stack) <- get gap, right
434 tmp <- get right, top
435 var result/eax: int <- copy *tmp
436 result <- add left-length
440 fn add-code-point-utf8-at-gap _self: (addr gap-buffer), g: code-point-utf8 {
441 var self/esi: (addr gap-buffer) <- copy _self
442 var left/eax: (addr grapheme-stack) <- get self, left
443 push-grapheme-stack left, g
446 fn add-code-point-at-gap self: (addr gap-buffer), c: code-point {
447 var g/eax: code-point-utf8 <- copy c
448 add-code-point-utf8-at-gap self, g
451 fn gap-to-start self: (addr gap-buffer) {
453 var curr/eax: code-point-utf8 <- gap-left self
459 fn gap-to-end self: (addr gap-buffer) {
461 var curr/eax: code-point-utf8 <- gap-right self
467 fn gap-at-start? _self: (addr gap-buffer) -> _/eax: boolean {
468 var self/esi: (addr gap-buffer) <- copy _self
469 var left/eax: (addr grapheme-stack) <- get self, left
470 var result/eax: boolean <- grapheme-stack-empty? left
474 fn gap-at-end? _self: (addr gap-buffer) -> _/eax: boolean {
475 var self/esi: (addr gap-buffer) <- copy _self
476 var right/eax: (addr grapheme-stack) <- get self, right
477 var result/eax: boolean <- grapheme-stack-empty? right
481 fn gap-right _self: (addr gap-buffer) -> _/eax: code-point-utf8 {
482 var self/esi: (addr gap-buffer) <- copy _self
483 var g/eax: code-point-utf8 <- copy 0
484 var right/ecx: (addr grapheme-stack) <- get self, right
485 g <- pop-grapheme-stack right
489 var left/ecx: (addr grapheme-stack) <- get self, left
490 push-grapheme-stack left, g
495 fn gap-left _self: (addr gap-buffer) -> _/eax: code-point-utf8 {
496 var self/esi: (addr gap-buffer) <- copy _self
497 var g/eax: code-point-utf8 <- copy 0
499 var left/ecx: (addr grapheme-stack) <- get self, left
500 g <- pop-grapheme-stack left
505 var right/ecx: (addr grapheme-stack) <- get self, right
506 push-grapheme-stack right, g
511 fn index-of-gap _self: (addr gap-buffer) -> _/eax: int {
512 var self/eax: (addr gap-buffer) <- copy _self
513 var left/eax: (addr grapheme-stack) <- get self, left
514 var top-addr/eax: (addr int) <- get left, top
515 var result/eax: int <- copy *top-addr
519 fn first-code-point-utf8-in-gap-buffer _self: (addr gap-buffer) -> _/eax: code-point-utf8 {
520 var self/esi: (addr gap-buffer) <- copy _self
521 # try to read from left
522 var left/eax: (addr grapheme-stack) <- get self, left
523 var top-addr/ecx: (addr int) <- get left, top
527 var data-ah/eax: (addr handle array code-point-utf8) <- get left, data
528 var data/eax: (addr array code-point-utf8) <- lookup *data-ah
529 var result-addr/eax: (addr code-point-utf8) <- index data, 0
532 # try to read from right
533 var right/eax: (addr grapheme-stack) <- get self, right
534 top-addr <- get right, top
538 var data-ah/eax: (addr handle array code-point-utf8) <- get right, data
539 var data/eax: (addr array code-point-utf8) <- lookup *data-ah
540 var top/ecx: int <- copy *top-addr
542 var result-addr/eax: (addr code-point-utf8) <- index data, top
549 fn code-point-utf8-before-cursor-in-gap-buffer _self: (addr gap-buffer) -> _/eax: code-point-utf8 {
550 var self/esi: (addr gap-buffer) <- copy _self
551 # try to read from left
552 var left/ecx: (addr grapheme-stack) <- get self, left
553 var top-addr/edx: (addr int) <- get left, top
557 var result/eax: code-point-utf8 <- pop-grapheme-stack left
558 push-grapheme-stack left, result
565 fn delete-before-gap _self: (addr gap-buffer) {
566 var self/eax: (addr gap-buffer) <- copy _self
567 var left/eax: (addr grapheme-stack) <- get self, left
568 var dummy/eax: code-point-utf8 <- pop-grapheme-stack left
571 fn pop-after-gap _self: (addr gap-buffer) -> _/eax: code-point-utf8 {
572 var self/eax: (addr gap-buffer) <- copy _self
573 var right/eax: (addr grapheme-stack) <- get self, right
574 var result/eax: code-point-utf8 <- pop-grapheme-stack right
578 fn gap-buffer-equal? _self: (addr gap-buffer), s: (addr array byte) -> _/eax: boolean {
579 var self/esi: (addr gap-buffer) <- copy _self
580 # complication: code-point-utf8s may be multiple bytes
581 # so don't rely on length
582 # instead turn the expected result into a stream and arrange to read from it in order
583 var stream-storage: (stream byte 0x10/capacity)
584 var expected-stream/ecx: (addr stream byte) <- address stream-storage
585 write expected-stream, s
587 var left/edx: (addr grapheme-stack) <- get self, left
588 var result/eax: boolean <- prefix-match? left, expected-stream
589 compare result, 0/false
595 var right/edx: (addr grapheme-stack) <- get self, right
596 result <- suffix-match? right, expected-stream
597 compare result, 0/false
602 # ensure there's nothing left over
603 result <- stream-empty? expected-stream
607 fn test-gap-buffer-equal-from-end {
609 var g/esi: (addr gap-buffer) <- address _g
610 initialize-gap-buffer g, 0x10
612 add-code-point-at-gap g, 0x61/a
613 add-code-point-at-gap g, 0x61/a
614 add-code-point-at-gap g, 0x61/a
615 # gap is at end (right is empty)
616 var result/eax: boolean <- gap-buffer-equal? g, "aaa"
617 check result, "F - test-gap-buffer-equal-from-end"
620 fn test-gap-buffer-equal-from-middle {
622 var g/esi: (addr gap-buffer) <- address _g
623 initialize-gap-buffer g, 0x10
625 add-code-point-at-gap g, 0x61/a
626 add-code-point-at-gap g, 0x61/a
627 add-code-point-at-gap g, 0x61/a
628 var dummy/eax: code-point-utf8 <- gap-left g
629 # gap is in the middle
630 var result/eax: boolean <- gap-buffer-equal? g, "aaa"
631 check result, "F - test-gap-buffer-equal-from-middle"
634 fn test-gap-buffer-equal-from-start {
636 var g/esi: (addr gap-buffer) <- address _g
637 initialize-gap-buffer g, 0x10
639 add-code-point-at-gap g, 0x61/a
640 add-code-point-at-gap g, 0x61/a
641 add-code-point-at-gap g, 0x61/a
642 var dummy/eax: code-point-utf8 <- gap-left g
645 # gap is at the start
646 var result/eax: boolean <- gap-buffer-equal? g, "aaa"
647 check result, "F - test-gap-buffer-equal-from-start"
650 fn test-gap-buffer-equal-fails {
653 var g/esi: (addr gap-buffer) <- address _g
654 initialize-gap-buffer g, 0x10
655 add-code-point-at-gap g, 0x61/a
656 add-code-point-at-gap g, 0x61/a
657 add-code-point-at-gap g, 0x61/a
659 var result/eax: boolean <- gap-buffer-equal? g, "aa"
660 check-not result, "F - test-gap-buffer-equal-fails"
663 fn gap-buffers-equal? self: (addr gap-buffer), g: (addr gap-buffer) -> _/eax: boolean {
664 var tmp/eax: int <- gap-buffer-length self
665 var len/ecx: int <- copy tmp
666 var leng/eax: int <- gap-buffer-length g
672 var i/edx: int <- copy 0
677 var tmp/eax: code-point-utf8 <- gap-index self, i
678 var curr/ecx: code-point-utf8 <- copy tmp
679 var currg/eax: code-point-utf8 <- gap-index g, i
690 fn gap-index _self: (addr gap-buffer), _n: int -> _/eax: code-point-utf8 {
691 var self/esi: (addr gap-buffer) <- copy _self
692 var n/ebx: int <- copy _n
693 # if n < left->length, index into left
694 var left/edi: (addr grapheme-stack) <- get self, left
695 var left-len-a/edx: (addr int) <- get left, top
696 compare n, *left-len-a
699 var data-ah/eax: (addr handle array code-point-utf8) <- get left, data
700 var data/eax: (addr array code-point-utf8) <- lookup *data-ah
701 var result/eax: (addr code-point-utf8) <- index data, n
705 n <- subtract *left-len-a
706 # if n < right->length, index into right
707 var right/edi: (addr grapheme-stack) <- get self, right
708 var right-len-a/edx: (addr int) <- get right, top
709 compare n, *right-len-a
712 var data-ah/eax: (addr handle array code-point-utf8) <- get right, data
713 var data/eax: (addr array code-point-utf8) <- lookup *data-ah
714 # idx = right->len - n - 1
715 var idx/ebx: int <- copy n
716 idx <- subtract *right-len-a
719 var result/eax: (addr code-point-utf8) <- index data, idx
723 abort "gap-index: out of bounds"
727 fn test-gap-buffers-equal? {
729 var a/esi: (addr gap-buffer) <- address _a
730 initialize-gap-buffer-with a, "abc"
732 var b/edi: (addr gap-buffer) <- address _b
733 initialize-gap-buffer-with b, "abc"
735 var c/ebx: (addr gap-buffer) <- address _c
736 initialize-gap-buffer-with c, "ab"
738 var d/edx: (addr gap-buffer) <- address _d
739 initialize-gap-buffer-with d, "abd"
741 var result/eax: boolean <- gap-buffers-equal? a, a
742 check result, "F - test-gap-buffers-equal? - reflexive"
743 result <- gap-buffers-equal? a, b
744 check result, "F - test-gap-buffers-equal? - equal"
746 result <- gap-buffers-equal? a, c
747 check-not result, "F - test-gap-buffers-equal? - not equal"
749 result <- gap-buffers-equal? a, d
750 check-not result, "F - test-gap-buffers-equal? - not equal 2"
751 result <- gap-buffers-equal? d, a
752 check-not result, "F - test-gap-buffers-equal? - not equal 3"
755 fn test-gap-buffer-index {
756 var gap-storage: gap-buffer
757 var gap/esi: (addr gap-buffer) <- address gap-storage
758 initialize-gap-buffer-with gap, "abc"
759 # gap is at end, all contents are in left
760 var g/eax: code-point-utf8 <- gap-index gap, 0
761 var x/ecx: int <- copy g
762 check-ints-equal x, 0x61/a, "F - test-gap-index/left-1"
763 var g/eax: code-point-utf8 <- gap-index gap, 1
764 var x/ecx: int <- copy g
765 check-ints-equal x, 0x62/b, "F - test-gap-index/left-2"
766 var g/eax: code-point-utf8 <- gap-index gap, 2
767 var x/ecx: int <- copy g
768 check-ints-equal x, 0x63/c, "F - test-gap-index/left-3"
769 # now check when everything is to the right
771 rewind-gap-buffer gap
772 var g/eax: code-point-utf8 <- gap-index gap, 0
773 var x/ecx: int <- copy g
774 check-ints-equal x, 0x61/a, "F - test-gap-index/right-1"
775 var g/eax: code-point-utf8 <- gap-index gap, 1
776 var x/ecx: int <- copy g
777 check-ints-equal x, 0x62/b, "F - test-gap-index/right-2"
778 var g/eax: code-point-utf8 <- gap-index gap, 2
779 var x/ecx: int <- copy g
780 check-ints-equal x, 0x63/c, "F - test-gap-index/right-3"
783 fn copy-gap-buffer _src-ah: (addr handle gap-buffer), _dest-ah: (addr handle gap-buffer) {
784 # obtain src-a, dest-a
785 var src-ah/eax: (addr handle gap-buffer) <- copy _src-ah
786 var _src-a/eax: (addr gap-buffer) <- lookup *src-ah
787 var src-a/esi: (addr gap-buffer) <- copy _src-a
788 var dest-ah/eax: (addr handle gap-buffer) <- copy _dest-ah
789 var _dest-a/eax: (addr gap-buffer) <- lookup *dest-ah
790 var dest-a/edi: (addr gap-buffer) <- copy _dest-a
791 # copy left grapheme-stack
792 var src/ecx: (addr grapheme-stack) <- get src-a, left
793 var dest/edx: (addr grapheme-stack) <- get dest-a, left
794 copy-grapheme-stack src, dest
795 # copy right grapheme-stack
796 src <- get src-a, right
797 dest <- get dest-a, right
798 copy-grapheme-stack src, dest
801 fn gap-buffer-is-decimal-integer? _self: (addr gap-buffer) -> _/eax: boolean {
802 var self/esi: (addr gap-buffer) <- copy _self
803 var curr/ecx: (addr grapheme-stack) <- get self, left
804 var result/eax: boolean <- grapheme-stack-is-decimal-integer? curr
806 compare result, 0/false
808 curr <- get self, right
809 result <- grapheme-stack-is-decimal-integer? curr
814 fn test-render-gap-buffer-without-cursor {
816 var gap-storage: gap-buffer
817 var gap/esi: (addr gap-buffer) <- address gap-storage
818 initialize-gap-buffer-with gap, "abc"
820 var screen-storage: screen
821 var screen/edi: (addr screen) <- address screen-storage
822 initialize-screen screen, 5, 4, 0/no-pixel-graphics
824 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 0/no-cursor, 3/fg, 0xc5/bg=blue-bg
825 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-without-cursor"
826 check-ints-equal x, 4, "F - test-render-gap-buffer-without-cursor: result"
828 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " ", "F - test-render-gap-buffer-without-cursor: bg"
831 fn test-render-gap-buffer-with-cursor-at-end {
833 var gap-storage: gap-buffer
834 var gap/esi: (addr gap-buffer) <- address gap-storage
835 initialize-gap-buffer-with gap, "abc"
838 var screen-storage: screen
839 var screen/edi: (addr screen) <- address screen-storage
840 initialize-screen screen, 5, 4, 0/no-pixel-graphics
842 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg
843 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-end"
844 # we've drawn one extra code-point-utf8 for the cursor
845 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-end: result"
847 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " |", "F - test-render-gap-buffer-with-cursor-at-end: bg"
850 fn test-render-gap-buffer-with-cursor-in-middle {
852 var gap-storage: gap-buffer
853 var gap/esi: (addr gap-buffer) <- address gap-storage
854 initialize-gap-buffer-with gap, "abc"
856 var dummy/eax: code-point-utf8 <- gap-left gap
858 var screen-storage: screen
859 var screen/edi: (addr screen) <- address screen-storage
860 initialize-screen screen, 5, 4, 0/no-pixel-graphics
862 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg
863 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-in-middle"
864 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-in-middle: result"
866 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " | ", "F - test-render-gap-buffer-with-cursor-in-middle: bg"
869 fn test-render-gap-buffer-with-cursor-at-start {
870 var gap-storage: gap-buffer
871 var gap/esi: (addr gap-buffer) <- address gap-storage
872 initialize-gap-buffer-with gap, "abc"
875 var screen-storage: screen
876 var screen/edi: (addr screen) <- address screen-storage
877 initialize-screen screen, 5, 4, 0/no-pixel-graphics
879 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg
880 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-start"
881 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-start: result"
883 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, "| ", "F - test-render-gap-buffer-with-cursor-at-start: bg"
886 fn test-render-gap-buffer-highlight-matching-close-paren {
887 var gap-storage: gap-buffer
888 var gap/esi: (addr gap-buffer) <- address gap-storage
889 initialize-gap-buffer-with gap, "(a)"
892 var screen-storage: screen
893 var screen/edi: (addr screen) <- address screen-storage
894 initialize-screen screen, 5, 4, 0/no-pixel-graphics
896 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg
897 check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-close-paren"
898 check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-close-paren: result"
899 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, "| ", "F - test-render-gap-buffer-highlight-matching-close-paren: cursor"
900 check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, " ) ", "F - test-render-gap-buffer-highlight-matching-close-paren: matching paren"
903 fn test-render-gap-buffer-highlight-matching-open-paren {
904 var gap-storage: gap-buffer
905 var gap/esi: (addr gap-buffer) <- address gap-storage
906 initialize-gap-buffer-with gap, "(a)"
908 var dummy/eax: code-point-utf8 <- gap-left gap
910 var screen-storage: screen
911 var screen/edi: (addr screen) <- address screen-storage
912 initialize-screen screen, 5, 4, 0/no-pixel-graphics
914 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg
915 check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren"
916 check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren: result"
917 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " | ", "F - test-render-gap-buffer-highlight-matching-open-paren: cursor"
918 check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, "( ", "F - test-render-gap-buffer-highlight-matching-open-paren: matching paren"
921 fn test-render-gap-buffer-highlight-matching-open-paren-of-end {
922 var gap-storage: gap-buffer
923 var gap/esi: (addr gap-buffer) <- address gap-storage
924 initialize-gap-buffer-with gap, "(a)"
927 var screen-storage: screen
928 var screen/edi: (addr screen) <- address screen-storage
929 initialize-screen screen, 5, 4, 0/no-pixel-graphics
931 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor, 3/fg, 0xc5/bg=blue-bg
932 check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end"
933 check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: result"
934 check-background-color-in-screen-row screen, 3/bg=reverse, 0/y, " |", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: cursor"
935 check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, "( ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: matching paren"
938 # should I highlight a matching open paren? And if so, at what depth from top of left?
939 # basically there are two cases to disambiguate here:
940 # Usually the cursor is at top of right. Highlight first '(' at depth 0 from top of left.
941 # If right is empty, match the ')' _before_ cursor. Highlight first '(' at depth _1_ from top of left.
942 fn highlight-matching-open-paren? _gap: (addr gap-buffer), render-cursor?: boolean -> _/ebx: boolean, _/edi: int {
943 # if not rendering cursor, return
944 compare render-cursor?, 0/false
949 var gap/esi: (addr gap-buffer) <- copy _gap
950 var stack/edi: (addr grapheme-stack) <- get gap, right
951 var top-addr/eax: (addr int) <- get stack, top
952 var top-index/ecx: int <- copy *top-addr
956 # if cursor at end, return (char before cursor == ')', 1)
957 stack <- get gap, left
958 top-addr <- get stack, top
959 top-index <- copy *top-addr
965 top-index <- decrement
966 var data-ah/eax: (addr handle array code-point-utf8) <- get stack, data
967 var data/eax: (addr array code-point-utf8) <- lookup *data-ah
968 var g/eax: (addr code-point-utf8) <- index data, top-index
969 compare *g, 0x29/close-paren
976 # cursor is not at end; return (char at cursor == ')')
977 top-index <- decrement
978 var data-ah/eax: (addr handle array code-point-utf8) <- get stack, data
979 var data/eax: (addr array code-point-utf8) <- lookup *data-ah
980 var g/eax: (addr code-point-utf8) <- index data, top-index
981 compare *g, 0x29/close-paren
989 fn test-highlight-matching-open-paren {
990 var gap-storage: gap-buffer
991 var gap/esi: (addr gap-buffer) <- address gap-storage
992 initialize-gap-buffer-with gap, "(a)"
994 var highlight-matching-open-paren?/ebx: boolean <- copy 0/false
995 var open-paren-depth/edi: int <- copy 0
996 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 0/no-cursor
997 check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: no cursor"
998 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor
999 check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: at end immediately after ')'"
1000 check-ints-equal open-paren-depth, 1, "F - test-highlight-matching-open-paren: depth at end immediately after ')'"
1001 var dummy/eax: code-point-utf8 <- gap-left gap
1002 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor
1003 check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: on ')'"
1004 dummy <- gap-left gap
1005 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor
1006 check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: not on ')'"
1009 ## some primitives for scanning through a gap buffer
1010 # don't modify the gap buffer while scanning
1011 # this includes moving the cursor around
1013 # restart scan without affecting gap-buffer contents
1014 fn rewind-gap-buffer _self: (addr gap-buffer) {
1015 var self/esi: (addr gap-buffer) <- copy _self
1016 var dest/eax: (addr int) <- get self, left-read-index
1018 dest <- get self, right-read-index
1022 fn gap-buffer-scan-done? _self: (addr gap-buffer) -> _/eax: boolean {
1023 var self/esi: (addr gap-buffer) <- copy _self
1025 var left/eax: (addr grapheme-stack) <- get self, left
1026 var left-size/eax: int <- grapheme-stack-length left
1027 var left-read-index/ecx: (addr int) <- get self, left-read-index
1028 compare *left-read-index, left-size
1034 var right/eax: (addr grapheme-stack) <- get self, right
1035 var right-size/eax: int <- grapheme-stack-length right
1036 var right-read-index/ecx: (addr int) <- get self, right-read-index
1037 compare *right-read-index, right-size
1046 fn peek-from-gap-buffer _self: (addr gap-buffer) -> _/eax: code-point-utf8 {
1047 var self/esi: (addr gap-buffer) <- copy _self
1049 var left/ecx: (addr grapheme-stack) <- get self, left
1050 var left-size/eax: int <- grapheme-stack-length left
1051 var left-read-index-a/edx: (addr int) <- get self, left-read-index
1052 compare *left-read-index-a, left-size
1055 var left-data-ah/eax: (addr handle array code-point-utf8) <- get left, data
1056 var left-data/eax: (addr array code-point-utf8) <- lookup *left-data-ah
1057 var left-read-index/ecx: int <- copy *left-read-index-a
1058 var result/eax: (addr code-point-utf8) <- index left-data, left-read-index
1062 var right/ecx: (addr grapheme-stack) <- get self, right
1063 var _right-size/eax: int <- grapheme-stack-length right
1064 var right-size/ebx: int <- copy _right-size
1065 var right-read-index-a/edx: (addr int) <- get self, right-read-index
1066 compare *right-read-index-a, right-size
1069 # read the right from reverse
1070 var right-data-ah/eax: (addr handle array code-point-utf8) <- get right, data
1071 var right-data/eax: (addr array code-point-utf8) <- lookup *right-data-ah
1072 var right-read-index/ebx: int <- copy right-size
1073 right-read-index <- subtract *right-read-index-a
1074 right-read-index <- subtract 1
1075 var result/eax: (addr code-point-utf8) <- index right-data, right-read-index
1078 # if we get here there's nothing left
1082 fn read-from-gap-buffer _self: (addr gap-buffer) -> _/eax: code-point-utf8 {
1083 var self/esi: (addr gap-buffer) <- copy _self
1085 var left/ecx: (addr grapheme-stack) <- get self, left
1086 var left-size/eax: int <- grapheme-stack-length left
1087 var left-read-index-a/edx: (addr int) <- get self, left-read-index
1088 compare *left-read-index-a, left-size
1091 var left-data-ah/eax: (addr handle array code-point-utf8) <- get left, data
1092 var left-data/eax: (addr array code-point-utf8) <- lookup *left-data-ah
1093 var left-read-index/ecx: int <- copy *left-read-index-a
1094 var result/eax: (addr code-point-utf8) <- index left-data, left-read-index
1095 increment *left-read-index-a
1099 var right/ecx: (addr grapheme-stack) <- get self, right
1100 var _right-size/eax: int <- grapheme-stack-length right
1101 var right-size/ebx: int <- copy _right-size
1102 var right-read-index-a/edx: (addr int) <- get self, right-read-index
1103 compare *right-read-index-a, right-size
1106 # read the right from reverse
1107 var right-data-ah/eax: (addr handle array code-point-utf8) <- get right, data
1108 var right-data/eax: (addr array code-point-utf8) <- lookup *right-data-ah
1109 var right-read-index/ebx: int <- copy right-size
1110 right-read-index <- subtract *right-read-index-a
1111 right-read-index <- subtract 1
1112 var result/eax: (addr code-point-utf8) <- index right-data, right-read-index
1113 increment *right-read-index-a
1116 # if we get here there's nothing left
1120 fn put-back-from-gap-buffer _self: (addr gap-buffer) {
1121 var self/esi: (addr gap-buffer) <- copy _self
1123 var right/eax: (addr grapheme-stack) <- get self, right
1124 var right-size/eax: int <- grapheme-stack-length right
1125 var right-read-index-a/eax: (addr int) <- get self, right-read-index
1126 compare *right-read-index-a, 0
1129 decrement *right-read-index-a
1133 var left/eax: (addr grapheme-stack) <- get self, left
1134 var left-size/eax: int <- grapheme-stack-length left
1135 var left-read-index-a/eax: (addr int) <- get self, left-read-index
1136 decrement *left-read-index-a
1139 fn test-read-from-gap-buffer {
1140 var gap-storage: gap-buffer
1141 var gap/esi: (addr gap-buffer) <- address gap-storage
1142 initialize-gap-buffer-with gap, "abc"
1143 # gap is at end, all contents are in left
1144 var done?/eax: boolean <- gap-buffer-scan-done? gap
1145 check-not done?, "F - test-read-from-gap-buffer/left-1/done"
1146 var g/eax: code-point-utf8 <- read-from-gap-buffer gap
1147 var x/ecx: int <- copy g
1148 check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/left-1"
1149 var done?/eax: boolean <- gap-buffer-scan-done? gap
1150 check-not done?, "F - test-read-from-gap-buffer/left-2/done"
1151 var g/eax: code-point-utf8 <- read-from-gap-buffer gap
1152 var x/ecx: int <- copy g
1153 check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/left-2"
1154 var done?/eax: boolean <- gap-buffer-scan-done? gap
1155 check-not done?, "F - test-read-from-gap-buffer/left-3/done"
1156 var g/eax: code-point-utf8 <- read-from-gap-buffer gap
1157 var x/ecx: int <- copy g
1158 check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/left-3"
1159 var done?/eax: boolean <- gap-buffer-scan-done? gap
1160 check done?, "F - test-read-from-gap-buffer/left-4/done"
1161 var g/eax: code-point-utf8 <- read-from-gap-buffer gap
1162 var x/ecx: int <- copy g
1163 check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/left-4"
1164 # now check when everything is to the right
1166 rewind-gap-buffer gap
1167 var done?/eax: boolean <- gap-buffer-scan-done? gap
1168 check-not done?, "F - test-read-from-gap-buffer/right-1/done"
1169 var g/eax: code-point-utf8 <- read-from-gap-buffer gap
1170 var x/ecx: int <- copy g
1171 check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/right-1"
1172 var done?/eax: boolean <- gap-buffer-scan-done? gap
1173 check-not done?, "F - test-read-from-gap-buffer/right-2/done"
1174 var g/eax: code-point-utf8 <- read-from-gap-buffer gap
1175 var x/ecx: int <- copy g
1176 check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/right-2"
1177 var done?/eax: boolean <- gap-buffer-scan-done? gap
1178 check-not done?, "F - test-read-from-gap-buffer/right-3/done"
1179 var g/eax: code-point-utf8 <- read-from-gap-buffer gap
1180 var x/ecx: int <- copy g
1181 check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/right-3"
1182 var done?/eax: boolean <- gap-buffer-scan-done? gap
1183 check done?, "F - test-read-from-gap-buffer/right-4/done"
1184 var g/eax: code-point-utf8 <- read-from-gap-buffer gap
1185 var x/ecx: int <- copy g
1186 check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/right-4"
1189 fn skip-spaces-from-gap-buffer self: (addr gap-buffer) {
1190 var done?/eax: boolean <- gap-buffer-scan-done? self
1191 compare done?, 0/false
1193 var g/eax: code-point-utf8 <- peek-from-gap-buffer self
1195 compare g, 0x20/space
1199 g <- read-from-gap-buffer self
1203 fn edit-gap-buffer self: (addr gap-buffer), key: code-point-utf8 {
1204 var g/edx: code-point-utf8 <- copy key
1206 compare g, 8/backspace
1208 delete-before-gap self
1212 compare g, 0x80/left-arrow
1214 var dummy/eax: code-point-utf8 <- gap-left self
1218 compare g, 0x83/right-arrow
1220 var dummy/eax: code-point-utf8 <- gap-right self
1226 gap-to-start-of-next-word self
1232 gap-to-end-of-previous-word self
1238 gap-to-previous-start-of-line self
1244 gap-to-next-end-of-line self
1248 compare g, 0x81/down-arrow
1254 compare g, 0x82/up-arrow
1260 compare g, 0x15/ctrl-u
1262 clear-gap-buffer self
1269 add-code-point-at-gap self, 0x20/space
1270 add-code-point-at-gap self, 0x20/space
1273 # default: insert character
1274 add-code-point-utf8-at-gap self, g
1277 fn gap-to-start-of-next-word self: (addr gap-buffer) {
1278 var curr/eax: code-point-utf8 <- copy 0
1279 # skip to next space
1281 curr <- gap-right self
1284 compare curr, 0x20/space
1286 compare curr, 0xa/newline
1292 curr <- gap-right self
1295 compare curr, 0x20/space
1297 compare curr, 0xa/space
1299 curr <- gap-left self
1304 fn gap-to-end-of-previous-word self: (addr gap-buffer) {
1305 var curr/eax: code-point-utf8 <- copy 0
1306 # skip to previous space
1308 curr <- gap-left self
1311 compare curr, 0x20/space
1313 compare curr, 0xa/newline
1317 # skip past all spaces but one
1319 curr <- gap-left self
1322 compare curr, 0x20/space
1324 compare curr, 0xa/space
1326 curr <- gap-right self
1331 fn gap-to-previous-start-of-line self: (addr gap-buffer) {
1332 # skip past immediate newline
1333 var dummy/eax: code-point-utf8 <- gap-left self
1334 # skip to previous newline
1336 dummy <- gap-left self
1343 compare dummy, 0xa/newline
1345 dummy <- gap-right self
1352 fn gap-to-next-end-of-line self: (addr gap-buffer) {
1353 # skip past immediate newline
1354 var dummy/eax: code-point-utf8 <- gap-right self
1355 # skip to next newline
1357 dummy <- gap-right self
1364 compare dummy, 0xa/newline
1366 dummy <- gap-left self
1373 fn gap-up self: (addr gap-buffer) {
1375 var col/edx: int <- count-columns-to-start-of-line self
1377 gap-to-previous-start-of-line self
1378 # skip ahead by up to col on previous line
1379 var i/ecx: int <- copy 0
1383 var curr/eax: code-point-utf8 <- gap-right self
1389 compare curr, 0xa/newline
1392 curr <- gap-left self
1400 fn gap-down self: (addr gap-buffer) {
1402 var col/edx: int <- count-columns-to-start-of-line self
1403 # skip to start of next line
1404 gap-to-end-of-line self
1405 var dummy/eax: code-point-utf8 <- gap-right self
1406 # skip ahead by up to col on previous line
1407 var i/ecx: int <- copy 0
1411 var curr/eax: code-point-utf8 <- gap-right self
1417 compare curr, 0xa/newline
1420 curr <- gap-left self
1428 fn count-columns-to-start-of-line self: (addr gap-buffer) -> _/edx: int {
1429 var count/edx: int <- copy 0
1430 var dummy/eax: code-point-utf8 <- copy 0
1431 # skip to previous newline
1433 dummy <- gap-left self
1440 compare dummy, 0xa/newline
1442 dummy <- gap-right self
1451 fn gap-to-end-of-line self: (addr gap-buffer) {
1452 var dummy/eax: code-point-utf8 <- copy 0
1453 # skip to next newline
1455 dummy <- gap-right self
1462 compare dummy, 0xa/newline
1464 dummy <- gap-left self