Compare commits

..

No commits in common. "69d38d704844de95302fe1fc7cdeea9fed9e4162" and "64bf7024d24342b7ae828fd08c8a112cf41f1d83" have entirely different histories.

22 changed files with 655 additions and 1090 deletions

View File

@ -4,9 +4,7 @@ import (
"strings" "strings"
"sync" "sync"
"hilbish/util" "github.com/yuin/gopher-lua"
rt "github.com/arnodel/golua/runtime"
) )
var aliases *aliasHandler var aliases *aliasHandler
@ -66,38 +64,41 @@ func (a *aliasHandler) Resolve(cmdstr string) string {
// lua section // lua section
func (a *aliasHandler) Loader(rtm *rt.Runtime) *rt.Table { func (a *aliasHandler) Loader(L *lua.LState) *lua.LTable {
// create a lua module with our functions // create a lua module with our functions
hshaliasesLua := map[string]util.LuaExport{ hshaliasesLua := map[string]lua.LGFunction{
"add": util.LuaExport{hlalias, 2, false}, "add": a.luaAdd,
"list": util.LuaExport{a.luaList, 0, false}, "list": a.luaList,
"del": util.LuaExport{a.luaDelete, 1, false}, "del": a.luaDelete,
} }
mod := rt.NewTable() mod := L.SetFuncs(L.NewTable(), hshaliasesLua)
util.SetExports(rtm, mod, hshaliasesLua)
return mod return mod
} }
func (a *aliasHandler) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (a *aliasHandler) luaAdd(L *lua.LState) int {
aliasesList := rt.NewTable() alias := L.CheckString(1)
for k, v := range a.All() { cmd := L.CheckString(2)
aliasesList.Set(rt.StringValue(k), rt.StringValue(v)) a.Add(alias, cmd)
}
return c.PushingNext1(t.Runtime, rt.TableValue(aliasesList)), nil return 0
} }
func (a *aliasHandler) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (a *aliasHandler) luaList(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { aliasesList := L.NewTable()
return nil, err for k, v := range a.All() {
} aliasesList.RawSetString(k, lua.LString(v))
alias, err := c.StringArg(0)
if err != nil {
return nil, err
} }
L.Push(aliasesList)
return 1
}
func (a *aliasHandler) luaDelete(L *lua.LState) int {
alias := L.CheckString(1)
a.Delete(alias) a.Delete(alias)
return c.Next(), nil return 0
} }

651
api.go
View File

