mirror of https://github.com/Hilbis/Hilbish
Compare commits
2 Commits
64bf7024d2
...
69d38d7048
Author | SHA1 | Date |
---|---|---|
TorchedSammy | 69d38d7048 | |
sammyette | 0fc5f457ad |
47
aliases.go
47
aliases.go
|
@ -4,7 +4,9 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/yuin/gopher-lua"
|
||||
"hilbish/util"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
)
|
||||
|
||||
var aliases *aliasHandler
|
||||
|
@ -64,41 +66,38 @@ func (a *aliasHandler) Resolve(cmdstr string) string {
|
|||
|
||||
// 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
|
||||
hshaliasesLua := map[string]lua.LGFunction{
|
||||
"add": a.luaAdd,
|
||||
"list": a.luaList,
|
||||
"del": a.luaDelete,
|
||||
hshaliasesLua := map[string]util.LuaExport{
|
||||
"add": util.LuaExport{hlalias, 2, false},
|
||||
"list": util.LuaExport{a.luaList, 0, false},
|
||||
"del": util.LuaExport{a.luaDelete, 1, false},
|
||||
}
|
||||
|
||||
mod := L.SetFuncs(L.NewTable(), hshaliasesLua)
|
||||
mod := rt.NewTable()
|
||||
util.SetExports(rtm, mod, hshaliasesLua)
|
||||
|
||||
return mod
|
||||
}
|
||||
|
||||
func (a *aliasHandler) luaAdd(L *lua.LState) int {
|
||||
alias := L.CheckString(1)
|
||||
cmd := L.CheckString(2)
|
||||
a.Add(alias, cmd)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (a *aliasHandler) luaList(L *lua.LState) int {
|
||||
aliasesList := L.NewTable()
|
||||
func (a *aliasHandler) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
aliasesList := rt.NewTable()
|
||||
for k, v := range a.All() {
|
||||
aliasesList.RawSetString(k, lua.LString(v))
|
||||
aliasesList.Set(rt.StringValue(k), rt.StringValue(v))
|
||||
}
|
||||
|
||||
L.Push(aliasesList)
|
||||
|
||||
return 1
|
||||
return c.PushingNext1(t.Runtime, rt.TableValue(aliasesList)), nil
|
||||
}
|
||||
|
||||
func (a *aliasHandler) luaDelete(L *lua.LState) int {
|
||||
alias := L.CheckString(1)
|
||||
func (a *aliasHandler) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
alias, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.Delete(alias)
|
||||
|
||||
return 0
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
|
657
api.go
657
api.go
|
@ -4,6 +4,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -14,38 +16,44 @@ import (
|
|||
|
||||
"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/blackfireio/osinfo"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
)
|
||||
|
||||
var exports = map[string]lua.LGFunction {
|
||||
"alias": hlalias,
|
||||
"appendPath": hlappendPath,
|
||||
"complete": hlcomplete,
|
||||
"cwd": hlcwd,
|
||||
"exec": hlexec,
|
||||
"runnerMode": hlrunnerMode,
|
||||
"goro": hlgoro,
|
||||
"highlighter": hlhighlighter,
|
||||
"hinter": hlhinter,
|
||||
"multiprompt": hlmlprompt,
|
||||
"prependPath": hlprependPath,
|
||||
"prompt": hlprompt,
|
||||
"inputMode": hlinputMode,
|
||||
"interval": hlinterval,
|
||||
"read": hlread,
|
||||
"run": hlrun,
|
||||
"timeout": hltimeout,
|
||||
"which": hlwhich,
|
||||
var exports = map[string]util.LuaExport{
|
||||
"alias": {hlalias, 2, false},
|
||||
"appendPath": {hlappendPath, 1, false},
|
||||
"complete": {hlcomplete, 2, false},
|
||||
"cwd": {hlcwd, 0, false},
|
||||
"exec": {hlexec, 1, false},
|
||||
"runnerMode": {hlrunnerMode, 1, false},
|
||||
"goro": {hlgoro, 1, true},
|
||||
"highlighter": {hlhighlighter, 1, false},
|
||||
"hinter": {hlhinter, 1, false},
|
||||
"multiprompt": {hlmultiprompt, 1, false},
|
||||
"prependPath": {hlprependPath, 1, false},
|
||||
"prompt": {hlprompt, 1, false},
|
||||
"inputMode": {hlinputMode, 1, false},
|
||||
"interval": {hlinterval, 2, false},
|
||||
"read": {hlread, 1, false},
|
||||
"run": {hlrun, 1, true},
|
||||
"timeout": {hltimeout, 2, false},
|
||||
"which": {hlwhich, 1, false},
|
||||
}
|
||||
|
||||
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 {
|
||||
mod := L.SetFuncs(L.NewTable(), exports)
|
||||
func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
||||
mod := rt.NewTable()
|
||||
util.SetExports(rtm, mod, exports)
|
||||
hshMod = mod
|
||||
|
||||
host, _ := os.Hostname()
|
||||
|
@ -59,151 +67,72 @@ func hilbishLoader(L *lua.LState) int {
|
|||
The nice lil shell for {blue}Lua{reset} fanatics!
|
||||
Check out the {blue}{bold}guide{reset} command to get started.
|
||||
`
|
||||
|
||||
util.SetField(L, mod, "ver", lua.LString(version), "Hilbish version")
|
||||
util.SetField(L, mod, "user", lua.LString(username), "Username of user")
|
||||
util.SetField(L, mod, "host", lua.LString(host), "Host name of the machine")
|
||||
util.SetField(L, mod, "home", lua.LString(curuser.HomeDir), "Home directory of the user")
|
||||
util.SetField(L, mod, "dataDir", lua.LString(dataDir), "Directory for Hilbish's data files")
|
||||
util.SetField(L, mod, "interactive", lua.LBool(interactive), "If this is an interactive shell")
|
||||
util.SetField(L, mod, "login", lua.LBool(interactive), "Whether this is a login shell")
|
||||
util.SetField(L, mod, "greeting", lua.LString(greeting), "Hilbish's welcome message for interactive shells. It has Lunacolors formatting.")
|
||||
util.SetField(l, mod, "vimMode", lua.LNil, "Current Vim mode of Hilbish (nil if not in Vim mode)")
|
||||
util.SetField(l, hshMod, "exitCode", lua.LNumber(0), "Exit code of last exected command")
|
||||
util.Document(L, mod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.")
|
||||
util.SetField(rtm, mod, "ver", rt.StringValue(version), "Hilbish version")
|
||||
util.SetField(rtm, mod, "user", rt.StringValue(username), "Username of user")
|
||||
util.SetField(rtm, mod, "host", rt.StringValue(host), "Host name of the machine")
|
||||
util.SetField(rtm, mod, "home", rt.StringValue(curuser.HomeDir), "Home directory of the user")
|
||||
util.SetField(rtm, mod, "dataDir", rt.StringValue(dataDir), "Directory for Hilbish's data files")
|
||||
util.SetField(rtm, mod, "interactive", rt.BoolValue(interactive), "If this is an interactive shell")
|
||||
util.SetField(rtm, mod, "login", rt.BoolValue(login), "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(rtm, mod, "vimMode", rt.NilValue, "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.Document(mod, "Hilbish's core API, containing submodules and functions which relate to the shell itself.")
|
||||
|
||||
// hilbish.userDir table
|
||||
hshuser := L.NewTable()
|
||||
hshuser := rt.NewTable()
|
||||
|
||||
util.SetField(L, hshuser, "config", lua.LString(confDir), "User's config directory")
|
||||
util.SetField(L, hshuser, "data", lua.LString(userDataDir), "XDG data directory")
|
||||
util.Document(L, hshuser, "User directories to store configs and/or modules.")
|
||||
L.SetField(mod, "userDir", hshuser)
|
||||
util.SetField(rtm, hshuser, "config", rt.StringValue(confDir), "User's config directory")
|
||||
util.SetField(rtm, hshuser, "data", rt.StringValue(userDataDir), "XDG data directory")
|
||||
util.Document(hshuser, "User directories to store configs and/or modules.")
|
||||
mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser))
|
||||
|
||||
// hilbish.os table
|
||||
hshos := L.NewTable()
|
||||
hshos := rt.NewTable()
|
||||
info, _ := osinfo.GetOSInfo()
|
||||
|
||||
util.SetField(L, hshos, "family", lua.LString(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(L, hshos, "version", lua.LString(info.Version), "Version of the current OS")
|
||||
util.Document(L, hshos, "OS info interface")
|
||||
L.SetField(mod, "os", hshos)
|
||||
util.SetField(rtm, hshos, "family", rt.StringValue(info.Family), "Family name of the current OS")
|
||||
util.SetField(rtm, hshos, "name", rt.StringValue(info.Name), "Pretty name of the current OS")
|
||||
util.SetField(rtm, hshos, "version", rt.StringValue(info.Version), "Version of the current OS")
|
||||
util.Document(hshos, "OS info interface")
|
||||
mod.Set(rt.StringValue("os"), rt.TableValue(hshos))
|
||||
|
||||
// hilbish.aliases table
|
||||
aliases = newAliases()
|
||||
aliasesModule := aliases.Loader(L)
|
||||
util.Document(L, aliasesModule, "Alias inferface for Hilbish.")
|
||||
L.SetField(mod, "aliases", aliasesModule)
|
||||
aliasesModule := aliases.Loader(rtm)
|
||||
util.Document(aliasesModule, "Alias inferface for Hilbish.")
|
||||
mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule))
|
||||
|
||||
// hilbish.history table
|
||||
historyModule := lr.Loader(L)
|
||||
util.Document(L, historyModule, "History interface for Hilbish.")
|
||||
L.SetField(mod, "history", historyModule)
|
||||
historyModule := lr.Loader(rtm)
|
||||
mod.Set(rt.StringValue("history"), rt.TableValue(historyModule))
|
||||
util.Document(historyModule, "History interface for Hilbish.")
|
||||
|
||||
// hilbish.completions table
|
||||
hshcomp := L.NewTable()
|
||||
// hilbish.completion table
|
||||
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(L, hshcomp, "bins", L.NewFunction(luaBinaryComplete), "Completer for executables/binaries")
|
||||
util.Document(L, hshcomp, "Completions interface for Hilbish.")
|
||||
L.SetField(mod, "completion", hshcomp)
|
||||
util.SetField(rtm, hshcomp, "bins",
|
||||
rt.FunctionValue(rt.NewGoFunction(luaBinaryComplete, "bins", 3, false)),
|
||||
"Completer for executables/binaries")
|
||||
|
||||
util.Document(hshcomp, "Completions interface for Hilbish.")
|
||||
mod.Set(rt.StringValue("completion"), rt.TableValue(hshcomp))
|
||||
|
||||
// hilbish.runner table
|
||||
runnerModule := runnerModeLoader(L)
|
||||
util.Document(L, runnerModule, "Runner/exec interface for Hilbish.")
|
||||
L.SetField(mod, "runner", runnerModule)
|
||||
runnerModule := runnerModeLoader(rtm)
|
||||
util.Document(runnerModule, "Runner/exec interface for Hilbish.")
|
||||
mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule))
|
||||
|
||||
// hilbish.jobs table
|
||||
jobs = newJobHandler()
|
||||
jobModule := jobs.loader(L)
|
||||
util.Document(L, jobModule, "(Background) job interface.")
|
||||
L.SetField(mod, "jobs", jobModule)
|
||||
jobModule := jobs.loader(rtm)
|
||||
util.Document(jobModule, "(Background) job interface.")
|
||||
mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule))
|
||||
|
||||
L.Push(mod)
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaFileComplete(L *lua.LState) int {
|
||||
query := L.CheckString(1)
|
||||
ctx := L.CheckString(2)
|
||||
fields := L.CheckTable(3)
|
||||
|
||||
var fds []string
|
||||
fields.ForEach(func(k lua.LValue, v lua.LValue) {
|
||||
fds = append(fds, v.String())
|
||||
})
|
||||
|
||||
completions := fileComplete(query, ctx, fds)
|
||||
luaComps := L.NewTable()
|
||||
|
||||
for _, comp := range completions {
|
||||
luaComps.Append(lua.LString(comp))
|
||||
}
|
||||
|
||||
L.Push(luaComps)
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaBinaryComplete(L *lua.LState) int {
|
||||
query := L.CheckString(1)
|
||||
ctx := L.CheckString(2)
|
||||
fields := L.CheckTable(3)
|
||||
|
||||
var fds []string
|
||||
fields.ForEach(func(k lua.LValue, v lua.LValue) {
|
||||
fds = append(fds, v.String())
|
||||
})
|
||||
|
||||
completions, _ := binaryComplete(query, ctx, fds)
|
||||
luaComps := L.NewTable()
|
||||
|
||||
for _, comp := range completions {
|
||||
luaComps.Append(lua.LString(comp))
|
||||
}
|
||||
|
||||
L.Push(luaComps)
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func setVimMode(mode string) {
|
||||
util.SetField(l, hshMod, "vimMode", lua.LString(mode), "Current Vim mode of Hilbish (nil if not in Vim mode)")
|
||||
hooks.Em.Emit("hilbish.vimMode", mode)
|
||||
}
|
||||
|
||||
func unsetVimMode() {
|
||||
util.SetField(l, hshMod, "vimMode", lua.LNil, "Current Vim mode of Hilbish (nil if not in Vim mode)")
|
||||
}
|
||||
|
||||
// run(cmd)
|
||||
// Runs `cmd` in Hilbish's sh interpreter.
|
||||
// --- @param cmd string
|
||||
func hlrun(L *lua.LState) int {
|
||||
var exitcode uint8
|
||||
cmd := L.CheckString(1)
|
||||
err := execCommand(cmd)
|
||||
|
||||
if code, ok := interp.IsExitStatus(err); ok {
|
||||
exitcode = code
|
||||
} else if err != nil {
|
||||
exitcode = 1
|
||||
}
|
||||
|
||||
L.Push(lua.LNumber(exitcode))
|
||||
return 1
|
||||
}
|
||||
|
||||
// cwd()
|
||||
// Returns the current directory of the shell
|
||||
func hlcwd(L *lua.LState) int {
|
||||
cwd, _ := os.Getwd()
|
||||
|
||||
L.Push(lua.LString(cwd))
|
||||
|
||||
return 1
|
||||
return rt.TableValue(mod), nil
|
||||
}
|
||||
|
||||
func getenv(key, fallback string) string {
|
||||
|
@ -214,24 +143,160 @@ func getenv(key, fallback string) string {
|
|||
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 input from the user, using Hilbish's line editor/input reader.
|
||||
// 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)
|
||||
// --- @param prompt string
|
||||
func hlread(L *lua.LState) int {
|
||||
luaprompt := L.CheckString(1)
|
||||
func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
luaprompt, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lualr := newLineReader("", true)
|
||||
lualr.SetPrompt(luaprompt)
|
||||
|
||||
input, err := lualr.Read()
|
||||
if err != nil {
|
||||
L.Push(lua.LNil)
|
||||
return 1
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
L.Push(lua.LString(input))
|
||||
return 1
|
||||
return c.PushingNext1(t.Runtime, rt.StringValue(input)), nil
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -244,52 +309,91 @@ These will be formatted and replaced with the appropriate values.
|
|||
`%h` - Hostname of device
|
||||
--- @param str string
|
||||
*/
|
||||
func hlprompt(L *lua.LState) int {
|
||||
prompt = L.CheckString(1)
|
||||
func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
var prompt string
|
||||
err := c.Check1Arg()
|
||||
if err == nil {
|
||||
prompt, err = c.StringArg(0)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lr.SetPrompt(fmtPrompt(prompt))
|
||||
|
||||
return 0
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// multiprompt(str)
|
||||
// Changes the continued line prompt to `str`
|
||||
// --- @param str string
|
||||
func hlmlprompt(L *lua.LState) int {
|
||||
multilinePrompt = L.CheckString(1)
|
||||
func hlmultiprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
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)
|
||||
// Sets an alias of `cmd` to `orig`
|
||||
// --- @param cmd string
|
||||
// --- @param orig string
|
||||
func hlalias(L *lua.LState) int {
|
||||
alias := L.CheckString(1)
|
||||
source := L.CheckString(2)
|
||||
func hlalias(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.CheckNArgs(2); err != nil {
|
||||
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)
|
||||
// Appends `dir` to $PATH
|
||||
// --- @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
|
||||
arg := L.Get(1)
|
||||
if arg.Type() == lua.LTTable {
|
||||
arg.(*lua.LTable).ForEach(func(k lua.LValue, v lua.LValue) {
|
||||
appendPath(v.String())
|
||||
})
|
||||
} else if arg.Type() == lua.LTString {
|
||||
appendPath(arg.String())
|
||||
} else {
|
||||
L.RaiseError("bad argument to appendPath (expected string or table, got %v)", L.Get(1).Type().String())
|
||||
if arg.Type() == rt.TableType {
|
||||
nextVal := rt.NilValue
|
||||
for {
|
||||
next, val, ok := arg.AsTable().Next(nextVal)
|
||||
if next == rt.NilValue {
|
||||
break
|
||||
}
|
||||
nextVal = next
|
||||
|
||||
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) {
|
||||
|
@ -305,8 +409,14 @@ func appendPath(dir string) {
|
|||
// exec(cmd)
|
||||
// Replaces running hilbish with `cmd`
|
||||
// --- @param cmd string
|
||||
func hlexec(L *lua.LState) int {
|
||||
cmd := L.CheckString(1)
|
||||
func hlexec(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
|
||||
}
|
||||
cmdArgs, _ := splitInput(cmd)
|
||||
if runtime.GOOS != "windows" {
|
||||
cmdPath, err := exec.LookPath(cmdArgs[0])
|
||||
|
@ -328,78 +438,89 @@ func hlexec(L *lua.LState) int {
|
|||
os.Exit(0)
|
||||
}
|
||||
|
||||
return 0
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// goro(fn)
|
||||
// Puts `fn` in a goroutine
|
||||
// --- @param fn function
|
||||
func hlgoro(L *lua.LState) int {
|
||||
fn := L.CheckFunction(1)
|
||||
argnum := L.GetTop()
|
||||
args := make([]lua.LValue, argnum)
|
||||
for i := 1; i <= argnum; i++ {
|
||||
args[i - 1] = L.Get(i)
|
||||
func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fn, err := c.ClosureArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// call fn
|
||||
go func() {
|
||||
if err := L.CallByParam(lua.P{
|
||||
Fn: fn,
|
||||
NRet: 0,
|
||||
Protect: true,
|
||||
}, args...); err != nil {
|
||||
_, err := rt.Call1(l.MainThread(), rt.FunctionValue(fn), c.Etc()...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error in goro function:\n\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return 0
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// timeout(cb, time)
|
||||
// Runs the `cb` function after `time` in milliseconds
|
||||
// --- @param cb function
|
||||
// --- @param time number
|
||||
func hltimeout(L *lua.LState) int {
|
||||
cb := L.CheckFunction(1)
|
||||
ms := L.CheckInt(2)
|
||||
func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.CheckNArgs(2); err != nil {
|
||||
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
|
||||
time.Sleep(timeout)
|
||||
|
||||
if err := L.CallByParam(lua.P{
|
||||
Fn: cb,
|
||||
NRet: 0,
|
||||
Protect: true,
|
||||
}); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error in goro function:\n\n", err)
|
||||
_, err = rt.Call1(l.MainThread(), rt.FunctionValue(cb))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return 0
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// interval(cb, time)
|
||||
// Runs the `cb` function every `time` milliseconds
|
||||
// --- @param cb function
|
||||
// --- @param time number
|
||||
func hlinterval(L *lua.LState) int {
|
||||
intervalfunc := L.CheckFunction(1)
|
||||
ms := L.CheckInt(2)
|
||||
func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.CheckNArgs(2); err != nil {
|
||||
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
|
||||
|
||||
ticker := time.NewTicker(interval)
|
||||
stop := make(chan lua.LValue)
|
||||
stop := make(chan rt.Value)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := L.CallByParam(lua.P{
|
||||
Fn: intervalfunc,
|
||||
NRet: 0,
|
||||
Protect: true,
|
||||
}); err != nil {
|
||||
_, err := rt.Call1(l.MainThread(), rt.FunctionValue(cb))
|
||||
if err != nil {
|
||||
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:
|
||||
ticker.Stop()
|
||||
|
@ -408,8 +529,8 @@ func hlinterval(L *lua.LState) int {
|
|||
}
|
||||
}()
|
||||
|
||||
L.Push(lua.LChannel(stop))
|
||||
return 1
|
||||
// TODO: return channel
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// complete(scope, cb)
|
||||
|
@ -422,20 +543,27 @@ func hlinterval(L *lua.LState) int {
|
|||
// `grid` (the normal file completion display) or `list` (with a description)
|
||||
// --- @param scope string
|
||||
// --- @param cb function
|
||||
func hlcomplete(L *lua.LState) int {
|
||||
scope := L.CheckString(1)
|
||||
cb := L.CheckFunction(2)
|
||||
|
||||
func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
scope, cb, err := util.HandleStrCallback(t, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
luaCompletions[scope] = cb
|
||||
|
||||
return 0
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// prependPath(dir)
|
||||
// Prepends `dir` to $PATH
|
||||
// --- @param dir string
|
||||
func hlprependPath(L *lua.LState) int {
|
||||
dir := L.CheckString(1)
|
||||
func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
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)
|
||||
pathenv := os.Getenv("PATH")
|
||||
|
||||
|
@ -444,29 +572,40 @@ func hlprependPath(L *lua.LState) int {
|
|||
os.Setenv("PATH", dir + string(os.PathListSeparator) + pathenv)
|
||||
}
|
||||
|
||||
return 0
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// which(binName)
|
||||
// Searches for an executable called `binName` in the directories of $PATH
|
||||
// --- @param binName string
|
||||
func hlwhich(L *lua.LState) int {
|
||||
binName := L.CheckString(1)
|
||||
func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
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)
|
||||
if err != nil {
|
||||
l.Push(lua.LNil)
|
||||
return 1
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
l.Push(lua.LString(path))
|
||||
return 1
|
||||
return c.PushingNext1(t.Runtime, rt.StringValue(path)), nil
|
||||
}
|
||||
|
||||
// inputMode(mode)
|
||||
// Sets the input mode for Hilbish's line reader. Accepts either emacs for vim
|
||||
// --- @param mode string
|
||||
func hlinputMode(L *lua.LState) int {
|
||||
mode := L.CheckString(1)
|
||||
func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case "emacs":
|
||||
unsetVimMode()
|
||||
|
@ -474,9 +613,11 @@ func hlinputMode(L *lua.LState) int {
|
|||
case "vim":
|
||||
setVimMode("insert")
|
||||
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)
|
||||
|
@ -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
|
||||
// will call it to execute user input instead.
|
||||
// --- @param mode string|function
|
||||
func hlrunnerMode(L *lua.LState) int {
|
||||
mode := L.CheckAny(1)
|
||||
switch mode.Type() {
|
||||
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)
|
||||
func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case lua.LTFunction: runnerMode = mode
|
||||
default: L.RaiseError("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received %v", mode)
|
||||
mode := c.Arg(0)
|
||||
|
||||
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)
|
||||
|
@ -512,11 +653,17 @@ func hlrunnerMode(L *lua.LState) int {
|
|||
// the current line and the position. It is expected to return a string
|
||||
// which will be used for the hint.
|
||||
// --- @param cb function
|
||||
func hlhinter(L *lua.LState) int {
|
||||
hinterCb := L.CheckFunction(1)
|
||||
func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hinterCb, err := c.ClosureArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hinter = hinterCb
|
||||
|
||||
return 0
|
||||
return c.Next(), err
|
||||
}
|
||||
|
||||
// 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
|
||||
// be used to display in the line.
|
||||
// --- @param cb function
|
||||
func hlhighlighter(L *lua.LState) int {
|
||||
highlighterCb := L.CheckFunction(1)
|
||||
func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
highlighterCb, err := c.ClosureArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
highlighter = highlighterCb
|
||||
|
||||
return 0
|
||||
return c.Next(), err
|
||||
}
|
||||
|
|
|
@ -45,7 +45,9 @@ read(prompt) -> input? > Read input from the user, using Hilbish's line editor/i
|
|||
This is a separate instance from the one Hilbish actually uses.
|
||||
Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
|
||||
|
||||
run(cmd) > Runs `cmd` in Hilbish's sh interpreter.
|
||||
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.
|
||||
|
||||
runnerMode(mode) > Sets the execution/runner mode for interactive Hilbish. This determines whether
|
||||
Hilbish wll try to run input as Lua and/or sh or only do one of either.
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
setRaw() > Puts the terminal in raw mode
|
||||
|
||||
restoreState() > Restores the last saved state of the terminal
|
||||
|
||||
saveState() > Saves the current state of the terminal
|
||||
|
||||
setRaw() > Puts the terminal in raw mode
|
||||
|
||||
size() > Gets the dimensions of the terminal. Returns a table with `width` and `height`
|
||||
Note: this is not the size in relation to the dimensions of the display
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ function hilbish.interval(cb, time) end
|
|||
|
||||
--- Changes the continued line prompt to `str`
|
||||
--- @param str string
|
||||
function hilbish.mlprompt(str) end
|
||||
function hilbish.multiprompt(str) end
|
||||
|
||||
--- Prepends `dir` to $PATH
|
||||
--- @param dir string
|
||||
|
@ -80,6 +80,8 @@ function hilbish.prompt(str) end
|
|||
function hilbish.read(prompt) end
|
||||
|
||||
--- 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
|
||||
function hilbish.run(cmd) end
|
||||
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
|
||||
local terminal = {}
|
||||
|
||||
--- Puts the terminal in raw mode
|
||||
function terminal.raw() end
|
||||
|
||||
--- Restores the last saved state of the terminal
|
||||
function terminal.restoreState() end
|
||||
|
||||
--- Saves the current state of the terminal
|
||||
function terminal.saveState() end
|
||||
|
||||
--- Puts the terminal in raw mode
|
||||
function terminal.setRaw() end
|
||||
|
||||
--- Gets the dimensions of the terminal. Returns a table with `width` and `height`
|
||||
--- Note: this is not the size in relation to the dimensions of the display
|
||||
function terminal.size() end
|
||||
|
|
84
exec.go
84
exec.go
|
@ -6,6 +6,7 @@ import (
|
|||
"errors"
|
||||
"os/exec"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
@ -15,7 +16,7 @@ import (
|
|||
|
||||
"hilbish/util"
|
||||
|
||||
"github.com/yuin/gopher-lua"
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
"mvdan.cc/sh/v3/shell"
|
||||
//"github.com/yuin/gopher-lua/parse"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
|
@ -24,15 +25,15 @@ import (
|
|||
)
|
||||
|
||||
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) {
|
||||
running = true
|
||||
cmdString := aliases.Resolve(input)
|
||||
hooks.Em.Emit("command.preexec", input, cmdString)
|
||||
|
||||
if runnerMode.Type() == lua.LTString {
|
||||
switch runnerMode.String() {
|
||||
if runnerMode.Type() == rt.StringType {
|
||||
switch runnerMode.AsString() {
|
||||
case "hybrid":
|
||||
_, err := handleLua(cmdString)
|
||||
if err == nil {
|
||||
|
@ -70,27 +71,23 @@ func runInput(input string, priv bool) {
|
|||
}
|
||||
} else {
|
||||
// can only be a string or function so
|
||||
err := l.CallByParam(lua.P{
|
||||
Fn: runnerMode,
|
||||
NRet: 2,
|
||||
Protect: true,
|
||||
}, lua.LString(cmdString))
|
||||
term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 2, false)
|
||||
err := rt.Call(l.MainThread(), runnerMode, []rt.Value{rt.StringValue(cmdString)}, term)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
cmdFinish(124, cmdString, priv)
|
||||
return
|
||||
}
|
||||
|
||||
luaexitcode := l.Get(-2) // first return value (makes sense right i love stacks)
|
||||
runErr := l.Get(-1)
|
||||
l.Pop(2)
|
||||
luaexitcode := term.Get(0) // first return value (makes sense right i love stacks)
|
||||
runErr := term.Get(1)
|
||||
|
||||
var exitCode uint8
|
||||
if code, ok := luaexitcode.(lua.LNumber); luaexitcode != lua.LNil && ok {
|
||||
if code, ok := luaexitcode.TryInt(); ok {
|
||||
exitCode = uint8(code)
|
||||
}
|
||||
|
||||
if runErr != lua.LNil {
|
||||
if runErr != rt.NilValue {
|
||||
fmt.Fprintln(os.Stderr, runErr)
|
||||
}
|
||||
cmdFinish(exitCode, cmdString, priv)
|
||||
|
@ -99,7 +96,7 @@ func runInput(input string, priv bool) {
|
|||
|
||||
func handleLua(cmdString string) (uint8, error) {
|
||||
// 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 {
|
||||
fmt.Println(err)
|
||||
/* 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
|
||||
if !noexecute {
|
||||
l.Push(fn)
|
||||
err = l.PCall(0, lua.MultRet, nil)
|
||||
if chunk != nil {
|
||||
_, err = rt.Call1(l.MainThread(), rt.FunctionValue(chunk))
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
return 0, nil
|
||||
|
@ -123,7 +121,7 @@ func handleLua(cmdString string) (uint8, error) {
|
|||
}
|
||||
|
||||
func handleSh(cmdString string) (uint8, error) {
|
||||
err := execCommand(cmdString)
|
||||
_, _, err := execCommand(cmdString, true)
|
||||
if err != nil {
|
||||
// If input is incomplete, start multiline prompting
|
||||
if syntax.IsIncomplete(err) {
|
||||
|
@ -132,7 +130,7 @@ func handleSh(cmdString string) (uint8, error) {
|
|||
if err != nil {
|
||||
break
|
||||
}
|
||||
err = execCommand(cmdString)
|
||||
_, _, err = execCommand(cmdString, true)
|
||||
if syntax.IsIncomplete(err) || strings.HasSuffix(cmdString, "\\") {
|
||||
continue
|
||||
} else if code, ok := interp.IsExitStatus(err); ok {
|
||||
|
@ -156,10 +154,16 @@ func handleSh(cmdString string) (uint8, error) {
|
|||
}
|
||||
|
||||
// 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), "")
|
||||
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
|
||||
|
@ -180,31 +184,26 @@ func execCommand(cmd string) error {
|
|||
}
|
||||
|
||||
// If command is defined in Lua then run it
|
||||
luacmdArgs := l.NewTable()
|
||||
for _, str := range args[1:] {
|
||||
luacmdArgs.Append(lua.LString(str))
|
||||
luacmdArgs := rt.NewTable()
|
||||
for i, str := range args[1:] {
|
||||
luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str))
|
||||
}
|
||||
|
||||
if commands[args[0]] != nil {
|
||||
err := l.CallByParam(lua.P{
|
||||
Fn: commands[args[0]],
|
||||
NRet: 1,
|
||||
Protect: true,
|
||||
}, luacmdArgs)
|
||||
|
||||
luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr,
|
||||
"Error in command:\n\n" + err.Error())
|
||||
fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error())
|
||||
return interp.NewExitStatus(1)
|
||||
}
|
||||
|
||||
luaexitcode := l.Get(-1)
|
||||
var exitcode uint8
|
||||
|
||||
l.Pop(1)
|
||||
|
||||
if code, ok := luaexitcode.(lua.LNumber); luaexitcode != lua.LNil && ok {
|
||||
if code, ok := luaexitcode.TryInt(); ok {
|
||||
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)
|
||||
|
@ -331,7 +330,7 @@ func execCommand(cmd string) error {
|
|||
}
|
||||
|
||||
runner, _ := interp.New(
|
||||
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
|
||||
interp.StdIO(os.Stdin, stdout, stderr),
|
||||
interp.ExecHandler(exechandle),
|
||||
)
|
||||
|
||||
|
@ -351,11 +350,11 @@ func execCommand(cmd string) error {
|
|||
|
||||
err = runner.Run(context.TODO(), stmt)
|
||||
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
|
||||
|
@ -441,6 +440,9 @@ func cmdFinish(code uint8, cmdstr string, private bool) {
|
|||
if interactive && !private {
|
||||
handleHistory(cmdstr)
|
||||
}
|
||||
util.SetField(l, hshMod, "exitCode", lua.LNumber(code), "Exit code of last exected command")
|
||||
hooks.Em.Emit("command.exit", code, cmdstr)
|
||||
util.SetField(l, hshMod, "exitCode", rt.IntValue(int64(code)), "Exit code of last exected command")
|
||||
// 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
|
||||
|
||||
require (
|
||||
github.com/arnodel/golua v0.0.0-20220221163911-dfcf252b6f86
|
||||
github.com/blackfireio/osinfo v1.0.3
|
||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9
|
||||
github.com/maxlandon/readline v0.1.0-beta.0.20211027085530-2b76cabb8036
|
||||
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
|
||||
layeh.com/gopher-luar v1.0.10
|
||||
mvdan.cc/sh/v3 v3.4.3
|
||||
)
|
||||
|
||||
require (
|
||||
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/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // 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 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/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/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/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/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.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc=
|
||||
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/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/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
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.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/layeh/gopher-luar v1.0.10 h1:8NIv4MX1Arz96kK4buGK1D87DyDxKZyq6KKvJ2diHp0=
|
||||
github.com/layeh/gopher-luar v1.0.10/go.mod h1:TPnIVCZ2RJBndm7ohXyaqfhzjlZ+OA2SZR/YwL8tECk=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
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/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/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk=
|
||||
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/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
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/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/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-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-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/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-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
|
||||
|
|
|
@ -4,13 +4,14 @@ import (
|
|||
"fmt"
|
||||
"hilbish/util"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
"github.com/arnodel/golua/lib/packagelib"
|
||||
"github.com/chuckpreslar/emission"
|
||||
"github.com/yuin/gopher-lua"
|
||||
"layeh.com/gopher-luar"
|
||||
)
|
||||
|
||||
type Bait struct{
|
||||
Em *emission.Emitter
|
||||
Loader packagelib.Loader
|
||||
}
|
||||
|
||||
func New() Bait {
|
||||
|
@ -19,15 +20,27 @@ func New() Bait {
|
|||
emitter.Off(hookname, hookfunc)
|
||||
fmt.Println(err)
|
||||
})
|
||||
return Bait{
|
||||
b := Bait{
|
||||
Em: emitter,
|
||||
}
|
||||
b.Loader = packagelib.Loader{
|
||||
Load: b.loaderFunc,
|
||||
Name: "bait",
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Bait) Loader(L *lua.LState) int {
|
||||
mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{})
|
||||
func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||
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?
|
||||
Because it throws hooks that you can catch (emits events
|
||||
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
|
||||
failed, etc. To find all available hooks, see doc hooks.`)
|
||||
|
||||
L.SetField(mod, "throw", luar.New(L, b.bthrow))
|
||||
L.SetField(mod, "catch", luar.New(L, b.bcatch))
|
||||
L.SetField(mod, "catchOnce", luar.New(L, b.bcatchOnce))
|
||||
return rt.TableValue(mod), nil
|
||||
}
|
||||
|
||||
L.Push(mod)
|
||||
|
||||
return 1
|
||||
func handleHook(t *rt.Thread, c *rt.GoCont, name string, catcher *rt.Closure, args ...interface{}) {
|
||||
funcVal := rt.FunctionValue(catcher)
|
||||
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)
|
||||
// Throws a hook with `name` with the provided `args`
|
||||
// --- @param name string
|
||||
// --- @vararg any
|
||||
func (b *Bait) bthrow(name string, args ...interface{}) {
|
||||
b.Em.Emit(name, args...)
|
||||
func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
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)
|
||||
// Catches a hook with `name`. Runs the `cb` when it is thrown
|
||||
// --- @param name string
|
||||
// --- @param cb function
|
||||
func (b *Bait) bcatch(name string, catcher func(...interface{})) {
|
||||
b.Em.On(name, catcher)
|
||||
func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
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)
|
||||
// Same as catch, but only runs the `cb` once and then removes the hook
|
||||
// --- @param name string
|
||||
// --- @param cb function
|
||||
func (b *Bait) bcatchOnce(name string, catcher func(...interface{})) {
|
||||
b.Em.Once(name, catcher)
|
||||
func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
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 (
|
||||
"hilbish/util"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
"github.com/arnodel/golua/lib/packagelib"
|
||||
"github.com/chuckpreslar/emission"
|
||||
"github.com/yuin/gopher-lua"
|
||||
)
|
||||
|
||||
type Commander struct{
|
||||
Events *emission.Emitter
|
||||
Loader packagelib.Loader
|
||||
}
|
||||
|
||||
func New() Commander {
|
||||
return Commander{
|
||||
c := Commander{
|
||||
Events: emission.NewEmitter(),
|
||||
}
|
||||
c.Loader = packagelib.Loader{
|
||||
Load: c.loaderFunc,
|
||||
Name: "commander",
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Commander) Loader(L *lua.LState) int {
|
||||
exports := map[string]lua.LGFunction{
|
||||
"register": c.cregister,
|
||||
"deregister": c.cderegister,
|
||||
func (c *Commander) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||
exports := map[string]util.LuaExport{
|
||||
"register": util.LuaExport{c.cregister, 2, false},
|
||||
"deregister": util.LuaExport{c.cderegister, 1, false},
|
||||
}
|
||||
mod := L.SetFuncs(L.NewTable(), exports)
|
||||
util.Document(L, mod, "Commander is Hilbish's custom command library, a way to write commands in Lua.")
|
||||
L.Push(mod)
|
||||
mod := rt.NewTable()
|
||||
util.SetExports(rtm, mod, exports)
|
||||
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 a command with `name` that runs `cb` when ran
|
||||
// --- @param name string
|
||||
// --- @param cb function
|
||||
func (c *Commander) cregister(L *lua.LState) int {
|
||||
cmdName := L.CheckString(1)
|
||||
cmd := L.CheckFunction(2)
|
||||
func (c *Commander) cregister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||
cmdName, cmd, err := util.HandleStrCallback(t, ct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.Events.Emit("commandRegister", cmdName, cmd)
|
||||
|
||||
return 0
|
||||
return ct.Next(), err
|
||||
}
|
||||
|
||||
// deregister(name)
|
||||
// Deregisters any command registered with `name`
|
||||
// --- @param name string
|
||||
func (c *Commander) cderegister(L *lua.LState) int {
|
||||
cmdName := L.CheckString(1)
|
||||
func (c *Commander) cderegister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
|
@ -8,51 +6,70 @@ import (
|
|||
"strings"
|
||||
|
||||
"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 {
|
||||
mod := L.SetFuncs(L.NewTable(), exports)
|
||||
var Loader = packagelib.Loader{
|
||||
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
|
||||
addition to the Lua standard library's I/O and fs functions.`)
|
||||
|
||||
L.Push(mod)
|
||||
return 1
|
||||
}
|
||||
|
||||
var exports = map[string]lua.LGFunction{
|
||||
"cd": fcd,
|
||||
"mkdir": fmkdir,
|
||||
"stat": fstat,
|
||||
"readdir": freaddir,
|
||||
return rt.TableValue(mod), nil
|
||||
}
|
||||
|
||||
// cd(dir)
|
||||
// Changes directory to `dir`
|
||||
// --- @param dir string
|
||||
func fcd(L *lua.LState) int {
|
||||
path := L.CheckString(1)
|
||||
|
||||
err := os.Chdir(strings.TrimSpace(path))
|
||||
func fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
e := err.(*os.PathError).Err.Error()
|
||||
L.RaiseError(e + ": " + path)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return 0
|
||||
err = os.Chdir(strings.TrimSpace(path))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.Next(), err
|
||||
}
|
||||
|
||||
// mkdir(name, recursive)
|
||||
// Makes a directory called `name`. If `recursive` is true, it will create its parent directories.
|
||||
// --- @param name string
|
||||
// --- @param recursive boolean
|
||||
func fmkdir(L *lua.LState) int {
|
||||
dirname := L.CheckString(1)
|
||||
recursive := L.ToBool(2)
|
||||
func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.CheckNArgs(2); err != nil {
|
||||
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)
|
||||
var err error
|
||||
|
||||
if recursive {
|
||||
err = os.MkdirAll(path, 0744)
|
||||
|
@ -60,51 +77,58 @@ func fmkdir(L *lua.LState) int {
|
|||
err = os.Mkdir(path, 0744)
|
||||
}
|
||||
if err != nil {
|
||||
L.RaiseError(err.Error() + ": " + path)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return 0
|
||||
return c.Next(), err
|
||||
}
|
||||
|
||||
// stat(path)
|
||||
// Returns info about `path`
|
||||
// --- @param path string
|
||||
func fstat(L *lua.LState) int {
|
||||
path := L.CheckString(1)
|
||||
func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
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)
|
||||
if err != nil {
|
||||
L.RaiseError(err.Error() + ": " + path)
|
||||
return 0
|
||||
return nil, err
|
||||
}
|
||||
statTbl := L.NewTable()
|
||||
L.SetField(statTbl, "name", lua.LString(pathinfo.Name()))
|
||||
L.SetField(statTbl, "size", lua.LNumber(pathinfo.Size()))
|
||||
L.SetField(statTbl, "mode", lua.LString("0" + strconv.FormatInt(int64(pathinfo.Mode().Perm()), 8)))
|
||||
L.SetField(statTbl, "isDir", lua.LBool(pathinfo.IsDir()))
|
||||
L.Push(statTbl)
|
||||
statTbl := rt.NewTable()
|
||||
statTbl.Set(rt.StringValue("name"), rt.StringValue(pathinfo.Name()))
|
||||
statTbl.Set(rt.StringValue("size"), rt.IntValue(pathinfo.Size()))
|
||||
statTbl.Set(rt.StringValue("mode"), rt.StringValue("0" + strconv.FormatInt(int64(pathinfo.Mode().Perm()), 8)))
|
||||
statTbl.Set(rt.StringValue("isDir"), rt.BoolValue(pathinfo.IsDir()))
|
||||
|
||||
return 1
|
||||
return c.PushingNext1(t.Runtime, rt.TableValue(statTbl)), nil
|
||||
}
|
||||
|
||||
// readdir(dir)
|
||||
// Returns a table of files in `dir`
|
||||
// --- @param dir string
|
||||
// --- @return table
|
||||
func freaddir(L *lua.LState) int {
|
||||
dir := L.CheckString(1)
|
||||
names := L.NewTable()
|
||||
func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dir, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names := rt.NewTable()
|
||||
|
||||
dirEntries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
L.RaiseError(err.Error() + ": " + dir)
|
||||
return 0
|
||||
return nil, err
|
||||
}
|
||||
for _, entry := range dirEntries {
|
||||
names.Append(lua.LString(entry.Name()))
|
||||
for i, entry := range dirEntries {
|
||||
names.Set(rt.IntValue(int64(i + 1)), rt.StringValue(entry.Name()))
|
||||
}
|
||||
|
||||
L.Push(names)
|
||||
|
||||
return 1
|
||||
return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil
|
||||
}
|
||||
|
|
|
@ -5,76 +5,78 @@ import (
|
|||
|
||||
"hilbish/util"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
"github.com/arnodel/golua/lib/packagelib"
|
||||
"golang.org/x/term"
|
||||
"github.com/yuin/gopher-lua"
|
||||
)
|
||||
|
||||
var termState *term.State
|
||||
|
||||
func Loader(L *lua.LState) int {
|
||||
mod := L.SetFuncs(L.NewTable(), exports)
|
||||
util.Document(L, mod, "The terminal library is a simple and lower level library for certain terminal interactions.")
|
||||
|
||||
L.Push(mod)
|
||||
|
||||
return 1
|
||||
var Loader = packagelib.Loader{
|
||||
Load: loaderFunc,
|
||||
Name: "terminal",
|
||||
}
|
||||
|
||||
var exports = map[string]lua.LGFunction{
|
||||
"setRaw": termraw,
|
||||
"restoreState": termrestoreState,
|
||||
"size": termsize,
|
||||
"saveState": termsaveState,
|
||||
func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||
exports := map[string]util.LuaExport{
|
||||
"setRaw": util.LuaExport{termsetRaw, 0, false},
|
||||
"restoreState": util.LuaExport{termrestoreState, 0, false},
|
||||
"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()
|
||||
// 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
|
||||
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()))
|
||||
if err != nil {
|
||||
L.RaiseError(err.Error())
|
||||
return 0
|
||||
return nil, err
|
||||
}
|
||||
dimensions := L.NewTable()
|
||||
L.SetField(dimensions, "width", lua.LNumber(w))
|
||||
L.SetField(dimensions, "height", lua.LNumber(h))
|
||||
|
||||
L.Push(dimensions)
|
||||
return 1
|
||||
dimensions := rt.NewTable()
|
||||
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()
|
||||
// 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()))
|
||||
if err != nil {
|
||||
L.RaiseError(err.Error())
|
||||
return 0
|
||||
return nil, err
|
||||
}
|
||||
|
||||
termState = state
|
||||
return 0
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// restoreState()
|
||||
// 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)
|
||||
if err != nil {
|
||||
L.RaiseError(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return 0
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// setRaw()
|
||||
// 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()))
|
||||
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"
|
||||
"os"
|
||||
|
||||
"github.com/yuin/gopher-lua"
|
||||
"hilbish/util"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
)
|
||||
|
||||
var jobs *jobHandler
|
||||
|
@ -38,29 +40,28 @@ func (j *job) setHandle(handle *os.Process) {
|
|||
j.proc = handle
|
||||
}
|
||||
|
||||
func (j *job) lua() *lua.LTable {
|
||||
// returns lua table for job
|
||||
// because userdata is gross
|
||||
jobFuncs := map[string]lua.LGFunction{
|
||||
"stop": j.luaStop,
|
||||
func (j *job) lua() rt.Value {
|
||||
jobFuncs := map[string]util.LuaExport{
|
||||
"stop": {j.luaStop, 0, false},
|
||||
}
|
||||
luaJob := l.SetFuncs(l.NewTable(), jobFuncs)
|
||||
luaJob := rt.NewTable()
|
||||
util.SetExports(l, luaJob, jobFuncs)
|
||||
|
||||
l.SetField(luaJob, "cmd", lua.LString(j.cmd))
|
||||
l.SetField(luaJob, "running", lua.LBool(j.running))
|
||||
l.SetField(luaJob, "id", lua.LNumber(j.id))
|
||||
l.SetField(luaJob, "pid", lua.LNumber(j.pid))
|
||||
l.SetField(luaJob, "exitCode", lua.LNumber(j.exitCode))
|
||||
luaJob.Set(rt.StringValue("cmd"), rt.StringValue(j.cmd))
|
||||
luaJob.Set(rt.StringValue("running"), rt.BoolValue(j.running))
|
||||
luaJob.Set(rt.StringValue("id"), rt.IntValue(int64(j.id)))
|
||||
luaJob.Set(rt.StringValue("pid"), rt.IntValue(int64(j.pid)))
|
||||
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 {
|
||||
j.stop()
|
||||
}
|
||||
|
||||
return 0
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
type jobHandler struct {
|
||||
|
@ -96,41 +97,46 @@ func (j *jobHandler) getLatest() *job {
|
|||
return j.jobs[j.latestID]
|
||||
}
|
||||
|
||||
|
||||
func (j *jobHandler) loader(L *lua.LState) *lua.LTable {
|
||||
jobFuncs := map[string]lua.LGFunction{
|
||||
"all": j.luaAllJobs,
|
||||
"get": j.luaGetJob,
|
||||
func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table {
|
||||
jobFuncs := map[string]util.LuaExport{
|
||||
"all": {j.luaAllJobs, 0, false},
|
||||
"get": {j.luaGetJob, 1, false},
|
||||
}
|
||||
|
||||
luaJob := l.SetFuncs(l.NewTable(), jobFuncs)
|
||||
luaJob := rt.NewTable()
|
||||
util.SetExports(rtm, luaJob, jobFuncs)
|
||||
|
||||
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()
|
||||
defer j.mu.RUnlock()
|
||||
|
||||
jobID := L.CheckInt(1)
|
||||
job := j.jobs[jobID]
|
||||
if job != nil {
|
||||
return 0
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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()
|
||||
defer j.mu.RUnlock()
|
||||
|
||||
jobTbl := L.NewTable()
|
||||
jobTbl := rt.NewTable()
|
||||
for id, job := range j.jobs {
|
||||
jobTbl.Insert(id, job.lua())
|
||||
jobTbl.Set(rt.IntValue(int64(id)), job.lua())
|
||||
}
|
||||
|
||||
L.Push(jobTbl)
|
||||
return 1
|
||||
return c.PushingNext1(t.Runtime, rt.TableValue(jobTbl)), nil
|
||||
}
|
||||
|
|
|
@ -89,21 +89,25 @@ end
|
|||
|
||||
ansikit.print = function(text)
|
||||
io.write(ansikit.format(text))
|
||||
io.flush()
|
||||
return ansikit
|
||||
end
|
||||
|
||||
ansikit.printCode = function(code, terminate)
|
||||
io.write(ansikit.getCode(code, terminate))
|
||||
io.flush()
|
||||
return ansikit
|
||||
end
|
||||
|
||||
ansikit.printCSI = function(code, endc)
|
||||
io.write(ansikit.getCSI(code, endc))
|
||||
io.flush()
|
||||
return ansikit
|
||||
end
|
||||
|
||||
ansikit.println = function(text)
|
||||
print(ansikit.print(text))
|
||||
io.write(ansikit.format(text) .. "\n")
|
||||
io.flush()
|
||||
return ansikit
|
||||
end
|
||||
|
||||
|
|
43
lua.go
43
lua.go
|
@ -4,40 +4,42 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"hilbish/util"
|
||||
"hilbish/golibs/bait"
|
||||
"hilbish/golibs/commander"
|
||||
"hilbish/golibs/fs"
|
||||
"hilbish/golibs/terminal"
|
||||
|
||||
"github.com/yuin/gopher-lua"
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
"github.com/arnodel/golua/lib"
|
||||
)
|
||||
|
||||
var minimalconf = `hilbish.prompt '& '`
|
||||
|
||||
func luaInit() {
|
||||
l = lua.NewState()
|
||||
l.OpenLibs()
|
||||
l = rt.New(os.Stdout)
|
||||
lib.LoadAll(l)
|
||||
|
||||
lib.LoadLibs(l, hilbishLoader)
|
||||
// yes this is stupid, i know
|
||||
l.PreloadModule("hilbish", hilbishLoader)
|
||||
l.DoString("hilbish = require 'hilbish'")
|
||||
util.DoString(l, "hilbish = require 'hilbish'")
|
||||
|
||||
// Add fs and terminal module module to Lua
|
||||
l.PreloadModule("fs", fs.Loader)
|
||||
l.PreloadModule("terminal", terminal.Loader)
|
||||
lib.LoadLibs(l, fs.Loader)
|
||||
lib.LoadLibs(l, terminal.Loader)
|
||||
|
||||
cmds := commander.New()
|
||||
// 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
|
||||
})
|
||||
cmds.Events.On("commandDeregister", func(cmdName string) {
|
||||
delete(commands, cmdName)
|
||||
})
|
||||
l.PreloadModule("commander", cmds.Loader)
|
||||
lib.LoadLibs(l, cmds.Loader)
|
||||
|
||||
hooks = bait.New()
|
||||
l.PreloadModule("bait", hooks.Loader)
|
||||
lib.LoadLibs(l, hooks.Loader)
|
||||
|
||||
// Add Ctrl-C handler
|
||||
hooks.Em.On("signal.sigint", func() {
|
||||
|
@ -47,26 +49,27 @@ func luaInit() {
|
|||
})
|
||||
|
||||
// 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 {
|
||||
err = l.DoFile(preloadPath)
|
||||
err = util.DoFile(l, preloadPath)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr,
|
||||
"Missing preload file, builtins may be missing.")
|
||||
fmt.Fprintln(os.Stderr, "Missing preload file, builtins may be missing.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runConfig(confpath string) {
|
||||
if !interactive {
|
||||
return
|
||||
}
|
||||
err := l.DoFile(confpath)
|
||||
err := util.DoFile(l, confpath)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err,
|
||||
"\nAn error has occured while loading your config! Falling back to minimal default config.")
|
||||
|
||||
l.DoString(minimalconf)
|
||||
fmt.Fprintln(os.Stderr, err, "\nAn error has occured while loading your config! Falling back to minimal default config.")
|
||||
util.DoString(l, minimalconf)
|
||||
}
|
||||
}
|
||||
|
|
19
main.go
19
main.go
|
@ -10,20 +10,21 @@ import (
|
|||
"runtime"
|
||||
"strings"
|
||||
|
||||
"hilbish/util"
|
||||
"hilbish/golibs/bait"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
"github.com/pborman/getopt"
|
||||
"github.com/yuin/gopher-lua"
|
||||
"github.com/maxlandon/readline"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
var (
|
||||
l *lua.LState
|
||||
l *rt.Runtime
|
||||
lr *lineReader
|
||||
|
||||
commands = map[string]*lua.LFunction{}
|
||||
luaCompletions = map[string]*lua.LFunction{}
|
||||
commands = map[string]*rt.Closure{}
|
||||
luaCompletions = map[string]*rt.Closure{}
|
||||
|
||||
confDir string
|
||||
userDataDir string
|
||||
|
@ -151,13 +152,13 @@ func main() {
|
|||
}
|
||||
|
||||
if getopt.NArgs() > 0 {
|
||||
luaArgs := l.NewTable()
|
||||
for _, arg := range getopt.Args() {
|
||||
luaArgs.Append(lua.LString(arg))
|
||||
luaArgs := rt.NewTable()
|
||||
for i, arg := range getopt.Args() {
|
||||
luaArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(arg))
|
||||
}
|
||||
|
||||
l.SetGlobal("args", luaArgs)
|
||||
err := l.DoFile(getopt.Arg(0))
|
||||
l.GlobalEnv().Set(rt.StringValue("args"), rt.TableValue(luaArgs))
|
||||
err := util.DoFile(l, getopt.Arg(0))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
|
|
|
@ -8,7 +8,7 @@ local _ = require 'succulent' -- Function additions
|
|||
local oldDir = hilbish.cwd()
|
||||
|
||||
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
|
||||
local recentDirs = {}
|
||||
|
|
205
rl.go
205
rl.go
|
@ -5,18 +5,19 @@ import (
|
|||
"io"
|
||||
"strings"
|
||||
|
||||
"hilbish/util"
|
||||
|
||||
"github.com/maxlandon/readline"
|
||||
"github.com/yuin/gopher-lua"
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
)
|
||||
|
||||
type lineReader struct {
|
||||
rl *readline.Instance
|
||||
}
|
||||
var fileHist *fileHistory
|
||||
var hinter lua.LValue = lua.LNil
|
||||
var highlighter lua.LValue = lua.LNil
|
||||
var hinter *rt.Closure
|
||||
var highlighter *rt.Closure
|
||||
|
||||
// other gophers might hate this naming but this is local, shut up
|
||||
func newLineReader(prompt string, noHist bool) *lineReader {
|
||||
rl := readline.NewInstance()
|
||||
// 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)
|
||||
}
|
||||
rl.HintText = func(line []rune, pos int) []rune {
|
||||
if hinter == lua.LNil {
|
||||
if hinter == nil {
|
||||
return []rune{}
|
||||
}
|
||||
|
||||
err := l.CallByParam(lua.P{
|
||||
Fn: hinter,
|
||||
NRet: 1,
|
||||
Protect: true,
|
||||
}, lua.LString(string(line)), lua.LNumber(pos))
|
||||
retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(highlighter),
|
||||
rt.StringValue(string(line)), rt.IntValue(int64(pos)))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return []rune{}
|
||||
}
|
||||
|
||||
retVal := l.Get(-1)
|
||||
hintText := ""
|
||||
if luaStr, ok := retVal.(lua.LString); retVal != lua.LNil && ok {
|
||||
hintText = luaStr.String()
|
||||
if luaStr, ok := retVal.TryString(); ok {
|
||||
hintText = luaStr
|
||||
}
|
||||
|
||||
return []rune(hintText)
|
||||
}
|
||||
rl.SyntaxHighlighter = func(line []rune) string {
|
||||
if highlighter == lua.LNil {
|
||||
if highlighter == nil {
|
||||
return string(line)
|
||||
}
|
||||
err := l.CallByParam(lua.P{
|
||||
Fn: highlighter,
|
||||
NRet: 1,
|
||||
Protect: true,
|
||||
}, lua.LString(string(line)))
|
||||
retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(highlighter),
|
||||
rt.StringValue(string(line)))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return string(line)
|
||||
}
|
||||
|
||||
retVal := l.Get(-1)
|
||||
highlighted := ""
|
||||
if luaStr, ok := retVal.(lua.LString); retVal != lua.LNil && ok {
|
||||
highlighted = luaStr.String()
|
||||
if luaStr, ok := retVal.TryString(); ok {
|
||||
highlighted = luaStr
|
||||
}
|
||||
|
||||
return highlighted
|
||||
|
@ -122,23 +115,20 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
|||
return prefix, compGroup
|
||||
} else {
|
||||
if completecb, ok := luaCompletions["command." + fields[0]]; ok {
|
||||
luaFields := l.NewTable()
|
||||
for _, f := range fields {
|
||||
luaFields.Append(lua.LString(f))
|
||||
luaFields := rt.NewTable()
|
||||
for i, f := range fields {
|
||||
luaFields.Set(rt.IntValue(int64(i + 1)), rt.StringValue(f))
|
||||
}
|
||||
err := l.CallByParam(lua.P{
|
||||
Fn: completecb,
|
||||
NRet: 1,
|
||||
Protect: true,
|
||||
}, lua.LString(query), lua.LString(ctx), luaFields)
|
||||
|
||||
// we must keep the holy 80 cols
|
||||
luacompleteTable, err := rt.Call1(l.MainThread(),
|
||||
rt.FunctionValue(completecb), rt.StringValue(query),
|
||||
rt.StringValue(ctx), rt.TableValue(luaFields))
|
||||
|
||||
if err != nil {
|
||||
return "", compGroup
|
||||
}
|
||||
|
||||
luacompleteTable := l.Get(-1)
|
||||
l.Pop(1)
|
||||
|
||||
/*
|
||||
as an example with git,
|
||||
completion table should be structured like:
|
||||
|
@ -163,40 +153,78 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
|||
it is the responsibility of the completer
|
||||
to work on subcommands and subcompletions
|
||||
*/
|
||||
if cmpTbl, ok := luacompleteTable.(*lua.LTable); ok {
|
||||
cmpTbl.ForEach(func(key lua.LValue, value lua.LValue) {
|
||||
if key.Type() == lua.LTNumber {
|
||||
// completion group
|
||||
if value.Type() == lua.LTTable {
|
||||
luaCmpGroup := value.(*lua.LTable)
|
||||
compType := luaCmpGroup.RawGet(lua.LString("type"))
|
||||
compItems := luaCmpGroup.RawGet(lua.LString("items"))
|
||||
if compType.Type() != lua.LTString {
|
||||
l.RaiseError("bad type name for completion (expected string, got %v)", compType.Type().String())
|
||||
if cmpTbl, ok := luacompleteTable.TryTable(); ok {
|
||||
nextVal := rt.NilValue
|
||||
for {
|
||||
next, val, ok := cmpTbl.Next(nextVal)
|
||||
if next == rt.NilValue {
|
||||
break
|
||||
}
|
||||
if compItems.Type() != lua.LTTable {
|
||||
l.RaiseError("bad items for completion (expected table, got %v)", compItems.Type().String())
|
||||
nextVal = next
|
||||
|
||||
_, 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
|
||||
itemDescriptions := make(map[string]string)
|
||||
compItems.(*lua.LTable).ForEach(func(k lua.LValue, v lua.LValue) {
|
||||
if k.Type() == lua.LTString {
|
||||
// ['--flag'] = {'description', '--flag-alias'}
|
||||
itm := v.(*lua.LTable)
|
||||
items = append(items, k.String())
|
||||
itemDescriptions[k.String()] = itm.RawGet(lua.LNumber(1)).String()
|
||||
} else {
|
||||
items = append(items, v.String())
|
||||
nxVal := rt.NilValue
|
||||
for {
|
||||
nx, vl, _ := compItems.Next(nxVal)
|
||||
if nx == rt.NilValue {
|
||||
break
|
||||
}
|
||||
nxVal = nx
|
||||
|
||||
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
|
||||
switch compType.String() {
|
||||
switch compType {
|
||||
case "grid": dispType = readline.TabDisplayGrid
|
||||
case "list": dispType = readline.TabDisplayList
|
||||
// need special cases, will implement later
|
||||
//case "map": dispType = readline.TabDisplayMap
|
||||
}
|
||||
|
||||
compGroup = append(compGroup, &readline.CompletionGroup{
|
||||
DisplayType: dispType,
|
||||
Descriptions: itemDescriptions,
|
||||
|
@ -206,8 +234,6 @@ func newLineReader(prompt string, noHist bool) *lineReader {
|
|||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(compGroup) == 0 {
|
||||
|
@ -268,56 +294,65 @@ func (lr *lineReader) Resize() {
|
|||
}
|
||||
|
||||
// lua module
|
||||
func (lr *lineReader) Loader(L *lua.LState) *lua.LTable {
|
||||
lrLua := map[string]lua.LGFunction{
|
||||
"add": lr.luaAddHistory,
|
||||
"all": lr.luaAllHistory,
|
||||
"clear": lr.luaClearHistory,
|
||||
"get": lr.luaGetHistory,
|
||||
"size": lr.luaSize,
|
||||
func (lr *lineReader) Loader(rtm *rt.Runtime) *rt.Table {
|
||||
lrLua := map[string]util.LuaExport{
|
||||
"add": {lr.luaAddHistory, 1, false},
|
||||
"all": {lr.luaAllHistory, 0, false},
|
||||
"clear": {lr.luaClearHistory, 0, false},
|
||||
"get": {lr.luaGetHistory, 1, false},
|
||||
"size": {lr.luaSize, 0, false},
|
||||
}
|
||||
|
||||
mod := l.SetFuncs(l.NewTable(), lrLua)
|
||||
mod := rt.NewTable()
|
||||
util.SetExports(rtm, mod, lrLua)
|
||||
|
||||
return mod
|
||||
}
|
||||
|
||||
func (lr *lineReader) luaAddHistory(l *lua.LState) int {
|
||||
cmd := l.CheckString(1)
|
||||
func (lr *lineReader) luaAddHistory(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
|
||||
}
|
||||
lr.AddHistory(cmd)
|
||||
|
||||
return 0
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
func (lr *lineReader) luaSize(L *lua.LState) int {
|
||||
L.Push(lua.LNumber(fileHist.Len()))
|
||||
|
||||
return 1
|
||||
func (lr *lineReader) luaSize(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
return c.PushingNext1(t.Runtime, rt.IntValue(int64(fileHist.Len()))), nil
|
||||
}
|
||||
|
||||
func (lr *lineReader) luaGetHistory(L *lua.LState) int {
|
||||
idx := L.CheckInt(1)
|
||||
cmd, _ := fileHist.GetLine(idx)
|
||||
L.Push(lua.LString(cmd))
|
||||
func (lr *lineReader) luaGetHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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 {
|
||||
tbl := L.NewTable()
|
||||
func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
tbl := rt.NewTable()
|
||||
size := fileHist.Len()
|
||||
|
||||
for i := 1; i < size; i++ {
|
||||
cmd, _ := fileHist.GetLine(i)
|
||||
tbl.Append(lua.LString(cmd))
|
||||
tbl.Set(rt.IntValue(int64(i)), rt.StringValue(cmd))
|
||||
}
|
||||
|
||||
L.Push(tbl)
|
||||
|
||||
return 1
|
||||
return c.PushingNext1(t.Runtime, rt.TableValue(tbl)), nil
|
||||
}
|
||||
|
||||
func (lr *lineReader) luaClearHistory(l *lua.LState) int {
|
||||
func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
fileHist.clear()
|
||||
return 0
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
|
|
@ -1,46 +1,56 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/yuin/gopher-lua"
|
||||
"hilbish/util"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
)
|
||||
|
||||
func runnerModeLoader(L *lua.LState) *lua.LTable {
|
||||
exports := map[string]lua.LGFunction{
|
||||
"sh": shRunner,
|
||||
"lua": luaRunner,
|
||||
"setMode": hlrunnerMode,
|
||||
func runnerModeLoader(rtm *rt.Runtime) *rt.Table {
|
||||
exports := map[string]util.LuaExport{
|
||||
"sh": {shRunner, 1, false},
|
||||
"lua": {luaRunner, 1, false},
|
||||
"setMode": {hlrunnerMode, 1, false},
|
||||
}
|
||||
|
||||
mod := L.SetFuncs(L.NewTable(), exports)
|
||||
L.SetField(mod, "mode", runnerMode)
|
||||
mod := rt.NewTable()
|
||||
util.SetExports(rtm, mod, exports)
|
||||
|
||||
return mod
|
||||
}
|
||||
|
||||
func shRunner(L *lua.LState) int {
|
||||
cmd := L.CheckString(1)
|
||||
func shRunner(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
|
||||
}
|
||||
|
||||
exitCode, err := handleSh(cmd)
|
||||
var luaErr lua.LValue = lua.LNil
|
||||
var luaErr rt.Value = rt.NilValue
|
||||
if err != nil {
|
||||
luaErr = lua.LString(err.Error())
|
||||
luaErr = rt.StringValue(err.Error())
|
||||
}
|
||||
|
||||
L.Push(lua.LNumber(exitCode))
|
||||
L.Push(luaErr)
|
||||
|
||||
return 2
|
||||
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitCode)), luaErr), nil
|
||||
}
|
||||
|
||||
func luaRunner(L *lua.LState) int {
|
||||
cmd := L.CheckString(1)
|
||||
func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exitCode, err := handleLua(cmd)
|
||||
var luaErr lua.LValue = lua.LNil
|
||||
var luaErr rt.Value = rt.NilValue
|
||||
if err != nil {
|
||||
luaErr = lua.LString(err.Error())
|
||||
luaErr = rt.StringValue(err.Error())
|
||||
}
|
||||
|
||||
L.Push(lua.LNumber(exitCode))
|
||||
L.Push(luaErr)
|
||||
|
||||
return 2
|
||||
return c.PushingNext(t.Runtime, rt.IntValue(int64(exitCode)), luaErr), nil
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
import "github.com/yuin/gopher-lua"
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
)
|
||||
|
||||
// Document adds a documentation string to a module.
|
||||
// It is accessible via the __doc metatable.
|
||||
func Document(L *lua.LState, module lua.LValue, doc string) {
|
||||
mt := L.GetMetatable(module)
|
||||
if mt == lua.LNil {
|
||||
mt = L.NewTable()
|
||||
L.SetMetatable(module, mt)
|
||||
func Document(module *rt.Table, doc string) {
|
||||
mt := module.Metatable()
|
||||
|
||||
if mt == nil {
|
||||
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.
|
||||
// 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) {
|
||||
mt := L.GetMetatable(module)
|
||||
if mt == lua.LNil {
|
||||
mt = L.NewTable()
|
||||
docProp := L.NewTable()
|
||||
L.SetField(mt, "__docProp", docProp)
|
||||
func SetField(rtm *rt.Runtime, module *rt.Table, field string, value rt.Value, doc string) {
|
||||
// TODO: ^ rtm isnt needed, i should remove it
|
||||
mt := module.Metatable()
|
||||
|
||||
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))
|
||||
L.SetField(module, field, value)
|
||||
docProp.AsTable().Set(rt.StringValue(field), rt.StringValue(doc))
|
||||
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