Skip to content

Commit

Permalink
luaish: tab-completion of command history
Browse files Browse the repository at this point in the history
  • Loading branch information
stevedonovan committed Dec 4, 2014
1 parent f55ffee commit 12455c0
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 23 deletions.
56 changes: 41 additions & 15 deletions luaish/lua.lua
Expand Up @@ -28,7 +28,7 @@ else
LUA_COPYRIGHT = "Copyright (C) 1994-2011 Lua.org, PUC-Rio"
eof_ender = '<eof>'
end
local EXTRA_COPYRIGHT = "lua.lua (c) David Manura, 2008-08"
local EXTRA_COPYRIGHT = "lua.lua (c) David Manura, 2008-2014"

-- Note: don't allow user scripts to change implementation.
-- Check for globals with "cat lua.lua | luac -p -l - | grep ETGLOBAL"
Expand All @@ -37,7 +37,7 @@ local _G = _G
local assert = assert
local collectgarbage = collectgarbage
local loadfile = loadfile
local loadstring = loadstring or load
local load = load or loadstring
local pcall = pcall
local rawget = rawget
local select = select
Expand Down Expand Up @@ -151,7 +151,7 @@ function dofile(name)
end

local function dostring(s, name)
local f, msg = loadstring(s, name)
local f, msg = load(s, name)
if f then f, msg = docall(f) end
return report(f, msg)
end
Expand All @@ -178,9 +178,15 @@ local function getargs (argv, n)
end

local function get_prompt (firstline)
-- use rawget to play fine with require 'strict'
local pmt = rawget(_G, firstline and "_PROMPT" or "_PROMPT2")
local tp = type(pmt)
local pmt, tp
if using_lsh then
pmt = lsh.get_prompt(firstline)
end
if not pmt then
-- use rawget to play fine with require 'strict'
pmt = rawget(_G, firstline and "_PROMPT" or "_PROMPT2")
end
tp = type(pmt)
if tp == "string" or tp == "number" then
return tostring(pmt)
end
Expand Down Expand Up @@ -217,28 +223,48 @@ local function pushline (firstline)
end
end

-- Try to compile line on the stack as 'return <line>'; on return, stack
--- has either compiled chunk or original line (if compilation failed).
local function addreturn (b)
return load('return '..b,"=stdin")
end

local function loadline ()
local b = pushline(true)
if not b then return -1 end -- no input
-- Read multiple lines until a complete Lua statement
local function multiline (b)
local f, msg
while true do -- repeat until gets a complete line
f, msg = loadstring(b, "=stdin")
f, msg = load(b, "=stdin")
if not incomplete(msg) then break end -- cannot try to add lines?
local b2 = pushline(false)
if not b2 then -- no more input?
return -1
end
b = b .. "\n" .. b2 -- join them
end
return f, msg, b
end

-- Read a line and try to load (compile) it first as an expression (by
-- adding "return " in front of it) and second as a statement. Return
-- the final status of load/call with the resulting function (if any)
-- in the top of the stack. Extension: one can suppress the return
-- by ending the expression with ';'
local function loadline ()
local f,msg
local b = pushline(true)
if not b then return -1 end -- no input
if not b:match ';$' then
f,msg = addreturn(b)
end
if not f then -- 'return ..' did not work
f,msg,b = multiline(b)
end
saveline(b)

return f, msg
end


local function dotty ()
-- Do the REPL
local function doREPL ()
local oldprogname = progname
progname = nil
using_lsh,lsh = pcall(require, 'luaish')
Expand Down Expand Up @@ -392,11 +418,11 @@ else
end

if has.i then
dotty()
doREPL()
elseif script == 0 and not has.e and not has.v then
if lua_stdin_is_tty() then
print_version()
dotty()
doREPL()
else
dofile(nil) -- executes stdin as a file
end
Expand Down
77 changes: 69 additions & 8 deletions luaish/luaish.lua
Expand Up @@ -52,6 +52,8 @@ local function expand_dollar(line,i1)
local f = lsh.file_by_index(name)
if not f then return end
return {prefix..f}
elseif name == '_' then -- last command
return {prefix..(_G['__'] or '')}
else -- environment variable?
prefix = line:sub(1,i1)
return append_candidates(nil,posix.getenv(),prefix,name)
Expand Down Expand Up @@ -99,9 +101,32 @@ local function lua_candidates(line)
if mt and is_pair_iterable(mt.__index) then
append_candidates(res,mt.__index,prefix,last)
end
-- smallest matches first
table.sort(res,function(a,b)
return #a < #b
end)
return res
end

