summaryrefslogtreecommitdiff
path: root/lib/lua-repl/repl/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lua-repl/repl/plugins')
-rw-r--r--lib/lua-repl/repl/plugins/autoreturn.lua29
-rw-r--r--lib/lua-repl/repl/plugins/completion.lua192
-rw-r--r--lib/lua-repl/repl/plugins/example.lua41
-rw-r--r--lib/lua-repl/repl/plugins/filename_completion.lua63
-rw-r--r--lib/lua-repl/repl/plugins/history.lua60
-rw-r--r--lib/lua-repl/repl/plugins/keep_last_eval.lua43
-rw-r--r--lib/lua-repl/repl/plugins/linenoise.lua59
-rw-r--r--lib/lua-repl/repl/plugins/pretty_print.lua262
-rw-r--r--lib/lua-repl/repl/plugins/rcfile.lua54
-rw-r--r--lib/lua-repl/repl/plugins/rlwrap.lua41
-rw-r--r--lib/lua-repl/repl/plugins/semicolon_suppress_output.lua36
11 files changed, 880 insertions, 0 deletions
diff --git a/lib/lua-repl/repl/plugins/autoreturn.lua b/lib/lua-repl/repl/plugins/autoreturn.lua
new file mode 100644
index 00000000..a0e15eab
--- /dev/null
+++ b/lib/lua-repl/repl/plugins/autoreturn.lua
@@ -0,0 +1,29 @@
+-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
+--
+-- 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.
+
+-- A plugin that causes the REPL to automatically return evaluation results
+
+function around:compilechunk(orig, chunk)
+ local f, err = orig(self, 'return ' .. chunk)
+
+ if not f then
+ f, err = orig(self, chunk)
+ end
+
+ return f, err
+end
diff --git a/lib/lua-repl/repl/plugins/completion.lua b/lib/lua-repl/repl/plugins/completion.lua
new file mode 100644
index 00000000..938c7439
--- /dev/null
+++ b/lib/lua-repl/repl/plugins/completion.lua
@@ -0,0 +1,192 @@
+-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
+--
+-- 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.
+
+local utils = require 'repl.utils'
+local getmetatable = getmetatable
+local pairs = pairs
+local sfind = string.find
+local sgmatch = string.gmatch
+local smatch = string.match
+local ssub = string.sub
+local tconcat = table.concat
+local tsort = table.sort
+local type = type
+
+local function isindexable(value)
+ if type(value) == 'table' then
+ return true
+ end
+
+ local mt = getmetatable(value)
+
+ return mt and mt.__index
+end
+
+local function getcompletions(t)
+ local union = {}
+
+ while isindexable(t) do
+ if type(t) == 'table' then
+ -- XXX what about the pairs metamethod in 5.2?
+ -- either we don't care, we implement a __pairs-friendly
+ -- pairs for 5.1, or implement a 'rawpairs' for 5.2
+ for k, v in pairs(t) do
+ if union[k] == nil then
+ union[k] = v
+ end
+ end
+ end
+
+ local mt = getmetatable(t)
+ t = mt and mt.__index or nil
+ end
+
+ return pairs(union)
+end
+
+local function split_ns(expr)
+ if expr == '' then
+ return { '' }
+ end
+
+ local pieces = {}
+
+ -- XXX method calls too (option?)
+ for m in sgmatch(expr, '[^.]+') do
+ pieces[#pieces + 1] = m
+ end
+
+ -- logic for determining whether to pad the matches with the empty
+ -- string (ugly)
+ if ssub(expr, -1) == '.' then
+ pieces[#pieces + 1] = ''
+ end
+
+ return pieces
+end
+
+local function determine_ns(expr)
+ local ns = _G -- XXX what if the REPL lives in a special context? (option?)
+ local pieces = split_ns(expr)
+
+ for index = 1, #pieces - 1 do
+ local key = pieces[index]
+ -- XXX rawget? or regular access? (option?)
+ ns = ns[key]
+
+ if not isindexable(ns) then
+ return {}, '', ''
+ end
+ end
+
+ expr = pieces[#pieces]
+
+ local prefix = ''
+
+ if #pieces > 1 then
+ prefix = tconcat(pieces, '.', 1, #pieces - 1) .. '.'
+ end
+
+ local last_piece = pieces[#pieces]
+
+ local before, after = smatch(last_piece, '(.*):(.*)')
+
+ if before then
+ ns = ns[before] -- XXX rawget
+ prefix = prefix .. before .. ':'
+ expr = after
+ end
+
+ return ns, prefix, expr
+end
+
+local isidentifierchar
+
+do
+ local ident_chars_set = {}
+ -- XXX I think this can be done with isalpha in C...
+ local ident_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:_0123456789'
+
+ for i = 1, #ident_chars do
+ local char = ssub(ident_chars, i, i)
+ ident_chars_set[char] = true
+ end
+
+ function isidentifierchar(char)
+ return ident_chars_set[char]
+ end
+end
+
+local function extract_innermost_expr(expr)
+ local index = #expr
+
+ while index > 0 do
+ local char = ssub(expr, index, index)
+ if isidentifierchar(char) then
+ index = index - 1
+ else
+ break
+ end
+ end
+
+ index = index + 1
+
+ return ssub(expr, 1, index - 1), ssub(expr, index)
+end
+
+-- XXX is this logic (namely, returning the entire line) too specific to
+-- linenoise?
+function repl:complete(expr, callback)
+ if utils.ends_in_unfinished_string(expr) then
+ return
+ end
+
+ local ns, prefix, path
+
+ prefix, expr = extract_innermost_expr(expr)
+
+ ns, path, expr = determine_ns(expr)
+
+ prefix = prefix .. path
+
+ local completions = {}
+
+ for k, v in getcompletions(ns) do
+ if sfind(k, expr, 1, true) == 1 then
+ local suffix = ''
+ local type = type(v)
+
+ -- XXX this should be optional
+ if type == 'function' then
+ suffix = '('
+ elseif type == 'table' then
+ suffix = '.'
+ end
+
+ completions[#completions + 1] = prefix .. k .. suffix
+ end
+ end
+
+ tsort(completions)
+
+ for _, completion in ipairs(completions) do
+ callback(completion)
+ end
+end
+
+features = 'completion'
diff --git a/lib/lua-repl/repl/plugins/example.lua b/lib/lua-repl/repl/plugins/example.lua
new file mode 100644
index 00000000..d55fd076
--- /dev/null
+++ b/lib/lua-repl/repl/plugins/example.lua
@@ -0,0 +1,41 @@
+-- Example plugin that demonstrates the objects available to a
+-- plugin, as well as the methods that a plugin should make use
+-- of
+
+-- Adding methods and properties to the repl object adds them to
+-- the REPL object loading the plugin. If such a method or property
+-- already exists, the current plugin will fail to load.
+function repl:newmethod(...)
+end
+
+-- Adding methods to the before object causes them to be called
+-- before the actual method itself. If the method being added
+-- (in this case displayresults) does not exist on the REPL object
+-- loading this plugin, the current plugin will fail to load.
+function before:displayresults(results)
+end
+
+-- Adding methods to the after object causes them to be called
+-- after the actual method itself. If the method being added
+-- (in this case displayresults) does not exist on the REPL object
+-- loading this plugin, the current plugin will fail to load.
+function after:displayresults(results)
+end
+
+-- Adding methods to the around object causes them to be called
+-- instead of the original method of the same name. The new
+-- method receives all of the arguments that the original would,
+-- except it also receives the original method as the first argument.
+-- This way, the new method may invoke the original as it pleases.
+-- If the method being added (in this case displayresults) does not exist on
+-- the REPL object loading this plugin, the current plugin will fail to load.
+function around:evalute(orig, chunk)
+end
+
+-- Adding methods to the override object causes them to be called
+-- instead of the original method of the same name. If the method being added
+-- (in this case displayresults) does not exist on the REPL object loading this
+-- plugin, the current plugin will fail to load.
+function override:name()
+ return 'Plugin!'
+end
diff --git a/lib/lua-repl/repl/plugins/filename_completion.lua b/lib/lua-repl/repl/plugins/filename_completion.lua
new file mode 100644
index 00000000..c16729f3
--- /dev/null
+++ b/lib/lua-repl/repl/plugins/filename_completion.lua
@@ -0,0 +1,63 @@
+local utils = require 'repl.utils'
+local lfs = require 'lfs'
+
+repl:requirefeature 'completion'
+
+local function guess_directory_separator(file_name)
+ return file_name:match('/') or
+ file_name:match('\\') or
+ '/'
+end
+
+local function split_parent_directory(file_name)
+ local parent_directory, directory_entry =
+ file_name:match('^(.+)[\\/](.+)$')
+ if not parent_directory then
+ parent_directory = '.'
+ directory_entry = file_name
+ end
+ return parent_directory, directory_entry
+end
+
+local function is_ignored_directory_entry(entry)
+ return entry == '.' or
+ entry == '..'
+end
+
+local function replace_end_of_string(str, suffix, replacement)
+ assert(str:sub(-#suffix) == suffix)
+ return str:sub(1, -(#suffix+1)) .. replacement
+end
+
+local function complete_file_name(file_name, expr, callback)
+ local directory, partial_entry = split_parent_directory(file_name)
+ for entry in lfs.dir(directory) do
+ if not is_ignored_directory_entry(entry) and
+ entry:find(partial_entry, 1, true) == 1 then
+ callback(replace_end_of_string(expr, partial_entry, entry))
+ end
+ end
+end
+
+local function complete_directory(directory, expr, callback)
+ for entry in lfs.dir(directory) do
+ if not is_ignored_directory_entry(entry) then
+ callback(expr..entry)
+ end
+ end
+end
+
+function after:complete(expr, callback)
+ if utils.ends_in_unfinished_string(expr) then
+ local file_name = expr:match('[%w@/\\.-_+#$%%{}[%]!~ ]+$')
+ if file_name then
+ if file_name:find('[/\\]$') then
+ complete_directory(file_name, expr, callback)
+ else
+ complete_file_name(file_name, expr, callback)
+ end
+ else
+ complete_directory('.', expr, callback)
+ end
+ end
+end
diff --git a/lib/lua-repl/repl/plugins/history.lua b/lib/lua-repl/repl/plugins/history.lua
new file mode 100644
index 00000000..6330dbd2
--- /dev/null
+++ b/lib/lua-repl/repl/plugins/history.lua
@@ -0,0 +1,60 @@
+-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
+--
+-- 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.
+
+local history_file
+
+local function invokecallback(self, name, ...)
+ if not self._history_callbacks then
+ return
+ end
+
+ local impl = self._history_callbacks[name]
+ return impl(...)
+end
+
+local function init()
+ if os.getenv 'HOME' then
+ history_file = os.getenv('HOME') .. '/.rep.lua.history'
+ end
+end
+
+-- XXX I don't know if this callback setup way
+-- is the best way to go about this (in fact
+-- I'm pretty sure it isn't), but I just need
+-- something that works right now.
+function repl:setuphistorycallbacks(callbacks)
+ self._history_callbacks = callbacks
+
+ if history_file then
+ invokecallback(self, 'load', history_file)
+ end
+end
+
+function after:handleline(line)
+ invokecallback(self, 'addline', line)
+end
+
+function before:shutdown()
+ if history_file then
+ invokecallback(self, 'save', history_file)
+ end
+end
+
+features = 'history'
+
+init()
diff --git a/lib/lua-repl/repl/plugins/keep_last_eval.lua b/lib/lua-repl/repl/plugins/keep_last_eval.lua
new file mode 100644
index 00000000..01a77946
--- /dev/null
+++ b/lib/lua-repl/repl/plugins/keep_last_eval.lua
@@ -0,0 +1,43 @@
+-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
+--
+-- 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.
+
+-- A plugin that stores the results of the last evaluation in _G._
+
+local tostring = tostring
+
+function before:displayresults(results)
+ local context = self:getcontext()
+
+ if self._keep_eval_lastn then
+ context._ = nil
+
+ for i = 1, self._keep_eval_lastn do
+ context['_' .. tostring(i)] = nil
+ end
+ end
+
+ if results.n > 0 then
+ context._ = results[1]
+
+ for i = 1, results.n do
+ context['_' .. tostring(i)] = results[i]
+ end
+
+ self._keep_eval_lastn = results.n
+ end
+end
diff --git a/lib/lua-repl/repl/plugins/linenoise.lua b/lib/lua-repl/repl/plugins/linenoise.lua
new file mode 100644
index 00000000..4407c535
--- /dev/null
+++ b/lib/lua-repl/repl/plugins/linenoise.lua
@@ -0,0 +1,59 @@
+-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
+--
+-- 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.
+
+-- A plugin that uses linenoise (https://github.com/hoelzro/lua-linenoise) for prompting
+
+local ln = require 'linenoise'
+
+repl:requirefeature 'console'
+
+function override:showprompt(prompt)
+ self._prompt = prompt -- XXX how do we make sure other plugins don't step on this?
+end
+
+function override:lines()
+ return function()
+ return ln.linenoise(self._prompt .. ' ')
+ end
+end
+
+repl:iffeature('completion', function()
+ ln.setcompletion(function(completions, line)
+ repl:complete(line, function(completion)
+ ln.addcompletion(completions, completion)
+ end)
+ end)
+end)
+
+repl:ifplugin('history', function()
+ repl:setuphistorycallbacks {
+ load = function(filename)
+ ln.historyload(filename)
+ end,
+
+ addline = function(line)
+ ln.historyadd(line)
+ end,
+
+ save = function(filename)
+ ln.historysave(filename)
+ end,
+ }
+end)
+
+features = 'input'
diff --git a/lib/lua-repl/repl/plugins/pretty_print.lua b/lib/lua-repl/repl/plugins/pretty_print.lua
new file mode 100644
index 00000000..2318f444
--- /dev/null
+++ b/lib/lua-repl/repl/plugins/pretty_print.lua
@@ -0,0 +1,262 @@
+-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
+--
+-- 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.
+
+-- Pretty prints expression results (console only)
+
+local format = string.format
+local tconcat = table.concat
+local tsort = table.sort
+local tostring = tostring
+local type = type
+local floor = math.floor
+local pairs = pairs
+local ipairs = ipairs
+local error = error
+local stderr = io.stderr
+
+pcall(require, 'luarocks.require')
+local ok, term = pcall(require, 'term')
+if not ok then
+ term = nil
+end
+
+local keywords = {
+ ['and'] = true,
+ ['break'] = true,
+ ['do'] = true,
+ ['else'] = true,
+ ['elseif'] = true,
+ ['end'] = true,
+ ['false'] = true,
+ ['for'] = true,
+ ['function'] = true,
+ ['if'] = true,
+ ['in'] = true,
+ ['local'] = true,
+ ['nil'] = true,
+ ['not'] = true,
+ ['or'] = true,
+ ['repeat'] = true,
+ ['return'] = true,
+ ['then'] = true,
+ ['true'] = true,
+ ['until'] = true,
+ ['while'] = true,
+}
+
+local function compose(f, g)
+ return function(...)
+ return f(g(...))
+ end
+end
+
+local emptycolormap = setmetatable({}, { __index = function()
+ return function(s)
+ return s
+ end
+end})
+
+local colormap = emptycolormap
+
+if term then
+ colormap = {
+ ['nil'] = term.colors.blue,
+ string = term.colors.yellow,
+ punctuation = compose(term.colors.green, term.colors.bright),
+ ident = term.colors.red,
+ boolean = term.colors.green,
+ number = term.colors.cyan,
+ path = term.colors.white,
+ misc = term.colors.magenta,
+ }
+end
+
+local function isinteger(n)
+ return type(n) == 'number' and floor(n) == n
+end
+
+local function isident(s)
+ return type(s) == 'string' and not keywords[s] and s:match('^[a-zA-Z_][a-zA-Z0-9_]*$')
+end
+
+-- most of these are arbitrary, I *do* want numbers first, though
+local type_order = {
+ number = 0,
+ string = 1,
+ userdata = 2,
+ table = 3,
+ thread = 4,
+ boolean = 5,
+ ['function'] = 6,
+ cdata = 7,
+}
+
+local function cross_type_order(a, b)
+ local pos_a = type_order[ type(a) ]
+ local pos_b = type_order[ type(b) ]
+
+ if pos_a == pos_b then
+ return a < b
+ else
+ return pos_a < pos_b
+ end
+end
+
+local function sortedpairs(t)
+ local keys = {}
+
+ local seen_non_string
+
+ for k in pairs(t) do
+ keys[#keys + 1] = k
+
+ if not seen_non_string and type(k) ~= 'string' then
+ seen_non_string = true
+ end
+ end
+
+ local sort_func = seen_non_string and cross_type_order or nil
+ tsort(keys, sort_func)
+
+ local index = 1
+ return function()
+ if keys[index] == nil then
+ return nil
+ else
+ local key = keys[index]
+ local value = t[key]
+ index = index + 1
+
+ return key, value
+ end
+ end, keys
+end
+
+local function find_longstring_nest_level(s)
+ local level = 0
+
+ while s:find(']' .. string.rep('=', level) .. ']', 1, true) do
+ level = level + 1
+ end
+
+ return level
+end
+
+local function dump(params)
+ local pieces = params.pieces
+ local seen = params.seen
+ local path = params.path
+ local v = params.value
+ local indent = params.indent
+
+ local t = type(v)
+
+ if t == 'nil' or t == 'boolean' or t == 'number' then
+ pieces[#pieces + 1] = colormap[t](tostring(v))
+ elseif t == 'string' then
+ if v:match '\n' then
+ local level = find_longstring_nest_level(v)
+ pieces[#pieces + 1] = colormap.string('[' .. string.rep('=', level) .. '[' .. v .. ']' .. string.rep('=', level) .. ']')
+ else
+ pieces[#pieces + 1] = colormap.string(format('%q', v))
+ end
+ elseif t == 'table' then
+ if seen[v] then
+ pieces[#pieces + 1] = colormap.path(seen[v])
+ return
+ end
+
+ seen[v] = path
+
+ local lastintkey = 0
+
+ pieces[#pieces + 1] = colormap.punctuation '{\n'
+ for i, v in ipairs(v) do
+ for j = 1, indent do
+ pieces[#pieces + 1] = ' '
+ end
+ dump {
+ pieces = pieces,
+ seen = seen,
+ path = path .. '[' .. tostring(i) .. ']',
+ value = v,
+ indent = indent + 1,
+ }
+ pieces[#pieces + 1] = colormap.punctuation ',\n'
+ lastintkey = i
+ end
+
+ for k, v in sortedpairs(v) do
+ if not (isinteger(k) and k <= lastintkey and k > 0) then
+ for j = 1, indent do
+ pieces[#pieces + 1] = ' '
+ end
+
+ if isident(k) then
+ pieces[#pieces + 1] = colormap.ident(k)
+ else
+ pieces[#pieces + 1] = colormap.punctuation '['
+ dump {
+ pieces = pieces,
+ seen = seen,
+ path = path .. '.' .. tostring(k),
+ value = k,
+ indent = indent + 1,
+ }
+ pieces[#pieces + 1] = colormap.punctuation ']'
+ end
+ pieces[#pieces + 1] = colormap.punctuation ' = '
+ dump {
+ pieces = pieces,
+ seen = seen,
+ path = path .. '.' .. tostring(k),
+ value = v,
+ indent = indent + 1,
+ }
+ pieces[#pieces + 1] = colormap.punctuation ',\n'
+ end
+ end
+
+ for j = 1, indent - 1 do
+ pieces[#pieces + 1] = ' '
+ end
+
+ pieces[#pieces + 1] = colormap.punctuation '}'
+ else
+ pieces[#pieces + 1] = colormap.misc(tostring(v))
+ end
+end
+
+repl:requirefeature 'console'
+
+function override:displayresults(results)
+ local pieces = {}
+
+ for i = 1, results.n do
+ dump {
+ pieces = pieces,
+ seen = {},
+ path = '<topvalue>',
+ value = results[i],
+ indent = 1,
+ }
+ pieces[#pieces + 1] = '\n'
+ end
+
+ stderr:write(tconcat(pieces, ''))
+end
diff --git a/lib/lua-repl/repl/plugins/rcfile.lua b/lib/lua-repl/repl/plugins/rcfile.lua
new file mode 100644
index 00000000..a74e81ac
--- /dev/null
+++ b/lib/lua-repl/repl/plugins/rcfile.lua
@@ -0,0 +1,54 @@
+-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
+--
+-- 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.
+
+-- A plugin that runs code in $HOME/.rep.lua before the REPL starts
+
+local setfenv = require('repl.utils').setfenv
+
+local function readable(filename)
+ local f = io.open(filename, 'r')
+ if not f then
+ return false
+ end
+ f:close()
+ return true
+end
+
+local function init()
+ local home = os.getenv 'HOME'
+
+ if not home then
+ return
+ end
+
+ local rcfile = home .. '/.rep.lua'
+
+ if not readable(rcfile) then
+ return
+ end
+
+ local chunk = assert(loadfile(rcfile))
+ local env = setmetatable({ repl = repl }, { __index = _G, __newindex = _G })
+
+ setfenv(chunk, env)
+
+ chunk()
+ return true
+end
+
+return init()
diff --git a/lib/lua-repl/repl/plugins/rlwrap.lua b/lib/lua-repl/repl/plugins/rlwrap.lua
new file mode 100644
index 00000000..9f195a3f
--- /dev/null
+++ b/lib/lua-repl/repl/plugins/rlwrap.lua
@@ -0,0 +1,41 @@
+-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
+--
+-- 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.
+
+if os.getenv 'LUA_REPL_RLWRAP' then
+ features = 'input'
+else
+ -- XXX check that we're not receiving input from a non-tty
+ local has_rlwrap = os.execute('which rlwrap >/dev/null 2>/dev/null')
+
+ if type(has_rlwrap) ~= 'boolean' then
+ has_rlwrap = has_rlwrap == 0
+ end
+
+ if not has_rlwrap then
+ error 'Please install rlwrap in order to use the rlwrap plugin'
+ end
+
+ local lowest_index = -1
+
+ while arg[lowest_index] ~= nil do
+ lowest_index = lowest_index - 1
+ end
+ lowest_index = lowest_index + 1
+ os.execute(string.format('LUA_REPL_RLWRAP=1 rlwrap %q %q', arg[lowest_index], arg[0]))
+ os.exit(0)
+end
diff --git a/lib/lua-repl/repl/plugins/semicolon_suppress_output.lua b/lib/lua-repl/repl/plugins/semicolon_suppress_output.lua
new file mode 100644
index 00000000..4adaecb6
--- /dev/null
+++ b/lib/lua-repl/repl/plugins/semicolon_suppress_output.lua
@@ -0,0 +1,36 @@
+-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
+--
+-- 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.
+
+local smatch = string.match
+
+-- XXX will this affect any other plugins?
+function around:compilechunk(orig, chunk)
+ local f, err = orig(self, chunk)
+
+ if not f then
+ return f, err
+ end
+
+ if smatch(chunk, ';%s*$') then
+ return function()
+ f()
+ end
+ end
+
+ return f
+end