summaryrefslogtreecommitdiff
path: root/lib/lua-repl/repl/plugins/completion.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lua-repl/repl/plugins/completion.lua')
-rw-r--r--lib/lua-repl/repl/plugins/completion.lua192
1 files changed, 192 insertions, 0 deletions
diff --git a/lib/lua-repl/repl/plugins/completion.lua b/lib/lua-repl/repl/plugins/completion.lua
new file mode 100644
index 00000000..938c7439
--- /dev/null
+++ b/lib/lua-repl/repl/plugins/completion.lua
@@ -0,0 +1,192 @@
+-- Copyright (c) 2011-2015 Rob Hoelz <rob@hoelz.ro>
+--
+-- Permission is hereby granted, free of charge, to any person obtaining a copy of
+-- this software and associated documentation files (the "Software"), to deal in
+-- the Software without restriction, including without limitation the rights to
+-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+-- the Software, and to permit persons to whom the Software is furnished to do so,
+-- subject to the following conditions:
+--
+-- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+-- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+-- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+-- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+-- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+local utils = require 'repl.utils'
+local getmetatable = getmetatable
+local pairs = pairs
+local sfind = string.find
+local sgmatch = string.gmatch
+local smatch = string.match
+local ssub = string.sub
+local tconcat = table.concat
+local tsort = table.sort
+local type = type
+
+local function isindexable(value)
+ if type(value) == 'table' then
+ return true
+ end
+
+ local mt = getmetatable(value)
+
+ return mt and mt.__index
+end
+
+local function getcompletions(t)
+ local union = {}
+
+ while isindexable(t) do
+ if type(t) == 'table' then
+ -- XXX what about the pairs metamethod in 5.2?
+ -- either we don't care, we implement a __pairs-friendly
+ -- pairs for 5.1, or implement a 'rawpairs' for 5.2
+ for k, v in pairs(t) do
+ if union[k] == nil then
+ union[k] = v
+ end
+ end
+ end
+
+ local mt = getmetatable(t)
+ t = mt and mt.__index or nil
+ end
+
+ return pairs(union)
+end
+
+local function split_ns(expr)
+ if expr == '' then
+ return { '' }
+ end
+
+ local pieces = {}
+
+ -- XXX method calls too (option?)
+ for m in sgmatch(expr, '[^.]+') do
+ pieces[#pieces + 1] = m
+ end
+
+ -- logic for determining whether to pad the matches with the empty
+ -- string (ugly)
+ if ssub(expr, -1) == '.' then
+ pieces[#pieces + 1] = ''
+ end
+
+ return pieces
+end
+
+local function determine_ns(expr)
+ local ns = _G -- XXX what if the REPL lives in a special context? (option?)
+ local pieces = split_ns(expr)
+
+ for index = 1, #pieces - 1 do
+ local key = pieces[index]
+ -- XXX rawget? or regular access? (option?)
+ ns = ns[key]
+
+ if not isindexable(ns) then
+ return {}, '', ''
+ end
+ end
+
+ expr = pieces[#pieces]
+
+ local prefix = ''
+
+ if #pieces > 1 then
+ prefix = tconcat(pieces, '.', 1, #pieces - 1) .. '.'
+ end
+
+ local last_piece = pieces[#pieces]
+
+ local before, after = smatch(last_piece, '(.*):(.*)')
+
+ if before then
+ ns = ns[before] -- XXX rawget
+ prefix = prefix .. before .. ':'
+ expr = after
+ end
+
+ return ns, prefix, expr
+end
+
+local isidentifierchar
+
+do
+ local ident_chars_set = {}
+ -- XXX I think this can be done with isalpha in C...
+ local ident_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.:_0123456789'
+
+ for i = 1, #ident_chars do
+ local char = ssub(ident_chars, i, i)
+ ident_chars_set[char] = true
+ end
+
+ function isidentifierchar(char)
+ return ident_chars_set[char]
+ end
+end
+
+local function extract_innermost_expr(expr)
+ local index = #expr
+
+ while index > 0 do
+ local char = ssub(expr, index, index)
+ if isidentifierchar(char) then
+ index = index - 1
+ else
+ break
+ end
+ end
+
+ index = index + 1
+
+ return ssub(expr, 1, index - 1), ssub(expr, index)
+end
+
+-- XXX is this logic (namely, returning the entire line) too specific to
+-- linenoise?
+function repl:complete(expr, callback)
+ if utils.ends_in_unfinished_string(expr) then
+ return
+ end
+
+ local ns, prefix, path
+
+ prefix, expr = extract_innermost_expr(expr)
+
+ ns, path, expr = determine_ns(expr)
+
+ prefix = prefix .. path
+
+ local completions = {}
+
+ for k, v in getcompletions(ns) do
+ if sfind(k, expr, 1, true) == 1 then
+ local suffix = ''
+ local type = type(v)
+
+ -- XXX this should be optional
+ if type == 'function' then
+ suffix = '('
+ elseif type == 'table' then
+ suffix = '.'
+ end
+
+ completions[#completions + 1] = prefix .. k .. suffix
+ end
+ end
+
+ tsort(completions)
+
+ for _, completion in ipairs(completions) do
+ callback(completion)
+ end
+end
+
+features = 'completion'