This is primarily meant for debugging; it allows you to use a repl to
see what's going on in the middle of a function and experiment with
whatever values are in scope interactively.
See the description in the documentation for details.
---
reference.md | 25 +++++++++++++++++++++++++
src/fennel/macros.fnl | 19 ++++++++++++++++++-
test/macro.fnl | 14 +++++++++++++-
3 files changed, 56 insertions(+), 2 deletions(-)
diff --git a/reference.md b/reference.md
index 61faef8..0a5985a 100644
--- a/reference.md
+++ b/reference.md
@@ -1476,6 +1476,31 @@ module paths similarly to `import-macros`. See the [relative
require](tutorial#relative-require) section in the tutorial for more
information.
+### `embed-repl`
+
+Sometimes it's helpful for debugging purposes to drop a repl right
+into the middle of your code to see what's really going on. You can
+use the `embed-repl` macro to do this:
+
+```fnl
+(let [input (get-input)
+ value []]
+ (fn helper [x]
+ (table.insert value (calculate x)))
+ (embed-repl)
+ value)
+```
+
+This will drop you into a repl when you hit that point in the
+code. The repl will have access to all the locals that are in
+scope. (This would be `input`, `value`, and `helper` in the example
+above.) It takes an optional options table which accepts all the same
+values as the `fennel.repl` function in the API.
+
+Note that this is meant for use in development and will not work with
+ahead-of-time compilation unless your build also includes Fennel as a
+library.
+
## Macros
All forms which introduce macros do so inside the current scope. This
diff --git a/src/fennel/macros.fnl b/src/fennel/macros.fnl
index 9b645d1..a4c2e07 100644
--- a/src/fennel/macros.fnl
+++ b/src/fennel/macros.fnl
@@ -1,3 +1,5 @@
+;; fennel-ls: macro-file
+
;; These macros are awkward because their definition cannot rely on the any
;; built-in macros, only special forms. (no when, no icollect, etc)
@@ -393,6 +395,20 @@ Example:
(tset scope.macros import-key (. macros* macro-name))))))
nil)
+(fn embed-repl* [?opts]
+ "Embed a repl with access to locals at the point of the call.
+Takes an optional table of arguments which will be passed to fennel.repl."
+ (let [locals []]
+ (fn add-locals [{: symmeta : parent}]
+ (each [name (pairs symmeta)]
+ (tset locals name (sym name)))
+ (if parent (add-locals parent)))
+ (add-locals (get-scope))
+ `(let [opts# (or ,?opts {})
+ fennel# (require (or opts#.module-name :fennel))]
+ (set opts#.env (collect [k# v# (pairs _G) &into ,locals] k# v#))
+ (fennel#.repl opts#))))
+
{:-> ->*
:->> ->>*
:-?> -?>*
@@ -413,4 +429,5 @@ Example:
:pick-values pick-values*
:macro macro*
:macrodebug macrodebug*
- :import-macros import-macros*}
+ :import-macros import-macros*
+ :embed-repl embed-repl*}
diff --git a/test/macro.fnl b/test/macro.fnl
index af44ab5..6991d9b 100644
--- a/test/macro.fnl
+++ b/test/macro.fnl
@@ -4,7 +4,7 @@
(macro == [form expected ?msg ?opts]
`(let [(ok# val#) (pcall fennel.eval ,(view form) ,?opts)]
(t.is ok# val#)
- (t.= val# ,expected ,?msg)))
+ (t.= ,expected val# ,?msg)))
(fn test-arrows []
(== (-> (+ 85 21) (+ 1) (- 99)) 8)
@@ -732,6 +732,17 @@
(splice {:greetings "comrade"}))
{:hello "world" :greetings "comrade"}))
+(fn test-embed-repl []
+ (== (let [inputs ["hello\n" "[:literal]\n" "(inc 991)\n" ",exit\n"]
+ outputs []
+ opts {:readChunk #(table.remove inputs 1)
+ :onValues #(table.insert outputs (. $ 1))}
+ hello :world]
+ (fn inc [x] (+ x 1))
+ (embed-repl opts)
+ outputs)
+ ["\"world\"" "[\"literal\"]" "992"]))
+
{: test-arrows
: test-doto
: test-?.
@@ -748,6 +759,7 @@
: test-case
: test-lua-module
: test-disabled-sandbox-searcher
+ : test-embed-repl
: test-expand
: test-match-try
: test-case-try
--
2.39.2