refactor!: move global functions to the hilbish module

its more organized this way and the `hilbish` lua module
is now what global was before anyway

a feature in this commit also is that `goro` now allows
passing any amount of args to the calling function
windows-fixes
TorchedSammy 2022-01-26 15:51:52 -04:00
parent ea9c3eac30
commit f2a2ac44d6
Signed by: sammyette
GPG Key ID: 904FC49417B44DCD
4 changed files with 226 additions and 204 deletions

197
api.go
View File

@ -4,10 +4,14 @@
package main package main
import ( import (
"fmt"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings" "strings"
"syscall"
"time"
"hilbish/util" "hilbish/util"
@ -17,10 +21,18 @@ import (
) )
var exports = map[string]lua.LGFunction { var exports = map[string]lua.LGFunction {
"run": hlrun, "alias": hlalias,
"flag": hlflag, "appendPath": hlappendPath,
"cwd": hlcwd, "cwd": hlcwd,
"exec": hlexec,
"flag": hlflag,
"multiprompt": hlmlprompt,
"prependPath": hlprependPath,
"prompt": hlprompt,
"interval": hlinterval,
"read": hlread, "read": hlread,
"run": hlrun,
"timeout": hltimeout,
} }
var greeting string var greeting string
@ -147,3 +159,184 @@ func hlread(L *lua.LState) int {
return 1 return 1
} }
/* prompt(str)
Changes the shell prompt to `str`
There are a few verbs that can be used in the prompt text.
These will be formatted and replaced with the appropriate values.
`%d` - Current working directory
`%u` - Name of current user
`%h` - Hostname of device */
func hlprompt(L *lua.LState) int {
prompt = L.CheckString(1)
return 0
}
// multiprompt(str)
// Changes the continued line prompt to `str`
func hlmlprompt(L *lua.LState) int {
multilinePrompt = L.CheckString(1)
return 0
}
// alias(cmd, orig)
// Sets an alias of `orig` to `cmd`
func hlalias(L *lua.LState) int {
alias := L.CheckString(1)
source := L.CheckString(2)
aliases.Add(alias, source)
return 1
}
// appendPath(dir)
// Appends `dir` to $PATH
func hlappendPath(L *lua.LState) int {
// check if dir is a table or a string
arg := L.Get(1)
fmt.Println(arg.Type())
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())
}
return 0
}
func appendPath(dir string) {
dir = strings.Replace(dir, "~", curuser.HomeDir, 1)
pathenv := os.Getenv("PATH")
// if dir isnt already in $PATH, add it
if !strings.Contains(pathenv, dir) {
os.Setenv("PATH", pathenv + string(os.PathListSeparator) + dir)
}
}
// exec(cmd)
// Replaces running hilbish with `cmd`
func hlexec(L *lua.LState) int {
cmd := L.CheckString(1)
cmdArgs, _ := splitInput(cmd)
cmdPath, err := exec.LookPath(cmdArgs[0])
if err != nil {
fmt.Println(err)
// if we get here, cmdPath will be nothing
// therefore nothing will run
}
// syscall.Exec requires an absolute path to a binary
// path, args, string slice of environments
// TODO: alternative for windows
syscall.Exec(cmdPath, cmdArgs, os.Environ())
return 0 // random thought: does this ever return?
}
// goro(fn)
// Puts `fn` in a goroutine
func hlgoroutine(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)
}
// call fn
go func() {
L.CallByParam(lua.P{
Fn: fn,
NRet: 0,
Protect: true,
}, args...)
}()
return 0
}
// timeout(cb, time)
// Runs the `cb` function after `time` in milliseconds
func hltimeout(L *lua.LState) int {
cb := L.CheckFunction(1)
ms := L.CheckInt(2)
timeout := time.Duration(ms) * time.Millisecond
time.Sleep(timeout)
L.CallByParam(lua.P{
Fn: cb,
NRet: 0,
Protect: true,
})
return 0
}
// interval(cb, time)
// Runs the `cb` function every `time` milliseconds
func hlinterval(L *lua.LState) int {
intervalfunc := L.CheckFunction(1)
ms := L.CheckInt(2)
interval := time.Duration(ms) * time.Millisecond
ticker := time.NewTicker(interval)
stop := make(chan lua.LValue)
go func() {
for {
select {
case <-ticker.C:
if err := L.CallByParam(lua.P{
Fn: intervalfunc,
NRet: 0,
Protect: true,
}); err != nil {
fmt.Fprintln(os.Stderr, "Error in interval function:\n\n", err)
stop <- lua.LTrue // stop the interval
}
case <-stop:
ticker.Stop()
return
}
}
}()
L.Push(lua.LChannel(stop))
return 1
}
// complete(scope, cb)
// Registers a completion handler for `scope`.
// A `scope` is currently only expected to be `command.<cmd>`,
// replacing <cmd> with the name of the command (for example `command.git`).
// `cb` must be a function that returns a table of the entries to complete.
// Nested tables will be used as sub-completions.
func hlcomplete(L *lua.LState) int {
scope := L.CheckString(1)
cb := L.CheckFunction(2)
luaCompletions[scope] = cb
return 0
}
// prependPath(dir)
// Prepends `dir` to $PATH
func hlprependPath(L *lua.LState) int {
dir := L.CheckString(1)
dir = strings.Replace(dir, "~", curuser.HomeDir, 1)
pathenv := os.Getenv("PATH")
// if dir isnt already in $PATH, add in
if !strings.Contains(pathenv, dir) {
os.Setenv("PATH", dir + string(os.PathListSeparator) + pathenv)
}
return 0
}

