]> git.lizzy.rs Git - micro.git/blobdiff - cmd/micro/terminal.go
LoadAll should reload plugins too
[micro.git] / cmd / micro / terminal.go
index 6049cf0f9782cfb94487ffc5350fdd38a76e06b8..04ca1242576216dd5632bc8516542798f53720d4 100644 (file)
@@ -1,9 +1,14 @@
 package main
 
 import (
+       "bytes"
+       "fmt"
+       "os"
        "os/exec"
        "strconv"
+       "strings"
 
+       "github.com/zyedidia/clipboard"
        "github.com/zyedidia/tcell"
        "github.com/zyedidia/terminal"
 )
@@ -16,24 +21,64 @@ const (
 
 // A Terminal holds information for the terminal emulator
 type Terminal struct {
-       state  terminal.State
-       term   *terminal.VT
-       title  string
-       status int
+       state     terminal.State
+       view      *View
+       vtOld     ViewType
+       term      *terminal.VT
+       title     string
+       status    int
+       selection [2]Loc
+       wait      bool
+       getOutput bool
+       output    *bytes.Buffer
+       callback  string
+}
+
+// HasSelection returns whether this terminal has a valid selection
+func (t *Terminal) HasSelection() bool {
+       return t.selection[0] != t.selection[1]
+}
+
+// GetSelection returns the selected text
+func (t *Terminal) GetSelection(width int) string {
+       start := t.selection[0]
+       end := t.selection[1]
+       if start.GreaterThan(end) {
+               start, end = end, start
+       }
+       var ret string
+       var l Loc
+       for y := start.Y; y <= end.Y; y++ {
+               for x := 0; x < width; x++ {
+                       l.X, l.Y = x, y
+                       if l.GreaterEqual(start) && l.LessThan(end) {
+                               c, _, _ := t.state.Cell(x, y)
+                               ret += string(c)
+                       }
+               }
+       }
+       return ret
 }
 
 // Start begins a new command in this terminal with a given view
-func (t *Terminal) Start(execCmd []string, view *View) error {
+func (t *Terminal) Start(execCmd []string, view *View, getOutput bool) error {
        if len(execCmd) <= 0 {
                return nil
        }
 
        cmd := exec.Command(execCmd[0], execCmd[1:]...)
-       term, _, err := terminal.Start(&t.state, cmd)
+       t.output = nil
+       if getOutput {
+               t.output = bytes.NewBuffer([]byte{})
+       }
+       term, _, err := terminal.Start(&t.state, cmd, t.output)
        if err != nil {
                return err
        }
        t.term = term
+       t.view = view
+       t.getOutput = getOutput
+       t.vtOld = view.Type
        t.status = VTRunning
        t.title = execCmd[0] + ":" + strconv.Itoa(cmd.Process.Pid)
 
@@ -41,6 +86,7 @@ func (t *Terminal) Start(execCmd []string, view *View) error {
                for {
                        err := term.Parse()
                        if err != nil {
+                               fmt.Fprintln(os.Stderr, "[Press enter to close]")
                                break
                        }
                        updateterm <- true
@@ -56,18 +102,80 @@ func (t *Terminal) Resize(width, height int) {
        t.term.Resize(width, height)
 }
 
+// HandleEvent handles a tcell event by forwarding it to the terminal emulator
+// If the event is a mouse event and the program running in the emulator
+// does not have mouse support, the emulator will support selections and
+// copy-paste
+func (t *Terminal) HandleEvent(event tcell.Event) {
+       if e, ok := event.(*tcell.EventKey); ok {
+               if t.status == VTDone {
+                       switch e.Key() {
+                       case tcell.KeyEscape, tcell.KeyCtrlQ, tcell.KeyEnter:
+                               t.Close()
+                               t.view.Type = vtDefault
+                       default:
+                       }
+               }
+               if e.Key() == tcell.KeyCtrlC && t.HasSelection() {
+                       clipboard.WriteAll(t.GetSelection(t.view.Width), "clipboard")
+                       messenger.Message("Copied selection to clipboard")
+               } else if t.status != VTDone {
+                       t.WriteString(event.EscSeq())
+               }
+       } else if e, ok := event.(*tcell.EventMouse); !ok || t.state.Mode(terminal.ModeMouseMask) {
+               t.WriteString(event.EscSeq())
+       } else {
+               x, y := e.Position()
+               x -= t.view.x
+               y += t.view.y
+
+               if e.Buttons() == tcell.Button1 {
+                       if !t.view.mouseReleased {
+                               // drag
+                               t.selection[1].X = x
+                               t.selection[1].Y = y
+                       } else {
+                               t.selection[0].X = x
+                               t.selection[0].Y = y
+                               t.selection[1].X = x
+                               t.selection[1].Y = y
+                       }
+
+                       t.view.mouseReleased = false
+               } else if e.Buttons() == tcell.ButtonNone {
+                       if !t.view.mouseReleased {
+                               t.selection[1].X = x
+                               t.selection[1].Y = y
+                       }
+                       t.view.mouseReleased = true
+               }
+       }
+}
+
 // Stop stops execution of the terminal and sets the status
 // to VTDone
 func (t *Terminal) Stop() {
        t.term.File().Close()
        t.term.Close()
-       t.status = VTDone
+       if t.wait {
+               t.status = VTDone
+       } else {
+               t.Close()
+               t.view.Type = t.vtOld
+       }
 }
 
 // Close sets the status to VTIdle indicating that the terminal
 // 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
+       if t.getOutput {
+               _, err := Call(t.callback, t.output.String())
+               if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
+                       TermMessage(err)
+               }
+       }
 }
 
 // WriteString writes a given string to this terminal's pty
@@ -76,24 +184,25 @@ func (t *Terminal) WriteString(str string) {
 }
 
 // Display displays this terminal in a view
-func (t *Terminal) Display(v *View) {
+func (t *Terminal) Display() {
        divider := 0
-       if v.x != 0 {
+       if t.view.x != 0 {
                divider = 1
                dividerStyle := defStyle
                if style, ok := colorscheme["divider"]; ok {
                        dividerStyle = style
                }
-               for i := 0; i < v.Height; i++ {
-                       screen.SetContent(v.x, v.y+i, '|', nil, dividerStyle.Reverse(true))
+               for i := 0; i < t.view.Height; i++ {
+                       screen.SetContent(t.view.x, t.view.y+i, '|', nil, dividerStyle.Reverse(true))
                }
        }
        t.state.Lock()
        defer t.state.Unlock()
 
-       for y := 0; y < v.Height; y++ {
-               for x := 0; x < v.Width; x++ {
-
+       var l Loc
+       for y := 0; y < t.view.Height; y++ {
+               for x := 0; x < t.view.Width; x++ {
+                       l.X, l.Y = x, y
                        c, f, b := t.state.Cell(x, y)
 
                        fg, bg := int(f), int(b)
@@ -105,11 +214,15 @@ func (t *Terminal) Display(v *View) {
                        }
                        st := tcell.StyleDefault.Foreground(GetColor256(int(fg))).Background(GetColor256(int(bg)))
 
-                       screen.SetContent(v.x+x+divider, v.y+y, c, nil, st)
+                       if l.LessThan(t.selection[1]) && l.GreaterEqual(t.selection[0]) || l.LessThan(t.selection[0]) && l.GreaterEqual(t.selection[1]) {
+                               st = st.Reverse(true)
+                       }
+
+                       screen.SetContent(t.view.x+x+divider, t.view.y+y, c, nil, st)
                }
        }
-       if t.state.CursorVisible() && tabs[curTab].CurView == v.Num {
+       if t.state.CursorVisible() && tabs[curTab].CurView == t.view.Num {
                curx, cury := t.state.Cursor()
-               screen.ShowCursor(curx+v.x+divider, cury+v.y)
+               screen.ShowCursor(curx+t.view.x+divider, cury+t.view.y)
        }
 }