From 4ef2e6f22d3629fa57c3e1c2abeed7fdde745419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 4 Aug 2014 22:49:02 +0200 Subject: [PATCH 01/11] Add tab key handler to term.read Adding this handler was necessary to allow creation of auto-complete mechanism --- .../opencomputers/loot/OpenOS/lib/term.lua | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua index bcecef08d..7634e33c7 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua @@ -101,7 +101,7 @@ function term.isAvailable() return component.isAvailable("gpu") and component.isAvailable("screen") end -function term.read(history, dobreak) +function term.read(history, dobreak, hint) checkArg(1, history, "table", "nil") history = history or {} table.insert(history, "") @@ -247,6 +247,18 @@ function term.read(history, dobreak) right(unicode.len(value)) end + local function tab() + if hint then + local after = hint(line()) + if type(after) == "string" then + local _, cby = getCursor() + history[cby] = after + end + redraw() --hint might have printed sth + ende() + end + end + local function onKeyDown(char, code) term.setCursorBlink(false) if code == keyboard.keys.back then @@ -265,6 +277,8 @@ function term.read(history, dobreak) up() elseif code == keyboard.keys.down then down() + elseif code == keyboard.keys.tab then + tab() elseif code == keyboard.keys.enter then local cbx, cby = getCursor() if cby ~= #history then -- bring entry to front From 8eeed80f0c1ebbddf4eee8243fe9aaceec9d535e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 4 Aug 2014 23:16:45 +0200 Subject: [PATCH 02/11] Add autocompletion to shell --- .../opencomputers/loot/OpenOS/bin/sh.lua | 76 ++++++++++++++++++- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua index c35e46249..02c248ac0 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua @@ -148,6 +148,76 @@ end local args, options = shell.parse(...) local history = {} +local lastSearch + +local function drawPrompt() + local foreground = component.gpu.setForeground(0xFF0000) + term.write(expand(os.getenv("PS1") or "$ ")) + component.gpu.setForeground(foreground) +end + +local function getMatchingPrograms(pattern) + local res = {} + for dir in string.gmatch(os.getenv("PATH"), "[a-zA-Z0-9/.]+") do + for file in fs.list(dir) do + if string.match("/" .. file, "/" .. pattern) and file:match("(.+)[.]lua") then + res[#res+1] = file:match("(.+).lua") + end + end + end + return res +end + +local function getMatchingFiles(pattern) + local res = {} + local dir = fs.isDirectory(pattern) and pattern or fs.path(pattern) or "/" + local name = (dir == pattern) and "" or fs.name(pattern) or "" + for file in fs.list(dir) do + if string.match("/" .. file, "/" .. name) then + res[#res+1] = file + end + end + return res +end + +local function hintHandler(line) + local base, space, after = string.match(line, "(.+)(%s)(.+)") + local searchProgram = not base + if not base then + base = "" + after = line or "" + end + if searchProgram then + local matches + if after:find("[/.]") == 1 then + matches = getMatchingFiles(after) + else + matches = getMatchingPrograms(after) + end + if #matches == 1 then + lastSearch = "" + return matches[1] .. " " + end + if lastSearch == line then + term.write("\n") + for _, name in ipairs(matches) do term.write(name .. " ", true)end + term.write("\n")drawPrompt() + end + else + local matches = getMatchingFiles(after) + if #matches == 1 then + lastSearch = "" + return base .. space .. matches[1] .. " " + end + if lastSearch == line then + term.write("\n") + for _, name in ipairs(matches) do term.write(name .. " ", true)end + term.write("\n")drawPrompt() + end + end + lastSearch = line +end + if #args == 0 and (io.input() == io.stdin or options.i) and not options.c then -- interactive shell. while true do @@ -158,10 +228,8 @@ if #args == 0 and (io.input() == io.stdin or options.i) and not options.c then term.clear() end while term.isAvailable() do - local foreground = component.gpu.setForeground(0xFF0000) - term.write(expand(os.getenv("PS1") or "$ ")) - component.gpu.setForeground(foreground) - local command = term.read(history) + drawPrompt() + local command = term.read(history, nil, hintHandler) if not command then term.write("exit\n") return -- eof From 4f91f00b55df85cc97c00178aa79c5c067c03122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 5 Aug 2014 00:18:15 +0200 Subject: [PATCH 03/11] Move hint check to onKeyDown --- .../opencomputers/loot/OpenOS/lib/term.lua | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua index 7634e33c7..66e3dc2c4 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua @@ -248,15 +248,13 @@ function term.read(history, dobreak, hint) end local function tab() - if hint then - local after = hint(line()) - if type(after) == "string" then - local _, cby = getCursor() - history[cby] = after - end - redraw() --hint might have printed sth - ende() + local after = hint(line()) + if type(after) == "string" then + local _, cby = getCursor() + history[cby] = after end + redraw() --hint might have printed sth + ende() end local function onKeyDown(char, code) @@ -277,7 +275,7 @@ function term.read(history, dobreak, hint) up() elseif code == keyboard.keys.down then down() - elseif code == keyboard.keys.tab then + elseif code == keyboard.keys.tab and hint then tab() elseif code == keyboard.keys.enter then local cbx, cby = getCursor() From f39b501b1a860b18df89715da38867769cc03d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 5 Aug 2014 00:22:02 +0200 Subject: [PATCH 04/11] Made getMatchingPrograms more accurate --- src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua index 02c248ac0..e29a9be7b 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua @@ -160,7 +160,7 @@ local function getMatchingPrograms(pattern) local res = {} for dir in string.gmatch(os.getenv("PATH"), "[a-zA-Z0-9/.]+") do for file in fs.list(dir) do - if string.match("/" .. file, "/" .. pattern) and file:match("(.+)[.]lua") then + if string.match(file, "^" .. pattern .. "(.+)[.]lua") then res[#res+1] = file:match("(.+).lua") end end From 47c95a62d8c6e217477df3899fa3d32750cd61e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 5 Aug 2014 03:25:58 +0200 Subject: [PATCH 05/11] Repaired file completion behavior --- .../opencomputers/loot/OpenOS/bin/sh.lua | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua index e29a9be7b..358aa3eff 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua @@ -173,7 +173,7 @@ local function getMatchingFiles(pattern) local dir = fs.isDirectory(pattern) and pattern or fs.path(pattern) or "/" local name = (dir == pattern) and "" or fs.name(pattern) or "" for file in fs.list(dir) do - if string.match("/" .. file, "/" .. name) then + if string.match(file, "^" .. name) then res[#res+1] = file end end @@ -191,13 +191,19 @@ local function hintHandler(line) local matches if after:find("[/.]") == 1 then matches = getMatchingFiles(after) + if #matches == 1 then + lastSearch = "" + local ret = base .. (space or "") .. after .. matches[1]:gsub(after:match("[/]*(%w+)$"),"",1) + return ret:gsub("[^/]$","%1 ") + end else matches = getMatchingPrograms(after) + if #matches == 1 then + lastSearch = "" + return matches[1] .. " " + end end - if #matches == 1 then - lastSearch = "" - return matches[1] .. " " - end + if lastSearch == line then term.write("\n") for _, name in ipairs(matches) do term.write(name .. " ", true)end @@ -207,7 +213,8 @@ local function hintHandler(line) local matches = getMatchingFiles(after) if #matches == 1 then lastSearch = "" - return base .. space .. matches[1] .. " " + local ret = base .. space .. after .. matches[1]:gsub(after:match("[/]*(%w+)$"),"",1) + return ret:gsub("[^/]$","%1 ") end if lastSearch == line then term.write("\n") From 62a507ed87d663b7d91c0349c34ea07ce6fa02be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 5 Aug 2014 14:50:25 +0200 Subject: [PATCH 06/11] Hint function can now return tables and iterators Returning table or iterator will display set of available completion options. When user will return table, he/she should also provide prompt function for (re)drawing prompt if one is used is his/her program --- .../assets/opencomputers/loot/OpenOS/lib/term.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua index 66e3dc2c4..5d823b289 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua @@ -101,7 +101,7 @@ function term.isAvailable() return component.isAvailable("gpu") and component.isAvailable("screen") end -function term.read(history, dobreak, hint) +function term.read(history, dobreak, hint, prompt) checkArg(1, history, "table", "nil") history = history or {} table.insert(history, "") @@ -109,6 +109,10 @@ function term.read(history, dobreak, hint) local scrollX, scrollY = 0, #history - 1 local cursorX = 1 + if type(prompt) == "function" then + pcall(prompt) + end + local function getCursor() return cursorX, 1 + scrollY end @@ -252,8 +256,15 @@ function term.read(history, dobreak, hint) if type(after) == "string" then local _, cby = getCursor() history[cby] = after + elseif type(after) == "table" or type(after) == "function" then + term.write("\n") + for _, v in type(after) == "table" and pairs(after) or (function()return _,after end) do + term.write(name .. " ", true) + end + term.write("\n") + if type(prompt) == "function" then pcall(prompt)end end - redraw() --hint might have printed sth + redraw() ende() end From 977f3bbfbea26a58a6d4e45aabd8e37fa40a5be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 5 Aug 2014 14:58:48 +0200 Subject: [PATCH 07/11] Fix term.lua completion list printing --- .../resources/assets/opencomputers/loot/OpenOS/lib/term.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua index 5d823b289..609f661a2 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua @@ -258,7 +258,7 @@ function term.read(history, dobreak, hint, prompt) history[cby] = after elseif type(after) == "table" or type(after) == "function" then term.write("\n") - for _, v in type(after) == "table" and pairs(after) or (function()return _,after end) do + for _, name in type(after) == "table" and pairs(after) or (function()local _,v pcall(after) return v,v end) do term.write(name .. " ", true) end term.write("\n") From c9d29b87de174fa5e34e9c9193b0f28a023d3175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 5 Aug 2014 15:25:54 +0200 Subject: [PATCH 08/11] Fixed the fix --- .../opencomputers/loot/OpenOS/lib/term.lua | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua index 609f661a2..17c00a1e0 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua @@ -105,14 +105,17 @@ function term.read(history, dobreak, hint, prompt) checkArg(1, history, "table", "nil") history = history or {} table.insert(history, "") + + if type(prompt) == "function" then + pcall(prompt) + elseif type(prompt) == "string" then + term.write(prompt) + end + local offset = term.getCursor() - 1 local scrollX, scrollY = 0, #history - 1 local cursorX = 1 - if type(prompt) == "function" then - pcall(prompt) - end - local function getCursor() return cursorX, 1 + scrollY end @@ -256,13 +259,22 @@ function term.read(history, dobreak, hint, prompt) if type(after) == "string" then local _, cby = getCursor() history[cby] = after - elseif type(after) == "table" or type(after) == "function" then + elseif type(after) == "table" then term.write("\n") - for _, name in type(after) == "table" and pairs(after) or (function()local _,v pcall(after) return v,v end) do + for _, name in pairs(after) do term.write(name .. " ", true) end term.write("\n") - if type(prompt) == "function" then pcall(prompt)end + if type(prompt) == "function" then pcall(prompt) + elseif type(prompt) == "string" then term.write(prompt)end + elseif type(after) == "function" then + term.write("\n") + for name in after do --This is possibly not the best solution + term.write(name .. " ", true) + end + term.write("\n") + if type(prompt) == "function" then pcall(prompt) + elseif type(prompt) == "string" then term.write(prompt)end end redraw() ende() From 61c1f02fe0cc108b5a3240404ad13cc62829e83f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 5 Aug 2014 15:30:00 +0200 Subject: [PATCH 09/11] Update hintHandler Updated hintHandler to return table when tab was dual-tapped and there are multiple possible completions --- .../assets/opencomputers/loot/OpenOS/bin/sh.lua | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua index 358aa3eff..fef601950 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua @@ -204,11 +204,7 @@ local function hintHandler(line) end end - if lastSearch == line then - term.write("\n") - for _, name in ipairs(matches) do term.write(name .. " ", true)end - term.write("\n")drawPrompt() - end + if lastSearch == line then return matches end else local matches = getMatchingFiles(after) if #matches == 1 then @@ -216,11 +212,7 @@ local function hintHandler(line) local ret = base .. space .. after .. matches[1]:gsub(after:match("[/]*(%w+)$"),"",1) return ret:gsub("[^/]$","%1 ") end - if lastSearch == line then - term.write("\n") - for _, name in ipairs(matches) do term.write(name .. " ", true)end - term.write("\n")drawPrompt() - end + if lastSearch == line then return matches end end lastSearch = line end @@ -235,8 +227,7 @@ if #args == 0 and (io.input() == io.stdin or options.i) and not options.c then term.clear() end while term.isAvailable() do - drawPrompt() - local command = term.read(history, nil, hintHandler) + local command = term.read(history, nil, hintHandler, drawPrompt) if not command then term.write("exit\n") return -- eof From c791bcc00cb91eac93091a005ce1667736cac680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 7 Aug 2014 17:13:33 +0200 Subject: [PATCH 10/11] Changed term.read hint behaviour When hint function returns table, It no longer writes it to screen. The result is cached, and user may view hintd by pressing tab key repetitively. --- .../opencomputers/loot/OpenOS/lib/term.lua | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua index 17c00a1e0..27092c9e7 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua @@ -105,17 +105,13 @@ function term.read(history, dobreak, hint, prompt) checkArg(1, history, "table", "nil") history = history or {} table.insert(history, "") - - if type(prompt) == "function" then - pcall(prompt) - elseif type(prompt) == "string" then - term.write(prompt) - end - local offset = term.getCursor() - 1 local scrollX, scrollY = 0, #history - 1 local cursorX = 1 + local hintCache = (type(hint)=="table" and #hint > 1)and hint + local selectedHint = 0 + local function getCursor() return cursorX, 1 + scrollY end @@ -255,45 +251,48 @@ function term.read(history, dobreak, hint, prompt) end local function tab() - local after = hint(line()) - if type(after) == "string" then + if not hintCache then + if type(hint) == "function" then + local h = hint(line()) + if type(h) == "string" then + local _, cby = getCursor() + history[cby] = after + elseif type(h) == "table" and #h > 0 then + hintCache = h + selectedHint = 1 + local _, cby = getCursor() + history[cby] = hintCache[selectedHint] or "" + end + end + else + selectedHint = (selectedHint+1)<=#hintCache and (selectedHint+1) or 1 local _, cby = getCursor() - history[cby] = after - elseif type(after) == "table" then - term.write("\n") - for _, name in pairs(after) do - term.write(name .. " ", true) - end - term.write("\n") - if type(prompt) == "function" then pcall(prompt) - elseif type(prompt) == "string" then term.write(prompt)end - elseif type(after) == "function" then - term.write("\n") - for name in after do --This is possibly not the best solution - term.write(name .. " ", true) - end - term.write("\n") - if type(prompt) == "function" then pcall(prompt) - elseif type(prompt) == "string" then term.write(prompt)end + history[cby] = hintCache[selectedHint] or "" end redraw() ende() end + local function cleanHint() + if type(hint) ~= "table" then + hintCache = nil + end + end + local function onKeyDown(char, code) term.setCursorBlink(false) if code == keyboard.keys.back then - if left() then delete() end + if left() then delete() end cleanHint() elseif code == keyboard.keys.delete then - delete() + delete()cleanHint() elseif code == keyboard.keys.left then left() elseif code == keyboard.keys.right then right() elseif code == keyboard.keys.home then - home() + home()cleanHint() elseif code == keyboard.keys["end"] then - ende() + ende()cleanHint() elseif code == keyboard.keys.up then up() elseif code == keyboard.keys.down then @@ -305,6 +304,7 @@ function term.read(history, dobreak, hint, prompt) if cby ~= #history then -- bring entry to front history[#history] = line() table.remove(history, cby) + cleanHint() end return true, history[#history] .. "\n" elseif keyboard.isControlDown() and code == keyboard.keys.d then @@ -317,6 +317,7 @@ function term.read(history, dobreak, hint, prompt) return true, nil elseif not keyboard.isControl(char) then insert(unicode.char(char)) + cleanHint() end term.setCursorBlink(true) term.setCursorBlink(true) -- force toggle to caret From 9c6bbbef757ca74c8e181a2a9dcec22e29f77b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 7 Aug 2014 17:16:03 +0200 Subject: [PATCH 11/11] Update shell to work with new term.lua --- .../assets/opencomputers/loot/OpenOS/bin/sh.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua index fef601950..2bab3714c 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/sh.lua @@ -207,12 +207,13 @@ local function hintHandler(line) if lastSearch == line then return matches end else local matches = getMatchingFiles(after) - if #matches == 1 then - lastSearch = "" - local ret = base .. space .. after .. matches[1]:gsub(after:match("[/]*(%w+)$"),"",1) - return ret:gsub("[^/]$","%1 ") + + for k in ipairs(matches)do + local ret = base .. space .. after .. (matches[k]):gsub(after:match("[/]*(%w+)$") or "","",1) + matches[k] = ret:gsub("[^/]$","%1 "):gsub("/$","") end - if lastSearch == line then return matches end + + return matches end lastSearch = line end @@ -227,7 +228,8 @@ if #args == 0 and (io.input() == io.stdin or options.i) and not options.c then term.clear() end while term.isAvailable() do - local command = term.read(history, nil, hintHandler, drawPrompt) + drawPrompt() + local command = term.read(history, nil, hintHandler) if not command then term.write("exit\n") return -- eof