subrepo:
  subdir:   "vendor/jeejah"
  merged:   "3ed9eb1"
upstream:
  origin:   "https://gitlab.com/technomancy/jeejah.git"
  branch:   "master"
  commit:   "3ed9eb1"
git-subrepo:
  version:  "0.4.2"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "65fde50"
This commit is contained in:
Jeremy Penner 2020-11-19 15:42:08 -05:00
parent 3d52b70bbc
commit c843deea3d
20 changed files with 3502 additions and 0 deletions

12
vendor/jeejah/.gitrepo vendored Normal file
View file

@ -0,0 +1,12 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = https://gitlab.com/technomancy/jeejah.git
branch = master
commit = 3ed9eb1f368c825e33e73dec0bcc9c553c33cf82
parent = 3d52b70bbcc475d8ef69accd990c367f1a72bbba
method = merge
cmdver = 0.4.2

31
vendor/jeejah/Changelog.md vendored Normal file
View file

@ -0,0 +1,31 @@
# Jeejah changelog: history of user-visible changes
## 0.3.1 / 2020-04-24
* Fix compatibility for Lua 5.1 and 5.2.
* Improve error reporting.
* Move Fennel support to special handler instead of middleware.
## 0.3.0 / 2019-08-01
* Fix a bug with socket timeout.
* Add foreground mode.
* Avoid burning CPU when there's nothing to do.
## 0.2.1 / 2019-05-21
* Add support for launching a Fennel server using middleware.
* Add support for middleware.
* Support Luas newer than 5.1.
## 0.2.0 / 2016-06-20
* Support requesting a read from stdin.
* Support stopping the server.
* Change module API to return a table, not a function.
* Support multiple sessions.
## 0.1.0 / 2016-06-09
* Initial release!

19
vendor/jeejah/LICENSE vendored Normal file
View file

@ -0,0 +1,19 @@
Copyright © 2016-2019 Phil Hagelberg and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
vendor/jeejah/Makefile vendored Normal file
View file

@ -0,0 +1 @@
check: ; luacheck --std max jeejah.lua bencode.lua

94
vendor/jeejah/Readme.md vendored Normal file
View file

