-
Notifications
You must be signed in to change notification settings - Fork 9
/
main.reb
450 lines (358 loc) · 14.3 KB
/
main.reb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
Rebol [
File: %main.reb
Type: module
Name: Main
; Things labeled with EXPORT in this module are IMPORT-ed to the user
; context due to inclusion in the %index.html via the <script> tag.
; They will not be added to LIB, but will be available in the console.
; That makes it nice for things like ABOUT and REDBOL, and the tricky
; WATCH loader command.
;
; If a module or script wishes to do something like add watches to
; variables, it should include <watchlist> itself, as opposed to trying
; to call things that were imported into the user context.
;
; It also exports MAIN, which isn't something the user should see. A
; way should be figured out to remove its visibility.
Description: {
The ReplPad console is a "widget" that tries to have few dependencies,
but can be integrated into bulkier contexts. One of those is as a
panel in Golden Layouts:
https://golden-layout.com/
Hence this %main.reb file is an attempt at separating functionality
for containers from the %replpad.reb.
The introduction text is a good example of something not all usages
would want, so that's an example of something that belongs here.
}
]
replpad-dir: what-dir ; %load-r3.js sets directory to URL bar path by default
; We don't just IMPORT the ReplPad definitions for things like NOW and WAIT
; into this module. Instead we use IMPORT* to put the definitions into lib.
; This makes them available to any script that's loaded. Review.
;
sys.util.import* lib %replpad.reb
replpad-git: https://github.com/hostilefork/replpad-js/blob/master/replpad.reb
console-git: https://github.com/metaeducation/ren-c/blob/master/extensions/console/ext-console-init.reb
chat: https://chat.stackoverflow.com/rooms/291/rebol
forum: https://forum.rebol.info
wasm-threads: https://developers.google.com/web/updates/2018/10/wasm-threads
instructions: https://github.com/hostilefork/replpad-js/wiki/Enable-WASM-Threads
link: [href label] -> [
unspaced [{<a href="} href {" target="_blank">} label {</a>}]
]
intro-note-html: spaced [
{<div class='note'>}
{<p>}
{<b><i>Guess what...</i></b> this REPL is actually written in Rebol!}
{Check out the} (link replpad-git {bridge to JavaScript})
{as well as the} unspaced [(link console-git {Console Module}) "."]
{While the techniques are still in early development, they show a}
{lot of promise for JavaScript/Rebol interoperability.}
{Discuss it on the} unspaced [(link forum {Discourse forum}) "."]
{</p>}
{<p><i>(Note: SHIFT-ENTER for multi-line code, Ctrl-Z to undo)</i></p>}
{</div>}
]
greeting-text:
{Welcome to Rebol. For more information please type in the commands below:
HELP - For starting information
ABOUT - Information about your Rebol
REDBOL - Experimental emulation of Rebol2/Red conventions}
; We don't want a deep stack when reporting errors or running user code. So
; a reb.Promise("main") is run. (If we called CONSOLE from inside main, then
; it would look like MAIN>CONSOLE in the stack...or worse, if the call was
; inside an IF, etc.)
;
; !!! Has to be an ADAPT of CONSOLE, for some reason--investigate:
; https://github.com/hostilefork/replpad-js/issues/10
;
export main: adapt :console [
!! "MAIN executing (this should show in browser console log)"
clear-screen ; clears the progress messages displayed during load
; Note: There is a URLSearchParams() object we could use to parse the
; search location as well (may not be in all browsers?)
;
let autorun: null
let importing: false
parse system.options.args [try some [
;
; local, remote, tracing_on, git_commit not passed through by the
; %load-r3.js for easier processing.
;
['do:] autorun: text! (importing: false)
|
['import:] autorun: text! (importing: true)
]] except [
print ["** Bad `window.location.search` string in page URL"]
print mold system.options.args
print newline
print trim/auto mutable {
OPTIONS ARE:
?do=scriptname
DEBUG OPTIONS ARE:
?local
?remote
?tracing_on
?git_commit=<shorthash>
They may be combined together, e.g.:
?local&do=scriptname
}
return 1
]
if autorun [ ; `?do=foo` suppresses banner and runs `do <foo>`
;
; !!! @gchiu wants to suppress the loading information for dependent
; modules, as well as not show any output from the import itself.
; While it seems like a reasonable default when running scripts in
; "release mode", a more holistic story for this is needed.
;
; https://forum.rebol.info/t/1801
;
sys.util.script-pre-load-hook: ~
if importing [
;
; !!! There's a lot of nuance involved in "adding commands to the
; console", because it straddles the line between being a script
; and a module. We have to do some hacking here to push the
; module exports from inside this %main.reb module out to where
; the console can see them. Think through this more!
;
; https://forum.rebol.info/t/1802
result: import as the-word! autorun
sys.util.import* system.contexts.user result
]
else [
result: do as the-word! autorun ; may be BAD-WORD!
]
; !!! Right now, all modules return void. This is a limitation of
; having DO be based on IMPORT:
;
; https://github.com/rebol/rebol-issues/issues/2373
;
; So if *any* modules require falling through to the console, we have
; to make all of them do it. This should be revisited, but for now
; any script that isn't supposed to drop to the console should never
; terminate.
;
comment [if result = ... [return 0]]
]
else [
replpad-write/html intro-note-html
]
; Fall through to normal CONSOLE loop handling, but use a skin that
; gives a custom message (other customizations could be done here,
; prompt/etc., and it's hoped the tutorial itself would be done with
; such hooks)
skin: make console! compose [ ; /SKIN is a refinement to CONSOLE
(spread either autorun [
[print-greeting: does []]
][
[greeting: greeting-text]
])
print-halted: meth [return: [~]] [
print "[interrupted by Escape key or HALT instruction]"
]
]
change-dir %/ ; switch to local filesystem as "current directory"
]
=== ABOUT COMMAND ===
; !!! The ABOUT command was not made part of the console extension, since
; non-console builds might want to be able to ask it from the command line.
; But it was put in HOST-START and not the mezzanine/help in general. This
; needs to be rethought, but including ABOUT doing *something* since it is
; mentioned when the console starts up.
export about: does [
print [
{This Rebol is running completely in your browser! The evaluations}
{aren't being sent to a remote server--the interpreter is client side!}
newline newline
{Please don't hesitate to submit any improvements, no matter how}
{small...and come join the discussion on the forum and chat!}
]
]
=== WATCHLIST STUB (INVOKES MODULE ON FIRST RUN) ===
; We don't want to pay for loading the watchlist unless it's used. Do a
; delayed-load that waits for the first use.
;
; Note: When it was being automatically loaded, it was observed that it
; could not be loaded before REPLPAD-WRITE/HTML. Investigate.
export watch: func [:arg] [
print "Loading watchlist extension for first use..."
import join replpad-dir %watchlist/main.reb
let watch: :system.modules.Watchlist.watch
system.contexts.user.watch: :watch
; !!! Watch hard quotes its argument...need some kind of variadic
; re-triggering mechanism (e.g. this WATCH shouldn't have any arguments,
; but be able to inline WATCH to gather args)
;
return do compose [watch (:arg)]
]
=== COMMAND FOR INVOKING REDBOL (Rebol2/Red Emulation) ===
export redbol: func [return: [~]] [
print delimit LF [
""
"Ren-C has many changes (e.g. replacing TYPE? with TYPE OF, where"
"OF is an infix version of REFLECT that quotes its left argument to"
"get the property to reflect!) But nearly all of these changes can be"
"'skinned' to provide alternative behaviors--including the old ones!"
""
"REDBOL is an experimental emulation of Rebol2/Red conventions. It"
"uses module isolation so emulated code can run side-by-side with"
"new code. Scripts that wish to use it should `import @redbol`."
"(But you just ran the REDBOL command which applies the change"
"irreversibly to the console context, just for trying it out.)"
""
"Note: Redbol PARSE is particularly slow right now because it's all"
"usermode. That will change as the parser-combinator-based 'UPARSE'"
"is hardened into be the design for native PARSE. Stay tuned."
""
"Discuss this experiment on the forum--and help if you can!"
]
print "Fetching %redbol.reb from GitHub..."
; !!! If we do just `import @redbol` here we will import it to this main
; (which will mess things up). We want it to go to the user context. This
; is a somewhat sloppy command...people should be doing their own imports.
; But it's just to demonstrate.
;
sys.util.import* system.contexts.user @redbol
system.console.prompt: "redbol>>"
]
=== GOLDEN LAYOUTS DEMO ===
export ensure-golden-layouts-loaded: func [
return: [~]
<static> loaded (false)
][
if loaded [return ~]
css-do join replpad-dir %libs/golden/css/goldenlayout-base.css
css-do join replpad-dir %libs/golden/css/themes/goldenlayout-replpad-theme.css
css-do {
h2 { /* this was in the golden layout simple demo */
font: 14px Arial, sans-serif;
color: #fff;
padding: 10px;
text-align: center;
}
}
; The interop is a module that makes `window.golden` available to this
; non-modularized code.
;
js-do/module join replpad-dir %golden-interop.js
loaded: true
]
=== CODEMIRROR 6 EDITOR DEMO ===
export edit: func [
return: [~]
source [url! text! file!]
/marks
<static> codemirror-loaded (false)
][
ensure-golden-layouts-loaded
if not codemirror-loaded [
;
; The interop is a module that makes `window.CodeMirror` available to
; this non-modularized code.
;
js-do/module join replpad-dir %codemirror-interop.js
css-do {
.cm-editor { /* https://discuss.codemirror.net/t/2882 */
height: 100% !important
}
.cm-scroller {
overflow-y: scroll !important; /* always show */
overflow-x: auto
}
}
codemirror-loaded: true
js-eval {
const { EditorState } = CodeMirror.state
const { EditorView } = CodeMirror.view
const {
lineNumbers,
highlightActiveLine, highlightActiveLineGutter,
highlightSpecialChars,
drawSelection, rectangularSelection,
dropCursor, crosshairCursor,
keymap
} = CodeMirror.view
const { Extension } = CodeMirror.state
golden.registerComponent('mirror', function (container, gl_state) {
// https://codemirror.net/6/docs/ref/#state
let cm_state = EditorState.create({
doc: gl_state.text,
extensions: [
lineNumbers(),
highlightActiveLine(),
highlightActiveLineGutter(),
highlightSpecialChars(),
drawSelection(),
rectangularSelection(),
dropCursor(),
crosshairCursor()
]
})
// https://codemirror.net/6/docs/ref/#view
let cm_view = new EditorView({
state: cm_state,
parent: container.getElement()
})
gl_state.cm_view = cm_view
window.cm = cm_view
// https://stackoverflow.com/a/40569014
// https://github.com/golden-layout/golden-layout/issues/173
//
let first_show = true
container.on('shown', function () {
if (!first_show)
cm_view.focus()
first_show = false
cm = cm_view // capture last editor in cm
})
})
}
]
let [text title]: pack switch/type source [
text! [
[source, "TEXT!"]
]
url! file! [
[as text! read source, split-path source]
]
]
js-eval [
{let text =} spell @text {;}
{let title =} spell @title {;}
{
let state = { text: text }
golden.addComponent('mirror', state, title)
}
]
]
export ed-text: js-native [] { // repeated in %eparse.reb
return reb.Text(cm.state.doc.text.join('\n'))
}
ed-clear-underlines: js-awaiter [ ; repeated in %eparse.reb
{Clear all underlines from the last activated editor}
] {
CodeMirror.ClearUnderlines()
}
=== "EPARSE" INTEGRATION DEMO OF UPARSE AND CODEMIRROR ===
import ensure url! clean-path %eparse.reb
export [eparse eparse-debug]
=== OVERRIDE QUIT IN LIB ===
; Having QUIT exit the interpreter can be useful in some debug builds which
; check various balances of state.
; https://github.com/hostilefork/replpad-js/issues/17
;
; This is an overwrite of LIB's quit. It's not clear where the right place
; for this is (should it be in %replpad.reb?) or what hook to use. Put it
; here for now, as it seems different embeddings of the console might want
; to do different things when quitting.
lib.quit: adapt copy :lib.quit [
replpad-write/html spaced [
{<div class='note'>}
{<p><b><i>Sorry to see you go...</i></b></p>}
{<p><a href=".">click to restart interpreter</a></p>}
</div>
]
; Fall through to normal QUIT handling
]