Change project layout and use go.mod

This commit is contained in:
Zachary Yedidia
2019-02-03 23:17:24 -05:00
parent c7f2c9c704
commit 0612af1590
103 changed files with 154 additions and 194 deletions

130
internal/shell/shell.go Normal file
View File

@@ -0,0 +1,130 @@
package shell
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
"os/signal"
"strings"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/micro/pkg/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
}
// RunCommand executes a shell command and returns the output/error
func RunCommand(input string) (string, error) {
args, err := shellwords.Split(input)
if err != nil {
return "", err
}
inputCmd := args[0]
return ExecCommand(inputCmd, args[1:]...)
}
// RunBackgroundShell runs a shell command in the background
// It returns a function which will run the command and returns a string
// message result
func RunBackgroundShell(input string) (func() string, error) {
args, err := shellwords.Split(input)
if err != nil {
return nil, err
}
inputCmd := args[0]
return func() string {
output, err := RunCommand(input)
totalLines := strings.Split(output, "\n")
str := output
if len(totalLines) < 3 {
if err == nil {
str = fmt.Sprint(inputCmd, " exited without error")
} else {
str = fmt.Sprint(inputCmd, " exited with error: ", err, ": ", output)
}
}
return str
}, nil
}
// RunInteractiveShell runs a shellcommand interactively
func RunInteractiveShell(input string, wait bool, getOutput bool) (string, error) {
args, err := shellwords.Split(input)
if err != nil {
return "", err
}
inputCmd := args[0]
// Shut down the screen because we're going to interact directly with the shell
screenb := screen.TempFini()
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 wait {
// This is just so we don't return right away and let the user press enter to return
screen.TermMessage("")
}
// Start the screen back up
screen.TempStart(screenb)
return output, err
}
// UserCommand runs the shell command
// The openTerm argument specifies whether a terminal should be opened (for viewing output
// or interacting with stdin)
// func UserCommand(input string, openTerm bool, waitToFinish bool) string {
// if !openTerm {
// RunBackgroundShell(input)
// return ""
// } else {
// output, _ := RunInteractiveShell(input, waitToFinish, false)
// return output
// }
// }

131
internal/shell/terminal.go Normal file
View File

@@ -0,0 +1,131 @@
package shell
import (
"bytes"
"fmt"
"os"
"os/exec"
"strconv"
"github.com/zyedidia/micro/internal/buffer"
"github.com/zyedidia/micro/internal/screen"
"github.com/zyedidia/terminal"
)
type TermType int
const (
TTClose = iota // Should be closed
TTRunning // Currently running a command
TTDone // Finished running a command
)
// 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 string
}
// 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) error {
if len(execCmd) <= 0 {
return nil
}
cmd := exec.Command(execCmd[0], execCmd[1:]...)
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.getOutput = getOutput
t.Status = TTRunning
t.title = execCmd[0] + ":" + strconv.Itoa(cmd.Process.Pid)
t.wait = wait
go func() {
for {
err := Term.Parse()
if err != nil {
fmt.Fprintln(os.Stderr, "[Press enter to close]")
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()
}
}
// 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 {
// TODO: plugin callback on Term emulator
// _, 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
func (t *Terminal) WriteString(str string) {
t.Term.File().WriteString(str)
}