~technomancy/fennel

5 2

Fennel -> Hammerspoon repl

Ag Ibragimov
Details
Message ID
<m1v9yka216.fsf@gmail.com>
Sender timestamp
1557362741
DKIM signature
pass
Download raw message
Hi, I just rewrote my entire Hammerspoon configuration using Fennel and I can't be happier. Fennel is awesome. Now I want even more out of it. I'm thinking if it would be possible to have a Fennel REPL that compiles and evals by sending forms to Hammerspoon's IPC. I doubt that anyone has ever tried that before, any hints how I can build something like this would be appreciated. Thanks!
Details
Message ID
<87mujrufwl.fsf@hagelb.org>
In-Reply-To
<m1v9yka216.fsf@gmail.com> (view parent)
Sender timestamp
1557722362
DKIM signature
missing
Download raw message
Ag Ibragimov <agzam.ibragimov@gmail.com> writes:

> Hi, I just rewrote my entire Hammerspoon configuration using Fennel
> and I can't be happier. Fennel is awesome. Now I want even more out of
> it. I'm thinking if it would be possible to have a Fennel REPL that
> compiles and evals by sending forms to Hammerspoon's IPC. I doubt that
> anyone has ever tried that before, any hints how I can build something
> like this would be appreciated. Thanks!

Sure; you can start a repl that has the read/write functions overridden
to use another function other than io.read and io.write; for instance,
something that communicates over IPC. Here is an example of how the repl
on fennel-lang.org does it; in that case the repl runs in a coroutine
which gets resumed when the enter button is pressed in the input
element, but depending on how your IPC API looks it could be simpler.

  https://github.com/technomancy/fennel-lang.org/blob/master/repl.fnl#L73

-Phil
Ag Ibragimov
Details
Message ID
<m15ziq5p8u.fsf@gmail.com>
In-Reply-To
<87mujrufwl.fsf@hagelb.org> (view parent)
DKIM signature
pass
Download raw message
Hey, sorry guys, it's been long time. I've been swamped with work, but I think I'm ready to finally solve this puzzle. Can you please describe how this suppose to work.

From my understanding:
I use Fennel from Hammerspoon, meaning I bootstrap Fennel via Hammerspoon, i.e. in Hammerspoon's init.lua module:

#+begin_src lua
 fennel = require("fennel")
 table.insert(package.loaders or package.searchers, fennel.searcher)
#+end_src

not sure what the second line does, but from that point I can just use modules written in Fennel, and Hammerspoon will be happy to use them.

Now. It seems I can't quite do the other way around. I can't do ~fennel --repl~ and hope to be able to use Hammerspoon's modules to try things.

