diff --git a/justfile b/justfile index 86edbe1..e312eed 100644 --- a/justfile +++ b/justfile @@ -15,6 +15,7 @@ run: [linux, macos] reload: @go build -o iris main.go + @if [ -n "${IRIS_PID:-}" ]; then kill -USR1 $IRIS_PID; fi @if [ -z "${IRIS_FD:-}" ]; then ./iris; fi # update pkg diff --git a/root/root.go b/root/root.go index 585406f..b622126 100644 --- a/root/root.go +++ b/root/root.go @@ -14,13 +14,13 @@ import ( "github.com/creack/pty" "github.com/spf13/cobra" + "github.com/versenilvis/iris/commands/core" _ "github.com/versenilvis/iris/commands/dev" _ "github.com/versenilvis/iris/commands/fs" _ "github.com/versenilvis/iris/commands/info" _ "github.com/versenilvis/iris/commands/runner" _ "github.com/versenilvis/iris/commands/search" _ "github.com/versenilvis/iris/commands/view" - "github.com/versenilvis/iris/commands/core" "github.com/versenilvis/iris/integration" "golang.org/x/term" ) @@ -36,44 +36,17 @@ It works exactly like coding editor suggestion menu drop down.`, } func Execute() { - go watchSelf() + if os.Getenv("IRIS_RELOADED") == "true" { + fmt.Printf("\r\n\033[35m[IRIS] reloading...\033[0m\r\n") + os.Unsetenv("IRIS_RELOADED") + } + if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } -func watchSelf() { - exe, err := os.Executable() - if err != nil { - return - } - - startTime := time.Now() - - for { - time.Sleep(500 * time.Millisecond) - info, err := os.Stat(exe) - if err != nil { - continue - } - - if info.ModTime().After(startTime) { - // clean up terminal before exec - fmt.Printf("\r\n\033[35m[IRIS] binary updated, reloading...\033[0m\r\n") - // we need to be careful about raw mode but syscall.Exec will replace the process - // which is fine as the new iris will setup its own raw mode - // however, to be safe for the terminal state, we'd ideally restore then exec - // but since we're inside a PTY wrapper, it's more complex - // the simplest way: just exec, the new iris will handle the terminal - err := syscall.Exec(exe, os.Args, os.Environ()) - if err != nil { - fmt.Printf("Reload failed: %v\n", err) - } - } - } -} - func runWrapper() { r, w, err := os.Pipe() if err != nil { @@ -86,7 +59,7 @@ func runWrapper() { // fd 0, 1, 2 are stdin, stdout, stderr (handled by pty) // fd 3 is our write pipe c.ExtraFiles = []*os.File{w} - c.Env = append(os.Environ(), "IRIS_FD=3") + c.Env = append(os.Environ(), "IRIS_FD=3", fmt.Sprintf("IRIS_PID=%d", os.Getpid())) ptmx, err := pty.Start(c) if err != nil { @@ -97,15 +70,22 @@ func runWrapper() { core.ShellPID = c.Process.Pid - ch := make(chan os.Signal, 1) - signal.Notify(ch, syscall.SIGWINCH) + // signal handling for resize and reload + sigCh := make(chan os.Signal, 2) + signal.Notify(sigCh, syscall.SIGWINCH, syscall.SIGUSR1) go func() { - for range ch { - if err := pty.InheritSize(os.Stdin, ptmx); err != nil { + for s := range sigCh { + switch s { + case syscall.SIGWINCH: + _ = pty.InheritSize(os.Stdin, ptmx) + case syscall.SIGUSR1: + exe, _ := os.Executable() + os.Setenv("IRIS_RELOADED", "true") + _ = syscall.Exec(exe, os.Args, os.Environ()) } } }() - ch <- syscall.SIGWINCH + sigCh <- syscall.SIGWINCH oldState, err := term.MakeRaw(int(os.Stdin.Fd())) if err != nil { @@ -234,12 +214,12 @@ func runWrapper() { selected := overlay.Items[overlay.Cursor].Cmd TermWrite([]byte(overlay.ClearAndDisable())) - + // auto add space after tab for next suggestion in spec mode if mode == "spec" || b == 0x09 { selected += " " } - + // sync our naive buffer with the selected result naiveBuffer = selected mode = "spec" // reset to spec mode after selection @@ -247,7 +227,7 @@ func runWrapper() { // delete line (ctrl+u) ptmx.Write([]byte{0x15}) ptmx.Write([]byte(selected)) - + if b == '\r' { ptmx.Write([]byte{'\r'}) naiveBuffer = ""