github.com/andrewrech/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  }