View File

@ -1,29 +0,0 @@
alias(cmd, orig) > Sets an alias of `orig` to `cmd`
appendPath(dir) > Appends `dir` to $PATH
complete(scope, cb) > Registers a completion handler for `scope`.
A `scope` is currently only expected to be `command.<cmd>`,
replacing <cmd> with the name of the command (for example `command.git`).
`cb` must be a function that returns a table of the entries to complete.
Nested tables will be used as sub-completions.
exec(cmd) > Replaces running hilbish with `cmd`
goro(fn) > Puts `fn` in a goroutine
interval(cb, time) > Runs the `cb` function every `time` milliseconds
multiprompt(str) > Changes the continued line prompt to `str`
prependPath(dir) > Prepends `dir` to $PATH
prompt(str) > Changes the shell prompt to `str`
There are a few verbs that can be used in the prompt text.
These will be formatted and replaced with the appropriate values.
`%d` - Current working directory
`%u` - Name of current user
`%h` - Hostname of device
timeout(cb, time) > Runs the `cb` function after `time` in milliseconds

View File

@ -1,10 +1,39 @@
alias(cmd, orig) > Sets an alias of `orig` to `cmd`
appendPath(dir) > Appends `dir` to $PATH
complete(scope, cb) > Registers a completion handler for `scope`.
A `scope` is currently only expected to be `command.<cmd>`,
replacing <cmd> with the name of the command (for example `command.git`).
`cb` must be a function that returns a table of the entries to complete.
Nested tables will be used as sub-completions.
cwd() > Returns the current directory of the shell cwd() > Returns the current directory of the shell
exec(cmd) > Replaces running hilbish with `cmd`
flag(f) > Checks if the `f` flag has been passed to Hilbish. flag(f) > Checks if the `f` flag has been passed to Hilbish.
goro(fn) > Puts `fn` in a goroutine
interval(cb, time) > Runs the `cb` function every `time` milliseconds
multiprompt(str) > Changes the continued line prompt to `str`
prependPath(dir) > Prepends `dir` to $PATH
prompt(str) > Changes the shell prompt to `str`
There are a few verbs that can be used in the prompt text.
These will be formatted and replaced with the appropriate values.
`%d` - Current working directory
`%u` - Name of current user
`%h` - Hostname of device
read(prompt) -> input? > Read input from the user, using Hilbish's line editor/input reader. 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. This is a separate instance from the one Hilbish actually uses.
Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen) Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen)
run(cmd) > Runs `cmd` in Hilbish's sh interpreter run(cmd) > Runs `cmd` in Hilbish's sh interpreter.
timeout(cb, time) > Runs the `cb` function after `time` in milliseconds

173
lua.go
View File

