: 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:
Explaining how to upgrade the version of Fennel shouldn't go in the readme.
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
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?
OK I think I've solved this in the latest commit to fennel-mode. The solution is similar to one in this patch, but instead I've updated the protocol itself to account for a different fennel location, so now it is a standard part of the initialization process. There are some other changes included as well, regarding the io.read handling. Additionally, I added a check if the module can't be loaded. The protocol will now signal an error properly, and thus the client will display an error message instead of just timeouting. Thanks for bringing this to my attention! If everything works for you, this patch can be discarded, or we can iterate further. -- Andrey Listopadov
-- Christian
Christian Packard <christian@cpacklabs.com> writes:
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)))) └────
What I meant is that you need to write "lib.fennel", not "\"lib.fennel\"". It mostly applies to the customize interface, as it doesn't show quotes for string values. So the correct code is just: ((fennel-mode . ((fennel-proto-repl-fennel-module-name . "lib.fennel")))) Sorry for the confusion.
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.
Maybe you can suggest a better wording? English isn't my first language, and I'm not all that familiar with customize docs
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
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 -3Learn more about email & git
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.
I don't think this is the correct way to handle it. First, the name of the variable is ambiguous, it doesn't say that it is the fennel module location - at first I thought this is for storing the protocol source code. Second, the protocol is meant to be compatible with the *running* REPL, which means that it already has Fennel loaded, and loading a different version of Fennel will probably only introduce weird errors. What exactly is the issue you're trying to solve here?Re-reading the patch, I think I now see the problem, that the Fennel can be unavailable by just requiring :fennel, and indeed you'll need to require :lib.fennel. However, I still don't think that this should be solved this way - an appropriate fennel-path should be configured when starting the REPL.No, there is precedent for storing fennel under a module name other than just "fennel"; for instance, the :moduleName field in options allows you to override the name of the module used to store metadata without changing the fennel.path setting. The min-love2d-fennel skeleton already uses this. You can work around this issue by just ensuring that it's available under both names: package.loaded.fennel = package.loaded["lib.fennel"]Sure enough I added this to my `main.lua` and proto-repl works again with the default `:fennel` name. Thank you for the example!But ideally it could be configured from elisp as well. However...-- Andrey Listopadov-- Andrey Listopadov
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: