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 323f50bc..868f4589 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 974a34d1..2024bb04 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 dc53ca99..76eab23a 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 d60cd77c..8467b87d 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 522c8d52..48d54f25 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 26798353..48a20391 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 6a6e7bce..0c53ed19 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 42add2f8..cdcda8fb 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 42b56cfd..ec985ff8 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 403941fa..d1bd7b68 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 48d54f25..49904ade 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 dacae022..d060597e 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 e1862ed1..d605a4ec 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 3112903b..89e0c4ab 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 d279e0cc..24f1c036 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 7dd07d32..709cc1f7 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 92ece054..419970ce 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 e53055d7..ee0f584b 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 6350aa29..59423ff1 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 bd5984cd..2e6c8852 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 9f67537b..42a9fff1 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 9f1e54bb..fdc712ff 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 01ca774f..a5ecebd8 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 b22f5a22..5f30a6c1 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 d605a4ec..dcf88457 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 79f7c711..0f6ca5aa 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 dcf88457..26c0b73e 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 eca33ba8..b8995cdc 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 ac32f321..ce682303 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 26c0b73e..6e247198 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 3a2899d8..76d65f77 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 59423ff1..f6cb6cde 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 13b33bef..93113cd7 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