Skip to content

Instantly share code, notes, and snippets.

@technomancy
Last active April 13, 2024 17:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save technomancy/9bb2be6bd1a5d8a242be4124306a63c7 to your computer and use it in GitHub Desktop.
Save technomancy/9bb2be6bd1a5d8a242be4124306a63c7 to your computer and use it in GitHub Desktop.
Fennel GUI REPL using love2d
;; since one of the strengths of Fennel is access to Lua frameworks, here's
;; a graphical REPL which runs inside LÖVE (https://love2d.org)
;; supports multiline input, showing errors in red, detecting incomplete input.
;; for a slightly more complex version which supports running inside a sandbox
;; see the wiki: https://github.com/bakpakin/Fennel/wiki/Repl
;; to run: fennel -c love-repl.fnl > main.lua && love .
(local fennel (require :fennel))
(local input []) ; store characters as they are typed
(local buffer []) ; output that has been printed
(var incomplete? false)
;; put things into the output buffer
(fn out [xs] (icollect [_ x (ipairs xs) :into buffer] x))
(fn _G.print [...] (out [...]) nil) ; override global print to use our buffer
;; display errors in red (love2d-specific convention for colored text)
(fn err [_errtype msg]
(each [line (msg:gmatch "([^\n]+)")]
(table.insert buffer [[0.9 0.4 0.5] line])))
;; create the repl inside a coroutine
(local repl (coroutine.create fennel.repl))
;; start it using the options table
(coroutine.resume repl {:readChunk coroutine.yield
:onValues out
:onError err})
(fn enter []
(let [input-text (table.concat (doto input (table.insert "\n")))
;; send the input to the repl
(_ {: stack-size}) (coroutine.resume repl input-text)]
(set incomplete? (< 0 stack-size)))
;; clear the input table afterwards
(while (next input) (table.remove input)))
(fn love.keypressed [key]
(match key
:return (enter)
:backspace (table.remove input)
:escape (love.event.quit)))
(fn love.textinput [text]
(table.insert input text))
(fn love.draw []
(let [(w h) (love.window.getMode)
fh (: (love.graphics.getFont) :getHeight)]
;; draw every line in the buffer (wasteful but easy)
(for [i (length buffer) 1 -1]
(match (. buffer i)
line (love.graphics.print line 2 (* i (+ fh 2)))))
;; draw the input text at the bottom
(love.graphics.line 0 (- h fh 4) w (- h fh 4))
(if incomplete? ; change the prompt character
(love.graphics.print "- " 2 (- h fh 2))
(love.graphics.print "> " 2 (- h fh 2)))
(love.graphics.print (table.concat input) 15 (- h fh 2))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment