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