Skip to content

Instantly share code, notes, and snippets.

@CoderPuppy
Last active August 29, 2015 13:58
Show Gist options
  • Select an option

  • Save CoderPuppy/10031323 to your computer and use it in GitHub Desktop.

Select an option

Save CoderPuppy/10031323 to your computer and use it in GitHub Desktop.
Doggish Shell
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