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), max-word-size: int {
12 var self/esi: (addr gap-buffer) <- copy _self
13 var left/eax: (addr grapheme-stack) <- get self, left
14 initialize-grapheme-stack left, max-word-size
15 var right/eax: (addr grapheme-stack) <- get self, right
16 initialize-grapheme-stack right, max-word-size
17 }
18
19 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/max-word-size
30 var stream-storage: (stream byte 0x10/max-word-size)
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 emit-gap-buffer _self: (addr gap-buffer), out: (addr stream byte) {
44 var self/esi: (addr gap-buffer) <- copy _self
45 clear-stream out
46 var left/eax: (addr grapheme-stack) <- get self, left
47 emit-stack-from-bottom left, out
48 var right/eax: (addr grapheme-stack) <- get self, right
49 emit-stack-from-top right, out
50 }
51
52
53 fn emit-stack-from-bottom _self: (addr grapheme-stack), out: (addr stream byte) {
54 var self/esi: (addr grapheme-stack) <- copy _self
55 var data-ah/edi: (addr handle array grapheme) <- get self, data
56 var _data/eax: (addr array grapheme) <- lookup *data-ah
57 var data/edi: (addr array grapheme) <- copy _data
58 var top-addr/ecx: (addr int) <- get self, top
59 var i/eax: int <- copy 0
60 {
61 compare i, *top-addr
62 break-if->=
63 var g/edx: (addr grapheme) <- index data, i
64 write-grapheme out, *g
65 i <- increment
66 loop
67 }
68 }
69
70
71 fn emit-stack-from-top _self: (addr grapheme-stack), out: (addr stream byte) {
72 var self/esi: (addr grapheme-stack) <- copy _self
73 var data-ah/edi: (addr handle array grapheme) <- get self, data
74 var _data/eax: (addr array grapheme) <- lookup *data-ah
75 var data/edi: (addr array grapheme) <- copy _data
76 var top-addr/ecx: (addr int) <- get self, top
77 var i/eax: int <- copy *top-addr
78 i <- decrement
79 {
80 compare i, 0
81 break-if-<
82 var g/edx: (addr grapheme) <- index data, i
83 write-grapheme out, *g
84 i <- decrement
85 loop
86 }
87 }
88
89
90
91 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 {
92 var gap/esi: (addr gap-buffer) <- copy _gap
93 var left/edx: (addr grapheme-stack) <- get gap, left
94 var highlight-matching-open-paren?/ebx: boolean <- copy 0/false
95 var matching-open-paren-depth/edi: int <- copy 0
96 highlight-matching-open-paren?, matching-open-paren-depth <- highlight-matching-open-paren? gap, render-cursor?
97 var x2/eax: int <- copy 0
98 var y2/ecx: int <- copy 0
99 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
100 var right/edx: (addr grapheme-stack) <- get gap, right
101 x2, y2 <- render-stack-from-top-wrapping-right-then-down screen, right, xmin, ymin, xmax, ymax, x2, y2, render-cursor?
102
103 var bg/ebx: int <- copy 0
104 compare render-cursor?, 0/false
105 {
106 break-if-=
107
108 var empty?/eax: boolean <- grapheme-stack-empty? right
109 compare empty?, 0/false
110 break-if-=
111 bg <- copy 7/cursor
112 }
113
114 var space/edx: grapheme <- copy 0x20
115 x2, y2 <- render-grapheme screen, space, xmin, ymin, xmax, ymax, x2, y2, 3/fg=cyan, bg
116 return x2, y2
117 }
118
119 fn render-gap-buffer screen: (addr screen), gap: (addr gap-buffer), x: int, y: int, render-cursor?: boolean -> _/eax: int {
120 var _width/eax: int <- copy 0
121 var _height/ecx: int <- copy 0
122 _width, _height <- screen-size screen
123 var width/edx: int <- copy _width
124 var height/ebx: int <- copy _height
125 var x2/eax: int <- copy 0
126 var y2/ecx: int <- copy 0
127 x2, y2 <- render-gap-buffer-wrapping-right-then-down screen, gap, x, y, width, height, render-cursor?
128 return x2
129 }
130
131 fn gap-buffer-length _gap: (addr gap-buffer) -> _/eax: int {
132 var gap/esi: (addr gap-buffer) <- copy _gap
133 var left/eax: (addr grapheme-stack) <- get gap, left
134 var tmp/eax: (addr int) <- get left, top
135 var left-length/ecx: int <- copy *tmp
136 var right/esi: (addr grapheme-stack) <- get gap, right
137 tmp <- get right, top
138 var result/eax: int <- copy *tmp
139 result <- add left-length
140 return result
141 }
142
143 fn add-grapheme-at-gap _self: (addr gap-buffer), g: grapheme {
144 var self/esi: (addr gap-buffer) <- copy _self
145 var left/eax: (addr grapheme-stack) <- get self, left
146 push-grapheme-stack left, g
147 }
148
149 fn gap-to-start self: (addr gap-buffer) {
150 {
151 var curr/eax: grapheme <- gap-left self
152 compare curr, -1
153 loop-if-!=
154 }
155 }
156
157 fn gap-to-end self: (addr gap-buffer) {
158 {
159 var curr/eax: grapheme <- gap-right self
160 compare curr, -1
161 loop-if-!=
162 }
163 }
164
165 fn gap-at-start? _self: (addr gap-buffer) -> _/eax: boolean {
166 var self/esi: (addr gap-buffer) <- copy _self
167 var left/eax: (addr grapheme-stack) <- get self, left
168 var result/eax: boolean <- grapheme-stack-empty? left
169 return result
170 }
171
172 fn gap-at-end? _self: (addr gap-buffer) -> _/eax: boolean {
173 var self/esi: (addr gap-buffer) <- copy _self
174 var right/eax: (addr grapheme-stack) <- get self, right
175 var result/eax: boolean <- grapheme-stack-empty? right
176 return result
177 }
178
179 fn gap-right _self: (addr gap-buffer) -> _/eax: grapheme {
180 var self/esi: (addr gap-buffer) <- copy _self
181 var g/eax: grapheme <- copy 0
182 var right/ecx: (addr grapheme-stack) <- get self, right
183 g <- pop-grapheme-stack right
184 compare g, -1
185 {
186 break-if-=
187 var left/ecx: (addr grapheme-stack) <- get self, left
188 push-grapheme-stack left, g
189 }
190 return g
191 }
192
193 fn gap-left _self: (addr gap-buffer) -> _/eax: grapheme {
194 var self/esi: (addr gap-buffer) <- copy _self
195 var g/eax: grapheme <- copy 0
196 {
197 var left/ecx: (addr grapheme-stack) <- get self, left
198 g <- pop-grapheme-stack left
199 }
200 compare g, -1
201 {
202 break-if-=
203 var right/ecx: (addr grapheme-stack) <- get self, right
204 push-grapheme-stack right, g
205 }
206 return g
207 }
208
209 fn index-of-gap _self: (addr gap-buffer) -> _/eax: int {
210 var self/eax: (addr gap-buffer) <- copy _self
211 var left/eax: (addr grapheme-stack) <- get self, left
212 var top-addr/eax: (addr int) <- get left, top
213 var result/eax: int <- copy *top-addr
214 return result
215 }
216
217 fn first-grapheme-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
218 var self/esi: (addr gap-buffer) <- copy _self
219
220 var left/eax: (addr grapheme-stack) <- get self, left
221 var top-addr/ecx: (addr int) <- get left, top
222 compare *top-addr, 0
223 {
224 break-if-<=
225 var data-ah/eax: (addr handle array grapheme) <- get left, data
226 var data/eax: (addr array grapheme) <- lookup *data-ah
227 var result-addr/eax: (addr grapheme) <- index data, 0
228 return *result-addr
229 }
230
231 var right/eax: (addr grapheme-stack) <- get self, right
232 top-addr <- get right, top
233 compare *top-addr, 0
234 {
235 break-if-<=
236 var data-ah/eax: (addr handle array grapheme) <- get right, data
237 var data/eax: (addr array grapheme) <- lookup *data-ah
238 var top/ecx: int <- copy *top-addr
239 top <- decrement
240 var result-addr/eax: (addr grapheme) <- index data, top
241 return *result-addr
242 }
243
244 return -1
245 }
246
247 fn grapheme-before-cursor-in-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
248 var self/esi: (addr gap-buffer) <- copy _self
249
250 var left/ecx: (addr grapheme-stack) <- get self, left
251 var top-addr/edx: (addr int) <- get left, top
252 compare *top-addr, 0
253 {
254 break-if-<=
255 var result/eax: grapheme <- pop-grapheme-stack left
256 push-grapheme-stack left, result
257 return result
258 }
259
260 return -1
261 }
262
263 fn delete-before-gap _self: (addr gap-buffer) {
264 var self/eax: (addr gap-buffer) <- copy _self
265 var left/eax: (addr grapheme-stack) <- get self, left
266 var dummy/eax: grapheme <- pop-grapheme-stack left
267 }
268
269 fn pop-after-gap _self: (addr gap-buffer) -> _/eax: grapheme {
270 var self/eax: (addr gap-buffer) <- copy _self
271 var right/eax: (addr grapheme-stack) <- get self, right
272 var result/eax: grapheme <- pop-grapheme-stack right
273 return result
274 }
275
276 fn gap-buffer-equal? _self: (addr gap-buffer), s: (addr array byte) -> _/eax: boolean {
277 var self/esi: (addr gap-buffer) <- copy _self
278
279
280
281 var stream-storage: (stream byte 0x10/max-word-size)
282 var expected-stream/ecx: (addr stream byte) <- address stream-storage
283 write expected-stream, s
284
285 var left/edx: (addr grapheme-stack) <- get self, left
286 var result/eax: boolean <- prefix-match? left, expected-stream
287 compare result, 0/false
288 {
289 break-if-!=
290 return result
291 }
292
293 var right/edx: (addr grapheme-stack) <- get self, right
294 result <- suffix-match? right, expected-stream
295 compare result, 0/false
296 {
297 break-if-!=
298 return result
299 }
300
301 result <- stream-empty? expected-stream
302 return result
303 }
304
305 fn test-gap-buffer-equal-from-end {
306 var _g: gap-buffer
307 var g/esi: (addr gap-buffer) <- address _g
308 initialize-gap-buffer g, 0x10
309
310 var c/eax: grapheme <- copy 0x61/a
311 add-grapheme-at-gap g, c
312 add-grapheme-at-gap g, c
313 add-grapheme-at-gap g, c
314
315 var result/eax: boolean <- gap-buffer-equal? g, "aaa"
316 check result, "F - test-gap-buffer-equal-from-end"
317 }
318
319 fn test-gap-buffer-equal-from-middle {
320 var _g: gap-buffer
321 var g/esi: (addr gap-buffer) <- address _g
322 initialize-gap-buffer g, 0x10
323
324 var c/eax: grapheme <- copy 0x61/a
325 add-grapheme-at-gap g, c
326 add-grapheme-at-gap g, c
327 add-grapheme-at-gap g, c
328 var dummy/eax: grapheme <- gap-left g
329
330 var result/eax: boolean <- gap-buffer-equal? g, "aaa"
331 check result, "F - test-gap-buffer-equal-from-middle"
332 }
333
334 fn test-gap-buffer-equal-from-start {
335 var _g: gap-buffer
336 var g/esi: (addr gap-buffer) <- address _g
337 initialize-gap-buffer g, 0x10
338
339 var c/eax: grapheme <- copy 0x61/a
340 add-grapheme-at-gap g, c
341 add-grapheme-at-gap g, c
342 add-grapheme-at-gap g, c
343 var dummy/eax: grapheme <- gap-left g
344 dummy <- gap-left g
345 dummy <- gap-left g
346
347 var result/eax: boolean <- gap-buffer-equal? g, "aaa"
348 check result, "F - test-gap-buffer-equal-from-start"
349 }
350
351 fn test-gap-buffer-equal-fails {
352
353 var _g: gap-buffer
354 var g/esi: (addr gap-buffer) <- address _g
355 initialize-gap-buffer g, 0x10
356 var c/eax: grapheme <- copy 0x61/a
357 add-grapheme-at-gap g, c
358 add-grapheme-at-gap g, c
359 add-grapheme-at-gap g, c
360
361 var result/eax: boolean <- gap-buffer-equal? g, "aa"
362 check-not result, "F - test-gap-buffer-equal-fails"
363 }
364
365 fn gap-buffers-equal? self: (addr gap-buffer), g: (addr gap-buffer) -> _/eax: boolean {
366 var tmp/eax: int <- gap-buffer-length self
367 var len/ecx: int <- copy tmp
368 var leng/eax: int <- gap-buffer-length g
369 compare len, leng
370 {
371 break-if-=
372 return 0/false
373 }
374 var i/edx: int <- copy 0
375 {
376 compare i, len
377 break-if->=
378 {
379 var tmp/eax: grapheme <- gap-index self, i
380 var curr/ecx: grapheme <- copy tmp
381 var currg/eax: grapheme <- gap-index g, i
382 compare curr, currg
383 break-if-=
384 return 0/false
385 }
386 i <- increment
387 loop
388 }
389 return 1/true
390 }
391
392 fn gap-index _self: (addr gap-buffer), _n: int -> _/eax: grapheme {
393 var self/esi: (addr gap-buffer) <- copy _self
394 var n/ebx: int <- copy _n
395
396 var left/edi: (addr grapheme-stack) <- get self, left
397 var left-len-a/edx: (addr int) <- get left, top
398 compare n, *left-len-a
399 {
400 break-if->=
401 var data-ah/eax: (addr handle array grapheme) <- get left, data
402 var data/eax: (addr array grapheme) <- lookup *data-ah
403 var result/eax: (addr grapheme) <- index data, n
404 return *result
405 }
406
407 n <- subtract *left-len-a
408
409 var right/edi: (addr grapheme-stack) <- get self, right
410 var right-len-a/edx: (addr int) <- get right, top
411 compare n, *right-len-a
412 {
413 break-if->=
414 var data-ah/eax: (addr handle array grapheme) <- get right, data
415 var data/eax: (addr array grapheme) <- lookup *data-ah
416
417 var idx/ebx: int <- copy n
418 idx <- subtract *right-len-a
419 idx <- negate
420 idx <- subtract 1
421 var result/eax: (addr grapheme) <- index data, idx
422 return *result
423 }
424
425 abort "gap-index: out of bounds"
426 return 0
427 }
428
429 fn test-gap-buffers-equal? {
430 var _a: gap-buffer
431 var a/esi: (addr gap-buffer) <- address _a
432 initialize-gap-buffer-with a, "abc"
433 var _b: gap-buffer
434 var b/edi: (addr gap-buffer) <- address _b
435 initialize-gap-buffer-with b, "abc"
436 var _c: gap-buffer
437 var c/ebx: (addr gap-buffer) <- address _c
438 initialize-gap-buffer-with c, "ab"
439 var _d: gap-buffer
440 var d/edx: (addr gap-buffer) <- address _d
441 initialize-gap-buffer-with d, "abd"
442
443 var result/eax: boolean <- gap-buffers-equal? a, a
444 check result, "F - test-gap-buffers-equal? - reflexive"
445 result <- gap-buffers-equal? a, b
446 check result, "F - test-gap-buffers-equal? - equal"
447
448 result <- gap-buffers-equal? a, c
449 check-not result, "F - test-gap-buffers-equal? - not equal"
450
451 result <- gap-buffers-equal? a, d
452 check-not result, "F - test-gap-buffers-equal? - not equal 2"
453 result <- gap-buffers-equal? d, a
454 check-not result, "F - test-gap-buffers-equal? - not equal 3"
455 }
456
457 fn test-gap-buffer-index {
458 var gap-storage: gap-buffer
459 var gap/esi: (addr gap-buffer) <- address gap-storage
460 initialize-gap-buffer-with gap, "abc"
461
462 var g/eax: grapheme <- gap-index gap, 0
463 var x/ecx: int <- copy g
464 check-ints-equal x, 0x61/a, "F - test-gap-index/left-1"
465 var g/eax: grapheme <- gap-index gap, 1
466 var x/ecx: int <- copy g
467 check-ints-equal x, 0x62/b, "F - test-gap-index/left-2"
468 var g/eax: grapheme <- gap-index gap, 2
469 var x/ecx: int <- copy g
470 check-ints-equal x, 0x63/c, "F - test-gap-index/left-3"
471
472 gap-to-start gap
473 rewind-gap-buffer gap
474 var g/eax: grapheme <- gap-index gap, 0
475 var x/ecx: int <- copy g
476 check-ints-equal x, 0x61/a, "F - test-gap-index/right-1"
477 var g/eax: grapheme <- gap-index gap, 1
478 var x/ecx: int <- copy g
479 check-ints-equal x, 0x62/b, "F - test-gap-index/right-2"
480 var g/eax: grapheme <- gap-index gap, 2
481 var x/ecx: int <- copy g
482 check-ints-equal x, 0x63/c, "F - test-gap-index/right-3"
483 }
484
485 fn copy-gap-buffer _src-ah: (addr handle gap-buffer), _dest-ah: (addr handle gap-buffer) {
486
487 var src-ah/eax: (addr handle gap-buffer) <- copy _src-ah
488 var _src-a/eax: (addr gap-buffer) <- lookup *src-ah
489 var src-a/esi: (addr gap-buffer) <- copy _src-a
490 var dest-ah/eax: (addr handle gap-buffer) <- copy _dest-ah
491 var _dest-a/eax: (addr gap-buffer) <- lookup *dest-ah
492 var dest-a/edi: (addr gap-buffer) <- copy _dest-a
493
494 var src/ecx: (addr grapheme-stack) <- get src-a, left
495 var dest/edx: (addr grapheme-stack) <- get dest-a, left
496 copy-grapheme-stack src, dest
497
498 src <- get src-a, right
499 dest <- get dest-a, right
500 copy-grapheme-stack src, dest
501 }
502
503 fn gap-buffer-is-decimal-integer? _self: (addr gap-buffer) -> _/eax: boolean {
504 var self/esi: (addr gap-buffer) <- copy _self
505 var curr/ecx: (addr grapheme-stack) <- get self, left
506 var result/eax: boolean <- grapheme-stack-is-decimal-integer? curr
507 {
508 compare result, 0/false
509 break-if-=
510 curr <- get self, right
511 result <- grapheme-stack-is-decimal-integer? curr
512 }
513 return result
514 }
515
516 fn test-render-gap-buffer-without-cursor {
517
518 var gap-storage: gap-buffer
519 var gap/esi: (addr gap-buffer) <- address gap-storage
520 initialize-gap-buffer-with gap, "abc"
521
522 var screen-on-stack: screen
523 var screen/edi: (addr screen) <- address screen-on-stack
524 initialize-screen screen, 5, 4
525
526 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 0/no-cursor
527 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-without-cursor"
528 check-ints-equal x, 4, "F - test-render-gap-buffer-without-cursor: result"
529
530 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " ", "F - test-render-gap-buffer-without-cursor: bg"
531 }
532
533 fn test-render-gap-buffer-with-cursor-at-end {
534
535 var gap-storage: gap-buffer
536 var gap/esi: (addr gap-buffer) <- address gap-storage
537 initialize-gap-buffer-with gap, "abc"
538 gap-to-end gap
539
540 var screen-on-stack: screen
541 var screen/edi: (addr screen) <- address screen-on-stack
542 initialize-screen screen, 5, 4
543
544 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
545 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-end"
546
547 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-end: result"
548
549 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " |", "F - test-render-gap-buffer-with-cursor-at-end: bg"
550 }
551
552 fn test-render-gap-buffer-with-cursor-in-middle {
553
554 var gap-storage: gap-buffer
555 var gap/esi: (addr gap-buffer) <- address gap-storage
556 initialize-gap-buffer-with gap, "abc"
557 gap-to-end gap
558 var dummy/eax: grapheme <- gap-left gap
559
560 var screen-on-stack: screen
561 var screen/edi: (addr screen) <- address screen-on-stack
562 initialize-screen screen, 5, 4
563
564 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
565 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-in-middle"
566 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-in-middle: result"
567
568 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-gap-buffer-with-cursor-in-middle: bg"
569 }
570
571 fn test-render-gap-buffer-with-cursor-at-start {
572 var gap-storage: gap-buffer
573 var gap/esi: (addr gap-buffer) <- address gap-storage
574 initialize-gap-buffer-with gap, "abc"
575 gap-to-start gap
576
577 var screen-on-stack: screen
578 var screen/edi: (addr screen) <- address screen-on-stack
579 initialize-screen screen, 5, 4
580
581 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
582 check-screen-row screen, 0/y, "abc ", "F - test-render-gap-buffer-with-cursor-at-start"
583 check-ints-equal x, 4, "F - test-render-gap-buffer-with-cursor-at-start: result"
584
585 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "| ", "F - test-render-gap-buffer-with-cursor-at-start: bg"
586 }
587
588 fn test-render-gap-buffer-highlight-matching-close-paren {
589 var gap-storage: gap-buffer
590 var gap/esi: (addr gap-buffer) <- address gap-storage
591 initialize-gap-buffer-with gap, "(a)"
592 gap-to-start gap
593
594 var screen-on-stack: screen
595 var screen/edi: (addr screen) <- address screen-on-stack
596 initialize-screen screen, 5, 4
597
598 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
599 check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-close-paren"
600 check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-close-paren: result"
601 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "| ", "F - test-render-gap-buffer-highlight-matching-close-paren: cursor"
602 check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, " ) ", "F - test-render-gap-buffer-highlight-matching-close-paren: matching paren"
603 }
604
605 fn test-render-gap-buffer-highlight-matching-open-paren {
606 var gap-storage: gap-buffer
607 var gap/esi: (addr gap-buffer) <- address gap-storage
608 initialize-gap-buffer-with gap, "(a)"
609 gap-to-end gap
610 var dummy/eax: grapheme <- gap-left gap
611
612 var screen-on-stack: screen
613 var screen/edi: (addr screen) <- address screen-on-stack
614 initialize-screen screen, 5, 4
615
616 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
617 check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren"
618 check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren: result"
619 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " | ", "F - test-render-gap-buffer-highlight-matching-open-paren: cursor"
620 check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, "( ", "F - test-render-gap-buffer-highlight-matching-open-paren: matching paren"
621 }
622
623 fn test-render-gap-buffer-highlight-matching-open-paren-of-end {
624 var gap-storage: gap-buffer
625 var gap/esi: (addr gap-buffer) <- address gap-storage
626 initialize-gap-buffer-with gap, "(a)"
627 gap-to-end gap
628
629 var screen-on-stack: screen
630 var screen/edi: (addr screen) <- address screen-on-stack
631 initialize-screen screen, 5, 4
632
633 var x/eax: int <- render-gap-buffer screen, gap, 0/x, 0/y, 1/show-cursor
634 check-screen-row screen, 0/y, "(a) ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end"
635 check-ints-equal x, 4, "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: result"
636 check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, " |", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: cursor"
637 check-screen-row-in-color screen, 0xf/fg=highlight, 0/y, "( ", "F - test-render-gap-buffer-highlight-matching-open-paren-of-end: matching paren"
638 }
639
640
641
642
643
644 fn highlight-matching-open-paren? _gap: (addr gap-buffer), render-cursor?: boolean -> _/ebx: boolean, _/edi: int {
645
646 compare render-cursor?, 0/false
647 {
648 break-if-!=
649 return 0/false, 0
650 }
651 var gap/esi: (addr gap-buffer) <- copy _gap
652 var stack/edi: (addr grapheme-stack) <- get gap, right
653 var top-addr/eax: (addr int) <- get stack, top
654 var top-index/ecx: int <- copy *top-addr
655 compare top-index, 0
656 {
657 break-if->
658
659 stack <- get gap, left
660 top-addr <- get stack, top
661 top-index <- copy *top-addr
662 compare top-index, 0
663 {
664 break-if->
665 return 0/false, 0
666 }
667 top-index <- decrement
668 var data-ah/eax: (addr handle array grapheme) <- get stack, data
669 var data/eax: (addr array grapheme) <- lookup *data-ah
670 var g/eax: (addr grapheme) <- index data, top-index
671 compare *g, 0x29/close-paren
672 {
673 break-if-=
674 return 0/false, 0
675 }
676 return 1/true, 1
677 }
678
679 top-index <- decrement
680 var data-ah/eax: (addr handle array grapheme) <- get stack, data
681 var data/eax: (addr array grapheme) <- lookup *data-ah
682 var g/eax: (addr grapheme) <- index data, top-index
683 compare *g, 0x29/close-paren
684 {
685 break-if-=
686 return 0/false, 0
687 }
688 return 1/true, 0
689 }
690
691 fn test-highlight-matching-open-paren {
692 var gap-storage: gap-buffer
693 var gap/esi: (addr gap-buffer) <- address gap-storage
694 initialize-gap-buffer-with gap, "(a)"
695 gap-to-end gap
696 var highlight-matching-open-paren?/ebx: boolean <- copy 0/false
697 var open-paren-depth/edi: int <- copy 0
698 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 0/no-cursor
699 check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: no cursor"
700 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor
701 check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: at end immediately after ')'"
702 check-ints-equal open-paren-depth, 1, "F - test-highlight-matching-open-paren: depth at end immediately after ')'"
703 var dummy/eax: grapheme <- gap-left gap
704 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor
705 check highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: on ')'"
706 dummy <- gap-left gap
707 highlight-matching-open-paren?, open-paren-depth <- highlight-matching-open-paren? gap, 1/render-cursor
708 check-not highlight-matching-open-paren?, "F - test-highlight-matching-open-paren: not on ')'"
709 }
710
711
712
713
714
715
716 fn rewind-gap-buffer _self: (addr gap-buffer) {
717 var self/esi: (addr gap-buffer) <- copy _self
718 var dest/eax: (addr int) <- get self, left-read-index
719 copy-to *dest, 0
720 dest <- get self, right-read-index
721 copy-to *dest, 0
722 }
723
724 fn gap-buffer-scan-done? _self: (addr gap-buffer) -> _/eax: boolean {
725 var self/esi: (addr gap-buffer) <- copy _self
726
727 var left/eax: (addr grapheme-stack) <- get self, left
728 var left-size/eax: int <- grapheme-stack-length left
729 var left-read-index/ecx: (addr int) <- get self, left-read-index
730 compare *left-read-index, left-size
731 {
732 break-if->=
733 return 0/false
734 }
735
736 var right/eax: (addr grapheme-stack) <- get self, right
737 var right-size/eax: int <- grapheme-stack-length right
738 var right-read-index/ecx: (addr int) <- get self, right-read-index
739 compare *right-read-index, right-size
740 {
741 break-if->=
742 return 0/false
743 }
744
745 return 1/true
746 }
747
748 fn peek-from-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
749 var self/esi: (addr gap-buffer) <- copy _self
750
751 var left/ecx: (addr grapheme-stack) <- get self, left
752 var left-size/eax: int <- grapheme-stack-length left
753 var left-read-index-a/edx: (addr int) <- get self, left-read-index
754 compare *left-read-index-a, left-size
755 {
756 break-if->=
757 var left-data-ah/eax: (addr handle array grapheme) <- get left, data
758 var left-data/eax: (addr array grapheme) <- lookup *left-data-ah
759 var left-read-index/ecx: int <- copy *left-read-index-a
760 var result/eax: (addr grapheme) <- index left-data, left-read-index
761 return *result
762 }
763
764 var right/ecx: (addr grapheme-stack) <- get self, right
765 var _right-size/eax: int <- grapheme-stack-length right
766 var right-size/ebx: int <- copy _right-size
767 var right-read-index-a/edx: (addr int) <- get self, right-read-index
768 compare *right-read-index-a, right-size
769 {
770 break-if->=
771
772 var right-data-ah/eax: (addr handle array grapheme) <- get right, data
773 var right-data/eax: (addr array grapheme) <- lookup *right-data-ah
774 var right-read-index/ebx: int <- copy right-size
775 right-read-index <- subtract *right-read-index-a
776 right-read-index <- subtract 1
777 var result/eax: (addr grapheme) <- index right-data, right-read-index
778 return *result
779 }
780
781 return 0/nul
782 }
783
784 fn read-from-gap-buffer _self: (addr gap-buffer) -> _/eax: grapheme {
785 var self/esi: (addr gap-buffer) <- copy _self
786
787 var left/ecx: (addr grapheme-stack) <- get self, left
788 var left-size/eax: int <- grapheme-stack-length left
789 var left-read-index-a/edx: (addr int) <- get self, left-read-index
790 compare *left-read-index-a, left-size
791 {
792 break-if->=
793 var left-data-ah/eax: (addr handle array grapheme) <- get left, data
794 var left-data/eax: (addr array grapheme) <- lookup *left-data-ah
795 var left-read-index/ecx: int <- copy *left-read-index-a
796 var result/eax: (addr grapheme) <- index left-data, left-read-index
797 increment *left-read-index-a
798 return *result
799 }
800
801 var right/ecx: (addr grapheme-stack) <- get self, right
802 var _right-size/eax: int <- grapheme-stack-length right
803 var right-size/ebx: int <- copy _right-size
804 var right-read-index-a/edx: (addr int) <- get self, right-read-index
805 compare *right-read-index-a, right-size
806 {
807 break-if->=
808
809 var right-data-ah/eax: (addr handle array grapheme) <- get right, data
810 var right-data/eax: (addr array grapheme) <- lookup *right-data-ah
811 var right-read-index/ebx: int <- copy right-size
812 right-read-index <- subtract *right-read-index-a
813 right-read-index <- subtract 1
814 var result/eax: (addr grapheme) <- index right-data, right-read-index
815 increment *right-read-index-a
816 return *result
817 }
818
819 return 0/nul
820 }
821
822 fn test-read-from-gap-buffer {
823 var gap-storage: gap-buffer
824 var gap/esi: (addr gap-buffer) <- address gap-storage
825 initialize-gap-buffer-with gap, "abc"
826
827 var done?/eax: boolean <- gap-buffer-scan-done? gap
828 check-not done?, "F - test-read-from-gap-buffer/left-1/done"
829 var g/eax: grapheme <- read-from-gap-buffer gap
830 var x/ecx: int <- copy g
831 check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/left-1"
832 var done?/eax: boolean <- gap-buffer-scan-done? gap
833 check-not done?, "F - test-read-from-gap-buffer/left-2/done"
834 var g/eax: grapheme <- read-from-gap-buffer gap
835 var x/ecx: int <- copy g
836 check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/left-2"
837 var done?/eax: boolean <- gap-buffer-scan-done? gap
838 check-not done?, "F - test-read-from-gap-buffer/left-3/done"
839 var g/eax: grapheme <- read-from-gap-buffer gap
840 var x/ecx: int <- copy g
841 check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/left-3"
842 var done?/eax: boolean <- gap-buffer-scan-done? gap
843 check done?, "F - test-read-from-gap-buffer/left-4/done"
844 var g/eax: grapheme <- read-from-gap-buffer gap
845 var x/ecx: int <- copy g
846 check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/left-4"
847
848 gap-to-start gap
849 rewind-gap-buffer gap
850 var done?/eax: boolean <- gap-buffer-scan-done? gap
851 check-not done?, "F - test-read-from-gap-buffer/right-1/done"
852 var g/eax: grapheme <- read-from-gap-buffer gap
853 var x/ecx: int <- copy g
854 check-ints-equal x, 0x61/a, "F - test-read-from-gap-buffer/right-1"
855 var done?/eax: boolean <- gap-buffer-scan-done? gap
856 check-not done?, "F - test-read-from-gap-buffer/right-2/done"
857 var g/eax: grapheme <- read-from-gap-buffer gap
858 var x/ecx: int <- copy g
859 check-ints-equal x, 0x62/b, "F - test-read-from-gap-buffer/right-2"
860 var done?/eax: boolean <- gap-buffer-scan-done? gap
861 check-not done?, "F - test-read-from-gap-buffer/right-3/done"
862 var g/eax: grapheme <- read-from-gap-buffer gap
863 var x/ecx: int <- copy g
864 check-ints-equal x, 0x63/c, "F - test-read-from-gap-buffer/right-3"
865 var done?/eax: boolean <- gap-buffer-scan-done? gap
866 check done?, "F - test-read-from-gap-buffer/right-4/done"
867 var g/eax: grapheme <- read-from-gap-buffer gap
868 var x/ecx: int <- copy g
869 check-ints-equal x, 0/nul, "F - test-read-from-gap-buffer/right-4"
870 }
871
872 fn skip-whitespace-from-gap-buffer self: (addr gap-buffer) {
873 var done?/eax: boolean <- gap-buffer-scan-done? self
874 compare done?, 0/false
875 break-if-!=
876 var g/eax: grapheme <- peek-from-gap-buffer self
877 {
878 compare g, 0x20/space
879 break-if-=
880 compare g, 0xa/newline
881 break-if-=
882 return
883 }
884 g <- read-from-gap-buffer self
885 loop
886 }
887
888 fn edit-gap-buffer self: (addr gap-buffer), key: grapheme {
889 var g/edx: grapheme <- copy key
890 {
891 compare g, 8/backspace
892 break-if-!=
893 delete-before-gap self
894 return
895 }
896 {
897 compare g, 0x80/left-arrow
898 break-if-!=
899 var dummy/eax: grapheme <- gap-left self
900 return
901 }
902 {
903 compare g, 0x83/right-arrow
904 break-if-!=
905 var dummy/eax: grapheme <- gap-right self
906 return
907 }
908 {
909 compare g, 1/ctrl-a
910 break-if-!=
911 gap-to-start self
912 return
913 }
914 {
915 compare g, 5/ctrl-e
916 break-if-!=
917 gap-to-end self
918 return
919 }
920 {
921 compare g, 0x15/ctrl-u
922 break-if-!=
923 clear-gap-buffer self
924 return
925 }
926
927 add-grapheme-at-gap self, g
928 }