@ -4,8 +4,6 @@
package main package main
import ( import (
"bytes"
"errors"
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
@ -16,44 +14,38 @@ import (
"hilbish/util" "hilbish/util"
rt "github.com/arnodel/golua/runtime" "github.com/yuin/gopher-lua"
"github.com/arnodel/golua/lib/packagelib"
"github.com/maxlandon/readline" "github.com/maxlandon/readline"
"github.com/blackfireio/osinfo" "github.com/blackfireio/osinfo"
"mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/interp"
) )
var exports = map[string]util.LuaExport{ var exports = map[string]lua.LGFunction {
"alias": {hlalias, 2, false}, "alias": hlalias,
"appendPath": {hlappendPath, 1, false}, "appendPath": hlappendPath,
"complete": {hlcomplete, 2, false}, "complete": hlcomplete,
"cwd": {hlcwd, 0, false}, "cwd": hlcwd,
"exec": {hlexec, 1, false}, "exec": hlexec,
"runnerMode": {hlrunnerMode, 1, false}, "runnerMode": hlrunnerMode,
"goro": {hlgoro, 1, true}, "goro": hlgoro,
"highlighter": {hlhighlighter, 1, false}, "highlighter": hlhighlighter,
"hinter": {hlhinter, 1, false}, "hinter": hlhinter,
"multiprompt": {hlmultiprompt, 1, false}, "multiprompt": hlmlprompt,
"prependPath": {hlprependPath, 1, false}, "prependPath": hlprependPath,
"prompt": {hlprompt, 1, false}, "prompt": hlprompt,
"inputMode": {hlinputMode, 1, false}, "inputMode": hlinputMode,
"interval": {hlinterval, 2, false}, "interval": hlinterval,
"read": {hlread, 1, false}, "read": hlread,
"run": {hlrun, 1, true}, "run": hlrun,
"timeout": {hltimeout, 2, false}, "timeout": hltimeout,
"which": {hlwhich, 1, false}, "which": hlwhich,
} }
var greeting string var greeting string
var hshMod *rt.Table var hshMod *lua.LTable
var hilbishLoader = packagelib.Loader{
Load: hilbishLoad,
Name: "hilbish",
}
func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) { func hilbishLoader(L *lua.LState) int {
mod := rt.NewTable() mod := L.SetFuncs(L.NewTable(), exports)
util.SetExports(rtm, mod, exports)
hshMod = mod hshMod = mod
host, _ := os.Hostname() host, _ := os.Hostname()
@ -67,72 +59,151 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
The nice lil shell for {blue}Lua{reset} fanatics! The nice lil shell for {blue}Lua{reset} fanatics!
Check out the {blue}{bold}guide{reset} command to get started. Check out the {blue}{bold}guide{reset} command to get started.
` `
util.SetField(rtm, mod, "ver", rt.StringValue(version), "Hilbish version")
util.SetField(rtm, mod, "user", rt.StringValue(username), "Username of user") util.SetField(L, mod, "ver", lua.LString(version), "Hilbish version")
util.SetField(rtm, mod, "host", rt.StringValue(host), "Host name of the machine") util.SetField(L, mod, "user", lua.LString(username), "Username of user")
util.SetField(rtm, mod, "home", rt.StringValue(curuser.HomeDir), "Home directory of the user") util.SetField(L, mod, "host", lua.LString(host), "Host name of the machine")
util.SetField(rtm, mod, "dataDir", rt.StringValue(dataDir), "Directory for Hilbish's data files") util.SetField(L, mod, "home", lua.LString(curuser.HomeDir), "Home directory of the user")
util.SetField(rtm, mod, "interactive", rt.BoolValue(interactive), "If this is an interactive shell") util.SetField(L, mod, "dataDir", lua.LString(dataDir), "Directory for Hilbish's data files")
util.SetField(rtm, mod, "login", rt.BoolValue(login), "Whether this is a login shell") util.SetField(L, mod, "interactive", lua.LBool(interactive), "If this is an interactive shell")
util.SetField(rtm, mod, "greeting", rt.StringValue(greeting), "Hilbish's welcome message for interactive shells. It has Lunacolors formatting.") util.SetField(L, mod, "login", lua.LBool(interactive), "Whether this is a login shell")
util.SetField(rtm, mod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)") util.SetField(L, mod, "greeting", lua.LString(greeting), "Hilbish's welcome message for interactive shells. It has Lunacolors formatting.")
util.SetField(rtm, hshMod, "exitCode", rt.IntValue(0), "Exit code of last exected command") util.SetField(l, mod, "vimMode", lua.LNil, "Current Vim mode of Hilbish (nil if not in Vim mode)")
util.Document(mod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.") util.SetField(l, hshMod, "exitCode", lua.LNumber(0), "Exit code of last exected command")
util.Document(L, mod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.")
// hilbish.userDir table // hilbish.userDir table
hshuser := rt.NewTable() hshuser := L.NewTable()
util.SetField(rtm, hshuser, "config", rt.StringValue(confDir), "User's config directory") util.SetField(L, hshuser, "config", lua.LString(confDir), "User's config directory")
util.SetField(rtm, hshuser, "data", rt.StringValue(userDataDir), "XDG data directory") util.SetField(L, hshuser, "data", lua.LString(userDataDir), "XDG data directory")
util.Document(hshuser, "User directories to store configs and/or modules.") util.Document(L, hshuser, "User directories to store configs and/or modules.")
mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser)) L.SetField(mod, "userDir", hshuser)
// hilbish.os table // hilbish.os table
hshos := rt.NewTable() hshos := L.NewTable()
info, _ := osinfo.GetOSInfo() info, _ := osinfo.GetOSInfo()
util.SetField(rtm, hshos, "family", rt.StringValue(info.Family), "Family name of the current OS") util.SetField(L, hshos, "family", lua.LString(info.Family), "Family name of the current OS")
util.SetField(rtm, hshos, "name", rt.StringValue(info.Name), "Pretty name of the current OS") util.SetField(L, hshos, "name", lua.LString(info.Name), "Pretty name of the current OS")
util.SetField(rtm, hshos, "version", rt.StringValue(info.Version), "Version of the current OS") util.SetField(L, hshos, "version", lua.LString(info.Version), "Version of the current OS")
util.Document(hshos, "OS info interface") util.Document(L, hshos, "OS info interface")
mod.Set(rt.StringValue("os"), rt.TableValue(hshos)) L.SetField(mod, "os", hshos)
// hilbish.aliases table // hilbish.aliases table
aliases = newAliases() aliases = newAliases()
aliasesModule := aliases.Loader(rtm) aliasesModule := aliases.Loader(L)
util.Document(aliasesModule, "Alias inferface for Hilbish.") util.Document(L, aliasesModule, "Alias inferface for Hilbish.")
mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule)) L.SetField(mod, "aliases", aliasesModule)
// hilbish.history table // hilbish.history table
historyModule := lr.Loader(rtm) historyModule := lr.Loader(L)
mod.Set(rt.StringValue("history"), rt.TableValue(historyModule)) util.Document(L, historyModule, "History interface for Hilbish.")
util.Document(historyModule, "History interface for Hilbish.") L.SetField(mod, "history", historyModule)
// hilbish.completion table // hilbish.completions table
hshcomp := rt.NewTable() hshcomp := L.NewTable()
util.SetField(rtm, hshcomp, "files",
rt.FunctionValue(rt.NewGoFunction(luaFileComplete, "files", 3, false)),
"Completer for files")
util.SetField(rtm, hshcomp, "bins", util.SetField(L, hshcomp, "files", L.NewFunction(luaFileComplete), "Completer for files")
rt.FunctionValue(rt.NewGoFunction(luaBinaryComplete, "bins", 3, false)), util.SetField(L, hshcomp, "bins", L.NewFunction(luaBinaryComplete), "Completer for executables/binaries")
"Completer for executables/binaries") util.Document(L, hshcomp, "Completions interface for Hilbish.")
L.SetField(mod, "completion", hshcomp)
util.Document(hshcomp, "Completions interface for Hilbish.")
mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp))
// hilbish.runner table // hilbish.runner table
runnerModule := runnerModeLoader(rtm) runnerModule := runnerModeLoader(L)
util.Document(runnerModule, "Runner/exec interface for Hilbish.") util.Document(L, runnerModule, "Runner/exec interface for Hilbish.")
mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule)) L.SetField(mod, "runner", runnerModule)
// hilbish.jobs table // hilbish.jobs table
jobs = newJobHandler() jobs = newJobHandler()
jobModule := jobs.loader(rtm) jobModule := jobs.loader(L)
util.Document(jobModule, "(Background) job interface.") util.Document(L, jobModule, "(Background) job interface.")
mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule)) L.SetField(mod, "jobs", jobModule)
return rt.TableValue(mod), nil L.Push(mod)
return 1
}
func luaFileComplete(L *lua.LState) int {
query := L.CheckString(1)
ctx := L.CheckString(2)
fields := L.CheckTable(3)
var fds []string
fields.ForEach(func(k lua.LValue, v lua.LValue) {
fds = append(fds, v.String())
})
completions := fileComplete(query, ctx, fds)
luaComps := L.NewTable()
for _, comp := range completions {
luaComps.Append(lua.LString(comp))
}
L.Push(luaComps)
return 1
}
func luaBinaryComplete(L *lua.LState) int {
query := L.CheckString(1)
ctx := L.CheckString(2)
fields := L.CheckTable(3)
var fds []string
fields.ForEach(func(k lua.LValue, v lua.LValue) {
fds = append(fds, v.String())
})
completions, _ := binaryComplete(query, ctx, fds)
luaComps := L.NewTable()
for _, comp := range completions {
luaComps.Append(lua.LString(comp))
}
L.Push(luaComps)
return 1
}
func setVimMode(mode string) {
util.SetField(l, hshMod, "vimMode", lua.LString(mode), "Current Vim mode of Hilbish (nil if not in Vim mode)")
hooks.Em.Emit("hilbish.vimMode", mode)
}
func unsetVimMode() {
util.SetField(l, hshMod, "vimMode", lua.LNil, "Current Vim mode of Hilbish (nil if not in Vim mode)")
}
// run(cmd)
// Runs `cmd` in Hilbish's sh interpreter.
// --- @param cmd string
func hlrun(L *lua.LState) int {
var exitcode uint8
cmd := L.CheckString(1)
err := execCommand(cmd)
if code, ok := interp.IsExitStatus(err); ok {
exitcode = code
} else if err != nil {
exitcode = 1
}
L.Push(lua.LNumber(exitcode))
return 1
}
// cwd()
// Returns the current directory of the shell
func hlcwd(L *lua.LState) int {
cwd, _ := os.Getwd()
L.Push(lua.LString(cwd))
return 1
} }
func getenv(key, fallback string) string { func getenv(key, fallback string) string {
@ -143,160 +214,24 @@ func getenv(key, fallback string) string {
return value return value
} }
func luaFileComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
query, ctx, fds, err := getCompleteParams(t, c)
if err != nil {
return nil, err
}
completions := fileComplete(query, ctx, fds)
luaComps := rt.NewTable()
for i, comp := range completions {
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
}
return c.PushingNext1(t.Runtime, rt.TableValue(luaComps)), nil
}
func luaBinaryComplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
query, ctx, fds, err := getCompleteParams(t, c)
if err != nil {
return nil, err
}
completions, _ := binaryComplete(query, ctx, fds)
luaComps := rt.NewTable()
for i, comp := range completions {
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
}
return c.PushingNext1(t.Runtime, rt.TableValue(luaComps)), nil
}
func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, error) {
if err := c.CheckNArgs(3); err != nil {
return "", "", []string{}, err
}
query, err := c.StringArg(0)
if err != nil {
return "", "", []string{}, err
}
ctx, err := c.StringArg(1)
if err != nil {
return "", "", []string{}, err
}
fields, err := c.TableArg(2)
if err != nil {
return "", "", []string{}, err
}
var fds []string
nextVal := rt.NilValue
for {
next, val, ok := fields.Next(nextVal)
if next == rt.NilValue {
break
}
nextVal = next
valStr, ok := val.TryString()
if !ok {
continue
}
fds = append(fds, valStr)
}
return query, ctx, fds, err
}
func setVimMode(mode string) {
util.SetField(l, hshMod, "vimMode", rt.StringValue(mode), "Current Vim mode of Hilbish (nil if not in Vim mode)")
hooks.Em.Emit("hilbish.vimMode", mode)
}
func unsetVimMode() {
util.SetField(l, hshMod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)")
}
// run(cmd, returnOut) -> exitCode, stdout, stderr
// Runs `cmd` in Hilbish's sh interpreter.
// If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
// 3rd values instead of being outputted to the terminal.
// --- @param cmd string
func hlrun(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
}
var terminalOut bool
if len(c.Etc()) != 0 {
tout := c.Etc()[0]
termOut, ok := tout.TryBool()
terminalOut = termOut
if !ok {
return nil, errors.New("bad argument to run (expected boolean, got " + tout.TypeName() + ")")
}
} else {
terminalOut = true
}
var exitcode uint8
stdout, stderr, err := execCommand(cmd, terminalOut)
if code, ok := interp.IsExitStatus(err); ok {
exitcode = code
} else if err != nil {
exitcode = 1
}
stdoutStr := ""
stderrStr := ""
if !terminalOut {
stdoutStr = stdout.(*bytes.Buffer).String()
stderrStr = stderr.(*bytes.Buffer).String()
}
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitcode)), rt.StringValue(stdoutStr), rt.StringValue(stderrStr)), nil
}
// cwd()
// Returns the current directory of the shell
func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
cwd, _ := os.Getwd()
return c.PushingNext1(t.Runtime, rt.StringValue(cwd)), nil
}
// read(prompt) -> input? // read(prompt) -> input?
// Read input from the user, using Hilbish's line editor/input reader. // Read input from the user, using Hilbish's line editor/input reader.
// This is a separate instance from the one Hilbish actually uses. // This is a separate instance from the one Hilbish actually uses.
// Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen) // Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
// --- @param prompt string // --- @param prompt string
func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlread(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { luaprompt := L.CheckString(1)
return nil, err
}
luaprompt, err := c.StringArg(0)
if err != nil {
return nil, err
}
lualr := newLineReader("", true) lualr := newLineReader("", true)
lualr.SetPrompt(luaprompt) lualr.SetPrompt(luaprompt)
input, err := lualr.Read() input, err := lualr.Read()
if err != nil { if err != nil {
return c.Next(), nil L.Push(lua.LNil)
return 1
} }
return c.PushingNext1(t.Runtime, rt.StringValue(input)), nil L.Push(lua.LString(input))
return 1
} }
/* /*
@ -309,91 +244,52 @@ These will be formatted and replaced with the appropriate values.
`%h` - Hostname of device `%h` - Hostname of device
--- @param str string --- @param str string
*/ */
func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlprompt(L *lua.LState) int {
var prompt string prompt = L.CheckString(1)
err := c.Check1Arg()
if err == nil {
prompt, err = c.StringArg(0)
}
if err != nil {
return nil, err
}
lr.SetPrompt(fmtPrompt(prompt)) lr.SetPrompt(fmtPrompt(prompt))
return c.Next(), nil return 0
} }
// multiprompt(str) // multiprompt(str)
// Changes the continued line prompt to `str` // Changes the continued line prompt to `str`
// --- @param str string // --- @param str string
func hlmultiprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlmlprompt(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { multilinePrompt = L.CheckString(1)
return nil, err
}
prompt, err := c.StringArg(0)
if err != nil {
return nil, err
}
multilinePrompt = prompt
return c.Next(), nil return 0
} }
// alias(cmd, orig) // alias(cmd, orig)
// Sets an alias of `cmd` to `orig` // Sets an alias of `cmd` to `orig`
// --- @param cmd string // --- @param cmd string
// --- @param orig string // --- @param orig string
func hlalias(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlalias(L *lua.LState) int {
if err := c.CheckNArgs(2); err != nil { alias := L.CheckString(1)
return nil, err source := L.CheckString(2)
}
cmd, err := c.StringArg(0)
if err != nil {
return nil, err
}
orig, err := c.StringArg(1)
if err != nil {
return nil, err
}
aliases.Add(cmd, orig) aliases.Add(alias, source)
return c.Next(), nil return 1
} }
// appendPath(dir) // appendPath(dir)
// Appends `dir` to $PATH // Appends `dir` to $PATH
// --- @param dir string|table // --- @param dir string|table
func hlappendPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlappendPath(L *lua.LState) int {
if err := c.Check1Arg(); err != nil {
return nil, err
}
arg := c.Arg(0)
// check if dir is a table or a string // check if dir is a table or a string
if arg.Type() == rt.TableType { arg := L.Get(1)
nextVal := rt.NilValue if arg.Type() == lua.LTTable {
for { arg.(*lua.LTable).ForEach(func(k lua.LValue, v lua.LValue) {
next, val, ok := arg.AsTable().Next(nextVal) appendPath(v.String())
if next == rt.NilValue { })
break } else if arg.Type() == lua.LTString {
} appendPath(arg.String())
nextVal = next
valStr, ok := val.TryString()
if !ok {
continue
}
appendPath(valStr)
}
} else if arg.Type() == rt.StringType {
appendPath(arg.AsString())
} else { } else {
return nil, errors.New("bad argument to appendPath (expected string or table, got " + arg.TypeName() + ")") L.RaiseError("bad argument to appendPath (expected string or table, got %v)", L.Get(1).Type().String())
} }
return c.Next(), nil return 0
} }
func appendPath(dir string) { func appendPath(dir string) {
@ -409,14 +305,8 @@ func appendPath(dir string) {
// exec(cmd) // exec(cmd)
// Replaces running hilbish with `cmd` // Replaces running hilbish with `cmd`
// --- @param cmd string // --- @param cmd string
func hlexec(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlexec(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { cmd := L.CheckString(1)
return nil, err
}
cmd, err := c.StringArg(0)
if err != nil {
return nil, err
}
cmdArgs, _ := splitInput(cmd) cmdArgs, _ := splitInput(cmd)
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
cmdPath, err := exec.LookPath(cmdArgs[0]) cmdPath, err := exec.LookPath(cmdArgs[0])
@ -438,89 +328,78 @@ func hlexec(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
os.Exit(0) os.Exit(0)
} }
return c.Next(), nil return 0
} }
// goro(fn) // goro(fn)
// Puts `fn` in a goroutine // Puts `fn` in a goroutine
// --- @param fn function // --- @param fn function
func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlgoro(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { fn := L.CheckFunction(1)
return nil, err argnum := L.GetTop()
} args := make([]lua.LValue, argnum)
fn, err := c.ClosureArg(0) for i := 1; i <= argnum; i++ {
if err != nil { args[i - 1] = L.Get(i)
return nil, err
} }
// call fn // call fn
go func() { go func() {
_, err := rt.Call1(l.MainThread(), rt.FunctionValue(fn), c.Etc()...) if err := L.CallByParam(lua.P{
if err != nil { Fn: fn,
NRet: 0,
Protect: true,
}, args...); err != nil {
fmt.Fprintln(os.Stderr, "Error in goro function:\n\n", err) fmt.Fprintln(os.Stderr, "Error in goro function:\n\n", err)
} }
}() }()
return c.Next(), nil return 0
} }
// timeout(cb, time) // timeout(cb, time)
// Runs the `cb` function after `time` in milliseconds // Runs the `cb` function after `time` in milliseconds
// --- @param cb function // --- @param cb function
// --- @param time number // --- @param time number
func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hltimeout(L *lua.LState) int {
if err := c.CheckNArgs(2); err != nil { cb := L.CheckFunction(1)
return nil, err ms := L.CheckInt(2)
}
cb, err := c.ClosureArg(0)
if err != nil {
return nil, err
}
ms, err := c.IntArg(1)
if err != nil {
return nil, err
}
timeout := time.Duration(ms) * time.Millisecond timeout := time.Duration(ms) * time.Millisecond
time.Sleep(timeout) time.Sleep(timeout)
_, err = rt.Call1(l.MainThread(), rt.FunctionValue(cb)) if err := L.CallByParam(lua.P{
if err != nil { Fn: cb,
return nil, err NRet: 0,
Protect: true,
}); err != nil {
fmt.Fprintln(os.Stderr, "Error in goro function:\n\n", err)
} }
return 0
return c.Next(), nil
} }
// interval(cb, time) // interval(cb, time)
// Runs the `cb` function every `time` milliseconds // Runs the `cb` function every `time` milliseconds
// --- @param cb function // --- @param cb function
// --- @param time number // --- @param time number
func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlinterval(L *lua.LState) int {
if err := c.CheckNArgs(2); err != nil { intervalfunc := L.CheckFunction(1)
return nil, err ms := L.CheckInt(2)
}
cb, err := c.ClosureArg(0)
if err != nil {
return nil, err
}
ms, err := c.IntArg(1)
if err != nil {
return nil, err
}
interval := time.Duration(ms) * time.Millisecond interval := time.Duration(ms) * time.Millisecond
ticker := time.NewTicker(interval) ticker := time.NewTicker(interval)
stop := make(chan rt.Value) stop := make(chan lua.LValue)
go func() { go func() {
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
_, err := rt.Call1(l.MainThread(), rt.FunctionValue(cb)) if err := L.CallByParam(lua.P{
if err != nil { Fn: intervalfunc,
NRet: 0,
Protect: true,
}); err != nil {
fmt.Fprintln(os.Stderr, "Error in interval function:\n\n", err) fmt.Fprintln(os.Stderr, "Error in interval function:\n\n", err)
stop <- rt.BoolValue(true) // stop the interval stop <- lua.LTrue // stop the interval
} }
case <-stop: case <-stop:
ticker.Stop() ticker.Stop()
@ -529,8 +408,8 @@ func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
} }
}() }()
// TODO: return channel L.Push(lua.LChannel(stop))
return c.Next(), nil return 1
} }
// complete(scope, cb) // complete(scope, cb)
@ -543,27 +422,20 @@ func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
// `grid` (the normal file completion display) or `list` (with a description) // `grid` (the normal file completion display) or `list` (with a description)
// --- @param scope string // --- @param scope string
// --- @param cb function // --- @param cb function
func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlcomplete(L *lua.LState) int {
scope, cb, err := util.HandleStrCallback(t, c) scope := L.CheckString(1)
if err != nil { cb := L.CheckFunction(2)
return nil, err
}
luaCompletions[scope] = cb luaCompletions[scope] = cb
return c.Next(), nil return 0
} }
// prependPath(dir) // prependPath(dir)
// Prepends `dir` to $PATH // Prepends `dir` to $PATH
// --- @param dir string // --- @param dir string
func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlprependPath(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { dir := L.CheckString(1)
return nil, err
}
dir, err := c.StringArg(0)
if err != nil {
return nil, err
}
dir = strings.Replace(dir, "~", curuser.HomeDir, 1) dir = strings.Replace(dir, "~", curuser.HomeDir, 1)
pathenv := os.Getenv("PATH") pathenv := os.Getenv("PATH")
@ -572,40 +444,29 @@ func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
os.Setenv("PATH", dir + string(os.PathListSeparator) + pathenv) os.Setenv("PATH", dir + string(os.PathListSeparator) + pathenv)
} }
return c.Next(), nil return 0
} }
// which(binName) // which(binName)
// Searches for an executable called `binName` in the directories of $PATH // Searches for an executable called `binName` in the directories of $PATH
// --- @param binName string // --- @param binName string
func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlwhich(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { binName := L.CheckString(1)
return nil, err
}
binName, err := c.StringArg(0)
if err != nil {
return nil, err
}
path, err := exec.LookPath(binName) path, err := exec.LookPath(binName)
if err != nil { if err != nil {
return c.Next(), nil l.Push(lua.LNil)
return 1
} }
return c.PushingNext1(t.Runtime, rt.StringValue(path)), nil l.Push(lua.LString(path))
return 1
} }
// inputMode(mode) // inputMode(mode)
// Sets the input mode for Hilbish's line reader. Accepts either emacs for vim // Sets the input mode for Hilbish's line reader. Accepts either emacs for vim
// --- @param mode string // --- @param mode string
func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlinputMode(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { mode := L.CheckString(1)
return nil, err
}
mode, err := c.StringArg(0)
if err != nil {
return nil, err
}
switch mode { switch mode {
case "emacs": case "emacs":
unsetVimMode() unsetVimMode()
@ -613,11 +474,9 @@ func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
case "vim": case "vim":
setVimMode("insert") setVimMode("insert")
lr.rl.InputMode = readline.Vim lr.rl.InputMode = readline.Vim
default: default: L.RaiseError("inputMode: expected vim or emacs, received " + mode)
return nil, errors.New("inputMode: expected vim or emacs, received " + mode)
} }
return 0
return c.Next(), nil
} }
// runnerMode(mode) // runnerMode(mode)
@ -627,24 +486,24 @@ func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
// sh, and lua. It also accepts a function, to which if it is passed one // sh, and lua. It also accepts a function, to which if it is passed one
// will call it to execute user input instead. // will call it to execute user input instead.
// --- @param mode string|function // --- @param mode string|function
func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlrunnerMode(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { mode := L.CheckAny(1)
return nil, err
}
mode := c.Arg(0)
switch mode.Type() { switch mode.Type() {
case rt.StringType: case lua.LTString:
switch mode.AsString() { switch mode.String() {
// no fallthrough doesnt work so eh // no fallthrough doesnt work so eh
case "hybrid", "hybridRev", "lua", "sh": runnerMode = mode case "hybrid": fallthrough
default: return nil, errors.New("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received " + mode.AsString()) case "hybridRev": fallthrough
case "lua": fallthrough
case "sh":
runnerMode = mode
default: L.RaiseError("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received %v", mode)
} }
case rt.FunctionType: runnerMode = mode case lua.LTFunction: runnerMode = mode
default: return nil, errors.New("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received " + mode.TypeName()) default: L.RaiseError("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received %v", mode)
} }
return c.Next(), nil return 0
} }
// hinter(cb) // hinter(cb)
@ -653,17 +512,11 @@ func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
// the current line and the position. It is expected to return a string // the current line and the position. It is expected to return a string
// which will be used for the hint. // which will be used for the hint.
// --- @param cb function // --- @param cb function
func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlhinter(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { hinterCb := L.CheckFunction(1)
return nil, err
}
hinterCb, err := c.ClosureArg(0)
if err != nil {
return nil, err
}
hinter = hinterCb hinter = hinterCb
return c.Next(), err return 0
} }
// highlighter(cb) // highlighter(cb)
@ -672,15 +525,9 @@ func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
// is passed the current line as typed and is expected to return a line that will // is passed the current line as typed and is expected to return a line that will
// be used to display in the line. // be used to display in the line.
// --- @param cb function // --- @param cb function
func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlhighlighter(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { highlighterCb := L.CheckFunction(1)
return nil, err
}
highlighterCb, err := c.ClosureArg(0)
if err != nil {
return nil, err
}
highlighter = highlighterCb highlighter = highlighterCb
return c.Next(), err return 0
} }

