]> git.lizzy.rs Git - micro.git/commitdiff
Change HandleShellCommand backend
authorZachary Yedidia <zyedidia@gmail.com>
Sun, 21 Jan 2018 03:23:52 +0000 (22:23 -0500)
committerZachary Yedidia <zyedidia@gmail.com>
Sun, 21 Jan 2018 03:23:52 +0000 (22:23 -0500)
I'm trying to add more options for plugins that want to run shell
commands. Also trying to add support for running shell commands in the
terminal emulator from a plugin and return the output.

More to come soon.

Ref #979

cmd/micro/command.go
cmd/micro/shell.go [new file with mode: 0644]
cmd/micro/shell_posix.go [new file with mode: 0644]
cmd/micro/shell_windows.go [new file with mode: 0644]
cmd/micro/terminal.go
cmd/micro/view.go

index 2607cdb8874b655849420ccd2cf3a27474ea26c2..ade999cdddba7f27c46f6a0c43bf8374cf3c60af 100644 (file)
@@ -1,11 +1,8 @@
 package main
 
 import (
-       "bytes"
        "fmt"
        "os"
-       "os/exec"
-       "os/signal"
        "path/filepath"
        "regexp"
        "runtime"
@@ -714,105 +711,15 @@ func ReplaceAll(args []string) {
 func Term(args []string) {
        var err error
        if len(args) == 0 {
-               err = CurView().StartTerminal([]string{os.Getenv("SHELL"), "-i"})
+               err = CurView().StartTerminal([]string{os.Getenv("SHELL"), "-i"}, true, false, "")
        } else {
-               err = CurView().StartTerminal(args)
+               err = CurView().StartTerminal(args, true, false, "")
        }
        if err != nil {
                messenger.Error(err)
        }
 }
 
-// RunShellCommand executes a shell command and returns the output/error
-func RunShellCommand(input string) (string, error) {
-       args, err := shellwords.Split(input)
-       if err != nil {
-               return "", err
-       }
-       inputCmd := args[0]
-
-       cmd := exec.Command(inputCmd, args[1:]...)
-       outputBytes := &bytes.Buffer{}
-       cmd.Stdout = outputBytes
-       cmd.Stderr = outputBytes
-       cmd.Start()
-       err = cmd.Wait() // wait for command to finish
-       outstring := outputBytes.String()
-       return outstring, err
-}
-
-// HandleShellCommand runs the shell command
-// The openTerm argument specifies whether a terminal should be opened (for viewing output
-// or interacting with stdin)
-func HandleShellCommand(input string, openTerm bool, waitToFinish bool) string {
-       args, err := shellwords.Split(input)
-       if err != nil {
-               return ""
-       }
-       inputCmd := args[0]
-       if !openTerm {
-               // Simply run the command in the background and notify the user when it's done
-               messenger.Message("Running...")
-               go func() {
-                       output, err := RunShellCommand(input)
-                       totalLines := strings.Split(output, "\n")
-
-                       if len(totalLines) < 3 {
-                               if err == nil {
-                                       messenger.Message(inputCmd, " exited without error")
-                               } else {
-                                       messenger.Message(inputCmd, " exited with error: ", err, ": ", output)
-                               }
-                       } else {
-                               messenger.Message(output)
-                       }
-                       // We have to make sure to redraw
-                       RedrawAll()
-               }()
-       } else {
-               // Shut down the screen because we're going to interact directly with the shell
-               screen.Fini()
-               screen = nil
-
-               args := args[1:]
-
-               // Set up everything for the command
-               var output string
-               cmd := exec.Command(inputCmd, args...)
-               cmd.Stdin = os.Stdin
-               cmd.Stdout = os.Stdout
-               cmd.Stderr = os.Stderr
-
-               // This is a trap for Ctrl-C so that it doesn't kill micro
-               // Instead we trap Ctrl-C to kill the program we're running
-               c := make(chan os.Signal, 1)
-               signal.Notify(c, os.Interrupt)
-               go func() {
-                       for range c {
-                               cmd.Process.Kill()
-                       }
-               }()
-
-               cmd.Start()
-               err := cmd.Wait()
-
-               if err != nil {
-                       output = err.Error()
-               }
-
-               if waitToFinish {
-                       // This is just so we don't return right away and let the user press enter to return
-                       TermMessage("")
-               }
-
-               // Start the screen back up
-               InitScreen()
-
-               return output
-       }
-       return ""
-}
-
 // HandleCommand handles input from the user
 func HandleCommand(input string) {
        args, err := shellwords.Split(input)
diff --git a/cmd/micro/shell.go b/cmd/micro/shell.go
new file mode 100644 (file)
index 0000000..94ed956
--- /dev/null
@@ -0,0 +1,131 @@
+package main
+
+import (
+       "bytes"
+       "io"
+       "os"
+       "os/exec"
+       "os/signal"
+       "strings"
+
+       "github.com/zyedidia/micro/cmd/micro/shellwords"
+)
+
+// ExecCommand executes a command using exec
+// It returns any output/errors
+func ExecCommand(name string, arg ...string) (string, error) {
+       var err error
+       cmd := exec.Command(name, arg...)
+       outputBytes := &bytes.Buffer{}
+       cmd.Stdout = outputBytes
+       cmd.Stderr = outputBytes
+       err = cmd.Start()
+       if err != nil {
+               return "", err
+       }
+       err = cmd.Wait() // wait for command to finish
+       outstring := outputBytes.String()
+       return outstring, err
+}
+
+// RunShellCommand executes a shell command and returns the output/error
+func RunShellCommand(input string) (string, error) {
+       args, err := shellwords.Split(input)
+       if err != nil {
+               return "", err
+       }
+       inputCmd := args[0]
+
+       return ExecCommand(inputCmd, args[1:]...)
+}
+
+func RunBackgroundShell(input string) {
+       args, err := shellwords.Split(input)
+       if err != nil {
+               messenger.Error(err)
+               return
+       }
+       inputCmd := args[0]
+       messenger.Message("Running...")
+       go func() {
+               output, err := RunShellCommand(input)
+               totalLines := strings.Split(output, "\n")
+
+               if len(totalLines) < 3 {
+                       if err == nil {
+                               messenger.Message(inputCmd, " exited without error")
+                       } else {
+                               messenger.Message(inputCmd, " exited with error: ", err, ": ", output)
+                       }
+               } else {
+                       messenger.Message(output)
+               }
+               // We have to make sure to redraw
+               RedrawAll()
+       }()
+}
+
+func RunInteractiveShell(input string, wait bool, getOutput bool) string {
+       args, err := shellwords.Split(input)
+       if err != nil {
+               return ""
+       }
+       inputCmd := args[0]
+
+       // Shut down the screen because we're going to interact directly with the shell
+       screen.Fini()
+       screen = nil
+
+       args = args[1:]
+
+       // Set up everything for the command
+       outputBytes := &bytes.Buffer{}
+       cmd := exec.Command(inputCmd, args...)
+       cmd.Stdin = os.Stdin
+       if getOutput {
+               cmd.Stdout = io.MultiWriter(os.Stdout, outputBytes)
+       } else {
+               cmd.Stdout = os.Stdout
+       }
+       cmd.Stderr = os.Stderr
+
+       // This is a trap for Ctrl-C so that it doesn't kill micro
+       // Instead we trap Ctrl-C to kill the program we're running
+       c := make(chan os.Signal, 1)
+       signal.Notify(c, os.Interrupt)
+       go func() {
+               for range c {
+                       cmd.Process.Kill()
+               }
+       }()
+
+       cmd.Start()
+       err = cmd.Wait()
+
+       output := outputBytes.String()
+       if err != nil {
+               output = err.Error()
+       }
+
+       if wait {
+               // This is just so we don't return right away and let the user press enter to return
+               TermMessage("")
+       }
+
+       // Start the screen back up
+       InitScreen()
+
+       return output
+}
+
+// HandleShellCommand runs the shell command
+// The openTerm argument specifies whether a terminal should be opened (for viewing output
+// or interacting with stdin)
+func HandleShellCommand(input string, openTerm bool, waitToFinish bool) string {
+       if !openTerm {
+               RunBackgroundShell(input)
+               return ""
+       } else {
+               return RunInteractiveShell(input, waitToFinish, false)
+       }
+}
diff --git a/cmd/micro/shell_posix.go b/cmd/micro/shell_posix.go
new file mode 100644 (file)
index 0000000..6c19bf4
--- /dev/null
@@ -0,0 +1,18 @@
+// +build linux darwin dragonfly solaris openbsd netbsd freebsd
+
+package main
+
+import (
+       "github.com/zyedidia/micro/cmd/micro/shellwords"
+)
+
+const TermEmuSupported = true
+
+func RunTermEmulator(input string, wait bool, getOutput bool) error {
+       args, err := shellwords.Split(input)
+       if err != nil {
+               return err
+       }
+       err = CurView().StartTerminal(args, wait, false, "")
+       return err
+}
diff --git a/cmd/micro/shell_windows.go b/cmd/micro/shell_windows.go
new file mode 100644 (file)
index 0000000..4ad4c41
--- /dev/null
@@ -0,0 +1,9 @@
+// +build plan9 nacl windows
+
+package main
+
+const TermEmuSupported = false
+
+func RunTermEmulator(input string, wait bool, getOutput bool) string {
+       return "Unsupported"
+}
index 06a24ce35850809cf743f87900e13bf60dd6b765..1defa61ff3a37126cdddf874e6addb185d8c960d 100644 (file)
@@ -5,6 +5,7 @@ import (
        "os"
        "os/exec"
        "strconv"
+       "strings"
 
        "github.com/zyedidia/clipboard"
        "github.com/zyedidia/tcell"
@@ -27,6 +28,8 @@ type Terminal struct {
        status    int
        selection [2]Loc
        wait      bool
+       getOutput bool
+       callback  string
 }
 
 // HasSelection returns whether this terminal has a valid selection
@@ -159,6 +162,11 @@ func (t *Terminal) Stop() {
 // is ready for a new command to execute
 func (t *Terminal) Close() {
        t.status = VTIdle
+       // call the lua function that the user has given as a callback
+       _, err := Call(t.callback)
+       if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
+               TermMessage(err)
+       }
 }
 
 // WriteString writes a given string to this terminal's pty
index fec3201344fd47ab46b6627b86c7a42e7e5ee9ba..7969c27441cd57366a4996482bad1988ee373934 100644 (file)
@@ -165,8 +165,11 @@ func (v *View) ToggleStatusLine() {
 }
 
 // StartTerminal execs a command in this view
-func (v *View) StartTerminal(execCmd []string) error {
+func (v *View) StartTerminal(execCmd []string, wait bool, getOutput bool, luaCallback string) error {
        err := v.term.Start(execCmd, v)
+       v.term.wait = wait
+       v.term.getOutput = getOutput
+       v.term.callback = luaCallback
        if err == nil {
                v.term.Resize(v.Width, v.Height)
                v.Type = vtTerm