github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/utils/readline/cursor.go (about)

     1  package readline
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"regexp"
     7  	"strconv"
     8  )
     9  
    10  func leftMost() []byte {
    11  	fd := int(os.Stdout.Fd())
    12  	w, _, err := GetSize(fd)
    13  	if err != nil {
    14  		return []byte{'\r', '\n'}
    15  	}
    16  
    17  	b := make([]byte, w+1)
    18  	for i := 0; i < w; i++ {
    19  		b[i] = ' '
    20  	}
    21  	b[w] = '\r'
    22  
    23  	return b
    24  }
    25  
    26  var rxRcvCursorPos = regexp.MustCompile("^\x1b([0-9]+);([0-9]+)R$")
    27  
    28  func (rl *Instance) getCursorPos() (x int, y int) {
    29  	if !ForceCrLf {
    30  		return 0, 0
    31  	}
    32  
    33  	if !rl.EnableGetCursorPos {
    34  		return -1, -1
    35  	}
    36  
    37  	disable := func() (int, int) {
    38  		printErr("\r\ngetCursorPos() not supported by terminal emulator, disabling....\r\n")
    39  		rl.EnableGetCursorPos = false
    40  		return -1, -1
    41  	}
    42  
    43  	print(seqGetCursorPos)
    44  	b := make([]byte, 64)
    45  	i, err := read(b)
    46  	if err != nil {
    47  		return disable()
    48  	}
    49  
    50  	if !rxRcvCursorPos.Match(b[:i]) {
    51  		return disable()
    52  	}
    53  
    54  	match := rxRcvCursorPos.FindAllStringSubmatch(string(b[:i]), 1)
    55  	y, err = strconv.Atoi(match[0][1])
    56  	if err != nil {
    57  		return disable()
    58  	}
    59  
    60  	x, err = strconv.Atoi(match[0][2])
    61  	if err != nil {
    62  		return disable()
    63  	}
    64  
    65  	return x, y
    66  }
    67  
    68  const (
    69  	cursorUpf   = "\x1b[%dA"
    70  	cursorDownf = "\x1b[%dB"
    71  	cursorForwf = "\x1b[%dC"
    72  	cursorBackf = "\x1b[%dD"
    73  )
    74  
    75  func moveCursorUpStr(i int) string {
    76  	if i < 1 {
    77  		return ""
    78  	}
    79  
    80  	return fmt.Sprintf(cursorUpf, i)
    81  }
    82  
    83  func moveCursorDownStr(i int) string {
    84  	if i < 1 {
    85  		return ""
    86  	}
    87  
    88  	return fmt.Sprintf(cursorDownf, i)
    89  }
    90  
    91  func moveCursorForwardsStr(i int) string {
    92  	if i < 1 {
    93  		return ""
    94  	}
    95  
    96  	return fmt.Sprintf(cursorForwf, i)
    97  }
    98  
    99  func moveCursorBackwardsStr(i int) string {
   100  	if i < 1 {
   101  		return ""
   102  	}
   103  
   104  	return fmt.Sprintf(cursorBackf, i)
   105  }
   106  
   107  func (rl *Instance) moveCursorToStartStr() string {
   108  	posX, posY := rl.lineWrapCellPos()
   109  	return moveCursorBackwardsStr(posX-rl.promptLen) + moveCursorUpStr(posY)
   110  }
   111  
   112  func (rl *Instance) moveCursorFromStartToLinePosStr() string {
   113  	posX, posY := rl.lineWrapCellPos()
   114  	output := moveCursorForwardsStr(posX)
   115  	output += moveCursorDownStr(posY)
   116  	return output
   117  }
   118  
   119  func (rl *Instance) moveCursorFromEndToLinePosStr() string {
   120  	lineX, lineY := rl.lineWrapCellLen()
   121  	posX, posY := rl.lineWrapCellPos()
   122  	output := moveCursorBackwardsStr(lineX - posX)
   123  	output += moveCursorUpStr(lineY - posY)
   124  	return output
   125  }
   126  
   127  func (rl *Instance) moveCursorByRuneAdjustStr(rAdjust int) string {
   128  	oldX, oldY := rl.lineWrapCellPos()
   129  
   130  	rl.line.SetRunePos(rl.line.RunePos() + rAdjust)
   131  
   132  	newX, newY := rl.lineWrapCellPos()
   133  
   134  	y := newY - oldY
   135  
   136  	var output string
   137  
   138  	switch {
   139  	case y < 0:
   140  		output += moveCursorUpStr(-y)
   141  	case y > 0:
   142  		output += moveCursorDownStr(y)
   143  	}
   144  
   145  	x := newX - oldX
   146  	switch {
   147  	case x < 0:
   148  		output += moveCursorBackwardsStr(-x)
   149  	case x > 0:
   150  		output += moveCursorForwardsStr(x)
   151  	}
   152  
   153  	return output
   154  }