github.com/fluffynuts/lazygit@v0.8.1/pkg/commands/exec_live_default.go (about) 1 // +build !windows 2 3 package commands 4 5 import ( 6 "bufio" 7 "bytes" 8 "strings" 9 "unicode/utf8" 10 11 "github.com/go-errors/errors" 12 13 "github.com/jesseduffield/pty" 14 ) 15 16 // RunCommandWithOutputLiveWrapper runs a command and return every word that gets written in stdout 17 // Output is a function that executes by every word that gets read by bufio 18 // As return of output you need to give a string that will be written to stdin 19 // NOTE: If the return data is empty it won't written anything to stdin 20 func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { 21 cmd := c.ExecutableFromString(command) 22 cmd.Env = append(cmd.Env, "LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8") 23 24 var stderr bytes.Buffer 25 cmd.Stderr = &stderr 26 27 ptmx, err := pty.Start(cmd) 28 29 if err != nil { 30 return err 31 } 32 33 go func() { 34 scanner := bufio.NewScanner(ptmx) 35 scanner.Split(scanWordsWithNewLines) 36 for scanner.Scan() { 37 toOutput := strings.Trim(scanner.Text(), " ") 38 _, _ = ptmx.WriteString(output(toOutput)) 39 } 40 }() 41 42 err = cmd.Wait() 43 ptmx.Close() 44 if err != nil { 45 return errors.New(stderr.String()) 46 } 47 48 return nil 49 } 50 51 // scanWordsWithNewLines is a copy of bufio.ScanWords but this also captures new lines 52 // For specific comments about this function take a look at: bufio.ScanWords 53 func scanWordsWithNewLines(data []byte, atEOF bool) (advance int, token []byte, err error) { 54 start := 0 55 for width := 0; start < len(data); start += width { 56 var r rune 57 r, width = utf8.DecodeRune(data[start:]) 58 if !isSpace(r) { 59 break 60 } 61 } 62 for width, i := 0, start; i < len(data); i += width { 63 var r rune 64 r, width = utf8.DecodeRune(data[i:]) 65 if isSpace(r) { 66 return i + width, data[start:i], nil 67 } 68 } 69 if atEOF && len(data) > start { 70 return len(data), data[start:], nil 71 } 72 return start, nil, nil 73 } 74 75 // isSpace is also copied from the bufio package and has been modified to also captures new lines 76 // For specific comments about this function take a look at: bufio.isSpace 77 func isSpace(r rune) bool { 78 if r <= '\u00FF' { 79 switch r { 80 case ' ', '\t', '\v', '\f': 81 return true 82 case '\u0085', '\u00A0': 83 return true 84 } 85 return false 86 } 87 if '\u2000' <= r && r <= '\u200a' { 88 return true 89 } 90 switch r { 91 case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000': 92 return true 93 } 94 return false 95 }