2
2
mirror of https://github.com/Hilbis/Hilbish synced 2025-07-01 16:52:03 +00:00

refactor: fully implement hilbish.runner in lua

This commit is contained in:
sammyette 2025-06-15 18:29:54 -04:00
parent 91d7acf093
commit 7d503750c0
Signed by: sammyette
GPG Key ID: 904FC49417B44DCD
5 changed files with 73 additions and 153 deletions

4
api.go
View File

@ -110,10 +110,6 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp)) mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp))
mod.Set(rt.StringValue("completions"), rt.TableValue(hshcomp)) mod.Set(rt.StringValue("completions"), rt.TableValue(hshcomp))
// hilbish.runner table
runnerModule := runnerModeLoader(rtm)
mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule))
// hilbish.jobs table // hilbish.jobs table
jobs = newJobHandler() jobs = newJobHandler()
jobModule := jobs.loader(rtm) jobModule := jobs.loader(rtm)

27
exec.go
View File

@ -18,33 +18,6 @@ func runInput(input string, priv bool) {
} }
} }
func handleLua(input string) (string, uint8, error) {
cmdString := aliases.Resolve(input)
// First try to load input, essentially compiling to bytecode
chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv()))
if err != nil && noexecute {
fmt.Println(err)
/* if lerr, ok := err.(*lua.ApiError); ok {
if perr, ok := lerr.Cause.(*parse.Error); ok {
print(perr.Pos.Line == parse.EOF)
}
}
*/
return cmdString, 125, err
}
// And if there's no syntax errors and -n isnt provided, run
if !noexecute {
if chunk != nil {
_, err = rt.Call1(l.MainThread(), rt.FunctionValue(chunk))
}
}
if err == nil {
return cmdString, 0, nil
}
return cmdString, 125, err
}
func splitInput(input string) ([]string, string) { func splitInput(input string) ([]string, string) {
// end my suffering // end my suffering
// TODO: refactor this garbage // TODO: refactor this garbage

31
main.go
View File

@ -251,37 +251,6 @@ func continuePrompt(prev string, newline bool) (string, error) {
} }
*/ */
// This semi cursed function formats our prompt (obviously)
func fmtPrompt(prompt string) string {
host, _ := os.Hostname()
cwd, _ := os.Getwd()
cwd = util.AbbrevHome(cwd)
username := curuser.Username
// this will be baked into binary since GOOS is a constant
if runtime.GOOS == "windows" {
username = strings.Split(username, "\\")[1] // for some reason Username includes the hostname on windows
}
args := []string{
"d", cwd,
"D", filepath.Base(cwd),
"h", host,
"u", username,
}
for i, v := range args {
if i%2 == 0 {
args[i] = "%" + v
}
}
r := strings.NewReplacer(args...)
nprompt := r.Replace(prompt)
return nprompt
}
func removeDupes(slice []string) []string { func removeDupes(slice []string) []string {
all := make(map[string]bool) all := make(map[string]bool)
newSlice := []string{} newSlice := []string{}

View File

@ -1,10 +1,53 @@
-- @module hilbish.runner -- @module hilbish.runner
--- interactive command runner customization
--- The runner interface contains functions that allow the user to change
--- how Hilbish interprets interactive input.
--- Users can add and change the default runner for interactive input to any
--- language or script of their choosing. A good example is using it to
--- write command in Fennel.
---
--- Runners are functions that evaluate user input. The default runners in
--- Hilbish can run shell script and Lua code.
---
--- A runner is passed the input and has to return a table with these values.
--- All are not required, only the useful ones the runner needs to return.
--- (So if there isn't an error, just omit `err`.)
---
--- - `exitCode` (number): Exit code of the command
--- - `input` (string): The text input of the user. This is used by Hilbish to append extra input, in case
--- more is requested.
--- - `err` (string): A string that represents an error from the runner.
--- This should only be set when, for example, there is a syntax error.
--- It can be set to a few special values for Hilbish to throw the right
--- hooks and have a better looking message.
--- - `<command>: not-found` will throw a `command.not-found` hook
--- based on what `<command>` is.
--- - `<command>: not-executable` will throw a `command.not-executable` hook.
--- - `continue` (boolean): Whether Hilbish should prompt the user for no input
--- - `newline` (boolean): Whether a newline should be added at the end of `input`.
---
--- Here is a simple example of a fennel runner. It falls back to
--- shell script if fennel eval has an error.
--- ```lua
--- local fennel = require 'fennel'
---
--- hilbish.runnerMode(function(input)
--- local ok = pcall(fennel.eval, input)
--- if ok then
--- return {
--- input = input
--- }
--- end
---
--- return hilbish.runner.sh(input)
--- end)
--- ```
local snail = require 'snail' local snail = require 'snail'
local currentRunner = 'hybrid' local currentRunner = 'hybrid'
local runners = {} local runners = {}
-- lsp shut up -- lsp shut up
hilbish = hilbish hilbish.runner = {}
--- Get a runner by name. --- Get a runner by name.
--- @param name string Name of the runner to retrieve. --- @param name string Name of the runner to retrieve.
@ -167,6 +210,35 @@ function hilbish.runner.sh(input)
return hilbish.snail:run(input) return hilbish.snail:run(input)
end end
--- lua(cmd)
--- Evaluates `cmd` as Lua input. This is the same as using `dofile`
--- or `load`, but is appropriated for the runner interface.
-- @param cmd string
function hilbish.runner.lua(input)
local fun, err = load(input)
if err then
return {
input = input,
exitCode = 125,
err = err
}
end
local ok = pcall(fun)
if not ok then
return {
input = input,
exitCode = 126,
err = err
}
end
return {
input = input,
exitCode = 0,
}
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)

View File

@ -1,90 +0,0 @@
package main
import (
"hilbish/util"
rt "github.com/arnodel/golua/runtime"
)
// #interface runner
// interactive command runner customization
/* The runner interface contains functions that allow the user to change
how Hilbish interprets interactive input.
Users can add and change the default runner for interactive input to any
language or script of their choosing. A good example is using it to
write command in Fennel.
Runners are functions that evaluate user input. The default runners in
Hilbish can run shell script and Lua code.
A runner is passed the input and has to return a table with these values.
All are not required, only the useful ones the runner needs to return.
(So if there isn't an error, just omit `err`.)
- `exitCode` (number): Exit code of the command
- `input` (string): The text input of the user. This is used by Hilbish to append extra input, in case
more is requested.
- `err` (string): A string that represents an error from the runner.
This should only be set when, for example, there is a syntax error.
It can be set to a few special values for Hilbish to throw the right
hooks and have a better looking message.
- `<command>: not-found` will throw a `command.not-found` hook
based on what `<command>` is.
- `<command>: not-executable` will throw a `command.not-executable` hook.
- `continue` (boolean): Whether Hilbish should prompt the user for no input
- `newline` (boolean): Whether a newline should be added at the end of `input`.
Here is a simple example of a fennel runner. It falls back to
shell script if fennel eval has an error.
```lua
local fennel = require 'fennel'
hilbish.runnerMode(function(input)
local ok = pcall(fennel.eval, input)
if ok then
return {
input = input
}
end
return hilbish.runner.sh(input)
end)
```
*/
func runnerModeLoader(rtm *rt.Runtime) *rt.Table {
exports := map[string]util.LuaExport{
"lua": {luaRunner, 1, false},
}
mod := rt.NewTable()
util.SetExports(rtm, mod, exports)
return mod
}
// #interface runner
// lua(cmd)
// Evaluates `cmd` as Lua input. This is the same as using `dofile`
// or `load`, but is appropriated for the runner interface.
// #param cmd string
func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
}
cmd, err := c.StringArg(0)
if err != nil {
return nil, err
}
input, exitCode, err := handleLua(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("err"), luaErr)
return c.PushingNext(t.Runtime, rt.TableValue(runnerRet)), nil
}