mirror of
https://github.com/zyedidia/micro.git
synced 2026-02-23 07:20:20 +09:00
Merge
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user