View File

@ -45,9 +45,7 @@ read(prompt) -> input? > Read input from the user, using Hilbish's line editor/i
This is a separate instance from the one Hilbish actually uses. This is a separate instance from the one Hilbish actually uses.
Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen) Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
run(cmd, returnOut) -> exitCode, stdout, stderr > Runs `cmd` in Hilbish's sh interpreter. run(cmd) > Runs `cmd` in Hilbish's sh interpreter.
If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
3rd values instead of being outputted to the terminal.
runnerMode(mode) > Sets the execution/runner mode for interactive Hilbish. This determines whether runnerMode(mode) > Sets the execution/runner mode for interactive Hilbish. This determines whether
Hilbish wll try to run input as Lua and/or sh or only do one of either. Hilbish wll try to run input as Lua and/or sh or only do one of either.

View File

@ -1,9 +1,9 @@
setRaw() > Puts the terminal in raw mode
restoreState() > Restores the last saved state of the terminal restoreState() > Restores the last saved state of the terminal
saveState() > Saves the current state of the terminal saveState() > Saves the current state of the terminal
setRaw() > Puts the terminal in raw mode
size() > Gets the dimensions of the terminal. Returns a table with `width` and `height` size() > Gets the dimensions of the terminal. Returns a table with `width` and `height`
Note: this is not the size in relation to the dimensions of the display Note: this is not the size in relation to the dimensions of the display

View File

@ -58,7 +58,7 @@ function hilbish.interval(cb, time) end
--- Changes the continued line prompt to `str` --- Changes the continued line prompt to `str`
--- @param str string --- @param str string
function hilbish.multiprompt(str) end function hilbish.mlprompt(str) end
--- Prepends `dir` to $PATH --- Prepends `dir` to $PATH
--- @param dir string --- @param dir string
@ -80,8 +80,6 @@ function hilbish.prompt(str) end
function hilbish.read(prompt) end function hilbish.read(prompt) end
--- Runs `cmd` in Hilbish's sh interpreter. --- Runs `cmd` in Hilbish's sh interpreter.
--- If returnOut is true, the outputs of `cmd` will be returned as the 2nd and
--- 3rd values instead of being outputted to the terminal.
--- @param cmd string --- @param cmd string
function hilbish.run(cmd) end function hilbish.run(cmd) end

View File

@ -2,15 +2,15 @@
local terminal = {} local terminal = {}
--- Puts the terminal in raw mode
function terminal.raw() end
--- Restores the last saved state of the terminal --- Restores the last saved state of the terminal
function terminal.restoreState() end function terminal.restoreState() end
--- Saves the current state of the terminal --- Saves the current state of the terminal
function terminal.saveState() end function terminal.saveState() end
--- Puts the terminal in raw mode
function terminal.setRaw() end
--- Gets the dimensions of the terminal. Returns a table with `width` and `height` --- Gets the dimensions of the terminal. Returns a table with `width` and `height`
--- Note: this is not the size in relation to the dimensions of the display --- Note: this is not the size in relation to the dimensions of the display
function terminal.size() end function terminal.size() end

84
exec.go
View File

