https://github.com/akkartik/mu/blob/master/edit/001-editor.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 e:&:editor <- new-editor text, 0/left, 5/right
11 render null/screen, e
12 wait-for-event null/console
13 close-console
14 ]
15
16 scenario editor-renders-text-to-screen [
17 local-scope
18 assume-screen 10/width, 5/height
19 e:&:editor <- new-editor [abc], 0/left, 10/right
20 run [
21 render screen, e
22 ]
23 screen-should-contain [
24
25 . .
26 .abc .
27 . .
28 ]
29 ]
30
31 container editor [
32
33 data:&:duplex-list:char
34 top-of-screen:&:duplex-list:char
35 bottom-of-screen:&:duplex-list:char
36
37 before-cursor:&:duplex-list:char
38
39
40
41 left:num
42 right:num
43 bottom:num
44
45 cursor-row:num
46 cursor-column:num
47 ]
48
49
50
51 def new-editor s:text, left:num, right:num -> result:&:editor [
52 local-scope
53 load-inputs
54
55 right <- subtract right, 1
56 result <- new editor:type
57
58 *result <- put *result, left:offset, left
59 *result <- put *result, right:offset, right
60
61 *result <- put *result, cursor-row:offset, 1/top
62 *result <- put *result, cursor-column:offset, left
63
64 init:&:duplex-list:char <- push 167/§, null
65 *result <- put *result, data:offset, init
66 *result <- put *result, top-of-screen:offset, init
67 *result <- put *result, before-cursor:offset, init
68 result <- insert-text result, s
69 <editor-initialization>
70 ]
71
72 def insert-text editor:&:editor, text:text -> editor:&:editor [
73 local-scope
74 load-inputs
75 curr:&:duplex-list:char <- get *editor, data:offset
76 insert curr, text
77 ]
78
79 scenario editor-initializes-without-data [
80 local-scope
81 assume-screen 5/width, 3/height
82 run [
83 e:&:editor <- new-editor null/data, 2/left, 5/right
84 1:editor/raw <- copy *e
85 ]
86 memory-should-contain [
87
88
89
90 5 <- 0
91 6 <- 0
92
93 9 <- 2
94 10 <- 4
95 11 <- 0
96 12 <- 1
97 13 <- 2
98 ]
99 screen-should-contain [
100 . .
101 . .
102 . .
103 ]
104 ]
105
106
107
108
109 def render screen:&:screen, editor:&:editor -> last-row:num, last-column:num, screen:&:screen, editor:&:editor [
110 local-scope
111 load-inputs
112 return-unless editor, 1/top, 0/left
113 left:num <- get *editor, left:offset
114 screen-height:num <- screen-height screen
115 right:num <- get *editor, right:offset
116
117 curr:&:duplex-list:char <- get *editor, top-of-screen:offset
118 prev:&:duplex-list:char <- copy curr
119 curr <- next curr
120
121 color:num <- copy 7/white
122 row:num <- copy 1/top
123 column:num <- copy left
124 cursor-row:num <- get *editor, cursor-row:offset
125 cursor-column:num <- get *editor, cursor-column:offset
126 before-cursor:&:duplex-list:char <- get *editor, before-cursor:offset
127 screen <- move-cursor screen, row, column
128 {
129 +next-character
130 break-unless curr
131 off-screen?:bool <- greater-or-equal row, screen-height
132 break-if off-screen?
133
134
135
136 {
137 at-cursor-row?:bool <- equal row, cursor-row
138 break-unless at-cursor-row?
139 at-cursor?:bool <- equal column, cursor-column
140 break-unless at-cursor?
141 before-cursor <- copy prev
142 }
143 c:char <- get *curr, value:offset
144 <character-c-received>
145 {
146
147 newline?:bool <- equal c, 10/newline
148 break-unless newline?
149
150 {
151 at-cursor-row?:bool <- equal row, cursor-row
152 break-unless at-cursor-row?
153 left-of-cursor?:bool <- lesser-than column, cursor-column
154 break-unless left-of-cursor?
155 cursor-column <- copy column
156 before-cursor <- prev curr
157 }
158
159 clear-line-until screen, right
160
161 row <- add row, 1
162 column <- copy left
163 screen <- move-cursor screen, row, column
164 curr <- next curr
165 prev <- next prev
166 loop +next-character
167 }
168 {
169
170
171 at-right?:bool <- equal column, right
172 break-unless at-right?
173
174 wrap-icon:char <- copy 8617/loop-back-to-left
175 print screen, wrap-icon, 245/grey
176 column <- copy left
177 row <- add row, 1
178 screen <- move-cursor screen, row, column
179
180 loop +next-character
181 }
182 print screen, c, color
183 curr <- next curr
184 prev <- next prev
185 column <- add column, 1
186 loop
187 }
188
189 *editor <- put *editor, bottom-of-screen:offset, curr
190
191 {
192 at-cursor-row?:bool <- equal row, cursor-row
193 cursor-outside-line?:bool <- lesser-or-equal column, cursor-column
194 before-cursor-on-same-line?:bool <- and at-cursor-row?, cursor-outside-line?
195 above-cursor-row?:bool <- lesser-than row, cursor-row
196 before-cursor?:bool <- or before-cursor-on-same-line?, above-cursor-row?
197 break-unless before-cursor?
198 cursor-row <- copy row
199 cursor-column <- copy column
200 before-cursor <- copy prev
201 }
202 *editor <- put *editor, bottom:offset, row
203 *editor <- put *editor, cursor-row:offset, cursor-row
204 *editor <- put *editor, cursor-column:offset, cursor-column
205 *editor <- put *editor, before-cursor:offset, before-cursor
206 clear-line-until screen, right
207 row <- add row, 1
208 return row, left/column
209 ]
210
211 def clear-screen-from screen:&:screen, row:num, column:num, left:num, right:num -> screen:&:screen [
212 local-scope
213 load-inputs
214
215 {
216 break-if screen
217 clear-display-from row, column, left, right
218 return
219 }
220
221 screen <- move-cursor screen, row, column
222 clear-line-until screen, right
223 clear-rest-of-screen screen, row, left, right
224 ]
225
226 def clear-rest-of-screen screen:&:screen, row:num, left:num, right:num -> screen:&:screen [
227 local-scope
228 load-inputs
229 row <- add row, 1
230
231 {
232 break-if screen
233 clear-display-from row, left, left, right
234 return
235 }
236 screen <- move-cursor screen, row, left
237 screen-height:num <- screen-height screen
238 {
239 at-bottom-of-screen?:bool <- greater-or-equal row, screen-height
240 break-if at-bottom-of-screen?
241 screen <- move-cursor screen, row, left
242 clear-line-until screen, right
243 row <- add row, 1
244 loop
245 }
246 ]
247
248 scenario editor-prints-multiple-lines [
249 local-scope
250 assume-screen 5/width, 5/height
251 s:text <- new [abc
252 def]
253 e:&:editor <- new-editor s, 0/left, 5/right
254 run [
255 render screen, e
256 ]
257 screen-should-contain [
258 . .
259 .abc .
260 .def .
261 . .
262 ]
263 ]
264
265 scenario editor-handles-offsets [
266 local-scope
267 assume-screen 5/width, 5/height
268 e:&:editor <- new-editor [abc], 1/left, 5/right
269 run [
270 render screen, e
271 ]
272 screen-should-contain [
273 . .
274 . abc .
275 . .
276 ]
277 ]
278
279 scenario editor-prints-multiple-lines-at-offset [
280 local-scope
281 assume-screen 5/width, 5/height
282 s:text <- new [abc
283 def]
284 e:&:editor <- new-editor s, 1/left, 5/right
285 run [
286 render screen, e
287 ]
288 screen-should-contain [
289 . .
290 . abc .
291 . def .
292 . .
293 ]
294 ]
295
296 scenario editor-wraps-long-lines [
297 local-scope
298 assume-screen 5/width, 5/height
299 e:&:editor <- new-editor [abc def], 0/left, 5/right
300 run [
301 render screen, e
302 ]
303 screen-should-contain [
304 . .
305 .abc ↩.
306 .def .
307 . .
308 ]
309 screen-should-contain-in-color 245/grey [
310 . .
311 . ↩.
312 . .
313 . .
314 ]
315 ]
316
317 scenario editor-wraps-barely-long-lines [
318 local-scope
319 assume-screen 5/width, 5/height
320 e:&:editor <- new-editor [abcde], 0/left, 5/right
321 run [
322 render screen, e
323 ]
324
325
326 screen-should-contain [
327 . .
328 .abcd↩.
329 .e .
330 . .
331 ]
332 screen-should-contain-in-color 245/grey [
333 . .
334 . ↩.
335 . .
336 . .
337 ]
338 ]
339
340 scenario editor-with-empty-text [
341 local-scope
342 assume-screen 5/width, 5/height
343 e:&:editor <- new-editor [], 0/left, 5/right
344 run [
345 render screen, e
346 3:num/raw <- get *e, cursor-row:offset
347 4:num/raw <- get *e, cursor-column:offset
348 ]
349 screen-should-contain [
350 . .
351 . .
352 . .
353 ]
354 memory-should-contain [
355 3 <- 1
356 4 <- 0
357 ]
358 ]
359
360
361
362 scenario render-colors-comments [
363 local-scope
364 assume-screen 5/width, 5/height
365 s:text <- new [abc
366 # de
367 f]
368 e:&:editor <- new-editor s, 0/left, 5/right
369 run [
370 render screen, e
371 ]
372 screen-should-contain [
373 . .
374 .abc .
375 .# de .
376 .f .
377 . .
378 ]
379 screen-should-contain-in-color 12/lightblue, [
380 . .
381 . .
382 .# de .
383 . .
384 . .
385 ]
386 screen-should-contain-in-color 7/white, [
387 . .
388 .abc .
389 . .
390 .f .
391 . .
392 ]
393 ]
394
395 after <character-c-received> [
396 color <- get-color color, c
397 ]
398
399
400 def get-color color:num, c:char -> color:num [
401 local-scope
402 load-inputs
403 color-is-white?:bool <- equal color, 7/white
404
405 {
406 break-unless color-is-white?
407 starting-comment?:bool <- equal c, 35/#
408 break-unless starting-comment?
409 trace 90, [app], [switch color back to blue]
410 return 12/lightblue
411 }
412
413 {
414 color-is-blue?:bool <- equal color, 12/lightblue
415 break-unless color-is-blue?
416 ending-comment?:bool <- equal c, 10/newline
417 break-unless ending-comment?
418 trace 90, [app], [switch color back to white]
419 return 7/white
420 }
421
422 {
423 break-unless color-is-white?
424 starting-assignment?:bool <- equal c, 60/<
425 break-unless starting-assignment?
426 return 1/red
427 }
428
429 {
430 color-is-red?:bool <- equal color, 1/red
431 break-unless color-is-red?
432 ending-assignment?:bool <- equal c, 32/space
433 break-unless ending-assignment?
434 return 7/white
435 }
436
437 return color
438 ]
439
440 scenario render-colors-assignment [
441 local-scope
442 assume-screen 8/width, 5/height
443 s:text <- new [abc
444 d <- e
445 f]
446 e:&:editor <- new-editor s, 0/left, 8/right
447 run [
448 render screen, e
449 ]
450 screen-should-contain [
451 . .
452 .abc .
453 .d <- e .
454 .f .
455 . .
456 ]
457 screen-should-contain-in-color 1/red, [
458 . .
459 . .
460 . <- .
461 . .
462 . .
463 ]
464 ]