1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
package shell
import (
"bytes"
"os/exec"
"strconv"
"github.com/micro-editor/terminal"
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/screen"
)
type TermType int
type CallbackFunc func(string)
const (
TTClose = iota // Should be closed
TTRunning // Currently running a command
TTDone // Finished running a command
)
var CloseTerms chan bool
func init() {
CloseTerms = make(chan bool)
}
// A Terminal holds information for the terminal emulator
type Terminal struct {
State terminal.State
Term *terminal.VT
title string
Status TermType
Selection [2]buffer.Loc
wait bool
getOutput bool
output *bytes.Buffer
callback CallbackFunc
}
// HasSelection returns whether this terminal has a valid selection
func (t *Terminal) HasSelection() bool {
return t.Selection[0] != t.Selection[1]
}
func (t *Terminal) Name() string {
return t.title
}
// 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 buffer.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, getOutput bool, wait bool, callback func(out string, userargs []any), userargs []any) error {
if len(execCmd) <= 0 {
return nil
}
cmd := exec.Command(execCmd[0], execCmd[1:]...)
t.output = nil
if getOutput {
t.output = bytes.NewBuffer([]byte{})
cmd.Stdout = t.output
}
Term, _, err := terminal.Start(&t.State, cmd)
if err != nil {
return err
}
t.Term = Term
t.getOutput = getOutput
t.Status = TTRunning
t.title = execCmd[0] + ":" + strconv.Itoa(cmd.Process.Pid)
t.wait = wait
t.callback = func(out string) {
callback(out, userargs)
}
go func() {
for {
err := Term.Parse()
if err != nil {
Term.Write([]byte("Press enter to close"))
screen.Redraw()
break
}
screen.Redraw()
}
t.Stop()
}()
return nil
}
// Stop stops execution of the terminal and sets the Status
// to TTDone
func (t *Terminal) Stop() {
t.Term.File().Close()
t.Term.Close()
if t.wait {
t.Status = TTDone
} else {
t.Close()
CloseTerms <- true
}
}
// Close sets the Status to TTClose indicating that the terminal
// is done and should be closed
func (t *Terminal) Close() {
t.Status = TTClose
// call the lua function that the user has given as a callback
if t.getOutput {
if t.callback != nil {
Jobs <- JobFunction{
Function: func(out string, args []any) {
t.callback(out)
},
Output: t.output.String(),
Args: nil,
}
}
}
}
// WriteString writes a given string to this terminal's pty
func (t *Terminal) WriteString(str string) {
t.Term.File().WriteString(str)
}
|