mirror of
				https://github.com/sammy-ette/Hilbish
				synced 2025-08-10 02:52:03 +00:00 
			
		
		
		
	Compare commits
	
		
			No commits in common. "69d38d704844de95302fe1fc7cdeea9fed9e4162" and "64bf7024d24342b7ae828fd08c8a112cf41f1d83" have entirely different histories.
		
	
	
		
			69d38d7048
			...
			64bf7024d2
		
	
		
							
								
								
									
										49
									
								
								aliases.go
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								aliases.go
									
									
									
									
									
								
							| @ -4,9 +4,7 @@ import ( | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"hilbish/util" | ||||
| 
 | ||||
| 	rt "github.com/arnodel/golua/runtime" | ||||
| 	"github.com/yuin/gopher-lua" | ||||
| ) | ||||
| 
 | ||||
| var aliases *aliasHandler | ||||
| @ -66,38 +64,41 @@ func (a *aliasHandler) Resolve(cmdstr string) string { | ||||
| 
 | ||||
| // lua section | ||||
| 
 | ||||
| func (a *aliasHandler) Loader(rtm *rt.Runtime) *rt.Table { | ||||
| func (a *aliasHandler) Loader(L *lua.LState) *lua.LTable { | ||||
| 	// create a lua module with our functions | ||||
| 	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}, | ||||
| 	hshaliasesLua := map[string]lua.LGFunction{ | ||||
| 		"add": a.luaAdd, | ||||
| 		"list": a.luaList, | ||||
| 		"del": a.luaDelete, | ||||
| 	} | ||||
| 
 | ||||
| 	mod := rt.NewTable() | ||||
| 	util.SetExports(rtm, mod, hshaliasesLua) | ||||
| 	mod := L.SetFuncs(L.NewTable(), hshaliasesLua) | ||||
| 
 | ||||
| 	return mod | ||||
| } | ||||
| 
 | ||||
| func (a *aliasHandler) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| 	aliasesList := rt.NewTable() | ||||
| 	for k, v := range a.All() { | ||||
| 		aliasesList.Set(rt.StringValue(k), rt.StringValue(v)) | ||||
| 	} | ||||
| func (a *aliasHandler) luaAdd(L *lua.LState) int { | ||||
| 	alias := L.CheckString(1) | ||||
| 	cmd := L.CheckString(2) | ||||
| 	a.Add(alias, cmd) | ||||
| 
 | ||||
| 	return c.PushingNext1(t.Runtime, rt.TableValue(aliasesList)), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| func (a *aliasHandler) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| 	if err := c.Check1Arg(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	alias, err := c.StringArg(0) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| func (a *aliasHandler) luaList(L *lua.LState) int { | ||||
| 	aliasesList := L.NewTable() | ||||
| 	for k, v := range a.All() { | ||||
| 		aliasesList.RawSetString(k, lua.LString(v)) | ||||
| 	} | ||||
| 
 | ||||
| 	L.Push(aliasesList) | ||||
| 
 | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| func (a *aliasHandler) luaDelete(L *lua.LState) int { | ||||
| 	alias := L.CheckString(1) | ||||
| 	a.Delete(alias) | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
							
								
								
									
										651
									
								
								api.go
									
									
									
									
									
								
							
							
						
						
									
										651
									
								
								api.go
									
									
									
									
									
								
							| @ -4,8 +4,6 @@ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| @ -16,44 +14,38 @@ import ( | ||||
| 
 | ||||
| 	"hilbish/util" | ||||
| 
 | ||||
| 	rt "github.com/arnodel/golua/runtime" | ||||
| 	"github.com/arnodel/golua/lib/packagelib" | ||||
| 	"github.com/yuin/gopher-lua" | ||||
| 	"github.com/maxlandon/readline" | ||||
| 	"github.com/blackfireio/osinfo" | ||||
| 	"mvdan.cc/sh/v3/interp" | ||||
| ) | ||||
| 
 | ||||
| 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 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 greeting string | ||||
| var hshMod *rt.Table | ||||
| var hilbishLoader = packagelib.Loader{ | ||||
| 	Load: hilbishLoad, | ||||
| 	Name: "hilbish", | ||||
| } | ||||
| var hshMod *lua.LTable | ||||
| 
 | ||||
| func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) { | ||||
| 	mod := rt.NewTable() | ||||
| 	util.SetExports(rtm, mod, exports) | ||||
| func hilbishLoader(L *lua.LState) int { | ||||
| 	mod := L.SetFuncs(L.NewTable(), exports) | ||||
| 	hshMod = mod | ||||
| 
 | ||||
| 	host, _ := os.Hostname() | ||||
| @ -67,72 +59,151 @@ func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) { | ||||
| The nice lil shell for {blue}Lua{reset} fanatics! | ||||
| Check out the {blue}{bold}guide{reset} command to get started. | ||||
| ` | ||||
| 	util.SetField(rtm, mod, "ver", rt.StringValue(version), "Hilbish version") | ||||
| 	util.SetField(rtm, mod, "user", rt.StringValue(username), "Username of user") | ||||
| 	util.SetField(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.") | ||||
| 
 | ||||
| 	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.") | ||||
| 
 | ||||
| 	// hilbish.userDir table | ||||
| 	hshuser := rt.NewTable() | ||||
| 	hshuser := L.NewTable() | ||||
| 
 | ||||
| 	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)) | ||||
| 	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) | ||||
| 
 | ||||
| 	// hilbish.os table | ||||
| 	hshos := rt.NewTable() | ||||
| 	hshos := L.NewTable() | ||||
| 	info, _ := osinfo.GetOSInfo() | ||||
| 
 | ||||
| 	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)) | ||||
| 	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) | ||||
| 
 | ||||
| 	// hilbish.aliases table | ||||
| 	aliases = newAliases() | ||||
| 	aliasesModule := aliases.Loader(rtm) | ||||
| 	util.Document(aliasesModule, "Alias inferface for Hilbish.") | ||||
| 	mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule)) | ||||
| 	aliasesModule := aliases.Loader(L) | ||||
| 	util.Document(L, aliasesModule, "Alias inferface for Hilbish.") | ||||
| 	L.SetField(mod, "aliases", aliasesModule) | ||||
| 
 | ||||
| 	// hilbish.history table | ||||
| 	historyModule := lr.Loader(rtm) | ||||
| 	mod.Set(rt.StringValue("history"), rt.TableValue(historyModule)) | ||||
| 	util.Document(historyModule, "History interface for Hilbish.") | ||||
| 	historyModule := lr.Loader(L) | ||||
| 	util.Document(L, historyModule, "History interface for Hilbish.") | ||||
| 	L.SetField(mod, "history", historyModule) | ||||
| 
 | ||||
| 	// hilbish.completion table | ||||
| 	hshcomp := rt.NewTable() | ||||
| 	util.SetField(rtm, hshcomp, "files", | ||||
| 	rt.FunctionValue(rt.NewGoFunction(luaFileComplete, "files", 3, false)), | ||||
| 	"Completer for files") | ||||
| 	// hilbish.completions table | ||||
| 	hshcomp := L.NewTable() | ||||
| 
 | ||||
| 	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)) | ||||
| 	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) | ||||
| 
 | ||||
| 	// hilbish.runner table | ||||
| 	runnerModule := runnerModeLoader(rtm) | ||||
| 	util.Document(runnerModule, "Runner/exec interface for Hilbish.") | ||||
| 	mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule)) | ||||
| 	runnerModule := runnerModeLoader(L) | ||||
| 	util.Document(L, runnerModule, "Runner/exec interface for Hilbish.") | ||||
| 	L.SetField(mod, "runner", runnerModule) | ||||
| 
 | ||||
| 	// hilbish.jobs table | ||||
| 	jobs = newJobHandler() | ||||
| 	jobModule := jobs.loader(rtm) | ||||
| 	util.Document(jobModule, "(Background) job interface.") | ||||
| 	mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule)) | ||||
| 	jobModule := jobs.loader(L) | ||||
| 	util.Document(L, jobModule, "(Background) job interface.") | ||||
| 	L.SetField(mod, "jobs", jobModule) | ||||
| 
 | ||||
| 	return rt.TableValue(mod), nil | ||||
| 	L.Push(mod) | ||||
| 
 | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| func luaFileComplete(L *lua.LState) int { | ||||
| 	query := L.CheckString(1) | ||||
| 	ctx := L.CheckString(2) | ||||
| 	fields := L.CheckTable(3) | ||||
| 
 | ||||
| 	var fds []string | ||||
| 	fields.ForEach(func(k lua.LValue, v lua.LValue) { | ||||
| 		fds = append(fds, v.String()) | ||||
| 	}) | ||||
| 
 | ||||
| 	completions := fileComplete(query, ctx, fds) | ||||
| 	luaComps := L.NewTable() | ||||
| 
 | ||||
| 	for _, comp := range completions { | ||||
| 		luaComps.Append(lua.LString(comp)) | ||||
| 	} | ||||
| 
 | ||||
| 	L.Push(luaComps) | ||||
| 
 | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| func luaBinaryComplete(L *lua.LState) int { | ||||
| 	query := L.CheckString(1) | ||||
| 	ctx := L.CheckString(2) | ||||
| 	fields := L.CheckTable(3) | ||||
| 
 | ||||
| 	var fds []string | ||||
| 	fields.ForEach(func(k lua.LValue, v lua.LValue) { | ||||
| 		fds = append(fds, v.String()) | ||||
| 	}) | ||||
| 
 | ||||
| 	completions, _ := binaryComplete(query, ctx, fds) | ||||
| 	luaComps := L.NewTable() | ||||
| 
 | ||||
| 	for _, comp := range completions { | ||||
| 		luaComps.Append(lua.LString(comp)) | ||||
| 	} | ||||
| 
 | ||||
| 	L.Push(luaComps) | ||||
| 
 | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| func setVimMode(mode string) { | ||||
| 	util.SetField(l, hshMod, "vimMode", lua.LString(mode), "Current Vim mode of Hilbish (nil if not in Vim mode)") | ||||
| 	hooks.Em.Emit("hilbish.vimMode", mode) | ||||
| } | ||||
| 
 | ||||
| func unsetVimMode() { | ||||
| 	util.SetField(l, hshMod, "vimMode", lua.LNil, "Current Vim mode of Hilbish (nil if not in Vim mode)") | ||||
| } | ||||
| 
 | ||||
| // run(cmd) | ||||
| // Runs `cmd` in Hilbish's sh interpreter. | ||||
| // --- @param cmd string | ||||
| func hlrun(L *lua.LState) int { | ||||
| 	var exitcode uint8 | ||||
| 	cmd := L.CheckString(1) | ||||
| 	err := execCommand(cmd) | ||||
| 
 | ||||
| 	if code, ok := interp.IsExitStatus(err); ok { | ||||
| 		exitcode = code | ||||
| 	} else if err != nil { | ||||
| 		exitcode = 1 | ||||
| 	} | ||||
| 
 | ||||
| 	L.Push(lua.LNumber(exitcode)) | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| // cwd() | ||||
| // Returns the current directory of the shell | ||||
| func hlcwd(L *lua.LState) int { | ||||
| 	cwd, _ := os.Getwd() | ||||
| 
 | ||||
| 	L.Push(lua.LString(cwd)) | ||||
| 
 | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| func getenv(key, fallback string) string { | ||||
| @ -143,160 +214,24 @@ 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(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 | ||||
| 	} | ||||
| func hlread(L *lua.LState) int { | ||||
| 	luaprompt := L.CheckString(1) | ||||
| 	lualr := newLineReader("", true) | ||||
| 	lualr.SetPrompt(luaprompt) | ||||
| 
 | ||||
| 	input, err := lualr.Read() | ||||
| 	if err != nil { | ||||
| 		return c.Next(), nil | ||||
| 		L.Push(lua.LNil) | ||||
| 		return 1 | ||||
| 	} | ||||
| 
 | ||||
| 	return c.PushingNext1(t.Runtime, rt.StringValue(input)), nil | ||||
| 	L.Push(lua.LString(input)) | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| @ -309,91 +244,52 @@ These will be formatted and replaced with the appropriate values. | ||||
| `%h` - Hostname of device | ||||
| --- @param str string | ||||
| */ | ||||
| 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 | ||||
| 	} | ||||
| func hlprompt(L *lua.LState) int { | ||||
| 	prompt = L.CheckString(1) | ||||
| 	lr.SetPrompt(fmtPrompt(prompt)) | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // multiprompt(str) | ||||
| // Changes the continued line prompt to `str` | ||||
| // --- @param str string | ||||
| 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 | ||||
| func hlmlprompt(L *lua.LState) int { | ||||
| 	multilinePrompt = L.CheckString(1) | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // alias(cmd, orig) | ||||
| // Sets an alias of `cmd` to `orig` | ||||
| // --- @param cmd string | ||||
| // --- @param orig string | ||||
| 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 | ||||
| 	} | ||||
| func hlalias(L *lua.LState) int { | ||||
| 	alias := L.CheckString(1) | ||||
| 	source := L.CheckString(2) | ||||
| 
 | ||||
| 	aliases.Add(cmd, orig) | ||||
| 	aliases.Add(alias, source) | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| // appendPath(dir) | ||||
| // Appends `dir` to $PATH | ||||
| // --- @param dir string|table | ||||
| func hlappendPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| 	if err := c.Check1Arg(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	arg := c.Arg(0) | ||||
| 
 | ||||
| func hlappendPath(L *lua.LState) int { | ||||
| 	// check if dir is a table or a 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 | ||||
| 			} | ||||
| 
 | ||||
| 			appendPath(valStr) | ||||
| 		} | ||||
| 	} else if arg.Type() == rt.StringType { | ||||
| 		appendPath(arg.AsString()) | ||||
| 	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 { | ||||
| 		return nil, errors.New("bad argument to appendPath (expected string or table, got " + arg.TypeName() + ")") | ||||
| 		L.RaiseError("bad argument to appendPath (expected string or table, got %v)", L.Get(1).Type().String()) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| func appendPath(dir string) { | ||||
| @ -409,14 +305,8 @@ func appendPath(dir string) { | ||||
| // exec(cmd) | ||||
| // Replaces running hilbish with `cmd` | ||||
| // --- @param cmd string | ||||
| 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 | ||||
| 	} | ||||
| func hlexec(L *lua.LState) int { | ||||
| 	cmd := L.CheckString(1) | ||||
| 	cmdArgs, _ := splitInput(cmd) | ||||
| 	if runtime.GOOS != "windows" { | ||||
| 		cmdPath, err := exec.LookPath(cmdArgs[0]) | ||||
| @ -438,89 +328,78 @@ func hlexec(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| 		os.Exit(0) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // goro(fn) | ||||
| // Puts `fn` in a goroutine | ||||
| // --- @param fn function | ||||
| 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 | ||||
| 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) | ||||
| 	} | ||||
| 
 | ||||
| 	// call fn | ||||
| 	go func() { | ||||
| 		_, err := rt.Call1(l.MainThread(), rt.FunctionValue(fn), c.Etc()...) | ||||
| 		if err != nil { | ||||
| 		if err := L.CallByParam(lua.P{ | ||||
| 			Fn: fn, | ||||
| 			NRet: 0, | ||||
| 			Protect: true, | ||||
| 		}, args...); err != nil { | ||||
| 			fmt.Fprintln(os.Stderr, "Error in goro function:\n\n", err) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // timeout(cb, time) | ||||
| // Runs the `cb` function after `time` in milliseconds | ||||
| // --- @param cb function | ||||
| // --- @param time number | ||||
| 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 | ||||
| 	} | ||||
| func hltimeout(L *lua.LState) int { | ||||
| 	cb := L.CheckFunction(1) | ||||
| 	ms := L.CheckInt(2) | ||||
| 
 | ||||
| 	timeout := time.Duration(ms) * time.Millisecond | ||||
| 	time.Sleep(timeout) | ||||
| 
 | ||||
| 	_, err = rt.Call1(l.MainThread(), rt.FunctionValue(cb))  | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	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) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // interval(cb, time) | ||||
| // Runs the `cb` function every `time` milliseconds | ||||
| // --- @param cb function | ||||
| // --- @param time number | ||||
| 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 | ||||
| 	} | ||||
| 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 rt.Value) | ||||
| 	stop := make(chan lua.LValue) | ||||
| 
 | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-ticker.C: | ||||
| 				_, err := rt.Call1(l.MainThread(), rt.FunctionValue(cb))  | ||||
| 				if err != nil { | ||||
| 				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 <- rt.BoolValue(true) // stop the interval | ||||
| 					stop <- lua.LTrue // stop the interval | ||||
| 				} | ||||
| 			case <-stop: | ||||
| 				ticker.Stop() | ||||
| @ -529,8 +408,8 @@ func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	// TODO: return channel | ||||
| 	return c.Next(), nil | ||||
| 	L.Push(lua.LChannel(stop)) | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| // complete(scope, cb) | ||||
| @ -543,27 +422,20 @@ func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| // `grid` (the normal file completion display) or `list` (with a description) | ||||
| // --- @param scope string | ||||
| // --- @param cb function | ||||
| func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| 	scope, cb, err := util.HandleStrCallback(t, c) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| func hlcomplete(L *lua.LState) int { | ||||
| 	scope := L.CheckString(1) | ||||
| 	cb := L.CheckFunction(2) | ||||
| 
 | ||||
| 	luaCompletions[scope] = cb | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // prependPath(dir) | ||||
| // Prepends `dir` to $PATH | ||||
| // --- @param dir string | ||||
| 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 | ||||
| 	} | ||||
| func hlprependPath(L *lua.LState) int { | ||||
| 	dir := L.CheckString(1) | ||||
| 	dir = strings.Replace(dir, "~", curuser.HomeDir, 1) | ||||
| 	pathenv := os.Getenv("PATH") | ||||
| 
 | ||||
| @ -572,40 +444,29 @@ func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| 		os.Setenv("PATH", dir + string(os.PathListSeparator) + pathenv) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // which(binName) | ||||
| // Searches for an executable called `binName` in the directories of $PATH | ||||
| // --- @param binName string | ||||
| 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 | ||||
| 	} | ||||
| func hlwhich(L *lua.LState) int { | ||||
| 	binName := L.CheckString(1) | ||||
| 	path, err := exec.LookPath(binName) | ||||
| 	if err != nil { | ||||
| 		return c.Next(), nil | ||||
| 		l.Push(lua.LNil) | ||||
| 		return 1 | ||||
| 	} | ||||
| 
 | ||||
| 	return c.PushingNext1(t.Runtime, rt.StringValue(path)), nil | ||||
| 	l.Push(lua.LString(path)) | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| // inputMode(mode) | ||||
| // Sets the input mode for Hilbish's line reader. Accepts either emacs for vim | ||||
| // --- @param mode string | ||||
| 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 | ||||
| 	} | ||||
| 
 | ||||
| func hlinputMode(L *lua.LState) int { | ||||
| 	mode := L.CheckString(1) | ||||
| 	switch mode { | ||||
| 		case "emacs": | ||||
| 			unsetVimMode() | ||||
| @ -613,11 +474,9 @@ func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| 		case "vim": | ||||
| 			setVimMode("insert") | ||||
| 			lr.rl.InputMode = readline.Vim | ||||
| 		default: | ||||
| 			return nil, errors.New("inputMode: expected vim or emacs, received " + mode) | ||||
| 		default: L.RaiseError("inputMode: expected vim or emacs, received " + mode) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // runnerMode(mode) | ||||
| @ -627,24 +486,24 @@ func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| // sh, and lua. It also accepts a function, to which if it is passed one | ||||
| // will call it to execute user input instead. | ||||
| // --- @param mode string|function | ||||
| func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| 	if err := c.Check1Arg(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	mode := c.Arg(0) | ||||
| 
 | ||||
| func hlrunnerMode(L *lua.LState) int { | ||||
| 	mode := L.CheckAny(1) | ||||
| 	switch mode.Type() { | ||||
| 		case rt.StringType: | ||||
| 			switch mode.AsString() { | ||||
| 		case lua.LTString: | ||||
| 			switch mode.String() { | ||||
| 				// 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 "hybrid": fallthrough | ||||
| 				case "hybridRev": fallthrough | ||||
| 				case "lua": fallthrough | ||||
| 				case "sh": | ||||
| 					runnerMode = mode | ||||
| 				default: L.RaiseError("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received %v", mode) | ||||
| 			} | ||||
| 		case rt.FunctionType: runnerMode = mode | ||||
| 		default: return nil, errors.New("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received " + mode.TypeName()) | ||||
| 		case lua.LTFunction: runnerMode = mode | ||||
| 		default: L.RaiseError("execMode: expected either a function or hybrid, hybridRev, lua, sh. Received %v", mode) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // hinter(cb) | ||||
| @ -653,17 +512,11 @@ func hlrunnerMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| // the current line and the position. It is expected to return a string | ||||
| // which will be used for the hint. | ||||
| // --- @param cb function | ||||
| 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 | ||||
| 	} | ||||
| func hlhinter(L *lua.LState) int { | ||||
| 	hinterCb := L.CheckFunction(1) | ||||
| 	hinter = hinterCb | ||||
| 	 | ||||
| 	return c.Next(), err | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // highlighter(cb) | ||||
| @ -672,15 +525,9 @@ func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| // is passed the current line as typed and is expected to return a line that will | ||||
| // be used to display in the line. | ||||
| // --- @param cb function | ||||
| 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 | ||||
| 	} | ||||
| func hlhighlighter(L *lua.LState) int { | ||||
| 	highlighterCb := L.CheckFunction(1) | ||||
| 	highlighter = highlighterCb | ||||
| 
 | ||||
| 	return c.Next(), err | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| @ -45,9 +45,7 @@ read(prompt) -> input? > Read input from the user, using Hilbish's line editor/i | ||||
| This is a separate instance from the one Hilbish actually uses. | ||||
| Returns `input`, will be nil if ctrl + d is pressed, or an error occurs (which shouldn't happen) | ||||
| 
 | ||||
| run(cmd, returnOut) -> exitCode, stdout, stderr > Runs `cmd` in Hilbish's sh interpreter. | ||||
| If returnOut is true, the outputs of `cmd` will be returned as the 2nd and | ||||
| 3rd values instead of being outputted to the terminal. | ||||
| run(cmd) > Runs `cmd` in Hilbish's sh interpreter. | ||||
| 
 | ||||
| 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.multiprompt(str) end | ||||
| function hilbish.mlprompt(str) end | ||||
| 
 | ||||
| --- Prepends `dir` to $PATH | ||||
| --- @param dir string | ||||
| @ -80,8 +80,6 @@ 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,7 +6,6 @@ import ( | ||||
| 	"errors" | ||||
| 	"os/exec" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"runtime" | ||||
| @ -16,7 +15,7 @@ import ( | ||||
| 
 | ||||
| 	"hilbish/util" | ||||
| 
 | ||||
| 	rt "github.com/arnodel/golua/runtime" | ||||
| 	"github.com/yuin/gopher-lua" | ||||
| 	"mvdan.cc/sh/v3/shell" | ||||
| 	//"github.com/yuin/gopher-lua/parse" | ||||
| 	"mvdan.cc/sh/v3/interp" | ||||
| @ -25,15 +24,15 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| var errNotExec = errors.New("not executable") | ||||
| var runnerMode rt.Value = rt.StringValue("hybrid") | ||||
| var runnerMode lua.LValue = lua.LString("hybrid") | ||||
| 
 | ||||
| func runInput(input string, priv bool) { | ||||
| 	running = true | ||||
| 	cmdString := aliases.Resolve(input) | ||||
| 	hooks.Em.Emit("command.preexec", input, cmdString) | ||||
| 
 | ||||
| 	if runnerMode.Type() == rt.StringType { | ||||
| 		switch runnerMode.AsString() { | ||||
| 	if runnerMode.Type() == lua.LTString { | ||||
| 		switch runnerMode.String() { | ||||
| 			case "hybrid": | ||||
| 				_, err := handleLua(cmdString) | ||||
| 				if err == nil { | ||||
| @ -71,23 +70,27 @@ func runInput(input string, priv bool) { | ||||
| 		} | ||||
| 	} else { | ||||
| 		// can only be a string or function so | ||||
| 		term := rt.NewTerminationWith(l.MainThread().CurrentCont(), 2, false) | ||||
| 		err := rt.Call(l.MainThread(), runnerMode, []rt.Value{rt.StringValue(cmdString)}, term) | ||||
| 		err := l.CallByParam(lua.P{ | ||||
| 			Fn: runnerMode, | ||||
| 			NRet: 2, | ||||
| 			Protect: true, | ||||
| 		}, lua.LString(cmdString)) | ||||
| 		if err != nil { | ||||
| 			fmt.Fprintln(os.Stderr, err) | ||||
| 			cmdFinish(124, cmdString, priv) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		luaexitcode := term.Get(0) // first return value (makes sense right i love stacks) | ||||
| 		runErr := term.Get(1) | ||||
| 		luaexitcode := l.Get(-2) // first return value (makes sense right i love stacks) | ||||
| 		runErr := l.Get(-1) | ||||
| 		l.Pop(2) | ||||
| 
 | ||||
| 		var exitCode uint8 | ||||
| 		if code, ok := luaexitcode.TryInt(); ok { | ||||
| 		if code, ok := luaexitcode.(lua.LNumber); luaexitcode != lua.LNil && ok { | ||||
| 			exitCode = uint8(code) | ||||
| 		} | ||||
| 
 | ||||
| 		if runErr != rt.NilValue { | ||||
| 		if runErr != lua.LNil { | ||||
| 			fmt.Fprintln(os.Stderr, runErr) | ||||
| 		} | ||||
| 		cmdFinish(exitCode, cmdString, priv) | ||||
| @ -96,7 +99,7 @@ func runInput(input string, priv bool) { | ||||
| 
 | ||||
| func handleLua(cmdString string) (uint8, error) { | ||||
| 	// First try to load input, essentially compiling to bytecode | ||||
| 	chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv())) | ||||
| 	fn, err := l.LoadString(cmdString) | ||||
| 	if err != nil && noexecute { | ||||
| 		fmt.Println(err) | ||||
| 	/*	if lerr, ok := err.(*lua.ApiError); ok { | ||||
| @ -109,9 +112,8 @@ func handleLua(cmdString string) (uint8, error) { | ||||
| 	} | ||||
| 	// And if there's no syntax errors and -n isnt provided, run | ||||
| 	if !noexecute { | ||||
| 		if chunk != nil { | ||||
| 			_, err = rt.Call1(l.MainThread(), rt.FunctionValue(chunk)) | ||||
| 		} | ||||
| 		l.Push(fn) | ||||
| 		err = l.PCall(0, lua.MultRet, nil) | ||||
| 	} | ||||
| 	if err == nil { | ||||
| 		return 0, nil | ||||
| @ -121,7 +123,7 @@ func handleLua(cmdString string) (uint8, error) { | ||||
| } | ||||
| 
 | ||||
| func handleSh(cmdString string) (uint8, error) { | ||||
| 	_, _, err := execCommand(cmdString, true) | ||||
| 	err := execCommand(cmdString) | ||||
| 	if err != nil { | ||||
| 		// If input is incomplete, start multiline prompting | ||||
| 		if syntax.IsIncomplete(err) { | ||||
| @ -130,7 +132,7 @@ func handleSh(cmdString string) (uint8, error) { | ||||
| 				if err != nil { | ||||
| 					break | ||||
| 				} | ||||
| 				_, _, err = execCommand(cmdString, true) | ||||
| 				err = execCommand(cmdString) | ||||
| 				if syntax.IsIncomplete(err) || strings.HasSuffix(cmdString, "\\") { | ||||
| 					continue | ||||
| 				} else if code, ok := interp.IsExitStatus(err); ok { | ||||
| @ -154,16 +156,10 @@ func handleSh(cmdString string) (uint8, error) { | ||||
| } | ||||
| 
 | ||||
| // Run command in sh interpreter | ||||
| func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) { | ||||
| func execCommand(cmd string) error { | ||||
| 	file, err := syntax.NewParser().Parse(strings.NewReader(cmd), "") | ||||
| 	if err != nil { | ||||
| 		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) | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	var bg bool | ||||
| @ -184,26 +180,31 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) { | ||||
| 		} | ||||
| 
 | ||||
| 		// If command is defined in Lua then run it | ||||
| 		luacmdArgs := rt.NewTable() | ||||
| 		for i, str := range args[1:] { | ||||
| 			luacmdArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(str)) | ||||
| 		luacmdArgs := l.NewTable() | ||||
| 		for _, str := range args[1:] { | ||||
| 			luacmdArgs.Append(lua.LString(str)) | ||||
| 		} | ||||
| 
 | ||||
| 		if commands[args[0]] != nil { | ||||
| 			luaexitcode, err := rt.Call1(l.MainThread(), rt.FunctionValue(commands[args[0]]), rt.TableValue(luacmdArgs)) | ||||
| 			err := l.CallByParam(lua.P{ | ||||
| 				Fn: commands[args[0]], | ||||
| 				NRet:    1, | ||||
| 				Protect: true, | ||||
| 			}, luacmdArgs) | ||||
| 
 | ||||
| 			if err != nil { | ||||
| 				fmt.Fprintln(os.Stderr, "Error in command:\n" + err.Error()) | ||||
| 				fmt.Fprintln(os.Stderr, | ||||
| 					"Error in command:\n\n" + err.Error()) | ||||
| 				return interp.NewExitStatus(1) | ||||
| 			} | ||||
| 
 | ||||
| 			luaexitcode := l.Get(-1) | ||||
| 			var exitcode uint8 | ||||
| 
 | ||||
| 			if code, ok := luaexitcode.TryInt(); ok { | ||||
| 			l.Pop(1) | ||||
| 
 | ||||
| 			if code, ok := luaexitcode.(lua.LNumber); luaexitcode != lua.LNil && ok { | ||||
| 				exitcode = uint8(code) | ||||
| 			} 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) | ||||
| @ -330,7 +331,7 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) { | ||||
| 	} | ||||
| 
 | ||||
| 	runner, _ := interp.New( | ||||
| 		interp.StdIO(os.Stdin, stdout, stderr), | ||||
| 		interp.StdIO(os.Stdin, os.Stdout, os.Stderr), | ||||
| 		interp.ExecHandler(exechandle), | ||||
| 	) | ||||
| 
 | ||||
| @ -350,11 +351,11 @@ func execCommand(cmd string, terminalOut bool) (io.Writer, io.Writer, error) { | ||||
| 
 | ||||
| 		err = runner.Run(context.TODO(), stmt) | ||||
| 		if err != nil { | ||||
| 			return stdout, stderr, err | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return stdout, stderr, nil | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func lookpath(file string) error { // custom lookpath function so we know if a command is found *and* is executable | ||||
| @ -440,9 +441,6 @@ func cmdFinish(code uint8, cmdstr string, private bool) { | ||||
| 	if interactive && !private { | ||||
| 		handleHistory(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) | ||||
| 	util.SetField(l, hshMod, "exitCode", lua.LNumber(code), "Exit code of last exected command") | ||||
| 	hooks.Em.Emit("command.exit", 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,5 +27,3 @@ replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.2022030614040 | ||||
| replace github.com/maxlandon/readline => ./readline | ||||
| 
 | ||||
| replace 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,24 +1,19 @@ | ||||
| github.com/Rosettea/golua v0.0.0-20220329151031-261b8fbd3f78 h1:9YuMWEHn85Av2ZF60OWkcha5Wt56+i6R7hRcHKB5how= | ||||
| github.com/Rosettea/golua v0.0.0-20220329151031-261b8fbd3f78/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE= | ||||
| github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e h1:P2XupP8SaylWaudD1DqbWtZ3mIa8OsE9635LmR+Q+lg= | ||||
| github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20220306140409-795a84b00b4e/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= | ||||
| @ -28,35 +23,32 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||
| github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||||
| github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= | ||||
| github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= | ||||
| 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/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-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,14 +4,13 @@ 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 { | ||||
| @ -20,27 +19,15 @@ func New() Bait { | ||||
| 		emitter.Off(hookname, hookfunc) | ||||
| 		fmt.Println(err) | ||||
| 	}) | ||||
| 	b := Bait{ | ||||
| 	return Bait{ | ||||
| 		Em: emitter, | ||||
| 	} | ||||
| 	b.Loader = packagelib.Loader{ | ||||
| 		Load: b.loaderFunc, | ||||
| 		Name: "bait", | ||||
| 	} | ||||
| 
 | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
| func (b *Bait) Loader(L *lua.LState) int { | ||||
| 	mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{}) | ||||
| 
 | ||||
| 	util.Document(mod, | ||||
| 	util.Document(L, 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 | ||||
| @ -49,81 +36,35 @@ 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.`) | ||||
| 
 | ||||
| 	return rt.TableValue(mod), nil | ||||
| } | ||||
| 	L.SetField(mod, "throw", luar.New(L, b.bthrow)) | ||||
| 	L.SetField(mod, "catch", luar.New(L, b.bcatch)) | ||||
| 	L.SetField(mod, "catchOnce", luar.New(L, b.bcatchOnce)) | ||||
| 
 | ||||
| func handleHook(t *rt.Thread, c *rt.GoCont, name string, catcher *rt.Closure, args ...interface{}) { | ||||
| 	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) | ||||
| 	} | ||||
| 	L.Push(mod) | ||||
| 
 | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| // throw(name, ...args) | ||||
| // Throws a hook with `name` with the provided `args` | ||||
| // --- @param name string | ||||
| // --- @vararg any | ||||
| 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 | ||||
| func (b *Bait) bthrow(name string, args ...interface{}) { | ||||
| 	b.Em.Emit(name, args...) | ||||
| } | ||||
| 
 | ||||
| // 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(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 | ||||
| func (b *Bait) bcatch(name string, catcher func(...interface{})) { | ||||
| 	b.Em.On(name, catcher) | ||||
| } | ||||
| 
 | ||||
| // 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(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 | ||||
| func (b *Bait) bcatchOnce(name string, catcher func(...interface{})) { | ||||
| 	b.Em.Once(name, catcher) | ||||
| } | ||||
|  | ||||
| @ -3,68 +3,52 @@ 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 { | ||||
| 	c := Commander{ | ||||
| 	return Commander{ | ||||
| 		Events: emission.NewEmitter(), | ||||
| 	} | ||||
| 	c.Loader = packagelib.Loader{ | ||||
| 		Load: c.loaderFunc, | ||||
| 		Name: "commander", | ||||
| 	} | ||||
| 
 | ||||
| 	return c | ||||
| } | ||||
| 
 | ||||
| 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}, | ||||
| func (c *Commander) Loader(L *lua.LState) int { | ||||
| 	exports := map[string]lua.LGFunction{ | ||||
| 		"register": c.cregister, | ||||
| 		"deregister": c.cderegister, | ||||
| 	} | ||||
| 	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.") | ||||
| 	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) | ||||
| 
 | ||||
| 	return rt.TableValue(mod), nil | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| // register(name, cb) | ||||
| // Register a command with `name` that runs `cb` when ran | ||||
| // --- @param name string | ||||
| // --- @param cb function | ||||
| 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 | ||||
| 	} | ||||
| func (c *Commander) cregister(L *lua.LState) int { | ||||
| 	cmdName := L.CheckString(1) | ||||
| 	cmd := L.CheckFunction(2) | ||||
| 
 | ||||
| 	c.Events.Emit("commandRegister", cmdName, cmd) | ||||
| 
 | ||||
| 	return ct.Next(), err | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // deregister(name) | ||||
| // Deregisters any command registered with `name` | ||||
| // --- @param name string | ||||
| 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 | ||||
| 	} | ||||
| func (c *Commander) cderegister(L *lua.LState) int { | ||||
| 	cmdName := L.CheckString(1) | ||||
| 
 | ||||
| 	c.Events.Emit("commandDeregister", cmdName) | ||||
| 
 | ||||
| 	return ct.Next(), err | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
							
								
								
									
										122
									
								
								golibs/fs/fs.go
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								golibs/fs/fs.go
									
									
									
									
									
								
							| @ -1,3 +1,5 @@ | ||||
| // The fs module provides easy and simple access to filesystem functions and other | ||||
| // things, and acts an addition to the Lua standard library's I/O and fs functions. | ||||
| package fs | ||||
| 
 | ||||
| import ( | ||||
| @ -6,70 +8,51 @@ import ( | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"hilbish/util" | ||||
| 
 | ||||
| 	rt "github.com/arnodel/golua/runtime" | ||||
| 	"github.com/arnodel/golua/lib/packagelib" | ||||
| 	"github.com/yuin/gopher-lua" | ||||
| ) | ||||
| 
 | ||||
| var Loader = packagelib.Loader{ | ||||
| 	Load: loaderFunc, | ||||
| 	Name: "fs", | ||||
| } | ||||
| func Loader(L *lua.LState) int { | ||||
| 	mod := L.SetFuncs(L.NewTable(), exports) | ||||
| 
 | ||||
| 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 | ||||
| 	util.Document(L, 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.`) | ||||
| 
 | ||||
| 	return rt.TableValue(mod), nil | ||||
| 	L.Push(mod) | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| var exports = map[string]lua.LGFunction{ | ||||
| 	"cd": fcd, | ||||
| 	"mkdir": fmkdir, | ||||
| 	"stat": fstat, | ||||
| 	"readdir": freaddir, | ||||
| } | ||||
| 
 | ||||
| // cd(dir) | ||||
| // Changes directory to `dir` | ||||
| // --- @param dir string | ||||
| 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) | ||||
| func fcd(L *lua.LState) int { | ||||
| 	path := L.CheckString(1) | ||||
| 
 | ||||
| 	err := os.Chdir(strings.TrimSpace(path)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		e := err.(*os.PathError).Err.Error() | ||||
| 		L.RaiseError(e + ": " + path) | ||||
| 	} | ||||
| 
 | ||||
| 	err = os.Chdir(strings.TrimSpace(path)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return c.Next(), err | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // 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(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 | ||||
| 	} | ||||
| func fmkdir(L *lua.LState) int { | ||||
| 	dirname := L.CheckString(1) | ||||
| 	recursive := L.ToBool(2) | ||||
| 	path := strings.TrimSpace(dirname) | ||||
| 	var err error | ||||
| 
 | ||||
| 	if recursive { | ||||
| 		err = os.MkdirAll(path, 0744) | ||||
| @ -77,58 +60,51 @@ func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| 		err = os.Mkdir(path, 0744) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		L.RaiseError(err.Error() + ": " + path) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.Next(), err | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // stat(path) | ||||
| // Returns info about `path` | ||||
| // --- @param path string | ||||
| 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 | ||||
| 	} | ||||
| func fstat(L *lua.LState) int { | ||||
| 	path := L.CheckString(1) | ||||
| 
 | ||||
| 	pathinfo, err := os.Stat(path) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		L.RaiseError(err.Error() + ": " + path) | ||||
| 		return 0 | ||||
| 	} | ||||
| 	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())) | ||||
| 	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) | ||||
| 
 | ||||
| 	return c.PushingNext1(t.Runtime, rt.TableValue(statTbl)), nil | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| // readdir(dir) | ||||
| // Returns a table of files in `dir` | ||||
| // --- @param dir string | ||||
| // --- @return table | ||||
| 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() | ||||
| func freaddir(L *lua.LState) int { | ||||
| 	dir := L.CheckString(1) | ||||
| 	names := L.NewTable() | ||||
| 
 | ||||
| 	dirEntries, err := os.ReadDir(dir) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		L.RaiseError(err.Error() + ": " + dir) | ||||
| 		return 0 | ||||
| 	} | ||||
| 	for i, entry := range dirEntries { | ||||
| 		names.Set(rt.IntValue(int64(i + 1)), rt.StringValue(entry.Name())) | ||||
| 	for _, entry := range dirEntries { | ||||
| 		names.Append(lua.LString(entry.Name())) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil | ||||
| 	L.Push(names) | ||||
| 
 | ||||
| 	return 1 | ||||
| } | ||||
|  | ||||
| @ -5,78 +5,76 @@ 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 | ||||
| var Loader = packagelib.Loader{ | ||||
| 	Load: loaderFunc, | ||||
| 	Name: "terminal", | ||||
| 
 | ||||
| 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 | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| var exports = map[string]lua.LGFunction{ | ||||
| 	"setRaw": termraw, | ||||
| 	"restoreState": termrestoreState, | ||||
| 	"size": termsize, | ||||
| 	"saveState": termsaveState, | ||||
| } | ||||
| 
 | ||||
| // 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(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| func termsize(L *lua.LState) int { | ||||
| 	w, h, err := term.GetSize(int(os.Stdin.Fd())) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		L.RaiseError(err.Error()) | ||||
| 		return 0 | ||||
| 	} | ||||
| 	dimensions := L.NewTable() | ||||
| 	L.SetField(dimensions, "width", lua.LNumber(w)) | ||||
| 	L.SetField(dimensions, "height", lua.LNumber(h)) | ||||
| 
 | ||||
| 	dimensions := rt.NewTable() | ||||
| 	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 | ||||
| 	L.Push(dimensions) | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| // saveState() | ||||
| // Saves the current state of the terminal | ||||
| func termsaveState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| func termsaveState(L *lua.LState) int { | ||||
| 	state, err := term.GetState(int(os.Stdin.Fd())) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		L.RaiseError(err.Error()) | ||||
| 		return 0 | ||||
| 	} | ||||
| 
 | ||||
| 	termState = state | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // restoreState() | ||||
| // Restores the last saved state of the terminal | ||||
| func termrestoreState(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| func termrestoreState(L *lua.LState) int { | ||||
| 	err := term.Restore(int(os.Stdin.Fd()), termState) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		L.RaiseError(err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| // setRaw() | ||||
| // Puts the terminal in raw mode | ||||
| func termsetRaw(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| func termraw(L *lua.LState) int { | ||||
| 	_, err := term.MakeRaw(int(os.Stdin.Fd())) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		L.RaiseError(err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
							
								
								
									
										72
									
								
								job.go
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								job.go
									
									
									
									
									
								
							| @ -4,9 +4,7 @@ import ( | ||||
| 	"sync" | ||||
| 	"os" | ||||
| 
 | ||||
| 	"hilbish/util" | ||||
| 
 | ||||
| 	rt "github.com/arnodel/golua/runtime" | ||||
| 	"github.com/yuin/gopher-lua" | ||||
| ) | ||||
| 
 | ||||
| var jobs *jobHandler | ||||
| @ -40,28 +38,29 @@ func (j *job) setHandle(handle *os.Process) { | ||||
| 	j.proc = handle | ||||
| } | ||||
| 
 | ||||
| func (j *job) lua() rt.Value { | ||||
| 	jobFuncs := map[string]util.LuaExport{ | ||||
| 		"stop": {j.luaStop, 0, false}, | ||||
| func (j *job) lua() *lua.LTable { | ||||
| 	// returns lua table for job | ||||
| 	// because userdata is gross | ||||
| 	jobFuncs := map[string]lua.LGFunction{ | ||||
| 		"stop": j.luaStop, | ||||
| 	} | ||||
| 	luaJob := rt.NewTable() | ||||
| 	util.SetExports(l, luaJob, jobFuncs) | ||||
| 	luaJob := l.SetFuncs(l.NewTable(), jobFuncs) | ||||
| 
 | ||||
| 	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))) | ||||
| 	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)) | ||||
| 
 | ||||
| 	return rt.TableValue(luaJob) | ||||
| 	return luaJob | ||||
| } | ||||
| 
 | ||||
| func (j *job) luaStop(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| func (j *job) luaStop(L *lua.LState) int { | ||||
| 	if j.running { | ||||
| 		j.stop() | ||||
| 	} | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| type jobHandler struct { | ||||
| @ -97,46 +96,41 @@ func (j *jobHandler) getLatest() *job { | ||||
| 	return j.jobs[j.latestID] | ||||
| } | ||||
| 
 | ||||
| func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table { | ||||
| 	jobFuncs := map[string]util.LuaExport{ | ||||
| 		"all": {j.luaAllJobs, 0, false}, | ||||
| 		"get": {j.luaGetJob, 1, false}, | ||||
| 
 | ||||
| func (j *jobHandler) loader(L *lua.LState) *lua.LTable { | ||||
| 	jobFuncs := map[string]lua.LGFunction{ | ||||
| 		"all": j.luaAllJobs, | ||||
| 		"get": j.luaGetJob, | ||||
| 	} | ||||
| 
 | ||||
| 	luaJob := rt.NewTable() | ||||
| 	util.SetExports(rtm, luaJob, jobFuncs) | ||||
| 	luaJob := l.SetFuncs(l.NewTable(), jobFuncs) | ||||
| 
 | ||||
| 	return luaJob | ||||
| } | ||||
| 
 | ||||
| func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| func (j *jobHandler) luaGetJob(L *lua.LState) int { | ||||
| 	j.mu.RLock() | ||||
| 	defer j.mu.RUnlock() | ||||
| 
 | ||||
| 	if err := c.Check1Arg(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	jobID, err := c.IntArg(0) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	jobID := L.CheckInt(1) | ||||
| 	job := j.jobs[jobID] | ||||
| 	if job != nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	L.Push(job.lua()) | ||||
| 
 | ||||
| 	job := j.jobs[int(jobID)] | ||||
| 	if job == nil { | ||||
| 		return c.Next(), nil | ||||
| 	} | ||||
| 
 | ||||
| 	return c.PushingNext1(t.Runtime, job.lua()), nil | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| func (j *jobHandler) luaAllJobs(L *lua.LState) int { | ||||
| 	j.mu.RLock() | ||||
| 	defer j.mu.RUnlock() | ||||
| 
 | ||||
| 	jobTbl := rt.NewTable() | ||||
| 	jobTbl := L.NewTable() | ||||
| 	for id, job := range j.jobs { | ||||
| 		jobTbl.Set(rt.IntValue(int64(id)), job.lua()) | ||||
| 		jobTbl.Insert(id, job.lua()) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.PushingNext1(t.Runtime, rt.TableValue(jobTbl)), nil | ||||
| 	L.Push(jobTbl) | ||||
| 	return 1 | ||||
| } | ||||
|  | ||||
| @ -89,25 +89,21 @@ 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) | ||||
| 	io.write(ansikit.format(text) .. "\n") | ||||
| 	io.flush() | ||||
| 	print(ansikit.print(text)) | ||||
| 	return ansikit | ||||
| end | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										43
									
								
								lua.go
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								lua.go
									
									
									
									
									
								
							| @ -4,42 +4,40 @@ import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 
 | ||||
| 	"hilbish/util" | ||||
| 	"hilbish/golibs/bait" | ||||
| 	"hilbish/golibs/commander" | ||||
| 	"hilbish/golibs/fs" | ||||
| 	"hilbish/golibs/terminal" | ||||
| 
 | ||||
| 	rt "github.com/arnodel/golua/runtime" | ||||
| 	"github.com/arnodel/golua/lib" | ||||
| 	"github.com/yuin/gopher-lua" | ||||
| ) | ||||
| 
 | ||||
| var minimalconf = `hilbish.prompt '& '` | ||||
| 
 | ||||
| func luaInit() { | ||||
| 	l = rt.New(os.Stdout) | ||||
| 	lib.LoadAll(l) | ||||
| 	l = lua.NewState() | ||||
| 	l.OpenLibs() | ||||
| 
 | ||||
| 	lib.LoadLibs(l, hilbishLoader) | ||||
| 	// yes this is stupid, i know | ||||
| 	util.DoString(l, "hilbish = require 'hilbish'") | ||||
| 	l.PreloadModule("hilbish", hilbishLoader) | ||||
| 	l.DoString("hilbish = require 'hilbish'") | ||||
| 
 | ||||
| 	// Add fs and terminal module module to Lua | ||||
| 	lib.LoadLibs(l, fs.Loader) | ||||
| 	lib.LoadLibs(l, terminal.Loader) | ||||
| 	l.PreloadModule("fs", fs.Loader) | ||||
| 	l.PreloadModule("terminal", terminal.Loader) | ||||
| 
 | ||||
| 	cmds := commander.New() | ||||
| 	// When a command from Lua is added, register it for use | ||||
| 	cmds.Events.On("commandRegister", func(cmdName string, cmd *rt.Closure) { | ||||
| 	cmds.Events.On("commandRegister", func(cmdName string, cmd *lua.LFunction) { | ||||
| 		commands[cmdName] = cmd | ||||
| 	}) | ||||
| 	cmds.Events.On("commandDeregister", func(cmdName string) { | ||||
| 		delete(commands, cmdName) | ||||
| 	}) | ||||
| 	lib.LoadLibs(l, cmds.Loader) | ||||
| 	l.PreloadModule("commander", cmds.Loader) | ||||
| 
 | ||||
| 	hooks = bait.New() | ||||
| 	lib.LoadLibs(l, hooks.Loader) | ||||
| 	l.PreloadModule("bait", hooks.Loader) | ||||
| 
 | ||||
| 	// Add Ctrl-C handler | ||||
| 	hooks.Em.On("signal.sigint", func() { | ||||
| @ -49,27 +47,26 @@ func luaInit() { | ||||
| 	}) | ||||
| 
 | ||||
| 	// Add more paths that Lua can require from | ||||
| 	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.") | ||||
| 	} | ||||
| 	l.DoString("package.path = package.path .. " + requirePaths) | ||||
| 
 | ||||
| 	err = util.DoFile(l, "prelude/init.lua") | ||||
| 	err := l.DoFile("prelude/init.lua") | ||||
| 	if err != nil { | ||||
| 		err = util.DoFile(l, preloadPath) | ||||
| 		err = l.DoFile(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 := util.DoFile(l, confpath) | ||||
| 	err := l.DoFile(confpath) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintln(os.Stderr, err, "\nAn error has occured while loading your config! Falling back to minimal default config.") | ||||
| 		util.DoString(l, minimalconf) | ||||
| 		fmt.Fprintln(os.Stderr, err, | ||||
| 			"\nAn error has occured while loading your config! Falling back to minimal default config.") | ||||
| 
 | ||||
| 		l.DoString(minimalconf) | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										19
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								main.go
									
									
									
									
									
								
							| @ -10,21 +10,20 @@ 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 *rt.Runtime | ||||
| 	l *lua.LState | ||||
| 	lr *lineReader | ||||
| 
 | ||||
| 	commands = map[string]*rt.Closure{} | ||||
| 	luaCompletions = map[string]*rt.Closure{} | ||||
| 	commands = map[string]*lua.LFunction{} | ||||
| 	luaCompletions = map[string]*lua.LFunction{} | ||||
| 
 | ||||
| 	confDir string | ||||
| 	userDataDir string | ||||
| @ -152,13 +151,13 @@ func main() { | ||||
| 	} | ||||
| 
 | ||||
| 	if getopt.NArgs() > 0 { | ||||
| 		luaArgs := rt.NewTable() | ||||
| 		for i, arg := range getopt.Args() { | ||||
| 			luaArgs.Set(rt.IntValue(int64(i + 1)), rt.StringValue(arg)) | ||||
| 		luaArgs := l.NewTable() | ||||
| 		for _, arg := range getopt.Args() { | ||||
| 			luaArgs.Append(lua.LString(arg)) | ||||
| 		} | ||||
| 
 | ||||
| 		l.GlobalEnv().Set(rt.StringValue("args"), rt.TableValue(luaArgs)) | ||||
| 		err := util.DoFile(l, getopt.Arg(0)) | ||||
| 		l.SetGlobal("args", luaArgs) | ||||
| 		err := l.DoFile(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', tostring(shlvl + 1)) else os.setenv('SHLVL', '0') end | ||||
| if shlvl ~= nil then os.setenv('SHLVL', shlvl + 1) else os.setenv('SHLVL', 0) end | ||||
| 
 | ||||
| -- Builtins | ||||
| local recentDirs = {} | ||||
|  | ||||
							
								
								
									
										235
									
								
								rl.go
									
									
									
									
									
								
							
							
						
						
									
										235
									
								
								rl.go
									
									
									
									
									
								
							| @ -5,19 +5,18 @@ import ( | ||||
| 	"io" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"hilbish/util" | ||||
| 
 | ||||
| 	"github.com/maxlandon/readline" | ||||
| 	rt "github.com/arnodel/golua/runtime" | ||||
| 	"github.com/yuin/gopher-lua" | ||||
| ) | ||||
| 
 | ||||
| type lineReader struct { | ||||
| 	rl *readline.Instance | ||||
| } | ||||
| var fileHist *fileHistory | ||||
| var hinter *rt.Closure | ||||
| var highlighter *rt.Closure | ||||
| var hinter lua.LValue = lua.LNil | ||||
| var highlighter lua.LValue = lua.LNil | ||||
| 
 | ||||
| // other gophers might hate this naming but this is local, shut up | ||||
| func newLineReader(prompt string, noHist bool) *lineReader { | ||||
| 	rl := readline.NewInstance() | ||||
| 	// we don't mind hilbish.read rl instances having completion, | ||||
| @ -48,38 +47,46 @@ func newLineReader(prompt string, noHist bool) *lineReader { | ||||
| 		hooks.Em.Emit("hilbish.vimAction", actionStr, args) | ||||
| 	} | ||||
| 	rl.HintText = func(line []rune, pos int) []rune { | ||||
| 		if hinter == nil { | ||||
| 		if hinter == lua.LNil { | ||||
| 			return []rune{} | ||||
| 		} | ||||
| 
 | ||||
| 		retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(highlighter), | ||||
| 		rt.StringValue(string(line)), rt.IntValue(int64(pos))) | ||||
| 		err := l.CallByParam(lua.P{ | ||||
| 			Fn: hinter, | ||||
| 			NRet: 1, | ||||
| 			Protect: true, | ||||
| 		}, lua.LString(string(line)), lua.LNumber(pos)) | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			return []rune{} | ||||
| 		} | ||||
| 		 | ||||
| 		retVal := l.Get(-1) | ||||
| 		hintText := "" | ||||
| 		if luaStr, ok := retVal.TryString(); ok { | ||||
| 			hintText = luaStr | ||||
| 		if luaStr, ok := retVal.(lua.LString); retVal != lua.LNil && ok { | ||||
| 			hintText = luaStr.String() | ||||
| 		} | ||||
| 		 | ||||
| 		return []rune(hintText) | ||||
| 	} | ||||
| 	rl.SyntaxHighlighter = func(line []rune) string { | ||||
| 		if highlighter == nil { | ||||
| 		if highlighter == lua.LNil { | ||||
| 			return string(line) | ||||
| 		} | ||||
| 		retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(highlighter), | ||||
| 		rt.StringValue(string(line))) | ||||
| 		err := l.CallByParam(lua.P{ | ||||
| 			Fn: highlighter, | ||||
| 			NRet: 1, | ||||
| 			Protect: true, | ||||
| 		}, lua.LString(string(line))) | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err) | ||||
| 			return string(line) | ||||
| 		} | ||||
| 		 | ||||
| 		retVal := l.Get(-1) | ||||
| 		highlighted := "" | ||||
| 		if luaStr, ok := retVal.TryString(); ok { | ||||
| 			highlighted = luaStr | ||||
| 		if luaStr, ok := retVal.(lua.LString); retVal != lua.LNil && ok { | ||||
| 			highlighted = luaStr.String() | ||||
| 		} | ||||
| 		 | ||||
| 		return highlighted | ||||
| @ -115,20 +122,23 @@ func newLineReader(prompt string, noHist bool) *lineReader { | ||||
| 			return prefix, compGroup | ||||
| 		} else { | ||||
| 			if completecb, ok := luaCompletions["command." + fields[0]]; ok { | ||||
| 				luaFields := rt.NewTable() | ||||
| 				for i, f := range fields { | ||||
| 					luaFields.Set(rt.IntValue(int64(i + 1)), rt.StringValue(f)) | ||||
| 				luaFields := l.NewTable() | ||||
| 				for _, f := range fields { | ||||
| 					luaFields.Append(lua.LString(f)) | ||||
| 				} | ||||
| 
 | ||||
| 				// 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)) | ||||
| 				err := l.CallByParam(lua.P{ | ||||
| 					Fn: completecb, | ||||
| 					NRet: 1, | ||||
| 					Protect: true, | ||||
| 				}, lua.LString(query), lua.LString(ctx), luaFields) | ||||
| 
 | ||||
| 				if err != nil { | ||||
| 					return "", compGroup | ||||
| 				} | ||||
| 
 | ||||
| 				luacompleteTable := l.Get(-1) | ||||
| 				l.Pop(1) | ||||
| 
 | ||||
| 				/* | ||||
| 					as an example with git, | ||||
| 					completion table should be structured like: | ||||
| @ -153,86 +163,50 @@ func newLineReader(prompt string, noHist bool) *lineReader { | ||||
| 					it is the responsibility of the completer | ||||
| 					to work on subcommands and subcompletions | ||||
| 				*/ | ||||
| 				if cmpTbl, ok := luacompleteTable.TryTable(); ok { | ||||
| 					nextVal := rt.NilValue | ||||
| 					for { | ||||
| 						next, val, ok := cmpTbl.Next(nextVal) | ||||
| 						if next == rt.NilValue { | ||||
| 							break | ||||
| 						} | ||||
| 						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) | ||||
| 						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 | ||||
| 				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()) | ||||
| 								} | ||||
| 								items = append(items, nxStr) | ||||
| 								itemDescription, ok := vlTbl.Get(rt.IntValue(1)).TryString() | ||||
| 								if !ok { | ||||
| 									// TODO: error | ||||
| 									continue | ||||
| 								if compItems.Type() != lua.LTTable { | ||||
| 									l.RaiseError("bad items for completion (expected table, got %v)", compItems.Type().String()) | ||||
| 								} | ||||
| 								itemDescriptions[nxStr] = itemDescription | ||||
| 							} else if tstr == rt.IntType { | ||||
| 								vlStr, okk := vl.TryString() | ||||
| 								if !okk { | ||||
| 									// TODO: error | ||||
| 									continue | ||||
| 								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()) | ||||
| 									} | ||||
| 								}) | ||||
| 
 | ||||
| 								var dispType readline.TabDisplayType | ||||
| 								switch compType.String() { | ||||
| 									case "grid": dispType = readline.TabDisplayGrid | ||||
| 									case "list": dispType = readline.TabDisplayList | ||||
| 									// need special cases, will implement later | ||||
| 									//case "map": dispType = readline.TabDisplayMap | ||||
| 								} | ||||
| 								items = append(items, vlStr) | ||||
| 							} else { | ||||
| 								// TODO: error | ||||
| 								continue | ||||
| 								compGroup = append(compGroup, &readline.CompletionGroup{ | ||||
| 									DisplayType: dispType, | ||||
| 									Descriptions: itemDescriptions, | ||||
| 									Suggestions: items, | ||||
| 									TrimSlash: false, | ||||
| 									NoSpace: true, | ||||
| 								}) | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| 						var dispType readline.TabDisplayType | ||||
| 						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, | ||||
| 							Suggestions: items, | ||||
| 							TrimSlash: false, | ||||
| 							NoSpace: true, | ||||
| 						}) | ||||
| 					} | ||||
| 					}) | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| @ -294,65 +268,56 @@ func (lr *lineReader) Resize() { | ||||
| } | ||||
| 
 | ||||
| // lua module | ||||
| 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}, | ||||
| 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, | ||||
| 	} | ||||
| 
 | ||||
| 	mod := rt.NewTable() | ||||
| 	util.SetExports(rtm, mod, lrLua) | ||||
| 	mod := l.SetFuncs(l.NewTable(), lrLua) | ||||
| 
 | ||||
| 	return mod | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| 	} | ||||
| func (lr *lineReader) luaAddHistory(l *lua.LState) int { | ||||
| 	cmd := l.CheckString(1) | ||||
| 	lr.AddHistory(cmd) | ||||
| 
 | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| 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) luaSize(L *lua.LState) int { | ||||
| 	L.Push(lua.LNumber(fileHist.Len())) | ||||
| 
 | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| 	} | ||||
| func (lr *lineReader) luaGetHistory(L *lua.LState) int { | ||||
| 	idx := L.CheckInt(1) | ||||
| 	cmd, _ := fileHist.GetLine(idx) | ||||
| 	L.Push(lua.LString(cmd)) | ||||
| 
 | ||||
| 	cmd, _ := fileHist.GetLine(int(idx)) | ||||
| 
 | ||||
| 	return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| func (lr *lineReader) luaAllHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| 	tbl := rt.NewTable() | ||||
| func (lr *lineReader) luaAllHistory(L *lua.LState) int { | ||||
| 	tbl := L.NewTable() | ||||
| 	size := fileHist.Len() | ||||
| 
 | ||||
| 	for i := 1; i < size; i++ { | ||||
| 		cmd, _ := fileHist.GetLine(i) | ||||
| 		tbl.Set(rt.IntValue(int64(i)), rt.StringValue(cmd)) | ||||
| 		tbl.Append(lua.LString(cmd)) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.PushingNext1(t.Runtime, rt.TableValue(tbl)), nil | ||||
| 	L.Push(tbl) | ||||
| 
 | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| func (lr *lineReader) luaClearHistory(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| func (lr *lineReader) luaClearHistory(l *lua.LState) int { | ||||
| 	fileHist.clear() | ||||
| 	return c.Next(), nil | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| @ -1,56 +1,46 @@ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"hilbish/util" | ||||
| 
 | ||||
| 	rt "github.com/arnodel/golua/runtime" | ||||
| 	"github.com/yuin/gopher-lua" | ||||
| ) | ||||
| 
 | ||||
| func runnerModeLoader(rtm *rt.Runtime) *rt.Table { | ||||
| 	exports := map[string]util.LuaExport{ | ||||
| 		"sh": {shRunner, 1, false}, | ||||
| 		"lua": {luaRunner, 1, false}, | ||||
| 		"setMode": {hlrunnerMode, 1, false}, | ||||
| func runnerModeLoader(L *lua.LState) *lua.LTable { | ||||
| 	exports := map[string]lua.LGFunction{ | ||||
| 		"sh": shRunner, | ||||
| 		"lua": luaRunner, | ||||
| 		"setMode": hlrunnerMode, | ||||
| 	} | ||||
| 
 | ||||
| 	mod := rt.NewTable() | ||||
| 	util.SetExports(rtm, mod, exports) | ||||
| 	mod := L.SetFuncs(L.NewTable(), exports) | ||||
| 	L.SetField(mod, "mode", runnerMode) | ||||
| 
 | ||||
| 	return mod | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| 	} | ||||
| 
 | ||||
| func shRunner(L *lua.LState) int { | ||||
| 	cmd := L.CheckString(1) | ||||
| 	exitCode, err := handleSh(cmd) | ||||
| 	var luaErr rt.Value = rt.NilValue | ||||
| 	var luaErr lua.LValue = lua.LNil | ||||
| 	if err != nil { | ||||
| 		luaErr = rt.StringValue(err.Error()) | ||||
| 		luaErr = lua.LString(err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.PushingNext(t.Runtime, rt.IntValue(int64(exitCode)), luaErr), nil | ||||
| 	L.Push(lua.LNumber(exitCode)) | ||||
| 	L.Push(luaErr) | ||||
| 
 | ||||
| 	return 2 | ||||
| } | ||||
| 
 | ||||
| func luaRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||
| 	if err := c.Check1Arg(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	cmd, err := c.StringArg(0) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| func luaRunner(L *lua.LState) int { | ||||
| 	cmd := L.CheckString(1) | ||||
| 	exitCode, err := handleLua(cmd) | ||||
| 	var luaErr rt.Value = rt.NilValue | ||||
| 	var luaErr lua.LValue = lua.LNil | ||||
| 	if err != nil { | ||||
| 		luaErr = rt.StringValue(err.Error()) | ||||
| 		luaErr = lua.LString(err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.PushingNext(t.Runtime, rt.IntValue(int64(exitCode)), luaErr), nil | ||||
| 	L.Push(lua.LNumber(exitCode)) | ||||
| 	L.Push(luaErr) | ||||
| 
 | ||||
| 	return 2 | ||||
| } | ||||
|  | ||||
| @ -1,19 +0,0 @@ | ||||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	rt "github.com/arnodel/golua/runtime" | ||||
| ) | ||||
| 
 | ||||
| // LuaExport represents a Go function which can be exported to Lua. | ||||
| type LuaExport struct { | ||||
| 	Function rt.GoFunctionFunc | ||||
| 	ArgNum int | ||||
| 	Variadic bool | ||||
| } | ||||
| 
 | ||||
| // SetExports puts the Lua function exports in the table. | ||||
| func SetExports(rtm *rt.Runtime, tbl *rt.Table, exports map[string]LuaExport) { | ||||
| 	for name, export := range exports { | ||||
| 		rtm.SetEnvGoFunc(tbl, name, export.Function, export.ArgNum, export.Variadic) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										122
									
								
								util/util.go
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								util/util.go
									
									
									
									
									
								
							| @ -1,120 +1,32 @@ | ||||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 
 | ||||
| 	rt "github.com/arnodel/golua/runtime" | ||||
| ) | ||||
| import "github.com/yuin/gopher-lua" | ||||
| 
 | ||||
| // Document adds a documentation string to a module. | ||||
| // It is accessible via the __doc metatable. | ||||
| func Document(module *rt.Table, doc string) { | ||||
| 	mt := module.Metatable() | ||||
| 	 | ||||
| 	if mt == nil { | ||||
| 		mt = rt.NewTable() | ||||
| 		module.SetMetatable(mt) | ||||
| 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) | ||||
| 	} | ||||
| 
 | ||||
| 	mt.Set(rt.StringValue("__doc"), rt.StringValue(doc)) | ||||
| 	L.SetField(mt, "__doc", lua.LString(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(rtm *rt.Runtime, module *rt.Table, field string, value rt.Value, doc string) { | ||||
| 	// TODO:    ^ rtm isnt needed, i should remove it | ||||
| 	mt := module.Metatable() | ||||
| 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) | ||||
| 
 | ||||
| 	if mt == nil { | ||||
| 		mt = rt.NewTable() | ||||
| 		docProp := rt.NewTable() | ||||
| 		mt.Set(rt.StringValue("__docProp"), rt.TableValue(docProp)) | ||||
| 
 | ||||
| 		module.SetMetatable(mt) | ||||
| 		L.SetMetatable(module, mt) | ||||
| 	} | ||||
| 	docProp := mt.Get(rt.StringValue("__docProp")) | ||||
| 	docProp := L.GetTable(mt, lua.LString("__docProp")) | ||||
| 
 | ||||
| 	docProp.AsTable().Set(rt.StringValue(field), rt.StringValue(doc)) | ||||
| 	module.Set(rt.StringValue(field), value) | ||||
| 	L.SetField(docProp, field, lua.LString(doc)) | ||||
| 	L.SetField(module, field, value) | ||||
| } | ||||
| 
 | ||||
| // DoString runs the code string in the Lua runtime. | ||||
| func DoString(rtm *rt.Runtime, code string) error { | ||||
| 	chunk, err := rtm.CompileAndLoadLuaChunk("<string>", []byte(code), rt.TableValue(rtm.GlobalEnv())) | ||||
| 	if chunk != nil { | ||||
| 		_, err = rt.Call1(rtm.MainThread(), rt.FunctionValue(chunk)) | ||||
| 	} | ||||
| 
 | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // DoFile runs the contents of the file in the Lua runtime. | ||||
| func DoFile(rtm *rt.Runtime, path string) error { | ||||
| 	f, err := os.Open(path) | ||||
| 	defer f.Close() | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	 | ||||
| 	reader := bufio.NewReader(f) | ||||
| 	c, err := reader.ReadByte() | ||||
| 	if err != nil && err != io.EOF { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// unread so a char won't be missing | ||||
| 	err = reader.UnreadByte() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	var buf []byte | ||||
| 	if c == byte('#') { | ||||
| 		// shebang - skip that line | ||||
| 		_, err := reader.ReadBytes('\n') | ||||
| 		if err != nil && err != io.EOF { | ||||
| 			return err | ||||
| 		} | ||||
| 		buf = []byte{'\n'} | ||||
| 	} | ||||
| 
 | ||||
| 	for { | ||||
| 		line, err := reader.ReadBytes('\n') | ||||
| 		if err != nil { | ||||
| 			if err == io.EOF { | ||||
| 				break | ||||
| 			} | ||||
| 			return err | ||||
| 		} | ||||
| 		 | ||||
| 		buf = append(buf, line...) | ||||
| 	} | ||||
| 
 | ||||
| 	chunk, err := rtm.CompileAndLoadLuaChunk(path, buf, rt.TableValue(rtm.GlobalEnv())) | ||||
| 	if chunk != nil { | ||||
| 		_, err = rt.Call1(rtm.MainThread(), rt.FunctionValue(chunk)) | ||||
| 	} | ||||
| 
 | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // HandleStrCallback handles function parameters for Go functions which take | ||||
| // a string and a closure. | ||||
| func HandleStrCallback(t *rt.Thread, c *rt.GoCont) (string, *rt.Closure, error) { | ||||
| 	if err := c.CheckNArgs(2); err != nil { | ||||
| 		return "", nil, err | ||||
| 	} | ||||
| 	name, err := c.StringArg(0) | ||||
| 	if err != nil { | ||||
| 		return "", nil, err | ||||
| 	} | ||||
| 	cb, err := c.ClosureArg(1) | ||||
| 	if err != nil { | ||||
| 		return "", nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return name, cb, err | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user