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