feat: allow runners to specify if they want more input (#162)

* refactor!: make runners require returning a table

allows for more options for runners in the future,
and makes it so that you can avoid passing
certain args more easily.

* feat: allow runners to specify continue in return to prompt for more input

* docs: update changelog

* refactor: reorder returns of handleSh function

* refactor: move out reprompting and runner handling to functions

makes codefactor happy hopefully. this commit includes
a fix to check if after reprompt the user hits ctrl d
and just exits cleanly
lazy-interfaces
sammyette 2022-06-02 19:33:30 -07:00 committed by GitHub
parent 1b4b41846f
commit 226605a996
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 108 additions and 63 deletions

View File

@ -54,10 +54,17 @@ having and using multiple runners.
This is probably one of (if not the) biggest things in this release. This is probably one of (if not the) biggest things in this release.
- **Breaking Change:** MacOS config paths now match Linux. - **Breaking Change:** MacOS config paths now match Linux.
- Overrides on the `hilbish` table are no longer permitted. - Overrides on the `hilbish` table are no longer permitted.
- **Breaking Change:** Runner functions are now required to return 3 values: - **Breaking Change:** Runner functions are now required to return a table.
user input, exit code, and error. User input has been added to the return to It can (at the moment) have 4 variables:
account for runners wanting to prompt for continued input, and to add it - `input` (user input)
properly to history. - `exitCode` (exit code)
- `error` (error message)
- `continue` (whether to prompt for more input)
User input has been added to the return to account for runners wanting to
prompt for continued input, and to add it properly to history. `continue`
got added so that it would be easier for runners to get continued input
without having to actually handle it at all.
- **Breaking Change:** Job objects and timers are now Lua userdata instead - **Breaking Change:** Job objects and timers are now Lua userdata instead
of a table, so their functions require you to call them with a colon instead of a table, so their functions require you to call them with a colon instead
of a dot. (ie. `job.stop()` -> `job:stop()`) of a dot. (ie. `job.stop()` -> `job:stop()`)

109
exec.go
View File

@ -87,19 +87,23 @@ func runInput(input string, priv bool) {
cmdString := aliases.Resolve(input) cmdString := aliases.Resolve(input)
hooks.Em.Emit("command.preexec", input, cmdString) hooks.Em.Emit("command.preexec", input, cmdString)
rerun:
var exitCode uint8 var exitCode uint8
var err error var err error
if runnerMode.Type() == rt.StringType { var cont bool
switch runnerMode.AsString() { // save incase it changes while prompting (For some reason)
currentRunner := runnerMode
if currentRunner.Type() == rt.StringType {
switch currentRunner.AsString() {
case "hybrid": case "hybrid":
_, _, err = handleLua(cmdString) _, _, err = handleLua(cmdString)
if err == nil { if err == nil {
cmdFinish(0, input, priv) cmdFinish(0, input, priv)
return return
} }
input, exitCode, err = handleSh(input) input, exitCode, cont, err = handleSh(input)
case "hybridRev": case "hybridRev":
_, _, err = handleSh(input) _, _, _, err = handleSh(input)
if err == nil { if err == nil {
cmdFinish(0, input, priv) cmdFinish(0, input, priv)
return return
@ -108,32 +112,24 @@ func runInput(input string, priv bool) {
case "lua": case "lua":
input, exitCode, err = handleLua(cmdString) input, exitCode, err = handleLua(cmdString)
case "sh": case "sh":
input, exitCode, err = handleSh(input) input, exitCode, cont, err = handleSh(input)
} }
} else { } else {
// can only be a string or function so // can only be a string or function so
term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 3, false) input, exitCode, cont, err = runLuaRunner(currentRunner, input)
err = rt.Call(l.MainThread(), runnerMode, []rt.Value{rt.StringValue(input)}, term)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
cmdFinish(124, input, priv) cmdFinish(124, input, priv)
return return
} }
luaInput := term.Get(0)
luaexitcode := term.Get(1)
runErr := term.Get(2)
if code, ok := luaexitcode.TryInt(); ok {
exitCode = uint8(code)
} }
if inp, ok := luaInput.TryString(); ok { if cont {
input = inp input, err = reprompt(input)
} if err == nil {
goto rerun
if errStr, ok := runErr.TryString(); ok { } else if err == io.EOF {
err = fmt.Errorf("%s", errStr) return
} }
} }
@ -147,6 +143,52 @@ func runInput(input string, priv bool) {
cmdFinish(exitCode, input, priv) cmdFinish(exitCode, input, priv)
} }
func reprompt(input string) (string, error) {
for {
in, err := continuePrompt(strings.TrimSuffix(input, "\\"))
if err != nil {
return input, err
}
if strings.HasSuffix(in, "\\") {
continue
}
return in, nil
}
}
func runLuaRunner(runr rt.Value, userInput string) (input string, exitCode uint8, continued bool, err error) {
term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 3, false)
err = rt.Call(l.MainThread(), runr, []rt.Value{rt.StringValue(userInput)}, term)
if err != nil {
return
}
var runner *rt.Table
var ok bool
runnerRet := term.Get(0)
if runner, ok = runnerRet.TryTable(); !ok {
fmt.Fprintln(os.Stderr, "runner did not return a table")
}
if code, ok := runner.Get(rt.StringValue("exitCode")).TryInt(); ok {
exitCode = uint8(code)
}
if inp, ok := runner.Get(rt.StringValue("input")).TryString(); ok {
input = inp
}
if errStr, ok := runner.Get(rt.StringValue("err")).TryString(); ok {
err = fmt.Errorf("%s", errStr)
}
if c, ok := runner.Get(rt.StringValue("continue")).TryBool(); ok {
continued = c
}
return
}
func handleLua(cmdString string) (string, uint8, error) { func handleLua(cmdString string) (string, uint8, error) {
// First try to load input, essentially compiling to bytecode // First try to load input, essentially compiling to bytecode
chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv())) chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv()))
@ -173,40 +215,25 @@ func handleLua(cmdString string) (string, uint8, error) {
return cmdString, 125, err return cmdString, 125, err
} }
func handleSh(cmdString string) (string, uint8, error) { func handleSh(cmdString string) (string, uint8, bool, error) {
_, _, err := execCommand(cmdString, true) _, _, err := execCommand(cmdString, true)
if err != nil { if err != nil {
// If input is incomplete, start multiline prompting // If input is incomplete, start multiline prompting
if syntax.IsIncomplete(err) { if syntax.IsIncomplete(err) {
if !interactive { if !interactive {
return cmdString, 126, err return cmdString, 126, false, err
}
for {
cmdString, err = continuePrompt(strings.TrimSuffix(cmdString, "\\"))
if err != nil {
break
}
_, _, err = execCommand(cmdString, true)
if syntax.IsIncomplete(err) || strings.HasSuffix(cmdString, "\\") {
continue
} else if code, ok := interp.IsExitStatus(err); ok {
return cmdString, code, nil
} else if err != nil {
return cmdString, 126, err
} else {
return cmdString, 0, nil
}
} }
return cmdString, 126, true, err
} else { } else {
if code, ok := interp.IsExitStatus(err); ok { if code, ok := interp.IsExitStatus(err); ok {
return cmdString, code, nil return cmdString, code, false, nil
} else { } else {
return cmdString, 126, err return cmdString, 126, false, err
} }
} }
} }
return cmdString, 0, nil return cmdString, 0, false, nil
} }
// Run command in sh interpreter // Run command in sh interpreter

