X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=cmd%2Fmicro%2Fterminal.go;h=04ca1242576216dd5632bc8516542798f53720d4;hb=6ef273accd61515ae9bbfab1acfb78e6034bb6e5;hp=6049cf0f9782cfb94487ffc5350fdd38a76e06b8;hpb=dd47f167f1901a62f15faa1b85d7d589eb58828c;p=micro.git diff --git a/cmd/micro/terminal.go b/cmd/micro/terminal.go index 6049cf0f..04ca1242 100644 --- a/cmd/micro/terminal.go +++ b/cmd/micro/terminal.go @@ -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) } }