opus/sys/apps/Lua.lua

316 lines
7.1 KiB
Lua
Raw Normal View History

2017-10-08 21:45:01 +00:00
local injector = _G.requireInjector or load(http.get('https://raw.githubusercontent.com/kepler155c/opus/master/sys/apis/injector.lua').readAll())()
injector()
2017-10-08 21:45:01 +00:00
local Event = require('event')
local History = require('history')
local Peripheral = require('peripheral')
local UI = require('ui')
local Util = require('util')
2016-12-11 19:24:52 +00:00
2017-10-08 21:45:01 +00:00
local multishell = _ENV.multishell
local sandboxEnv = setmetatable(Util.shallowCopy(_ENV), { __index = _G })
sandboxEnv.exit = function() Event.exitPullEvents() end
2017-10-03 04:50:54 +00:00
sandboxEnv._echo = function( ... ) return ... end
2017-10-08 21:45:01 +00:00
injector(sandboxEnv)
2016-12-11 19:24:52 +00:00
multishell.setTitle(multishell.getCurrent(), 'Lua')
UI:configure('Lua', ...)
local command = ''
2017-05-20 22:27:26 +00:00
local history = History.load('usr/.lua_history', 25)
2016-12-11 19:24:52 +00:00
2017-10-03 04:50:54 +00:00
local page = UI.Page {
menuBar = UI.MenuBar {
2016-12-11 19:24:52 +00:00
buttons = {
{ text = 'Local', event = 'local' },
{ text = 'Global', event = 'global' },
2016-12-23 04:22:04 +00:00
{ text = 'Device', event = 'device', name = 'Device' },
2016-12-11 19:24:52 +00:00
},
2017-10-03 04:50:54 +00:00
},
prompt = UI.TextEntry {
2016-12-11 19:24:52 +00:00
y = 2,
shadowText = 'enter command',
limit = 256,
accelerators = {
2016-12-27 03:26:43 +00:00
enter = 'command_enter',
up = 'history_back',
down = 'history_forward',
mouse_rightclick = 'clear_prompt',
2016-12-27 21:01:06 +00:00
-- [ 'control-space' ] = 'autocomplete',
2016-12-11 19:24:52 +00:00
},
2017-10-03 04:50:54 +00:00
},
grid = UI.ScrollingGrid {
2016-12-11 19:24:52 +00:00
y = 3,
columns = {
{ heading = 'Key', key = 'name' },
{ heading = 'Value', key = 'value' },
},
sortColumn = 'name',
autospace = true,
2017-10-03 04:50:54 +00:00
},
2016-12-11 19:24:52 +00:00
notification = UI.Notification(),
2017-10-03 04:50:54 +00:00
}
2016-12-11 19:24:52 +00:00
2016-12-18 19:38:48 +00:00
function page:setPrompt(value, focus)
2016-12-11 19:24:52 +00:00
self.prompt:setValue(value)
self.prompt.scroll = 0
self.prompt:setPosition(#value)
self.prompt:updateScroll()
if value:sub(-1) == ')' then
self.prompt:setPosition(#value - 1)
end
self.prompt:draw()
if focus then
2016-12-18 19:38:48 +00:00
page:setFocus(self.prompt)
2016-12-11 19:24:52 +00:00
end
end
2016-12-18 19:38:48 +00:00
function page:enable()
2016-12-11 19:24:52 +00:00
self:setFocus(self.prompt)
UI.Page.enable(self)
end
2016-12-27 21:01:06 +00:00
local function autocomplete(env, oLine, x)
2016-12-27 03:26:43 +00:00
local sLine = oLine:sub(1, x)
local nStartPos = sLine:find("[a-zA-Z0-9_%.]+$")
if nStartPos then
sLine = sLine:sub(nStartPos)
end
if #sLine > 0 then
local results = textutils.complete(sLine, env)
if #results == 0 then
-- setError('No completions available')
elseif #results == 1 then
return Util.insertString(oLine, results[1], x + 1)
elseif #results > 1 then
local prefix = results[1]
for n = 1, #results do
local result = results[n]
while #prefix > 0 do
if result:find(prefix, 1, true) == 1 then
break
end
prefix = prefix:sub(1, #prefix - 1)
end
end
if #prefix > 0 then
return Util.insertString(oLine, prefix, x + 1)
else
-- setStatus('Too many results')
end
end
end
return oLine
end
2016-12-18 19:38:48 +00:00
function page:eventHandler(event)
2016-12-11 19:24:52 +00:00
if event.type == 'global' then
2016-12-27 03:26:43 +00:00
self:setPrompt('', true)
2017-10-08 21:45:01 +00:00
self:executeStatement('_G')
2016-12-11 19:24:52 +00:00
command = nil
elseif event.type == 'local' then
2016-12-27 03:26:43 +00:00
self:setPrompt('', true)
2017-10-08 21:45:01 +00:00
self:executeStatement('_ENV')
2016-12-11 19:24:52 +00:00
command = nil
2016-12-27 03:26:43 +00:00
elseif event.type == 'autocomplete' then
local sz = #self.prompt.value
local pos = self.prompt.pos
self:setPrompt(autocomplete(sandboxEnv, self.prompt.value, self.prompt.pos))
self.prompt:setPosition(pos + #self.prompt.value - sz)
self.prompt:updateCursor()
2016-12-11 19:24:52 +00:00
elseif event.type == 'device' then
2017-04-01 23:21:49 +00:00
if not _G.device then
2017-10-08 21:45:01 +00:00
sandboxEnv.device = Peripheral.getList()
2017-04-01 23:21:49 +00:00
end
2016-12-27 03:26:43 +00:00
self:setPrompt('device', true)
2016-12-11 19:24:52 +00:00
self:executeStatement('device')
elseif event.type == 'history_back' then
2017-10-03 04:50:54 +00:00
local value = history:back()
2016-12-11 19:24:52 +00:00
if value then
self:setPrompt(value)
end
elseif event.type == 'history_forward' then
2017-10-03 04:50:54 +00:00
self:setPrompt(history:forward() or '')
2016-12-11 19:24:52 +00:00
elseif event.type == 'clear_prompt' then
self:setPrompt('')
2017-10-03 04:50:54 +00:00
history:reset()
2016-12-11 19:24:52 +00:00
elseif event.type == 'command_enter' then
local s = tostring(self.prompt.value)
if #s > 0 then
2017-10-03 04:50:54 +00:00
history:add(s)
history:back()
2016-12-11 19:24:52 +00:00
self:executeStatement(s)
else
local t = { }
for k = #history.entries, 1, -1 do
table.insert(t, {
name = #t + 1,
value = history.entries[k],
isHistory = true,
pos = k,
})
end
2017-10-03 04:50:54 +00:00
history:reset()
2016-12-11 19:24:52 +00:00
command = nil
self.grid:setValues(t)
self.grid:setIndex(1)
self.grid:adjustWidth()
self:draw()
end
return true
else
return UI.Page.eventHandler(self, event)
end
return true
end
2016-12-18 19:38:48 +00:00
function page:setResult(result)
2016-12-11 19:24:52 +00:00
local t = { }
local function safeValue(v)
2017-10-08 21:45:01 +00:00
if type(v) == 'string' or type(v) == 'number' then
2016-12-11 19:24:52 +00:00
return v
end
return tostring(v)
end
if type(result) == 'table' then
for k,v in pairs(result) do
local entry = {
name = safeValue(k),
rawName = k,
value = safeValue(v),
rawValue = v,
}
if type(v) == 'table' then
if Util.size(v) == 0 then
entry.value = 'table: (empty)'
2017-10-08 21:45:01 +00:00
else
2017-10-11 15:37:52 +00:00
entry.value = tostring(v)
2016-12-11 19:24:52 +00:00
end
end
table.insert(t, entry)
end
else
table.insert(t, {
name = type(result),
value = tostring(result),
rawValue = result,
})
end
self.grid:setValues(t)
self.grid:setIndex(1)
self.grid:adjustWidth()
self:draw()
end
2016-12-18 19:38:48 +00:00
function page.grid:eventHandler(event)
2016-12-11 19:24:52 +00:00
local entry = self:getSelected()
local function commandAppend()
if entry.isHistory then
2017-10-03 04:50:54 +00:00
--history.setPosition(entry.pos)
2016-12-11 19:24:52 +00:00
return entry.value
end
if type(entry.rawValue) == 'function' then
if command then
return command .. '.' .. entry.name .. '()'
end
return entry.name .. '()'
end
if command then
if type(entry.rawName) == 'number' then
return command .. '[' .. entry.name .. ']'
end
2017-10-08 21:45:01 +00:00
if entry.name:match("%W") or
2016-12-11 19:24:52 +00:00
entry.name:sub(1, 1):match("%d") then
return command .. "['" .. tostring(entry.name) .. "']"
end
return command .. '.' .. entry.name
end
return entry.name
end
if event.type == 'grid_focus_row' then
if self.focused then
2016-12-18 19:38:48 +00:00
page:setPrompt(commandAppend())
2016-12-11 19:24:52 +00:00
end
elseif event.type == 'grid_select' then
2016-12-18 19:38:48 +00:00
page:setPrompt(commandAppend(), true)
page:executeStatement(commandAppend())
elseif event.type == 'copy' then
if entry then
clipboard.setData(entry.rawValue)
end
2016-12-11 19:24:52 +00:00
else
2017-10-03 04:50:54 +00:00
return UI.ScrollingGrid.eventHandler(self, event)
2016-12-11 19:24:52 +00:00
end
return true
end
2016-12-18 19:38:48 +00:00
function page:rawExecute(s)
2017-10-03 04:50:54 +00:00
local fn, m = load('return _echo(' ..s.. ');', 'lua', nil, sandboxEnv)
2017-09-27 19:42:40 +00:00
if fn then
m = { pcall(fn) }
fn = table.remove(m, 1)
if #m == 1 then
m = m[1]
end
return fn, m
2016-12-11 19:24:52 +00:00
end
2017-10-03 04:50:54 +00:00
fn, m = load(s, 'lua', nil, sandboxEnv)
2016-12-11 19:24:52 +00:00
if fn then
fn, m = pcall(fn)
end
return fn, m
end
2016-12-18 19:38:48 +00:00
function page:executeStatement(statement)
2016-12-11 19:24:52 +00:00
command = statement
local s, m = self:rawExecute(command)
if s and m then
self:setResult(m)
else
self.grid:setValues({ })
self.grid:draw()
if m then
self.notification:error(m, 5)
end
end
end
2016-12-23 04:22:04 +00:00
local args = { ... }
if args[1] then
2016-12-18 19:38:48 +00:00
command = 'args[1]'
2016-12-23 04:22:04 +00:00
sandboxEnv.args = args
page:setResult(args[1])
2016-12-18 19:38:48 +00:00
end
UI:setPage(page)
2016-12-11 19:24:52 +00:00
Event.pullEvents()
UI.term:reset()