mirror of https://github.com/Hilbis/Hilbish
refactor!: support lua 5.4 (#129)
major rewrite which changes the library hilbish uses for it's lua vm this one implements lua 5.4, and since that's a major version bump, it's a breaking change. introduced here also is a fix for `hilbish.login` not being the right value * refactor: start work on lua 5.4 lots of commented out code ive found a go lua library which implements lua 5.4 and found an opportunity to start working on it. this commit basically removes everything and just leaves enough for the shell to be "usable" and able to start. there are no builtins or libraries (besides the `hilbish` global) * fix: call cont next in prompt function this continues execution of lua, very obvious fixes an issue with code stopping at the prompt function * fix: handle errors in user config * fix: handle panic in lua input if it is incorrect * feat: implement bait * refactor: use util funcs to run lua where possible * refactor: move arg handle function to util * feat: implement commander * feat: implement fs * feat: add hilbish module functions used by prelude * chore: use custom fork of golua * fix: make sure args to setenv are strings in prelude * feat: implement completions * chore: remove comment * feat: implement terminal * feat: implement hilbish.interval * chore: update lunacolors * chore: update golua * feat: implement aliases * feat: add input mode * feat: implement runner mode * style: use comma separated cases instead of fallthrough * feat: implement syntax highlight and hints * chore: add comments to document util functions * chore: fix dofile comment doc * refactor: make loader functions for go modules unexported * feat: implement job management * feat: add hilbish properties * feat: implement all hilbish module functions * feat: implement history interface * feat: add completion interface * feat: add module description docs * feat: implement os interface * refactor: use hlalias for add function in hilbish.alias interface * feat: make it so hilbish.run can return command output * fix: set hilbish.exitCode to last command exit code * fix(ansikit): flush on io.write * fix: deregister commander if return isnt number * feat: run script when provided path * fix: read file manually in DoFile to avoid shebang * chore: add comment for reason of unreading byte * fix: remove prelude error printing * fix: add names at chunk load for context in errors * fix: add newline at the beginning of file buffer when there is shebang this makes the line count in error messages line up properly * fix: remove extra newline after errorctrl-delete
parent
64bf7024d2
commit
0fc5f457ad
47
aliases.go
47
aliases.go
|
@ -4,7 +4,9 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/yuin/gopher-lua"
|
"hilbish/util"
|
||||||
|
|
||||||
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var aliases *aliasHandler
|
var aliases *aliasHandler
|
||||||
|
@ -64,41 +66,38 @@ func (a *aliasHandler) Resolve(cmdstr string) string {
|
||||||
|
|
||||||
// lua section
|
// lua section
|
||||||
|
|
||||||
func (a *aliasHandler) Loader(L *lua.LState) *lua.LTable {
|
func (a *aliasHandler) Loader(rtm *rt.Runtime) *rt.Table {
|
||||||
// create a lua module with our functions
|
// create a lua module with our functions
|
||||||
hshaliasesLua := map[string]lua.LGFunction{
|
hshaliasesLua := map[string]util.LuaExport{
|
||||||
"add": a.luaAdd,
|
"add": util.LuaExport{hlalias, 2, false},
|
||||||
"list": a.luaList,
|
"list": util.LuaExport{a.luaList, 0, false},
|
||||||
"del": a.luaDelete,
|
"del": util.LuaExport{a.luaDelete, 1, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
mod := L.SetFuncs(L.NewTable(), hshaliasesLua)
|
mod := rt.NewTable()
|
||||||
|
util.SetExports(rtm, mod, hshaliasesLua)
|
||||||
|
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aliasHandler) luaAdd(L *lua.LState) int {
|
func (a *aliasHandler) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
alias := L.CheckString(1)
|
aliasesList := rt.NewTable()
|
||||||
cmd := L.CheckString(2)
|
|
||||||
a.Add(alias, cmd)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *aliasHandler) luaList(L *lua.LState) int {
|
|
||||||
aliasesList := L.NewTable()
|
|
||||||
for k, v := range a.All() {
|
for k, v := range a.All() {
|
||||||
aliasesList.RawSetString(k, lua.LString(v))
|
aliasesList.Set(rt.StringValue(k), rt.StringValue(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
L.Push(aliasesList)
|
return c.PushingNext1(t.Runtime, rt.TableValue(aliasesList)), nil
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *aliasHandler) luaDelete(L *lua.LState) int {
|
func (a *aliasHandler) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
alias := L.CheckString(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
alias, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
a.Delete(alias)
|
a.Delete(alias)
|
||||||
|
|
||||||
return 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
657
api.go
657
api.go
|
@ -4,6 +4,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -14,38 +16,44 @@ import (
|
||||||
|
|
||||||
"hilbish/util"
|
"hilbish/util"
|
||||||
|
|
||||||
"github.com/yuin/gopher-lua"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
|
"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]lua.LGFunction {
|
var exports = map[string]util.LuaExport{
|
||||||
"alias": hlalias,
|
"alias": {hlalias, 2, false},
|
||||||
"appendPath": hlappendPath,
|
"appendPath": {hlappendPath, 1, false},
|
||||||
"complete": hlcomplete,
|
"complete": {hlcomplete, 2, false},
|
||||||
"cwd": hlcwd,
|
"cwd": {hlcwd, 0, false},
|
||||||
"exec": hlexec,
|
"exec": {hlexec, 1, false},
|
||||||
"runnerMode": hlrunnerMode,
|
"runnerMode": {hlrunnerMode, 1, false},
|
||||||
"goro": hlgoro,
|
"goro": {hlgoro, 1, true},
|
||||||
"highlighter": hlhighlighter,
|
"highlighter": {hlhighlighter, 1, false},
|
||||||
"hinter": hlhinter,
|
"hinter": {hlhinter, 1, false},
|
||||||
"multiprompt": hlmlprompt,
|
"multiprompt": {hlmultiprompt, 1, false},
|
||||||
"prependPath": hlprependPath,
|
"prependPath": {hlprependPath, 1, false},
|
||||||
"prompt": hlprompt,
|
"prompt": {hlprompt, 1, false},
|
||||||
"inputMode": hlinputMode,
|
"inputMode": {hlinputMode, 1, false},
|
||||||
"interval": hlinterval,
|
"interval": {hlinterval, 2, false},
|
||||||
"read": hlread,
|
"read": {hlread, 1, false},
|
||||||
"run": hlrun,
|
"run": {hlrun, 1, true},
|
||||||
"timeout": hltimeout,
|
"timeout": {hltimeout, 2, false},
|
||||||
"which": hlwhich,
|
"which": {hlwhich, 1, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
var greeting string
|
var greeting string
|
||||||
var hshMod *lua.LTable
|
var hshMod *rt.Table
|
||||||
|
var hilbishLoader = packagelib.Loader{
|
||||||
|
Load: hilbishLoad,
|
||||||
|
Name: "hilbish",
|
||||||
|
}
|
||||||
|
|
||||||
func hilbishLoader(L *lua.LState) int {
|
func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
mod := L.SetFuncs(L.NewTable(), exports)
|
mod := rt.NewTable()
|
||||||
|
util.SetExports(rtm, mod, exports)
|
||||||
hshMod = mod
|
hshMod = mod
|
||||||
|
|
||||||
host, _ := os.Hostname()
|
host, _ := os.Hostname()
|
||||||
|
@ -59,151 +67,72 @@ func hilbishLoader(L *lua.LState) int {
|
||||||
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(L, mod, "ver", lua.LString(version), "Hilbish version")
|
util.SetField(rtm, mod, "user", rt.StringValue(username), "Username of user")
|
||||||
util.SetField(L, mod, "user", lua.LString(username), "Username of user")
|
util.SetField(rtm, mod, "host", rt.StringValue(host), "Host name of the machine")
|
||||||
util.SetField(L, mod, "host", lua.LString(host), "Host name of the machine")
|
util.SetField(rtm, mod, "home", rt.StringValue(curuser.HomeDir), "Home directory of the user")
|
||||||
util.SetField(L, mod, "home", lua.LString(curuser.HomeDir), "Home directory of the user")
|
util.SetField(rtm, mod, "dataDir", rt.StringValue(dataDir), "Directory for Hilbish's data files")
|
||||||
util.SetField(L, mod, "dataDir", lua.LString(dataDir), "Directory for Hilbish's data files")
|
util.SetField(rtm, mod, "interactive", rt.BoolValue(interactive), "If this is an interactive shell")
|
||||||
util.SetField(L, mod, "interactive", lua.LBool(interactive), "If this is an interactive shell")
|
util.SetField(rtm, mod, "login", rt.BoolValue(login), "Whether this is a login shell")
|
||||||
util.SetField(L, mod, "login", lua.LBool(interactive), "Whether this is a login shell")
|
util.SetField(rtm, mod, "greeting", rt.StringValue(greeting), "Hilbish's welcome message for interactive shells. It has Lunacolors formatting.")
|
||||||
util.SetField(L, mod, "greeting", lua.LString(greeting), "Hilbish's welcome message for interactive shells. It has Lunacolors formatting.")
|
util.SetField(rtm, mod, "vimMode", rt.NilValue, "Current Vim mode of Hilbish (nil if not in Vim mode)")
|
||||||
util.SetField(l, mod, "vimMode", lua.LNil, "Current Vim mode of Hilbish (nil if not in Vim mode)")
|
util.SetField(rtm, hshMod, "exitCode", rt.IntValue(0), "Exit code of last exected command")
|
||||||
util.SetField(l, hshMod, "exitCode", lua.LNumber(0), "Exit code of last exected command")
|
util.Document(mod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.")
|
||||||
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 := L.NewTable()
|
hshuser := rt.NewTable()
|
||||||
|
|
||||||
util.SetField(L, hshuser, "config", lua.LString(confDir), "User's config directory")
|
util.SetField(rtm, hshuser, "config", rt.StringValue(confDir), "User's config directory")
|
||||||
util.SetField(L, hshuser, "data", lua.LString(userDataDir), "XDG data directory")
|
util.SetField(rtm, hshuser, "data", rt.StringValue(userDataDir), "XDG data directory")
|
||||||
util.Document(L, hshuser, "User directories to store configs and/or modules.")
|
util.Document(hshuser, "User directories to store configs and/or modules.")
|
||||||
L.SetField(mod, "userDir", hshuser)
|
mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser))
|
||||||
|
|
||||||
// hilbish.os table
|
// hilbish.os table
|
||||||
hshos := L.NewTable()
|
hshos := rt.NewTable()
|
||||||
info, _ := osinfo.GetOSInfo()
|
info, _ := osinfo.GetOSInfo()
|
||||||
|
|
||||||
util.SetField(L, hshos, "family", lua.LString(info.Family), "Family name of the current OS")
|
util.SetField(rtm, hshos, "family", rt.StringValue(info.Family), "Family name of the current OS")
|
||||||
util.SetField(L, hshos, "name", lua.LString(info.Name), "Pretty name of the current OS")
|
util.SetField(rtm, hshos, "name", rt.StringValue(info.Name), "Pretty name of the current OS")
|
||||||
util.SetField(L, hshos, "version", lua.LString(info.Version), "Version of the current OS")
|
util.SetField(rtm, hshos, "version", rt.StringValue(info.Version), "Version of the current OS")
|
||||||
util.Document(L, hshos, "OS info interface")
|
util.Document(hshos, "OS info interface")
|
||||||
L.SetField(mod, "os", hshos)
|
mod.Set(rt.StringValue("os"), rt.TableValue(hshos))
|
||||||
|
|
||||||
// hilbish.aliases table
|
// hilbish.aliases table
|
||||||
aliases = newAliases()
|
aliases = newAliases()
|
||||||
aliasesModule := aliases.Loader(L)
|
aliasesModule := aliases.Loader(rtm)
|
||||||
util.Document(L, aliasesModule, "Alias inferface for Hilbish.")
|
util.Document(aliasesModule, "Alias inferface for Hilbish.")
|
||||||
L.SetField(mod, "aliases", aliasesModule)
|
mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule))
|
||||||
|
|
||||||
// hilbish.history table
|
// hilbish.history table
|
||||||
historyModule := lr.Loader(L)
|
historyModule := lr.Loader(rtm)
|
||||||
util.Document(L, historyModule, "History interface for Hilbish.")
|
mod.Set(rt.StringValue("history"), rt.TableValue(historyModule))
|
||||||
L.SetField(mod, "history", historyModule)
|
util.Document(historyModule, "History interface for Hilbish.")
|
||||||
|
|
||||||
// hilbish.completions table
|
// hilbish.completion table
|
||||||
hshcomp := L.NewTable()
|
hshcomp := rt.NewTable()
|
||||||
|
util.SetField(rtm, hshcomp, "files",
|
||||||
|
rt.FunctionValue(rt.NewGoFunction(luaFileComplete, "files", 3, false)),
|
||||||
|
"Completer for files")
|
||||||
|
|
||||||
util.SetField(L, hshcomp, "files", L.NewFunction(luaFileComplete), "Completer for files")
|
util.SetField(rtm, hshcomp, "bins",
|
||||||
util.SetField(L, hshcomp, "bins", L.NewFunction(luaBinaryComplete), "Completer for executables/binaries")
|
rt.FunctionValue(rt.NewGoFunction(luaBinaryComplete, "bins", 3, false)),
|
||||||
util.Document(L, hshcomp, "Completions interface for Hilbish.")
|
"Completer for executables/binaries")
|
||||||
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(L)
|
runnerModule := runnerModeLoader(rtm)
|
||||||
util.Document(L, runnerModule, "Runner/exec interface for Hilbish.")
|
util.Document(runnerModule, "Runner/exec interface for Hilbish.")
|
||||||
L.SetField(mod, "runner", runnerModule)
|
mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule))
|
||||||
|
|
||||||
// hilbish.jobs table
|
// hilbish.jobs table
|
||||||
jobs = newJobHandler()
|
jobs = newJobHandler()
|
||||||
jobModule := jobs.loader(L)
|
jobModule := jobs.loader(rtm)
|
||||||
util.Document(L, jobModule, "(Background) job interface.")
|
util.Document(jobModule, "(Background) job interface.")
|
||||||
L.SetField(mod, "jobs", jobModule)
|
mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule))
|
||||||
|
|
||||||
L.Push(mod)
|
return rt.TableValue(mod), nil
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -214,24 +143,160 @@ 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(L *lua.LState) int {
|
func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
luaprompt := L.CheckString(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
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 {
|
||||||
L.Push(lua.LNil)
|
return c.Next(), nil
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
L.Push(lua.LString(input))
|
return c.PushingNext1(t.Runtime, rt.StringValue(input)), nil
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -244,52 +309,91 @@ 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(L *lua.LState) int {
|
func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
prompt = L.CheckString(1)
|
var prompt string
|
||||||
|
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 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 hlmlprompt(L *lua.LState) int {
|
func hlmultiprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
multilinePrompt = L.CheckString(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
prompt, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
multilinePrompt = prompt
|
||||||
|
|
||||||
return 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(L *lua.LState) int {
|
func hlalias(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
alias := L.CheckString(1)
|
if err := c.CheckNArgs(2); err != nil {
|
||||||
source := L.CheckString(2)
|
return nil, err
|
||||||
|
}
|
||||||
|
cmd, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
orig, err := c.StringArg(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
aliases.Add(alias, source)
|
aliases.Add(cmd, orig)
|
||||||
|
|
||||||
return 1
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendPath(dir)
|
// appendPath(dir)
|
||||||
// Appends `dir` to $PATH
|
// Appends `dir` to $PATH
|
||||||
// --- @param dir string|table
|
// --- @param dir string|table
|
||||||
func hlappendPath(L *lua.LState) int {
|
func hlappendPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
|
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
|
||||||
arg := L.Get(1)
|
if arg.Type() == rt.TableType {
|
||||||
if arg.Type() == lua.LTTable {
|
nextVal := rt.NilValue
|
||||||
arg.(*lua.LTable).ForEach(func(k lua.LValue, v lua.LValue) {
|
for {
|
||||||
appendPath(v.String())
|
next, val, ok := arg.AsTable().Next(nextVal)
|
||||||
})
|
if next == rt.NilValue {
|
||||||
} else if arg.Type() == lua.LTString {
|
break
|
||||||
appendPath(arg.String())
|
}
|
||||||
} else {
|
nextVal = next
|
||||||
L.RaiseError("bad argument to appendPath (expected string or table, got %v)", L.Get(1).Type().String())
|
|
||||||
|
valStr, ok := val.TryString()
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
appendPath(valStr)
|
||||||
|
}
|
||||||
|
} else if arg.Type() == rt.StringType {
|
||||||
|
appendPath(arg.AsString())
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("bad argument to appendPath (expected string or table, got " + arg.TypeName() + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendPath(dir string) {
|
func appendPath(dir string) {
|
||||||
|
@ -305,8 +409,14 @@ 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(L *lua.LState) int {
|
func hlexec(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
cmd := L.CheckString(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
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])
|
||||||
|
@ -328,78 +438,89 @@ func hlexec(L *lua.LState) int {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// goro(fn)
|
// goro(fn)
|
||||||
// Puts `fn` in a goroutine
|
// Puts `fn` in a goroutine
|
||||||
// --- @param fn function
|
// --- @param fn function
|
||||||
func hlgoro(L *lua.LState) int {
|
func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
fn := L.CheckFunction(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
argnum := L.GetTop()
|
return nil, err
|
||||||
args := make([]lua.LValue, argnum)
|
}
|
||||||
for i := 1; i <= argnum; i++ {
|
fn, err := c.ClosureArg(0)
|
||||||
args[i - 1] = L.Get(i)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// call fn
|
// call fn
|
||||||
go func() {
|
go func() {
|
||||||
if err := L.CallByParam(lua.P{
|
_, err := rt.Call1(l.MainThread(), rt.FunctionValue(fn), c.Etc()...)
|
||||||
Fn: fn,
|
if err != nil {
|
||||||
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 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(L *lua.LState) int {
|
func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
cb := L.CheckFunction(1)
|
if err := c.CheckNArgs(2); err != nil {
|
||||||
ms := L.CheckInt(2)
|
return nil, err
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
|
||||||
if err := L.CallByParam(lua.P{
|
_, err = rt.Call1(l.MainThread(), rt.FunctionValue(cb))
|
||||||
Fn: cb,
|
if err != nil {
|
||||||
NRet: 0,
|
return nil, err
|
||||||
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(L *lua.LState) int {
|
func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
intervalfunc := L.CheckFunction(1)
|
if err := c.CheckNArgs(2); err != nil {
|
||||||
ms := L.CheckInt(2)
|
return nil, err
|
||||||
|
}
|
||||||
|
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 lua.LValue)
|
stop := make(chan rt.Value)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
if err := L.CallByParam(lua.P{
|
_, err := rt.Call1(l.MainThread(), rt.FunctionValue(cb))
|
||||||
Fn: intervalfunc,
|
if err != nil {
|
||||||
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 <- lua.LTrue // stop the interval
|
stop <- rt.BoolValue(true) // stop the interval
|
||||||
}
|
}
|
||||||
case <-stop:
|
case <-stop:
|
||||||
ticker.Stop()
|
ticker.Stop()
|
||||||
|
@ -408,8 +529,8 @@ func hlinterval(L *lua.LState) int {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
L.Push(lua.LChannel(stop))
|
// TODO: return channel
|
||||||
return 1
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// complete(scope, cb)
|
// complete(scope, cb)
|
||||||
|
@ -422,20 +543,27 @@ func hlinterval(L *lua.LState) int {
|
||||||
// `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(L *lua.LState) int {
|
func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
scope := L.CheckString(1)
|
scope, cb, err := util.HandleStrCallback(t, c)
|
||||||
cb := L.CheckFunction(2)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
luaCompletions[scope] = cb
|
luaCompletions[scope] = cb
|
||||||
|
|
||||||
return 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// prependPath(dir)
|
// prependPath(dir)
|
||||||
// Prepends `dir` to $PATH
|
// Prepends `dir` to $PATH
|
||||||
// --- @param dir string
|
// --- @param dir string
|
||||||
func hlprependPath(L *lua.LState) int {
|
func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
dir := L.CheckString(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
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")
|
||||||
|
|
||||||
|
@ -444,29 +572,40 @@ func hlprependPath(L *lua.LState) int {
|
||||||
os.Setenv("PATH", dir + string(os.PathListSeparator) + pathenv)
|
os.Setenv("PATH", dir + string(os.PathListSeparator) + pathenv)
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(L *lua.LState) int {
|
func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
binName := L.CheckString(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
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 {
|
||||||
l.Push(lua.LNil)
|
return c.Next(), nil
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Push(lua.LString(path))
|
return c.PushingNext1(t.Runtime, rt.StringValue(path)), nil
|
||||||
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(L *lua.LState) int {
|
func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
mode := L.CheckString(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mode, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case "emacs":
|
case "emacs":
|
||||||
unsetVimMode()
|
unsetVimMode()
|
||||||
|
@ -474,9 +613,11 @@ func hlinputMode(L *lua.LState) int {
|
||||||
case "vim":
|
case "vim":
|
||||||
setVimMode("insert")
|
setVimMode("insert")
|
||||||
lr.rl.InputMode = readline.Vim
|
lr.rl.InputMode = readline.Vim
|
||||||
default: L.RaiseError("inputMode: expected vim or emacs, received " + mode)
|
default:
|
||||||
|
return nil, errors.New("inputMode: expected vim or emacs, received " + mode)
|
||||||
}
|
}
|
||||||
return 0
|
|
||||||
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runnerMode(mode)
|
// runnerMode(mode)
|
||||||
|
@ -486,24 +627,24 @@ func hlinputMode(L *lua.LState) int {
|
||||||
// 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(L *lua.LState) int {
|
func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
mode := L.CheckAny(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
switch mode.Type() {
|
return nil, err
|
||||||
case lua.LTString:
|
|
||||||
switch mode.String() {
|
|
||||||
// no fallthrough doesnt work so eh
|
|
||||||
case "hybrid": fallthrough
|
|
||||||
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 lua.LTFunction: runnerMode = mode
|
mode := c.Arg(0)
|
||||||
default: L.RaiseError("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received %v", mode)
|
|
||||||
|
switch mode.Type() {
|
||||||
|
case rt.StringType:
|
||||||
|
switch mode.AsString() {
|
||||||
|
// no fallthrough doesnt work so eh
|
||||||
|
case "hybrid", "hybridRev", "lua", "sh": runnerMode = mode
|
||||||
|
default: return nil, errors.New("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received " + mode.AsString())
|
||||||
|
}
|
||||||
|
case rt.FunctionType: runnerMode = mode
|
||||||
|
default: return nil, errors.New("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received " + mode.TypeName())
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// hinter(cb)
|
// hinter(cb)
|
||||||
|
@ -512,11 +653,17 @@ func hlrunnerMode(L *lua.LState) int {
|
||||||
// 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(L *lua.LState) int {
|
func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
hinterCb := L.CheckFunction(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hinterCb, err := c.ClosureArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
hinter = hinterCb
|
hinter = hinterCb
|
||||||
|
|
||||||
return 0
|
return c.Next(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// highlighter(cb)
|
// highlighter(cb)
|
||||||
|
@ -525,9 +672,15 @@ func hlhinter(L *lua.LState) int {
|
||||||
// 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(L *lua.LState) int {
|
func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
highlighterCb := L.CheckFunction(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
highlighterCb, err := c.ClosureArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
highlighter = highlighterCb
|
highlighter = highlighterCb
|
||||||
|
|
||||||
return 0
|
return c.Next(), err
|
||||||
}
|
}
|
||||||
|
|
84
exec.go
84
exec.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -15,7 +16,7 @@ import (
|
||||||
|
|
||||||
"hilbish/util"
|
"hilbish/util"
|
||||||
|
|
||||||
"github.com/yuin/gopher-lua"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
"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"
|
||||||
|
@ -24,15 +25,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var errNotExec = errors.New("not executable")
|
var errNotExec = errors.New("not executable")
|
||||||
var runnerMode lua.LValue = lua.LString("hybrid")
|
var runnerMode rt.Value = rt.StringValue("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() == lua.LTString {
|
if runnerMode.Type() == rt.StringType {
|
||||||
switch runnerMode.String() {
|
switch runnerMode.AsString() {
|
||||||
case "hybrid":
|
case "hybrid":
|
||||||
_, err := handleLua(cmdString)
|
_, err := handleLua(cmdString)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -70,27 +71,23 @@ func runInput(input string, priv bool) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// can only be a string or function so
|
// can only be a string or function so
|
||||||
err := l.CallByParam(lua.P{
|
term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 2, false)
|
||||||
Fn: runnerMode,
|
err := rt.Call(l.MainThread(), runnerMode, []rt.Value{rt.StringValue(cmdString)}, term)
|
||||||
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 := l.Get(-2) // first return value (makes sense right i love stacks)
|
luaexitcode := term.Get(0) // first return value (makes sense right i love stacks)
|
||||||
runErr := l.Get(-1)
|
runErr := term.Get(1)
|
||||||
l.Pop(2)
|
|
||||||
|
|
||||||
var exitCode uint8
|
var exitCode uint8
|
||||||
if code, ok := luaexitcode.(lua.LNumber); luaexitcode != lua.LNil && ok {
|
if code, ok := luaexitcode.TryInt(); ok {
|
||||||
exitCode = uint8(code)
|
exitCode = uint8(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
if runErr != lua.LNil {
|
if runErr != rt.NilValue {
|
||||||
fmt.Fprintln(os.Stderr, runErr)
|
fmt.Fprintln(os.Stderr, runErr)
|
||||||
}
|
}
|
||||||
cmdFinish(exitCode, cmdString, priv)
|
cmdFinish(exitCode, cmdString, priv)
|
||||||
|
@ -99,7 +96,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
|
||||||
fn, err := l.LoadString(cmdString)
|
chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv()))
|
||||||
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 {
|
||||||
|
@ -112,8 +109,9 @@ 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 {
|
||||||
l.Push(fn)
|
if chunk != nil {
|
||||||
err = l.PCall(0, lua.MultRet, nil)
|
_, err = rt.Call1(l.MainThread(), rt.FunctionValue(chunk))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
|
@ -123,7 +121,7 @@ func handleLua(cmdString string) (uint8, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSh(cmdString string) (uint8, error) {
|
func handleSh(cmdString string) (uint8, error) {
|
||||||
err := execCommand(cmdString)
|
_, _, err := execCommand(cmdString, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If input is incomplete, start multiline prompting
|
// If input is incomplete, start multiline prompting
|
||||||
if syntax.IsIncomplete(err) {
|
if syntax.IsIncomplete(err) {
|
||||||
|
@ -132,7 +130,7 @@ func handleSh(cmdString string) (uint8, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
err = execCommand(cmdString)
|
_, _, err = execCommand(cmdString, true)
|
||||||
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 {
|
||||||
|
@ -156,10 +154,16 @@ func handleSh(cmdString string) (uint8, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run command in sh interpreter
|
// Run command in sh interpreter
|
||||||
func execCommand(cmd string) error {
|
func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) {
|
||||||
file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "")
|
file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, nil, 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
|
||||||
|
@ -180,31 +184,26 @@ func execCommand(cmd string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If command is defined in Lua then run it
|
// If command is defined in Lua then run it
|
||||||
luacmdArgs := l.NewTable()
|
luacmdArgs := rt.NewTable()
|
||||||
for _, str := range args[1:] {
|
for i, str := range args[1:] {
|
||||||
luacmdArgs.Append(lua.LString(str))
|
luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str))
|
||||||
}
|
}
|
||||||
|
|
||||||
if commands[args[0]] != nil {
|
if commands[args[0]] != nil {
|
||||||
err := l.CallByParam(lua.P{
|
luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs))
|
||||||
Fn: commands[args[0]],
|
|
||||||
NRet: 1,
|
|
||||||
Protect: true,
|
|
||||||
}, luacmdArgs)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr,
|
fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error())
|
||||||
"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
|
||||||
|
|
||||||
l.Pop(1)
|
if code, ok := luaexitcode.TryInt(); ok {
|
||||||
|
|
||||||
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)
|
||||||
|
@ -331,7 +330,7 @@ func execCommand(cmd string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
runner, _ := interp.New(
|
runner, _ := interp.New(
|
||||||
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
|
interp.StdIO(os.Stdin, stdout, stderr),
|
||||||
interp.ExecHandler(exechandle),
|
interp.ExecHandler(exechandle),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -351,11 +350,11 @@ func execCommand(cmd string) error {
|
||||||
|
|
||||||
err = runner.Run(context.TODO(), stmt)
|
err = runner.Run(context.TODO(), stmt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return stdout, stderr, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return stdout, stderr, 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
|
||||||
|
@ -441,6 +440,9 @@ func cmdFinish(code uint8, cmdstr string, private bool) {
|
||||||
if interactive && !private {
|
if interactive && !private {
|
||||||
handleHistory(cmdstr)
|
handleHistory(cmdstr)
|
||||||
}
|
}
|
||||||
util.SetField(l, hshMod, "exitCode", lua.LNumber(code), "Exit code of last exected command")
|
util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code)), "Exit code of last exected command")
|
||||||
hooks.Em.Emit("command.exit", code, cmdstr)
|
// using AsValue (to convert to lua type) on an interface which is an int
|
||||||
|
// 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
6
go.mod
|
@ -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,3 +27,5 @@ 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
26
go.sum
|
@ -1,19 +1,24 @@
|
||||||
|
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=
|
||||||
|
@ -23,32 +28,35 @@ 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/layeh/gopher-luar v1.0.10 h1:8NIv4MX1Arz96kK4buGK1D87DyDxKZyq6KKvJ2diHp0=
|
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/layeh/gopher-luar v1.0.10/go.mod h1:TPnIVCZ2RJBndm7ohXyaqfhzjlZ+OA2SZR/YwL8tECk=
|
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
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-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
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=
|
||||||
|
|
|
@ -4,13 +4,14 @@ 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 {
|
||||||
|
@ -19,15 +20,27 @@ func New() Bait {
|
||||||
emitter.Off(hookname, hookfunc)
|
emitter.Off(hookname, hookfunc)
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
})
|
})
|
||||||
return Bait{
|
b := Bait{
|
||||||
Em: emitter,
|
Em: emitter,
|
||||||
}
|
}
|
||||||
|
b.Loader = packagelib.Loader{
|
||||||
|
Load: b.loaderFunc,
|
||||||
|
Name: "bait",
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bait) Loader(L *lua.LState) int {
|
func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{})
|
exports := map[string]util.LuaExport{
|
||||||
|
"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(L, mod,
|
util.Document(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
|
||||||
|
@ -36,35 +49,81 @@ 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.`)
|
||||||
|
|
||||||
L.SetField(mod, "throw", luar.New(L, b.bthrow))
|
return rt.TableValue(mod), nil
|
||||||
L.SetField(mod, "catch", luar.New(L, b.bcatch))
|
}
|
||||||
L.SetField(mod, "catchOnce", luar.New(L, b.bcatchOnce))
|
|
||||||
|
|
||||||
L.Push(mod)
|
func handleHook(t *rt.Thread, c *rt.GoCont, name string, catcher *rt.Closure, args ...interface{}) {
|
||||||
|
funcVal := rt.FunctionValue(catcher)
|
||||||
return 1
|
var luaArgs []rt.Value
|
||||||
|
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(name string, args ...interface{}) {
|
func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
b.Em.Emit(name, args...)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
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(name string, catcher func(...interface{})) {
|
func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
b.Em.On(name, catcher)
|
name, catcher, err := util.HandleStrCallback(t, c)
|
||||||
|
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(name string, catcher func(...interface{})) {
|
func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
b.Em.Once(name, catcher)
|
name, catcher, err := util.HandleStrCallback(t, c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Em.Once(name, func(args ...interface{}) {
|
||||||
|
handleHook(t, c, name, catcher, args...)
|
||||||
|
})
|
||||||
|
|
||||||
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,52 +3,68 @@ 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 {
|
||||||
return Commander{
|
c := Commander{
|
||||||
Events: emission.NewEmitter(),
|
Events: emission.NewEmitter(),
|
||||||
}
|
}
|
||||||
|
c.Loader = packagelib.Loader{
|
||||||
|
Load: c.loaderFunc,
|
||||||
|
Name: "commander",
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commander) Loader(L *lua.LState) int {
|
func (c *Commander) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
exports := map[string]lua.LGFunction{
|
exports := map[string]util.LuaExport{
|
||||||
"register": c.cregister,
|
"register": util.LuaExport{c.cregister, 2, false},
|
||||||
"deregister": c.cderegister,
|
"deregister": util.LuaExport{c.cderegister, 1, false},
|
||||||
}
|
}
|
||||||
mod := L.SetFuncs(L.NewTable(), exports)
|
mod := rt.NewTable()
|
||||||
util.Document(L, mod, "Commander is Hilbish's custom command library, a way to write commands in Lua.")
|
util.SetExports(rtm, mod, exports)
|
||||||
L.Push(mod)
|
util.Document(mod, "Commander is Hilbish's custom command library, a way to write commands in Lua.")
|
||||||
|
|
||||||
return 1
|
return rt.TableValue(mod), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(L *lua.LState) int {
|
func (c *Commander) cregister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||||
cmdName := L.CheckString(1)
|
cmdName, cmd, err := util.HandleStrCallback(t, ct)
|
||||||
cmd := L.CheckFunction(2)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
c.Events.Emit("commandRegister", cmdName, cmd)
|
c.Events.Emit("commandRegister", cmdName, cmd)
|
||||||
|
|
||||||
return 0
|
return ct.Next(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(L *lua.LState) int {
|
func (c *Commander) cderegister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||||
cmdName := L.CheckString(1)
|
if err := ct.Check1Arg(); err != nil {
|
||||||
|
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 0
|
return ct.Next(), err
|
||||||
}
|
}
|
||||||
|
|
122
golibs/fs/fs.go
122
golibs/fs/fs.go
|
@ -1,5 +1,3 @@
|
||||||
// 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 (
|
||||||
|
@ -8,51 +6,70 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"hilbish/util"
|
"hilbish/util"
|
||||||
"github.com/yuin/gopher-lua"
|
|
||||||
|
rt "github.com/arnodel/golua/runtime"
|
||||||
|
"github.com/arnodel/golua/lib/packagelib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Loader(L *lua.LState) int {
|
var Loader = packagelib.Loader{
|
||||||
mod := L.SetFuncs(L.NewTable(), exports)
|
Load: loaderFunc,
|
||||||
|
Name: "fs",
|
||||||
|
}
|
||||||
|
|
||||||
util.Document(L, mod, `The fs module provides easy and simple access to
|
func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
|
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.`)
|
||||||
|
|
||||||
L.Push(mod)
|
return rt.TableValue(mod), nil
|
||||||
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(L *lua.LState) int {
|
func fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
path := L.CheckString(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
err := os.Chdir(strings.TrimSpace(path))
|
}
|
||||||
|
path, err := c.StringArg(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e := err.(*os.PathError).Err.Error()
|
return nil, err
|
||||||
L.RaiseError(e + ": " + path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
err = os.Chdir(strings.TrimSpace(path))
|
||||||
|
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(L *lua.LState) int {
|
func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
dirname := L.CheckString(1)
|
if err := c.CheckNArgs(2); err != nil {
|
||||||
recursive := L.ToBool(2)
|
return nil, err
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
@ -60,51 +77,58 @@ func fmkdir(L *lua.LState) int {
|
||||||
err = os.Mkdir(path, 0744)
|
err = os.Mkdir(path, 0744)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
L.RaiseError(err.Error() + ": " + path)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return c.Next(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// stat(path)
|
// stat(path)
|
||||||
// Returns info about `path`
|
// Returns info about `path`
|
||||||
// --- @param path string
|
// --- @param path string
|
||||||
func fstat(L *lua.LState) int {
|
func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
path := L.CheckString(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
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 {
|
||||||
L.RaiseError(err.Error() + ": " + path)
|
return nil, err
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
statTbl := L.NewTable()
|
statTbl := rt.NewTable()
|
||||||
L.SetField(statTbl, "name", lua.LString(pathinfo.Name()))
|
statTbl.Set(rt.StringValue("name"), rt.StringValue(pathinfo.Name()))
|
||||||
L.SetField(statTbl, "size", lua.LNumber(pathinfo.Size()))
|
statTbl.Set(rt.StringValue("size"), rt.IntValue(pathinfo.Size()))
|
||||||
L.SetField(statTbl, "mode", lua.LString("0" + strconv.FormatInt(int64(pathinfo.Mode().Perm()), 8)))
|
statTbl.Set(rt.StringValue("mode"), rt.StringValue("0" + strconv.FormatInt(int64(pathinfo.Mode().Perm()), 8)))
|
||||||
L.SetField(statTbl, "isDir", lua.LBool(pathinfo.IsDir()))
|
statTbl.Set(rt.StringValue("isDir"), rt.BoolValue(pathinfo.IsDir()))
|
||||||
L.Push(statTbl)
|
|
||||||
|
|
||||||
return 1
|
return c.PushingNext1(t.Runtime, rt.TableValue(statTbl)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(L *lua.LState) int {
|
func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
dir := L.CheckString(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
names := L.NewTable()
|
return nil, err
|
||||||
|
}
|
||||||
|
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 {
|
||||||
L.RaiseError(err.Error() + ": " + dir)
|
return nil, err
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
for _, entry := range dirEntries {
|
for i, entry := range dirEntries {
|
||||||
names.Append(lua.LString(entry.Name()))
|
names.Set(rt.IntValue(int64(i + 1)), rt.StringValue(entry.Name()))
|
||||||
}
|
}
|
||||||
|
|
||||||
L.Push(names)
|
return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,76 +5,78 @@ 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{
|
||||||
func Loader(L *lua.LState) int {
|
Load: loaderFunc,
|
||||||
mod := L.SetFuncs(L.NewTable(), exports)
|
Name: "terminal",
|
||||||
util.Document(L, mod, "The terminal library is a simple and lower level library for certain terminal interactions.")
|
|
||||||
|
|
||||||
L.Push(mod)
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var exports = map[string]lua.LGFunction{
|
func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||||
"setRaw": termraw,
|
exports := map[string]util.LuaExport{
|
||||||
"restoreState": termrestoreState,
|
"setRaw": util.LuaExport{termsetRaw, 0, false},
|
||||||
"size": termsize,
|
"restoreState": util.LuaExport{termrestoreState, 0, false},
|
||||||
"saveState": termsaveState,
|
"size": util.LuaExport{termsize, 0, false},
|
||||||
|
"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(L *lua.LState) int {
|
func termsize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
w, h, err := term.GetSize(int(os.Stdin.Fd()))
|
w, h, err := term.GetSize(int(os.Stdin.Fd()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
L.RaiseError(err.Error())
|
return nil, err
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
dimensions := L.NewTable()
|
|
||||||
L.SetField(dimensions, "width", lua.LNumber(w))
|
|
||||||
L.SetField(dimensions, "height", lua.LNumber(h))
|
|
||||||
|
|
||||||
L.Push(dimensions)
|
dimensions := rt.NewTable()
|
||||||
return 1
|
dimensions.Set(rt.StringValue("width"), rt.IntValue(int64(w)))
|
||||||
|
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(L *lua.LState) int {
|
func termsaveState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
state, err := term.GetState(int(os.Stdin.Fd()))
|
state, err := term.GetState(int(os.Stdin.Fd()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
L.RaiseError(err.Error())
|
return nil, err
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
termState = state
|
termState = state
|
||||||
return 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// restoreState()
|
// restoreState()
|
||||||
// Restores the last saved state of the terminal
|
// Restores the last saved state of the terminal
|
||||||
func termrestoreState(L *lua.LState) int {
|
func termrestoreState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
err := term.Restore(int(os.Stdin.Fd()), termState)
|
err := term.Restore(int(os.Stdin.Fd()), termState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
L.RaiseError(err.Error())
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setRaw()
|
// setRaw()
|
||||||
// Puts the terminal in raw mode
|
// Puts the terminal in raw mode
|
||||||
func termraw(L *lua.LState) int {
|
func termsetRaw(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
_, err := term.MakeRaw(int(os.Stdin.Fd()))
|
_, err := term.MakeRaw(int(os.Stdin.Fd()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
L.RaiseError(err.Error())
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
72
job.go
72
job.go
|
@ -4,7 +4,9 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/yuin/gopher-lua"
|
"hilbish/util"
|
||||||
|
|
||||||
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var jobs *jobHandler
|
var jobs *jobHandler
|
||||||
|
@ -38,29 +40,28 @@ func (j *job) setHandle(handle *os.Process) {
|
||||||
j.proc = handle
|
j.proc = handle
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *job) lua() *lua.LTable {
|
func (j *job) lua() rt.Value {
|
||||||
// returns lua table for job
|
jobFuncs := map[string]util.LuaExport{
|
||||||
// because userdata is gross
|
"stop": {j.luaStop, 0, false},
|
||||||
jobFuncs := map[string]lua.LGFunction{
|
|
||||||
"stop": j.luaStop,
|
|
||||||
}
|
}
|
||||||
luaJob := l.SetFuncs(l.NewTable(), jobFuncs)
|
luaJob := rt.NewTable()
|
||||||
|
util.SetExports(l, luaJob, jobFuncs)
|
||||||
|
|
||||||
l.SetField(luaJob, "cmd", lua.LString(j.cmd))
|
luaJob.Set(rt.StringValue("cmd"), rt.StringValue(j.cmd))
|
||||||
l.SetField(luaJob, "running", lua.LBool(j.running))
|
luaJob.Set(rt.StringValue("running"), rt.BoolValue(j.running))
|
||||||
l.SetField(luaJob, "id", lua.LNumber(j.id))
|
luaJob.Set(rt.StringValue("id"), rt.IntValue(int64(j.id)))
|
||||||
l.SetField(luaJob, "pid", lua.LNumber(j.pid))
|
luaJob.Set(rt.StringValue("pid"), rt.IntValue(int64(j.pid)))
|
||||||
l.SetField(luaJob, "exitCode", lua.LNumber(j.exitCode))
|
luaJob.Set(rt.StringValue("exitCode"), rt.IntValue(int64(j.exitCode)))
|
||||||
|
|
||||||
return luaJob
|
return rt.TableValue(luaJob)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *job) luaStop(L *lua.LState) int {
|
func (j *job) luaStop(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
if j.running {
|
if j.running {
|
||||||
j.stop()
|
j.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type jobHandler struct {
|
type jobHandler struct {
|
||||||
|
@ -96,41 +97,46 @@ func (j *jobHandler) getLatest() *job {
|
||||||
return j.jobs[j.latestID]
|
return j.jobs[j.latestID]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table {
|
||||||
func (j *jobHandler) loader(L *lua.LState) *lua.LTable {
|
jobFuncs := map[string]util.LuaExport{
|
||||||
jobFuncs := map[string]lua.LGFunction{
|
"all": {j.luaAllJobs, 0, false},
|
||||||
"all": j.luaAllJobs,
|
"get": {j.luaGetJob, 1, false},
|
||||||
"get": j.luaGetJob,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
luaJob := l.SetFuncs(l.NewTable(), jobFuncs)
|
luaJob := rt.NewTable()
|
||||||
|
util.SetExports(rtm, luaJob, jobFuncs)
|
||||||
|
|
||||||
return luaJob
|
return luaJob
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jobHandler) luaGetJob(L *lua.LState) int {
|
func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
j.mu.RLock()
|
j.mu.RLock()
|
||||||
defer j.mu.RUnlock()
|
defer j.mu.RUnlock()
|
||||||
|
|
||||||
jobID := L.CheckInt(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
job := j.jobs[jobID]
|
return nil, err
|
||||||
if job != nil {
|
}
|
||||||
return 0
|
jobID, err := c.IntArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
L.Push(job.lua())
|
|
||||||
|
|
||||||
return 1
|
job := j.jobs[int(jobID)]
|
||||||
|
if job == nil {
|
||||||
|
return c.Next(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.PushingNext1(t.Runtime, job.lua()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *jobHandler) luaAllJobs(L *lua.LState) int {
|
func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
j.mu.RLock()
|
j.mu.RLock()
|
||||||
defer j.mu.RUnlock()
|
defer j.mu.RUnlock()
|
||||||
|
|
||||||
jobTbl := L.NewTable()
|
jobTbl := rt.NewTable()
|
||||||
for id, job := range j.jobs {
|
for id, job := range j.jobs {
|
||||||
jobTbl.Insert(id, job.lua())
|
jobTbl.Set(rt.IntValue(int64(id)), job.lua())
|
||||||
}
|
}
|
||||||
|
|
||||||
L.Push(jobTbl)
|
return c.PushingNext1(t.Runtime, rt.TableValue(jobTbl)), nil
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,21 +89,25 @@ 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)
|
||||||
print(ansikit.print(text))
|
io.write(ansikit.format(text) .. "\n")
|
||||||
|
io.flush()
|
||||||
return ansikit
|
return ansikit
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
43
lua.go
43
lua.go
|
@ -4,40 +4,42 @@ 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"
|
||||||
|
|
||||||
"github.com/yuin/gopher-lua"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
|
"github.com/arnodel/golua/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
var minimalconf = `hilbish.prompt '& '`
|
var minimalconf = `hilbish.prompt '& '`
|
||||||
|
|
||||||
func luaInit() {
|
func luaInit() {
|
||||||
l = lua.NewState()
|
l = rt.New(os.Stdout)
|
||||||
l.OpenLibs()
|
lib.LoadAll(l)
|
||||||
|
|
||||||
|
lib.LoadLibs(l, hilbishLoader)
|
||||||
// yes this is stupid, i know
|
// yes this is stupid, i know
|
||||||
l.PreloadModule("hilbish", hilbishLoader)
|
util.DoString(l, "hilbish = require 'hilbish'")
|
||||||
l.DoString("hilbish = require 'hilbish'")
|
|
||||||
|
|
||||||
// Add fs and terminal module module to Lua
|
// Add fs and terminal module module to Lua
|
||||||
l.PreloadModule("fs", fs.Loader)
|
lib.LoadLibs(l, fs.Loader)
|
||||||
l.PreloadModule("terminal", terminal.Loader)
|
lib.LoadLibs(l, 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 *lua.LFunction) {
|
cmds.Events.On("commandRegister", func(cmdName string, cmd *rt.Closure) {
|
||||||
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)
|
||||||
})
|
})
|
||||||
l.PreloadModule("commander", cmds.Loader)
|
lib.LoadLibs(l, cmds.Loader)
|
||||||
|
|
||||||
hooks = bait.New()
|
hooks = bait.New()
|
||||||
l.PreloadModule("bait", hooks.Loader)
|
lib.LoadLibs(l, hooks.Loader)
|
||||||
|
|
||||||
// Add Ctrl-C handler
|
// Add Ctrl-C handler
|
||||||
hooks.Em.On("signal.sigint", func() {
|
hooks.Em.On("signal.sigint", func() {
|
||||||
|
@ -47,26 +49,27 @@ func luaInit() {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add more paths that Lua can require from
|
// Add more paths that Lua can require from
|
||||||
l.DoString("package.path = package.path .. " + requirePaths)
|
err := util.DoString(l, "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 := l.DoFile("prelude/init.lua")
|
err = util.DoFile(l, "prelude/init.lua")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = l.DoFile(preloadPath)
|
err = util.DoFile(l, preloadPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr,
|
fmt.Fprintln(os.Stderr, "Missing preload file, builtins may be missing.")
|
||||||
"Missing preload file, builtins may be missing.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runConfig(confpath string) {
|
func runConfig(confpath string) {
|
||||||
if !interactive {
|
if !interactive {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := l.DoFile(confpath)
|
err := util.DoFile(l, confpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err,
|
fmt.Fprintln(os.Stderr, err, "\nAn error has occured while loading your config! Falling back to minimal default config.")
|
||||||
"\nAn error has occured while loading your config! Falling back to minimal default config.")
|
util.DoString(l, minimalconf)
|
||||||
|
|
||||||
l.DoString(minimalconf)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
main.go
19
main.go
|
@ -10,20 +10,21 @@ 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 *lua.LState
|
l *rt.Runtime
|
||||||
lr *lineReader
|
lr *lineReader
|
||||||
|
|
||||||
commands = map[string]*lua.LFunction{}
|
commands = map[string]*rt.Closure{}
|
||||||
luaCompletions = map[string]*lua.LFunction{}
|
luaCompletions = map[string]*rt.Closure{}
|
||||||
|
|
||||||
confDir string
|
confDir string
|
||||||
userDataDir string
|
userDataDir string
|
||||||
|
@ -151,13 +152,13 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if getopt.NArgs() > 0 {
|
if getopt.NArgs() > 0 {
|
||||||
luaArgs := l.NewTable()
|
luaArgs := rt.NewTable()
|
||||||
for _, arg := range getopt.Args() {
|
for i, arg := range getopt.Args() {
|
||||||
luaArgs.Append(lua.LString(arg))
|
luaArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(arg))
|
||||||
}
|
}
|
||||||
|
|
||||||
l.SetGlobal("args", luaArgs)
|
l.GlobalEnv().Set(rt.StringValue("args"), rt.TableValue(luaArgs))
|
||||||
err := l.DoFile(getopt.Arg(0))
|
err := util.DoFile(l, 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)
|
||||||
|
|
|
@ -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', shlvl + 1) else os.setenv('SHLVL', 0) end
|
if shlvl ~= nil then os.setenv('SHLVL', tostring(shlvl + 1)) else os.setenv('SHLVL', '0') end
|
||||||
|
|
||||||
-- Builtins
|
-- Builtins
|
||||||
local recentDirs = {}
|
local recentDirs = {}
|
||||||
|
|
205
rl.go
205
rl.go
|
@ -5,18 +5,19 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"hilbish/util"
|
||||||
|
|
||||||
"github.com/maxlandon/readline"
|
"github.com/maxlandon/readline"
|
||||||
"github.com/yuin/gopher-lua"
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
type lineReader struct {
|
type lineReader struct {
|
||||||
rl *readline.Instance
|
rl *readline.Instance
|
||||||
}
|
}
|
||||||
var fileHist *fileHistory
|
var fileHist *fileHistory
|
||||||
var hinter lua.LValue = lua.LNil
|
var hinter *rt.Closure
|
||||||
var highlighter lua.LValue = lua.LNil
|
var highlighter *rt.Closure
|
||||||
|
|
||||||
// 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,
|
||||||
|
@ -47,46 +48,38 @@ 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 == lua.LNil {
|
if hinter == nil {
|
||||||
return []rune{}
|
return []rune{}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := l.CallByParam(lua.P{
|
retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(highlighter),
|
||||||
Fn: hinter,
|
rt.StringValue(string(line)), rt.IntValue(int64(pos)))
|
||||||
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.(lua.LString); retVal != lua.LNil && ok {
|
if luaStr, ok := retVal.TryString(); ok {
|
||||||
hintText = luaStr.String()
|
hintText = luaStr
|
||||||
}
|
}
|
||||||
|
|
||||||
return []rune(hintText)
|
return []rune(hintText)
|
||||||
}
|
}
|
||||||
rl.SyntaxHighlighter = func(line []rune) string {
|
rl.SyntaxHighlighter = func(line []rune) string {
|
||||||
if highlighter == lua.LNil {
|
if highlighter == nil {
|
||||||
return string(line)
|
return string(line)
|
||||||
}
|
}
|
||||||
err := l.CallByParam(lua.P{
|
retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(highlighter),
|
||||||
Fn: highlighter,
|
rt.StringValue(string(line)))
|
||||||
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.(lua.LString); retVal != lua.LNil && ok {
|
if luaStr, ok := retVal.TryString(); ok {
|
||||||
highlighted = luaStr.String()
|
highlighted = luaStr
|
||||||
}
|
}
|
||||||
|
|
||||||
return highlighted
|
return highlighted
|
||||||
|
@ -122,23 +115,20 @@ 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 := l.NewTable()
|
luaFields := rt.NewTable()
|
||||||
for _, f := range fields {
|
for i, f := range fields {
|
||||||
luaFields.Append(lua.LString(f))
|
luaFields.Set(rt.IntValue(int64(i + 1)), rt.StringValue(f))
|
||||||
}
|
}
|
||||||
err := l.CallByParam(lua.P{
|
|
||||||
Fn: completecb,
|
// we must keep the holy 80 cols
|
||||||
NRet: 1,
|
luacompleteTable, err := rt.Call1(l.MainThread(),
|
||||||
Protect: true,
|
rt.FunctionValue(completecb), rt.StringValue(query),
|
||||||
}, lua.LString(query), lua.LString(ctx), luaFields)
|
rt.StringValue(ctx), rt.TableValue(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:
|
||||||
|
@ -163,40 +153,78 @@ 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.(*lua.LTable); ok {
|
if cmpTbl, ok := luacompleteTable.TryTable(); ok {
|
||||||
cmpTbl.ForEach(func(key lua.LValue, value lua.LValue) {
|
nextVal := rt.NilValue
|
||||||
if key.Type() == lua.LTNumber {
|
for {
|
||||||
// completion group
|
next, val, ok := cmpTbl.Next(nextVal)
|
||||||
if value.Type() == lua.LTTable {
|
if next == rt.NilValue {
|
||||||
luaCmpGroup := value.(*lua.LTable)
|
break
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
if compItems.Type() != lua.LTTable {
|
nextVal = next
|
||||||
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)
|
||||||
compItems.(*lua.LTable).ForEach(func(k lua.LValue, v lua.LValue) {
|
nxVal := rt.NilValue
|
||||||
if k.Type() == lua.LTString {
|
for {
|
||||||
// ['--flag'] = {'description', '--flag-alias'}
|
nx, vl, _ := compItems.Next(nxVal)
|
||||||
itm := v.(*lua.LTable)
|
if nx == rt.NilValue {
|
||||||
items = append(items, k.String())
|
break
|
||||||
itemDescriptions[k.String()] = itm.RawGet(lua.LNumber(1)).String()
|
}
|
||||||
} else {
|
nxVal = nx
|
||||||
items = append(items, v.String())
|
|
||||||
|
if tstr := nx.Type(); tstr == rt.StringType {
|
||||||
|
// ['--flag'] = {'description', '--flag-alias'}
|
||||||
|
nxStr, ok := nx.TryString()
|
||||||
|
vlTbl, okk := vl.TryTable()
|
||||||
|
if !ok || !okk {
|
||||||
|
// 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 {
|
||||||
|
// TODO: error
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
var dispType readline.TabDisplayType
|
var dispType readline.TabDisplayType
|
||||||
switch compType.String() {
|
switch compType {
|
||||||
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,
|
||||||
|
@ -206,8 +234,6 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(compGroup) == 0 {
|
if len(compGroup) == 0 {
|
||||||
|
@ -268,56 +294,65 @@ func (lr *lineReader) Resize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// lua module
|
// lua module
|
||||||
func (lr *lineReader) Loader(L *lua.LState) *lua.LTable {
|
func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table {
|
||||||
lrLua := map[string]lua.LGFunction{
|
lrLua := map[string]util.LuaExport{
|
||||||
"add": lr.luaAddHistory,
|
"add": {lr.luaAddHistory, 1, false},
|
||||||
"all": lr.luaAllHistory,
|
"all": {lr.luaAllHistory, 0, false},
|
||||||
"clear": lr.luaClearHistory,
|
"clear": {lr.luaClearHistory, 0, false},
|
||||||
"get": lr.luaGetHistory,
|
"get": {lr.luaGetHistory, 1, false},
|
||||||
"size": lr.luaSize,
|
"size": {lr.luaSize, 0, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
mod := l.SetFuncs(l.NewTable(), lrLua)
|
mod := rt.NewTable()
|
||||||
|
util.SetExports(rtm, mod, lrLua)
|
||||||
|
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) luaAddHistory(l *lua.LState) int {
|
func (lr *lineReader) luaAddHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
cmd := l.CheckString(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cmd, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
lr.AddHistory(cmd)
|
lr.AddHistory(cmd)
|
||||||
|
|
||||||
return 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) luaSize(L *lua.LState) int {
|
func (lr *lineReader) luaSize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
L.Push(lua.LNumber(fileHist.Len()))
|
return c.PushingNext1(t.Runtime, rt.IntValue(int64(fileHist.Len()))), nil
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) luaGetHistory(L *lua.LState) int {
|
func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
idx := L.CheckInt(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
cmd, _ := fileHist.GetLine(idx)
|
return nil, err
|
||||||
L.Push(lua.LString(cmd))
|
}
|
||||||
|
idx, err := c.IntArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return 1
|
cmd, _ := fileHist.GetLine(int(idx))
|
||||||
|
|
||||||
|
return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) luaAllHistory(L *lua.LState) int {
|
func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
tbl := L.NewTable()
|
tbl := rt.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.Append(lua.LString(cmd))
|
tbl.Set(rt.IntValue(int64(i)), rt.StringValue(cmd))
|
||||||
}
|
}
|
||||||
|
|
||||||
L.Push(tbl)
|
return c.PushingNext1(t.Runtime, rt.TableValue(tbl)), nil
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *lineReader) luaClearHistory(l *lua.LState) int {
|
func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
fileHist.clear()
|
fileHist.clear()
|
||||||
return 0
|
return c.Next(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +1,56 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/yuin/gopher-lua"
|
"hilbish/util"
|
||||||
|
|
||||||
|
rt "github.com/arnodel/golua/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runnerModeLoader(L *lua.LState) *lua.LTable {
|
func runnerModeLoader(rtm *rt.Runtime) *rt.Table {
|
||||||
exports := map[string]lua.LGFunction{
|
exports := map[string]util.LuaExport{
|
||||||
"sh": shRunner,
|
"sh": {shRunner, 1, false},
|
||||||
"lua": luaRunner,
|
"lua": {luaRunner, 1, false},
|
||||||
"setMode": hlrunnerMode,
|
"setMode": {hlrunnerMode, 1, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
mod := L.SetFuncs(L.NewTable(), exports)
|
mod := rt.NewTable()
|
||||||
L.SetField(mod, "mode", runnerMode)
|
util.SetExports(rtm, mod, exports)
|
||||||
|
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
|
|
||||||
func shRunner(L *lua.LState) int {
|
func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
cmd := L.CheckString(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cmd, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
exitCode, err := handleSh(cmd)
|
exitCode, err := handleSh(cmd)
|
||||||
var luaErr lua.LValue = lua.LNil
|
var luaErr rt.Value = rt.NilValue
|
||||||
if err != nil {
|
if err != nil {
|
||||||
luaErr = lua.LString(err.Error())
|
luaErr = rt.StringValue(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
L.Push(lua.LNumber(exitCode))
|
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitCode)), luaErr), nil
|
||||||
L.Push(luaErr)
|
|
||||||
|
|
||||||
return 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func luaRunner(L *lua.LState) int {
|
func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||||
cmd := L.CheckString(1)
|
if err := c.Check1Arg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cmd, err := c.StringArg(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
exitCode, err := handleLua(cmd)
|
exitCode, err := handleLua(cmd)
|
||||||
var luaErr lua.LValue = lua.LNil
|
var luaErr rt.Value = rt.NilValue
|
||||||
if err != nil {
|
if err != nil {
|
||||||
luaErr = lua.LString(err.Error())
|
luaErr = rt.StringValue(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
L.Push(lua.LNumber(exitCode))
|
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitCode)), luaErr), nil
|
||||||
L.Push(luaErr)
|
|
||||||
|
|
||||||
return 2
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
122
util/util.go
122
util/util.go
|
@ -1,32 +1,120 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import "github.com/yuin/gopher-lua"
|
import (
|
||||||
|
"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(L *lua.LState, module lua.LValue, doc string) {
|
func Document(module *rt.Table, doc string) {
|
||||||
mt := L.GetMetatable(module)
|
mt := module.Metatable()
|
||||||
if mt == lua.LNil {
|
|
||||||
mt = L.NewTable()
|
if mt == nil {
|
||||||
L.SetMetatable(module, mt)
|
mt = rt.NewTable()
|
||||||
|
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(L *lua.LState, module lua.LValue, field string, value lua.LValue, doc string) {
|
func SetField(rtm *rt.Runtime, module *rt.Table, field string, value rt.Value, doc string) {
|
||||||
mt := L.GetMetatable(module)
|
// TODO: ^ rtm isnt needed, i should remove it
|
||||||
if mt == lua.LNil {
|
mt := module.Metatable()
|
||||||
mt = L.NewTable()
|
|
||||||
docProp := L.NewTable()
|
|
||||||
L.SetField(mt, "__docProp", docProp)
|
|
||||||
|
|
||||||
L.SetMetatable(module, mt)
|
if mt == nil {
|
||||||
|
mt = rt.NewTable()
|
||||||
|
docProp := rt.NewTable()
|
||||||
|
mt.Set(rt.StringValue("__docProp"), rt.TableValue(docProp))
|
||||||
|
|
||||||
|
module.SetMetatable(mt)
|
||||||
}
|
}
|
||||||
docProp := L.GetTable(mt, lua.LString("__docProp"))
|
docProp := mt.Get(rt.StringValue("__docProp"))
|
||||||
|
|
||||||
L.SetField(docProp, field, lua.LString(doc))
|
docProp.AsTable().Set(rt.StringValue(field), rt.StringValue(doc))
|
||||||
L.SetField(module, field, value)
|
module.Set(rt.StringValue(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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue