diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua b/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua index 088bf82a2..36810fd56 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua @@ -1,3 +1,322 @@ +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"} + 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 w + if not _tOptions.limit then + w = term.getSize() + else + w = _tOptions.limit + end + + local sx = term.getCursorPos() + + 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 + term.write( string.sub( string.rep( sReplace, math.max( string.len(sLine) + nScroll, 0 ) ), nScroll + 1, nScroll + w ) ) + else + term.write( 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 + term.write( string.rep( sReplace, string.len( sCompletion ) ) ) + else + term.write( 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( w + 1, cy ) + print() + end + + return sLine +end function slowWrite( sText, nRate ) if nRate ~= nil and type( nRate ) ~= "number" then