~technomancy/fennel

fennel-mode: add var to customize location of `fennel` v1 PROPOSED

: 1
 add var to customize location of `fennel`

 2 files changed, 86 insertions(+), 41 deletions(-)
Hi Andrey,

Yes that’s exactly the issue I was running into. Can you explain how fennel-path can be configured when starting the REPL? If there’s a better way to solve this I’m happy to test it out, I just couldn’t find any mention of it in the docs.

--
Christian Packard
Andrey Listopadov <andreyorst@gmail.com> writes:
Next
Explaining how to upgrade the version of Fennel shouldn't go in the readme.
Next
I think this should just be called `fennel-proto-repl-module-name`.

I'm not familiar enough with the proto-repl to comment on the rest of it
but the underlying motivation is sound.

-Phil
Next
I can see the benefits of a standard solution. Armed with the knowledge 
above I’d be fine using the configured path and discarding this patch.

What originally prompted me to submit it is that there wasn’t an indication 
that the misconfigured path was the issue. Even when setting 
`fennel-proto-repl-log-communication` to `t` and 
`fennel-proto-repl-kill-process-buffers` to `nil` the only message I had was 
`fennel-proto-repl--start-server: Unable to initialize Fennel Proto REPL: timeout`;
no other debug info or buffers were available.

Is there a sanity-test function or other documentation I can add so future
users know to check their path config if they get the same error?

--
Christian
Next
Christian Packard <christian@cpacklabs.com> writes:
Next
Andrey Listopadov <andreyorst@gmail.com> writes:
Thank you for the update! I pulled the latest changes and indeed saw
the error message when a module can’t be loaded as well as being able
to set `fennel-proto-repl-fennel-module-name` to the custom “lib.fennel”
location and start a working Proto REPL.

One small note to share: in the docstring of
`fennel-proto-repl-fennel-module-name` it says that there’s no need
to include quotes in module name, but I found that I did need quotes.
For example:

┌────
│ ((fennel-mode . ((fennel-proto-repl-fennel-module-name . lib\.fennel))))
└────

Without quotes I get the generic timeout error
“Unable to initialize Fennel Proto REPL: timeout”, presumably because
it results in a compile error instead of a runtime error:

┌────
│ ❯ fennel
│ Welcome to Fennel 1.3.1 on PUC Lua 5.4!
│ Use ,help to see available commands.
│ >> (require lib.fennel)
│ Compile error: unknown:1:9 Compile error: unknown identifier: lib
│ ...
│ >> (require "lib.fennel")
│ runtime error: module 'lib.fennel' not found:
│ ...
└────

I found if I coerced the variable with
`(prin1-to-string fennel-proto-repl-fennel-module-name t)` the REPL
was able to start correctly, but it may be easier to simply enforce that
variable being a string? Either way thanks again!

–
Christian
Ah that makes sense, no worries and thanks for clarifying.


Your docs are very well written, this case was just general ambiguities of English and myself reading them very literally :)

I think if there was another clause in the last sentence like “Note, this variable must be a string, but there’s no need to include quotes in the variable itself.”, that would make the distinction clear.

--
Christian
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~technomancy/fennel/patches/43836/mbox | git am -3
Learn more about email & git

[PATCH fennel-mode] add var to customize location of `fennel` Export this patch

From: Christian Packard <christian@cpacklabs.com>

Add a var `fennel-proto-repl-lib-module` which allows users to customize
the location of the `fennel` library module.
This allows projects such as min-love2d-fennel to work with the Proto
REPL, where the `fennel` command is in a non-top-level location like the `lib` folder.
---
 README.md            | 29 +++++++++++++
 fennel-proto-repl.el | 98 ++++++++++++++++++++++++++------------------
 2 files changed, 86 insertions(+), 41 deletions(-)

diff --git a/README.md b/README.md
index a49932a..6e13e2c 100644
--- a/README.md
+++ b/README.md
@@ -106,6 +106,32 @@ Note that `fennel-proto-repl` requires a Fennel version recent enough
to include the `___repl___` variable.  If the Fennel doesn't have the
`___repl___` variable the Proto REPL won't be started.

### Integration with min-love2d-fennel

To integrate the Proto REPL with a [min-love2d-fennel][9] project you can run the following steps:

* Ensure Fennel version in `lib/fennel` is at least 1.3.1. You can verify this by running `./lib/fennel --version` from your project root directory.
  * If you have a lower version, you can download a supported version at the  [Fennel downloads page][10] like [fennel-1.3.1][11] and extract the `fennel` and `fennel.lua` files to the `lib` folder of your project. Then, make the new fennel version executable via `chmod +x lib/fennel`.
* Create a `.dir-locals.el` file at the root of your project and set `"lib.fennel"` as the value of the `fennel-proto-repl-lib-module` variable.
  * For example: `((fennel-mode . ((fennel-proto-repl-lib-module . "lib.fennel"))))`.

