This commit is contained in:
Zachary Yedidia
2016-04-20 12:50:36 -04:00
3 changed files with 131 additions and 118 deletions

View File

@@ -4,92 +4,70 @@ import (
"bytes"
"os"
"os/exec"
"os/signal"
"regexp"
"strings"
"github.com/gdamore/tcell"
)
// HandleShellCommand runs the shell command and outputs to DisplayBlock
func HandleShellCommand(input string, view *View) {
// RunShellCommand executes a shell command and returns the output/error
func RunShellCommand(input string) (string, error) {
inputCmd := strings.Split(input, " ")[0]
args := strings.Split(input, " ")[1:]
// Execute Command
cmd := exec.Command(inputCmd, args...)
outputBytes := &bytes.Buffer{}
cmd.Stdout = outputBytes // send output to buffer
cmd.Stdout = outputBytes
cmd.Stderr = outputBytes
cmd.Start()
cmd.Wait() // wait for command to finish
err := cmd.Wait() // wait for command to finish
outstring := outputBytes.String()
totalLines := strings.Split(outstring, "\n")
if len(totalLines) < 3 {
messenger.Message(outstring)
return
}
if outstring != "" {
// Display nonblank output
DisplayBlock(outstring)
}
return outstring, err
}
// DisplayBlock displays txt
// It blocks the main loop
func DisplayBlock(text string) {
topline := 0
_, height := screen.Size()
screen.HideCursor()
totalLines := strings.Split(text, "\n")
for {
screen.Clear()
// HandleShellCommand runs the shell command and outputs to DisplayBlock
func HandleShellCommand(input string, view *View, openTerm bool) {
inputCmd := strings.Split(input, " ")[0]
if !openTerm {
messenger.Message("Running...")
go func() {
output, err := RunShellCommand(input)
totalLines := strings.Split(output, "\n")
lineEnd := topline + height
if lineEnd > len(totalLines) {
lineEnd = len(totalLines)
}
lines := totalLines[topline:lineEnd]
for y, line := range lines {
for x, ch := range line {
st := defStyle
screen.SetContent(x, y, ch, nil, st)
}
}
screen.Show()
event := screen.PollEvent()
switch e := event.(type) {
case *tcell.EventResize:
_, height = e.Size()
case *tcell.EventKey:
switch e.Key() {
case tcell.KeyPgUp:
if topline > height {
topline = topline - height
if len(totalLines) < 3 {
if err == nil {
messenger.Message(inputCmd, " exited without error")
} else {
topline = 0
messenger.Message(inputCmd, " exited with error: ", err, ": ", output)
}
case tcell.KeyPgDn:
if topline < len(totalLines)-height {
topline = topline + height
}
case tcell.KeyUp:
if topline > 0 {
topline--
}
case tcell.KeyDown:
if topline < len(totalLines)-height {
topline++
}
case tcell.KeyCtrlQ, tcell.KeyCtrlW, tcell.KeyEscape, tcell.KeyCtrlC:
return
default:
return
} else {
messenger.Message(output)
}
}
Redraw(view)
}()
} else {
screen.Fini()
args := strings.Split(input, " ")[1:]
cmd := exec.Command(inputCmd, args...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for range c {
cmd.Process.Kill()
}
}()
cmd.Start()
cmd.Wait()
TermMessage("")
InitScreen()
}
}
@@ -98,7 +76,7 @@ func HandleCommand(input string, view *View) {
inputCmd := strings.Split(input, " ")[0]
args := strings.Split(input, " ")[1:]
commands := []string{"set", "quit", "save", "replace"}
commands := []string{"set", "quit", "save", "replace", "run"}
i := 0
cmd := inputCmd
@@ -116,6 +94,8 @@ func HandleCommand(input string, view *View) {
switch inputCmd {
case "set":
SetOption(view, args)
case "run":
HandleShellCommand(strings.Join(args, " "), view, false)
case "quit":
if view.CanClose("Quit anyway? (yes, no, save) ") {
screen.Fini()

View File

@@ -109,6 +109,57 @@ func InitConfigDir() {
}
}
// InitScreen creates and initializes the tcell screen
func InitScreen() {
// Should we enable true color?
truecolor := os.Getenv("MICRO_TRUECOLOR") == "1"
// In order to enable true color, we have to set the TERM to `xterm-truecolor` when
// initializing tcell, but after that, we can set the TERM back to whatever it was
oldTerm := os.Getenv("TERM")
if truecolor {
os.Setenv("TERM", "xterm-truecolor")
}
// Initilize tcell
var err error
screen, err = tcell.NewScreen()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if err = screen.Init(); err != nil {
fmt.Println(err)
os.Exit(1)
}
// Now we can put the TERM back to what it was before
if truecolor {
os.Setenv("TERM", oldTerm)
}
// Default style
defStyle = tcell.StyleDefault.
Foreground(tcell.ColorDefault).
Background(tcell.ColorDefault)
// There may be another default style defined in the colorscheme
if style, ok := colorscheme["default"]; ok {
defStyle = style
}
screen.SetStyle(defStyle)
screen.EnableMouse()
}
// Redraw redraws the screen and the given view
func Redraw(view *View) {
screen.Clear()
view.Display()
messenger.Display()
screen.Show()
}
var flagVersion = flag.Bool("version", false, "Show version number")
func main() {
@@ -125,6 +176,7 @@ func main() {
}
encoding.Register()
tcell.SetEncodingFallback(tcell.EncodingFallbackASCII)
// Find the user's configuration directory (probably $XDG_CONFIG_HOME/micro)
InitConfigDir()
@@ -135,31 +187,7 @@ func main() {
buf := NewBuffer(string(input), filename)
// Should we enable true color?
truecolor := os.Getenv("MICRO_TRUECOLOR") == "1"
// In order to enable true color, we have to set the TERM to `xterm-truecolor` when
// initializing tcell, but after that, we can set the TERM back to whatever it was
oldTerm := os.Getenv("TERM")
if truecolor {
os.Setenv("TERM", "xterm-truecolor")
}
// Initilize tcell
screen, err = tcell.NewScreen()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if err = screen.Init(); err != nil {
fmt.Println(err)
os.Exit(1)
}
// Now we can put the TERM back to what it was before
if truecolor {
os.Setenv("TERM", oldTerm)
}
InitScreen()
// This is just so if we have an error, we can exit cleanly and not completely
// mess up the terminal being worked in
@@ -173,30 +201,12 @@ func main() {
}
}()
// Default style
defStyle = tcell.StyleDefault.
Foreground(tcell.ColorDefault).
Background(tcell.ColorDefault)
// There may be another default style defined in the colorscheme
if style, ok := colorscheme["default"]; ok {
defStyle = style
}
screen.SetStyle(defStyle)
screen.EnableMouse()
messenger = new(Messenger)
view := NewView(buf)
for {
// Display everything
screen.Clear()
view.Display()
messenger.Display()
screen.Show()
Redraw(view)
// Wait for the user's action
event := screen.PollEvent()
@@ -211,7 +221,7 @@ func main() {
case tcell.KeyCtrlQ:
// Make sure not to quit if there are unsaved changes
if helpOpen {
view.buf = buf
view.OpenBuffer(buf)
helpOpen = false
} else {
if view.CanClose("Quit anyway? (yes, no, save) ") {
@@ -227,16 +237,16 @@ func main() {
case tcell.KeyCtrlB:
input, canceled := messenger.Prompt("$ ")
if !canceled {
HandleShellCommand(input, view)
HandleShellCommand(input, view, true)
}
case tcell.KeyCtrlG:
if !helpOpen {
helpBuffer := NewBuffer(helpTxt, "")
helpBuffer.name = "Help"
helpOpen = true
view.buf = helpBuffer
view.OpenBuffer(helpBuffer)
} else {
view.buf = buf
view.OpenBuffer(buf)
helpOpen = false
}
}

View File

@@ -276,6 +276,29 @@ func (v *View) SelectAll() {
v.cursor.y = 0
}
// OpenBuffer opens a new buffer in this view.
// This resets the topline, event handler and cursor.
func (v *View) OpenBuffer(buf *Buffer) {
v.buf = buf
v.topline = 0
// Put the cursor at the first spot
v.cursor = Cursor{
x: 0,
y: 0,
v: v,
}
v.cursor.ResetSelection()
v.eh = NewEventHandler(v)
v.matches = Match(v)
// Set mouseReleased to true because we assume the mouse is not being pressed when
// the editor is opened
v.mouseReleased = true
v.lastClickTime = time.Time{}
}
// OpenFile opens a new file in the current view
// It makes sure that the current buffer can be closed first (unsaved changes)
func (v *View) OpenFile() {