https://github.com/akkartik/mu1/blob/master/edit/002-typing.mu
1
2
3
4
5 def! main text:text [
6 local-scope
7 load-inputs
8 open-console
9 clear-screen null/screen
10 editor:&:editor <- new-editor text, 5/left, 45/right
11 editor-render null/screen, editor
12 editor-event-loop null/screen, null/console, editor
13 close-console
14 ]
15
16 def editor-event-loop screen:&:screen, console:&:console, editor:&:editor -> screen:&:screen, console:&:console, editor:&:editor [
17 local-scope
18 load-inputs
19 {
20
21 +next-event
22 cursor-row:num <- get *editor, cursor-row:offset
23 cursor-column:num <- get *editor, cursor-column:offset
24 screen <- move-cursor screen, cursor-row, cursor-column
25 e:event, found?:bool, quit?:bool, console <- read-event console
26 loop-unless found?
27 break-if quit?
28 trace 10, [app], [next-event]
29
30 t:touch-event, is-touch?:bool <- maybe-convert e, touch:variant
31 {
32 break-unless is-touch?
33 move-cursor editor, screen, t
34 loop +next-event
35 }
36
37 {
38 break-if is-touch?
39 go-render?:bool <- handle-keyboard-event screen, editor, e
40 {
41 break-unless go-render?
42 screen <- editor-render screen, editor
43 }
44 }
45 loop
46 }
47 ]
48
49
50 def move-cursor editor:&:editor, screen:&:screen, t:touch-event -> in-focus?:bool, editor:&:editor [
51 local-scope
52 load-inputs
53 return-unless editor, false
54 click-row:num <- get t, row:offset
55 return-unless click-row, false
56 click-column:num <- get t, column:offset
57 left:num <- get *editor, left:offset
58 too-far-left?:bool <- lesser-than click-column, left
59 return-if too-far-left?, false
60 right:num <- get *editor, right:offset
61 too-far-right?:bool <- greater-than click-column, right
62 return-if too-far-right?, false
63
64 <begin-move-cursor>
65 editor <- snap-cursor editor, screen, click-row, click-column
66 undo-coalesce-tag:num <- copy 0/never
67 <end-move-cursor>
68
69 return true
70 ]
71
72
73
74
75 def snap-cursor editor:&:editor, screen:&:screen, target-row:num, target-column:num -> editor:&:editor [
76 local-scope
77 load-inputs
78 return-unless editor
79 left:num <- get *editor, left:offset
80 right:num <- get *editor, right:offset
81 screen-height:num <- screen-height screen
82
83 curr:&:duplex-list:char <- get *editor, top-of-screen:offset
84 prev:&:duplex-list:char <- copy curr
85 curr <- next curr
86 row:num <- copy 1/top
87 column:num <- copy left
88 *editor <- put *editor, cursor-row:offset, target-row
89 cursor-row:num <- copy target-row
90 *editor <- put *editor, cursor-column:offset, target-column
91 cursor-column:num <- copy target-column
92 before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
93 {
94 +next-character
95 break-unless curr
96 off-screen?:bool <- greater-or-equal row, screen-height
97 break-if off-screen?
98
99
100
101 {
102 at-cursor-row?:bool <- equal row, cursor-row
103 break-unless at-cursor-row?
104 at-cursor?:bool <- equal column, cursor-column
105 break-unless at-cursor?
106 before-cursor <- copy prev
107 *editor <- put *editor, before-cursor:offset, before-cursor
108 }
109 c:char <- get *curr, value:offset
110 {
111
112 newline?:bool <- equal c, 10/newline
113 break-unless newline?
114
115 {
116 at-cursor-row?:bool <- equal row, cursor-row
117 break-unless at-cursor-row?
118 left-of-cursor?:bool <- lesser-than column, cursor-column
119 break-unless left-of-cursor?
120 cursor-column <- copy column
121 *editor <- put *editor, cursor-column:offset, cursor-column
122 before-cursor <- copy prev
123 *editor <- put *editor, before-cursor:offset, before-cursor
124 }
125
126 row <- add row, 1
127 column <- copy left
128 curr <- next curr
129 prev <- next prev
130 loop +next-character
131 }
132 {
133
134
135 at-right?:bool <- equal column, right
136 break-unless at-right?
137 column <- copy left
138 row <- add row, 1
139
140 loop +next-character
141 }
142 curr <- next curr
143 prev <- next prev
144 column <- add column, 1
145 loop
146 }
147
148 {
149 at-cursor-row?:bool <- equal row, cursor-row
150 cursor-outside-line?:bool <- lesser-or-equal column, cursor-column
151 before-cursor-on-same-line?:bool <- and at-cursor-row?, cursor-outside-line?
152 above-cursor-row?:bool <- lesser-than row, cursor-row
153 before-cursor?:bool <- or before-cursor-on-same-line?, above-cursor-row?
154 break-unless before-cursor?
155 cursor-row <- copy row
156 *editor <- put *editor, cursor-row:offset, cursor-row
157 cursor-column <- copy column
158 *editor <- put *editor, cursor-column:offset, cursor-column
159 before-cursor <- copy prev
160 *editor <- put *editor, before-cursor:offset, before-cursor
161 }
162 ]
163
164
165
166 def handle-keyboard-event screen:&:screen, editor:&:editor, e:event -> go-render?:bool, screen:&:screen, editor:&:editor [
167 local-scope
168 load-inputs
169 return-unless editor, false/don't-render
170 screen-width:num <- screen-width screen
171 screen-height:num <- screen-height screen
172 left:num <- get *editor, left:offset
173 right:num <- get *editor, right:offset
174 before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
175 cursor-row:num <- get *editor, cursor-row:offset
176 cursor-column:num <- get *editor, cursor-column:offset
177 save-row:num <- copy cursor-row
178 save-column:num <- copy cursor-column
179
180 {
181 c:char, is-unicode?:bool <- maybe-convert e, text:variant
182 break-unless is-unicode?
183 trace 10, [app], [handle-keyboard-event: special character]
184
185 <handle-special-character>
186
187 regular-character?:bool <- greater-or-equal c, 32/space
188 return-unless regular-character?, false/don't-render
189
190 <begin-insert-character>
191 go-render? <- insert-at-cursor editor, c, screen
192 <end-insert-character>
193 return
194 }
195
196 k:num, is-keycode?:bool <- maybe-convert e:event, keycode:variant
197 assert is-keycode?, [event was of unknown type; neither keyboard nor mouse]
198
199 <handle-special-key>
200 return true/go-render
201 ]
202
203 def insert-at-cursor editor:&:editor, c:char, screen:&:screen -> go-render?:bool, editor:&:editor, screen:&:screen [
204 local-scope
205 load-inputs
206 before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
207 insert c, before-cursor
208 before-cursor <- next before-cursor
209 *editor <- put *editor, before-cursor:offset, before-cursor
210 cursor-row:num <- get *editor, cursor-row:offset
211 cursor-column:num <- get *editor, cursor-column:offset
212 left:num <- get *editor, left:offset
213 right:num <- get *editor, right:offset
214 save-row:num <- copy cursor-row
215 save-column:num <- copy cursor-column
216 screen-width:num <- screen-width screen
217 screen-height:num <- screen-height screen
218
219 <insert-character-special-case>
220
221 cursor-column <- add cursor-column, 1
222 *editor <- put *editor, cursor-column:offset, cursor-column
223 next:&:duplex-list:char <- next before-cursor
224 {
225
226 at-end?:bool <- equal next, null
227 break-unless at-end?
228 bottom:num <- subtract screen-height, 1
229 at-bottom?:bool <- equal save-row, bottom
230 at-right?:bool <- equal save-column, right
231 overflow?:bool <- and at-bottom?, at-right?
232 break-if overflow?
233 move-cursor screen, save-row, save-column
234 print screen, c
235 return false/don't-render
236 }
237 {
238
239 break-unless next
240 at-right?:bool <- greater-or-equal cursor-column, screen-width
241 break-if at-right?
242 curr:&:duplex-list:char <- copy before-cursor
243 move-cursor screen, save-row, save-column
244 curr-column:num <- copy save-column
245 {
246
247 at-right?:bool <- greater-than curr-column, right
248 return-if at-right?, true/go-render
249 break-unless curr
250
251 currc:char <- get *curr, value:offset
252 at-newline?:bool <- equal currc, 10/newline
253 break-if at-newline?
254 print screen, currc
255 curr-column <- add curr-column, 1
256 curr <- next curr
257 loop
258 }
259 return false/don't-render
260 }
261 return true/go-render
262 ]
263
264
265 def editor-render screen:&:screen, editor:&:editor -> screen:&:screen, editor:&:editor [
266 local-scope
267 load-inputs
268 old-top-idx:num <- save-top-idx screen
269 left:num <- get *editor, left:offset
270 right:num <- get *editor, right:offset
271 row:num, column:num <- render screen, editor
272 draw-horizontal screen, row, left, right, 9480/horizontal-dotted
273 row <- add row, 1
274 clear-screen-from screen, row, left, left, right
275 assert-no-scroll screen, old-top-idx
276 ]
277
278 scenario editor-handles-empty-event-queue [
279 local-scope
280 assume-screen 10/width, 5/height
281 e:&:editor <- new-editor [abc], 0/left, 10/right
282 editor-render screen, e
283 assume-console []
284 run [
285 editor-event-loop screen, console, e
286 ]
287 screen-should-contain [
288 . .
289 .abc .
290 .╌╌╌╌╌╌╌╌╌╌.
291 . .
292 ]
293 ]
294
295 scenario editor-handles-mouse-clicks [
296 local-scope
297 assume-screen 10/width, 5/height
298 e:&:editor <- new-editor [abc], 0/left, 10/right
299 editor-render screen, e
300 $clear-trace
301 assume-console [
302 left-click 1, 1
303 ]
304 run [
305 editor-event-loop screen, console, e
306 3:num/raw <- get *e, cursor-row:offset
307 4:num/raw <- get *e, cursor-column:offset
308 ]
309 screen-should-contain [
310 . .
311 .abc .
312 .╌╌╌╌╌╌╌╌╌╌.
313 . .
314 ]
315 memory-should-contain [
316 3 <- 1
317 4 <- 1
318 ]
319 check-trace-count-for-label 0, [print-character]
320 ]
321
322 scenario editor-handles-mouse-clicks-outside-text [
323 local-scope
324 assume-screen 10/width, 5/height
325 e:&:editor <- new-editor [abc], 0/left, 10/right
326 $clear-trace
327 assume-console [
328 left-click 1, 7
329 ]
330 run [
331 editor-event-loop screen, console, e
332 3:num/raw <- get *e, cursor-row:offset
333 4:num/raw <- get *e, cursor-column:offset
334 ]
335 memory-should-contain [
336 3 <- 1
337 4 <- 3
338 ]
339 check-trace-count-for-label 0, [print-character]
340 ]
341
342 scenario editor-handles-mouse-clicks-outside-text-2 [
343 local-scope
344 assume-screen 10/width, 5/height
345 s:text <- new [abc
346 def]
347 e:&:editor <- new-editor s, 0/left, 10/right
348 $clear-trace
349 assume-console [
350 left-click 1, 7
351 ]
352 run [
353 editor-event-loop screen, console, e
354 3:num/raw <- get *e, cursor-row:offset
355 4:num/raw <- get *e, cursor-column:offset
356 ]
357 memory-should-contain [
358 3 <- 1
359 4 <- 3
360 ]
361 check-trace-count-for-label 0, [print-character]
362 ]
363
364 scenario editor-handles-mouse-clicks-outside-text-3 [
365 local-scope
366 assume-screen 10/width, 5/height
367 s:text <- new [abc
368 def]
369 e:&:editor <- new-editor s, 0/left, 10/right
370 $clear-trace
371 assume-console [
372 left-click 3, 7
373 ]
374 run [
375 editor-event-loop screen, console, e
376 3:num/raw <- get *e, cursor-row:offset
377 4:num/raw <- get *e, cursor-column:offset
378 ]
379 memory-should-contain [
380 3 <- 2
381 4 <- 3
382 ]
383 check-trace-count-for-label 0, [print-character]
384 ]
385
386 scenario editor-handles-mouse-clicks-outside-column [
387 local-scope
388 assume-screen 10/width, 5/height
389
390 e:&:editor <- new-editor [abc], 0/left, 5/right
391 editor-render screen, e
392 $clear-trace
393 assume-console [
394
395 left-click 3, 8
396 ]
397 run [
398 editor-event-loop screen, console, e
399 3:num/raw <- get *e, cursor-row:offset
400 4:num/raw <- get *e, cursor-column:offset
401 ]
402 screen-should-contain [
403 . .
404 .abc .
405 .╌╌╌╌╌ .
406 . .
407 ]
408 memory-should-contain [
409 3 <- 1
410 4 <- 0
411 ]
412 check-trace-count-for-label 0, [print-character]
413 ]
414
415 scenario editor-handles-mouse-clicks-in-menu-area [
416 local-scope
417 assume-screen 10/width, 5/height
418 e:&:editor <- new-editor [abc], 0/left, 5/right
419 editor-render screen, e
420 $clear-trace
421 assume-console [
422
423 left-click 0, 3
424 ]
425 run [
426 editor-event-loop screen, console, e
427 3:num/raw <- get *e, cursor-row:offset
428 4:num/raw <- get *e, cursor-column:offset
429 ]
430
431 memory-should-contain [
432 3 <- 1
433 4 <- 0
434 ]
435 ]
436
437 scenario editor-inserts-characters-into-empty-editor [
438 local-scope
439 assume-screen 10/width, 5/height
440 e:&:editor <- new-editor [], 0/left, 5/right
441 editor-render screen, e
442 $clear-trace
443 assume-console [
444 type [abc]
445 ]
446 run [
447 editor-event-loop screen, console, e
448 ]
449 screen-should-contain [
450 . .
451 .abc .
452 .╌╌╌╌╌ .
453 . .
454 ]
455 check-trace-count-for-label 3, [print-character]
456 ]
457
458 scenario editor-inserts-characters-at-cursor [
459 local-scope
460 assume-screen 10/width, 5/height
461 e:&:editor <- new-editor [abc], 0/left, 10/right
462 editor-render screen, e
463 $clear-trace
464
465 assume-console [
466 type [0]
467 left-click 1, 2
468 type [d]
469 ]
470 run [
471 editor-event-loop screen, console, e
472 ]
473 screen-should-contain [
474 . .
475 .0adbc .
476 .╌╌╌╌╌╌╌╌╌╌.
477 . .
478 ]
479 check-trace-count-for-label 7, [print-character]
480 ]
481
482 scenario editor-inserts-characters-at-cursor-2 [
483 local-scope
484 assume-screen 10/width, 5/height
485 e:&:editor <- new-editor [abc], 0/left, 10/right
486 editor-render screen, e
487 $clear-trace
488 assume-console [
489 left-click 1, 5
490 type [d]
491 ]
492 run [
493 editor-event-loop screen, console, e
494 ]
495 screen-should-contain [
496 . .
497 .abcd .
498 .╌╌╌╌╌╌╌╌╌╌.
499 . .
500 ]
501 check-trace-count-for-label 1, [print-character]
502 ]
503
504 scenario editor-inserts-characters-at-cursor-5 [
505 local-scope
506 assume-screen 10/width, 5/height
507 s:text <- new [abc
508 d]
509 e:&:editor <- new-editor s, 0/left, 10/right
510 editor-render screen, e
511 $clear-trace
512 assume-console [
513 left-click 1, 5
514 type [e]
515 ]
516 run [
517 editor-event-loop screen, console, e
518 ]
519 screen-should-contain [
520 . .
521 .abce .
522 .d .
523 .╌╌╌╌╌╌╌╌╌╌.
524 . .
525 ]
526 check-trace-count-for-label 1, [print-character]
527 ]
528
529 scenario editor-inserts-characters-at-cursor-3 [
530 local-scope
531 assume-screen 10/width, 5/height
532 e:&:editor <- new-editor [abc], 0/left, 10/right
533 editor-render screen, e
534 $clear-trace
535 assume-console [
536 left-click 3, 5
537 type [d]
538 ]
539 run [
540 editor-event-loop screen, console, e
541 ]
542 screen-should-contain [
543 . .
544 .abcd .
545 .╌╌╌╌╌╌╌╌╌╌.
546 . .
547 ]
548 check-trace-count-for-label 1, [print-character]
549 ]
550
551 scenario editor-inserts-characters-at-cursor-4 [
552 local-scope
553 assume-screen 10/width, 5/height
554 s:text <- new [abc
555 d]
556 e:&:editor <- new-editor s, 0/left, 10/right
557 editor-render screen, e
558 $clear-trace
559 assume-console [
560 left-click 3, 5
561 type [e]
562 ]
563 run [
564 editor-event-loop screen, console, e
565 ]
566 screen-should-contain [
567 . .
568 .abc .
569 .de .
570 .╌╌╌╌╌╌╌╌╌╌.
571 . .
572 ]
573 check-trace-count-for-label 1, [print-character]
574 ]
575
576 scenario editor-inserts-characters-at-cursor-6 [
577 local-scope
578 assume-screen 10/width, 5/height
579 s:text <- new [abc
580 d]
581 e:&:editor <- new-editor s, 0/left, 10/right
582 editor-render screen, e
583 $clear-trace
584 assume-console [
585 left-click 3, 5
586 type [ef]
587 ]
588 run [
589 editor-event-loop screen, console, e
590 ]
591 screen-should-contain [
592 . .
593 .abc .
594 .def .
595 .╌╌╌╌╌╌╌╌╌╌.
596 . .
597 ]
598 check-trace-count-for-label 2, [print-character]
599 ]
600
601 scenario editor-moves-cursor-after-inserting-characters [
602 local-scope
603 assume-screen 10/width, 5/height
604 e:&:editor <- new-editor [ab], 0/left, 5/right
605 editor-render screen, e
606 assume-console [
607 type [01]
608 ]
609 run [
610 editor-event-loop screen, console, e
611 ]
612 screen-should-contain [
613 . .
614 .01ab .
615 .╌╌╌╌╌ .
616 . .
617 ]
618 ]
619
620
621
622 scenario editor-wraps-line-on-insert [
623 local-scope
624 assume-screen 5/width, 5/height
625 e:&:editor <- new-editor [abc], 0/left, 5/right
626 editor-render screen, e
627
628 assume-console [
629 type [e]
630 ]
631 run [
632 editor-event-loop screen, console, e
633 ]
634
635 screen-should-contain [
636 . .
637 .eabc .
638 .╌╌╌╌╌.
639 . .
640 . .
641 ]
642
643 assume-console [
644 type [f]
645 ]
646 run [
647 editor-event-loop screen, console, e
648 ]
649
650 screen-should-contain [
651 . .
652 .efab↩.
653 .c .
654 .╌╌╌╌╌.
655 . .
656 ]
657 ]
658
659 scenario editor-wraps-line-on-insert-2 [
660 local-scope
661
662 assume-screen 10/width, 5/height
663 s:text <- new [abcdefg
664 defg]
665 e:&:editor <- new-editor s, 0/left, 5/right
666 editor-render screen, e
667
668 assume-console [
669 left-click 3, 0
670 type [abc]
671 ]
672 run [
673 editor-event-loop screen, console, e
674 3:num/raw <- get *e, cursor-row:offset
675 4:num/raw <- get *e, cursor-column:offset
676 ]
677
678 memory-should-contain [
679 3 <- 3
680 4 <- 3
681 ]
682
683 screen-should-contain [
684 . .
685 .abcd↩ .
686 .efg .
687 .abcd↩ .
688 .efg .
689 ]
690 ]
691
692 after <insert-character-special-case> [
693
694 {
695
696
697
698 wrap-column:num <- copy right
699 before-wrap-column:num <- subtract wrap-column, 1
700 at-wrap?:bool <- greater-or-equal cursor-column, wrap-column
701 just-before-wrap?:bool <- greater-or-equal cursor-column, before-wrap-column
702 next:&:duplex-list:char <- next before-cursor
703
704 at-end-of-line?:bool <- equal next, null
705 {
706 break-if at-end-of-line?
707 next-character:char <- get *next, value:offset
708 at-end-of-line? <- equal next-character, 10/newline
709 }
710
711 move-cursor-to-next-line?:bool <- copy false
712 {
713 break-if at-end-of-line?
714 move-cursor-to-next-line? <- copy just-before-wrap?
715
716
717 potential-new-cursor-column:num <- copy left
718 }
719 {
720 break-unless at-end-of-line?
721 move-cursor-to-next-line? <- copy at-wrap?
722
723
724
725 potential-new-cursor-column:num <- add left, 1/make-room-for-wrap-indicator
726 }
727 break-unless move-cursor-to-next-line?
728 cursor-column <- copy potential-new-cursor-column
729 *editor <- put *editor, cursor-column:offset, cursor-column
730 cursor-row <- add cursor-row, 1
731 *editor <- put *editor, cursor-row:offset, cursor-row
732
733 {
734 below-screen?:bool <- greater-or-equal cursor-row, screen-height
735 break-unless below-screen?
736 <scroll-down>
737 }
738 return true/go-render
739 }
740 ]
741
742 scenario editor-wraps-cursor-after-inserting-characters-in-middle-of-line [
743 local-scope
744 assume-screen 10/width, 5/height
745 e:&:editor <- new-editor [abcde], 0/left, 5/right
746 assume-console [
747 left-click 1, 3
748 type [f]
749 ]
750 run [
751 editor-event-loop screen, console, e
752 3:num/raw <- get *e, cursor-row:offset
753 4:num/raw <- get *e, cursor-column:offset
754 ]
755 screen-should-contain [
756 . .
757 .abcf↩ .
758 .de .
759 .╌╌╌╌╌ .
760 . .
761 ]
762 memory-should-contain [
763 3 <- 2
764 4 <- 0
765 ]
766 ]
767
768 scenario editor-wraps-cursor-after-inserting-characters-at-end-of-line [
769 local-scope
770 assume-screen 10/width, 5/height
771
772 s:text <- new [abc
773 xyz]
774 e:&:editor <- new-editor s, 0/left, 5/right
775 editor-render screen, e
776 screen-should-contain [
777 . .
778 .abc .
779 .xyz .
780 .╌╌╌╌╌ .
781 . .
782 ]
783 assume-console [
784 left-click 1, 4
785 type [de]
786 ]
787 run [
788 editor-event-loop screen, console, e
789 ]
790 screen-should-contain [
791 . .
792 .abcd↩ .
793 .e .
794 .xyz .
795 .╌╌╌╌╌ .
796 ]
797 ]
798
799 scenario editor-wraps-cursor-to-left-margin [
800 local-scope
801 assume-screen 10/width, 5/height
802 e:&:editor <- new-editor [abcde], 2/left, 7/right
803 assume-console [
804 left-click 1, 5
805 type [01]
806 ]
807 run [
808 editor-event-loop screen, console, e
809 3:num/raw <- get *e, cursor-row:offset
810 4:num/raw <- get *e, cursor-column:offset
811 ]
812 screen-should-contain [
813 . .
814 . abc0↩ .
815 . 1de .
816 . ╌╌╌╌╌ .
817 . .
818 ]
819 memory-should-contain [
820 3 <- 2
821 4 <- 3
822 ]
823 ]
824
825
826
827 container editor [
828 indent?:bool
829 ]
830
831 after <editor-initialization> [
832 *result <- put *result, indent?:offset, true
833 ]
834
835 scenario editor-moves-cursor-down-after-inserting-newline [
836 local-scope
837 assume-screen 10/width, 5/height
838 e:&:editor <- new-editor [abc], 0/left, 10/right
839 assume-console [
840 type [0
841 1]
842 ]
843 run [
844 editor-event-loop screen, console, e
845 ]
846 screen-should-contain [
847 . .
848 .0 .
849 .1abc .
850 .╌╌╌╌╌╌╌╌╌╌.
851 . .
852 ]
853 ]
854
855 after <handle-special-character> [
856 {
857 newline?:bool <- equal c, 10/newline
858 break-unless newline?
859 <begin-insert-enter>
860 insert-new-line-and-indent editor, screen
861 <end-insert-enter>
862 return true/go-render
863 }
864 ]
865
866 def insert-new-line-and-indent editor:&:editor, screen:&:screen -> editor:&:editor, screen:&:screen [
867 local-scope
868 load-inputs
869 cursor-row:num <- get *editor, cursor-row:offset
870 cursor-column:num <- get *editor, cursor-column:offset
871 before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
872 left:num <- get *editor, left:offset
873 right:num <- get *editor, right:offset
874 screen-height:num <- screen-height screen
875
876 at-start-of-wrapped-line?:bool <- at-start-of-wrapped-line? editor
877 {
878 break-if at-start-of-wrapped-line?
879 cursor-row <- add cursor-row, 1
880 *editor <- put *editor, cursor-row:offset, cursor-row
881 }
882 cursor-column <- copy left
883 *editor <- put *editor, cursor-column:offset, cursor-column
884
885 {
886 below-screen?:bool <- greater-or-equal cursor-row, screen-height
887 break-unless below-screen?
888 <scroll-down2>
889 cursor-row <- subtract cursor-row, 1
890 *editor <- put *editor, cursor-row:offset, cursor-row
891 }
892
893 insert 10/newline, before-cursor
894 before-cursor <- next before-cursor
895 *editor <- put *editor, before-cursor:offset, before-cursor
896
897 indent?:bool <- get *editor, indent?:offset
898 return-unless indent?
899 d:&:duplex-list:char <- get *editor, data:offset
900 end-of-previous-line:&:duplex-list:char <- prev before-cursor
901 indent:num <- line-indent end-of-previous-line, d
902 i:num <- copy 0
903 {
904 indent-done?:bool <- greater-or-equal i, indent
905 break-if indent-done?
906 insert-at-cursor editor, 32/space, screen
907 i <- add i, 1
908 loop
909 }
910 ]
911
912 def at-start-of-wrapped-line? editor:&:editor -> result:bool [
913 local-scope
914 load-inputs
915 left:num <- get *editor, left:offset
916 cursor-column:num <- get *editor, cursor-column:offset
917 cursor-at-left?:bool <- equal cursor-column, left
918 return-unless cursor-at-left?, false
919 before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
920 before-before-cursor:&:duplex-list:char <- prev before-cursor
921 return-unless before-before-cursor, false
922 char-before-cursor:char <- get *before-cursor, value:offset
923 cursor-after-newline?:bool <- equal char-before-cursor, 10/newline
924 return-if cursor-after-newline?, false
925
926
927 return true
928 ]
929
930
931
932 def line-indent curr:&:duplex-list:char, start:&:duplex-list:char -> result:num [
933 local-scope
934 load-inputs
935 result:num <- copy 0
936 return-unless curr
937 at-start?:bool <- equal curr, start
938 return-if at-start?
939 {
940 curr <- prev curr
941 break-unless curr
942 at-start?:bool <- equal curr, start
943 break-if at-start?
944 c:char <- get *curr, value:offset
945 at-newline?:bool <- equal c, 10/newline
946 break-if at-newline?
947
948 is-space?:bool <- equal c, 32/space
949 {
950 break-unless is-space?
951 result <- add result, 1
952 }
953
954 {
955 break-if is-space?
956 result <- copy 0
957 }
958 loop
959 }
960 ]
961
962 scenario editor-moves-cursor-down-after-inserting-newline-2 [
963 local-scope
964 assume-screen 10/width, 5/height
965 e:&:editor <- new-editor [abc], 1/left, 10/right
966 assume-console [
967 type [0
968 1]
969 ]
970 run [
971 editor-event-loop screen, console, e
972 ]
973 screen-should-contain [
974 . .
975 . 0 .
976 . 1abc .
977 . ╌╌╌╌╌╌╌╌╌.
978 . .
979 ]
980 ]
981
982 scenario editor-clears-previous-line-completely-after-inserting-newline [
983 local-scope
984 assume-screen 10/width, 5/height
985 e:&:editor <- new-editor [abcde], 0/left, 5/right
986 editor-render screen, e
987 screen-should-contain [
988 . .
989 .abcd↩ .
990 .e .
991 .╌╌╌╌╌ .
992 . .
993 ]
994 assume-console [
995 press enter
996 ]
997 run [
998 editor-event-loop screen, console, e
999 ]
1000
1001 screen-should-contain [
1002 . .
1003 . .
1004 .abcd↩ .
1005 .e .
1006 .╌╌╌╌╌ .
1007 ]
1008 ]
1009
1010 scenario editor-splits-wrapped-line-after-inserting-newline [
1011 local-scope
1012 assume-screen 10/width, 5/height
1013 e:&:editor <- new-editor [abcdef], 0/left, 5/right
1014 editor-render screen, e
1015 screen-should-contain [
1016 . .
1017 .abcd↩ .
1018 .ef .
1019 .╌╌╌╌╌ .
1020 . .
1021 ]
1022 assume-console [
1023 left-click 2, 0
1024 press enter
1025 ]
1026 run [
1027 editor-event-loop screen, console, e
1028 10:num/raw <- get *e, cursor-row:offset
1029 11:num/raw <- get *e, cursor-column:offset
1030 ]
1031 screen-should-contain [
1032 . .
1033 .abcd .
1034 .ef .
1035 .╌╌╌╌╌ .
1036 ]
1037 memory-should-contain [
1038 10 <- 2
1039 11 <- 0
1040 ]
1041 ]
1042
1043 scenario editor-inserts-indent-after-newline [
1044 local-scope
1045 assume-screen 10/width, 10/height
1046 s:text <- new [ab
1047 cd
1048 ef]
1049 e:&:editor <- new-editor s, 0/left, 10/right
1050
1051 assume-console [
1052 left-click 2, 8
1053 type [
1054 ]
1055 ]
1056 run [
1057 editor-event-loop screen, console, e
1058 3:num/raw <- get *e, cursor-row:offset
1059 4:num/raw <- get *e, cursor-column:offset
1060 ]
1061
1062 memory-should-contain [
1063 3 <- 3
1064 4 <- 2
1065 ]
1066 ]
1067
1068 scenario editor-skips-indent-around-paste [
1069 local-scope
1070 assume-screen 10/width, 10/height
1071 s:text <- new [ab
1072 cd
1073 ef]
1074 e:&:editor <- new-editor s, 0/left, 10/right
1075
1076 assume-console [
1077 left-click 2, 8
1078 press 65507
1079 press enter
1080 press 65506
1081 ]
1082 run [
1083 editor-event-loop screen, console, e
1084 3:num/raw <- get *e, cursor-row:offset
1085 4:num/raw <- get *e, cursor-column:offset
1086 ]
1087
1088 memory-should-contain [
1089 3 <- 3
1090 4 <- 0
1091 ]
1092 ]
1093
1094 after <handle-special-key> [
1095 {
1096 paste-start?:bool <- equal k, 65507/paste-start
1097 break-unless paste-start?
1098 *editor <- put *editor, indent?:offset, false
1099 return true/go-render
1100 }
1101 ]
1102
1103 after <handle-special-key> [
1104 {
1105 paste-end?:bool <- equal k, 65506/paste-end
1106 break-unless paste-end?
1107 *editor <- put *editor, indent?:offset, true
1108 return true/go-render
1109 }
1110 ]
1111
1112
1113
1114 def draw-horizontal screen:&:screen, row:num, x:num, right:num -> screen:&:screen [
1115 local-scope
1116 load-inputs
1117 height:num <- screen-height screen
1118 past-bottom?:bool <- greater-or-equal row, height
1119 return-if past-bottom?
1120 style:char, style-found?:bool <- next-input
1121 {
1122 break-if style-found?
1123 style <- copy 9472/horizontal
1124 }
1125 color:num, color-found?:bool <- next-input
1126 {
1127
1128 break-if color-found?
1129 color <- copy 245/grey
1130 }
1131 bg-color:num, bg-color-found?:bool <- next-input
1132 {
1133 break-if bg-color-found?
1134 bg-color <- copy 0/black
1135 }
1136 screen <- move-cursor screen, row, x
1137 {
1138 continue?:bool <- lesser-or-equal x, right
1139 break-unless continue?
1140 print screen, style, color, bg-color
1141 x <- add x, 1
1142 loop
1143 }
1144 ]