1
2
3
4
5
6 def! main [
7 local-scope
8 open-console
9 clear-screen 0/screen
10 env:&:environment <- new-programming-environment 0/filesystem, 0/screen
11 render-all 0/screen, env, render
12 event-loop 0/screen, 0/console, env, 0/filesystem
13 ]
14
15 container environment [
16 recipes:&:editor
17 current-sandbox:&:editor
18 sandbox-in-focus?:bool
19 ]
20
21 def new-programming-environment resources:&:resources, screen:&:screen, test-sandbox-editor-contents:text -> result:&:environment [
22 local-scope
23 load-ingredients
24 width:num <- screen-width screen
25 result <- new environment:type
26
27 initial-recipe-contents:text <- slurp resources, [lesson/recipes.mu]
28 divider:num, _ <- divide-with-remainder width, 2
29 recipes:&:editor <- new-editor initial-recipe-contents, 0/left, divider/right
30
31 sandbox-left:num <- add divider, 1
32 current-sandbox:&:editor <- new-editor test-sandbox-editor-contents, sandbox-left, width/right
33 *result <- put *result, recipes:offset, recipes
34 *result <- put *result, current-sandbox:offset, current-sandbox
35 *result <- put *result, sandbox-in-focus?:offset, 0/false
36 <programming-environment-initialization>
37 ]
38
39 def event-loop screen:&:screen, console:&:console, env:&:environment, resources:&:resources -> screen:&:screen, console:&:console, env:&:environment, resources:&:resources [
40 local-scope
41 load-ingredients
42 recipes:&:editor <- get *env, recipes:offset
43 current-sandbox:&:editor <- get *env, current-sandbox:offset
44 sandbox-in-focus?:bool <- get *env, sandbox-in-focus?:offset
45
46
47
48 render-recipes-on-no-more-events?:bool <- copy 0/false
49 render-sandboxes-on-no-more-events?:bool <- copy 0/false
50 {
51 ¦
52 ¦ +next-event
53 ¦ e:event, found?:bool, quit?:bool, console <- read-event console
54 ¦ loop-unless found?
55 ¦ break-if quit?
56 ¦ trace 10, [app], [next-event]
57 ¦ <handle-event>
58 ¦
59 ¦ {
60 ¦ ¦ k:num, is-keycode?:bool <- maybe-convert e:event, keycode:variant
61 ¦ ¦ break-unless is-keycode?
62 ¦ ¦ <global-keypress>
63 ¦ }
64 ¦ {
65 ¦ ¦ c:char, is-unicode?:bool <- maybe-convert e:event, text:variant
66 ¦ ¦ break-unless is-unicode?
67 ¦ ¦ <global-type>
68 ¦ }
69 ¦
70 ¦ {
71 ¦ ¦ t:touch-event, is-touch?:bool <- maybe-convert e:event, touch:variant
72 ¦ ¦ break-unless is-touch?
73 ¦ ¦
74 ¦ ¦
75 ¦ ¦ touch-type:num <- get t, type:offset
76 ¦ ¦ is-left-click?:bool <- equal touch-type, 65513/mouse-left
77 ¦ ¦ loop-unless is-left-click?, +next-event
78 ¦ ¦ click-row:num <- get t, row:offset
79 ¦ ¦ click-column:num <- get t, column:offset
80 ¦ ¦
81 ¦ ¦ <global-touch>
82 ¦ ¦
83 ¦ ¦ _ <- move-cursor-in-editor screen, recipes, t
84 ¦ ¦ sandbox-in-focus?:bool <- move-cursor-in-editor screen, current-sandbox, t
85 ¦ ¦ *env <- put *env, sandbox-in-focus?:offset, sandbox-in-focus?
86 ¦ ¦ screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env
87 ¦ ¦ loop +next-event
88 ¦ }
89 ¦
90 ¦
91 ¦ {
92 ¦ ¦ r:resize-event, is-resize?:bool <- maybe-convert e:event, resize:variant
93 ¦ ¦ break-unless is-resize?
94 ¦ ¦ env, screen <- resize screen, env
95 ¦ ¦ screen <- render-all screen, env, render-without-moving-cursor
96 ¦ ¦ loop +next-event
97 ¦ }
98 ¦
99 ¦ {
100 ¦ ¦ sandbox-in-focus?:bool <- get *env, sandbox-in-focus?:offset
101 ¦ ¦ {
102 ¦ ¦ ¦ break-if sandbox-in-focus?
103 ¦ ¦ ¦ render?:bool <- handle-keyboard-event screen, recipes, e:event
104 ¦ ¦ ¦ render-recipes-on-no-more-events? <- or render?, render-recipes-on-no-more-events?
105 ¦ ¦ }
106 ¦ ¦ {
107 ¦ ¦ ¦ break-unless sandbox-in-focus?
108 ¦ ¦ ¦ render?:bool <- handle-keyboard-event screen, current-sandbox, e:event
109 ¦ ¦ ¦ render-sandboxes-on-no-more-events? <- or render?, render-sandboxes-on-no-more-events?
110 ¦ ¦ }
111 ¦ ¦ more-events?:bool <- has-more-events? console
112 ¦ ¦ {
113 ¦ ¦ ¦ break-if more-events?
114 ¦ ¦ ¦ {
115 ¦ ¦ ¦ ¦ break-unless render-recipes-on-no-more-events?
116 ¦ ¦ ¦ ¦ render-recipes-on-no-more-events? <- copy 0/false
117 ¦ ¦ ¦ ¦ screen <- render-recipes screen, env, render
118 ¦ ¦ ¦ }
119 ¦ ¦ ¦ {
120 ¦ ¦ ¦ ¦ break-unless render-sandboxes-on-no-more-events?
121 ¦ ¦ ¦ ¦ render-sandboxes-on-no-more-events? <- copy 0/false
122 ¦ ¦ ¦ ¦ screen <- render-sandbox-side screen, env, render
123 ¦ ¦ ¦ }
124 ¦ ¦ }
125 ¦ ¦ screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env
126 ¦ }
127 ¦ loop
128 }
129 ]
130
131 def resize screen:&:screen, env:&:environment -> env:&:environment, screen:&:screen [
132 local-scope
133 load-ingredients
134 clear-screen screen
135 width:num <- screen-width screen
136 divider:num, _ <- divide-with-remainder width, 2
137
138 recipes:&:editor <- get *env, recipes:offset
139 right:num <- subtract divider, 1
140 *recipes <- put *recipes, right:offset, right
141
142 *recipes <- put *recipes, cursor-row:offset, 1
143 *recipes <- put *recipes, cursor-column:offset, 0
144
145 current-sandbox:&:editor <- get *env, current-sandbox:offset
146 left:num <- add divider, 1
147 *current-sandbox <- put *current-sandbox, left:offset, left
148 right:num <- subtract width, 1
149 *current-sandbox <- put *current-sandbox, right:offset, right
150
151 *current-sandbox <- put *current-sandbox, cursor-row:offset, 1
152 *current-sandbox <- put *current-sandbox, cursor-column:offset, left
153 ]
154
155
156
157
158 def render-without-moving-cursor screen:&:screen, editor:&:editor -> last-row:num, last-column:num, screen:&:screen, editor:&:editor [
159 local-scope
160 load-ingredients
161 return-unless editor, 1/top, 0/left
162 left:num <- get *editor, left:offset
163 screen-height:num <- screen-height screen
164 right:num <- get *editor, right:offset
165 curr:&:duplex-list:char <- get *editor, top-of-screen:offset
166 prev:&:duplex-list:char <- copy curr
167 curr <- next curr
168 color:num <- copy 7/white
169 row:num <- copy 1/top
170 column:num <- copy left
171
172 old-before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
173
174
175 *editor <- put *editor, cursor-row:offset, row
176 *editor <- put *editor, cursor-column:offset, column
177 top-of-screen:&:duplex-list:char <- get *editor, top-of-screen:offset
178 *editor <- put *editor, before-cursor:offset, top-of-screen
179 screen <- move-cursor screen, row, column
180 {
181 ¦ +next-character
182 ¦ break-unless curr
183 ¦ off-screen?:bool <- greater-or-equal row, screen-height
184 ¦ break-if off-screen?
185 ¦
186 ¦
187 ¦
188 ¦ {
189 ¦ ¦ at-cursor?:bool <- equal old-before-cursor, prev
190 ¦ ¦ break-unless at-cursor?
191 ¦ ¦ *editor <- put *editor, cursor-row:offset, row
192 ¦ ¦ *editor <- put *editor, cursor-column:offset, column
193 ¦ ¦ *editor <- put *editor, before-cursor:offset, old-before-cursor
194 ¦ }
195 ¦ c:char <- get *curr, value:offset
196 ¦ <character-c-received>
197 ¦ {
198 ¦ ¦
199 ¦ ¦ newline?:bool <- equal c, 10/newline
200 ¦ ¦ break-unless newline?
201 ¦ ¦
202 ¦ ¦ clear-line-until screen, right
203 ¦ ¦
204 ¦ ¦ row <- add row, 1
205 ¦ ¦ column <- copy left
206 ¦ ¦ screen <- move-cursor screen, row, column
207 ¦ ¦ curr <- next curr
208 ¦ ¦ prev <- next prev
209 ¦ ¦ loop +next-character
210 ¦ }
211 ¦ {
212 ¦ ¦
213 ¦ ¦
214 ¦ ¦ at-right?:bool <- equal column, right
215 ¦ ¦ break-unless at-right?
216 ¦ ¦
217 ¦ ¦ wrap-icon:char <- copy 8617/loop-back-to-left
218 ¦ ¦ print screen, wrap-icon, 245/grey
219 ¦ ¦ column <- copy left
220 ¦ ¦ row <- add row, 1
221 ¦ ¦ screen <- move-cursor screen, row, column
222 ¦ ¦
223 ¦ ¦ loop +next-character
224 ¦ }
225 ¦ print screen, c, color
226 ¦ curr <- next curr
227 ¦ prev <- next prev
228 ¦ column <- add column, 1
229 ¦ loop
230 }
231
232 *editor <- put *editor, bottom-of-screen:offset, curr
233 *editor <- put *editor, bottom:offset, row
234 return row, column
235 ]
236
237 scenario point-at-multiple-editors [
238 local-scope
239 trace-until 100/app
240 assume-screen 30/width, 5/height
241
242 assume-resources [
243 ¦ [lesson/recipes.mu] <- [
244 ¦ ¦ |abc|
245 ¦ ]
246 ]
247 env:&:environment <- new-programming-environment resources, screen, [def]
248
249 assume-console [
250 ¦ left-click 1, 1
251 ¦ left-click 1, 17
252 ]
253
254 run [
255 ¦ event-loop screen, console, env, resources
256 ¦ recipes:&:editor <- get *env, recipes:offset
257 ¦ 5:num/raw <- get *recipes, cursor-column:offset
258 ¦ sandbox:&:editor <- get *env, current-sandbox:offset
259 ¦ 7:num/raw <- get *sandbox, cursor-column:offset
260 ]
261 memory-should-contain [
262 ¦ 5 <- 1
263 ¦ 7 <- 17
264 ]
265 ]
266
267 scenario edit-multiple-editors [
268 local-scope
269 trace-until 100/app
270 assume-screen 30/width, 5/height
271
272 assume-resources [
273 ¦ [lesson/recipes.mu] <- [
274 ¦ ¦ |abc|
275 ¦ ]
276 ]
277 env:&:environment <- new-programming-environment resources, screen, [def]
278 render-all screen, env, render
279
280 assume-console [
281 ¦ left-click 1, 1
282 ¦ type [0]
283 ¦ left-click 1, 17
284 ¦ type [1]
285 ]
286 run [
287 ¦ event-loop screen, console, env, resources
288 ¦ recipes:&:editor <- get *env, recipes:offset
289 ¦ 5:num/raw <- get *recipes, cursor-column:offset
290 ¦ sandbox:&:editor <- get *env, current-sandbox:offset
291 ¦ 7:num/raw <- get *sandbox, cursor-column:offset
292 ]
293 screen-should-contain [
294 ¦ . run (F4) . # this line has a different background, but we don't test that yet
295 ¦ .a0bc ╎d1ef .
296 ¦ . ╎──────────────.
297 ¦ .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎ .
298 ¦ . ╎ .
299 ]
300 memory-should-contain [
301 ¦ 5 <- 2
302 ¦ 7 <- 18
303 ]
304
305 run [
306 ¦ cursor:char <- copy 9251/␣
307 ¦ print screen, cursor
308 ]
309 screen-should-contain [
310 ¦ . run (F4) .
311 ¦ .a0bc ╎d1␣f .
312 ¦ . ╎──────────────.
313 ¦ .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎ .
314 ¦ . ╎ .
315 ]
316 ]
317
318 scenario editor-in-focus-keeps-cursor [
319 local-scope
320 trace-until 100/app
321 assume-screen 30/width, 5/height
322 assume-resources [
323 ¦ [lesson/recipes.mu] <- [
324 ¦ ¦ |abc|
325 ¦ ]
326 ]
327 env:&:environment <- new-programming-environment resources, screen, [def]
328 render-all screen, env, render
329
330 assume-console []
331 run [
332 ¦ event-loop screen, console, env, resources
333 ¦ cursor:char <- copy 9251/␣
334 ¦ print screen, cursor
335 ]
336
337 screen-should-contain [
338 ¦ . run (F4) .
339 ¦ .␣bc ╎def .
340 ¦ . ╎──────────────.
341 ¦ .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎ .
342 ¦ . ╎ .
343 ]
344
345 assume-console [
346 ¦ type [z]
347 ]
348 run [
349 ¦ event-loop screen, console, env, resources
350 ¦ cursor:char <- copy 9251/␣
351 ¦ print screen, cursor
352 ]
353
354 screen-should-contain [
355 ¦ . run (F4) .
356 ¦ .z␣bc ╎def .
357 ¦ . ╎──────────────.
358 ¦ .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎ .
359 ¦ . ╎ .
360 ]
361 ]
362
363 scenario backspace-in-sandbox-editor-joins-lines [
364 local-scope
365 trace-until 100/app
366 assume-screen 30/width, 5/height
367 assume-resources [
368 ]
369
370 test-sandbox-editor-contents:text <- new [abc
371 def]
372 env:&:environment <- new-programming-environment resources, screen, test-sandbox-editor-contents
373 render-all screen, env, render
374 screen-should-contain [
375 ¦ . run (F4) .
376 ¦ . ╎abc .
377 ¦ .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎def .
378 ¦ . ╎──────────────.
379 ¦ . ╎ .
380 ]
381
382 assume-console [
383 ¦ left-click 2, 16
384 ¦ press backspace
385 ]
386 run [
387 ¦ event-loop screen, console, env, resources
388 ¦ cursor:char <- copy 9251/␣
389 ¦ print screen, cursor
390 ]
391
392 screen-should-contain [
393 ¦ . run (F4) .
394 ¦ . ╎abc␣ef .
395 ¦ .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎──────────────.
396 ¦ . ╎ .
397 ]
398 ]
399
400 def render-all screen:&:screen, env:&:environment, {render-editor: (recipe (address screen) (address editor) -> number number (address screen) (address editor))} -> screen:&:screen, env:&:environment [
401 local-scope
402 load-ingredients
403 trace 10, [app], [render all]
404 old-top-idx:num <- save-top-idx screen
405
406 trace 11, [app], [render top menu]
407 width:num <- screen-width screen
408 draw-horizontal screen, 0, 0/left, width, 32/space, 0/black, 238/grey
409 button-start:num <- subtract width, 20
410 button-on-screen?:bool <- greater-or-equal button-start, 0
411 assert button-on-screen?, [screen too narrow for menu]
412 screen <- move-cursor screen, 0/row, button-start
413 print screen, [ run (F4) ], 255/white, 161/reddish
414
415 trace 11, [app], [render divider]
416 divider:num, _ <- divide-with-remainder width, 2
417 height:num <- screen-height screen
418 draw-vertical screen, divider, 1/top, height, 9482/vertical-dotted
419
420 screen <- render-recipes screen, env, render-editor
421 screen <- render-sandbox-side screen, env, render-editor
422 <render-components-end>
423
424 recipes:&:editor <- get *env, recipes:offset
425 current-sandbox:&:editor <- get *env, current-sandbox:offset
426 sandbox-in-focus?:bool <- get *env, sandbox-in-focus?:offset
427 screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env
428
429 assert-no-scroll screen, old-top-idx
430 ]
431
432 def render-recipes screen:&:screen, env:&:environment, {render-editor: (recipe (address screen) (address editor) -> number number (address screen) (address editor))} -> screen:&:screen, env:&:environment [
433 local-scope
434 load-ingredients
435 trace 11, [app], [render recipes]
436 recipes:&:editor <- get *env, recipes:offset
437
438 left:num <- get *recipes, left:offset
439 right:num <- get *recipes, right:offset
440 row:num, column:num, screen <- call render-editor, screen, recipes
441 clear-line-until screen, right
442 row <- add row, 1
443 <render-recipe-components-end>
444
445 draw-horizontal screen, row, left, right, 9480/horizontal-dotted
446 row <- add row, 1
447 clear-screen-from screen, row, left, left, right
448 ]
449
450
451 def render-sandbox-side screen:&:screen, env:&:environment, {render-editor: (recipe (address screen) (address editor) -> number number (address screen) (address editor))} -> screen:&:screen, env:&:environment [
452 local-scope
453 load-ingredients
454 current-sandbox:&:editor <- get *env, current-sandbox:offset
455 left:num <- get *current-sandbox, left:offset
456 right:num <- get *current-sandbox, right:offset
457 row:num, column:num, screen, current-sandbox <- call render-editor, screen, current-sandbox
458 clear-line-until screen, right
459 row <- add row, 1
460
461 draw-horizontal screen, row, left, right
462 row <- add row, 1
463 clear-screen-from screen, row, left, left, right
464 ]
465
466 def update-cursor screen:&:screen, recipes:&:editor, current-sandbox:&:editor, sandbox-in-focus?:bool, env:&:environment -> screen:&:screen [
467 local-scope
468 load-ingredients
469 <update-cursor-special-cases>
470 {
471 ¦ break-if sandbox-in-focus?
472 ¦ cursor-row:num <- get *recipes, cursor-row:offset
473 ¦ cursor-column:num <- get *recipes, cursor-column:offset
474 }
475 {
476 ¦ break-unless sandbox-in-focus?
477 ¦ cursor-row:num <- get *current-sandbox, cursor-row:offset
478 ¦ cursor-column:num <- get *current-sandbox, cursor-column:offset
479 }
480 screen <- move-cursor screen, cursor-row, cursor-column
481 ]
482
483
484
485
486 after <global-type> [
487 {
488 ¦ switch-side?:bool <- equal c, 14/ctrl-n
489 ¦ break-unless switch-side?
490 ¦ sandbox-in-focus?:bool <- get *env, sandbox-in-focus?:offset
491 ¦ sandbox-in-focus? <- not sandbox-in-focus?
492 ¦ *env <- put *env, sandbox-in-focus?:offset, sandbox-in-focus?
493 ¦ screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env
494 ¦ loop +next-event
495 }
496 ]
497
498
499
500 def draw-vertical screen:&:screen, col:num, y:num, bottom:num -> screen:&:screen [
501 local-scope
502 load-ingredients
503 style:char, style-found?:bool <- next-ingredient
504 {
505 ¦ break-if style-found?
506 ¦ style <- copy 9474/vertical
507 }
508 color:num, color-found?:bool <- next-ingredient
509 {
510 ¦
511 ¦ break-if color-found?
512 ¦ color <- copy 245/grey
513 }
514 {
515 ¦ continue?:bool <- lesser-than y, bottom
516 ¦ break-unless continue?
517 ¦ screen <- move-cursor screen, y, col
518 ¦ print screen, style, color
519 ¦ y <- add y, 1
520 ¦ loop
521 }
522 ]
523
524 scenario backspace-over-text [
525 local-scope
526 trace-until 100/app
527 assume-screen 50/width, 15/height
528
529 assume-resources [
530 ]
531
532 env:&:environment <- new-programming-environment resources, screen, []
533
534 assume-console [
535 ¦ type [a]
536 ¦ press backspace
537 ]
538 run [
539 ¦ event-loop screen, console, env, resources
540 ¦ 10:num/raw <- get *screen, cursor-row:offset
541 ¦ 11:num/raw <- get *screen, cursor-column:offset
542 ]
543 memory-should-contain [
544 ¦ 10 <- 1
545 ¦ 11 <- 0
546 ]
547 ]