diff --git a/cmd/micro/command.go b/cmd/micro/command.go index 39bc2f8f..028493c9 100644 --- a/cmd/micro/command.go +++ b/cmd/micro/command.go @@ -4,92 +4,70 @@ import ( "bytes" "os" "os/exec" + "os/signal" "regexp" "strings" - - "github.com/gdamore/tcell" ) -// HandleShellCommand runs the shell command and outputs to DisplayBlock -func HandleShellCommand(input string, view *View) { +// RunShellCommand executes a shell command and returns the output/error +func RunShellCommand(input string) (string, error) { inputCmd := strings.Split(input, " ")[0] args := strings.Split(input, " ")[1:] - // Execute Command cmd := exec.Command(inputCmd, args...) outputBytes := &bytes.Buffer{} cmd.Stdout = outputBytes // send output to buffer cmd.Start() - cmd.Wait() // wait for command to finish + err := cmd.Wait() // wait for command to finish outstring := outputBytes.String() - totalLines := strings.Split(outstring, "\n") - - if len(totalLines) < 3 { - messenger.Message(outstring) - return - } - - if outstring != "" { - // Display nonblank output - DisplayBlock(outstring) - } + return outstring, err } -// DisplayBlock displays txt -// It blocks the main loop -func DisplayBlock(text string) { - topline := 0 - _, height := screen.Size() - screen.HideCursor() - totalLines := strings.Split(text, "\n") - for { - screen.Clear() +// HandleShellCommand runs the shell command and outputs to DisplayBlock +func HandleShellCommand(input string, view *View, openTerm bool) { + inputCmd := strings.Split(input, " ")[0] + if !openTerm { + messenger.Message("Running...") + go func() { + output, err := RunShellCommand(input) + totalLines := strings.Split(output, "\n") - lineEnd := topline + height - if lineEnd > len(totalLines) { - lineEnd = len(totalLines) - } - lines := totalLines[topline:lineEnd] - for y, line := range lines { - for x, ch := range line { - st := defStyle - screen.SetContent(x, y, ch, nil, st) - } - } - - screen.Show() - - event := screen.PollEvent() - switch e := event.(type) { - case *tcell.EventResize: - _, height = e.Size() - case *tcell.EventKey: - switch e.Key() { - case tcell.KeyPgUp: - if topline > height { - topline = topline - height + if len(totalLines) < 3 { + if err == nil { + messenger.Message(inputCmd, " exited without error") } else { - topline = 0 + messenger.Message(inputCmd, " exited with error: ", err) } - case tcell.KeyPgDn: - if topline < len(totalLines)-height { - topline = topline + height - } - case tcell.KeyUp: - if topline > 0 { - topline-- - } - case tcell.KeyDown: - if topline < len(totalLines)-height { - topline++ - } - case tcell.KeyCtrlQ, tcell.KeyCtrlW, tcell.KeyEscape, tcell.KeyCtrlC: - return - default: - return + } else { + messenger.Message(output) } - } + Redraw(view) + }() + } else { + screen.Fini() + + args := strings.Split(input, " ")[1:] + + cmd := exec.Command(inputCmd, args...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + go func() { + for range c { + cmd.Process.Kill() + } + }() + + cmd.Start() + cmd.Wait() + + TermMessage("") + + InitScreen() } } @@ -98,7 +76,7 @@ func HandleCommand(input string, view *View) { inputCmd := strings.Split(input, " ")[0] args := strings.Split(input, " ")[1:] - commands := []string{"set", "quit", "save", "replace"} + commands := []string{"set", "quit", "save", "replace", "run"} i := 0 cmd := inputCmd @@ -116,6 +94,8 @@ func HandleCommand(input string, view *View) { switch inputCmd { case "set": SetOption(view, args) + case "run": + HandleShellCommand(strings.Join(args, " "), view, false) case "quit": if view.CanClose("Quit anyway? (yes, no, save) ") { screen.Fini() diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index ecc9d288..01885c38 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -109,6 +109,57 @@ func InitConfigDir() { } } +// InitScreen creates and initializes the tcell screen +func InitScreen() { + // Should we enable true color? + truecolor := os.Getenv("MICRO_TRUECOLOR") == "1" + + // In order to enable true color, we have to set the TERM to `xterm-truecolor` when + // initializing tcell, but after that, we can set the TERM back to whatever it was + oldTerm := os.Getenv("TERM") + if truecolor { + os.Setenv("TERM", "xterm-truecolor") + } + + // Initilize tcell + var err error + screen, err = tcell.NewScreen() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + if err = screen.Init(); err != nil { + fmt.Println(err) + os.Exit(1) + } + + // Now we can put the TERM back to what it was before + if truecolor { + os.Setenv("TERM", oldTerm) + } + + // Default style + defStyle = tcell.StyleDefault. + Foreground(tcell.ColorDefault). + Background(tcell.ColorDefault) + + // There may be another default style defined in the colorscheme + if style, ok := colorscheme["default"]; ok { + defStyle = style + } + + screen.SetStyle(defStyle) + screen.EnableMouse() +} + +// Redraw redraws the screen and the given view +func Redraw(view *View) { + screen.Clear() + view.Display() + messenger.Display() + screen.Show() +} + var flagVersion = flag.Bool("version", false, "Show version number") func main() { @@ -135,31 +186,7 @@ func main() { buf := NewBuffer(string(input), filename) - // Should we enable true color? - truecolor := os.Getenv("MICRO_TRUECOLOR") == "1" - - // In order to enable true color, we have to set the TERM to `xterm-truecolor` when - // initializing tcell, but after that, we can set the TERM back to whatever it was - oldTerm := os.Getenv("TERM") - if truecolor { - os.Setenv("TERM", "xterm-truecolor") - } - - // Initilize tcell - screen, err = tcell.NewScreen() - if err != nil { - fmt.Println(err) - os.Exit(1) - } - if err = screen.Init(); err != nil { - fmt.Println(err) - os.Exit(1) - } - - // Now we can put the TERM back to what it was before - if truecolor { - os.Setenv("TERM", oldTerm) - } + InitScreen() // This is just so if we have an error, we can exit cleanly and not completely // mess up the terminal being worked in @@ -173,30 +200,12 @@ func main() { } }() - // Default style - defStyle = tcell.StyleDefault. - Foreground(tcell.ColorDefault). - Background(tcell.ColorDefault) - - // There may be another default style defined in the colorscheme - if style, ok := colorscheme["default"]; ok { - defStyle = style - } - - screen.SetStyle(defStyle) - screen.EnableMouse() - messenger = new(Messenger) view := NewView(buf) for { // Display everything - screen.Clear() - - view.Display() - messenger.Display() - - screen.Show() + Redraw(view) // Wait for the user's action event := screen.PollEvent() @@ -227,7 +236,7 @@ func main() { case tcell.KeyCtrlB: input, canceled := messenger.Prompt("$ ") if !canceled { - HandleShellCommand(input, view) + HandleShellCommand(input, view, true) } case tcell.KeyCtrlG: if !helpOpen {