Now when you start a REPL from one of your source files (i.e. *not* from the `lib` folder) you should have a fully functioning Proto REPL with all LÖVE modules available and connected to your running game:

```lisp
;; Welcome to Fennel Proto REPL 0.3.0
;; Fennel version: 1.3.1
;; Lua version: LuaJIT 2.1.0-beta3 macOS/arm64
>> love.event
{:clear #<function: 0x0107abef18>
 :poll #<function: 0x0107abf258>
 :poll_i #<function: 0x0107abee40>
 :pump #<function: 0x0107abedd0>
 :push #<function: 0x0107abee08>
 :quit #<function: 0x0107abeeb0>
 :wait #<function: 0x0107abee78>}
>>
```

## Org Babel support

Installation is similar to the other modules:
@@ -171,3 +197,6 @@ Licensed under the same license as Emacs (GPL v3 or later); see LICENSE
[6]: https://git.sr.ht/~technomancy/antifennel
[7]: https://gitlab.com/andreyorst/fennel-proto-repl-protocol
[8]: https://github.com/cask/cask
[9]: https://gitlab.com/alexjgriffith/min-love2d-fennel
[10]: https://fennel-lang.org/downloads/
[11]: https://fennel-lang.org/downloads/fennel-1.3.1.tar.gz
diff --git a/fennel-proto-repl.el b/fennel-proto-repl.el
index 9de8b15..fbeee38 100644
--- a/fennel-proto-repl.el
+++ b/fennel-proto-repl.el
@@ -103,10 +103,22 @@
(require 'fennel-mode)
(declare-function markdown-mode "ext:markdown-mode")

(defvar fennel-proto-repl--protocol
  "(fn protocol [format-function]
;;;###autoload
(defcustom fennel-proto-repl-lib-module "fennel"
  "Location of the fennel library module."
  :group 'fennel-proto-repl-mode
  :type 'string
  :package-version '(fennel-mode "0.8.1"))

(defun fennel-proto-repl--protocol ()
  "Upgrade code for the basic Fennel REPL.

This code is sent to the REPL as a part of the initialization process.
Once evaluated, the REPL starts accepting messages accordingly to the
defined protocol."
  (format "(fn protocol [format-function]
     (let [{: view : eval : traceback : parser
            : version &as fennel} (require :fennel)
            : version &as fennel} (require :%s)
           {:concat t/concat} table
           InternalError {}
           protocol-env (collect [k v (pairs _G)]
@@ -123,7 +135,7 @@
                          (fn [self k v]
                            (if (. protocol* k)
                                (protocol.internal-error
                                 (: \"modification of the protocol.%s field is forbidden\" :format k))
                                 (: \"modification of the protocol.%%s field is forbidden\" :format k))
                                (rawset self k v)))}
                         (setmetatable protocol))]
       (doto protocol*.env
@@ -153,7 +165,7 @@
         ;; Create a named FIFO pipe.  Continiously tries to create a
         ;; temporary name via `protocol.tmpname` and create a FIFO pipe with it.
         (fn open-fifo [name]
           (: (io.popen (: \"mkfifo '%s' 2>/dev/null\" :format name)) :close))
           (: (io.popen (: \"mkfifo '%%s' 2>/dev/null\" :format name)) :close))
         (var (i name) (values 0 (tmpname)))
         (while (and name (not (open-fifo name)))
           (when (> i 10)
@@ -171,7 +183,7 @@
                                  [:pipe {:string fifo}]])
                      data (with-open [f (io.open fifo :r)]
                             (f:read mode))]
                  (: (io.popen (: \"rm -f '%s'\" :format fifo)) :close)
                  (: (io.popen (: \"rm -f '%%s'\" :format fifo)) :close)
                  data)
           nil (protocol.internal-error \"unable to create FIFO pipe.\")))

@@ -293,7 +305,7 @@
           (fn remove-locus [msg]
             ;; Removes error information from the message.
             (if (= :string (type msg))
                 (pick-values 1 (msg:gsub \"^[^:]*:%d+:%s+\" \"\"))
                 (pick-values 1 (msg:gsub \"^[^:]*:%%d+:%%s+\" \"\"))
                 (view msg)))

           (fn downgrade []
@@ -371,11 +383,7 @@
              [:status {:string \"fail\"}]
              [:data {:string (.. \"unsupported Fennel version: \" version)}]]
             (setmetatable {:__fennelview #(protocol.format $)})))))"
  "Upgrade code for the basic Fennel REPL.

This code is sent to the REPL as a part of the initialization process.
Once evaluated, the REPL starts accepting messages accordingly to the
defined protocol.")
          fennel-proto-repl-lib-module))

(defvar fennel-proto-repl--format-plist
  "(fn [env data]
@@ -404,39 +412,47 @@ defined protocol.")
(defvar-local fennel-proto-repl--process-buffer nil)
(defvar-local fennel-proto-repl--message-callbacks nil)

(defvar fennel-proto-repl--arglist-query-template
  "(let [fennel (require :fennel)
(defun fennel-proto-repl--arglist-query-template ()
  "Argslist query template."
  (format "(let [fennel (require :%s)
         scope (fennel.scope)]
     (-> (. scope.specials %S)
         (or (. scope.macros %S))
         (or (. _G.___replLocals___ %S))
         (or (. _G %S))
     (-> (. scope.specials %%S)
         (or (. scope.macros %%S))
         (or (. _G.___replLocals___ %%S))
         (or (. _G %%S))
         (fennel.metadata:get :fnl/arglist)
         (or nil)))")
         (or nil)))"
          fennel-proto-repl-lib-module))

(defvar fennel-proto-repl--multisym-arglist-query-template
  "(let [fennel (require :fennel)
(defun fennel-proto-repl--multisym-arglist-query-template ()
  "Argslist query template for multisym."
  (format "(let [fennel (require :%s)
         scope (fennel.scope)]
     (-> %s
     (-> %%s
         (fennel.metadata:get :fnl/arglist)
         (or nil)))")
         (or nil)))"
          fennel-proto-repl-lib-module))

(defvar fennel-proto-repl--doc-query-template
  "(let [fennel (require :fennel)
(defun fennel-proto-repl--doc-query-template ()
  "Docstring query template."
  (format "(let [fennel (require :%s)
         scope (fennel.scope)]
     (-> (. scope.specials %S)
         (or (. scope.macros %S))
         (or (. _G.___replLocals___ %S))
         (or (. _G %S))
     (-> (. scope.specials %%S)
         (or (. scope.macros %%S))
         (or (. _G.___replLocals___ %%S))
         (or (. _G %%S))
         (fennel.metadata:get :fnl/docstring)
         (or nil)))")
         (or nil)))"
          fennel-proto-repl-lib-module))

(defvar fennel-proto-repl--multisym-doc-query-template
  "(let [fennel (require :fennel)
(defun fennel-proto-repl--multisym-doc-query-template ()
  "Docstring query template for multisym."
  (format "(let [fennel (require :%s)
         scope (fennel.scope)]
     (-> %s
     (-> %%s
         (fennel.metadata:get :fnl/docstring)
         (or nil)))")
         (or nil)))"
          fennel-proto-repl-lib-module))

(defun fennel-proto-repl--method-to-sym (symbol)
  "Convert SYMBOL from a method call to an ordinary table lookup call.
@@ -947,7 +963,7 @@ REPL-BUFFER is provided, REPL is started in that buffer.  Returns
the REPL buffer."
  (let* ((upgrade-code (fennel-proto-repl--minify-body
                        (format "(%s %s)"
                                fennel-proto-repl--protocol
                                (fennel-proto-repl--protocol)
                                fennel-proto-repl--format-plist)
                        'delete-indentation))
         (command (if (fboundp 'split-string-shell-command)
@@ -1554,8 +1570,8 @@ buffer, or when given a prefix arg."
  (fennel-proto-repl-send-message
   :eval (fennel-proto-repl--generate-query-command
          symbol
          fennel-proto-repl--arglist-query-template
          fennel-proto-repl--multisym-arglist-query-template)
          (fennel-proto-repl--arglist-query-template)
          (fennel-proto-repl--multisym-arglist-query-template))
   (lambda (message)
     (when-let ((arglist
                 (condition-case nil
@@ -1731,8 +1747,8 @@ REPL process."
    (when-let ((fn (car fn-info)))
      (let* ((sym (substring-no-properties fn))
             (command (fennel-proto-repl--generate-query-command
                       sym fennel-proto-repl--arglist-query-template
                       fennel-proto-repl--multisym-arglist-query-template)))
                       sym (fennel-proto-repl--arglist-query-template)
                       (fennel-proto-repl--multisym-arglist-query-template))))
        (if (not (fennel-proto-repl--callbacks-pending))
            (condition-case nil
                (fennel-proto-repl--eldoc-fn-handler
@@ -1770,8 +1786,8 @@ by the Fennel REPL process."
                  (nth 3 ppss)
                  (nth 4 ppss))
        (let ((command (fennel-proto-repl--generate-query-command
                        sym fennel-proto-repl--doc-query-template
                        fennel-proto-repl--multisym-doc-query-template)))
                        sym (fennel-proto-repl--doc-query-template)
                        (fennel-proto-repl--multisym-doc-query-template))))
          (if (not (fennel-proto-repl--callbacks-pending))
              (condition-case nil
                  (fennel-proto-repl--eldoc-var-handler
-- 
2.39.2 (Apple Git-143)
christian@cpacklabs.com writes: