https://github.com/akkartik/mu/blob/main/shell/gap-buffer.mu
1
2
3 type gap-buffer {
4 left: grapheme-stack
5 right: grapheme-stack
6
7 left-read-index: int
8 right-read-index: int
9 }
10
11 fn initialize-gap-buffer _self: (addr gap-buffer), capacity: int {
12 var self/esi: (addr gap-buffer) <- copy _self
13 var left/eax: (addr grapheme-stack) <- get self, left
14 initialize-grapheme-stack left, capacity
15 var right/eax: (addr grapheme-stack) <- get self, right
16 initialize-grapheme-stack right, capacity
17 }
18
19 fn clear-gap-buffer _self: (addr gap-buffer) {
20 var self/esi: (addr gap-buffer) <- copy _self
21 var left/eax: (addr grapheme-stack) <- get self, left
22 clear-grapheme-stack left
23 var right/eax: (addr grapheme-stack) <- get self, right
24 clear-grapheme-stack right
25 }
26
27
28 fn initialize-gap-buffer-with self: (addr gap-buffer), s: (addr array byte) {
29 initialize-gap-buffer self, 0x10/capacity
30 var stream-storage: (stream byte 0x10/capacity)
31 var stream/ecx: (addr stream byte) <- address stream-storage
32 write stream, s
33 {
34 var done?/eax: boolean <- stream-empty? stream
35 compare done?, 0/false
36 break-if-!=
37 var g/eax: grapheme <- read-grapheme stream
38 add-grapheme-at-gap self, g
39 loop
40 }
41 }
42
43 fn load-gap-buffer-from-stream self: (addr gap-buffer), in: (addr stream byte) {
44 rewind-stream in
45 {
46 var done?/eax: boolean <- stream-empty? in
47 compare done?, 0/false
48 break-if-!=
49 var key/eax: byte <- read-byte in
50 compare key, 0/null
51 break-if-=
52 var g/eax: grapheme <- copy key
53 edit-gap-buffer self, g
54 loop
55 }
56 }
57
58 fn emit-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) {
59 var self/esi: (addr gap-buffer) <- copy _self
60 clear-stream out
61 var left/eax: (addr grapheme-stack) <- get self, left
62 emit-stack-from-bottom left, out
63 var right/eax: (addr grapheme-stack) <- get self, right
64 emit-stack-from-top right, out
65 }
66
67 fn append-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) {
68 var self/esi: (addr gap-buffer) <- copy _self
69 var left/eax: (addr grapheme-stack) <- get self, left
70 emit-stack-from-bottom left, out
71 var right/eax: (addr grapheme-stack) <- get self, right
72 emit-stack-from-top right, out
73 }
74
75
76 fn emit-stack-from-bottom _self: (addr grapheme-stack), out: (addr stream byte) {
77 var self/esi: (addr grapheme-stack) <- copy _self
78 var data-ah/edi: (addr handle array grapheme) <- get self, data
79 var _data/eax: (addr array grapheme) <- lookup *data-ah
80 var data/edi: (addr array grapheme) <- copy _data
81 var top-addr/ecx: (addr int) <- get self, top
82 var i/eax: int <- copy 0
83 {
84 compare i, *top-addr
85 break-if->=
86 var g/edx: (addr grapheme) <- index data, i
87 write-grapheme out, *g
88 i <- increment
89 loop
90 }
91 }
92
93
94 fn emit-stack-from-top _self: (addr grapheme-stack), out: (addr stream byte) {
95 var self/esi: (addr grapheme-stack) <- copy _self
96 var data-ah/edi: (addr handle array grapheme) <- get self, data
97 var _data/eax: (addr array grapheme) <- lookup *data-ah
98 var data/edi: (addr array grapheme) <- copy _data
99 var top-addr/ecx: (addr int) <- get self, top
100 var i/eax: int <- copy *top-addr
101 i <- decrement
102 {
103 compare i, 0
104 break-if-<
105 var g/edx: (addr grapheme) <- index data, i
106 write-grapheme out, *g
107 i <- decrement
108 loop
109 }
110 }
111
112
113
114 fn render-gap-buffer-wrapping-right-then-down screen: (addr screen), _gap: (addr gap-buffer), xmin: int, ymin: int, xmax: int, ymax: int, render-cursor?: boolean -> _/eax: int, _/ecx: int {
115 var gap/esi: (addr gap-buffer) <- copy _gap
116 var left/edx: (addr grapheme-stack) <- get gap, left
117 var highlight-matching-open-paren?/ebx: boolean <- copy 0/false
118 var matching-open-paren-depth/edi: int <- copy 0
119 highlight-matching-open-paren?, matching-open-paren-depth <- highlight-matching-open-paren? gap, render-cursor?
120 var x2/eax: int <- copy 0
121 var y2/ecx: int <- copy 0
122 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
123 var right/edx: (addr grapheme-stack) <- get gap, right
124 x2, y2 <- render-stack-from-top-wrapping-right-then-down screen, right, xmin, ymin, xmax, ymax, x2, y2, render-cursor?
125
126 var bg/ebx: int <- copy 0
127 compare render-cursor?, 0/false
128 {
129 break-if-=
130
131 var empty?/eax: boolean <- grapheme-stack-empty? right
132 compare empty?, 0/false
133 break-if-=
134 bg <- copy 7/cursor
135 }
136
137 var space/edx: grapheme <- copy 0x20
138 x2, y2 <- render-grapheme screen, space, xmin, ymin, xmax, ymax, x2, y2, 3/fg=cyan, bg
139 return x2, y2
140 }
141
142 fn render-gap-buffer screen: (addr screen), gap: (addr gap-buffer), x: int, y: int, render-cursor?: boolean -> _/eax: int {
143 var _width/eax: int <- copy 0
144 var _height/ecx: int <- copy 0
145 _width, _height <- screen-size screen
146 var width/edx: int <- copy _width
147 var height/ebx: int <- copy _height
148 var x2/eax: int <- copy 0
149 var y2/ecx: int <- copy 0
150 x2, y2 <- render-gap-buffer-wrapping-right-then-down screen, gap, x, y, width, height, render-cursor?
151 return x2
152 }
153
154 fn gap-buffer-length _gap: (addr gap-buffer) -> _/eax: int {
155 var gap/esi: (addr gap-buffer) <- copy _gap
156 var left/eax: (addr grapheme-stack) <- get gap, left
157 var tmp/eax: (addr int) <- get left, top
158 var left-length/ecx: int <- copy *tmp
159 var right/esi: (addr grapheme-stack) <- get gap, right
160 tmp <- get right, top
161 var result/eax: int <- copy *tmp
162 result <- add left-length
163 return result
164 }
165
166 fn add-grapheme-at-gap _self: (addr gap-buffer), g: grapheme {
167 var self/esi: (addr gap-buffer) <- copy _self
168 var left/eax: (addr grapheme-stack) <- get self, left
169 push-grapheme-stack left, g
170 }
171
172 fn add-code-point-at-gap self: (addr gap-buffer), c: code-point {
173 var g/eax: grapheme <- copy c
174 add-grapheme-at-gap self, g
175 }
176
177 fn gap-to-start self: (addr gap-buffer) {
178 {
179 var curr/eax: grapheme <- gap-left self
180 compare curr, -1
181 loop-if-!=
182 }
183 }
184
185 fn gap-to-end self: (addr gap-buffer) {
186 {
187 var curr/eax: grapheme <- gap-right self
188 compare curr, -1
189 loop-if-!=
190 }
191 }
192
193 fn gap-at-start? _self: (addr gap-buffer) -> _/eax: boolean {
194 var self/esi: (addr gap-buffer) <- copy _self
195 var left/eax: (addr grapheme-stack) <- get self, left
196 var result/eax: boolean <- grapheme-stack-empty? left
197 return result
198 }
199
200 fn gap-at-end? _self: (addr gap-buffer) -> _/eax: boolean {
201 var self/esi: (addr gap-buffer) <- copy _self
202 var right/eax: (addr grapheme-stack) <- get self, right
203 var result/eax: boolean <- grapheme-stack-empty? right
204 return result
205 }
206
207 fn gap-right _self: (addr gap-buffer) -> _/eax: grapheme {
208 var self/esi: (addr gap-buffer) <- copy _self
209 var g/eax: grapheme <- copy 0
210 var right/ecx: (addr grapheme-stack) <- get self, right
211 g <- pop-grapheme-stack right
212 compare g, -1
213 {
214 break-if-=
215 var left/ecx: (addr grapheme-stack) <- get self, left
216 push-grapheme-stack left, g
217 }
218 return g
219 }
220
221 fn gap-left _self: (addr gap-buffer) -> _/eax: grapheme {
222 var self/esi: (addr gap-buffer) <- copy _self
223 var g/eax: grapheme <- copy 0
224 {
225 var left/ecx: (addr grapheme-stack) <- get self, left
226 g <- pop-grapheme-stack left
227 }
228 compare g, -1
229 {
230 break-if-=
231 var right/ecx: (addr grapheme-stack) <- get self, right
232 push-grapheme-stack right, g
233 }
234 return g
235 }
236
237 fn index-of-gap _self: (addr gap-buffer) -> _/eax: int {
238 var self/eax: (addr gap-buffer) <- copy _self
239 var left/eax: (addr grapheme-stack) <- get self, left
240 var top-addr/eax: (addr int) <- get left, top
241 var result/eax: int <- copy *top-addr
242 return result
243 }
244
245 fn first-grapheme-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
246 var self/esi: (addr gap-buffer) <- copy _self
247
248 var left/eax: (addr grapheme-stack) <- get self, left
249 var top-addr/ecx: (addr int) <- get left, top
250 compare *top-addr, 0
251 {
252 break-if-<=
253 var data-ah/eax: (addr handle array grapheme) <- get left, data
254 var data/eax: (addr array grapheme) <- lookup *data-ah
255 var result-addr/eax: (addr grapheme) <- index data, 0
256 return *result-addr
257 }
258
259 var right/eax: (addr grapheme-stack) <- get self, right
260 top-addr <- get right, top
261 compare *top-addr, 0
262 {
263 break-if-<=
264 var data-ah/eax: (addr handle array grapheme) <- get right, data
265 var data/eax: (addr array grapheme) <- lookup *data-ah
266 var top/ecx: int <- copy *top-addr
267 top <- decrement
268 var result-addr/eax: (addr grapheme) <- index data, top
269 return *result-addr
270 }
271
272 return -1
273 }
274
275 fn grapheme-before-cursor-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
276 var self/esi: (addr gap-buffer) <- copy _self
277
278 var left/ecx: (addr grapheme-stack) <- get self, left
279 var top-addr/edx: (addr int) <- get left, top
280 compare *top-addr, 0
281 {
282 break-if-<=
283 var result/eax: grapheme <- pop-grapheme-stack left
284 push-grapheme-stack left, result
285 return result
286 }
287
288 return -1
289 }
290
291 fn delete-before-gap _self: (addr gap-buffer) {
292 var self/eax: (addr gap-buffer) <- copy _self
293 var left/eax: (addr grapheme-stack) <- get self, left
294 var dummy/eax: grapheme <- pop-grapheme-stack left
295 }
296
297 fn pop-after-gap _self: (addr gap-buffer) -> _/eax: grapheme {
298 var self/eax: (addr gap-buffer) <- copy _self
299 var right/eax: (addr grapheme-stack) <- get self, right
300 var result/eax: grapheme <- pop-grapheme-stack right
301 return result
302 }
303
304 fn gap-buffer-equal? _self: (addr gap-buffer), s: (addr array byte) -> _/eax: boolean {
305 var self/esi: (addr gap-buffer) <- copy _self
306
307
308
309 var stream-storage: (stream byte 0x10/capacity)
310 var expected-stream/ecx: (addr stream byte) <- address stream-storage
311 write expected-stream, s
312
313 var left/edx: (addr grapheme-stack) <- get self, left
314 var result/eax: boolean <- prefix-match? left, expected-stream
315 compare result, 0/false
316 {
317 break-if-!=
318 return result
319 }
320
321 var right/edx: (addr grapheme-stack) <- get self, right
322 result <- suffix-match? right, expected-stream
323 compare result, 0/false
324 {
325 break-if-!=
326 return result
327 }
328
329 result <- stream-empty? expected-stream
330 return result
331 }
332
333 fn test-gap-buffer-equal-from-end {
334 var _g: gap-buffer
335 var g/esi: (addr gap-buffer) <- address _g
336 initialize-gap-buffer g, 0x10
337
338 add-code-point-at-gap g, 0x61/a
339 add-code-point-at-gap g, 0x61/a
340 add-code-point-at-gap g, 0x61/a
341
342 var result/eax: boolean <- gap-buffer-equal? g, "aaa"
343 check result, "F - test-gap-buffer-equal-from-end"
344 }
345
346 fn test-gap-buffer-equal-from-middle {
347 var _g: gap-buffer
348 var g/esi: (addr gap-buffer) <- address _g
349 initialize-gap-buffer g, 0x10
350
351 add-code-point-at-gap g, 0x61/a
352 add-code-point-at-gap g, 0x61/a
353 add-code-point-at-gap g, 0x61/a
354 var dummy/eax: grapheme <- gap-left g
355
356 var result/eax: boolean <- gap-buffer-equal? g, "aaa"
357 check result, "F - test-gap-buffer-equal-from-middle"
358 }
359
360 fn test-gap-buffer-equal-from-start {
361 var _g: gap-buffer
362 var g/esi: (addr gap-buffer) <- address _g
363 initialize-gap-buffer g, 0x10
364
365 add-code-point-at-gap g, 0x61/a
366 add-code-point-at-gap g, 0x61/a
367 add-code-point-at-gap g, 0x61/a
368 var dummy/eax: grapheme <- gap-left g
369 dummy <- gap-left g
370 dummy <- gap-left g
371
372 var result/eax: boolean <- gap-buffer-equal? g, "aaa"
373 check result, "F - test-gap-buffer-equal-from-start"
374 }
375
376 fn test-gap-buffer-equal-fails {
377
378 var _g: gap-buffer
379 var g/esi: (addr gap-buffer) <- address _g
380 initialize-gap-buffer g, 0x10
381 add-code-point-at-gap g, 0x61/a
382 add-code-point-at-gap g, 0x61/a
383 add-code-point-at-gap g, 0x61/a
384
385 var result/eax: boolean <- gap-buffer-equal? g, "aa"
386 check-not result, "F - test-gap-buffer-equal-fails"
387 }
388
389 fn gap-buffers-equal? self: (addr gap-buffer), g: (addr gap-buffer) -> _/eax: boolean {
390 var tmp/eax: int <- gap-buffer-length self
391 var len/ecx: int <- copy tmp
392 var leng/eax: int <- gap-buffer-length g
393 compare len, leng
394 {
395 break-if-=
396 return 0/false
397 }
398 var i/edx: int <- copy 0
399 {
400 compare i, len
401 break-if->=
402 {
403 var tmp/eax: grapheme <- gap-index self, i
404 var curr/ecx: grapheme <- copy tmp
405 var currg/eax: grapheme <- gap-index g, i
406 compare curr, currg
407 break-if-=
408 return 0/false
409 }
410 i <- increment
411 loop
412 }
413 return 1/true
414 }
415
416 fn gap-index _self: (addr gap-buffer), _n: int -> _/eax: grapheme {
417 var self/esi: (addr gap-buffer) <- copy _self
418 var n/ebx: int <- copy _n
419
420 var left/edi: (addr grapheme-stack) <- get self, left
421 var left-len-a/edx: (addr int) <- get left, top
422 compare n, *left-len-a
423 {
424 break-if->=
425 var data-ah/eax: (addr handle array grapheme) <- get left, data
426 var data/eax: (addr array grapheme) <- lookup *data-ah
427 var result/eax: (addr grapheme) <- index data, n
428 return *result
429 }
430
431 n <- subtract *left-len-a
432
433 var right/edi: (addr grapheme-stack) <- get self, right
434 var right-len-a/edx: (addr int) <- get right, top
435 compare n, *right-len-a
436 {
437 break-if->=
438 var data-ah/eax: (addr handle array grapheme) <- get right, data
439 var data/eax: (addr array grapheme) <- lookup *data-ah
440
441 var idx/ebx: int <- copy n
442 idx <- subtract *right-len-a
443 idx <- negate
444 idx <- subtract 1
445 var result/eax: (addr grapheme) <- index data, idx
446 return *result
447 }
448
449 abort "gap-index: out of bounds"
450 return 0
451 }
452
453 fn test-gap-buffers-equal? {
454 var _a: gap-buffer
455 var a/esi: (addr gap-buffer) <- address _a
456 initialize-gap-buffer-with a, "abc"
457 var _b: gap-buffer
458 var b/edi: (addr gap-buffer) <- address _b
459 initialize-gap-buffer-with b, "abc"
460 var _c: gap-buffer
461 var c/ebx: (addr gap-buffer) <- address _c
462 initialize-gap-buffer-with c, "ab"
463 var _d: gap-buffer
464 var d/edx: (addr gap-buffer) <- address _d
465 initialize-gap-buffer-with d, "abd"
466
467 var result/eax: boolean <- gap-buffers-equal? a, a
468 check result, "F - test-gap-buffers-equal? - reflexive"
469 result <- gap-buffers-equal? a, b
470 check result, "F - test-gap-buffers-equal? - equal"
471
472 result <- gap-buffers-equal? a, c
473 check-not result, "F - test-gap-buffers-equal? - not equal"
474
475 result <- gap-buffers-equal? a, d
476 check-not result, "F - test-gap-buffers-equal? - not equal 2"
477 result <- gap-buffers-equal? d, a
478 check-not result, "F - test-gap-buffers-equal? - not equal 3"
479 }
480
481 fn test-gap-buffer-index {
482 var gap-storage: gap-buffer
483 var gap/esi: (addr gap-buffer) <- address gap-storage
484 initialize-gap-buffer-with gap, "abc"
485
486 var g/eax: grapheme <- gap-index gap, 0
487 var x/ecx: int <- copy g
488 check-ints-equal x, 0x61/a, "F - test-gap-index/left-1"
489 var g/eax: grapheme <- gap-index gap, 1
490 var x/ecx: int <- copy g
491 check-ints-equal x, 0x62/b, "F - test-gap-index/left-2"
492 var g/eax: grapheme <- gap-index gap, 2
493 var x/ecx: int <- copy g
494 check-ints-equal x, 0x63/c, "F - test-gap-index/left-3"
495
496 gap-to-start gap
497 rewind-gap-buffer gap
498 var g/eax: grapheme <- gap-index gap, 0
499 var x/ecx: int <- copy g
500 check-ints-equal x, 0x61/a, "F - test-gap-index/right-1"
501 var g/eax: grapheme <- gap-index gap, 1
502 var x/ecx: int <- copy g
503 check-ints-equal x, 0x62/b, "F - test-gap-index/right-2"
504 var g/eax: grapheme <- gap-index gap, 2
505 var x/ecx: int <- copy g
506 check-ints-equal x, 0x63/c, "F - test-gap-index/right-3"
507 }
508
509 fn copy-gap-buffer _src-ah: (addr handle gap-buffer), _dest-ah: (addr handle gap-buffer) {
510
511 var src-ah/eax: (addr handle gap-buffer) <- copy _src-ah
512 var _src-a/eax: (addr gap-buffer) <- lookup *src-ah
513 var src-a/esi: (addr gap-buffer) <- copy _src-a
514 var dest-ah/eax: (addr handle gap-buffer) <- copy _dest-ah
515 var _dest-a/eax: (addr gap-buffer) <- lookup *dest-ah
516 var dest-a/edi: (addr gap-buffer) <- copy _dest-a
517
518 var src/ecx: (addr grapheme-stack) <- get src-a, left
519 var dest/edx: (addr grapheme-stack) <- get dest-a, left
520 copy-grapheme-stack src, dest
521
522 src <- get src-a, right
523 dest <- get dest-a, right
524 copy-grapheme-stack src, dest
525 }
526
527 fn gap-buffer-is-decimal-integer? _self: (addr gap-buffer) -> _/eax: boolean {
528 var self/esi: (addr gap-buffer) <- copy _self
529 var curr/ecx: (addr grapheme-stack) <- get self, left
530 var result/eax: boolean <- grapheme-stack-is-decimal-integer? curr
531 {
532 compare result, 0/false
533 break-if-=
534 curr <- get self, right
535 result <- grapheme-stack-is-decimal-integer? curr
536 }
537 return result
538 }
539
540 fn test-render-gap-buffer-without-cursor {
541
542 var gap-storage: gap-buffer
543 var gap/esi: (addr gap-buffer) <- address gap-storage
544 initialize-gap-buffer-with gap, "abc"
545
546 var screen-on-stack: screen
547 var screen/edi: (addr screen) <- address screen-on-stack
548 initialize-screen screen, 5, 4, 0/no-pixel-graphics
549
550 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 0/no-cursor
551 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-without-cursor"
552 check-ints-equal x, 4, "F - test-render-gap-buffer-without-cursor: result"
553
554 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-render-gap-buffer-without-cursor: bg"
555 }
556
557 fn test-render-gap-buffer-with-cursor-at-end {
558
559 var gap-storage: gap-buffer
560 var gap/esi: (addr gap-buffer) <- address gap-storage
561 initialize-gap-buffer-with gap, "abc"
562 gap-to-end gap
563
564 var screen-on-stack: screen
565 var screen/edi: (addr screen) <- address screen-on-stack
566 initialize-screen screen, 5, 4, 0/no-pixel-graphics
567
568 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
569 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-end"
570
571 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-end: result"
572
573 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " |", "F - test-render-gap-buffer-with-cursor-at-end: bg"
574 }
575
576 fn test-render-gap-buffer-with-cursor-in-middle {
577
578 var gap-storage: gap-buffer
579 var gap/esi: (addr gap-buffer) <- address gap-storage
580 initialize-gap-buffer-with gap, "abc"
581 gap-to-end gap
582 var dummy/eax: grapheme <- gap-left gap
583
584 var screen-on-stack: screen
585 var screen/edi: (addr screen) <- address screen-on-stack
586 initialize-screen screen, 5, 4, 0/no-pixel-graphics
587
588 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
589 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-in-middle"
590 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-in-middle: result"
591
592 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-gap-buffer-with-cursor-in-middle: bg"
593 }
594
595 fn test-render-gap-buffer-with-cursor-at-start {
596 var gap-storage: gap-buffer
597 var gap/esi: (addr gap-buffer) <- address gap-storage
598 initialize-gap-buffer-with gap, "abc"
599 gap-to-start gap
600
601 var screen-on-stack: screen
602 var screen/edi: (addr screen) <- address screen-on-stack
603 initialize-screen screen, 5, 4, 0/no-pixel-graphics
604
605 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
606 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-start"
607 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-start: result"
608
609 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "| ", "F - test-render-gap-buffer-with-cursor-at-start: bg"
610 }
611
612 fn test-render-gap-buffer-highlight-matching-close-paren {
613 var gap-storage: gap-buffer
614 var gap/esi: (addr gap-buffer) <- address gap-storage
615 initialize-gap-buffer-with gap, "(a)"
616 gap-to-start gap
617
618 var screen-on-stack: screen
619 var screen/edi: (addr screen) <- address screen-on-stack
620 initialize-screen screen, 5, 4, 0/no-pixel-graphics
621
622 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
623 check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-close-paren"
624 check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-close-paren: result"
625 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "| ", "F - test-render-gap-buffer-highlight-matching-close-paren: cursor"
626 check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, " ) ", "F - test-render-gap-buffer-highlight-matching-close-paren: matching paren"
627 }
628
629 fn test-render-gap-buffer-highlight-matching-open-paren {
630 var gap-storage: gap-buffer
631 var gap/esi: (addr gap-buffer) <- address gap-storage
632 initialize-gap-buffer-with gap, "(a)"
633 gap-to-end gap
634 var dummy/eax: grapheme <- gap-left gap
635
636 var screen-on-stack: screen
637 var screen/edi: (addr screen) <- address screen-on-stack
638 initialize-screen screen, 5, 4, 0/no-pixel-graphics
639
640 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
641 check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren"
642 check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren: result"
643 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-gap-buffer-highlight-matching-open-paren: cursor"
644 check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, "( ", "F - test-render-gap-buffer-highlight-matching-open-paren: matching paren"
645 }
646
647 fn test-render-gap-buffer-highlight-matching-open-paren-of-end {
648 var gap-storage: gap-buffer
649 var gap/esi: (addr gap-buffer) <- address gap-storage
650 initialize-gap-buffer-with gap, "(a)"
651 gap-to-end gap
652
653 var screen-on-stack: screen
654 var screen/edi: (addr screen) <- address screen-on-stack
655 initialize-screen screen, 5, 4, 0/no-pixel-graphics
656
657 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
658 check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end"
659 check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: result"
660 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " |", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: cursor"
661 check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, "( ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: matching paren"
662 }
663
664
665
666
667
668 fn highlight-matching-open-paren? _gap: (addr gap-buffer), render-cursor?: boolean -> _/ebx: boolean, _/edi: int {
669
670 compare render-cursor?, 0/false
671 {
672 break-if-!=
673 return 0/false, 0
674 }
675 var gap/esi: (addr gap-buffer) <- copy _gap
676 var stack/edi: (addr grapheme-stack) <- get gap, right
677 var top-addr/eax: (addr int) <- get stack, top
678 var top-index/ecx: int <- copy *top-addr
679 compare top-index, 0
680 {
681 break-if->
682
683 stack <- get gap, left
684 top-addr <- get stack, top
685 top-index <- copy *top-addr
686 compare top-index, 0
687 {
688 break-if->
689 return 0/false, 0
690 }
691 top-index <- decrement
692 var data-ah/eax: (addr handle array grapheme) <- get stack, data
693 var data/eax: (addr array grapheme) <- lookup *data-ah
694 var g/eax: (addr grapheme) <- index data, top-index
695 compare *g, 0x29/close-paren
696 {
697 break-if-=
698 return 0/false, 0
699 }
700 return 1/true, 1
701 }
702
703 top-index <- decrement
704 var data-ah/eax: (addr handle array grapheme) <- get stack, data
705 var data/eax: (addr array grapheme) <- lookup *data-ah
706 var g/eax: (addr grapheme) <- index data, top-index
707 compare *g, 0x29/close-paren
708 {
709 break-if-=
710 return 0/false, 0
711 }
712 return 1/true, 0
713 }
714
715 fn test-highlight-matching-open-paren {
716 var gap-storage: gap-buffer
717 var gap/esi: (addr gap-buffer) <- address gap-storage
718 initialize-gap-buffer-with gap, "(a)"
719 gap-to-end gap
720 var highlight-matching-open-paren?/ebx: boolean <- copy 0/false
721 var open-paren-depth/edi: int <- copy 0
722 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 0/no-cursor
723 check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: no cursor"
724 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor
725 check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: at end immediately after ')'"
726 check-ints-equal open-paren-depth, 1, "F - test-highlight-matching-open-paren: depth at end immediately after ')'"
727 var dummy/eax: grapheme <- gap-left gap
728 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor
729 check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: on ')'"
730 dummy <- gap-left gap
731 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor
732 check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: not on ')'"
733 }
734
735
736
737
738
739
740 fn rewind-gap-buffer _self: (addr gap-buffer) {
741 var self/esi: (addr gap-buffer) <- copy _self
742 var dest/eax: (addr int) <- get self, left-read-index
743 copy-to *dest, 0
744 dest <- get self, right-read-index
745 copy-to *dest, 0
746 }
747
748 fn gap-buffer-scan-done? _self: (addr gap-buffer) -> _/eax: boolean {
749 var self/esi: (addr gap-buffer) <- copy _self
750
751 var left/eax: (addr grapheme-stack) <- get self, left
752 var left-size/eax: int <- grapheme-stack-length left
753 var left-read-index/ecx: (addr int) <- get self, left-read-index
754 compare *left-read-index, left-size
755 {
756 break-if->=
757 return 0/false
758 }
759
760 var right/eax: (addr grapheme-stack) <- get self, right
761 var right-size/eax: int <- grapheme-stack-length right
762 var right-read-index/ecx: (addr int) <- get self, right-read-index
763 compare *right-read-index, right-size
764 {
765 break-if->=
766 return 0/false
767 }
768
769 return 1/true
770 }
771
772 fn peek-from-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
773 var self/esi: (addr gap-buffer) <- copy _self
774
775 var left/ecx: (addr grapheme-stack) <- get self, left
776 var left-size/eax: int <- grapheme-stack-length left
777 var left-read-index-a/edx: (addr int) <- get self, left-read-index
778 compare *left-read-index-a, left-size
779 {
780 break-if->=
781 var left-data-ah/eax: (addr handle array grapheme) <- get left, data
782 var left-data/eax: (addr array grapheme) <- lookup *left-data-ah
783 var left-read-index/ecx: int <- copy *left-read-index-a
784 var result/eax: (addr grapheme) <- index left-data, left-read-index
785 return *result
786 }
787
788 var right/ecx: (addr grapheme-stack) <- get self, right
789 var _right-size/eax: int <- grapheme-stack-length right
790 var right-size/ebx: int <- copy _right-size
791 var right-read-index-a/edx: (addr int) <- get self, right-read-index
792 compare *right-read-index-a, right-size
793 {
794 break-if->=
795
796 var right-data-ah/eax: (addr handle array grapheme) <- get right, data
797 var right-data/eax: (addr array grapheme) <- lookup *right-data-ah
798 var right-read-index/ebx: int <- copy right-size
799 right-read-index <- subtract *right-read-index-a
800 right-read-index <- subtract 1
801 var result/eax: (addr grapheme) <- index right-data, right-read-index
802 return *result
803 }
804
805 return 0/nul
806 }
807
808 fn read-from-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
809 var self/esi: (addr gap-buffer) <- copy _self
810
811 var left/ecx: (addr grapheme-stack) <- get self, left
812 var left-size/eax: int <- grapheme-stack-length left
813 var left-read-index-a/edx: (addr int) <- get self, left-read-index
814 compare *left-read-index-a, left-size
815 {
816 break-if->=
817 var left-data-ah/eax: (addr handle array grapheme) <- get left, data
818 var left-data/eax: (addr array grapheme) <- lookup *left-data-ah
819 var left-read-index/ecx: int <- copy *left-read-index-a
820 var result/eax: (addr grapheme) <- index left-data, left-read-index
821 increment *left-read-index-a
822 return *result
823 }
824
825 var right/ecx: (addr grapheme-stack) <- get self, right
826 var _right-size/eax: int <- grapheme-stack-length right
827 var right-size/ebx: int <- copy _right-size
828 var right-read-index-a/edx: (addr int) <- get self, right-read-index
829 compare *right-read-index-a, right-size
830 {
831 break-if->=
832
833 var right-data-ah/eax: (addr handle array grapheme) <- get right, data
834 var right-data/eax: (addr array grapheme) <- lookup *right-data-ah
835 var right-read-index/ebx: int <- copy right-size
836 right-read-index <- subtract *right-read-index-a
837 right-read-index <- subtract 1
838 var result/eax: (addr grapheme) <- index right-data, right-read-index
839 increment *right-read-index-a
840 return *result
841 }
842
843 return 0/nul
844 }
845
846 fn test-read-from-gap-buffer {
847 var gap-storage: gap-buffer
848 var gap/esi: (addr gap-buffer) <- address gap-storage
849 initialize-gap-buffer-with gap, "abc"
850
851 var done?/eax: boolean <- gap-buffer-scan-done? gap
852 check-not done?, "F - test-read-from-gap-buffer/left-1/done"
853 var g/eax: grapheme <- read-from-gap-buffer gap
854 var x/ecx: int <- copy g
855 check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/left-1"
856 var done?/eax: boolean <- gap-buffer-scan-done? gap
857 check-not done?, "F - test-read-from-gap-buffer/left-2/done"
858 var g/eax: grapheme <- read-from-gap-buffer gap
859 var x/ecx: int <- copy g
860 check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/left-2"
861 var done?/eax: boolean <- gap-buffer-scan-done? gap
862 check-not done?, "F - test-read-from-gap-buffer/left-3/done"
863 var g/eax: grapheme <- read-from-gap-buffer gap
864 var x/ecx: int <- copy g
865 check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/left-3"
866 var done?/eax: boolean <- gap-buffer-scan-done? gap
867 check done?, "F - test-read-from-gap-buffer/left-4/done"
868 var g/eax: grapheme <- read-from-gap-buffer gap
869 var x/ecx: int <- copy g
870 check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/left-4"
871
872 gap-to-start gap
873 rewind-gap-buffer gap
874 var done?/eax: boolean <- gap-buffer-scan-done? gap
875 check-not done?, "F - test-read-from-gap-buffer/right-1/done"
876 var g/eax: grapheme <- read-from-gap-buffer gap
877 var x/ecx: int <- copy g
878 check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/right-1"
879 var done?/eax: boolean <- gap-buffer-scan-done? gap
880 check-not done?, "F - test-read-from-gap-buffer/right-2/done"
881 var g/eax: grapheme <- read-from-gap-buffer gap
882 var x/ecx: int <- copy g
883 check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/right-2"
884 var done?/eax: boolean <- gap-buffer-scan-done? gap
885 check-not done?, "F - test-read-from-gap-buffer/right-3/done"
886 var g/eax: grapheme <- read-from-gap-buffer gap
887 var x/ecx: int <- copy g
888 check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/right-3"
889 var done?/eax: boolean <- gap-buffer-scan-done? gap
890 check done?, "F - test-read-from-gap-buffer/right-4/done"
891 var g/eax: grapheme <- read-from-gap-buffer gap
892 var x/ecx: int <- copy g
893 check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/right-4"
894 }
895
896 fn skip-whitespace-from-gap-buffer self: (addr gap-buffer) {
897 var done?/eax: boolean <- gap-buffer-scan-done? self
898 compare done?, 0/false
899 break-if-!=
900 var g/eax: grapheme <- peek-from-gap-buffer self
901 {
902 compare g, 0x20/space
903 break-if-=
904 compare g, 0xa/newline
905 break-if-=
906 return
907 }
908 g <- read-from-gap-buffer self
909 loop
910 }
911
912 fn edit-gap-buffer self: (addr gap-buffer), key: grapheme {
913 var g/edx: grapheme <- copy key
914 {
915 compare g, 8/backspace
916 break-if-!=
917 delete-before-gap self
918 return
919 }
920 {
921 compare g, 0x80/left-arrow
922 break-if-!=
923 var dummy/eax: grapheme <- gap-left self
924 return
925 }
926 {
927 compare g, 0x83/right-arrow
928 break-if-!=
929 var dummy/eax: grapheme <- gap-right self
930 return
931 }
932 {
933 compare g, 6/ctrl-f
934 break-if-!=
935 gap-to-start-of-next-word self
936 return
937 }
938 {
939 compare g, 2/ctrl-b
940 break-if-!=
941 gap-to-end-of-previous-word self
942 return
943 }
944 {
945 compare g, 1/ctrl-a
946 break-if-!=
947 gap-to-previous-start-of-line self
948 return
949 }
950 {
951 compare g, 5/ctrl-e
952 break-if-!=
953 gap-to-next-end-of-line self
954 return
955 }
956 {
957 compare g, 0x81/down-arrow
958 break-if-!=
959 gap-down self
960 return
961 }
962 {
963 compare g, 0x82/up-arrow
964 break-if-!=
965 gap-up self
966 return
967 }
968 {
969 compare g, 0x15/ctrl-u
970 break-if-!=
971 clear-gap-buffer self
972 return
973 }
974
975 add-grapheme-at-gap self, g
976 }
977
978 fn gap-to-start-of-next-word self: (addr gap-buffer) {
979 var curr/eax: grapheme <- copy 0
980
981 {
982 curr <- gap-right self
983 compare curr, -1
984 break-if-=
985 compare curr, 0x20/space
986 break-if-=
987 compare curr, 0xa/newline
988 break-if-=
989 loop
990 }
991
992 {
993 curr <- gap-right self
994 compare curr, -1
995 break-if-=
996 compare curr, 0x20/space
997 loop-if-=
998 compare curr, 0xa/space
999 loop-if-=
1000 curr <- gap-left self
1001 break
1002 }
1003 }
1004
1005 fn gap-to-end-of-previous-word self: (addr gap-buffer) {
1006 var curr/eax: grapheme <- copy 0
1007
1008 {
1009 curr <- gap-left self
1010 compare curr, -1
1011 break-if-=
1012 compare curr, 0x20/space
1013 break-if-=
1014 compare curr, 0xa/newline
1015 break-if-=
1016 loop
1017 }
1018
1019 {
1020 curr <- gap-left self
1021 compare curr, -1
1022 break-if-=
1023 compare curr, 0x20/space
1024 loop-if-=
1025 compare curr, 0xa/space
1026 loop-if-=
1027 curr <- gap-right self
1028 break
1029 }
1030 }
1031
1032 fn gap-to-previous-start-of-line self: (addr gap-buffer) {
1033
1034 var dummy/eax: grapheme <- gap-left self
1035
1036 {
1037 dummy <- gap-left self
1038 {
1039 compare dummy, -1
1040 break-if-!=
1041 return
1042 }
1043 {
1044 compare dummy, 0xa/newline
1045 break-if-!=
1046 dummy <- gap-right self
1047 return
1048 }
1049 loop
1050 }
1051 }
1052
1053 fn gap-to-next-end-of-line self: (addr gap-buffer) {
1054
1055 var dummy/eax: grapheme <- gap-right self
1056
1057 {
1058 dummy <- gap-right self
1059 {
1060 compare dummy, -1
1061 break-if-!=
1062 return
1063 }
1064 {
1065 compare dummy, 0xa/newline
1066 break-if-!=
1067 dummy <- gap-left self
1068 return
1069 }
1070 loop
1071 }
1072 }
1073
1074 fn gap-up self: (addr gap-buffer) {
1075
1076 var col/edx: int <- count-columns-to-start-of-line self
1077
1078 gap-to-previous-start-of-line self
1079
1080 var i/ecx: int <- copy 0
1081 {
1082 compare i, col
1083 break-if->=
1084 var curr/eax: grapheme <- gap-right self
1085 {
1086 compare curr, -1
1087 break-if-!=
1088 return
1089 }
1090 compare curr, 0xa/newline
1091 {
1092 break-if-!=
1093 curr <- gap-left self
1094 return
1095 }
1096 i <- increment
1097 loop
1098 }
1099 }
1100
1101 fn gap-down self: (addr gap-buffer) {
1102
1103 var col/edx: int <- count-columns-to-start-of-line self
1104
1105 gap-to-end-of-line self
1106 var dummy/eax: grapheme <- gap-right self
1107
1108 var i/ecx: int <- copy 0
1109 {
1110 compare i, col
1111 break-if->=
1112 var curr/eax: grapheme <- gap-right self
1113 {
1114 compare curr, -1
1115 break-if-!=
1116 return
1117 }
1118 compare curr, 0xa/newline
1119 {
1120 break-if-!=
1121 curr <- gap-left self
1122 return
1123 }
1124 i <- increment
1125 loop
1126 }
1127 }
1128
1129 fn count-columns-to-start-of-line self: (addr gap-buffer) -> _/edx: int {
1130 var count/edx: int <- copy 0
1131 var dummy/eax: grapheme <- copy 0
1132
1133 {
1134 dummy <- gap-left self
1135 {
1136 compare dummy, -1
1137 break-if-!=
1138 return count
1139 }
1140 {
1141 compare dummy, 0xa/newline
1142 break-if-!=
1143 dummy <- gap-right self
1144 return count
1145 }
1146 count <- increment
1147 loop
1148 }
1149 return count
1150 }
1151
1152 fn gap-to-end-of-line self: (addr gap-buffer) {
1153 var dummy/eax: grapheme <- copy 0
1154
1155 {
1156 dummy <- gap-right self
1157 {
1158 compare dummy, -1
1159 break-if-!=
1160 return
1161 }
1162 {
1163 compare dummy, 0xa/newline
1164 break-if-!=
1165 dummy <- gap-left self
1166 return
1167 }
1168 loop
1169 }
1170 }