View File

@ -1,23 +1,25 @@
local fs = require 'fs' local fs = require 'fs'
function cdHandle(inp) function cdHandle(inp)
local input, exit, err = hilbish.runner.lua(inp) local res = hilbish.runner.lua(inp)
if not err then if not res.err then
return input, exit, err return res
end end
input, exit, err = hilbish.runner.sh(inp) res = hilbish.runner.sh(inp)
if exit ~= 0 and hilbish.opts.autocd then if res.exit ~= 0 and hilbish.opts.autocd then
local ok, stat = pcall(fs.stat, input) local ok, stat = pcall(fs.stat, res.input)
if ok and stat.isDir then if ok and stat.isDir then
-- discard here to not append the cd, which will be in history -- discard here to not append the cd, which will be in history
_, exit, err = hilbish.runner.sh('cd ' .. input) local _, exitCode, err = hilbish.runner.sh('cd ' .. res.input)
res.exitCode = exitCode
res.err = err
end end
end end
return input, exit, err return res
end end
hilbish.runner.setMode(cdHandle) hilbish.runner.setMode(cdHandle)

View File

@ -77,18 +77,18 @@ end
hilbish.runner.add('hybrid', function(input) hilbish.runner.add('hybrid', function(input)
local cmdStr = hilbish.aliases.resolve(input) local cmdStr = hilbish.aliases.resolve(input)
local _, _, err = hilbish.runner.lua(cmdStr) local res = hilbish.runner.lua(cmdStr)
if not err then if not res.err then
return input, 0, nil return res
end end
return hilbish.runner.sh(input) return hilbish.runner.sh(input)
end) end)
hilbish.runner.add('hybridRev', function(input) hilbish.runner.add('hybridRev', function(input)
local _, _, err = hilbish.runner.sh(input) local res = hilbish.runner.sh(input)
if not err then if not res.err then
return input, 0, nil return res
end end
local cmdStr = hilbish.aliases.resolve(input) local cmdStr = hilbish.aliases.resolve(input)

View File

@ -28,13 +28,18 @@ func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return nil, err return nil, err
} }
input, exitCode, err := handleSh(cmd) input, exitCode, cont, err := handleSh(cmd)
var luaErr rt.Value = rt.NilValue var luaErr rt.Value = rt.NilValue
if err != nil { if err != nil {
luaErr = rt.StringValue(err.Error()) luaErr = rt.StringValue(err.Error())
} }
runnerRet := rt.NewTable()
runnerRet.Set(rt.StringValue("input"), rt.StringValue(input))
runnerRet.Set(rt.StringValue("exitCode"), rt.IntValue(int64(exitCode)))
runnerRet.Set(rt.StringValue("continue"), rt.BoolValue(cont))
runnerRet.Set(rt.StringValue("err"), luaErr)
return c.PushingNext(t.Runtime, rt.StringValue(input), rt.IntValue(int64(exitCode)), luaErr), nil return c.PushingNext(t.Runtime, rt.TableValue(runnerRet)), nil
} }
func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
@ -51,6 +56,10 @@ func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err != nil { if err != nil {
luaErr = rt.StringValue(err.Error()) luaErr = rt.StringValue(err.Error())
} }
runnerRet := rt.NewTable()
runnerRet.Set(rt.StringValue("input"), rt.StringValue(input))
runnerRet.Set(rt.StringValue("exitCode"), rt.IntValue(int64(exitCode)))
runnerRet.Set(rt.StringValue("err"), luaErr)
return c.PushingNext(t.Runtime, rt.StringValue(input), rt.IntValue(int64(exitCode)), luaErr), nil return c.PushingNext(t.Runtime, rt.TableValue(runnerRet)), nil
} }