So essentially I have to call ~fennel.repl~ function from Hammerspoon context, but what does that do? Does it try to connect to existing fennel REPL instance? Does it create a new one? How do I connect to that "specialized" REPL instance (that is capable to send messages to Hammerspoon's IPC) from Emacs, from fennel-mode? How do I type things in the REPL buffer and expect it to be handled by Hammerspoon?

Hammerspoon has some API to talk to its own IPC https://www.hammerspoon.org/docs/hs.ipc.html, but I have no idea how to get all these to work together:

fennel.repl module, Emacs's `run-lisp` and Hammerspoon's IPC.

Sorry for being stupid here, but using a Lisp and not being able to use a REPL is a downer.

On Sun 12 May 2019 at 21:39, Phil Hagelberg <phil@hagelb.org> wrote:

> Ag Ibragimov <agzam.ibragimov@gmail.com> writes:
>
>> Hi, I just rewrote my entire Hammerspoon configuration using Fennel
>> and I can't be happier. Fennel is awesome. Now I want even more out of
>> it. I'm thinking if it would be possible to have a Fennel REPL that
>> compiles and evals by sending forms to Hammerspoon's IPC. I doubt that
>> anyone has ever tried that before, any hints how I can build something
>> like this would be appreciated. Thanks!
>
> Sure; you can start a repl that has the read/write functions overridden
> to use another function other than io.read and io.write; for instance,
> something that communicates over IPC. Here is an example of how the repl
> on fennel-lang.org does it; in that case the repl runs in a coroutine
> which gets resumed when the enter button is pressed in the input
> element, but depending on how your IPC API looks it could be simpler.
>
>   https://github.com/technomancy/fennel-lang.org/blob/master/repl.fnl#L73
>
> -Phil
Details
Message ID
<878snmuso1.fsf@hagelb.org>
In-Reply-To
<m15ziq5p8u.fsf@gmail.com> (view parent)
DKIM signature
missing
Download raw message
Ag Ibragimov <agzam.ibragimov@gmail.com> writes:

> Now. It seems I can't quite do the other way around. I can't do
> ~fennel --repl~ and hope to be able to use Hammerspoon's modules to
> try things.

You should be able to do this if you figure out the right package.path
settings, but I don't know anything about Hammerspoon, so I can't say
much about this.

> So essentially I have to call ~fennel.repl~ function from Hammerspoon
> context, but what does that do? Does it try to connect to existing
> fennel REPL instance? Does it create a new one? How do I connect to
> that "specialized" REPL instance (that is capable to send messages to
> Hammerspoon's IPC) from Emacs, from fennel-mode? How do I type things
> in the REPL buffer and expect it to be handled by Hammerspoon?

The default values for fennel.repl start a new repl, but it will only
work if you can use io.write and io.read. One solution would be to
replace io.write/io.read with functions that send output to the right
place and get input from the right place, but that's kind of hacky. A
better solution is to provide fennel.repl with options to teach it how
to do input and output for the context you have.

For example, here's how it's done in fennel-lang.org, which uses
fennel.repl but hooks into the web page's DOM elements instead of using
standard in and standard out.

    (fennel.repl {:readChunk (fn []
                               (let [input (coroutine.yield)]
                                 (set last-input input)
                                 (printLuacode (fennel.compileString input))
                                 (print (.. "> " input))
                                 (.. input "\n")))
                  :onValues (fn [xs]
                              (print (table.concat xs "\t"))
                              (set last-value (. xs 1))
                              (coroutine.resume tutorial))
                  :onError (fn [_ msg] (printError msg))})

If you run this inside a coroutine, it will yield waiting for input;
whenever you get repl input, you can resume the coroutine with it and it
will be used as repl input. This Lua code is used as a key event handler
for the HTML <input> element being used for input:

function input.onkeydown(_, e)
    local key = e.key or e.which
    if key == "Enter" and not e.shiftKey then
        historyIndex = nil
        if #input.value > 0 then
           if history[#history] ~= input.value then
              table.insert(history, input.value)
              if #history > historyLimit then
                 table.remove(history, 1)
              end
           end
           coroutine.resume(repl, input.value)
           input.value = ""
        end
    end
    ...
end

Here's another example which starts a repl that is hooked up to a
Channel inside loved2 so that reading from stdin doesn't block the whole
game loop:

  https://gitlab.com/alexjgriffith/min-love2d-fennel/blob/master/lib/stdio.fnl

Maybe you can hook up something like that to Hammerspoon's IPC or
something. Another option is to use Jeejah as an nrepl server, but
that's a bit more complicated: https://gitlab.com/technomancy/jeejah

Hope that helps.

-Phil
Ag Ibragimov
Details
Message ID
<m136du54ud.fsf@gmail.com>
In-Reply-To
<878snmuso1.fsf@hagelb.org> (view parent)
DKIM signature
pass
Download raw message
Phil, this is really helpful, thank you. Unfortunately I am still a bit confused. Let's say I figured out the correct parameters for ~fennel.repl~ function, I run it. How do I connect to it using inferior-lisp, i.e. ~M-x run-lisp~?

On Sun 08 Dec 2019 at 18:41, Phil Hagelberg <phil@hagelb.org> wrote:

> Ag Ibragimov <agzam.ibragimov@gmail.com> writes:
>
>> Now. It seems I can't quite do the other way around. I can't do
>> ~fennel --repl~ and hope to be able to use Hammerspoon's modules to
>> try things.
>
> You should be able to do this if you figure out the right package.path
> settings, but I don't know anything about Hammerspoon, so I can't say
> much about this.
>
>> So essentially I have to call ~fennel.repl~ function from Hammerspoon
>> context, but what does that do? Does it try to connect to existing
>> fennel REPL instance? Does it create a new one? How do I connect to
>> that "specialized" REPL instance (that is capable to send messages to
>> Hammerspoon's IPC) from Emacs, from fennel-mode? How do I type things
>> in the REPL buffer and expect it to be handled by Hammerspoon?
>
> The default values for fennel.repl start a new repl, but it will only
> work if you can use io.write and io.read. One solution would be to
> replace io.write/io.read with functions that send output to the right
> place and get input from the right place, but that's kind of hacky. A
> better solution is to provide fennel.repl with options to teach it how
> to do input and output for the context you have.
>
> For example, here's how it's done in fennel-lang.org, which uses
> fennel.repl but hooks into the web page's DOM elements instead of using
> standard in and standard out.
>
>     (fennel.repl {:readChunk (fn []
>                                (let [input (coroutine.yield)]
>                                  (set last-input input)
>                                  (printLuacode (fennel.compileString input))
>                                  (print (.. "> " input))
>                                  (.. input "\n")))
>                   :onValues (fn [xs]
>                               (print (table.concat xs "\t"))
>                               (set last-value (. xs 1))
>                               (coroutine.resume tutorial))
>                   :onError (fn [_ msg] (printError msg))})
>
> If you run this inside a coroutine, it will yield waiting for input;
> whenever you get repl input, you can resume the coroutine with it and it
> will be used as repl input. This Lua code is used as a key event handler
> for the HTML <input> element being used for input:
>
> function input.onkeydown(_, e)
>     local key = e.key or e.which
>     if key == "Enter" and not e.shiftKey then
>         historyIndex = nil
>         if #input.value > 0 then
>            if history[#history] ~= input.value then
>               table.insert(history, input.value)
>               if #history > historyLimit then
>                  table.remove(history, 1)
>               end
>            end
>            coroutine.resume(repl, input.value)
>            input.value = ""
>         end
>     end
>     ...
> end
>
> Here's another example which starts a repl that is hooked up to a
> Channel inside loved2 so that reading from stdin doesn't block the whole
> game loop:
>
>   https://gitlab.com/alexjgriffith/min-love2d-fennel/blob/master/lib/stdio.fnl
>
> Maybe you can hook up something like that to Hammerspoon's IPC or
> something. Another option is to use Jeejah as an nrepl server, but
> that's a bit more complicated: https://gitlab.com/technomancy/jeejah
>
> Hope that helps.
>
> -Phil
Details
Message ID
<877e35v6m2.fsf@hagelb.org>
In-Reply-To
<m136du54ud.fsf@gmail.com> (view parent)
DKIM signature
missing
Download raw message
Ag Ibragimov <agzam.ibragimov@gmail.com> writes:

> Phil, this is really helpful, thank you. Unfortunately I am still a
> bit confused. Let's say I figured out the correct parameters for
> ~fennel.repl~ function, I run it. How do I connect to it using
> inferior-lisp, i.e. ~M-x run-lisp~?

This depends on how your IPC works; you would have to run whatever
program could use IPC to provide input and get output over the mechanism
that you used in your fennel.repl call.

If you don't have anything existing that can do that using hammerspoon
IPC, then maybe that's not the best way to do it. If you embed a Jeejah
repl server you can connect with an nrepl client like Monroe:

    https://github.com/sanel/monroe/

-Phil