Skip to content

Instantly share code, notes, and snippets.

@hugeblank
Last active January 31, 2019 20:52
Show Gist options
  • Save hugeblank/771e752c05a0f5493d5f5b3f2031b9b0 to your computer and use it in GitHub Desktop.
Save hugeblank/771e752c05a0f5493d5f5b3f2031b9b0 to your computer and use it in GitHub Desktop.
A modified, more advanced form of the read() computercraft function.
--[[
Features:
- All arguments are put into a table, so that they don't have to be put in any particular order, like how it is in read().
- Type error protection has been condensed into a more understandable form.
- Adds a few arguments, including those from read(), and others that would never make it into read():
- replaceChar (_sReplaceChar)
- history (_tHistory)
- complete (_fnComplete)
- prefix (_sDefault)
- limit (number): Size of the input box, defaults to the end of the screen
- newline (boolean): If set to false will remove the newline read appends
- completeBGColor (number): Changes the Background color of the suggested completion values
- completeTextColor (number): Changes the Text color of the suggested completion values
- filter (function): takes the current input string and feeds it to a function. The function can then manipulate the string and return it.
- writer (function): changes the function that is used to write to the screen.
- customkeys (table): a table consisting of all the "special" keys, any and all of which can now be redirected to any other key. Keys that can be redirected (if not redirected, their default is just the key name, prefixed by "keys."):
- enter
- up
- down
- left
- right
- backspace
- home
- delete
- tab
- end (["end"])
]]
function prompt( _tOptions )
local tVerify = {replaceChar = "string", history = "table", complete = "function", prefix = "string", limit = "number", newline = "boolean", completeBGColor = "number", completeTextColor = "number", filter = "function", customkeys = "table", writer = "function"}
if not _tOptions then
_tOptions = {}
end
for k, v in pairs(tVerify) do
if _tOptions[k] ~= nil and type( _tOptions[k] ) ~= v then
error( "bad argument " .. k .. " (expected " .. v .. " got ".. type( _tOptions[k] ) .. ")", 2)
end
end
term.setCursorBlink( true )
local sLine
if type( _tOptions.prefix ) == "string" then
sLine = _tOptions.prefix
else
sLine = ""
end
local nhistoryPos
local nPos = #sLine
if _tOptions.replaceChar then
_tOptions.replaceChar = string.sub( _tOptions.replaceChar, 1, 1 )
end
if not _tOptions.customkeys then
_tOptions.customkeys = {}
end
local tCustomKeyNames = {enter = keys.enter, up = keys.up, down = keys.down, left = keys.left, right = keys.right, backspace = keys.backspace, home = keys.home, delete = keys.delete, tab = keys.tab, ["end"] = keys["end"]}
for k, v in pairs(tCustomKeyNames) do
if not _tOptions.customkeys[k] then
_tOptions.customkeys[k] = v
end
end
local tCompletions
local nCompletion
local function recomplete()
if _tOptions.complete and nPos == string.len(sLine) then
tCompletions = _tOptions.complete( sLine )
if tCompletions and #tCompletions > 0 then
nCompletion = 1
else
nCompletion = nil
end
else
tCompletions = nil
nCompletion = nil
end
end
local function uncomplete()
tCompletions = nil
nCompletion = nil
end
local sx = term.getCursorPos()
local w
if not _tOptions.limit then
w = term.getSize()
else
w = _tOptions.limit+sx
end
local writeFunc = term.write
if _tOptions.writer then
writeFunc = _tOptions.writer
end
local function redraw( _bClear )
local nScroll = 0
if sx + nPos >= w then
nScroll = (sx + nPos) - w
end
local cx,cy = term.getCursorPos()
term.setCursorPos( sx, cy )
local sReplace = (_bClear and " ") or _tOptions.replaceChar
if sReplace then
writeFunc( string.sub( string.rep( sReplace, math.max( string.len(sLine) + 1, 0 ) ), nScroll + 1, nScroll + w ) )
else
writeFunc( string.sub( sLine, nScroll + 1, nScroll + w ) )
end
if nCompletion then
local sCompletion = tCompletions[ nCompletion ]
local oldText, oldBg
if not _bClear then
oldText = term.getTextColor()
oldBg = term.getBackgroundColor()
if not _tOptions.completeTextColor then
term.setTextColor( colors.white )
else
term.setTextColor( _tOptions.completeTextColor )
end
if not _tOptions.completeBGColor then
term.setBackgroundColor( colors.gray )
else
term.setBackgroundColor( _tOptions.completeBGColor )
end
end
if sReplace then
writeFunc( string.rep( sReplace, string.len( sCompletion ) ) )
else
writeFunc( sCompletion )
end
if not _bClear then
term.setTextColor( oldText )
term.setBackgroundColor( oldBg )
end
end
term.setCursorPos( sx + nPos - nScroll, cy )
end
local function clear()
redraw( true )
end
recomplete()
redraw()
local function acceptCompletion()
if nCompletion then
-- Clear
clear()
-- Find the common prefix of all the other suggestions which start with the same letter as the current one
local sCompletion = tCompletions[ nCompletion ]
sLine = sLine .. sCompletion
nPos = string.len( sLine )
-- Redraw
recomplete()
redraw()
end
end
while true do
local sEvent, param = os.pullEvent()
if sEvent == "char" then
-- Typed key
clear()
sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
nPos = nPos + 1
recomplete()
redraw()
elseif sEvent == "paste" then
-- Pasted text
clear()
sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
nPos = nPos + string.len( param )
recomplete()
redraw()
elseif sEvent == "key" then
if _tOptions.customkeys.enter == param then
-- Enter
if nCompletion then
clear()
uncomplete()
redraw()
end
break
elseif _tOptions.customkeys.left == param then
-- Left
if nPos > 0 then
clear()
nPos = nPos - 1
recomplete()
redraw()
end
elseif _tOptions.customkeys.right == param then
-- Right
if nPos < string.len(sLine) then
-- Move right
clear()
nPos = nPos + 1
recomplete()
redraw()
else
-- Accept autocomplete
acceptCompletion()
end
elseif _tOptions.customkeys.up == param or _tOptions.customkeys.down == param then
-- Up or down
if nCompletion then
-- Cycle completions
clear()
if _tOptions.customkeys.up == param then
nCompletion = nCompletion - 1
if nCompletion < 1 then
nCompletion = #tCompletions
end
elseif _tOptions.customkeys.down == param then
nCompletion = nCompletion + 1
if nCompletion > #tCompletions then
nCompletion = 1
end
end
redraw()
elseif _tOptions.history then
-- Cycle history
clear()
if _tOptions.customkeys.up == param then
-- Up
if nhistoryPos == nil then
if #_tOptions.history > 0 then
nhistoryPos = #_tOptions.history
end
elseif nhistoryPos > 1 then
nhistoryPos = nhistoryPos - 1
end
elseif _tOptions.customkeys.down == param then
-- Down
if nhistoryPos == #_tOptions.history then
nhistoryPos = nil
elseif nhistoryPos ~= nil then
nhistoryPos = nhistoryPos + 1
end
end
if nhistoryPos then
sLine = _tOptions.history[nhistoryPos]
nPos = string.len( sLine )
else
sLine = ""
nPos = 0
end
uncomplete()
redraw()
end
elseif _tOptions.customkeys.backspace == param then
-- Backspace
if nPos > 0 then
clear()
sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 )
nPos = nPos - 1
recomplete()
redraw()
end
elseif _tOptions.customkeys.home == param then
-- Home
if nPos > 0 then
clear()
nPos = 0
recomplete()
redraw()
end
elseif _tOptions.customkeys.delete == param then
-- Delete
if nPos < string.len(sLine) then
clear()
sLine = string.sub( sLine, 1, nPos ) .. string.sub( sLine, nPos + 2 )
recomplete()
redraw()
end
elseif _tOptions.customkeys["end"] == param then
-- End
if nPos < string.len(sLine ) then
clear()
nPos = string.len(sLine)
recomplete()
redraw()
end
elseif _tOptions.customkeys.tab == param then
-- Tab (accept autocomplete)
acceptCompletion()
end
elseif sEvent == "term_resize" then
-- Terminal resized
if not _tOptions.limit then
w = term.getSize()
else
w = _tOptions.limit
end
redraw()
end
if _tOptions.filter then
-- Filter out all unwanted characters/strings using a function defined by the user
local sPreFilterLine = sLine
sLine = _tOptions.filter( sLine )
if string.len( sPreFilterLine ) ~= string.len( sLine ) then
local sPreClearLine = sLine
sLine = sPreFilterLine
clear()
sLine = sPreClearLine
end
if not sLine then
sLine = sPreFilterLine
else
if nPos >= ( string.len( sPreFilterLine ) - string.len( sLine ) ) then
nPos = nPos - ( string.len( sPreFilterLine ) - string.len( sLine ) )
else
nPos = 0
end
end
redraw()
end
end
local cx, cy = term.getCursorPos()
term.setCursorBlink( false )
if _tOptions.newline == nil or _tOptions.newline == true then
term.setCursorPos( 1, cy + 1)
end
return sLine
end
return prompt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment