1
2
3
4
5 def! main text:text [
6 local-scope
7 load-inputs
8 open-console
9 clear-screen 0/screen
10 editor:&:editor <- new-editor text, 5/left, 45/right
11 editor-render 0/screen, editor
12 editor-event-loop 0/screen, 0/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, 0/false
54 click-row:num <- get t, row:offset
55 return-unless click-row, 0/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?, 0/false
60 right:num <- get *editor, right:offset
61 too-far-right?:bool <- greater-than click-column, right
62 return-if too-far-right?, 0/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 1/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, 0/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?, 0/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 1/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, 0/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 0/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?, 1/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 0/don't-render
260 }
261 return 1/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 clear-line-until screen, right
273 row <- add row, 1
274 draw-horizontal screen, row, left, right, 9480/horizontal-dotted
275 row <- add row, 1
276 clear-screen-from screen, row, left, left, right
277 assert-no-scroll screen, old-top-idx
278 ]
279
280 scenario editor-handles-empty-event-queue [
281 local-scope
282 assume-screen 10/width, 5/height
283 e:&:editor <- new-editor [abc], 0/left, 10/right
284 editor-render screen, e
285 assume-console []
286 run [
287 ¦ editor-event-loop screen, console, e
288 ]
289 screen-should-contain [
290 ¦ . .
291 ¦ .abc .
292 ¦ .╌╌╌╌╌╌╌╌╌╌.
293 ¦ . .
294 ]
295 ]
296
297 scenario editor-handles-mouse-clicks [
298 local-scope
299 assume-screen 10/width, 5/height
300 e:&:editor <- new-editor [abc], 0/left, 10/right
301 editor-render screen, e
302 $clear-trace
303 assume-console [
304 ¦ left-click 1, 1
305 ]
306 run [
307 ¦ editor-event-loop screen, console, e
308 ¦ 3:num/raw <- get *e, cursor-row:offset
309 ¦ 4:num/raw <- get *e, cursor-column:offset
310 ]
311 screen-should-contain [
312 ¦ . .
313 ¦ .abc .
314 ¦ .╌╌╌╌╌╌╌╌╌╌.
315 ¦ . .
316 ]
317 memory-should-contain [
318 ¦ 3 <- 1
319 ¦ 4 <- 1
320 ]
321 check-trace-count-for-label 0, [print-character]
322 ]
323
324 scenario editor-handles-mouse-clicks-outside-text [
325 local-scope
326 assume-screen 10/width, 5/height
327 e:&:editor <- new-editor [abc], 0/left, 10/right
328 $clear-trace
329 assume-console [
330 ¦ left-click 1, 7
331 ]
332 run [
333 ¦ editor-event-loop screen, console, e
334 ¦ 3:num/raw <- get *e, cursor-row:offset
335 ¦ 4:num/raw <- get *e, cursor-column:offset
336 ]
337 memory-should-contain [
338 ¦ 3 <- 1
339 ¦ 4 <- 3
340 ]
341 check-trace-count-for-label 0, [print-character]
342 ]
343
344 scenario editor-handles-mouse-clicks-outside-text-2 [
345 local-scope
346 assume-screen 10/width, 5/height
347 s:text <- new [abc
348 def]
349 e:&:editor <- new-editor s, 0/left, 10/right
350 $clear-trace
351 assume-console [
352 ¦ left-click 1, 7
353 ]
354 run [
355 ¦ editor-event-loop screen, console, e
356 ¦ 3:num/raw <- get *e, cursor-row:offset
357 ¦ 4:num/raw <- get *e, cursor-column:offset
358 ]
359 memory-should-contain [
360 ¦ 3 <- 1
361 ¦ 4 <- 3
362 ]
363 check-trace-count-for-label 0, [print-character]
364 ]
365
366 scenario editor-handles-mouse-clicks-outside-text-3 [
367 local-scope
368 assume-screen 10/width, 5/height
369 s:text <- new [abc
370 def]
371 e:&:editor <- new-editor s, 0/left, 10/right
372 $clear-trace
373 assume-console [
374 ¦ left-click 3, 7
375 ]
376 run [
377 ¦ editor-event-loop screen, console, e
378 ¦ 3:num/raw <- get *e, cursor-row:offset
379 ¦ 4:num/raw <- get *e, cursor-column:offset
380 ]
381 memory-should-contain [
382 ¦ 3 <- 2
383 ¦ 4 <- 3
384 ]
385 check-trace-count-for-label 0, [print-character]
386 ]
387
388 scenario editor-handles-mouse-clicks-outside-column [
389 local-scope
390 assume-screen 10/width, 5/height
391
392 e:&:editor <- new-editor [abc], 0/left, 5/right
393 editor-render screen, e
394 $clear-trace
395 assume-console [
396 ¦
397 ¦ left-click 3, 8
398 ]
399 run [
400 ¦ editor-event-loop screen, console, e
401 ¦ 3:num/raw <- get *e, cursor-row:offset
402 ¦ 4:num/raw <- get *e, cursor-column:offset
403 ]
404 screen-should-contain [
405 ¦ . .
406 ¦ .abc .
407 ¦ .╌╌╌╌╌ .
408 ¦ . .
409 ]
410 memory-should-contain [
411 ¦ 3 <- 1
412 ¦ 4 <- 0
413 ]
414 check-trace-count-for-label 0, [print-character]
415 ]
416
417 scenario editor-handles-mouse-clicks-in-menu-area [
418 local-scope
419 assume-screen 10/width, 5/height
420 e:&:editor <- new-editor [abc], 0/left, 5/right
421 editor-render screen, e
422 $clear-trace
423 assume-console [
424 ¦
425 ¦ left-click 0, 3
426 ]
427 run [
428 ¦ editor-event-loop screen, console, e
429 ¦ 3:num/raw <- get *e, cursor-row:offset
430 ¦ 4:num/raw <- get *e, cursor-column:offset
431 ]
432
433 memory-should-contain [
434 ¦ 3 <- 1
435 ¦ 4 <- 0
436 ]
437 ]
438
439 scenario editor-inserts-characters-into-empty-editor [
440 local-scope
441 assume-screen 10/width, 5/height
442 e:&:editor <- new-editor [], 0/left, 5/right
443 editor-render screen, e
444 $clear-trace
445 assume-console [
446 ¦ type [abc]
447 ]
448 run [
449 ¦ editor-event-loop screen, console, e
450 ]
451 screen-should-contain [
452 ¦ . .
453 ¦ .abc .
454 ¦ .╌╌╌╌╌ .
455 ¦ . .
456 ]
457 check-trace-count-for-label 3, [print-character]
458 ]
459
460 scenario editor-inserts-characters-at-cursor [
461 local-scope
462 assume-screen 10/width, 5/height
463 e:&:editor <- new-editor [abc], 0/left, 10/right
464 editor-render screen, e
465 $clear-trace
466
467 assume-console [
468 ¦ type [0]
469 ¦ left-click 1, 2
470 ¦ type [d]
471 ]
472 run [
473 ¦ editor-event-loop screen, console, e
474 ]
475 screen-should-contain [
476 ¦ . .
477 ¦ .0adbc .
478 ¦ .╌╌╌╌╌╌╌╌╌╌.
479 ¦ . .
480 ]
481 check-trace-count-for-label 7, [print-character]
482 ]
483
484 scenario editor-inserts-characters-at-cursor-2 [
485 local-scope
486 assume-screen 10/width, 5/height
487 e:&:editor <- new-editor [abc], 0/left, 10/right
488 editor-render screen, e
489 $clear-trace
490 assume-console [
491 ¦ left-click 1, 5
492 ¦ type [d]
493 ]
494 run [
495 ¦ editor-event-loop screen, console, e
496 ]
497 screen-should-contain [
498 ¦ . .
499 ¦ .abcd .
500 ¦ .╌╌╌╌╌╌╌╌╌╌.
501 ¦ . .
502 ]
503 check-trace-count-for-label 1, [print-character]
504 ]
505
506 scenario editor-inserts-characters-at-cursor-5 [
507 local-scope
508 assume-screen 10/width, 5/height
509 s:text <- new [abc
510 d]
511 e:&:editor <- new-editor s, 0/left, 10/right
512 editor-render screen, e
513 $clear-trace
514 assume-console [
515 ¦ left-click 1, 5
516 ¦ type [e]
517 ]
518 run [
519 ¦ editor-event-loop screen, console, e
520 ]
521 screen-should-contain [
522 ¦ . .
523 ¦ .abce .
524 ¦ .d .
525 ¦ .╌╌╌╌╌╌╌╌╌╌.
526 ¦ . .
527 ]
528 check-trace-count-for-label 1, [print-character]
529 ]
530
531 scenario editor-inserts-characters-at-cursor-3 [
532 local-scope
533 assume-screen 10/width, 5/height
534 e:&:editor <- new-editor [abc], 0/left, 10/right
535 editor-render screen, e
536 $clear-trace
537 assume-console [
538 ¦ left-click 3, 5
539 ¦ type [d]
540 ]
541 run [
542 ¦ editor-event-loop screen, console, e
543 ]
544 screen-should-contain [
545 ¦ . .
546 ¦ .abcd .
547 ¦ .╌╌╌╌╌╌╌╌╌╌.
548 ¦ . .
549 ]
550 check-trace-count-for-label 1, [print-character]
551 ]
552
553 scenario editor-inserts-characters-at-cursor-4 [
554 local-scope
555 assume-screen 10/width, 5/height
556 s:text <- new [abc
557 d]
558 e:&:editor <- new-editor s, 0/left, 10/right
559 editor-render screen, e
560 $clear-trace
561 assume-console [
562 ¦ left-click 3, 5
563 ¦ type [e]
564 ]
565 run [
566 ¦ editor-event-loop screen, console, e
567 ]
568 screen-should-contain [
569 ¦ . .
570 ¦ .abc .
571 ¦ .de .
572 ¦ .╌╌╌╌╌╌╌╌╌╌.
573 ¦ . .
574 ]
575 check-trace-count-for-label 1, [print-character]
576 ]
577
578 scenario editor-inserts-characters-at-cursor-6 [
579 local-scope
580 assume-screen 10/width, 5/height
581 s:text <- new [abc
582 d]
583 e:&:editor <- new-editor s, 0/left, 10/right
584 editor-render screen, e
585 $clear-trace
586 assume-console [
587 ¦ left-click 3, 5
588 ¦ type [ef]
589 ]
590 run [
591 ¦ editor-event-loop screen, console, e
592 ]
593 screen-should-contain [
594 ¦ . .
595 ¦ .abc .
596 ¦ .def .
597 ¦ .╌╌╌╌╌╌╌╌╌╌.
598 ¦ . .
599 ]
600 check-trace-count-for-label 2, [print-character]
601 ]
602
603 scenario editor-moves-cursor-after-inserting-characters [
604 local-scope
605 assume-screen 10/width, 5/height
606 e:&:editor <- new-editor [ab], 0/left, 5/right
607 editor-render screen, e
608 assume-console [
609 ¦ type [01]
610 ]
611 run [
612 ¦ editor-event-loop screen, console, e
613 ]
614 screen-should-contain [
615 ¦ . .
616 ¦ .01ab .
617 ¦ .╌╌╌╌╌ .
618 ¦ . .
619 ]
620 ]
621
622
623
624 scenario editor-wraps-line-on-insert [
625 local-scope
626 assume-screen 5/width, 5/height
627 e:&:editor <- new-editor [abc], 0/left, 5/right
628 editor-render screen, e
629
630 assume-console [
631 ¦ type [e]
632 ]
633 run [
634 ¦ editor-event-loop screen, console, e
635 ]
636
637 screen-should-contain [
638 ¦ . .
639 ¦ .eabc .
640 ¦ .╌╌╌╌╌.
641 ¦ . .
642 ¦ . .
643 ]
644
645 assume-console [
646 ¦ type [f]
647 ]
648 run [
649 ¦ editor-event-loop screen, console, e
650 ]
651
652 screen-should-contain [
653 ¦ . .
654 ¦ .efab↩.
655 ¦ .c .
656 ¦ .╌╌╌╌╌.
657 ¦ . .
658 ]
659 ]
660
661 scenario editor-wraps-line-on-insert-2 [
662 local-scope
663
664 assume-screen 10/width, 5/height
665 s:text <- new [abcdefg
666 defg]
667 e:&:editor <- new-editor s, 0/left, 5/right
668 editor-render screen, e
669
670 assume-console [
671 ¦ left-click 3, 0
672 ¦ type [abc]
673 ]
674 run [
675 ¦ editor-event-loop screen, console, e
676 ¦ 3:num/raw <- get *e, cursor-row:offset
677 ¦ 4:num/raw <- get *e, cursor-column:offset
678 ]
679
680 memory-should-contain [
681 ¦ 3 <- 3
682 ¦ 4 <- 3
683 ]
684
685 screen-should-contain [
686 ¦ . .
687 ¦ .abcd↩ .
688 ¦ .efg .
689 ¦ .abcd↩ .
690 ¦ .efg .
691 ]
692 ]
693
694 after <insert-character-special-case> [
695
696 {
697 ¦
698 ¦
699 ¦
700 ¦ wrap-column:num <- copy right
701 ¦ before-wrap-column:num <- subtract wrap-column, 1
702 ¦ at-wrap?:bool <- greater-or-equal cursor-column, wrap-column
703 ¦ just-before-wrap?:bool <- greater-or-equal cursor-column, before-wrap-column
704 ¦ next:&:duplex-list:char <- next before-cursor
705 ¦
706 ¦ at-end-of-line?:bool <- equal next, 0
707 ¦ {
708 ¦ ¦ break-if at-end-of-line?
709 ¦ ¦ next-character:char <- get *next, value:offset
710 ¦ ¦ at-end-of-line? <- equal next-character, 10/newline
711 ¦ }
712 ¦
713 ¦ move-cursor-to-next-line?:bool <- copy 0/false
714 ¦ {
715 ¦ ¦ break-if at-end-of-line?
716 ¦ ¦ move-cursor-to-next-line? <- copy just-before-wrap?
717 ¦ ¦
718 ¦ ¦
719 ¦ ¦ potential-new-cursor-column:num <- copy left
720 ¦ }
721 ¦ {
722 ¦ ¦ break-unless at-end-of-line?
723 ¦ ¦ move-cursor-to-next-line? <- copy at-wrap?
724 ¦ ¦
725 ¦ ¦
726 ¦ ¦
727 ¦ ¦ potential-new-cursor-column:num <- add left, 1/make-room-for-wrap-indicator
728 ¦ }
729 ¦ break-unless move-cursor-to-next-line?
730 ¦ cursor-column <- copy potential-new-cursor-column
731 ¦ *editor <- put *editor, cursor-column:offset, cursor-column
732 ¦ cursor-row <- add cursor-row, 1
733 ¦ *editor <- put *editor, cursor-row:offset, cursor-row
734 ¦
735 ¦ {
736 ¦ ¦ below-screen?:bool <- greater-or-equal cursor-row, screen-height
737 ¦ ¦ break-unless below-screen?
738 ¦ ¦ <scroll-down>
739 ¦ }
740 ¦ return 1/go-render
741 }
742 ]
743
744 scenario editor-wraps-cursor-after-inserting-characters-in-middle-of-line [
745 local-scope
746 assume-screen 10/width, 5/height
747 e:&:editor <- new-editor [abcde], 0/left, 5/right
748 assume-console [
749 ¦ left-click 1, 3
750 ¦ type [f]
751 ]
752 run [
753 ¦ editor-event-loop screen, console, e
754 ¦ 3:num/raw <- get *e, cursor-row:offset
755 ¦ 4:num/raw <- get *e, cursor-column:offset
756 ]
757 screen-should-contain [
758 ¦ . .
759 ¦ .abcf↩ .
760 ¦ .de .
761 ¦ .╌╌╌╌╌ .
762 ¦ . .
763 ]
764 memory-should-contain [
765 ¦ 3 <- 2
766 ¦ 4 <- 0
767 ]
768 ]
769
770 scenario editor-wraps-cursor-after-inserting-characters-at-end-of-line [
771 local-scope
772 assume-screen 10/width, 5/height
773
774 s:text <- new [abc
775 xyz]
776 e:&:editor <- new-editor s, 0/left, 5/right
777 editor-render screen, e
778 screen-should-contain [
779 ¦ . .
780 ¦ .abc .
781 ¦ .xyz .
782 ¦ .╌╌╌╌╌ .
783 ¦ . .
784 ]
785 assume-console [
786 ¦ left-click 1, 4
787 ¦ type [de]
788 ]
789 run [
790 ¦ editor-event-loop screen, console, e
791 ]
792 screen-should-contain [
793 ¦ . .
794 ¦ .abcd↩ .
795 ¦ .e .
796 ¦ .xyz .
797 ¦ .╌╌╌╌╌ .
798 ]
799 ]
800
801 scenario editor-wraps-cursor-to-left-margin [
802 local-scope
803 assume-screen 10/width, 5/height
804 e:&:editor <- new-editor [abcde], 2/left, 7/right
805 assume-console [
806 ¦ left-click 1, 5
807 ¦ type [01]
808 ]
809 run [
810 ¦ editor-event-loop screen, console, e
811 ¦ 3:num/raw <- get *e, cursor-row:offset
812 ¦ 4:num/raw <- get *e, cursor-column:offset
813 ]
814 screen-should-contain [
815 ¦ . .
816 ¦ . abc0↩ .
817 ¦ . 1de .
818 ¦ . ╌╌╌╌╌ .
819 ¦ . .
820 ]
821 memory-should-contain [
822 ¦ 3 <- 2
823 ¦ 4 <- 3
824 ]
825 ]
826
827
828
829 container editor [
830 indent?:bool
831 ]
832
833 after <editor-initialization> [
834 *result <- put *result, indent?:offset, 1/true
835 ]
836
837 scenario editor-moves-cursor-down-after-inserting-newline [
838 local-scope
839 assume-screen 10/width, 5/height
840 e:&:editor <- new-editor [abc], 0/left, 10/right
841 assume-console [
842 ¦ type [0
843 1]
844 ]
845 run [
846 ¦ editor-event-loop screen, console, e
847 ]
848 screen-should-contain [
849 ¦ . .
850 ¦ .0 .
851 ¦ .1abc .
852 ¦ .╌╌╌╌╌╌╌╌╌╌.
853 ¦ . .
854 ]
855 ]
856
857 after <handle-special-character> [
858 {
859 ¦ newline?:bool <- equal c, 10/newline
860 ¦ break-unless newline?
861 ¦ <begin-insert-enter>
862 ¦ insert-new-line-and-indent editor, screen
863 ¦ <end-insert-enter>
864 ¦ return 1/go-render
865 }
866 ]
867
868 def insert-new-line-and-indent editor:&:editor, screen:&:screen -> editor:&:editor, screen:&:screen [
869 local-scope
870 load-inputs
871 cursor-row:num <- get *editor, cursor-row:offset
872 cursor-column:num <- get *editor, cursor-column:offset
873 before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
874 left:num <- get *editor, left:offset
875 right:num <- get *editor, right:offset
876 screen-height:num <- screen-height screen
877
878 at-start-of-wrapped-line?:bool <- at-start-of-wrapped-line? editor
879 {
880 ¦ break-if at-start-of-wrapped-line?
881 ¦ cursor-row <- add cursor-row, 1
882 ¦ *editor <- put *editor, cursor-row:offset, cursor-row
883 }
884 cursor-column <- copy left
885 *editor <- put *editor, cursor-column:offset, cursor-column
886
887 {
888 ¦ below-screen?:bool <- greater-or-equal cursor-row, screen-height
889 ¦ break-unless below-screen?
890 ¦ <scroll-down>
891 ¦ cursor-row <- subtract cursor-row, 1
892 ¦ *editor <- put *editor, cursor-row:offset, cursor-row
893 }
894
895 insert 10/newline, before-cursor
896 before-cursor <- next before-cursor
897 *editor <- put *editor, before-cursor:offset, before-cursor
898
899 indent?:bool <- get *editor, indent?:offset
900 return-unless indent?
901 d:&:duplex-list:char <- get *editor, data:offset
902 end-of-previous-line:&:duplex-list:char <- prev before-cursor
903 indent:num <- line-indent end-of-previous-line, d
904 i:num <- copy 0
905 {
906 ¦ indent-done?:bool <- greater-or-equal i, indent
907 ¦ break-if indent-done?
908 ¦ insert-at-cursor editor, 32/space, screen
909 ¦ i <- add i, 1
910 ¦ loop
911 }
912 ]
913
914 def at-start-of-wrapped-line? editor:&:editor -> result:bool [
915 local-scope
916 load-inputs
917 left:num <- get *editor, left:offset
918 cursor-column:num <- get *editor, cursor-column:offset
919 cursor-at-left?:bool <- equal cursor-column, left
920 return-unless cursor-at-left?, 0/false
921 before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
922 before-before-cursor:&:duplex-list:char <- prev before-cursor
923 return-unless before-before-cursor, 0/false
924 char-before-cursor:char <- get *before-cursor, value:offset
925 cursor-after-newline?:bool <- equal char-before-cursor, 10/newline
926 return-if cursor-after-newline?, 0/false
927
928
929 return 1/true
930 ]
931
932
933
934 def line-indent curr:&:duplex-list:char, start:&:duplex-list:char -> result:num [
935 local-scope
936 load-inputs
937 result:num <- copy 0
938 return-unless curr
939 at-start?:bool <- equal curr, start
940 return-if at-start?
941 {
942 ¦ curr <- prev curr
943 ¦ break-unless curr
944 ¦ at-start?:bool <- equal curr, start
945 ¦ break-if at-start?
946 ¦ c:char <- get *curr, value:offset
947 ¦ at-newline?:bool <- equal c, 10/newline
948 ¦ break-if at-newline?
949 ¦
950 ¦ is-space?:bool <- equal c, 32/space
951 ¦ {
952 ¦ ¦ break-unless is-space?
953 ¦ ¦ result <- add result, 1
954 ¦ }
955 ¦
956 ¦ {
957 ¦ ¦ break-if is-space?
958 ¦ ¦ result <- copy 0
959 ¦ }
960 ¦ loop
961 }
962 ]
963
964 scenario editor-moves-cursor-down-after-inserting-newline-2 [
965 local-scope
966 assume-screen 10/width, 5/height
967 e:&:editor <- new-editor [abc], 1/left, 10/right
968 assume-console [
969 ¦ type [0
970 1]
971 ]
972 run [
973 ¦ editor-event-loop screen, console, e
974 ]
975 screen-should-contain [
976 ¦ . .
977 ¦ . 0 .
978 ¦ . 1abc .
979 ¦ . ╌╌╌╌╌╌╌╌╌.
980 ¦ . .
981 ]
982 ]
983
984 scenario editor-clears-previous-line-completely-after-inserting-newline [
985 local-scope
986 assume-screen 10/width, 5/height
987 e:&:editor <- new-editor [abcde], 0/left, 5/right
988 editor-render screen, e
989 screen-should-contain [
990 ¦ . .
991 ¦ .abcd↩ .
992 ¦ .e .
993 ¦ .╌╌╌╌╌ .
994 ¦ . .
995 ]
996 assume-console [
997 ¦ press enter
998 ]
999 run [
1000 ¦ editor-event-loop screen, console, e
1001 ]
1002
1003 screen-should-contain [
1004 ¦ . .
1005 ¦ . .
1006 ¦ .abcd↩ .
1007 ¦ .e .
1008 ¦ .╌╌╌╌╌ .
1009 ]
1010 ]
1011
1012 scenario editor-splits-wrapped-line-after-inserting-newline [
1013 local-scope
1014 assume-screen 10/width, 5/height
1015 e:&:editor <- new-editor [abcdef], 0/left, 5/right
1016 editor-render screen, e
1017 screen-should-contain [
1018 ¦ . .
1019 ¦ .abcd↩ .
1020 ¦ .ef .
1021 ¦ .╌╌╌╌╌ .
1022 ¦ . .
1023 ]
1024 assume-console [
1025 ¦ left-click 2, 0
1026 ¦ press enter
1027 ]
1028 run [
1029 ¦ editor-event-loop screen, console, e
1030 ¦ 10:num/raw <- get *e, cursor-row:offset
1031 ¦ 11:num/raw <- get *e, cursor-column:offset
1032 ]
1033 screen-should-contain [
1034 ¦ . .
1035 ¦ .abcd .
1036 ¦ .ef .
1037 ¦ .╌╌╌╌╌ .
1038 ]
1039 memory-should-contain [
1040 ¦ 10 <- 2
1041 ¦ 11 <- 0
1042 ]
1043 ]
1044
1045 scenario editor-inserts-indent-after-newline [
1046 local-scope
1047 assume-screen 10/width, 10/height
1048 s:text <- new [ab
1049 cd
1050 ef]
1051 e:&:editor <- new-editor s, 0/left, 10/right
1052
1053 assume-console [
1054 ¦ left-click 2, 8
1055 ¦ type [
1056 ]
1057 ]
1058 run [
1059 ¦ editor-event-loop screen, console, e
1060 ¦ 3:num/raw <- get *e, cursor-row:offset
1061 ¦ 4:num/raw <- get *e, cursor-column:offset
1062 ]
1063
1064 memory-should-contain [
1065 ¦ 3 <- 3
1066 ¦ 4 <- 2
1067 ]
1068 ]
1069
1070 scenario editor-skips-indent-around-paste [
1071 local-scope
1072 assume-screen 10/width, 10/height
1073 s:text <- new [ab
1074 cd
1075 ef]
1076 e:&:editor <- new-editor s, 0/left, 10/right
1077
1078 assume-console [
1079 ¦ left-click 2, 8
1080 ¦ press 65507
1081 ¦ press enter
1082 ¦ press 65506
1083 ]
1084 run [
1085 ¦ editor-event-loop screen, console, e
1086 ¦ 3:num/raw <- get *e, cursor-row:offset
1087 ¦ 4:num/raw <- get *e, cursor-column:offset
1088 ]
1089
1090 memory-should-contain [
1091 ¦ 3 <- 3
1092 ¦ 4 <- 0
1093 ]
1094 ]
1095
1096 after <handle-special-key> [
1097 {
1098 ¦ paste-start?:bool <- equal k, 65507/paste-start
1099 ¦ break-unless paste-start?
1100 ¦ *editor <- put *editor, indent?:offset, 0/false
1101 ¦ return 1/go-render
1102 }
1103 ]
1104
1105 after <handle-special-key> [
1106 {
1107 ¦ paste-end?:bool <- equal k, 65506/paste-end
1108 ¦ break-unless paste-end?
1109 ¦ *editor <- put *editor, indent?:offset, 1/true
1110 ¦ return 1/go-render
1111 }
1112 ]
1113
1114
1115
1116 def draw-horizontal screen:&:screen, row:num, x:num, right:num -> screen:&:screen [
1117 local-scope
1118 load-inputs
1119 height:num <- screen-height screen
1120 past-bottom?:bool <- greater-or-equal row, height
1121 return-if past-bottom?
1122 style:char, style-found?:bool <- next-input
1123 {
1124 ¦ break-if style-found?
1125 ¦ style <- copy 9472/horizontal
1126 }
1127 color:num, color-found?:bool <- next-input
1128 {
1129 ¦
1130 ¦ break-if color-found?
1131 ¦ color <- copy 245/grey
1132 }
1133 bg-color:num, bg-color-found?:bool <- next-input
1134 {
1135 ¦ break-if bg-color-found?
1136 ¦ bg-color <- copy 0/black
1137 }
1138 screen <- move-cursor screen, row, x
1139 {
1140 ¦ continue?:bool <- lesser-or-equal x, right
1141 ¦ break-unless continue?
1142 ¦ print screen, style, color, bg-color
1143 ¦ x <- add x, 1
1144 ¦ loop
1145 }
1146 ]