@ -6,7 +6,6 @@ import (
"errors" "errors"
"os/exec" "os/exec"
"fmt" "fmt"
"io"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -16,7 +15,7 @@ import (
"hilbish/util" "hilbish/util"
rt "github.com/arnodel/golua/runtime" "github.com/yuin/gopher-lua"
"mvdan.cc/sh/v3/shell" "mvdan.cc/sh/v3/shell"
//"github.com/yuin/gopher-lua/parse" //"github.com/yuin/gopher-lua/parse"
"mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/interp"
@ -25,15 +24,15 @@ import (
) )
var errNotExec = errors.New("not executable") var errNotExec = errors.New("not executable")
var runnerMode rt.Value = rt.StringValue("hybrid") var runnerMode lua.LValue = lua.LString("hybrid")
func runInput(input string, priv bool) { func runInput(input string, priv bool) {
running = true running = true
cmdString := aliases.Resolve(input) cmdString := aliases.Resolve(input)
hooks.Em.Emit("command.preexec", input, cmdString) hooks.Em.Emit("command.preexec", input, cmdString)
if runnerMode.Type() == rt.StringType { if runnerMode.Type() == lua.LTString {
switch runnerMode.AsString() { switch runnerMode.String() {
case "hybrid": case "hybrid":
_, err := handleLua(cmdString) _, err := handleLua(cmdString)
if err == nil { if err == nil {
@ -71,23 +70,27 @@ func runInput(input string, priv bool) {
} }
} else { } else {
// can only be a string or function so // can only be a string or function so
term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 2, false) err := l.CallByParam(lua.P{
err := rt.Call(l.MainThread(), runnerMode, []rt.Value{rt.StringValue(cmdString)}, term) Fn: runnerMode,
NRet: 2,
Protect: true,
}, lua.LString(cmdString))
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
cmdFinish(124, cmdString, priv) cmdFinish(124, cmdString, priv)
return return
} }
luaexitcode := term.Get(0) // first return value (makes sense right i love stacks) luaexitcode := l.Get(-2) // first return value (makes sense right i love stacks)
runErr := term.Get(1) runErr := l.Get(-1)
l.Pop(2)
var exitCode uint8 var exitCode uint8
if code, ok := luaexitcode.TryInt(); ok { if code, ok := luaexitcode.(lua.LNumber); luaexitcode != lua.LNil && ok {
exitCode = uint8(code) exitCode = uint8(code)
} }
if runErr != rt.NilValue { if runErr != lua.LNil {
fmt.Fprintln(os.Stderr, runErr) fmt.Fprintln(os.Stderr, runErr)
} }
cmdFinish(exitCode, cmdString, priv) cmdFinish(exitCode, cmdString, priv)
@ -96,7 +99,7 @@ func runInput(input string, priv bool) {
func handleLua(cmdString string) (uint8, error) { func handleLua(cmdString 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())) fn, err := l.LoadString(cmdString)
if err != nil && noexecute { if err != nil && noexecute {
fmt.Println(err) fmt.Println(err)
/* if lerr, ok := err.(*lua.ApiError); ok { /* if lerr, ok := err.(*lua.ApiError); ok {
@ -109,9 +112,8 @@ func handleLua(cmdString string) (uint8, error) {
} }
// And if there's no syntax errors and -n isnt provided, run // And if there's no syntax errors and -n isnt provided, run
if !noexecute { if !noexecute {
if chunk != nil { l.Push(fn)
_, err = rt.Call1(l.MainThread(), rt.FunctionValue(chunk)) err = l.PCall(0, lua.MultRet, nil)
}
} }
if err == nil { if err == nil {
return 0, nil return 0, nil
@ -121,7 +123,7 @@ func handleLua(cmdString string) (uint8, error) {
} }
func handleSh(cmdString string) (uint8, error) { func handleSh(cmdString string) (uint8, error) {
_, _, err := execCommand(cmdString, true) err := execCommand(cmdString)
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) {
@ -130,7 +132,7 @@ func handleSh(cmdString string) (uint8, error) {
if err != nil { if err != nil {
break break
} }
_, _, err = execCommand(cmdString, true) err = execCommand(cmdString)
if syntax.IsIncomplete(err) || strings.HasSuffix(cmdString, "\\") { if syntax.IsIncomplete(err) || strings.HasSuffix(cmdString, "\\") {
continue continue
} else if code, ok := interp.IsExitStatus(err); ok { } else if code, ok := interp.IsExitStatus(err); ok {
@ -154,16 +156,10 @@ func handleSh(cmdString string) (uint8, error) {
} }
// Run command in sh interpreter // Run command in sh interpreter
func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) { func execCommand(cmd string) error {
file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "") file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "")
if err != nil { if err != nil {
return nil, nil, err return err
}
var stdout io.Writer = os.Stdout
var stderr io.Writer = os.Stderr
if !terminalOut {
stdout = new(bytes.Buffer)
stderr = new(bytes.Buffer)
} }
var bg bool var bg bool
@ -184,26 +180,31 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
} }
// If command is defined in Lua then run it // If command is defined in Lua then run it
luacmdArgs := rt.NewTable() luacmdArgs := l.NewTable()
for i, str := range args[1:] { for _, str := range args[1:] {
luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str)) luacmdArgs.Append(lua.LString(str))
} }
if commands[args[0]] != nil { if commands[args[0]] != nil {
luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs)) err := l.CallByParam(lua.P{
Fn: commands[args[0]],
NRet: 1,
Protect: true,
}, luacmdArgs)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error()) fmt.Fprintln(os.Stderr,
"Error in command:\n\n" + err.Error())
return interp.NewExitStatus(1) return interp.NewExitStatus(1)
} }
luaexitcode := l.Get(-1)
var exitcode uint8 var exitcode uint8
if code, ok := luaexitcode.TryInt(); ok { l.Pop(1)
if code, ok := luaexitcode.(lua.LNumber); luaexitcode != lua.LNil && ok {
exitcode = uint8(code) exitcode = uint8(code)
} else if luaexitcode != rt.NilValue {
// deregister commander
delete(commands, args[0])
fmt.Fprintf(os.Stderr, "Commander did not return number for exit code. %s, you're fired.\n", args[0])
} }
return interp.NewExitStatus(exitcode) return interp.NewExitStatus(exitcode)
@ -330,7 +331,7 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
} }
runner, _ := interp.New( runner, _ := interp.New(
interp.StdIO(os.Stdin, stdout, stderr), interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
interp.ExecHandler(exechandle), interp.ExecHandler(exechandle),
) )
@ -350,11 +351,11 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
err = runner.Run(context.TODO(), stmt) err = runner.Run(context.TODO(), stmt)
if err != nil { if err != nil {
return stdout, stderr, err return err
} }
} }
return stdout, stderr, nil return nil
} }
func lookpath(file string) error { // custom lookpath function so we know if a command is found *and* is executable func lookpath(file string) error { // custom lookpath function so we know if a command is found *and* is executable
@ -440,9 +441,6 @@ func cmdFinish(code uint8, cmdstr string, private bool) {
if interactive && !private { if interactive && !private {
handleHistory(cmdstr) handleHistory(cmdstr)
} }
util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code)), "Exit code of last exected command") util.SetField(l, hshMod, "exitCode", lua.LNumber(code), "Exit code of last exected command")
// using AsValue (to convert to lua type) on an interface which is an int hooks.Em.Emit("command.exit", code, cmdstr)
// results in it being unknown in lua .... ????
// so we allow the hook handler to take lua runtime Values
hooks.Em.Emit("command.exit", rt.IntValue(int64(code)), cmdstr)
} }

6
go.mod
View File

@ -3,18 +3,18 @@ module hilbish
go 1.17 go 1.17
require ( require (
github.com/arnodel/golua v0.0.0-20220221163911-dfcf252b6f86
github.com/blackfireio/osinfo v1.0.3 github.com/blackfireio/osinfo v1.0.3
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9
github.com/maxlandon/readline v0.1.0-beta.0.20211027085530-2b76cabb8036 github.com/maxlandon/readline v0.1.0-beta.0.20211027085530-2b76cabb8036
github.com/pborman/getopt v1.1.0 github.com/pborman/getopt v1.1.0
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
layeh.com/gopher-luar v1.0.10
mvdan.cc/sh/v3 v3.4.3 mvdan.cc/sh/v3 v3.4.3
) )
require ( require (
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/arnodel/strftime v0.1.6 // indirect
github.com/evilsocket/islazy v1.10.6 // indirect github.com/evilsocket/islazy v1.10.6 // indirect
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect
@ -27,5 +27,3 @@ replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.2022030614040
replace github.com/maxlandon/readline => ./readline replace github.com/maxlandon/readline => ./readline
replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10 replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10
replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20220329151031-261b8fbd3f78

26
go.sum
View File

@ -1,24 +1,19 @@
github.com/Rosettea/golua v0.0.0-20220329151031-261b8fbd3f78 h1:9YuMWEHn85Av2ZF60OWkcha5Wt56+i6R7hRcHKB5how=
github.com/Rosettea/golua v0.0.0-20220329151031-261b8fbd3f78/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e h1:P2XupP8SaylWaudD1DqbWtZ3mIa8OsE9635LmR+Q+lg= github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e h1:P2XupP8SaylWaudD1DqbWtZ3mIa8OsE9635LmR+Q+lg=
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs= github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e/go.mod h1:R09vh/04ILvP2Gj8/Z9Jd0Dh0ZIvaucowMEs6abQpWs=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/arnodel/edit v0.0.0-20220202110212-dfc8d7a13890/go.mod h1:AcpttpuZBaL9xl8/CX+Em4fBTUbwIkJ66RiAsJlNrBk=
github.com/arnodel/strftime v0.1.6 h1:0hc0pUvk8KhEMXE+htyaOUV42zNcf/csIbjzEFCJqsw=
github.com/arnodel/strftime v0.1.6/go.mod h1:5NbK5XqYK8QpRZpqKNt4OlxLtIB8cotkLk4KTKzJfWs=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/blackfireio/osinfo v1.0.3 h1:Yk2t2GTPjBcESv6nDSWZKO87bGMQgO+Hi9OoXPpxX8c= github.com/blackfireio/osinfo v1.0.3 h1:Yk2t2GTPjBcESv6nDSWZKO87bGMQgO+Hi9OoXPpxX8c=
github.com/blackfireio/osinfo v1.0.3/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA= github.com/blackfireio/osinfo v1.0.3/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA=
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 h1:xz6Nv3zcwO2Lila35hcb0QloCQsc38Al13RNEzWRpX4= github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 h1:xz6Nv3zcwO2Lila35hcb0QloCQsc38Al13RNEzWRpX4=
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9/go.mod h1:2wSM9zJkl1UQEFZgSd68NfCgRz1VL1jzy/RjCg+ULrs= github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9/go.mod h1:2wSM9zJkl1UQEFZgSd68NfCgRz1VL1jzy/RjCg+ULrs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc= github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc=
github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/evilsocket/islazy v1.10.6 h1:MFq000a1ByoumoJWlytqg0qon0KlBeUfPsDjY0hK0bo= github.com/evilsocket/islazy v1.10.6 h1:MFq000a1ByoumoJWlytqg0qon0KlBeUfPsDjY0hK0bo=
github.com/evilsocket/islazy v1.10.6/go.mod h1:OrwQGYg3DuZvXUfmH+KIZDjwTCbrjy48T24TUpGqVVw= github.com/evilsocket/islazy v1.10.6/go.mod h1:OrwQGYg3DuZvXUfmH+KIZDjwTCbrjy48T24TUpGqVVw=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk= github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@ -28,35 +23,32 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/layeh/gopher-luar v1.0.10 h1:8NIv4MX1Arz96kK4buGK1D87DyDxKZyq6KKvJ2diHp0=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/layeh/gopher-luar v1.0.10/go.mod h1:TPnIVCZ2RJBndm7ohXyaqfhzjlZ+OA2SZR/YwL8tECk=
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw= github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw=
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0= github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0=
github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0= github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0=
github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk= github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I= github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451 h1:d1PiN4RxzIFXCJTvRkvSkKqwtRAl5ZV4lATKtQI0B7I=
github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.8.1-0.20210923151022-86f73c517451/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw=
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210916214954-140adaaadfaf/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0= mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=

View File

@ -4,14 +4,13 @@ import (
"fmt" "fmt"
"hilbish/util" "hilbish/util"
rt "github.com/arnodel/golua/runtime"
"github.com/arnodel/golua/lib/packagelib"
"github.com/chuckpreslar/emission" "github.com/chuckpreslar/emission"
"github.com/yuin/gopher-lua"
"layeh.com/gopher-luar"
) )
type Bait struct{ type Bait struct{
Em *emission.Emitter Em *emission.Emitter
Loader packagelib.Loader
} }
func New() Bait { func New() Bait {
@ -20,27 +19,15 @@ func New() Bait {
emitter.Off(hookname, hookfunc) emitter.Off(hookname, hookfunc)
fmt.Println(err) fmt.Println(err)
}) })
b := Bait{ return Bait{
Em: emitter, Em: emitter,
} }
b.Loader = packagelib.Loader{
Load: b.loaderFunc,
Name: "bait",
}
return b
} }
func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { func (b *Bait) Loader(L *lua.LState) int {
exports := map[string]util.LuaExport{ mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{})
"catch": util.LuaExport{b.bcatch, 2, false},
"catchOnce": util.LuaExport{b.bcatchOnce, 2, false},
"throw": util.LuaExport{b.bthrow, 1, true},
}
mod := rt.NewTable()
util.SetExports(rtm, mod, exports)
util.Document(mod, util.Document(L, mod,
`Bait is the event emitter for Hilbish. Why name it bait? `Bait is the event emitter for Hilbish. Why name it bait?
Because it throws hooks that you can catch (emits events Because it throws hooks that you can catch (emits events
that you can listen to) and because why not, fun naming that you can listen to) and because why not, fun naming
@ -49,81 +36,35 @@ in on hooks to know when certain things have happened,
like when you've changed directory, a command has like when you've changed directory, a command has
failed, etc. To find all available hooks, see doc hooks.`) failed, etc. To find all available hooks, see doc hooks.`)
return rt.TableValue(mod), nil L.SetField(mod, "throw", luar.New(L, b.bthrow))
} L.SetField(mod, "catch", luar.New(L, b.bcatch))
L.SetField(mod, "catchOnce", luar.New(L, b.bcatchOnce))
func handleHook(t *rt.Thread, c *rt.GoCont, name string, catcher *rt.Closure, args ...interface{}) { L.Push(mod)
funcVal := rt.FunctionValue(catcher)
var luaArgs []rt.Value return 1
for _, arg := range args {
var luarg rt.Value
switch arg.(type) {
case rt.Value: luarg = arg.(rt.Value)
default: luarg = rt.AsValue(arg)
}
luaArgs = append(luaArgs, luarg)
}
_, err := rt.Call1(t, funcVal, luaArgs...)
if err != nil {
e := rt.NewError(rt.StringValue(err.Error()))
e = e.AddContext(c.Next(), 1)
// panicking here won't actually cause hilbish to panic and instead will
// print the error and remove the hook (look at emission recover from above)
panic(e)
}
} }
// throw(name, ...args) // throw(name, ...args)
// Throws a hook with `name` with the provided `args` // Throws a hook with `name` with the provided `args`
// --- @param name string // --- @param name string
// --- @vararg any // --- @vararg any
func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (b *Bait) bthrow(name string, args ...interface{}) {
if err := c.Check1Arg(); err != nil { b.Em.Emit(name, args...)
return nil, err
}
name, err := c.StringArg(0)
if err != nil {
return nil, err
}
ifaceSlice := make([]interface{}, len(c.Etc()))
for i, v := range c.Etc() {
ifaceSlice[i] = v
}
b.Em.Emit(name, ifaceSlice...)
return c.Next(), nil
} }
// catch(name, cb) // catch(name, cb)
// Catches a hook with `name`. Runs the `cb` when it is thrown // Catches a hook with `name`. Runs the `cb` when it is thrown
// --- @param name string // --- @param name string
// --- @param cb function // --- @param cb function
func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (b *Bait) bcatch(name string, catcher func(...interface{})) {
name, catcher, err := util.HandleStrCallback(t, c) b.Em.On(name, catcher)
if err != nil {
return nil, err
}
b.Em.On(name, func(args ...interface{}) {
handleHook(t, c, name, catcher, args...)
})
return c.Next(), nil
} }
// catchOnce(name, cb) // catchOnce(name, cb)
// Same as catch, but only runs the `cb` once and then removes the hook // Same as catch, but only runs the `cb` once and then removes the hook
// --- @param name string // --- @param name string
// --- @param cb function // --- @param cb function
func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (b *Bait) bcatchOnce(name string, catcher func(...interface{})) {
name, catcher, err := util.HandleStrCallback(t, c) b.Em.Once(name, catcher)
if err != nil {
return nil, err
}
b.Em.Once(name, func(args ...interface{}) {
handleHook(t, c, name, catcher, args...)
})
return c.Next(), nil
} }

View File

@ -3,68 +3,52 @@ package commander
import ( import (
"hilbish/util" "hilbish/util"
rt "github.com/arnodel/golua/runtime"
"github.com/arnodel/golua/lib/packagelib"
"github.com/chuckpreslar/emission" "github.com/chuckpreslar/emission"
"github.com/yuin/gopher-lua"
) )
type Commander struct{ type Commander struct{
Events *emission.Emitter Events *emission.Emitter
Loader packagelib.Loader
} }
func New() Commander { func New() Commander {
c := Commander{ return Commander{
Events: emission.NewEmitter(), Events: emission.NewEmitter(),
} }
c.Loader = packagelib.Loader{
Load: c.loaderFunc,
Name: "commander",
}
return c
} }
func (c *Commander) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { func (c *Commander) Loader(L *lua.LState) int {
exports := map[string]util.LuaExport{ exports := map[string]lua.LGFunction{
"register": util.LuaExport{c.cregister, 2, false}, "register": c.cregister,
"deregister": util.LuaExport{c.cderegister, 1, false}, "deregister": c.cderegister,
} }
mod := rt.NewTable() mod := L.SetFuncs(L.NewTable(), exports)
util.SetExports(rtm, mod, exports) util.Document(L, mod, "Commander is Hilbish's custom command library, a way to write commands in Lua.")
util.Document(mod, "Commander is Hilbish's custom command library, a way to write commands in Lua.") L.Push(mod)
return rt.TableValue(mod), nil return 1
} }
// register(name, cb) // register(name, cb)
// Register a command with `name` that runs `cb` when ran // Register a command with `name` that runs `cb` when ran
// --- @param name string // --- @param name string
// --- @param cb function // --- @param cb function
func (c *Commander) cregister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) { func (c *Commander) cregister(L *lua.LState) int {
cmdName, cmd, err := util.HandleStrCallback(t, ct) cmdName := L.CheckString(1)
if err != nil { cmd := L.CheckFunction(2)
return nil, err
}
c.Events.Emit("commandRegister", cmdName, cmd) c.Events.Emit("commandRegister", cmdName, cmd)
return ct.Next(), err return 0
} }
// deregister(name) // deregister(name)
// Deregisters any command registered with `name` // Deregisters any command registered with `name`
// --- @param name string // --- @param name string
func (c *Commander) cderegister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) { func (c *Commander) cderegister(L *lua.LState) int {
if err := ct.Check1Arg(); err != nil { cmdName := L.CheckString(1)
return nil, err
}
cmdName, err := ct.StringArg(0)
if err != nil {
return nil, err
}
c.Events.Emit("commandDeregister", cmdName) c.Events.Emit("commandDeregister", cmdName)
return ct.Next(), err return 0
} }

View File

@ -1,3 +1,5 @@
// The fs module provides easy and simple access to filesystem functions and other
// things, and acts an addition to the Lua standard library's I/O and fs functions.
package fs package fs
import ( import (
@ -6,70 +8,51 @@ import (
"strings" "strings"
"hilbish/util" "hilbish/util"
"github.com/yuin/gopher-lua"
rt "github.com/arnodel/golua/runtime"
"github.com/arnodel/golua/lib/packagelib"
) )
var Loader = packagelib.Loader{ func Loader(L *lua.LState) int {
Load: loaderFunc, mod := L.SetFuncs(L.NewTable(), exports)
Name: "fs",
}
func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { util.Document(L, mod, `The fs module provides easy and simple access to
exports := map[string]util.LuaExport{
"cd": util.LuaExport{fcd, 1, false},
"mkdir": util.LuaExport{fmkdir, 2, false},
"stat": util.LuaExport{fstat, 1, false},
"readdir": util.LuaExport{freaddir, 1, false},
}
mod := rt.NewTable()
util.SetExports(rtm, mod, exports)
util.Document(mod, `The fs module provides easy and simple access to
filesystem functions and other things, and acts an filesystem functions and other things, and acts an
addition to the Lua standard library's I/O and fs functions.`) addition to the Lua standard library's I/O and fs functions.`)
return rt.TableValue(mod), nil L.Push(mod)
return 1
}
var exports = map[string]lua.LGFunction{
"cd": fcd,
"mkdir": fmkdir,
"stat": fstat,
"readdir": freaddir,
} }
// cd(dir) // cd(dir)
// Changes directory to `dir` // Changes directory to `dir`
// --- @param dir string // --- @param dir string
func fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func fcd(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { path := L.CheckString(1)
return nil, err
} err := os.Chdir(strings.TrimSpace(path))
path, err := c.StringArg(0)
if err != nil { if err != nil {
return nil, err e := err.(*os.PathError).Err.Error()
L.RaiseError(e + ": " + path)
} }
err = os.Chdir(strings.TrimSpace(path)) return 0
if err != nil {
return nil, err
}
return c.Next(), err
} }
// mkdir(name, recursive) // mkdir(name, recursive)
// Makes a directory called `name`. If `recursive` is true, it will create its parent directories. // Makes a directory called `name`. If `recursive` is true, it will create its parent directories.
// --- @param name string // --- @param name string
// --- @param recursive boolean // --- @param recursive boolean
func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func fmkdir(L *lua.LState) int {
if err := c.CheckNArgs(2); err != nil { dirname := L.CheckString(1)
return nil, err recursive := L.ToBool(2)
}
dirname, err := c.StringArg(0)
if err != nil {
return nil, err
}
recursive, err := c.BoolArg(1)
if err != nil {
return nil, err
}
path := strings.TrimSpace(dirname) path := strings.TrimSpace(dirname)
var err error
if recursive { if recursive {
err = os.MkdirAll(path, 0744) err = os.MkdirAll(path, 0744)
@ -77,58 +60,51 @@ func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
err = os.Mkdir(path, 0744) err = os.Mkdir(path, 0744)
} }
if err != nil { if err != nil {
return nil, err L.RaiseError(err.Error() + ": " + path)
} }
return c.Next(), err return 0
} }
// stat(path) // stat(path)
// Returns info about `path` // Returns info about `path`
// --- @param path string // --- @param path string
func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func fstat(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { path := L.CheckString(1)
return nil, err
}
path, err := c.StringArg(0)
if err != nil {
return nil, err
}
pathinfo, err := os.Stat(path) pathinfo, err := os.Stat(path)
if err != nil { if err != nil {
return nil, err L.RaiseError(err.Error() + ": " + path)
return 0
} }
statTbl := rt.NewTable() statTbl := L.NewTable()
statTbl.Set(rt.StringValue("name"), rt.StringValue(pathinfo.Name())) L.SetField(statTbl, "name", lua.LString(pathinfo.Name()))
statTbl.Set(rt.StringValue("size"), rt.IntValue(pathinfo.Size())) L.SetField(statTbl, "size", lua.LNumber(pathinfo.Size()))
statTbl.Set(rt.StringValue("mode"), rt.StringValue("0" + strconv.FormatInt(int64(pathinfo.Mode().Perm()), 8))) L.SetField(statTbl, "mode", lua.LString("0" + strconv.FormatInt(int64(pathinfo.Mode().Perm()), 8)))
statTbl.Set(rt.StringValue("isDir"), rt.BoolValue(pathinfo.IsDir())) L.SetField(statTbl, "isDir", lua.LBool(pathinfo.IsDir()))
L.Push(statTbl)
return c.PushingNext1(t.Runtime, rt.TableValue(statTbl)), nil return 1
} }
// readdir(dir) // readdir(dir)
// Returns a table of files in `dir` // Returns a table of files in `dir`
// --- @param dir string // --- @param dir string
// --- @return table // --- @return table
func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func freaddir(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { dir := L.CheckString(1)
return nil, err names := L.NewTable()
}
dir, err := c.StringArg(0)
if err != nil {
return nil, err
}
names := rt.NewTable()
dirEntries, err := os.ReadDir(dir) dirEntries, err := os.ReadDir(dir)
if err != nil { if err != nil {
return nil, err L.RaiseError(err.Error() + ": " + dir)
return 0
} }
for i, entry := range dirEntries { for _, entry := range dirEntries {
names.Set(rt.IntValue(int64(i + 1)), rt.StringValue(entry.Name())) names.Append(lua.LString(entry.Name()))
} }
return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil L.Push(names)
return 1
} }

View File

@ -5,78 +5,76 @@ import (
"hilbish/util" "hilbish/util"
rt "github.com/arnodel/golua/runtime"
"github.com/arnodel/golua/lib/packagelib"
"golang.org/x/term" "golang.org/x/term"
"github.com/yuin/gopher-lua"
) )
var termState *term.State var termState *term.State
var Loader = packagelib.Loader{
Load: loaderFunc, func Loader(L *lua.LState) int {
Name: "terminal", mod := L.SetFuncs(L.NewTable(), exports)
util.Document(L, mod, "The terminal library is a simple and lower level library for certain terminal interactions.")
L.Push(mod)
return 1
} }
func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) { var exports = map[string]lua.LGFunction{
exports := map[string]util.LuaExport{ "setRaw": termraw,
"setRaw": util.LuaExport{termsetRaw, 0, false}, "restoreState": termrestoreState,
"restoreState": util.LuaExport{termrestoreState, 0, false}, "size": termsize,
"size": util.LuaExport{termsize, 0, false}, "saveState": termsaveState,
"saveState": util.LuaExport{termsaveState, 0, false},
}
mod := rt.NewTable()
util.SetExports(rtm, mod, exports)
util.Document(mod, "The terminal library is a simple and lower level library for certain terminal interactions.")
return rt.TableValue(mod), nil
} }
// size() // size()
// Gets the dimensions of the terminal. Returns a table with `width` and `height` // Gets the dimensions of the terminal. Returns a table with `width` and `height`
// Note: this is not the size in relation to the dimensions of the display // Note: this is not the size in relation to the dimensions of the display
func termsize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func termsize(L *lua.LState) int {
w, h, err := term.GetSize(int(os.Stdin.Fd())) w, h, err := term.GetSize(int(os.Stdin.Fd()))
if err != nil { if err != nil {
return nil, err L.RaiseError(err.Error())
return 0
} }
dimensions := L.NewTable()
L.SetField(dimensions, "width", lua.LNumber(w))
L.SetField(dimensions, "height", lua.LNumber(h))
dimensions := rt.NewTable() L.Push(dimensions)
dimensions.Set(rt.StringValue("width"), rt.IntValue(int64(w))) return 1
dimensions.Set(rt.StringValue("height"), rt.IntValue(int64(h)))
return c.PushingNext1(t.Runtime, rt.TableValue(dimensions)), nil
} }
// saveState() // saveState()
// Saves the current state of the terminal // Saves the current state of the terminal
func termsaveState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func termsaveState(L *lua.LState) int {
state, err := term.GetState(int(os.Stdin.Fd())) state, err := term.GetState(int(os.Stdin.Fd()))
if err != nil { if err != nil {
return nil, err L.RaiseError(err.Error())
return 0
} }
termState = state termState = state
return c.Next(), nil return 0
} }
// restoreState() // restoreState()
// Restores the last saved state of the terminal // Restores the last saved state of the terminal
func termrestoreState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func termrestoreState(L *lua.LState) int {
err := term.Restore(int(os.Stdin.Fd()), termState) err := term.Restore(int(os.Stdin.Fd()), termState)
if err != nil { if err != nil {
return nil, err L.RaiseError(err.Error())
} }
return c.Next(), nil return 0
} }
// setRaw() // setRaw()
// Puts the terminal in raw mode // Puts the terminal in raw mode
func termsetRaw(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func termraw(L *lua.LState) int {
_, err := term.MakeRaw(int(os.Stdin.Fd())) _, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil { if err != nil {
return nil, err L.RaiseError(err.Error())
} }
return c.Next(), nil return 0
} }

72
job.go
View File

@ -4,9 +4,7 @@ import (
"sync" "sync"
"os" "os"
"hilbish/util" "github.com/yuin/gopher-lua"
rt "github.com/arnodel/golua/runtime"
) )
var jobs *jobHandler var jobs *jobHandler
@ -40,28 +38,29 @@ func (j *job) setHandle(handle *os.Process) {
j.proc = handle j.proc = handle
} }
func (j *job) lua() rt.Value { func (j *job) lua() *lua.LTable {
jobFuncs := map[string]util.LuaExport{ // returns lua table for job
"stop": {j.luaStop, 0, false}, // because userdata is gross
jobFuncs := map[string]lua.LGFunction{
"stop": j.luaStop,
} }
luaJob := rt.NewTable() luaJob := l.SetFuncs(l.NewTable(), jobFuncs)
util.SetExports(l, luaJob, jobFuncs)
luaJob.Set(rt.StringValue("cmd"), rt.StringValue(j.cmd)) l.SetField(luaJob, "cmd", lua.LString(j.cmd))
luaJob.Set(rt.StringValue("running"), rt.BoolValue(j.running)) l.SetField(luaJob, "running", lua.LBool(j.running))
luaJob.Set(rt.StringValue("id"), rt.IntValue(int64(j.id))) l.SetField(luaJob, "id", lua.LNumber(j.id))
luaJob.Set(rt.StringValue("pid"), rt.IntValue(int64(j.pid))) l.SetField(luaJob, "pid", lua.LNumber(j.pid))
luaJob.Set(rt.StringValue("exitCode"), rt.IntValue(int64(j.exitCode))) l.SetField(luaJob, "exitCode", lua.LNumber(j.exitCode))
return rt.TableValue(luaJob) return luaJob
} }
func (j *job) luaStop(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (j *job) luaStop(L *lua.LState) int {
if j.running { if j.running {
j.stop() j.stop()
} }
return c.Next(), nil return 0
} }
type jobHandler struct { type jobHandler struct {
@ -97,46 +96,41 @@ func (j *jobHandler) getLatest() *job {
return j.jobs[j.latestID] return j.jobs[j.latestID]
} }
func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table {
jobFuncs := map[string]util.LuaExport{ func (j *jobHandler) loader(L *lua.LState) *lua.LTable {
"all": {j.luaAllJobs, 0, false}, jobFuncs := map[string]lua.LGFunction{
"get": {j.luaGetJob, 1, false}, "all": j.luaAllJobs,
"get": j.luaGetJob,
} }
luaJob := rt.NewTable() luaJob := l.SetFuncs(l.NewTable(), jobFuncs)
util.SetExports(rtm, luaJob, jobFuncs)
return luaJob return luaJob
} }
func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (j *jobHandler) luaGetJob(L *lua.LState) int {
j.mu.RLock() j.mu.RLock()
defer j.mu.RUnlock() defer j.mu.RUnlock()
if err := c.Check1Arg(); err != nil { jobID := L.CheckInt(1)
return nil, err job := j.jobs[jobID]
} if job != nil {
jobID, err := c.IntArg(0) return 0
if err != nil {
return nil, err
} }
L.Push(job.lua())
job := j.jobs[int(jobID)] return 1
if job == nil {
return c.Next(), nil
}
return c.PushingNext1(t.Runtime, job.lua()), nil
} }
func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (j *jobHandler) luaAllJobs(L *lua.LState) int {
j.mu.RLock() j.mu.RLock()
defer j.mu.RUnlock() defer j.mu.RUnlock()
jobTbl := rt.NewTable() jobTbl := L.NewTable()
for id, job := range j.jobs { for id, job := range j.jobs {
jobTbl.Set(rt.IntValue(int64(id)), job.lua()) jobTbl.Insert(id, job.lua())
} }
return c.PushingNext1(t.Runtime, rt.TableValue(jobTbl)), nil L.Push(jobTbl)
return 1
} }

View File

@ -89,25 +89,21 @@ end
ansikit.print = function(text) ansikit.print = function(text)
io.write(ansikit.format(text)) io.write(ansikit.format(text))
io.flush()
return ansikit return ansikit
end end
ansikit.printCode = function(code, terminate) ansikit.printCode = function(code, terminate)
io.write(ansikit.getCode(code, terminate)) io.write(ansikit.getCode(code, terminate))
io.flush()
return ansikit return ansikit
end end
ansikit.printCSI = function(code, endc) ansikit.printCSI = function(code, endc)
io.write(ansikit.getCSI(code, endc)) io.write(ansikit.getCSI(code, endc))
io.flush()
return ansikit return ansikit
end end
ansikit.println = function(text) ansikit.println = function(text)
io.write(ansikit.format(text) .. "\n") print(ansikit.print(text))
io.flush()
return ansikit return ansikit
end end

43
lua.go
View File

@ -4,42 +4,40 @@ import (
"fmt" "fmt"
"os" "os"
"hilbish/util"
"hilbish/golibs/bait" "hilbish/golibs/bait"
"hilbish/golibs/commander" "hilbish/golibs/commander"
"hilbish/golibs/fs" "hilbish/golibs/fs"
"hilbish/golibs/terminal" "hilbish/golibs/terminal"
rt "github.com/arnodel/golua/runtime" "github.com/yuin/gopher-lua"
"github.com/arnodel/golua/lib"
) )
var minimalconf = `hilbish.prompt '& '` var minimalconf = `hilbish.prompt '& '`
func luaInit() { func luaInit() {
l = rt.New(os.Stdout) l = lua.NewState()
lib.LoadAll(l) l.OpenLibs()
lib.LoadLibs(l, hilbishLoader)
// yes this is stupid, i know // yes this is stupid, i know
util.DoString(l, "hilbish = require 'hilbish'") l.PreloadModule("hilbish", hilbishLoader)
l.DoString("hilbish = require 'hilbish'")
// Add fs and terminal module module to Lua // Add fs and terminal module module to Lua
lib.LoadLibs(l, fs.Loader) l.PreloadModule("fs", fs.Loader)
lib.LoadLibs(l, terminal.Loader) l.PreloadModule("terminal", terminal.Loader)
cmds := commander.New() cmds := commander.New()
// When a command from Lua is added, register it for use // When a command from Lua is added, register it for use
cmds.Events.On("commandRegister", func(cmdName string, cmd *rt.Closure) { cmds.Events.On("commandRegister", func(cmdName string, cmd *lua.LFunction) {
commands[cmdName] = cmd commands[cmdName] = cmd
}) })
cmds.Events.On("commandDeregister", func(cmdName string) { cmds.Events.On("commandDeregister", func(cmdName string) {
delete(commands, cmdName) delete(commands, cmdName)
}) })
lib.LoadLibs(l, cmds.Loader) l.PreloadModule("commander", cmds.Loader)
hooks = bait.New() hooks = bait.New()
lib.LoadLibs(l, hooks.Loader) l.PreloadModule("bait", hooks.Loader)
// Add Ctrl-C handler // Add Ctrl-C handler
hooks.Em.On("signal.sigint", func() { hooks.Em.On("signal.sigint", func() {
@ -49,27 +47,26 @@ func luaInit() {
}) })
// Add more paths that Lua can require from // Add more paths that Lua can require from
err := util.DoString(l, "package.path = package.path .. " + requirePaths) l.DoString("package.path = package.path .. " + requirePaths)
if err != nil {
fmt.Fprintln(os.Stderr, "Could not add preload paths! Libraries will be missing. This shouldn't happen.")
}
err = util.DoFile(l, "prelude/init.lua") err := l.DoFile("prelude/init.lua")
if err != nil { if err != nil {
err = util.DoFile(l, preloadPath) err = l.DoFile(preloadPath)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, "Missing preload file, builtins may be missing.") fmt.Fprintln(os.Stderr,
"Missing preload file, builtins may be missing.")
} }
} }
} }
func runConfig(confpath string) { func runConfig(confpath string) {
if !interactive { if !interactive {
return return
} }
err := util.DoFile(l, confpath) err := l.DoFile(confpath)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err, "\nAn error has occured while loading your config! Falling back to minimal default config.") fmt.Fprintln(os.Stderr, err,
util.DoString(l, minimalconf) "\nAn error has occured while loading your config! Falling back to minimal default config.")
l.DoString(minimalconf)
} }
} }

19
main.go
View File

@ -10,21 +10,20 @@ import (
"runtime" "runtime"
"strings" "strings"
"hilbish/util"
"hilbish/golibs/bait" "hilbish/golibs/bait"
rt "github.com/arnodel/golua/runtime"
"github.com/pborman/getopt" "github.com/pborman/getopt"
"github.com/yuin/gopher-lua"
"github.com/maxlandon/readline" "github.com/maxlandon/readline"
"golang.org/x/term" "golang.org/x/term"
) )
var ( var (
l *rt.Runtime l *lua.LState
lr *lineReader lr *lineReader
commands = map[string]*rt.Closure{} commands = map[string]*lua.LFunction{}
luaCompletions = map[string]*rt.Closure{} luaCompletions = map[string]*lua.LFunction{}
confDir string confDir string
userDataDir string userDataDir string
@ -152,13 +151,13 @@ func main() {
} }
if getopt.NArgs() > 0 { if getopt.NArgs() > 0 {
luaArgs := rt.NewTable() luaArgs := l.NewTable()
for i, arg := range getopt.Args() { for _, arg := range getopt.Args() {
luaArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(arg)) luaArgs.Append(lua.LString(arg))
} }
l.GlobalEnv().Set(rt.StringValue("args"), rt.TableValue(luaArgs)) l.SetGlobal("args", luaArgs)
err := util.DoFile(l, getopt.Arg(0)) err := l.DoFile(getopt.Arg(0))
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(1) os.Exit(1)

View File

@ -8,7 +8,7 @@ local _ = require 'succulent' -- Function additions
local oldDir = hilbish.cwd() local oldDir = hilbish.cwd()
local shlvl = tonumber(os.getenv 'SHLVL') local shlvl = tonumber(os.getenv 'SHLVL')
if shlvl ~= nil then os.setenv('SHLVL', tostring(shlvl + 1)) else os.setenv('SHLVL', '0') end if shlvl ~= nil then os.setenv('SHLVL', shlvl + 1) else os.setenv('SHLVL', 0) end
-- Builtins -- Builtins
local recentDirs = {} local recentDirs = {}

201
rl.go
View File

@ -5,19 +5,18 @@ import (
"io" "io"
"strings" "strings"
"hilbish/util"
"github.com/maxlandon/readline" "github.com/maxlandon/readline"
rt "github.com/arnodel/golua/runtime" "github.com/yuin/gopher-lua"
) )
type lineReader struct { type lineReader struct {
rl *readline.Instance rl *readline.Instance
} }
var fileHist *fileHistory var fileHist *fileHistory
var hinter *rt.Closure var hinter lua.LValue = lua.LNil
var highlighter *rt.Closure var highlighter lua.LValue = lua.LNil
// other gophers might hate this naming but this is local, shut up
func newLineReader(prompt string, noHist bool) *lineReader { func newLineReader(prompt string, noHist bool) *lineReader {
rl := readline.NewInstance() rl := readline.NewInstance()
// we don't mind hilbish.read rl instances having completion, // we don't mind hilbish.read rl instances having completion,
@ -48,38 +47,46 @@ func newLineReader(prompt string, noHist bool) *lineReader {
hooks.Em.Emit("hilbish.vimAction", actionStr, args) hooks.Em.Emit("hilbish.vimAction", actionStr, args)
} }
rl.HintText = func(line []rune, pos int) []rune { rl.HintText = func(line []rune, pos int) []rune {
if hinter == nil { if hinter == lua.LNil {
return []rune{} return []rune{}
} }
retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(highlighter), err := l.CallByParam(lua.P{
rt.StringValue(string(line)), rt.IntValue(int64(pos))) Fn: hinter,
NRet: 1,
Protect: true,
}, lua.LString(string(line)), lua.LNumber(pos))
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return []rune{} return []rune{}
} }
retVal := l.Get(-1)
hintText := "" hintText := ""
if luaStr, ok := retVal.TryString(); ok { if luaStr, ok := retVal.(lua.LString); retVal != lua.LNil && ok {
hintText = luaStr hintText = luaStr.String()
} }
return []rune(hintText) return []rune(hintText)
} }
rl.SyntaxHighlighter = func(line []rune) string { rl.SyntaxHighlighter = func(line []rune) string {
if highlighter == nil { if highlighter == lua.LNil {
return string(line) return string(line)
} }
retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(highlighter), err := l.CallByParam(lua.P{
rt.StringValue(string(line))) Fn: highlighter,
NRet: 1,
Protect: true,
}, lua.LString(string(line)))
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return string(line) return string(line)
} }
retVal := l.Get(-1)
highlighted := "" highlighted := ""
if luaStr, ok := retVal.TryString(); ok { if luaStr, ok := retVal.(lua.LString); retVal != lua.LNil && ok {
highlighted = luaStr highlighted = luaStr.String()
} }
return highlighted return highlighted
@ -115,20 +122,23 @@ func newLineReader(prompt string, noHist bool) *lineReader {
return prefix, compGroup return prefix, compGroup
} else { } else {
if completecb, ok := luaCompletions["command." + fields[0]]; ok { if completecb, ok := luaCompletions["command." + fields[0]]; ok {
luaFields := rt.NewTable() luaFields := l.NewTable()
for i, f := range fields { for _, f := range fields {
luaFields.Set(rt.IntValue(int64(i + 1)), rt.StringValue(f)) luaFields.Append(lua.LString(f))
} }
err := l.CallByParam(lua.P{
// we must keep the holy 80 cols Fn: completecb,
luacompleteTable, err := rt.Call1(l.MainThread(), NRet: 1,
rt.FunctionValue(completecb), rt.StringValue(query), Protect: true,
rt.StringValue(ctx), rt.TableValue(luaFields)) }, lua.LString(query), lua.LString(ctx), luaFields)
if err != nil { if err != nil {
return "", compGroup return "", compGroup
} }
luacompleteTable := l.Get(-1)
l.Pop(1)
/* /*
as an example with git, as an example with git,
completion table should be structured like: completion table should be structured like:
@ -153,78 +163,40 @@ func newLineReader(prompt string, noHist bool) *lineReader {
it is the responsibility of the completer it is the responsibility of the completer
to work on subcommands and subcompletions to work on subcommands and subcompletions
*/ */
if cmpTbl, ok := luacompleteTable.TryTable(); ok { if cmpTbl, ok := luacompleteTable.(*lua.LTable); ok {
nextVal := rt.NilValue cmpTbl.ForEach(func(key lua.LValue, value lua.LValue) {
for { if key.Type() == lua.LTNumber {
next, val, ok := cmpTbl.Next(nextVal) // completion group
if next == rt.NilValue { if value.Type() == lua.LTTable {
break luaCmpGroup := value.(*lua.LTable)
compType := luaCmpGroup.RawGet(lua.LString("type"))
compItems := luaCmpGroup.RawGet(lua.LString("items"))
if compType.Type() != lua.LTString {
l.RaiseError("bad type name for completion (expected string, got %v)", compType.Type().String())
} }
nextVal = next if compItems.Type() != lua.LTTable {
l.RaiseError("bad items for completion (expected table, got %v)", compItems.Type().String())
_, ok = next.TryInt()
valTbl, okk := val.TryTable()
if !ok || !okk {
// TODO: error?
break
} }
luaCompType := valTbl.Get(rt.StringValue("type"))
luaCompItems := valTbl.Get(rt.StringValue("items"))
compType, ok := luaCompType.TryString()
compItems, okk := luaCompItems.TryTable()
if !ok || !okk {
// TODO: error
break
}
var items []string var items []string
itemDescriptions := make(map[string]string) itemDescriptions := make(map[string]string)
nxVal := rt.NilValue compItems.(*lua.LTable).ForEach(func(k lua.LValue, v lua.LValue) {
for { if k.Type() == lua.LTString {
nx, vl, _ := compItems.Next(nxVal)
if nx == rt.NilValue {
break
}
nxVal = nx
if tstr := nx.Type(); tstr == rt.StringType {
// ['--flag'] = {'description', '--flag-alias'} // ['--flag'] = {'description', '--flag-alias'}
nxStr, ok := nx.TryString() itm := v.(*lua.LTable)
vlTbl, okk := vl.TryTable() items = append(items, k.String())
if !ok || !okk { itemDescriptions[k.String()] = itm.RawGet(lua.LNumber(1)).String()
// TODO: error
continue
}
items = append(items, nxStr)
itemDescription, ok := vlTbl.Get(rt.IntValue(1)).TryString()
if !ok {
// TODO: error
continue
}
itemDescriptions[nxStr] = itemDescription
} else if tstr == rt.IntType {
vlStr, okk := vl.TryString()
if !okk {
// TODO: error
continue
}
items = append(items, vlStr)
} else { } else {
// TODO: error items = append(items, v.String())
continue
}
} }
})
var dispType readline.TabDisplayType var dispType readline.TabDisplayType
switch compType { switch compType.String() {
case "grid": dispType = readline.TabDisplayGrid case "grid": dispType = readline.TabDisplayGrid
case "list": dispType = readline.TabDisplayList case "list": dispType = readline.TabDisplayList
// need special cases, will implement later // need special cases, will implement later
//case "map": dispType = readline.TabDisplayMap //case "map": dispType = readline.TabDisplayMap
} }
compGroup = append(compGroup, &readline.CompletionGroup{ compGroup = append(compGroup, &readline.CompletionGroup{
DisplayType: dispType, DisplayType: dispType,
Descriptions: itemDescriptions, Descriptions: itemDescriptions,
@ -234,6 +206,8 @@ func newLineReader(prompt string, noHist bool) *lineReader {
}) })
} }
} }
})
}
} }
if len(compGroup) == 0 { if len(compGroup) == 0 {
@ -294,65 +268,56 @@ func (lr *lineReader) Resize() {
} }
// lua module // lua module
func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table { func (lr *lineReader) Loader(L *lua.LState) *lua.LTable {
lrLua := map[string]util.LuaExport{ lrLua := map[string]lua.LGFunction{
"add": {lr.luaAddHistory, 1, false}, "add": lr.luaAddHistory,
"all": {lr.luaAllHistory, 0, false}, "all": lr.luaAllHistory,
"clear": {lr.luaClearHistory, 0, false}, "clear": lr.luaClearHistory,
"get": {lr.luaGetHistory, 1, false}, "get": lr.luaGetHistory,
"size": {lr.luaSize, 0, false}, "size": lr.luaSize,
} }
mod := rt.NewTable() mod := l.SetFuncs(l.NewTable(), lrLua)
util.SetExports(rtm, mod, lrLua)
return mod return mod
} }
func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (lr *lineReader) luaAddHistory(l *lua.LState) int {
if err := c.Check1Arg(); err != nil { cmd := l.CheckString(1)
return nil, err
}
cmd, err := c.StringArg(0)
if err != nil {
return nil, err
}
lr.AddHistory(cmd) lr.AddHistory(cmd)
return c.Next(), nil return 0
} }
func (lr *lineReader) luaSize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (lr *lineReader) luaSize(L *lua.LState) int {
return c.PushingNext1(t.Runtime, rt.IntValue(int64(fileHist.Len()))), nil L.Push(lua.LNumber(fileHist.Len()))
return 1
} }
func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (lr *lineReader) luaGetHistory(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { idx := L.CheckInt(1)
return nil, err cmd, _ := fileHist.GetLine(idx)
} L.Push(lua.LString(cmd))
idx, err := c.IntArg(0)
if err != nil {
return nil, err
}
cmd, _ := fileHist.GetLine(int(idx)) return 1
return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
} }
func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (lr *lineReader) luaAllHistory(L *lua.LState) int {
tbl := rt.NewTable() tbl := L.NewTable()
size := fileHist.Len() size := fileHist.Len()
for i := 1; i < size; i++ { for i := 1; i < size; i++ {
cmd, _ := fileHist.GetLine(i) cmd, _ := fileHist.GetLine(i)
tbl.Set(rt.IntValue(int64(i)), rt.StringValue(cmd)) tbl.Append(lua.LString(cmd))
} }
return c.PushingNext1(t.Runtime, rt.TableValue(tbl)), nil L.Push(tbl)
return 1
} }
func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (lr *lineReader) luaClearHistory(l *lua.LState) int {
fileHist.clear() fileHist.clear()
return c.Next(), nil return 0
} }

View File

@ -1,56 +1,46 @@
package main package main
import ( import (
"hilbish/util" "github.com/yuin/gopher-lua"
rt "github.com/arnodel/golua/runtime"
) )
func runnerModeLoader(rtm *rt.Runtime) *rt.Table { func runnerModeLoader(L *lua.LState) *lua.LTable {
exports := map[string]util.LuaExport{ exports := map[string]lua.LGFunction{
"sh": {shRunner, 1, false}, "sh": shRunner,
"lua": {luaRunner, 1, false}, "lua": luaRunner,
"setMode": {hlrunnerMode, 1, false}, "setMode": hlrunnerMode,
} }
mod := rt.NewTable() mod := L.SetFuncs(L.NewTable(), exports)
util.SetExports(rtm, mod, exports) L.SetField(mod, "mode", runnerMode)
return mod return mod
} }
func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func shRunner(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { cmd := L.CheckString(1)
return nil, err
}
cmd, err := c.StringArg(0)
if err != nil {
return nil, err
}
exitCode, err := handleSh(cmd) exitCode, err := handleSh(cmd)
var luaErr rt.Value = rt.NilValue var luaErr lua.LValue = lua.LNil
if err != nil { if err != nil {
luaErr = rt.StringValue(err.Error()) luaErr = lua.LString(err.Error())
} }
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitCode)), luaErr), nil L.Push(lua.LNumber(exitCode))
L.Push(luaErr)
return 2
} }
func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func luaRunner(L *lua.LState) int {
if err := c.Check1Arg(); err != nil { cmd := L.CheckString(1)
return nil, err
}
cmd, err := c.StringArg(0)
if err != nil {
return nil, err
}
exitCode, err := handleLua(cmd) exitCode, err := handleLua(cmd)
var luaErr rt.Value = rt.NilValue var luaErr lua.LValue = lua.LNil
if err != nil { if err != nil {
luaErr = rt.StringValue(err.Error()) luaErr = lua.LString(err.Error())
} }
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitCode)), luaErr), nil L.Push(lua.LNumber(exitCode))
L.Push(luaErr)
return 2
} }