@ -3,10 +3,6 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"strings"
"syscall"
"time"
"hilbish/golibs/bait" "hilbish/golibs/bait"
"hilbish/golibs/commander" "hilbish/golibs/commander"
@ -14,7 +10,6 @@ import (
"hilbish/golibs/terminal" "hilbish/golibs/terminal"
"github.com/yuin/gopher-lua" "github.com/yuin/gopher-lua"
"layeh.com/gopher-luar"
) )
var minimalconf = `prompt '& '` var minimalconf = `prompt '& '`
@ -23,16 +18,6 @@ func luaInit() {
l = lua.NewState() l = lua.NewState()
l.OpenLibs() l.OpenLibs()
l.SetGlobal("prompt", l.NewFunction(hshprompt))
l.SetGlobal("multiprompt", l.NewFunction(hshmlprompt))
l.SetGlobal("alias", l.NewFunction(hshalias))
l.SetGlobal("appendPath", l.NewFunction(hshappendPath))
l.SetGlobal("prependPath", l.NewFunction(hshprependPath))
l.SetGlobal("exec", l.NewFunction(hshexec))
l.SetGlobal("goro", luar.New(l, hshgoroutine))
l.SetGlobal("timeout", luar.New(l, hshtimeout))
l.SetGlobal("interval", l.NewFunction(hshinterval))
// yes this is stupid, i know // yes this is stupid, i know
l.PreloadModule("hilbish", hilbishLoader) l.PreloadModule("hilbish", hilbishLoader)
l.DoString("hilbish = require 'hilbish'") l.DoString("hilbish = require 'hilbish'")
@ -61,7 +46,7 @@ func luaInit() {
} }
}) })
l.SetGlobal("complete", l.NewFunction(hshcomplete)) l.SetGlobal("complete", l.NewFunction(hlcomplete))
// Add more paths that Lua can require from // Add more paths that Lua can require from
l.DoString("package.path = package.path .. " + requirePaths) l.DoString("package.path = package.path .. " + requirePaths)
@ -102,159 +87,3 @@ func runLogin() {
} }
} }
/* prompt(str)
Changes the shell prompt to `str`
There are a few verbs that can be used in the prompt text.
These will be formatted and replaced with the appropriate values.
`%d` - Current working directory
`%u` - Name of current user
`%h` - Hostname of device */
func hshprompt(L *lua.LState) int {
prompt = L.CheckString(1)
return 0
}
// multiprompt(str)
// Changes the continued line prompt to `str`
func hshmlprompt(L *lua.LState) int {
multilinePrompt = L.CheckString(1)
return 0
}
// alias(cmd, orig)
// Sets an alias of `orig` to `cmd`
func hshalias(L *lua.LState) int {
alias := L.CheckString(1)
source := L.CheckString(2)
aliases.Add(alias, source)
return 1
}
// appendPath(dir)
// Appends `dir` to $PATH
func hshappendPath(L *lua.LState) int {
// check if dir is a table or a string
arg := L.Get(1)
fmt.Println(arg.Type())
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())
}
return 0
}
func appendPath(dir string) {
dir = strings.Replace(dir, "~", curuser.HomeDir, 1)
pathenv := os.Getenv("PATH")
// if dir isnt already in $PATH, add it
if !strings.Contains(pathenv, dir) {
os.Setenv("PATH", pathenv + string(os.PathListSeparator) + dir)
}
}
// exec(cmd)
// Replaces running hilbish with `cmd`
func hshexec(L *lua.LState) int {
cmd := L.CheckString(1)
cmdArgs, _ := splitInput(cmd)
cmdPath, err := exec.LookPath(cmdArgs[0])
if err != nil {
fmt.Println(err)
// if we get here, cmdPath will be nothing
// therefore nothing will run
}
// syscall.Exec requires an absolute path to a binary
// path, args, string slice of environments
// TODO: alternative for windows
syscall.Exec(cmdPath, cmdArgs, os.Environ())
return 0 // random thought: does this ever return?
}
// goro(fn)
// Puts `fn` in a goroutine
func hshgoroutine(gofunc func()) {
go gofunc()
}
// timeout(cb, time)
// Runs the `cb` function after `time` in milliseconds
func hshtimeout(timeoutfunc func(), ms int) {
timeout := time.Duration(ms) * time.Millisecond
time.Sleep(timeout)
timeoutfunc()
}
// interval(cb, time)
// Runs the `cb` function every `time` milliseconds
func hshinterval(L *lua.LState) int {
intervalfunc := L.CheckFunction(1)
ms := L.CheckInt(2)
interval := time.Duration(ms) * time.Millisecond
ticker := time.NewTicker(interval)
stop := make(chan lua.LValue)
go func() {
for {
select {
case <-ticker.C:
if err := L.CallByParam(lua.P{
Fn: intervalfunc,
NRet: 0,
Protect: true,
}); err != nil {
fmt.Fprintln(os.Stderr, "Error in interval function:\n\n", err)
stop <- lua.LTrue // stop the interval
}
case <-stop:
ticker.Stop()
return
}
}
}()
L.Push(lua.LChannel(stop))
return 1
}
// complete(scope, cb)
// Registers a completion handler for `scope`.
// A `scope` is currently only expected to be `command.<cmd>`,
// replacing <cmd> with the name of the command (for example `command.git`).
// `cb` must be a function that returns a table of the entries to complete.
// Nested tables will be used as sub-completions.
func hshcomplete(L *lua.LState) int {
scope := L.CheckString(1)
cb := L.CheckFunction(2)
luaCompletions[scope] = cb
return 0
}
// prependPath(dir)
// Prepends `dir` to $PATH
func hshprependPath(L *lua.LState) int {
dir := L.CheckString(1)
dir = strings.Replace(dir, "~", curuser.HomeDir, 1)
pathenv := os.Getenv("PATH")
// if dir isnt already in $PATH, add in
if !strings.Contains(pathenv, dir) {
os.Setenv("PATH", dir + string(os.PathListSeparator) + pathenv)
}
return 0
}