Add 32x32 example.
This commit is contained in:
parent
109c23517b
commit
841bd316f7
|
@ -0,0 +1,39 @@
|
||||||
|
local lynx = require "lynx"
|
||||||
|
local raylua_funcs = require "lynx.raylua_lynx"
|
||||||
|
|
||||||
|
local menu = lynx.menu ({
|
||||||
|
lynx.text("Test Lynx menu in Raylib", { selectable = false }),
|
||||||
|
lynx.text("", { selectable = false }),
|
||||||
|
lynx.text("Long life to Lynx !"),
|
||||||
|
lynx.text("the best UI library ever !"),
|
||||||
|
lynx.text("which works everywhere !"),
|
||||||
|
lynx.text("", { selectable = false }),
|
||||||
|
lynx.text("@TSnake41"),
|
||||||
|
}, {
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
w = 500,
|
||||||
|
h = 500,
|
||||||
|
default_height = 24,
|
||||||
|
current = 3,
|
||||||
|
funcs = raylua_funcs
|
||||||
|
})
|
||||||
|
|
||||||
|
rl.SetConfigFlags(rl.FLAG_VSYNC_HINT)
|
||||||
|
|
||||||
|
rl.InitWindow(800, 450, "raylib [lua] example - lynx menu")
|
||||||
|
|
||||||
|
while not rl.WindowShouldClose() do
|
||||||
|
rl.BeginDrawing()
|
||||||
|
|
||||||
|
rl.ClearBackground(rl.BLACK)
|
||||||
|
local pos = rl.GetMousePosition()
|
||||||
|
menu:input_mouse(pos.x, pos.y, 0)
|
||||||
|
|
||||||
|
menu:update(rl.GetFrameTime())
|
||||||
|
menu:draw()
|
||||||
|
|
||||||
|
rl.EndDrawing()
|
||||||
|
end
|
||||||
|
|
||||||
|
rl.CloseWindow()
|
|
@ -0,0 +1,40 @@
|
||||||
|
local lynx = require "lynx"
|
||||||
|
local raylua_funcs = require "lynx.raylua_lynx"
|
||||||
|
|
||||||
|
local menu = lynx.menu ({
|
||||||
|
lynx.text("Test Lynx menu in Raylib", { selectable = false }),
|
||||||
|
lynx.text("", { selectable = false }),
|
||||||
|
lynx.text("Long life to Lynx !"),
|
||||||
|
lynx.text("the best UI library ever !"),
|
||||||
|
lynx.text("which works everywhere !"),
|
||||||
|
lynx.text("", { selectable = false }),
|
||||||
|
lynx.text("@TSnake41"),
|
||||||
|
}, {
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
w = 500,
|
||||||
|
h = 500,
|
||||||
|
default_height = 24,
|
||||||
|
current = 3,
|
||||||
|
funcs = raylua_funcs
|
||||||
|
})
|
||||||
|
|
||||||
|
rl.SetConfigFlags(rl.FLAG_VSYNC_HINT)
|
||||||
|
--rl.SetTargetFPS(60)
|
||||||
|
|
||||||
|
rl.InitWindow(800, 450, "raylib [lua] example - lynx menu")
|
||||||
|
|
||||||
|
while not rl.WindowShouldClose() do
|
||||||
|
rl.BeginDrawing()
|
||||||
|
|
||||||
|
rl.ClearBackground(rl.BLACK)
|
||||||
|
local pos = rl.GetMousePosition()
|
||||||
|
menu:input_mouse(pos.x, pos.y, 0)
|
||||||
|
|
||||||
|
menu:update(rl.GetFrameTime())
|
||||||
|
menu:draw()
|
||||||
|
|
||||||
|
rl.EndDrawing()
|
||||||
|
end
|
||||||
|
|
||||||
|
rl.CloseWindow()
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 088321f96efc444eb99f1b35ed1718003bad3b78
|
|
@ -0,0 +1,90 @@
|
||||||
|
--[[
|
||||||
|
raylib 32x32 game/demo competition
|
||||||
|
|
||||||
|
Competition consist in developing a videogame in a 32x32 pixels screen size.
|
||||||
|
|
||||||
|
RULES:
|
||||||
|
|
||||||
|
1) Use only raylib (and included libraries), no external libraries allowed
|
||||||
|
2) The submission should consist of just one source file
|
||||||
|
3) Render your game/demo to a 32x32 pixels render texture,
|
||||||
|
show what you could do with a 32x32 RGB LED matrix!
|
||||||
|
4) No external resources, you CAN only create them programmatically,
|
||||||
|
5) Game/demo can be 2D or 3D, choose wisely
|
||||||
|
5) Shaders (if used) should be included in the source as string (char *)
|
||||||
|
and loaded with LoadShaderCode()
|
||||||
|
6) Code must compile and execute in PLATFORM_DESKTOP (Windows, Linux, macOS)
|
||||||
|
|
||||||
|
|
||||||
|
LICENSE: zlib/libpng
|
||||||
|
|
||||||
|
Copyright (c) 2020 <TODO: participant_name> (<TODO: participant_github_name>)
|
||||||
|
|
||||||
|
This software is provided "as-is", without any express or implied warranty. In no event
|
||||||
|
will the authors be held liable for any damages arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose, including commercial
|
||||||
|
applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not claim that you
|
||||||
|
wrote the original software. If you use this software in a product, an acknowledgment
|
||||||
|
in the product documentation would be appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be misrepresented
|
||||||
|
as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local ffi = require "ffi"
|
||||||
|
|
||||||
|
local windowWidth = 512
|
||||||
|
local windowHeight = 512
|
||||||
|
local gameScreenWidth = 32
|
||||||
|
local gameScreenHeight = 32
|
||||||
|
|
||||||
|
rl.SetTraceLogLevel(rl.LOG_WARNING)
|
||||||
|
rl.SetConfigFlags(rl.FLAG_VSYNC_HINT)
|
||||||
|
rl.InitWindow(windowWidth, windowHeight, "my 32x32 game/demo")
|
||||||
|
|
||||||
|
local target = rl.LoadRenderTexture(gameScreenWidth, gameScreenHeight)
|
||||||
|
rl.SetTextureFilter(target.texture, rl.FILTER_POINT)
|
||||||
|
|
||||||
|
rl.SetTargetFPS(60)
|
||||||
|
|
||||||
|
while not rl.WindowShouldClose() do
|
||||||
|
local scale = math.min(rl.GetScreenWidth() / gameScreenWidth, rl.GetScreenHeight() / gameScreenHeight)
|
||||||
|
|
||||||
|
rl.BeginDrawing()
|
||||||
|
|
||||||
|
rl.BeginTextureMode(target)
|
||||||
|
rl.ClearBackground(rl.WHITE)
|
||||||
|
rl.DrawTextEx(rl.GetFontDefault(), "Hello", ffi.new("Vector2", 4, 0), rl.GetFontDefault().baseSize, 1, ffi.new("Color", 0, 64, 255, 255))
|
||||||
|
rl.DrawTextEx(rl.GetFontDefault(), "it's", ffi.new("Vector2", 8, 10), rl.GetFontDefault().baseSize, 1, ffi.new("Color", 0, 64, 255, 255))
|
||||||
|
rl.DrawTextEx(rl.GetFontDefault(), "raylua", ffi.new("Vector2", 0, 20), rl.GetFontDefault().baseSize, 1, ffi.new("Color", 0, 64, 255, 255))
|
||||||
|
|
||||||
|
rl.EndTextureMode()
|
||||||
|
|
||||||
|
rl.DrawTexturePro(
|
||||||
|
target.texture,
|
||||||
|
ffi.new("Rectangle", 0, 0, target.texture.width, -target.texture.height),
|
||||||
|
ffi.new("Rectangle",
|
||||||
|
(rl.GetScreenWidth() - gameScreenWidth * scale) * 0.5,
|
||||||
|
(rl.GetScreenHeight() - gameScreenHeight * scale) * 0.5,
|
||||||
|
gameScreenWidth * scale, gameScreenHeight * scale
|
||||||
|
), ffi.new("Vector2", 0, 0), 0, rl.WHITE
|
||||||
|
)
|
||||||
|
|
||||||
|
for x=0,rl.GetScreenWidth(),16 do
|
||||||
|
rl.DrawRectangle(x, 0, 4, rl.GetScreenHeight(), rl.BLACK)
|
||||||
|
end
|
||||||
|
|
||||||
|
for y=0,rl.GetScreenHeight(),16 do
|
||||||
|
rl.DrawRectangle(0, y, rl.GetScreenWidth(), 4, rl.BLACK)
|
||||||
|
end
|
||||||
|
rl.EndDrawing()
|
||||||
|
end
|
||||||
|
|
||||||
|
rl.UnloadRenderTexture(target)
|
||||||
|
|
||||||
|
rl.CloseWindow()
|
|
@ -0,0 +1,19 @@
|
||||||
|
local repl = require 'repl.console'
|
||||||
|
local rcfile_loaded = repl:loadplugin 'rcfile'
|
||||||
|
|
||||||
|
if not rcfile_loaded then
|
||||||
|
local has_linenoise = pcall(require, 'linenoise')
|
||||||
|
|
||||||
|
if has_linenoise then
|
||||||
|
repl:loadplugin 'linenoise'
|
||||||
|
else
|
||||||
|
pcall(repl.loadplugin, repl, 'rlwrap')
|
||||||
|
end
|
||||||
|
|
||||||
|
repl:loadplugin 'history'
|
||||||
|
repl:loadplugin 'completion'
|
||||||
|
repl:loadplugin 'autoreturn'
|
||||||
|
end
|
||||||
|
|
||||||
|
print('Lua REPL ' .. tostring(repl.VERSION))
|
||||||
|
repl:run()
|
|
@ -0,0 +1,30 @@
|
||||||
|
-- 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.
|
||||||
|
|
||||||
|
return {
|
||||||
|
-- unpack was moved to table.unpack on Lua version 5.2
|
||||||
|
-- See https://www.lua.org/manual/5.2/manual.html#8
|
||||||
|
unpack = unpack or table.unpack,
|
||||||
|
-- loadstring was deprecated in favor of load, which was updated
|
||||||
|
-- to handle string arguments
|
||||||
|
loadstring = loadstring or load,
|
||||||
|
-- package.loaders was renamed package.searchers in Lua 5.2
|
||||||
|
package = {
|
||||||
|
searchers = package.loaders or package.searchers
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
-- 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.
|
||||||
|
|
||||||
|
-- @class repl.console
|
||||||
|
--- This module implements a command line-based REPL,
|
||||||
|
--- similar to the standalone Lua interpreter.
|
||||||
|
|
||||||
|
local sync_repl = require 'repl.sync'
|
||||||
|
local compat = require 'repl.compat'
|
||||||
|
local console_repl = sync_repl:clone()
|
||||||
|
local stdout = io.stdout
|
||||||
|
local stdin = io.stdin
|
||||||
|
local print = print
|
||||||
|
local unpack = unpack
|
||||||
|
|
||||||
|
-- @see repl:showprompt(prompt)
|
||||||
|
function console_repl:showprompt(prompt)
|
||||||
|
stdout:write(prompt .. ' ')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- @see repl.sync:lines()
|
||||||
|
function console_repl:lines()
|
||||||
|
return stdin:lines()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- @see repl:displayresults(results)
|
||||||
|
function console_repl:displayresults(results)
|
||||||
|
if results.n == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
print(compat.unpack(results, 1, results.n))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- @see repl:displayerror(err)
|
||||||
|
function console_repl:displayerror(err)
|
||||||
|
print(err)
|
||||||
|
end
|
||||||
|
|
||||||
|
console_repl._features.console = true
|
||||||
|
|
||||||
|
return console_repl
|
|
@ -0,0 +1,415 @@
|
||||||
|
-- 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.
|
||||||
|
|
||||||
|
-- @class repl
|
||||||
|
--- This module implements the core functionality of a REPL.
|
||||||
|
|
||||||
|
local plugins_lookup_meta = { __mode = 'k' }
|
||||||
|
|
||||||
|
local repl = { _buffer = '', _plugins = setmetatable({}, plugins_lookup_meta), _features = {}, _ifplugin = {}, _iffeature = {}, VERSION = 0.9 }
|
||||||
|
local compat = require 'repl.compat'
|
||||||
|
local select = select
|
||||||
|
local dtraceback = debug.traceback
|
||||||
|
local setmetatable = setmetatable
|
||||||
|
local sformat = string.format
|
||||||
|
local smatch = string.match
|
||||||
|
local error = error
|
||||||
|
local setfenv = require('repl.utils').setfenv
|
||||||
|
|
||||||
|
local function gather_results(success, ...)
|
||||||
|
local n = select('#', ...)
|
||||||
|
return success, { n = n, ... }
|
||||||
|
end
|
||||||
|
|
||||||
|
local function tcopy(t, copy)
|
||||||
|
copy = copy or {}
|
||||||
|
|
||||||
|
for k, v in pairs(t) do
|
||||||
|
copy[k] = v
|
||||||
|
end
|
||||||
|
|
||||||
|
return copy
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the prompt for a given level.
|
||||||
|
-- @param level The prompt level. Either 1 or 2.
|
||||||
|
function repl:getprompt(level)
|
||||||
|
return level == 1 and '>' or '>>'
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Displays a prompt for the given prompt level.
|
||||||
|
-- @param level The prompt level. Either 1 or 2.
|
||||||
|
function repl:prompt(level)
|
||||||
|
self:showprompt(self:getprompt(level))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the name of the REPL. For usage in chunk compilation.
|
||||||
|
-- @return The REPL's name.
|
||||||
|
-- @see load
|
||||||
|
function repl:name()
|
||||||
|
return 'REPL'
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets a traceback for an error.
|
||||||
|
-- @param ... All of the stuff that xpcall passes to error functions.
|
||||||
|
-- @see xpcall
|
||||||
|
-- @return A stack trace. The default implementation returns a simple string-based trace.
|
||||||
|
function repl:traceback(...)
|
||||||
|
return dtraceback(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Uses the compilation error to determine whether or not further input
|
||||||
|
--- is pending after the last line. You shouldn't have to override this
|
||||||
|
--- unless you use an implementation of Lua that varies in its error
|
||||||
|
--- messages.
|
||||||
|
-- @param err The compilation error from Lua.
|
||||||
|
-- @return Whether or not the input should continue after this line.
|
||||||
|
function repl:detectcontinue(err)
|
||||||
|
return smatch(err, "'<eof>'$") or smatch(err, "<eof>$")
|
||||||
|
end
|
||||||
|
|
||||||
|
function repl:compilechunk(chunk)
|
||||||
|
return compat.loadstring(chunk, self:name())
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Evaluates a line of input, and displays return value(s).
|
||||||
|
-- @param line The line to evaluate
|
||||||
|
-- @return The prompt level (1 or 2)
|
||||||
|
function repl:handleline(line)
|
||||||
|
local chunk = self._buffer .. line
|
||||||
|
local f, err = self:compilechunk(chunk)
|
||||||
|
|
||||||
|
if f then
|
||||||
|
self._buffer = ''
|
||||||
|
|
||||||
|
setfenv(f, self:getcontext())
|
||||||
|
local success, results = gather_results(xpcall(f, function(...) return self:traceback(...) end))
|
||||||
|
if success then
|
||||||
|
self:displayresults(results)
|
||||||
|
else
|
||||||
|
self:displayerror(results[1])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if self:detectcontinue(err) then
|
||||||
|
self._buffer = chunk .. '\n'
|
||||||
|
return 2
|
||||||
|
else
|
||||||
|
self:displayerror(err)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Creates a new REPL object, so you can override methods without fear.
|
||||||
|
-- @return A REPL clone.
|
||||||
|
function repl:clone()
|
||||||
|
local plugins_copy = tcopy(self._plugins, setmetatable({}, plugins_lookup_meta))
|
||||||
|
local features_copy = tcopy(self._features)
|
||||||
|
local ifplugin_copy = {}
|
||||||
|
local iffeature_copy = {}
|
||||||
|
|
||||||
|
for k, v in pairs(self._ifplugin) do
|
||||||
|
ifplugin_copy[k] = tcopy(v)
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in pairs(self._iffeature) do
|
||||||
|
iffeature_copy[k] = tcopy(v)
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable({
|
||||||
|
_buffer = '',
|
||||||
|
_plugins = plugins_copy,
|
||||||
|
_features = features_copy,
|
||||||
|
_ifplugin = ifplugin_copy,
|
||||||
|
_iffeature = iffeature_copy,
|
||||||
|
}, { __index = self })
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Displays the given prompt to the user. Must be overriden.
|
||||||
|
-- @param prompt The prompt to display.
|
||||||
|
function repl:showprompt(prompt)
|
||||||
|
error 'You must implement the showprompt method'
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Displays the results from evaluate(). Must be overriden.
|
||||||
|
-- @param results The results to display. The results are a table, with the integer keys containing the results, and the 'n' key containing the highest integer key.
|
||||||
|
|
||||||
|
function repl:displayresults(results)
|
||||||
|
error 'You must implement the displayresults method'
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Displays errors from evaluate(). Must be overriden.
|
||||||
|
-- @param err The error value returned from repl:traceback().
|
||||||
|
-- @see repl:traceback
|
||||||
|
function repl:displayerror(err)
|
||||||
|
error 'You must implement the displayerror method'
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Checks whether this REPL object has loaded the given plugin.
|
||||||
|
-- @param plugin The plugin that the REPL may have loaded.
|
||||||
|
function repl:hasplugin(plugin)
|
||||||
|
return self._plugins[plugin]
|
||||||
|
end
|
||||||
|
|
||||||
|
function repl:hasfeature(feature)
|
||||||
|
return self._features[feature]
|
||||||
|
end
|
||||||
|
|
||||||
|
function repl:requirefeature(feature)
|
||||||
|
if not self:hasfeature(feature) then
|
||||||
|
error(sformat('required feature %q not present', feature), 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function repl:ifplugin(plugin, action)
|
||||||
|
if self:hasplugin(plugin) then
|
||||||
|
action()
|
||||||
|
else
|
||||||
|
local pending_actions = self._ifplugin[plugin]
|
||||||
|
|
||||||
|
if not pending_actions then
|
||||||
|
pending_actions = {}
|
||||||
|
self._ifplugin[plugin] = pending_actions
|
||||||
|
end
|
||||||
|
|
||||||
|
pending_actions[#pending_actions + 1] = action
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- If the given feature has been loaded, call `action`. Otherwise, if the
|
||||||
|
-- feature is ever loaded in the future, call `action` after that loading occurs.
|
||||||
|
function repl:iffeature(feature, action)
|
||||||
|
if self:hasfeature(feature) then
|
||||||
|
action()
|
||||||
|
else
|
||||||
|
local pending_features = self._iffeature[feature]
|
||||||
|
|
||||||
|
if not pending_features then
|
||||||
|
pending_features = {}
|
||||||
|
self._iffeature[feature] = pending_features
|
||||||
|
end
|
||||||
|
|
||||||
|
pending_features[#pending_features + 1] = action
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_before(repl)
|
||||||
|
local mt = {}
|
||||||
|
|
||||||
|
function mt:__newindex(key, value)
|
||||||
|
if type(value) ~= 'function' then
|
||||||
|
error(tostring(value) .. " is not a function", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local old_value = repl[key]
|
||||||
|
|
||||||
|
if old_value == nil then
|
||||||
|
error(sformat("The '%s' method does not exist", key), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
repl[key] = function(...)
|
||||||
|
value(...)
|
||||||
|
return old_value(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable({}, mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_after(repl)
|
||||||
|
local mt = {}
|
||||||
|
|
||||||
|
function mt:__newindex(key, value)
|
||||||
|
if type(value) ~= 'function' then
|
||||||
|
error(tostring(value) .. " is not a function", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local old_value = repl[key]
|
||||||
|
|
||||||
|
if old_value == nil then
|
||||||
|
error(sformat("The '%s' method does not exist", key), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
repl[key] = function(...)
|
||||||
|
local _, results = gather_results(true, old_value(...))
|
||||||
|
value(...)
|
||||||
|
return compat.unpack(results, 1, results.n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable({}, mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_around(repl)
|
||||||
|
local mt = {}
|
||||||
|
|
||||||
|
function mt:__newindex(key, value)
|
||||||
|
if type(value) ~= 'function' then
|
||||||
|
error(tostring(value) .. " is not a function", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local old_value = repl[key]
|
||||||
|
|
||||||
|
if old_value == nil then
|
||||||
|
error(sformat("The '%s' method does not exist", key), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
repl[key] = function(self, ...)
|
||||||
|
return value(self, old_value, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable({}, mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_override(repl)
|
||||||
|
local mt = {}
|
||||||
|
|
||||||
|
function mt:__newindex(key, value)
|
||||||
|
if type(value) ~= 'function' then
|
||||||
|
error(tostring(value) .. " is not a function", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local old_value = repl[key]
|
||||||
|
|
||||||
|
if old_value == nil then
|
||||||
|
error(sformat("The '%s' method does not exist", key), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
repl[key] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable({}, mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setup_repl(repl)
|
||||||
|
local mt = {}
|
||||||
|
|
||||||
|
function mt:__newindex(key, value)
|
||||||
|
local old_value = repl[key]
|
||||||
|
|
||||||
|
if old_value ~= nil then
|
||||||
|
error(sformat("The '%s' method already exists", key), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
repl[key] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:__index(key)
|
||||||
|
local value = repl[key]
|
||||||
|
|
||||||
|
if type(value) == 'function' then
|
||||||
|
-- XXX cache this?
|
||||||
|
return function(_, ...)
|
||||||
|
return value(repl, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable({}, mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO use lua-procure for this (eventually)
|
||||||
|
local function findchunk(name)
|
||||||
|
for _, loader in pairs(compat.package.searchers) do
|
||||||
|
local chunk = loader(name)
|
||||||
|
|
||||||
|
if type(chunk) == 'function' then
|
||||||
|
return chunk
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
error('unable to locate plugin', 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
function repl:loadplugin(chunk)
|
||||||
|
if self:hasplugin(chunk) then
|
||||||
|
error(sformat('plugin %q has already been loaded', tostring(chunk)), 2)
|
||||||
|
end
|
||||||
|
self._plugins[chunk] = true
|
||||||
|
|
||||||
|
local plugin_actions = self._ifplugin[chunk]
|
||||||
|
self._ifplugin[chunk] = nil
|
||||||
|
|
||||||
|
if type(chunk) == 'string' then
|
||||||
|
chunk = findchunk('repl.plugins.' .. chunk)
|
||||||
|
end
|
||||||
|
|
||||||
|
local plugin_env = {
|
||||||
|
repl = setup_repl(self),
|
||||||
|
before = setup_before(self),
|
||||||
|
after = setup_after(self),
|
||||||
|
around = setup_around(self),
|
||||||
|
override = setup_override(self),
|
||||||
|
init = function() end,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function ro_globals(_, key, _)
|
||||||
|
error(sformat('global environment is read-only (key = %q)', key), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
plugin_env._G = plugin_env
|
||||||
|
plugin_env.features = {}
|
||||||
|
setmetatable(plugin_env, { __index = _G, __newindex = ro_globals })
|
||||||
|
|
||||||
|
setfenv(chunk, plugin_env)
|
||||||
|
local _, results = gather_results(nil, chunk())
|
||||||
|
|
||||||
|
local features = plugin_env.features or {}
|
||||||
|
|
||||||
|
if type(features) == 'string' then
|
||||||
|
features = { features }
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, feature in ipairs(features) do
|
||||||
|
if self._features[feature] then
|
||||||
|
error(sformat('feature %q already present', feature), 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
self._features[feature] = true
|
||||||
|
|
||||||
|
local feature_actions = self._iffeature[feature]
|
||||||
|
self._iffeature[feature] = nil
|
||||||
|
if feature_actions then
|
||||||
|
for _, action in ipairs(feature_actions) do
|
||||||
|
action()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if plugin_actions then
|
||||||
|
for _, action in ipairs(plugin_actions) do
|
||||||
|
action()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return compat.unpack(results, 1, results.n)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- XXX how to guarantee this gets called?
|
||||||
|
function repl:shutdown()
|
||||||
|
end
|
||||||
|
|
||||||
|
function repl:getcontext()
|
||||||
|
return _G
|
||||||
|
end
|
||||||
|
|
||||||
|
return repl
|
|
@ -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
|
|
@ -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'
|
|
@ -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
|
|
@ -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
|
|
@ -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()
|
|
@ -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
|
|
@ -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'
|
|
@ -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
|
|
@ -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()
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,46 @@
|
||||||
|
-- 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 repl = require 'repl'
|
||||||
|
local sync_repl = repl:clone()
|
||||||
|
local error = error
|
||||||
|
|
||||||
|
-- @class repl.sync
|
||||||
|
--- This module implements a synchronous REPL. It provides
|
||||||
|
--- a run() method for actually running the REPL, and requires
|
||||||
|
--- that implementors implement the lines() method.
|
||||||
|
|
||||||
|
--- Run a REPL loop in a synchronous fashion.
|
||||||
|
-- @name repl.sync:run
|
||||||
|
function sync_repl:run()
|
||||||
|
self:prompt(1)
|
||||||
|
for line in self:lines() do
|
||||||
|
local level = self:handleline(line)
|
||||||
|
self:prompt(level)
|
||||||
|
end
|
||||||
|
self:shutdown()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns an iterator that yields lines to be evaluated.
|
||||||
|
-- @name repl.sync:lines
|
||||||
|
-- @return An iterator.
|
||||||
|
function sync_repl:lines()
|
||||||
|
error 'You must implement the lines method'
|
||||||
|
end
|
||||||
|
|
||||||
|
return sync_repl
|
|
@ -0,0 +1,70 @@
|
||||||
|
-- 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 setfenv = setfenv or function(f, t)
|
||||||
|
local upvalue_index = 1
|
||||||
|
|
||||||
|
-- XXX we may need a utility library if debug isn't available
|
||||||
|
while true do
|
||||||
|
local name = debug.getupvalue(f, upvalue_index)
|
||||||
|
-- some functions don't have an _ENV upvalue, because
|
||||||
|
-- they never refer to globals
|
||||||
|
if not name then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if name == '_ENV' then
|
||||||
|
debug.setupvalue(f, upvalue_index, t)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
upvalue_index = upvalue_index + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Tests wheter an expression ends in an unfinished string literal.
|
||||||
|
-- @return First position in the unfinished string literal or `nil`.
|
||||||
|
local function ends_in_unfinished_string(expr)
|
||||||
|
local position = 0
|
||||||
|
local quote
|
||||||
|
local current_delimiter
|
||||||
|
local last_unmatched_start
|
||||||
|
while true do
|
||||||
|
-- find all quotes:
|
||||||
|
position, quote = expr:match('()([\'"])', position+1)
|
||||||
|
if not position then break end
|
||||||
|
-- if we're currently in a string:
|
||||||
|
if current_delimiter then
|
||||||
|
-- would end current string?
|
||||||
|
if current_delimiter == quote then
|
||||||
|
-- not escaped?
|
||||||
|
if expr:sub(position-1, position-1) ~= '\\' then
|
||||||
|
current_delimiter = nil
|
||||||
|
last_unmatched_start = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
current_delimiter = quote
|
||||||
|
last_unmatched_start = position+1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return last_unmatched_start
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
setfenv = setfenv,
|
||||||
|
ends_in_unfinished_string = ends_in_unfinished_string
|
||||||
|
}
|
Loading…
Reference in New Issue