summaryrefslogtreecommitdiffstatshomepage
path: root/plugins/console/init.lua
diff options
context:
space:
mode:
author cracyc <cracyc@users.noreply.github.com>2017-05-11 17:28:14 -0500
committer cracyc <cracyc@users.noreply.github.com>2017-05-13 16:57:47 -0500
commit4ebc18aeec6929bd1396daef1289df9821422834 (patch)
tree759538020e787b21eb45113acb485c628185871b /plugins/console/init.lua
parent3553a7bf422d50d331c2cf59c9405f190be41f28 (diff)
linenoise: replace linenoise-ng with a different port that is simpler and uses a different UTF8 parser [Carl]
plugins/console: better completions [Carl]
Diffstat (limited to 'plugins/console/init.lua')
-rw-r--r--plugins/console/init.lua174
1 files changed, 139 insertions, 35 deletions
diff --git a/plugins/console/init.lua b/plugins/console/init.lua
index c27e55164bc..1218cba914f 100644
--- a/plugins/console/init.lua
+++ b/plugins/console/init.lua
@@ -1,5 +1,6 @@
--- license:BSD-3-Clause
--- copyright-holders:Carl
+-- license:MIT
+-- copyright-holders:Carl, Patrick Rapin, Reuben Thomas
+-- completion from https://github.com/rrthomas/lua-rlcompleter
local exports = {}
exports.name = "console"
exports.version = "0.0.1"
@@ -14,6 +15,7 @@ function console.startplugin()
local started = false
local ln = require("linenoise")
local preload = false
+ local matches = {}
print(" _/ _/ _/_/ _/ _/ _/_/_/_/");
print(" _/_/ _/_/ _/ _/ _/_/ _/_/ _/ ");
print(" _/ _/ _/ _/_/_/_/ _/ _/ _/ _/_/_/ ");
@@ -27,13 +29,80 @@ function console.startplugin()
ln.historysetmaxlen(10)
local scr = [[
local ln = require('linenoise')
-ln.setcompletion(function(c, str)
- status = str
+ln.setcompletion(function(c, str, pos)
+ status = str .. "\x01" .. tostring(pos)
yield()
- status:gsub('[^,]*', function(s) if s ~= '' then ln.addcompletion(c, s) end end)
+ ln.addcompletion(c, status:match("([^\x01]*)\x01(.*)"))
end)
return ln.linenoise('\x1b[1;36m[MAME]\x1b[0m> ')
]]
+ local keywords = {
+ 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for',
+ 'function', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
+ 'return', 'then', 'true', 'until', 'while'
+ }
+
+ -- Main completion function. It evaluates the current sub-expression
+ -- to determine its type. Currently supports tables fields, global
+ -- variables and function prototype completion.
+ local function contextual_list(expr, sep, str, word)
+ local function add(value)
+ value = tostring(value)
+ if value:match("^" .. word) then
+ matches[#matches + 1] = value
+ end
+ end
+
+ -- This function is called in a context where a keyword or a global
+ -- variable can be inserted. Local variables cannot be listed!
+ local function add_globals()
+ for _, k in ipairs(keywords) do
+ add(k)
+ end
+ for k in pairs(_G) do
+ add(k)
+ end
+ end
+
+ if expr and expr ~= "" then
+ local v = loadstring("return " .. expr)
+ if v then
+ err, v = pcall(v)
+ if (not err) or (not v) then
+ add_globals()
+ return
+ end
+ local t = type(v)
+ if sep == '.' or sep == ':' then
+ if t == 'table' then
+ for k, v in pairs(v) do
+ if type(k) == 'string' and (sep ~= ':' or type(v) == "function") then
+ add(k)
+ end
+ end
+ elseif t == 'userdata' then
+ for k, v in pairs(getmetatable(v)) do
+ if type(k) == 'string' and (sep ~= ':' or type(v) == "function") then
+ add(k)
+ end
+ end
+ end
+ elseif sep == '[' then
+ if t == 'table' then
+ for k in pairs(v) do
+ if type(k) == 'number' then
+ add(k .. "]")
+ end
+ end
+ if word ~= "" then add_globals() end
+ end
+ end
+ end
+ end
+ if #matches == 0 then
+ add_globals()
+ end
+ end
local function find_unmatch(str, openpar, pair)
local done = false
@@ -56,46 +125,81 @@ return ln.linenoise('\x1b[1;36m[MAME]\x1b[0m> ')
return str
end
- local function get_completions(str)
- local comps = ","
- local rest, dot, last = str:match("(.-)([.:]?)([^.:]*)$")
- str = find_unmatch(str, "%(", "%b()")
- str = find_unmatch(str, "%[", "%b[]")
- local table = str:match("([%w_%.:%(%)%[%]]-)[:.][%w_]*$")
- local err
- if rest == "" or not table then
- if dot == "" then
- table = "_G"
+ -- This complex function tries to simplify the input line, by removing
+ -- literal strings, full table constructors and balanced groups of
+ -- parentheses. Returns the sub-expression preceding the word, the
+ -- separator item ( '.', ':', '[', '(' ) and the current string in case
+ -- of an unfinished string literal.
+ local function simplify_expression(expr, word)
+ -- Replace annoying sequences \' and \" inside literal strings
+ expr = expr:gsub("\\(['\"])", function (c)
+ return string.format("\\%03d", string.byte(c))
+ end)
+ local curstring
+ -- Remove (finished and unfinished) literal strings
+ while true do
+ local idx1, _, equals = expr:find("%[(=*)%[")
+ local idx2, _, sign = expr:find("(['\"])")
+ if idx1 == nil and idx2 == nil then
+ break
+ end
+ local idx, startpat, endpat
+ if (idx1 or math.huge) < (idx2 or math.huge) then
+ idx, startpat, endpat = idx1, "%[" .. equals .. "%[", "%]" .. equals .. "%]"
else
- return comps
+ idx, startpat, endpat = idx2, sign, sign
end
- end
- err, tablef = pcall(load("return " .. table))
- if (not err) or (not tablef) then
- return comps
- end
- rest = rest .. dot
- if type(tablef) == 'table' then
- for k, v in pairs(tablef) do
- if k:match("^" .. last) then
- comps = comps .. "," .. rest .. k
- end
+ if expr:sub(idx):find("^" .. startpat .. ".-" .. endpat) then
+ expr = expr:gsub(startpat .. "(.-)" .. endpat, " STRING ")
+ else
+ expr = expr:gsub(startpat .. "(.*)", function (str)
+ curstring = str
+ return "(CURSTRING "
+ end)
end
end
- if type(tablef) == "userdata" then
- local tablef = getmetatable(tablef)
- for k, v in pairs(tablef) do
- if k:match("^" .. last) then
- comps = comps .. "," .. rest .. k
- end
+ -- crop string at unmatched open paran
+ expr = find_unmatch(expr, "%(", "%b()")
+ expr = find_unmatch(expr, "%[", "%b[]")
+ --expr = expr:gsub("%b()"," PAREN ") -- Remove groups of parentheses
+ expr = expr:gsub("%b{}"," TABLE ") -- Remove table constructors
+ -- Avoid two consecutive words without operator
+ expr = expr:gsub("(%w)%s+(%w)","%1|%2")
+ expr = expr:gsub("%s+", "") -- Remove now useless spaces
+ -- This main regular expression looks for table indexes and function calls.
+ return curstring, expr:match("([%.:%w%(%)%[%]_]-)([:%.%[%(])" .. word .. "$")
+ end
+
+ local function get_completions(line, endpos)
+ matches = {}
+ local endstr = line:sub(endpos + 1, -1)
+ line = line:sub(1, endpos)
+ endstr = endstr or ""
+ local start, word = line:match("^(.*[ \t\n\"\\'><=;:%+%-%*/%%^~#{}%(%)%[%].,])(.-)$")
+ if not start then
+ start = ""
+ word = word or line
+ else
+ word = word or ""
+ end
+
+ local str, expr, sep = simplify_expression(line, word)
+ contextual_list(expr, sep, str, word)
+ if #matches > 1 then
+ print("\n")
+ for k, v in pairs(matches) do
+ print(v)
end
+ return "\x01" .. "-1"
+ elseif #matches == 1 then
+ return start .. matches[1] .. endstr .. "\x01" .. (#start + #matches[1])
end
- return comps
+ return "\x01" .. "-1"
end
emu.register_periodic(function()
if conth.yield then
- conth:continue(get_completions(conth.result))
+ conth:continue(get_completions(conth.result:match("([^\x01]*)\x01(.*)")))
return
elseif conth.busy then
return