;; fennel awesome repl
;; AlexJGriffith
;; MIT 2025

;;; Usage

;;; Require this module and call start in your rc.lua module.

;; local repl = require ("awesome-repl")
;; repl.start()

;;; You can then point your repl to port 5000 or the port
;;; you provided as an optional paramater to start.
;;; https://paste.alexjgriffith.com/fennel-repl--start-tcp.html

(local default-port 5000)

(local socket (require :socket))
(local fennel (require :lib.fennel))

(local logging false)

(fn log [message]
  (when logging
    (let [naughty (require :naughty)]
      (naughty.notify {:preset naughty.config.presets.info
                       :title "REPL"
                       :text (fennel.view message)}))))

(local state {:client nil :server nil :coro nil})

(set _G.__repl_debug state)

(fn read-chunk [{: stack-size}]
  (state.client:send (if (> stack-size 0) ".. " ">> "))
  (let [input (coroutine.yield)]
    (log (.. :read-chunk: (fennel.view input)))
    (.. (table.concat (case (type input) :table input _ [input]) "\t") "\n")))

(fn on-values [vals]
  (log (.. :on-values: (table.concat vals "\t")))
  (state.client:send (.. "" (table.concat vals "\t") "\n")))

(fn on-error [_errtype err]
  (log (.. :on-error: err))
  (state.client:send (tostring err)))

(local options {:readChunk read-chunk
                :onValues on-values
                :onError on-error
                :moduleName :fennel
                :env _G
                :message (string.format
                          "Welcome to Fennel %s on %s!\nThis repl is running in Awesome %s."
                          fennel.version _VERSION awesome.version)
                :pp fennel.view})

(fn once [fun ...]
  (when (not _G.once-table) (set _G.once-table {}))
  ;; change with module and line number
  (when (not (. _G.once-table fun))
    (fun ...)
    (tset _G.once-table fun true)))

(fn print-in-loop []
  (log "Entered loop and listening for server or client"))

(fn loop []
  (once print-in-loop)
  (local (s r reason) (socket.select [state.server state.client] [] 0))
  (when (> (# s) 0)
    (log {:send s :rec r :client state.client :reason reason}))
  (var ret true)
  (each [_ c (ipairs s)]
    (match c
      state.server
      (do
        (when state.client
          (state.client:shutdown))
        (log [:connecting])
        (log "Starting Fennel REPL")
        (set state.client (state.server:accept))
        (set state.coro (coroutine.create (partial fennel.repl)))
        (coroutine.resume state.coro options))
      state.client
      (let [str (state.client:receive :*line)]
        (log [:incoming str])
        (when str (coroutine.resume state.coro str)))))
  ;; needs to return true or or the timer will end
  true)

(fn start [port?]
  (let [port (or port? default-port)]
    (set state.server (socket.bind :localhost port))    
    (if (not state.server)
        (log (.. "Couldn't connect to port: " port))
        (log (.. "Waiting for Connection on port: " port "\n" (fennel.view state.server))))
    (when state.server
      (case (require :gears)
        gears (do
                (log  "Starting main loop.")
                (gears.timer.start_new (/ 1 60) loop))
        nil (log  "Couldn't load gears! Repl not starting.")))))

{: start}

;; Local Variables:
;; fennel-repl--buffer-name: "*Fennel Awesome*"
;; End:

Generated by alexjgriffith using scpaste at Fri Nov 14 01:41:19 2025. EST. (original)