Phil Hagelberg: 1 Add embed-repl macro. 3 files changed, 56 insertions(+), 2 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~technomancy/fennel/patches/44820/mbox | git am -3Learn more about email & git
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