From 3e0a2d630b6983a8c163e7746a89e3ed65e8eb51 Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Tue, 19 Jul 2022 17:55:03 -0400 Subject: [PATCH 01/15] feat(hilbish.editor): add getLine function to get contents of line --- editor.go | 7 +++++++ readline/line.go | 2 +- readline/timer.go | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/editor.go b/editor.go index 323f50b..868f458 100644 --- a/editor.go +++ b/editor.go @@ -11,6 +11,7 @@ func editorLoader(rtm *rt.Runtime) *rt.Table { "insert": {editorInsert, 1, false}, "setVimRegister": {editorSetRegister, 1, false}, "getVimRegister": {editorGetRegister, 2, false}, + "getLine": {editorGetLine, 0, false}, } mod := rt.NewTable() @@ -68,3 +69,9 @@ func editorGetRegister(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil } + +func editorGetLine(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + buf := lr.rl.GetLine() + + return c.PushingNext1(t.Runtime, rt.StringValue(string(buf))), nil +} diff --git a/readline/line.go b/readline/line.go index 974a34d..2024bb0 100644 --- a/readline/line.go +++ b/readline/line.go @@ -18,7 +18,7 @@ func (rl *Instance) updateLine(line []rune) { // getLine - In many places we need the current line input. We either return the real line, // or the one that includes the current completion candidate, if there is any. -func (rl *Instance) getLine() []rune { +func (rl *Instance) GetLine() []rune { if len(rl.currentComp) > 0 { return rl.lineComp } diff --git a/readline/timer.go b/readline/timer.go index dc53ca9..76eab23 100644 --- a/readline/timer.go +++ b/readline/timer.go @@ -24,7 +24,7 @@ func delayedSyntaxTimer(rl *Instance, i int64) { // } // We pass either the current line or the one with the current completion. - newLine := rl.DelayedSyntaxWorker(rl.getLine()) + newLine := rl.DelayedSyntaxWorker(rl.GetLine()) var sLine string count := atomic.LoadInt64(&rl.delayedSyntaxCount) if count != i { From 7de835fab440680575bde1ac87d260d59dd28e44 Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Tue, 26 Jul 2022 10:41:12 -0400 Subject: [PATCH 02/15] chore: update lunacolors (adds blackBg format arg) --- libs/lunacolors | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/lunacolors b/libs/lunacolors index d60cd77..8467b87 160000 --- a/libs/lunacolors +++ b/libs/lunacolors @@ -1 +1 @@ -Subproject commit d60cd77c73875b5bb55e5a2fdc30bae01a7ac499 +Subproject commit 8467b87dd8d49c68b4100b2d129d5f071544b8cf From 387d7d2243c1378ccd59fb77e7dd767870feb662 Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Tue, 26 Jul 2022 19:24:02 -0400 Subject: [PATCH 03/15] fix: percentages in completion entries causing a problem in the completion menus --- CHANGELOG.md | 2 ++ readline/comp-grid.go | 4 ++-- readline/comp-group.go | 6 ++++++ readline/comp-list.go | 4 ++-- readline/comp-map.go | 4 ++-- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 522c8d5..48d54f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -138,6 +138,8 @@ menu is open. - `hilbish.dataDir` now has tilde (`~`) expanded. - Arrow keys now work on Windows terminals. - Escape codes now work. +- Escape percentage symbols in completion entries, so you will no longer see +an error of missing format variable ## [1.2.0] - 2022-03-17 ### Added diff --git a/readline/comp-grid.go b/readline/comp-grid.go index 2679835..48a2039 100644 --- a/readline/comp-grid.go +++ b/readline/comp-grid.go @@ -99,7 +99,7 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) { // If group title, print it and adjust offset. if g.Name != "" { - comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, g.Name, RESET) + comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, fmtEscape(g.Name), RESET) rl.tcUsedY++ } @@ -124,7 +124,7 @@ func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) { comp += seqInvert } - comp += fmt.Sprintf("%-"+cellWidth+"s %s", g.Suggestions[i], seqReset) + comp += fmt.Sprintf("%-"+cellWidth+"s %s", fmtEscape(g.Suggestions[i]), seqReset) } // Always add a newline to the group if the end if not punctuated with one diff --git a/readline/comp-group.go b/readline/comp-group.go index 6a6e7bc..0c53ed1 100644 --- a/readline/comp-group.go +++ b/readline/comp-group.go @@ -1,5 +1,7 @@ package readline +import "strings" + // CompletionGroup - A group/category of items offered to completion, with its own // name, descriptions and completion display format/type. // The output, if there are multiple groups available for a given completion input, @@ -285,3 +287,7 @@ func (g *CompletionGroup) goLastCell() { g.tcPosX = 0 } } + +func fmtEscape(s string) string { + return strings.Replace(s, "%", "%%", -1) +} diff --git a/readline/comp-list.go b/readline/comp-list.go index 42add2f..cdcda8f 100644 --- a/readline/comp-list.go +++ b/readline/comp-list.go @@ -206,12 +206,12 @@ func (g *CompletionGroup) writeList(rl *Instance) (comp string) { if len(item) > maxLength { item = item[:maxLength-3] + "..." } - sugg := fmt.Sprintf("\r%s%-"+cellWidth+"s", highlight(y, 0), item) + sugg := fmt.Sprintf("\r%s%-"+cellWidth+"s", highlight(y, 0), fmtEscape(item)) // Alt suggestion alt, ok := g.Aliases[item] if ok { - alt = fmt.Sprintf(" %s%"+cellWidthAlt+"s", highlight(y, 1), alt) + alt = fmt.Sprintf(" %s%"+cellWidthAlt+"s", highlight(y, 1), fmtEscape(alt)) } else { // Else, make an empty cell alt = strings.Repeat(" ", maxLengthAlt+1) // + 2 to keep account of spaces diff --git a/readline/comp-map.go b/readline/comp-map.go index 42b56cf..ec985ff 100644 --- a/readline/comp-map.go +++ b/readline/comp-map.go @@ -76,7 +76,7 @@ func (g *CompletionGroup) writeMap(rl *Instance) (comp string) { if g.Name != "" { // Print group title (changes with line returns depending on type) - comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, g.Name, RESET) + comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, fmtEscape(g.Name), RESET) rl.tcUsedY++ } @@ -126,7 +126,7 @@ func (g *CompletionGroup) writeMap(rl *Instance) (comp string) { } comp += fmt.Sprintf("\r%-"+cellWidth+"s %s %-"+itemWidth+"s %s\n", - description, highlight(y), item, seqReset) + description, highlight(y), fmtEscape(item), seqReset) } // Add the equivalent of this group's size to final screen clearing From 6ce4fb3973c3e484d84c901c92dd87c1a99880cb Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Tue, 16 Aug 2022 12:30:39 -0400 Subject: [PATCH 04/15] fix: add dot to sample config path on windows --- vars_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vars_windows.go b/vars_windows.go index 403941f..d1bd7b6 100644 --- a/vars_windows.go +++ b/vars_windows.go @@ -12,6 +12,6 @@ var ( .. hilbish.userDir.config .. '\\Hilbish\\libs\\?.lua;'` dataDir = util.ExpandHome("~\\Appdata\\Roaming\\Hilbish") // ~ and \ gonna cry? preloadPath = dataDir + "\\nature\\init.lua" - sampleConfPath = dataDir + "\\hilbishrc.lua" // Path to default/sample config + sampleConfPath = dataDir + "\\.hilbishrc.lua" // Path to default/sample config defaultConfDir = "" ) From 2337f9ab60d0b2773420ac46db467a03694d85e7 Mon Sep 17 00:00:00 2001 From: sammy <38820196+TorchedSammy@users.noreply.github.com> Date: Wed, 17 Aug 2022 18:01:32 -0400 Subject: [PATCH 05/15] refactor: use custom event emitter (#193) * refactor: use custom event emitter * fix: sigint hook emit on windows * fix: restore correct hilbish conf file * fix: call recoverer for go listeners * refactor(golibs/bait): use 1 map for listeners * feat: add once listeners, ability to remove listeners and remove listener on error * perf: reslice listener slice instead of trying to do ordered move with append * feat(bait): add release function to remove event listener * perf: remove listener directly from once emit instead of using off function * refactor: use bait event emitter on commander * docs(golibs/bait): add doc strings for functions * docs: set changelog * docs(golibs/bait): add docs for lua release function --- CHANGELOG.md | 5 + api.go | 2 +- exec.go | 6 +- golibs/bait/bait.go | 202 ++++++++++++++++++++++++++++++---- golibs/commander/commander.go | 8 +- job.go | 6 +- lua.go | 22 +++- main.go | 10 +- rl.go | 4 +- signal_unix.go | 8 +- signal_windows.go | 2 +- 11 files changed, 226 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48d54f2..49904ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,11 @@ set `hilbish.opts.motd` to false. disables commands being added to history. - `hilbish.rawInput` hook for input from the readline library - Completion of files in quotes +- A new and "safer" event emitter has been added. This causes a performance deficit, but avoids a lot of +random errors introduced with the new Lua runtime (see [#197]) +- `bait.release(name, catcher)` removes `handler` for the named `event` + +[#197]: https://github.com/Rosettea/Hilbish/issues/197 ### Changed - **Breaking Change:** Upgraded to Lua 5.4. diff --git a/api.go b/api.go index dacae02..d060597 100644 --- a/api.go +++ b/api.go @@ -189,7 +189,7 @@ func getenv(key, fallback string) string { 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) + hooks.Emit("hilbish.vimMode", mode) } func unsetVimMode() { diff --git a/exec.go b/exec.go index e1862ed..d605a4e 100644 --- a/exec.go +++ b/exec.go @@ -85,7 +85,7 @@ func isExecError(err error) (execError, bool) { func runInput(input string, priv bool) { running = true cmdString := aliases.Resolve(input) - hooks.Em.Emit("command.preexec", input, cmdString) + hooks.Emit("command.preexec", input, cmdString) rerun: var exitCode uint8 @@ -140,7 +140,7 @@ func runInput(input string, priv bool) { if err != nil { if exErr, ok := isExecError(err); ok { - hooks.Em.Emit("command." + exErr.typ, exErr.cmd) + hooks.Emit("command." + exErr.typ, exErr.cmd) err = exErr.sprint() } fmt.Fprintln(os.Stderr, err) @@ -544,5 +544,5 @@ func cmdFinish(code uint8, cmdstr string, private bool) { // 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, private) + hooks.Emit("command.exit", rt.IntValue(int64(code)), cmdstr, private) } diff --git a/golibs/bait/bait.go b/golibs/bait/bait.go index 3112903..89e0c4a 100644 --- a/golibs/bait/bait.go +++ b/golibs/bait/bait.go @@ -1,27 +1,41 @@ package bait import ( - "fmt" "hilbish/util" rt "github.com/arnodel/golua/runtime" "github.com/arnodel/golua/lib/packagelib" - "github.com/chuckpreslar/emission" ) -type Bait struct{ - Em *emission.Emitter - Loader packagelib.Loader +type listenerType int +const ( + goListener listenerType = iota + luaListener +) + +// Recoverer is a function which is called when a panic occurs in an event. +type Recoverer func(event string, handler *Listener, err interface{}) + +// Listener is a struct that holds the handler for an event. +type Listener struct{ + typ listenerType + once bool + caller func(...interface{}) + luaCaller *rt.Closure } -func New() Bait { - emitter := emission.NewEmitter() - emitter.RecoverWith(func(hookname, hookfunc interface{}, err error) { - emitter.Off(hookname, hookfunc) - fmt.Println(err) - }) - b := Bait{ - Em: emitter, +type Bait struct{ + Loader packagelib.Loader + recoverer Recoverer + handlers map[string][]*Listener + rtm *rt.Runtime +} + +// New creates a new Bait instance. +func New(rtm *rt.Runtime) *Bait { + b := &Bait{ + handlers: make(map[string][]*Listener), + rtm: rtm, } b.Loader = packagelib.Loader{ Load: b.loaderFunc, @@ -31,11 +45,148 @@ func New() Bait { return b } +// Emit throws an event. +func (b *Bait) Emit(event string, args ...interface{}) { + handles := b.handlers[event] + if handles == nil { + return + } + + for idx, handle := range handles { + defer func() { + if err := recover(); err != nil { + b.callRecoverer(event, handle, err) + } + }() + + if handle.typ == luaListener { + funcVal := rt.FunctionValue(handle.luaCaller) + 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(b.rtm.MainThread(), funcVal, luaArgs...) + if err != nil { + // panicking here won't actually cause hilbish to panic and instead will + // print the error and remove the hook. reference the recoverer function in lua.go + panic(err) + } + } else { + handle.caller(args...) + } + + if handle.once { + b.removeListener(event, idx) + } + } +} + +// On adds a Go function handler for an event. +func (b *Bait) On(event string, handler func(...interface{})) *Listener { + listener := &Listener{ + typ: goListener, + caller: handler, + } + + b.addListener(event, listener) + return listener +} + +// OnLua adds a Lua function handler for an event. +func (b *Bait) OnLua(event string, handler *rt.Closure) *Listener { + listener :=&Listener{ + typ: luaListener, + luaCaller: handler, + } + b.addListener(event, listener) + + return listener +} + +// Off removes a Go function handler for an event. +func (b *Bait) Off(event string, listener *Listener) { + handles := b.handlers[event] + + for i, handle := range handles { + if handle == listener { + b.removeListener(event, i) + } + } +} + +// OffLua removes a Lua function handler for an event. +func (b *Bait) OffLua(event string, handler *rt.Closure) { + handles := b.handlers[event] + + for i, handle := range handles { + if handle.luaCaller == handler { + b.removeListener(event, i) + } + } +} + +// Once adds a Go function listener for an event that only runs once. +func (b *Bait) Once(event string, handler func(...interface{})) *Listener { + listener := &Listener{ + typ: goListener, + once: true, + caller: handler, + } + b.addListener(event, listener) + + return listener +} + +// OnceLua adds a Lua function listener for an event that only runs once. +func (b *Bait) OnceLua(event string, handler *rt.Closure) *Listener { + listener := &Listener{ + typ: luaListener, + once: true, + luaCaller: handler, + } + b.addListener(event, listener) + + return listener +} + +// SetRecoverer sets the function to be executed when a panic occurs in an event. +func (b *Bait) SetRecoverer(recoverer Recoverer) { + b.recoverer = recoverer +} + +func (b *Bait) addListener(event string, listener *Listener) { + if b.handlers[event] == nil { + b.handlers[event] = []*Listener{} + } + + b.handlers[event] = append(b.handlers[event], listener) +} + + +func (b *Bait) removeListener(event string, idx int) { + b.handlers[event][idx] = b.handlers[event][len(b.handlers[event]) - 1] + + b.handlers[event] = b.handlers[event][:len(b.handlers[event]) - 1] +} + +func (b *Bait) callRecoverer(event string, handler *Listener, err interface{}) { + if b.recoverer == nil { + panic(err) + } + b.recoverer(event, handler, err) +} + 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}, + "release": util.LuaExport{b.brelease, 2, false}, } mod := rt.NewTable() util.SetExports(rtm, mod, exports) @@ -89,7 +240,7 @@ func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { for i, v := range c.Etc() { ifaceSlice[i] = v } - b.Em.Emit(name, ifaceSlice...) + b.Emit(name, ifaceSlice...) return c.Next(), nil } @@ -104,9 +255,7 @@ func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return nil, err } - b.Em.On(name, func(args ...interface{}) { - handleHook(t, c, name, catcher, args...) - }) + b.OnLua(name, catcher) return c.Next(), nil } @@ -121,9 +270,22 @@ func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return nil, err } - b.Em.Once(name, func(args ...interface{}) { - handleHook(t, c, name, catcher, args...) - }) + b.OnceLua(name, catcher) + + return c.Next(), nil +} + +// release(name, catcher) +// Removes the `catcher` for the event with `name` +// For this to work, `catcher` has to be the same function used to catch +// an event, like one saved to a variable. +func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { + name, catcher, err := util.HandleStrCallback(t, c) + if err != nil { + return nil, err + } + + b.OffLua(name, catcher) return c.Next(), nil } diff --git a/golibs/commander/commander.go b/golibs/commander/commander.go index d279e0c..24f1c03 100644 --- a/golibs/commander/commander.go +++ b/golibs/commander/commander.go @@ -2,20 +2,20 @@ package commander import ( "hilbish/util" + "hilbish/golibs/bait" rt "github.com/arnodel/golua/runtime" "github.com/arnodel/golua/lib/packagelib" - "github.com/chuckpreslar/emission" ) type Commander struct{ - Events *emission.Emitter + Events *bait.Bait Loader packagelib.Loader } -func New() Commander { +func New(rtm *rt.Runtime) Commander { c := Commander{ - Events: emission.NewEmitter(), + Events: bait.New(rtm), } c.Loader = packagelib.Loader{ Load: c.loaderFunc, diff --git a/job.go b/job.go index 7dd07d3..709cc1f 100644 --- a/job.go +++ b/job.go @@ -67,7 +67,7 @@ func (j *job) start() error { j.pid = proc.Pid j.running = true - hooks.Em.Emit("job.start", rt.UserDataValue(j.ud)) + hooks.Emit("job.start", rt.UserDataValue(j.ud)) return err } @@ -82,7 +82,7 @@ func (j *job) stop() { func (j *job) finish() { j.running = false - hooks.Em.Emit("job.done", rt.UserDataValue(j.ud)) + hooks.Emit("job.done", rt.UserDataValue(j.ud)) } func (j *job) wait() { @@ -236,7 +236,7 @@ func (j *jobHandler) add(cmd string, args []string, path string) *job { jb.ud = jobUserData(jb) j.jobs[j.latestID] = jb - hooks.Em.Emit("job.add", rt.UserDataValue(jb.ud)) + hooks.Emit("job.add", rt.UserDataValue(jb.ud)) return jb } diff --git a/lua.go b/lua.go index 92ece05..419970c 100644 --- a/lua.go +++ b/lua.go @@ -32,28 +32,38 @@ func luaInit() { lib.LoadLibs(l, fs.Loader) lib.LoadLibs(l, terminal.Loader) - cmds := commander.New() + cmds := commander.New(l) // 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(args ...interface{}) { + cmdName := args[0].(string) + cmd := args[1].(*rt.Closure) + commands[cmdName] = cmd }) - cmds.Events.On("commandDeregister", func(cmdName string) { + cmds.Events.On("commandDeregister", func(args ...interface{}) { + cmdName := args[0].(string) + delete(commands, cmdName) }) lib.LoadLibs(l, cmds.Loader) - hooks = bait.New() + hooks = bait.New(l) + hooks.SetRecoverer(func(event string, handler *bait.Listener, err interface{}) { + fmt.Println("Error in", event, "event:", err) + hooks.Off(event, handler) + }) + lib.LoadLibs(l, hooks.Loader) // Add Ctrl-C handler - hooks.Em.On("signal.sigint", func() { + hooks.On("signal.sigint", func(...interface{}) { if !interactive { os.Exit(0) } }) lr.rl.RawInputCallback = func(r []rune) { - hooks.Em.Emit("hilbish.rawInput", string(r)) + hooks.Emit("hilbish.rawInput", string(r)) } // Add more paths that Lua can require from diff --git a/main.go b/main.go index e53055d..ee0f584 100644 --- a/main.go +++ b/main.go @@ -30,7 +30,7 @@ var ( userDataDir string curuser *user.User - hooks bait.Bait + hooks *bait.Bait defaultConfPath string defaultHistPath string ) @@ -138,7 +138,7 @@ func main() { } else { runConfig(*configflag) } - hooks.Em.Emit("hilbish.init") + hooks.Emit("hilbish.init") if fileInfo, _ := os.Stdin.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 { scanner := bufio.NewScanner(bufio.NewReader(os.Stdin)) @@ -177,7 +177,7 @@ input: if err == io.EOF { // Exit if user presses ^D (ctrl + d) - hooks.Em.Emit("hilbish.exit") + hooks.Emit("hilbish.exit") break } if err != nil { @@ -196,7 +196,7 @@ input: input = strings.TrimSpace(input) if len(input) == 0 { running = true - hooks.Em.Emit("command.exit", 0) + hooks.Emit("command.exit", 0) continue } @@ -227,7 +227,7 @@ input: } func continuePrompt(prev string) (string, error) { - hooks.Em.Emit("multiline", nil) + hooks.Emit("multiline", nil) lr.SetPrompt(multilinePrompt) cont, err := lr.Read() if err != nil { diff --git a/rl.go b/rl.go index 6350aa2..59423ff 100644 --- a/rl.go +++ b/rl.go @@ -48,7 +48,7 @@ func newLineReader(prompt string, noHist bool) *lineReader { case readline.VimActionPaste: actionStr = "paste" case readline.VimActionYank: actionStr = "yank" } - hooks.Em.Emit("hilbish.vimAction", actionStr, args) + hooks.Emit("hilbish.vimAction", actionStr, args) } rl.HintText = func(line []rune, pos int) []rune { if hinter == nil { @@ -179,7 +179,7 @@ func newLineReader(prompt string, noHist bool) *lineReader { } func (lr *lineReader) Read() (string, error) { - hooks.Em.Emit("command.precmd", nil) + hooks.Emit("command.precmd", nil) s, err := lr.rl.Readline() // this is so dumb if err == readline.EOF { diff --git a/signal_unix.go b/signal_unix.go index bd5984c..2e6c885 100644 --- a/signal_unix.go +++ b/signal_unix.go @@ -15,11 +15,11 @@ func handleSignals() { for s := range c { switch s { - case os.Interrupt: hooks.Em.Emit("signal.sigint") + case os.Interrupt: hooks.Emit("signal.sigint") case syscall.SIGTERM: exit(0) - case syscall.SIGWINCH: hooks.Em.Emit("signal.resize") - case syscall.SIGUSR1: hooks.Em.Emit("signal.sigusr1") - case syscall.SIGUSR2: hooks.Em.Emit("signal.sigusr2") + case syscall.SIGWINCH: hooks.Emit("signal.resize") + case syscall.SIGUSR1: hooks.Emit("signal.sigusr1") + case syscall.SIGUSR2: hooks.Emit("signal.sigusr2") } } } diff --git a/signal_windows.go b/signal_windows.go index 9f67537..42a9fff 100644 --- a/signal_windows.go +++ b/signal_windows.go @@ -14,7 +14,7 @@ func handleSignals() { for s := range c { switch s { case os.Interrupt: - hooks.Em.Emit("signal.sigint") + hooks.Emit("signal.sigint") if !running && interactive { lr.ClearInput() } From 3dcd99563a2c8de16745893cce115c8eb28bbc0f Mon Sep 17 00:00:00 2001 From: TorchedSammy Date: Wed, 17 Aug 2022 22:01:55 +0000 Subject: [PATCH 06/15] docs: [ci] generate new docs --- docs/bait.txt | 4 ++++ emmyLuaDocs/bait.lua | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/docs/bait.txt b/docs/bait.txt index 9f1e54b..fdc712f 100644 --- a/docs/bait.txt +++ b/docs/bait.txt @@ -2,5 +2,9 @@ catch(name, cb) > Catches a hook with `name`. Runs the `cb` when it is thrown catchOnce(name, cb) > Same as catch, but only runs the `cb` once and then removes the hook +release(name, catcher) > Removes the `catcher` for the event with `name` +For this to work, `catcher` has to be the same function used to catch +an event, like one saved to a variable. + throw(name, ...args) > Throws a hook with `name` with the provided `args` diff --git a/emmyLuaDocs/bait.lua b/emmyLuaDocs/bait.lua index 01ca774..a5ecebd 100644 --- a/emmyLuaDocs/bait.lua +++ b/emmyLuaDocs/bait.lua @@ -12,6 +12,11 @@ function bait.catch(name, cb) end --- @param cb function function bait.catchOnce(name, cb) end +--- Removes the `catcher` for the event with `name` +--- For this to work, `catcher` has to be the same function used to catch +--- an event, like one saved to a variable. +function bait.release() end + --- Throws a hook with `name` with the provided `args` --- @param name string --- @vararg any From 20870b9004b9cad0441db069c8c11897b7f65686 Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Wed, 17 Aug 2022 18:05:20 -0400 Subject: [PATCH 07/15] fix: only print motd when interactive --- nature/opts/motd.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nature/opts/motd.lua b/nature/opts/motd.lua index b22f5a2..5f30a6c 100644 --- a/nature/opts/motd.lua +++ b/nature/opts/motd.lua @@ -7,7 +7,7 @@ anymore, that will definitely be why! A MOTD, very message, much day. ]] bait.catch('hilbish.init', function() - if hilbish.opts.motd then + if hilbish.interactive and hilbish.opts.motd then print(lunacolors.format(hilbish.motd)) end end) From a1410ae7ad259a6f7e1f42f48060c82bb4fb017f Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Tue, 30 Aug 2022 21:52:07 -0400 Subject: [PATCH 08/15] fix: set prompt to normal after ctrl d exit this prevents the prompt being stuck at the multiline prompt instead of normal prompt after exiting via the ctrl d bind --- exec.go | 1 + 1 file changed, 1 insertion(+) diff --git a/exec.go b/exec.go index d605a4e..dcf8845 100644 --- a/exec.go +++ b/exec.go @@ -152,6 +152,7 @@ func reprompt(input string) (string, error) { for { in, err := continuePrompt(strings.TrimSuffix(input, "\\")) if err != nil { + lr.SetPrompt(fmtPrompt(prompt)) return input, err } From a1ce2ecba683589851aa2f3c6684824c67f52e87 Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Tue, 30 Aug 2022 22:37:21 -0400 Subject: [PATCH 09/15] fix(readline): correct function to count len of prompt to accomodate east asian characters --- readline/prompt.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/readline/prompt.go b/readline/prompt.go index 79f7c71..0f6ca5a 100644 --- a/readline/prompt.go +++ b/readline/prompt.go @@ -4,7 +4,6 @@ import ( "fmt" ansi "github.com/acarl005/stripansi" - "github.com/rivo/uniseg" ) // SetPrompt will define the readline prompt string. @@ -209,7 +208,7 @@ func (rl *Instance) colorizeVimPrompt(p []rune) (cp []rune) { // getting its real-printed length. func getRealLength(s string) (l int) { stripped := ansi.Strip(s) - return uniseg.GraphemeClusterCount(stripped) + return getWidth([]rune(stripped)) } func (rl *Instance) echoRightPrompt() { From c96605e79c85ee62b56ddf20be87d0b8dc7d96a8 Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Tue, 30 Aug 2022 23:07:24 -0400 Subject: [PATCH 10/15] feat: allow hilbish.runner.sh to be overridden --- exec.go | 12 +++++++++++- runnermode.go | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/exec.go b/exec.go index dcf8845..26c0b73 100644 --- a/exec.go +++ b/exec.go @@ -221,7 +221,17 @@ func handleLua(cmdString string) (string, uint8, error) { return cmdString, 125, err } -func handleSh(cmdString string) (string, uint8, bool, error) { +func handleSh(cmdString string) (input string, exitCode uint8, cont bool, runErr error) { + shRunner := hshMod.Get(rt.StringValue("runner")).AsTable().Get(rt.StringValue("sh")) + var err error + input, exitCode, cont, runErr, err = runLuaRunner(shRunner, cmdString) + if err != nil { + runErr = err + } + return +} + +func execSh(cmdString string) (string, uint8, bool, error) { _, _, err := execCommand(cmdString, true) if err != nil { // If input is incomplete, start multiline prompting diff --git a/runnermode.go b/runnermode.go index eca33ba..b8995cd 100644 --- a/runnermode.go +++ b/runnermode.go @@ -28,7 +28,7 @@ func shRunner(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { return nil, err } - input, exitCode, cont, err := handleSh(cmd) + input, exitCode, cont, err := execSh(cmd) var luaErr rt.Value = rt.NilValue if err != nil { luaErr = rt.StringValue(err.Error()) From 2e192be2e1c11a95d5de968f860b05bbe4a4f3d8 Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Tue, 30 Aug 2022 23:08:22 -0400 Subject: [PATCH 11/15] refactor: setup autocd opt in a better way with the previous commit allowing users to override hilbish.runner.sh and it being ran by hilbish, the code for the autocd opt can just override that function and do the autocd functionality instead of reimplementing a hybrid runner. this means that if any other custom runner wants autocd functionality they can have it with the sh runner --- nature/opts/autocd.lua | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/nature/opts/autocd.lua b/nature/opts/autocd.lua index ac32f32..ce68230 100644 --- a/nature/opts/autocd.lua +++ b/nature/opts/autocd.lua @@ -1,13 +1,8 @@ local fs = require 'fs' -function cdHandle(inp) - local res = hilbish.runner.lua(inp) - - if not res.err then - return res - end - - res = hilbish.runner.sh(inp) +local oldShRunner = hilbish.runner.sh +function hilbish.runner.sh(input) + local res = oldShRunner(input) if res.exit ~= 0 and hilbish.opts.autocd then local ok, stat = pcall(fs.stat, res.input) @@ -21,5 +16,3 @@ function cdHandle(inp) return res end - -hilbish.runner.setMode(cdHandle) From c13889592f194ea98abc692043e04cf14fa843c8 Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Tue, 30 Aug 2022 23:10:47 -0400 Subject: [PATCH 12/15] fix: pass alias expanded string to sh runner (fixes #201) i have no idea why it didnt before, it *shouldnt* introduce any problems and fixes this one. --- exec.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exec.go b/exec.go index 26c0b73..6e24719 100644 --- a/exec.go +++ b/exec.go @@ -101,7 +101,7 @@ func runInput(input string, priv bool) { cmdFinish(0, input, priv) return } - input, exitCode, cont, err = handleSh(input) + input, exitCode, cont, err = handleSh(cmdString) case "hybridRev": _, _, _, err = handleSh(input) if err == nil { @@ -112,7 +112,7 @@ func runInput(input string, priv bool) { case "lua": input, exitCode, err = handleLua(cmdString) case "sh": - input, exitCode, cont, err = handleSh(input) + input, exitCode, cont, err = handleSh(cmdString) } } else { // can only be a string or function so From 1eed4cc7ee70deb3eda5026db43007d00fe61929 Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Tue, 30 Aug 2022 23:38:46 -0400 Subject: [PATCH 13/15] fix: add back empty string in command line split this fixes file completion in normal usage without using quotes. it basically cut out the space at the end which prevented normal usage without adding an additional space or using quotes for file completion --- complete.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/complete.go b/complete.go index 3a2899d..76d65f7 100644 --- a/complete.go +++ b/complete.go @@ -27,6 +27,9 @@ func splitQuote(str string) []string { sb.WriteRune(r) } } + if strings.HasSuffix(str, " ") { + split = append(split, "") + } if sb.Len() > 0 { split = append(split, sb.String()) From 59cec0ffa50bfcc8744c1cf42140b289d85df339 Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Fri, 2 Sep 2022 22:19:30 -0400 Subject: [PATCH 14/15] fix: call hinter for hint text handler --- rl.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rl.go b/rl.go index 59423ff..f6cb6cd 100644 --- a/rl.go +++ b/rl.go @@ -55,7 +55,7 @@ func newLineReader(prompt string, noHist bool) *lineReader { return []rune{} } - retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(highlighter), + retVal, err := rt.Call1(l.MainThread(), rt.FunctionValue(hinter), rt.StringValue(string(line)), rt.IntValue(int64(pos))) if err != nil { fmt.Println(err) From 959030f70d615dda47c9a75b0c6494ed4fad1890 Mon Sep 17 00:00:00 2001 From: TorchedSammy <38820196+TorchedSammy@users.noreply.github.com> Date: Fri, 2 Sep 2022 23:01:39 -0400 Subject: [PATCH 15/15] refactor: automatically load all nature commands --- nature/commands/init.lua | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/nature/commands/init.lua b/nature/commands/init.lua index 13b33be..93113cd 100644 --- a/nature/commands/init.lua +++ b/nature/commands/init.lua @@ -1,8 +1,19 @@ --- Add command builtins -require 'nature.commands.cd' -require 'nature.commands.cdr' -require 'nature.commands.doc' -require 'nature.commands.exit' -require 'nature.commands.disown' -require 'nature.commands.fg' -require 'nature.commands.bg' +local fs = require 'fs' + +-- explanation: this specific function gives to us info about +-- the currently running source. this includes a path to the +-- source file (info.source) +-- we will use that to automatically load all commands by reading +-- all the files in this dir and just requiring it. +local info = debug.getinfo(1) +local commandDir = fs.dir(info.source) +if commandDir == '.' then return end + +local commands = fs.readdir(commandDir) +for _, command in ipairs(commands) do + local name = command:gsub('%.lua', '') -- chop off extension + if name ~= 'init' then + -- skip this file (for obvious reasons) + require('nature.commands.' .. name) + end +end