@ -0,0 +1,94 @@
# JeeJah
An nREPL server for [Fennel](https://fennel-lang.org) and [Lua](https://lua.org).
## A what now?
The [nREPL protocol](https://nrepl.org/nrepl/index.html#_why_nrepl)
allows developers to embed a server in their programs to which
external programs can connect for development, debugging, etc.
The original implementation of the protocol was written in Clojure,
and many clients assume they will connect to a Clojure server; however
the protocol is quite agnostic about what language is being
evaluated. It supports evaluating snippets of code or whole files with
`print` and `io.write` redirected back to the connected client.
This library was originally written to add Emacs support to
[Bussard](https://gitlab.com/technomancy/bussard), a spaceflight
programming game.
Currently mainly tested with
[monroe](https://github.com/sanel/monroe/) and
[shevek](https://git.sr.ht/~technomancy/shevek/) as
clients. [grenchman](https://leiningen.org/grench.html) version 0.3.0+
works. Other clients exist for Vim, Eclipse, and Atom, as well as
several independent command-line clients; however these may require
some adaptation to work with Jeejah. If you try your favorite client
and find that it makes Clojure-specific assumptions, please report a
bug with it so that it can gracefully degrade when those assumptions
don't hold.
## Installation
The pure-Lua dependencies are included (`bencode`, `serpent`, and
`fennel`) but you will need to install `luasocket` yourself. If your
operating system does not provide it, you can install it using LuaRocks:
$ luarocks install --local luasocket
Note that [LÖVE](https://love2d.org) ships with its own copy of
luasocket, so there is no need to install it there.
You can symlink `bin/jeejah` to your `$PATH` or something.
## Usage
You can launch a standalone nREPL server:
$ bin/jeejah
Pass in a `--fennel` flag to start a server for evaluating Fennel code
instead of Lua. Accepts a `--port` argument and a `--debug` flag.
You can use it as a library too, of course:
```lua
local jeejah = require("jeejah")
local coro = jeejah.start(port, {debug=true, sandbox={x=12}})
```
The function returns a coroutine which you'll need to repeatedly
resume in order to handle requests. Each accepted connection is stored
in a coroutine internal to that function; these are each repeatedly
resumed by the main coroutine. If all you're doing is starting an
nrepl server, you can pass `foreground=true` in the options table to
leave the server running in the foreground and skip the step of
resuming the coroutine.
Note that the sandbox feature is not well-tested or audited and should
not be trusted to provide robust security. It currently only works
with Lua 5.1 and LuaJIT.
You can also pass in a `handlers` table where the keys are custom
[nREPL ops](https://nrepl.org/nrepl/ops.html)
you want to handle yourself.
## Completion
The included `monroe-lua-complete.el` file adds support for completion
to the Monroe client by querying the connected nREPL server for
possibilities. Simply invoke `completion-at-point` (bound to `C-M-i`
by default) when connected.
## Caveats
PUC Lua 5.1 does not allow yielding coroutines from inside protected
calls, which means you cannot use `io.read`, though LuaJIT and
Lua 5.2+ allow it.
## License
Copyright © 2016-2020 Phil Hagelberg and contributors
Distributed under the MIT license; see file LICENSE

78
vendor/jeejah/bencode.lua vendored Normal file
View file

@ -0,0 +1,78 @@
local encode, decode
local function decode_list(str, t, total_len)
-- print("list", str, lume.serialize(t))
if(str:sub(1,1) == "e") then return t, total_len + 1 end
local value, v_len = decode(str)
table.insert(t, value)
total_len = total_len + v_len
return decode_list(str:sub(v_len + 1), t, total_len)
end
local function decode_table(str, t, total_len)
-- print("table", str, lume.serialize(t))
if(str:sub(1,1) == "e") then return t, total_len + 1 end
local key, k_len = decode(str)
local value, v_len = decode(str:sub(k_len+1))
local end_pos = 1 + k_len + v_len
t[key] = value
total_len = total_len + k_len + v_len
return decode_table(str:sub(end_pos), t, total_len)
end
function decode(str)
-- print("decoding", str)
if(str:sub(1,1) == "l") then
return decode_list(str:sub(2), {}, 1)
elseif(str:sub(1,1) == "d") then
return decode_table(str:sub(2), {}, 1)
elseif(str:sub(1,1) == "i") then
return(tonumber(str:sub(2, str:find("e") - 1))), str:find("e")
elseif(str:match("[0-9]+")) then
local num_str = str:match("[0-9]+")
local beginning_of_string = #num_str + 2
local str_len = tonumber(num_str)
local total_len = beginning_of_string + str_len - 1
return str:sub(beginning_of_string, total_len), total_len
else
error("Could not parse "..str)
end
end
local function encode_str(s) return #s .. ":" .. s end
local function encode_int(n) return "i" .. tostring(n) .. "e" end
local function encode_table(t)
local s = "d"
for k,v in pairs(t) do s = s .. encode(k) .. encode(v) end
return s .. "e"
end
local function encode_list(l)
local s = "l"
for _,x in ipairs(l) do s = s .. encode(x) end
return s .. "e"
end
local function count(tbl)
local i = 0
for _ in pairs(tbl) do i = i + 1 end
return i
end
function encode(x)
local unpack = unpack or table.unpack
if(type(x) == "table" and select("#", unpack(x)) == count(x)) then
return encode_list(x)
elseif(type(x) == "table") then
return encode_table(x)
elseif(type(x) == "number" and math.floor(x) == x) then
return encode_int(x)
elseif(type(x) == "string") then
return encode_str(x)
else
error("Could not encode " .. type(x) .. ": " .. tostring(x))
end
end
return {decode=decode, encode=encode}

35
vendor/jeejah/bin/jeejah vendored Executable file
View file

@ -0,0 +1,35 @@
#!/usr/bin/env lua
require "luarocks.loader"
local opts, port = {foreground = true}
for n,v in ipairs(arg) do
if(v == "--port") then
port = arg[n+1]
elseif(v == "--fennel") then
opts.fennel = true
elseif(v == "--debug") then
opts.debug = true
elseif(v == "--empty-sandbox") then
opts.sandbox = {}
elseif(v == "--version" or v == "--help") then
print("jeejah 0.2.0\n")
print("Options:")
print(" --fennel Start a Fennel server instead of Lua")
print(" --port Port on which to listen")
print(" --debug Print verbose debugging information")
os.exit(0)
end
end
local root_dir = debug.getinfo(1).source:sub(2, -(1+#"bin/jeejah"))
local search_parent = string.format("%s?.lua;%s", root_dir, package.path)
if(package.searchpath) then
local jeejah = dofile(package.searchpath("jeejah", search_parent))
jeejah.start(port, opts)
else -- 5.1
if root_dir == "" then root_dir = "." end
local jeejah = dofile(root_dir .. "/jeejah.lua")
jeejah.start(port, opts)
end

2229
vendor/jeejah/fennel.lua vendored Normal file

File diff suppressed because it is too large Load diff

156
vendor/jeejah/fennelview.fnl vendored Normal file
View file

@ -0,0 +1,156 @@
;; A pretty-printer that outputs tables in Fennel syntax.
;; Loosely based on inspect.lua: http://github.com/kikito/inspect.lua
(local view-quote (fn [str] (.. '"' (: str :gsub '"' '\\"') '"')))
(local short-control-char-escapes
{"\a" "\\a" "\b" "\\b" "\f" "\\f" "\n" "\\n"
"\r" "\\r" "\t" "\\t" "\v" "\\v"})
(local long-control-char-esapes
(let [long {}]
(for [i 0 31]
(let [ch (string.char i)]
(when (not (. short-control-char-escapes ch))
(tset short-control-char-escapes ch (.. "\\" i))
(tset long ch (: "\\%03d" :format i)))))
long))
(fn escape [str]
(let [str (: str :gsub "\\" "\\\\")
str (: str :gsub "(%c)%f[0-9]" long-control-char-esapes)]
(: str :gsub "%c" short-control-char-escapes)))
(fn sequence-key? [k len]
(and (= (type k) "number")
(<= 1 k)
(<= k len)
(= (math.floor k) k)))
(local type-order {:number 1 :boolean 2 :string 3 :table 4
:function 5 :userdata 6 :thread 7})
(fn sort-keys [a b]
(let [ta (type a) tb (type b)]
(if (and (= ta tb) (~= ta "boolean")
(or (= ta "string") (= ta "number")))
(< a b)
(let [dta (. type-order a)
dtb (. type-order b)]
(if (and dta dtb)
(< dta dtb)
dta true
dtb false
:else (< ta tb))))))
(fn get-sequence-length [t]
(var len 1)
(each [i (ipairs t)] (set len i))
len)
(fn get-nonsequential-keys [t]
(let [keys {}
sequence-length (get-sequence-length t)]
(each [k (pairs t)]
(when (not (sequence-key? k sequence-length))
(table.insert keys k)))
(table.sort keys sort-keys)
(values keys sequence-length)))
(fn count-table-appearances [t appearances]
(if (= (type t) "table")
(when (not (. appearances t))
(tset appearances t 1)
(each [k v (pairs t)]
(count-table-appearances k appearances)
(count-table-appearances v appearances)))
(when (and t (= t t)) ; no nans please
(tset appearances t (+ (or (. appearances t) 0) 1))))
appearances)
(var put-value nil) ; mutual recursion going on; defined below
(fn puts [self ...]
(each [_ v (ipairs [...])]
(table.insert self.buffer v)))
(fn tabify [self] (puts self "\n" (: self.indent :rep self.level)))
(fn already-visited? [self v] (~= (. self.ids v) nil))
(fn get-id [self v]
(var id (. self.ids v))
(when (not id)
(let [tv (type v)]
(set id (+ (or (. self.max-ids tv) 0) 1))
(tset self.max-ids tv id)
(tset self.ids v id)))
(tostring id))
(fn put-sequential-table [self t length]
(puts self "[")
(set self.level (+ self.level 1))
(for [i 1 length]
(puts self " ")
(put-value self (. t i)))
(set self.level (- self.level 1))
(puts self " ]"))
(fn put-key [self k]
(if (and (= (type k) "string")
(: k :find "^[-%w?\\^_`!#$%&*+./@~:|<=>]+$"))
(puts self ":" k)
(put-value self k)))
(fn put-kv-table [self t]
(puts self "{")
(set self.level (+ self.level 1))
(each [k v (pairs t)]
(tabify self)
(put-key self k)
(puts self " ")
(put-value self v))
(set self.level (- self.level 1))
(tabify self)
(puts self "}"))
(fn put-table [self t]
(if (already-visited? self t)
(puts self "#<table " (get-id self t) ">")
(>= self.level self.depth)
(puts self "{...}")
:else
(let [(non-seq-keys length) (get-nonsequential-keys t)
id (get-id self t)]
(if (> (. self.appearances t) 1)
(puts self "#<" id ">")
(and (= (# non-seq-keys) 0) (= (# t) 0))
(puts self "{}")
(= (# non-seq-keys) 0)
(put-sequential-table self t length)
:else
(put-kv-table self t)))))
(set put-value (fn [self v]
(let [tv (type v)]
(if (= tv "string")
(puts self (view-quote (escape v)))
(or (= tv "number") (= tv "boolean") (= tv "nil"))
(puts self (tostring v))
(= tv "table")
(put-table self v)
:else
(puts self "#<" (tostring v) ">")))))
(fn fennelview [root options]
(let [options (or options {})
inspector {:appearances (count-table-appearances root {})
:depth (or options.depth 128)
:level 0 :buffer {} :ids {} :max-ids {}
:indent (or options.indent " ")}]
(put-value inspector root)
(table.concat inspector.buffer)))

357
vendor/jeejah/jeejah.lua vendored Normal file
View file

@ -0,0 +1,357 @@
local socket = require "socket"
local serpent = require "serpent"
local bencode = require "bencode"
local load = loadstring or load
local timeout = 0.001
local d = os.getenv("DEBUG") and print or function(_) end
local serpent_pp = function(p) return function(x)
local serpent_opts = {maxlevel=8,maxnum=64,nocode=true}
p(serpent.block(x, serpent_opts)) end
end
local sessions = {}
local response_for = function(old_msg, msg)
-- certain implementations break when the ns field is empty; see
-- https://gitlab.com/technomancy/jeejah/issues/5
msg.session, msg.id, msg.ns = old_msg.session, old_msg.id, ">"
return msg
end
local send = function(conn, msg)
d("Sending", bencode.encode(msg))
conn:send(bencode.encode(msg))
end
local write_for = function(conn, msg)
return function(...)
send(conn, response_for(msg, {out=table.concat({...}, "\t")}))
end
end
local print_for = function(write)
return function(...)
local args = {...}
for i,x in ipairs(args) do args[i] = tostring(x) end
table.insert(args, "\n")
write(table.concat(args, " "))
end
end
local read_for = function(conn, msg)
return function()
send(conn, response_for(msg, {status={"need-input"}}))
while(not sessions[msg.session].input) do
coroutine.yield()
d("yielded")
end
local input = sessions[msg.session].input
sessions[msg.session].input = nil
return input
end
end
local sandbox_for = function(write, provided_sandbox)
local sandbox = { io = { write = write },
print = print_for(write), }
for k,v in pairs(provided_sandbox) do
sandbox[k] = v
end
return sandbox
end
-- for stuff that's shared between eval and load_file
local execute_chunk = function(session, chunk, pp)
local old_write, old_print, old_read = io.write, print, io.read
if(session.sandbox) then
setfenv(chunk, session.sandbox)
pp = pp or serpent_pp(session.sandbox.print)
else
_G.print = print_for(session.write)
_G.io.write, _G.io.read = session.write, session.read
pp = pp or serpent_pp(_G.print)
end
local trace, err
local result = {xpcall(chunk, function(e)
trace = debug.traceback()
err = e end)}
_G.print, _G.io.write, _G.io.read = old_print, old_write, old_read
if(result[1]) then
local res, i = pp(result[2]), 3
while i <= #result do
res = res .. ', ' .. pp(result[i])
i = i + 1
end
return res
else
return nil, (err or "Unknown error") .. "\n" .. trace
end
end
local eval = function(session, code, pp)
local chunk, err = load("return " .. code, "*socket*")
if(err and not chunk) then -- statement, not expression
chunk, err = load(code, "*socket*")
if(not chunk) then
return nil, "Compilation error: " .. (err or "unknown")
end
end
return execute_chunk(session, chunk, pp)
end
local load_file = function(session, file, loader)
local chunk, err = (loader or loadfile)(file)
if(not chunk) then
return nil, "Compilation error in " .. file ": ".. (err or "unknown")
end
return execute_chunk(session, chunk)
end
local register_session = function(conn, msg, provided_sandbox)
local id = tostring(math.random(999999999))
local write = write_for(conn, msg)
local sandbox = provided_sandbox and sandbox_for(write, provided_sandbox)
sessions[id] = { conn = conn, write = write, print = print_for(write),
sandbox = sandbox, coros = {}, id = id}
return response_for(msg, {["new-session"]=id, status={"done"}})
end
local unregister_session = function(msg)
sessions[msg.session] = nil
return response_for(msg, {status={"done"}})
end
local describe = function(msg, handlers)
local ops = { "clone", "close", "describe", "eval", "load-file",
"ls-sessions", "complete", "stdin", "interrupt" }
for op in handlers do table.insert(ops, op) end
return response_for(msg, {ops=ops, status={"done"}})
end
local session_for = function(conn, msg, sandbox)
local s = sessions[msg.session] or register_session(conn, msg, sandbox)
s.write = write_for(conn, msg)
s.read = read_for(conn, msg)
return s
end
local complete = function(msg, sandbox)
local clone = function(t)
local n = {} for k,v in pairs(t) do n[k] = v end return n
end
local top_ctx = clone(sandbox or _G)
for k,v in pairs(msg.libs or {}) do
top_ctx[k] = require(v:sub(2,-2))
end
local function cpl_for(input_parts, ctx)
if type(ctx) ~= "table" then return {} end
if #input_parts == 0 and ctx ~= top_ctx then
return ctx
elseif #input_parts == 1 then
local matches = {}
for k in pairs(ctx) do
if k:find('^' .. input_parts[1]) then
table.insert(matches, k)
end
end
return matches
else
local token1 = table.remove(input_parts, 1)
return cpl_for(input_parts, ctx[token1])
end
end
local input_parts = {}
for i in string.gmatch(msg.input, "([^.%s]+)") do
table.insert(input_parts, i)
end
return response_for(msg, {completions = cpl_for(input_parts, top_ctx)})
end
-- see https://github.com/clojure/tools.nrepl/blob/master/doc/ops.md
local handle = function(conn, handlers, sandbox, msg)
if(handlers and handlers[msg.op]) then
d("Custom op:", msg.op)
handlers[msg.op](conn, msg, session_for(conn, msg, sandbox),
send, response_for)
elseif(msg.op == "clone") then
d("New session.")
send(conn, register_session(conn, msg, sandbox))
elseif(msg.op == "describe") then
d("Describe.")
send(conn, describe(msg, handlers))
elseif(msg.op == "eval") then
d("Evaluating", msg.code)
local value, err = eval(session_for(conn, msg, sandbox), msg.code, msg.pp)
d("Got", value, err)
-- monroe bug means you have to send done status separately
send(conn, response_for(msg, {value=value, ex=err}))
send(conn, response_for(msg, {status={"done"}}))
elseif(msg.op == "load-file") then
d("Loading file", msg.file)
local value, err = load_file(session_for(conn, msg, sandbox),
msg.file, msg.loader)
d("Got", value, err)
send(conn, response_for(msg, {value=value, ex=err, status={"done"}}))
elseif(msg.op == "ls-sessions") then
d("List sessions")
local session_ids = {}
for id in pairs(sessions) do table.insert(session_ids, id) end
send(conn, response_for(msg, {sessions=session_ids, status={"done"}}))
elseif(msg.op == "complete") then
d("Complete", msg.input)
local session_sandbox = session_for(conn, msg, sandbox).sandbox
send(conn, complete(msg, session_sandbox))
elseif(msg.op == "stdin") then
d("Stdin", serpent.block(msg))
sessions[msg.session].input = msg.stdin
send(conn, response_for(msg, {status={"done"}}))
return
elseif(msg.op ~= "interrupt") then -- silently ignore interrupt
send(conn, response_for(msg, {status={"unknown-op"}}))
print(" | Unknown op", serpent.block(msg))
end
end
local handler_coros = {}
local function receive(conn, partial)
local s, err = conn:receive(1) -- wow this is primitive
-- iterate backwards so we can safely remove
for i=#handler_coros, 1, -1 do
local ok, err2 = coroutine.resume(handler_coros[i])
if(coroutine.status(handler_coros[i]) ~= "suspended") then
if(not ok) then print(" | Handler error", err2) end
table.remove(handler_coros, i)
end
end
if(s) then
return receive(conn, (partial or "") .. s)
elseif(err == "timeout" and partial == nil) then
coroutine.yield()
return receive(conn)
elseif(err == "timeout") then
return partial
else
return nil, err
end
end
local function client_loop(conn, sandbox, handlers, middleware, partial)
local input, r_err = receive(conn, partial)
if(input) then
local decoded, d_err = bencode.decode(input)
if decoded and d_err < #input then
partial = input:sub(d_err + 1)
else
partial = nil
end
coroutine.yield()
if(decoded and decoded.op == "close") then
d("End session.")
return send(conn, unregister_session(decoded))
elseif(decoded and decoded.op ~= "close") then
-- If we don't spin up a coroutine here, we can't io.read, because
-- that requires waiting for a response from the client. But most
-- messages don't need to stick around.
local coro = coroutine.create(handle)
if(middleware) then
middleware(function(msg)
local ok, err = coroutine.resume(coro, conn, handlers,
sandbox, msg)
if(not ok) then print(" | Handler error", err) end
end, decoded)
else
local ok, err = coroutine.resume(coro, conn, handlers,
sandbox, decoded)
if(not ok) then print(" | Handler error", err) end
end
if(coroutine.status(coro) == "suspended") then
table.insert(handler_coros, coro)
end
else
print(" | Decoding error:", d_err)
end
return client_loop(conn, sandbox, handlers, middleware, partial)
else
return r_err
end
end
local connections = {}
local function loop(server, sandbox, handlers, middleware, foreground)
socket.sleep(timeout)
local conn, err = server:accept()
local stop = (not foreground) and (coroutine.yield() == "stop")
if(conn) then
conn:settimeout(timeout)
d("Connected.")
local coro = coroutine.create(function()
local _, h_err = pcall(client_loop, conn, sandbox, handlers, middleware)
if(h_err ~= "closed") then print("Connection closed: " .. h_err) end
end)
table.insert(connections, coro)
return loop(server, sandbox, handlers, middleware, foreground)
else
if(err ~= "timeout") then print(" | Socket error: " .. err) end
for _,c in ipairs(connections) do coroutine.resume(c) end
if(stop or err == "closed") then
server:close()
print("Server stopped.")
else
return loop(server, sandbox, handlers, middleware, foreground)
end
end
end
return {
-- Start an nrepl socket server on the given port. For opts you can pass a
-- table with foreground=true to run in the foreground, debug=true for
-- verbose logging, and sandbox={...} to evaluate all code in a sandbox. You
-- can also give an opts.handlers table keying ops to handler functions which
-- take the socket, the decoded message, and the optional sandbox table.
start = function(port, opts)
port = port or 7888
opts = opts or {}
opts.handlers = opts.handlers or {}
-- host should always be localhost on a PC, but not always on a micro
local server = assert(socket.bind(opts.host or "localhost", port))
if(opts.debug) then d = print end
if(opts.timeout) then timeout = tonumber(opts.timeout) end
if(opts.fennel) then
local fenneleval = require("jeejah.fenneleval")
opts.handlers.eval = fenneleval
opts.handlers.stdin = fenneleval
end
assert(not opts.sandbox or setfenv, "Can't use sandbox on 5.2+")
server:settimeout(timeout)
print("Server started on port " .. port .. "...")
if opts.foreground then
return loop(server, opts.sandbox, opts.handlers,
opts.middleware, opts.foreground)
else
return coroutine.create(function()
loop(server, opts.sandbox, opts.handlers, opts.middleware)
end)
end
end,
-- Pass in the coroutine from jeejah.start to this function to stop it.
stop = function(coro)
coroutine.resume(coro, "stop")
end,
broadcast = function(msg)
for _,session in pairs(sessions) do
send(session.conn, msg)
end
end,
}

77
vendor/jeejah/jeejah/fenneleval.lua vendored Normal file
View file

@ -0,0 +1,77 @@
local fennel = require("fennel")
local fennelview_ok, fennelview = pcall(require, "fennelview")
if not fennelview_ok then fennelview = fennel.dofile("fennelview.fnl") end
local d = os.getenv("DEBUG") and print or function(_) end
local repls = {}
local print_for = function(write)
return function(...)
local args = {...}
for i,x in ipairs(args) do args[i] = tostring(x) end
table.insert(args, "\n")
write(table.concat(args, " "))
end
end
local make_repl = function(session, repls)
local on_values = function(xs)
session.values(xs)
session.done({status={"done"}})
end
local read = function()
-- If we skip empty input, it confuses the client.
local input = coroutine.yield()
if(input:find("^%s*$")) then return "nil\n" else return input end
end
local err = function(errtype, msg)
session.write(table.concat({errtype, msg}, ": ")) session.done()
end
local env = session.sandbox
if not env then
env = {}
for k, v in pairs(_G) do env[k] = v end
env.io = {}
end
env.print = print_for(session.write)
env.io.write = session.write
env.io.read = function()
session.needinput()
local input, done = coroutine.yield()
done()
return input
end
local f = function()
return fennel.repl({readChunk = read,
onValues = on_values,
onError = err,
env = env,
pp = fennelview})
end
repls[session.id] = coroutine.wrap(f)
repls[session.id]()
return repls[session.id]
end
return function(conn, msg, session, send, response_for)
d("Evaluating", msg.code)
local repl = repls[session.id] or make_repl(session, repls)
if msg.op == "eval" then
session.values = function(xs)
send(conn, response_for(msg, {value=table.concat(xs, "\n") .. "\n"}))
end
session.done = function()
send(conn, response_for(msg, {status={"done"}}))
end
session.needinput = function()
send(conn, response_for(msg, {status={"need-input"}}))
end
repl(msg.code .. "\n")
elseif msg.op == "stdin" then
repl(msg.stdin,
function() send(conn, response_for(msg, {status={"done"}})) end)
end
end

89
vendor/jeejah/monroe-lua-complete.el vendored Normal file
View file

@ -0,0 +1,89 @@
;;; monroe-lua-complete.el --- Completion for Lua over Monroe connection
;; Copyright © 2016 Phil Hagelberg
;;
;; Author: Phil Hagelberg
;; URL: https://gitlab.com/technomancy/jeejah
;; Version: 0.1.0
;; Keywords: languages, nrepl, lua
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Provides live completion results for Lua by querying a Lua process
;; over an nREPL connection. Uses `completion-at-point' but can be
;; adapted for other completion methods.
;;; Installation:
;; Copy it to your load-path and (require 'monroe-lua-complete)
;;; Usage:
;; * Launch an nREPL server using jeejah.
;; * Connect to it with M-x monroe.
;; * Complete the expression at point with M-x completion-at-point
;; also bound to M-tab or C-M-i.
;;; Code:
(require 'lua-mode) ; requires a newer lua-mode that has lua-start-of-expr
(require 'monroe)
(defvar monroe-completion-candidates nil)
(defun monroe-completion-handler (response)
"Set `monroe-completion-candidates' based on response from Lua server.
Since monroe doesn't have any synchronous communication available, we
have to `sit-for' and hope a response has been returned and handled."
(monroe-dbind-response response (id completions status output)
(let ((process (get-buffer-process monroe-repl-buffer)))
(comint-output-filter process output)
(when completions
(setq monroe-completion-candidates completions))
(when status
(when (member "done" status)
(remhash id monroe-requests))))))
(defun monroe-lua-complete-function ()
"Completion function for `completion-at-point-functions'.
Queries over current lua connection for possible completions."
(let ((expr (buffer-substring-no-properties (lua-start-of-expr) (point))))
(monroe-send-request `("op" "complete"
"input" ,expr
;; TODO: at this time, monroe cannot bencode
;; nested values, only string->string dicts
;; "libs" ,(lua-local-libs)
"session" ,(monroe-current-session))
'monroe-completion-handler))
(sit-for 0.1)
(list (save-excursion (when (symbol-at-point) (backward-sexp)) (point))
(point)
monroe-completion-candidates))
;;;###autoload
(defun monroe-lua-hook ()
(make-local-variable 'completion-at-point-functions)
(add-to-list 'completion-at-point-functions 'monroe-lua-complete-function))
;;;###autoload
(eval-after-load 'lua-mode
'(add-to-list 'lua-mode-hook 'monroe-lua-hook))
;;; monroe-lua-complete.el ends here

View file

@ -0,0 +1,28 @@
-- -*- lua -*-
package = "jeejah"
version = "0.1.0-1"
source = {
url = "https://gitlab.com/technomancy/jeejah.git",
tag = "0.1.0",
}
description = {
summary = "An nREPL server",
detailed = [[
Implements a server that speaks the nREPL protocol and allows
clients to connect and evaluate code over a network connection.
]],
homepage = "https://gitlab.com/technomancy/jeejah.git",
license = "MIT/X11",
}
dependencies = {
"lua ~> 5.1",
"luasocket = 3.0rc1-2",
"serpent = 0.28-1",
"bencode = 2.2.0-1",
}
build = {
type = "builtin",
modules = { jeejah = "jeejah.lua", },
install = { bin = { "bin/jeejah" } },
}

View file

@ -0,0 +1,28 @@
-- -*- lua -*-
package = "jeejah"
version = "0.2.1-1"
source = {
url = "git+https://gitlab.com/technomancy/jeejah.git",
tag = "0.2.1",
}
description = {
summary = "An nREPL server",
detailed = [[
Implements a server that speaks the nREPL protocol and allows
clients to connect and evaluate code over a network connection.
]],
homepage = "https://gitlab.com/technomancy/jeejah",
license = "MIT/X11",
}
dependencies = {
"lua >= 5.1",
"luasocket = 3.0rc1-2",
"serpent = 0.28-1",
"bencode = 2.2.0-1",
}
build = {
type = "builtin",
modules = { jeejah = "jeejah.lua", },
install = { bin = { "bin/jeejah" } },
}

View file

@ -0,0 +1,28 @@
-- -*- lua -*-
package = "jeejah"
version = "0.2.1-4"
source = {
url = "git+https://gitlab.com/technomancy/jeejah.git",
tag = "0.2.4",
}
description = {
summary = "An nREPL server",
detailed = [[
Implements a server that speaks the nREPL protocol and allows
clients to connect and evaluate code over a network connection.
]],
homepage = "https://gitlab.com/technomancy/jeejah",
license = "MIT/X11",
}
dependencies = {
"lua >= 5.1",
"luasocket = 3.0rc1-2",
"serpent = 0.28-1",
"bencode = 2.2.0-1",
}
build = {
type = "builtin",
modules = { jeejah = "jeejah.lua", },
install = { bin = { "bin/jeejah" } },
}

View file

@ -0,0 +1,28 @@
-- -*- lua -*-
package = "jeejah"
version = "0.3.0-1"
source = {
url = "git+https://gitlab.com/technomancy/jeejah.git",
tag = "0.3.0",
}
description = {
summary = "An nREPL server",
detailed = [[
Implements a server that speaks the nREPL protocol and allows
clients to connect and evaluate code over a network connection.
]],
homepage = "https://gitlab.com/technomancy/jeejah",
license = "MIT/X11",
}
dependencies = {
"lua >= 5.1",
"luasocket = 3.0rc1-2",
"serpent = 0.28-1",
"bencode = 2.2.0-1",
}
build = {
type = "builtin",
modules = { jeejah = "jeejah.lua", },
install = { bin = { "bin/jeejah" } },
}

View file

@ -0,0 +1,28 @@
-- -*- lua -*-
package = "jeejah"
version = "0.3.1-1"
source = {
url = "git+https://gitlab.com/technomancy/jeejah.git",
tag = "0.3.1",
}
description = {
summary = "An nREPL server",
detailed = [[
Implements a server that speaks the nREPL protocol and allows
clients to connect and evaluate code over a network connection.
]],
homepage = "https://gitlab.com/technomancy/jeejah",
license = "MIT/X11",
}
dependencies = {
"lua >= 5.1",
"luasocket = 3.0rc1-2",
"serpent = 0.28-1",
"bencode = 2.2.0-1",
}
build = {
type = "builtin",
modules = { jeejah = "jeejah.lua", },
install = { bin = { "bin/jeejah" } },
}

View file

@ -0,0 +1,29 @@
-- -*- lua -*-
package = "jeejah"
version = "0.3.1-2"
source = {
url = "git+https://gitlab.com/technomancy/jeejah.git",
tag = "0.3.1",
}
description = {
summary = "An nREPL server",
detailed = [[
Implements a server that speaks the nREPL protocol and allows
clients to connect and evaluate code over a network connection.
]],
homepage = "https://gitlab.com/technomancy/jeejah",
license = "MIT/X11",
}
dependencies = {
"lua >= 5.1",
"luasocket = 3.0rc1-2",
"serpent = 0.28-1",
"bencode = 2.2.0-1",
}
build = {
type = "builtin",
modules = { jeejah = "jeejah.lua",
["jeejah.fenneleval"] = "jeejah/fenneleval.lua" },
install = { bin = { "bin/jeejah" } },
}

View file

@ -0,0 +1,30 @@
-- -*- lua -*-
package = "jeejah"
version = "0.3.1-4"
source = {
url = "git+https://gitlab.com/technomancy/jeejah.git",
tag = "0.3.1",
}
description = {
summary = "An nREPL server",
detailed = [[
Implements a server that speaks the nREPL protocol and allows
clients to connect and evaluate code over a network connection.
]],
homepage = "https://gitlab.com/technomancy/jeejah",
license = "MIT/X11",
}
dependencies = {
"lua >= 5.1",
"luasocket = 3.0rc1-2",
"serpent = 0.28-1",
}
build = {
type = "builtin",
modules = { jeejah = "jeejah.lua",
["jeejah.fenneleval"] = "jeejah/fenneleval.lua",
bencode = "bencode.lua",
},
install = { bin = { "bin/jeejah" } },
}

125
vendor/jeejah/serpent.lua vendored Normal file
View file

@ -0,0 +1,125 @@
local n, v = "serpent", 0.28 -- (C) 2012-15 Paul Kulchenko; MIT License
local c, d = "Paul Kulchenko", "Lua serializer and pretty printer"
local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'}
local badtype = {thread = true, userdata = true, cdata = true}
local keyword, globals, G = {}, {}, (_G or _ENV)
for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false',
'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end
for k,v in pairs(G) do globals[v] = k end -- build func to name mapping
for _,g in ipairs({'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os'}) do
for k,v in pairs(G[g] or {}) do globals[v] = g..'.'..k end end
local function s(t, opts)
local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum
local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge
local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge)
local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge)
local seen, sref, syms, symn = {}, {'local '..iname..'={}'}, {}, 0
local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)",
-- tostring(val) is needed because __tostring may return a non-string value
function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return tostring(syms[s]) end)) end
local function safestr(s) return type(s) == "number" and tostring(huge and snum[tostring(s)] or s)
or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026
or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end
local function comment(s,l) return comm and (l or 0) < comm and ' --[['..tostring(s)..']]' or '' end
local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal
and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end
local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r']
local n = name == nil and '' or name
local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n]
local safe = plain and n or '['..safestr(n)..']'
return (path or '')..(plain and path and '.' or '')..safe, safe end
local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding
local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'}
local function padnum(d) return ("%0"..tostring(maxn).."d"):format(tonumber(d)) end
table.sort(k, function(a,b)
-- sort numeric keys first: k[key] is not nil for numerical keys
return (k[a] ~= nil and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum))
< (k[b] ~= nil and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end
local function val2str(t, name, indent, insref, path, plainindex, level)
local ttype, level, mt = type(t), (level or 0), getmetatable(t)
local spath, sname = safename(path, name)
local tag = plainindex and
((type(name) == "number") and '' or name..space..'='..space) or
(name ~= nil and sname..space..'='..space or '')
if seen[t] then -- already seen this element
sref[#sref+1] = spath..space..'='..space..seen[t]
return tag..'nil'..comment('ref', level) end
if type(mt) == 'table' and (mt.__serialize or mt.__tostring) then -- knows how to serialize itself
seen[t] = insref or spath
if mt.__serialize then t = mt.__serialize(t) else t = tostring(t) end
ttype = type(t) end -- new value falls through to be serialized
if ttype == "table" then
if level >= maxl then return tag..'{}'..comment('max', level) end
seen[t] = insref or spath
-- PNH: this breaks with our metatable monkeypatch to support iterators
-- if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty
local maxn, o, out = math.min(#t, maxnum or #t), {}, {}
for key = 1, maxn do o[key] = key end
if not maxnum or #o < maxnum then
local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables
for key in pairs(t) do if o[key] ~= key then n = n + 1; o[n] = key end end end
if maxnum and #o > maxnum then o[maxnum+1] = nil end
if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end
local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output)
for n, key in ipairs(o) do
local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse
if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing
or opts.keyallow and not opts.keyallow[key]
or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types
or sparse and value == nil then -- skipping nils; do nothing
elseif ktype == 'table' or ktype == 'function' or badtype[ktype] then
if not seen[key] and not globals[key] then
sref[#sref+1] = 'placeholder'
local sname = safename(iname, gensym(key)) -- iname is table for local variables
sref[#sref] = val2str(key,sname,indent,sname,iname,true) end
sref[#sref+1] = 'placeholder'
local path = seen[t]..'['..tostring(seen[key] or globals[key] or gensym(key))..']'
sref[#sref] = path..space..'='..space..tostring(seen[value] or val2str(value,nil,indent,path))
else
out[#out+1] = val2str(value,key,indent,insref,seen[t],plainindex,level+1)
end
end
local prefix = string.rep(indent or '', level)
local head = indent and '{\n'..prefix..indent or '{'
local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space))
local tail = indent and "\n"..prefix..'}' or '}'
return (custom and custom(tag,head,body,tail) or tag..head..body..tail)..comment(t, level)
elseif badtype[ttype] then
seen[t] = insref or spath
return tag..globerr(t, level)
elseif ttype == 'function' then
seen[t] = insref or spath
local ok, res = pcall(string.dump, t)
local func = ok and ((opts.nocode and "function() --[[..skipped..]] end" or
"((loadstring or load)("..safestr(res)..",'@serialized'))")..comment(t, level))
return tag..(func or globerr(t, level))
else return tag..safestr(t) end -- handle all other types
end
local sepr = indent and "\n" or ";"..space
local body = val2str(t, name, indent) -- this call also populates sref
local tail = #sref>1 and table.concat(sref, sepr)..sepr or ''
local warn = opts.comment and #sref>1 and space.."--[[incomplete output with shared/self-references skipped]]" or ''
return not name and body..warn or "do local "..body..sepr..tail.."return "..name..sepr.."end"
end
local function deserialize(data, opts)
local env = (opts and opts.safe == false) and G
or setmetatable({}, {
__index = function(t,k) return t end,
__call = function(t,...) error("cannot call functions") end
})
local f, res = (loadstring or load)('return '..data, nil, nil, env)
if not f then f, res = (loadstring or load)(data, nil, nil, env) end
if not f then return f, res end
if setfenv then setfenv(f, env) end
return pcall(f)
end
local function merge(a, b) if b then for k,v in pairs(b) do a[k] = v end end; return a; end
return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s,
load = deserialize,
dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end,
line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end,
block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end }