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.
- **Breaking Change:** MacOS config paths now match Linux.
- Overrides on the `hilbish` table are no longer permitted.
- **Breaking Change:** Runner functions are now required to return 3 values:
user input, exit code, and error. 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.
- **Breaking Change:** Runner functions are now required to return a table.
It can (at the moment) have 4 variables:
- `input` (user input)
- `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
of a table, so their functions require you to call them with a colon instead
of a dot. (ie. `job.stop()` -> `job:stop()`)

111
exec.go
View File

@ -87,19 +87,23 @@ func runInput(input string, priv bool) {
cmdString := aliases.Resolve(input)
hooks.Em.Emit("command.preexec", input, cmdString)
rerun:
var exitCode uint8
var err error
if runnerMode.Type() == rt.StringType {
switch runnerMode.AsString() {
var cont bool
// save incase it changes while prompting (For some reason)
currentRunner := runnerMode
if currentRunner.Type() == rt.StringType {
switch currentRunner.AsString() {
case "hybrid":
_, _, err = handleLua(cmdString)
if err == nil {
cmdFinish(0, input, priv)
return
}
input, exitCode, err = handleSh(input)
input, exitCode, cont, err = handleSh(input)
case "hybridRev":
_, _, err = handleSh(input)
_, _, _, err = handleSh(input)
if err == nil {
cmdFinish(0, input, priv)
return
@ -108,32 +112,24 @@ func runInput(input string, priv bool) {
case "lua":
input, exitCode, err = handleLua(cmdString)
case "sh":
input, exitCode, err = handleSh(input)
input, exitCode, cont, err = handleSh(input)
}
} else {
// can only be a string or function so
term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 3, false)
err = rt.Call(l.MainThread(), runnerMode, []rt.Value{rt.StringValue(input)}, term)
input, exitCode, cont, err = runLuaRunner(currentRunner, input)
if err != nil {
fmt.Fprintln(os.Stderr, err)
cmdFinish(124, input, priv)
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 {
input = inp
}
if errStr, ok := runErr.TryString(); ok {
err = fmt.Errorf("%s", errStr)
if cont {
input, err = reprompt(input)
if err == nil {
goto rerun
} else if err == io.EOF {
return
}
}
@ -147,6 +143,52 @@ func runInput(input string, priv bool) {
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) {
// First try to load input, essentially compiling to bytecode
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
}
func handleSh(cmdString string) (string, uint8, error) {
func handleSh(cmdString string) (string, uint8, bool, error) {
_, _, err := execCommand(cmdString, true)
if err != nil {
// If input is incomplete, start multiline prompting
if syntax.IsIncomplete(err) {
if !interactive {
return cmdString, 126, 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, false, err
}
return cmdString, 126, true, err
} else {
if code, ok := interp.IsExitStatus(err); ok {
return cmdString, code, nil
return cmdString, code, false, nil
} else {
return cmdString, 126, err
return cmdString, 126, false, err
}
}
}
return cmdString, 0, nil
return cmdString, 0, false, nil
}
// Run command in sh interpreter

View File

@ -1,23 +1,25 @@
local fs = require 'fs'
function cdHandle(inp)
local input, exit, err = hilbish.runner.lua(inp)
local res = hilbish.runner.lua(inp)
if not err then
return input, exit, err
if not res.err then
return res
end
input, exit, err = hilbish.runner.sh(inp)
res = hilbish.runner.sh(inp)
if exit ~= 0 and hilbish.opts.autocd then
local ok, stat = pcall(fs.stat, input)
if res.exit ~= 0 and hilbish.opts.autocd then
local ok, stat = pcall(fs.stat, res.input)
if ok and stat.isDir then
-- 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
return input, exit, err
return res
end
hilbish.runner.setMode(cdHandle)

View File

@ -77,18 +77,18 @@ end
hilbish.runner.add('hybrid', function(input)
local cmdStr = hilbish.aliases.resolve(input)
local _, _, err = hilbish.runner.lua(cmdStr)
if not err then
return input, 0, nil
local res = hilbish.runner.lua(cmdStr)
if not res.err then
return res
end
return hilbish.runner.sh(input)
end)
hilbish.runner.add('hybridRev', function(input)
local _, _, err = hilbish.runner.sh(input)
if not err then
return input, 0, nil
local res = hilbish.runner.sh(input)
if not res.err then
return res
end
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
}
input, exitCode, err := handleSh(cmd)
input, exitCode, cont, err := handleSh(cmd)
var luaErr rt.Value = rt.NilValue
if err != nil {
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) {
@ -51,6 +56,10 @@ func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err != nil {
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
}