Last active
August 29, 2015 13:58
-
-
Save CoderPuppy/10031323 to your computer and use it in GitHub Desktop.
Doggish Shell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| local styles | |
| if term.isColor() then | |
| styles = { | |
| prompt = { | |
| text = colors.yellow; | |
| back = colors.black; | |
| }; | |
| input = { | |
| text = colors.white; | |
| back = colors.black; | |
| }; | |
| suggestion = { | |
| text = colors.grey; | |
| back = colors.black; | |
| }; | |
| } | |
| else | |
| styles = { | |
| } | |
| end | |
| local function setStyle(style) | |
| term.setTextColor(style.text) | |
| term.setBackgroundColor(style.back) | |
| end | |
| local function tokenify(parts) | |
| local tokens = {} | |
| for _, part in ipairs(parts) do | |
| -- if part.text ~= '' then | |
| tokens[#tokens + 1] = part.text | |
| -- end | |
| end | |
| return tokens | |
| end | |
| local function partify(tokens) | |
| local parts = {} | |
| local i = 0 | |
| for _, token in ipairs(tokens) do | |
| parts[#parts + 1] = { | |
| text = token; | |
| offset = i; | |
| } | |
| i = i + #token + 1 | |
| end | |
| return parts | |
| end | |
| local function tokenizedRead(opts) | |
| if type(opts) ~= 'table' then | |
| opts = {} | |
| end | |
| local curX, curY = term.getCursorPos() | |
| if type(opts.x) ~= 'number' then | |
| opts.x = curX | |
| end | |
| if type(opts.y) ~= 'number' then | |
| opts.y = curY | |
| end | |
| if type(opts.length) ~= 'number' then | |
| opts.length = term.getSize() - opts.x | |
| end | |
| if type(opts.history) ~= 'table' then | |
| opts.history = {} | |
| end | |
| if type(opts.completer) ~= 'function' then | |
| opts.completer = function(tokens, tokenIndex) | |
| return {} | |
| end | |
| end | |
| if type(opts.style) ~= 'table' then | |
| opts.style = { | |
| text = colors.white; | |
| back = colors.black; | |
| } | |
| end | |
| local pos = 0 | |
| local partIndex = 1 | |
| local parts = {{ | |
| text = ''; | |
| offset = 0; | |
| }} | |
| local part = parts[partIndex] | |
| local completions = {} | |
| local completionIndex = 1 | |
| local oldCompletions = completions | |
| local oldCompletionIndex = 1 | |
| local function updateCompletions() | |
| oldCompletions = completions | |
| oldCompletionIndex = completionIndex | |
| local tokens = tokenify(parts) | |
| completions = opts.completer(tokens, partIndex) | |
| completionIndex = partIndex | |
| end | |
| local function redraw() | |
| local scroll = 0 | |
| if opts.x + pos + part.offset >= opts.length then | |
| scroll = (opts.x + pos + part.offset) - opts.length | |
| end | |
| setStyle(opts.style) | |
| -- Clear the area | |
| term.setCursorPos(opts.x, opts.y) | |
| term.write(string.rep(" ", opts.length)) | |
| term.setCursorPos(opts.x, opts.y) | |
| for i, part in ipairs(parts) do | |
| term.write(part.text) | |
| if i < #parts then | |
| term.write(' ') | |
| end | |
| end | |
| if oldCompletions ~= completions or oldCompletionIndex ~= partIndex then | |
| local x = opts.x + parts[oldCompletionIndex].offset | |
| local y = opts.y + 1 | |
| for i, completion in ipairs(oldCompletions) do | |
| term.setCursorPos(x, y) | |
| term.write(string.rep(' ', #part.text)) | |
| term.setCursorPos(x, y) | |
| term.write(string.rep(' ', #completion)) | |
| y = y + 1 | |
| end | |
| oldCompletions = completions | |
| oldCompletionIndex = completionIndex | |
| end | |
| do | |
| local x = opts.x + parts[completionIndex].offset | |
| local y = opts.y + 1 | |
| for i, completion in ipairs(completions) do | |
| term.setCursorPos(x, y) | |
| term.write(string.rep(' ', #part.text)) | |
| term.setCursorPos(x, y) | |
| term.write(completion) | |
| y = y + 1 | |
| end | |
| end | |
| term.setCursorBlink(true) | |
| term.setCursorPos(opts.x + pos + part.offset - scroll, opts.y) | |
| end | |
| updateCompletions() | |
| redraw() | |
| while true do | |
| local ev = {os.pullEvent()} | |
| if ev[1] == 'char' then | |
| if ev[2] == ' ' then | |
| if #part.text > 0 then | |
| local newPart = { | |
| text = part.text:sub(pos + 1); | |
| offset = part.offset + pos + 1; | |
| } | |
| part.text = part.text:sub(1, pos) | |
| table.insert(parts, partIndex + 1, newPart) | |
| partIndex = partIndex + 1 | |
| part = parts[partIndex] | |
| updateCompletions() | |
| end | |
| else | |
| part.text = part.text:sub(1, pos) .. ev[2] .. part.text:sub(pos + 1) | |
| pos = pos + 1 | |
| updateCompletions() | |
| end | |
| elseif ev[1] == 'key' then | |
| if ev[2] == keys.backspace then | |
| if pos == 0 then | |
| if partIndex > 1 then | |
| local prevPart = parts[partIndex - 1] | |
| prevPart.text = prevPart.text .. part.text | |
| table.remove(parts, partIndex) | |
| pos = #prevPart.text | |
| partIndex = partIndex - 1 | |
| part = parts[partIndex] | |
| updateCompletions() | |
| end | |
| else | |
| part.text = part.text:sub(1, pos - 1) .. part.text:sub(pos + 1) | |
| updateCompletions() | |
| end | |
| elseif ev[2] == keys.delete then | |
| if pos == #part.text then | |
| if partIndex < #parts then | |
| local nextPart = parts[partIndex + 1] | |
| part.text = part.text .. nextPart.text | |
| table.remove(parts, partIndex + 1) | |
| updateCompletions() | |
| end | |
| else | |
| part.text = part.text:sub(1, pos) .. part.text:sub(pos + 2) | |
| updateCompletions() | |
| end | |
| elseif ev[2] == keys.left then | |
| if pos == 0 then | |
| pos = #parts[partIndex].text | |
| partIndex = partIndex - 1 | |
| part = parts[partIndex] | |
| updateCompletions() | |
| else | |
| pos = pos - 1 | |
| end | |
| elseif ev[2] == keys.right then | |
| if pos == #part.text then | |
| pos = 0 | |
| partIndex = partIndex + 1 | |
| part = parts[partIndex] | |
| updateCompletions() | |
| else | |
| pos = pos + 1 | |
| end | |
| elseif ev[2] == keys.home then | |
| pos = 0 | |
| partIndex = 1 | |
| part = parts[partIndex] | |
| updateCompletions() | |
| elseif ev[2] == keys['end'] then | |
| partIndex = #parts | |
| part = parts[partIndex] | |
| pos = #part.text | |
| updateCompletions() | |
| elseif ev[2] == keys.enter then | |
| break | |
| elseif ev[2] == keys.tab then | |
| if #completions > 0 then | |
| oldCompletions = completions | |
| completions = {} | |
| for i, completion in ipairs(oldCompletions) do | |
| if i ~= 1 then | |
| completions[#completions + 1] = completion | |
| end | |
| end | |
| completions[#completions + 1] = part.text | |
| part.text = oldCompletions[1] | |
| pos = #part.text | |
| -- else | |
| -- updateCompletions() | |
| end | |
| end | |
| end | |
| if pos < 0 then | |
| pos = 0 | |
| end | |
| while pos > #part.text do | |
| if partIndex == #parts then | |
| pos = #part.text | |
| else | |
| partIndex = partIndex + 1 | |
| part = parts[partIndex] | |
| end | |
| end | |
| -- for i = 6, term.getSize() do | |
| -- term.setCursorPos(1, i) | |
| -- term.clearLine() | |
| -- end | |
| -- term.setCursorPos(1, 6) | |
| -- write(textutils.serialize({ | |
| -- parts = parts; | |
| -- -- part = part; | |
| -- partIndex = partIndex; | |
| -- pos = pos; | |
| -- completions = completions; | |
| -- completionIndex = completionIndex; | |
| -- })) | |
| redraw() | |
| end | |
| term.setCursorBlink(false) | |
| term.setCursorPos(opts.x, opts.y + 1) | |
| if term.getCursorPos() >= term.getSize() then | |
| term.scroll(-1) | |
| end | |
| return tokenify(parts) | |
| end | |
| local function completer(tokens, index) | |
| -- return {'hi', 'test', 'foo', 'bar', 'baz'} | |
| if index == 1 then | |
| local programs = shell.programs() | |
| local completions = {} | |
| for _, program in ipairs(programs) do | |
| if program:sub(1, #tokens[index]) == tokens[index] then | |
| completions[#completions + 1] = program | |
| end | |
| end | |
| return completions | |
| else | |
| return fs.find(tokens[index] .. '*') | |
| end | |
| end | |
| setStyle(styles.prompt) | |
| print('Doggish Shell') | |
| local history = {} | |
| while true do | |
| setStyle(styles.prompt) | |
| term.setCursorPos(1, select(2, term.getCursorPos())) | |
| write(shell.dir() .. '> ') | |
| local input = tokenizedRead({ | |
| history = history; | |
| completer = completer; | |
| style = styles.input; | |
| }) | |
| history[#history + 1] = input | |
| -- print(textutils.serialize(input)) | |
| shell.run(unpack(input)) | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment