(local core (require :core)) (local socket (require :socket)) ; (local socket {:connect #nil}) (local bencode (require :plugins.fennel-xl.bencode)) (local lume (require :plugins.fennel-xl.lume)) (fn contains? [tbl item] (or (= tbl item) (lume.find tbl item))) (local nrepl {:active-requests {} :session-handlers {} :default-handlers {:out #(core.log $2) :value #(core.log $2) :ex #(core.err $2) :status/interrupted #($1:done $3.id) :status/done #($1:done $3.id)} :merge-handlers (fn [self message] (lume.merge self.default-handlers (or (. self.session-handlers message.session) {}) (or (. self.active-requests message.id) {}))) :chain-handlers (fn [self keys ...] (local new-handlers {}) (each [_ key (ipairs keys)] (each [_ handlers (ipairs [self.default-handlers ...])] (local next-handler (. handlers key)) (local prev-handler (. new-handlers key)) (if (and next-handler prev-handler) (tset new-handlers key #(do (prev-handler $1 $2 $3) (next-handler $1 $2 $3))) next-handler (tset new-handlers key next-handler)))) new-handlers) :counter 1 :input "" :parse-input (fn [self] (match (pcall #(bencode.decode self.input)) (true val len) (do (set self.input (self.input:sub (+ len 1))) val) (false :Incomplete) nil (false _) (do (set self.input "") nil))) :receive (fn [self] (when self.connection (local (data err part) (self.connection:receive "*a")) (local response (or data part)) (when (> (response:len) 0) (set self.input (.. self.input response))) (match (self:parse-input) nil nil input (self:handle input)) (when (= err :closed) (self:disconnect)))) :send (fn [self msg ?handlers ?session] (when self.connection (when (not msg.id) (set msg.id self.counter) (set self.counter (+ self.counter 1))) (when (not msg.session) (set msg.session (or ?session self.default-session))) (when ?handlers (tset self.active-requests msg.id ?handlers)) (self.connection:send (bencode.encode msg)))) :done (fn [self msg-id] (tset self.active-requests msg-id nil)) :handle (fn [self response] (local handlers (self:merge-handlers response)) (each [prop handler (pairs handlers)] (local idiv (prop:find :/)) (local key (if idiv (prop:sub 1 (- idiv 1)) prop)) (when (and (. response key) (or (= idiv nil) (contains? (. response key) (prop:sub (+ idiv 1))))) (handler self (. response key) response)))) :disconnect (fn [self] (when self.connection (self.connection:close) (set self.connection nil) (set self.default-session nil) (set self.active-requests {}) (set self.session-handlers {}))) :connect (fn [self ?opt] (when (not self.connection) (local opt (or ?opt {})) (local (port host) (values (or opt.port 7888) (or opt.host :localhost))) (set self.connection (assert (socket.connect host port))) (self.connection:settimeout 0) (core.add_thread #(while true (self:receive) (coroutine.yield 0.1)) self) (self:send {:op :clone} {:new-session #(do (set self.default-session $2) (when opt.on-connect (opt.on-connect self)))}))) :connected? (fn [self] (not= self.default-session nil)) :new-session (fn [self callback ?handler] (self:send {:op :clone} {:new-session (fn [self session message] (tset self.session-handlers session ?handler) (callback self session message))}))}) nrepl