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 recipes, screen, t
84 ¦ ¦ sandbox-in-focus?:bool <- move-cursor current-sandbox, screen, 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 type render-recipe = (recipe (address screen) (address editor) -> number number (address screen) (address editor))
401
402 def render-all screen:&:screen, env:&:environment, render-editor:render-recipe -> screen:&:screen, env:&:environment [
403 local-scope
404 load-ingredients
405 trace 10, [app], [render all]
406 old-top-idx:num <- save-top-idx screen
407
408 trace 11, [app], [render top menu]
409 width:num <- screen-width screen
410 draw-horizontal screen, 0, 0/left, width, 32/space, 0/black, 238/grey
411 button-start:num <- subtract width, 20
412 button-on-screen?:bool <- greater-or-equal button-start, 0
413 assert button-on-screen?, [screen too narrow for menu]
414 screen <- move-cursor screen, 0/row, button-start
415 print screen, [ run (F4) ], 255/white, 161/reddish
416
417 trace 11, [app], [render divider]
418 divider:num, _ <- divide-with-remainder width, 2
419 height:num <- screen-height screen
420 draw-vertical screen, divider, 1/top, height, 9482/vertical-dotted
421
422 screen <- render-recipes screen, env, render-editor
423 screen <- render-sandbox-side screen, env, render-editor
424 <render-components-end>
425
426 recipes:&:editor <- get *env, recipes:offset
427 current-sandbox:&:editor <- get *env, current-sandbox:offset
428 sandbox-in-focus?:bool <- get *env, sandbox-in-focus?:offset
429 screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env
430
431 assert-no-scroll screen, old-top-idx
432 ]
433
434 def render-recipes screen:&:screen, env:&:environment, render-editor:render-recipe -> screen:&:screen, env:&:environment [
435 local-scope
436 load-ingredients
437 trace 11, [app], [render recipes]
438 recipes:&:editor <- get *env, recipes:offset
439
440 left:num <- get *recipes, left:offset
441 right:num <- get *recipes, right:offset
442 row:num, column:num, screen <- call render-editor, screen, recipes
443 clear-line-until screen, right
444 row <- add row, 1
445 <render-recipe-components-end>
446
447 draw-horizontal screen, row, left, right, 9480/horizontal-dotted
448 row <- add row, 1
449 clear-screen-from screen, row, left, left, right
450 ]
451
452
453 def render-sandbox-side screen:&:screen, env:&:environment, render-editor:render-recipe -> screen:&:screen, env:&:environment [
454 local-scope
455 load-ingredients
456 trace 11, [app], [render sandboxes]
457 current-sandbox:&:editor <- get *env, current-sandbox:offset
458 left:num <- get *current-sandbox, left:offset
459 right:num <- get *current-sandbox, right:offset
460 row:num, column:num, screen, current-sandbox <- call render-editor, screen, current-sandbox
461 clear-line-until screen, right
462 row <- add row, 1
463
464 draw-horizontal screen, row, left, right
465 row <- add row, 1
466 clear-screen-from screen, row, left, left, right
467 ]
468
469 def update-cursor screen:&:screen, recipes:&:editor, current-sandbox:&:editor, sandbox-in-focus?:bool, env:&:environment -> screen:&:screen [
470 local-scope
471 load-ingredients
472 <update-cursor-special-cases>
473 {
474 ¦ break-if sandbox-in-focus?
475 ¦ cursor-row:num <- get *recipes, cursor-row:offset
476 ¦ cursor-column:num <- get *recipes, cursor-column:offset
477 }
478 {
479 ¦ break-unless sandbox-in-focus?
480 ¦ cursor-row:num <- get *current-sandbox, cursor-row:offset
481 ¦ cursor-column:num <- get *current-sandbox, cursor-column:offset
482 }
483 screen <- move-cursor screen, cursor-row, cursor-column
484 ]
485
486
487
488
489 after <global-type> [
490 {
491 ¦ switch-side?:bool <- equal c, 14/ctrl-n
492 ¦ break-unless switch-side?
493 ¦ sandbox-in-focus?:bool <- get *env, sandbox-in-focus?:offset
494 ¦ sandbox-in-focus? <- not sandbox-in-focus?
495 ¦ *env <- put *env, sandbox-in-focus?:offset, sandbox-in-focus?
496 ¦ screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env
497 ¦ loop +next-event
498 }
499 ]
500
501
502
503 def draw-vertical screen:&:screen, col:num, y:num, bottom:num -> screen:&:screen [
504 local-scope
505 load-ingredients
506 style:char, style-found?:bool <- next-ingredient
507 {
508 ¦ break-if style-found?
509 ¦ style <- copy 9474/vertical
510 }
511 color:num, color-found?:bool <- next-ingredient
512 {
513 ¦
514 ¦ break-if color-found?
515 ¦ color <- copy 245/grey
516 }
517 {
518 ¦ continue?:bool <- lesser-than y, bottom
519 ¦ break-unless continue?
520 ¦ screen <- move-cursor screen, y, col
521 ¦ print screen, style, color
522 ¦ y <- add y, 1
523 ¦ loop
524 }
525 ]
526
527 scenario backspace-over-text [
528 local-scope
529 trace-until 100/app
530 assume-screen 50/width, 15/height
531
532 assume-resources [
533 ]
534
535 env:&:environment <- new-programming-environment resources, screen, []
536
537 assume-console [
538 ¦ type [a]
539 ¦ press backspace
540 ]
541 run [
542 ¦ event-loop screen, console, env, resources
543 ¦ 10:num/raw <- get *screen, cursor-row:offset
544 ¦ 11:num/raw <- get *screen, cursor-column:offset
545 ]
546 memory-should-contain [
547 ¦ 10 <- 1
548 ¦ 11 <- 0
549 ]
550 ]