View File

@ -1,19 +0,0 @@
package util
import (
rt "github.com/arnodel/golua/runtime"
)
// LuaExport represents a Go function which can be exported to Lua.
type LuaExport struct {
Function rt.GoFunctionFunc
ArgNum int
Variadic bool
}
// SetExports puts the Lua function exports in the table.
func SetExports(rtm *rt.Runtime, tbl *rt.Table, exports map[string]LuaExport) {
for name, export := range exports {
rtm.SetEnvGoFunc(tbl, name, export.Function, export.ArgNum, export.Variadic)
}
}

View File

@ -1,120 +1,32 @@
package util package util
import ( import "github.com/yuin/gopher-lua"
"bufio"
"io"
"os"
rt "github.com/arnodel/golua/runtime"
)
// Document adds a documentation string to a module. // Document adds a documentation string to a module.
// It is accessible via the __doc metatable. // It is accessible via the __doc metatable.
func Document(module *rt.Table, doc string) { func Document(L *lua.LState, module lua.LValue, doc string) {
mt := module.Metatable() mt := L.GetMetatable(module)
if mt == lua.LNil {
if mt == nil { mt = L.NewTable()
mt = rt.NewTable() L.SetMetatable(module, mt)
module.SetMetatable(mt)
} }
L.SetField(mt, "__doc", lua.LString(doc))
mt.Set(rt.StringValue("__doc"), rt.StringValue(doc))
} }
// SetField sets a field in a table, adding docs for it. // SetField sets a field in a table, adding docs for it.
// It is accessible via the __docProp metatable. It is a table of the names of the fields. // It is accessible via the __docProp metatable. It is a table of the names of the fields.
func SetField(rtm *rt.Runtime, module *rt.Table, field string, value rt.Value, doc string) { func SetField(L *lua.LState, module lua.LValue, field string, value lua.LValue, doc string) {
// TODO: ^ rtm isnt needed, i should remove it mt := L.GetMetatable(module)
mt := module.Metatable() if mt == lua.LNil {
mt = L.NewTable()
docProp := L.NewTable()
L.SetField(mt, "__docProp", docProp)
if mt == nil { L.SetMetatable(module, mt)
mt = rt.NewTable()
docProp := rt.NewTable()
mt.Set(rt.StringValue("__docProp"), rt.TableValue(docProp))
module.SetMetatable(mt)
} }
docProp := mt.Get(rt.StringValue("__docProp")) docProp := L.GetTable(mt, lua.LString("__docProp"))
docProp.AsTable().Set(rt.StringValue(field), rt.StringValue(doc)) L.SetField(docProp, field, lua.LString(doc))
module.Set(rt.StringValue(field), value) L.SetField(module, field, value)
} }
// DoString runs the code string in the Lua runtime.
func DoString(rtm *rt.Runtime, code string) error {
chunk, err := rtm.CompileAndLoadLuaChunk("<string>", []byte(code), rt.TableValue(rtm.GlobalEnv()))
if chunk != nil {
_, err = rt.Call1(rtm.MainThread(), rt.FunctionValue(chunk))
}
return err
}
// DoFile runs the contents of the file in the Lua runtime.
func DoFile(rtm *rt.Runtime, path string) error {
f, err := os.Open(path)
defer f.Close()
if err != nil {
return err
}
reader := bufio.NewReader(f)
c, err := reader.ReadByte()
if err != nil && err != io.EOF {
return err
}
// unread so a char won't be missing
err = reader.UnreadByte()
if err != nil {
return err
}
var buf []byte
if c == byte('#') {
// shebang - skip that line
_, err := reader.ReadBytes('\n')
if err != nil && err != io.EOF {
return err
}
buf = []byte{'\n'}
}
for {
line, err := reader.ReadBytes('\n')
if err != nil {
if err == io.EOF {
break
}
return err
}
buf = append(buf, line...)
}
chunk, err := rtm.CompileAndLoadLuaChunk(path, buf, rt.TableValue(rtm.GlobalEnv()))
if chunk != nil {
_, err = rt.Call1(rtm.MainThread(), rt.FunctionValue(chunk))
}
return err
}
// HandleStrCallback handles function parameters for Go functions which take
// a string and a closure.
func HandleStrCallback(t *rt.Thread, c *rt.GoCont) (string, *rt.Closure, error) {
if err := c.CheckNArgs(2); err != nil {
return "", nil, err
}
name, err := c.StringArg(0)
if err != nil {
return "", nil, err
}
cb, err := c.ClosureArg(1)
if err != nil {
return "", nil, err
}
return name, cb, err
}