local function command_history_candidates(line)
local esc = at(line,1)
line = line:sub(1)
local cmd = ("grep '^%s' %s/.luai-history"):format(_G.SHELL_ESC,home)
local f = io.popen(cmd,'r')
local matches = {}
for line in f:lines() do
append(matches,line)
end
f:close()
local cc = {}
for i = #matches,1,-1 do
if matches[i]:sub(1,#line) == line then
append(cc,matches[i])
end
end
return cc
end

local function is_directory(path)
return posix.stat(path,'type') == 'directory'
end
Expand All @@ -115,6 +140,9 @@ local function path_candidates(line)
elseif not i1 then
return
end
if line:match '^.[%w_]+$' then
return command_history_candidates(line)
end
front, path = line:sub(1,i1-1), line:sub(i1)
i1 = path:find '[.%w_%-]*$'
if not i1 then return end
Expand All @@ -136,7 +164,7 @@ local function path_candidates(line)
local res = {}
local all = name == ''
for _,f in ipairs(posix.dir(path)) do
if all or f:sub(1,#name)==name then
if not (f=='.' or f=='..') and (all or f:sub(1,#name)==name) then
push(res,front..dpath..f)
end
end
Expand All @@ -147,9 +175,22 @@ local function path_candidates(line)
return res
end

local auto_shell_mode

local function shell_mode(s)
return at(s,1) == _G.SHELL_ESC or auto_shell_mode
end

function lsh.get_prompt(firstline)
if not auto_shell_mode then return nil
else
return '$> '
end
end

local function completion_handler(c,s)
local cc
if at(s,1) == SHELL_ESC then -- shell path completion
if shell_mode(s) then -- shell path completion
cc = path_candidates(s)
if not cc then return end
else -- otherwise Lua...
Expand Down Expand Up @@ -185,7 +226,7 @@ end

function lsh.add_completion(pat,cf)
our_completions[pat] = cf
end
end

local file_list = {}

Expand Down Expand Up @@ -238,7 +279,7 @@ end
local shell_command_handler

function lsh.checkline(b)
if at(b,1) == _G.SHELL_ESC then
if shell_mode(b) then
local err, res = pcall(shell_command_handler,b)
if not err then
print('luaish error',res)
Expand Down Expand Up @@ -294,7 +335,6 @@ lsh.print = function(f,name)
end
end


--- managing directory stack----
local dirstack = {}

Expand Down Expand Up @@ -386,6 +426,12 @@ function lsh.add_alias(name,cmd)
alias[name] = cmd
end

function lsh.show_aliases()
for k,v in pairs(alias) do
print(k,v)
end
end

local function expand_lua_globals(line)
return line:gsub('%$([%w_]+)',function(name)
if name == '_' then name = '__' end
Expand All @@ -397,8 +443,19 @@ local function expand_lua_globals(line)
end

function shell_command_handler (line)
line = line:sub(2)
if not auto_shell_mode then
line = line:sub(2)
end
line = line:gsub ('^%s*','')
if line == "" then
auto_shell_mode = true
return true
elseif line == 'exit' then
if auto_shell_mode then
auto_shell_mode = false
end
return true
end
line = expand_lua_globals(line)
local cmd,args = line:match '^(%S+)%s*(.*)$'
if not args then
Expand Down Expand Up @@ -460,6 +517,10 @@ function shell_command_handler (line)
local cmd = (('%s && echo %s \\"$%s\\" |-lsetenv'):format(args,var,var))
return exec(cmd)
elseif cmd == 'set' then
if args:match '^%s*$' then
lsh.show_aliases()
return true
end
local name,exp = args:match '(%S+)%s+(.+)'
if not name then
print("syntax is 'set name command'")
Expand Down Expand Up @@ -491,14 +552,14 @@ ok,_G.config = pcall(require,'config')
lsh.set_shortcut ('fn', "function ")
lsh.set_shortcut('rt','return')

_G.SHELL_ESC='.'
_G.SHELL_ESC='!'
_G.posix = posix
_G.luaish = lsh -- global for rc file

local data

if not safe_dofile(home..'/.luairc.lua') then
print("customize with ~/.luaric.lua")
print("customize with ~/.luairc.lua")
end
local pfile = home..'/.luai-data'
local chunk = loadfile(pfile)
Expand Down

0 comments on commit 12455c0

Please sign in to comment.