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

     1  package readline
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strings"
     7  
     8  	"github.com/mattn/go-runewidth"
     9  )
    10  
    11  func printf(format string, a ...interface{}) {
    12  	s := fmt.Sprintf(format, a...)
    13  	print(s)
    14  }
    15  
    16  // var rxAnsiSgr = regexp.MustCompile("\x1b\\[[:;0-9]+m")
    17  var rxAnsiSgr = regexp.MustCompile(`\x1b\[([0-9]{1,2}(;[0-9]{1,2})*)?[m|K]`)
    18  
    19  // Gets the number of runes in a string and
    20  func strLen(s string) int {
    21  	s = rxAnsiSgr.ReplaceAllString(s, "")
    22  	return runewidth.StringWidth(s)
    23  }
    24  
    25  func (rl *Instance) echoStr() string {
    26  	if len(rl.multiSplit) == 0 {
    27  		rl.syntaxCompletion()
    28  	}
    29  
    30  	lineX, lineY := rl.lineWrapCellLen()
    31  	posX, posY := rl.lineWrapCellPos()
    32  
    33  	// reset cursor to start
    34  	line := "\r"
    35  	if posY > 0 {
    36  		line += fmt.Sprintf(cursorUpf, posY)
    37  	}
    38  
    39  	// clear the line
    40  	line += strings.Repeat("\x1b[2K\n", lineY+1) // clear line + move cursor down 1
    41  	line += fmt.Sprintf(cursorUpf, lineY+1)
    42  	//line += seqClearScreenBelow
    43  
    44  	promptLen := rl.promptLen
    45  	if promptLen < rl.termWidth {
    46  		line += rl.prompt
    47  	} else {
    48  		promptLen = 0
    49  	}
    50  
    51  	switch {
    52  	case rl.PasswordMask != 0:
    53  		line += strings.Repeat(string(rl.PasswordMask), rl.line.CellLen())
    54  
    55  	case rl.line.CellLen()+promptLen > rl.termWidth:
    56  		fallthrough
    57  
    58  	case rl.SyntaxHighlighter == nil:
    59  		line += strings.Join(lineWrap(rl, rl.termWidth), "\r\n")
    60  
    61  	default:
    62  		syntax := rl.cacheSyntax.Get(rl.line.Runes())
    63  		if len(syntax) == 0 {
    64  			syntax = rl.SyntaxHighlighter(rl.line.Runes())
    65  
    66  			if rl.DelayedSyntaxWorker == nil {
    67  				rl.cacheSyntax.Append(rl.line.Runes(), syntax)
    68  			}
    69  		}
    70  		line += syntax
    71  	}
    72  
    73  	y := lineY - posY
    74  	if y > 0 {
    75  		line += fmt.Sprintf(cursorUpf, y)
    76  	}
    77  	x := lineX - posX + 1
    78  	if x > 0 {
    79  		line += fmt.Sprintf(cursorBackf, x)
    80  	}
    81  	//print(line)
    82  	return line
    83  }
    84  
    85  func lineWrap(rl *Instance, termWidth int) []string {
    86  	var promptLen int
    87  	if rl.promptLen < termWidth {
    88  		promptLen = rl.promptLen
    89  	}
    90  
    91  	var (
    92  		wrap       []string
    93  		wrapRunes  [][]rune
    94  		bufCellLen int
    95  		length     = termWidth - promptLen
    96  		line       = rl.line.Runes() //append(rl.line.Runes(), []rune{' ', ' '}...) // double space to work around wide characters
    97  		lPos       int
    98  	)
    99  
   100  	wrapRunes = append(wrapRunes, []rune{})
   101  
   102  	for r := range line {
   103  		w := runewidth.RuneWidth(line[r])
   104  		if bufCellLen+w > length {
   105  			wrapRunes = append(wrapRunes, []rune(strings.Repeat(" ", promptLen)))
   106  			lPos++
   107  			bufCellLen = 0
   108  		}
   109  		bufCellLen += w
   110  		wrapRunes[lPos] = append(wrapRunes[lPos], line[r])
   111  	}
   112  
   113  	wrap = make([]string, lPos+1)
   114  	for i := range wrap {
   115  		wrap[i] = string(wrapRunes[i])
   116  	}
   117  
   118  	return wrap
   119  }
   120  
   121  func (rl *Instance) lineWrapCellLen() (x, y int) {
   122  	return lineWrapCell(rl.promptLen, rl.line.Runes(), rl.termWidth)
   123  }
   124  
   125  func (rl *Instance) lineWrapCellPos() (x, y int) {
   126  	return lineWrapCell(rl.promptLen, rl.line.Runes()[:rl.line.RunePos()], rl.termWidth)
   127  }
   128  
   129  func lineWrapCell(promptLen int, line []rune, termWidth int) (x, y int) {
   130  	if promptLen >= termWidth {
   131  		promptLen = 0
   132  	}
   133  
   134  	// avoid divide by zero error
   135  	if termWidth-promptLen == 0 {
   136  		return 0, 0
   137  	}
   138  
   139  	x = promptLen
   140  	for i := range line {
   141  		w := runewidth.RuneWidth(line[i])
   142  		if x+w > termWidth {
   143  			x = promptLen
   144  			y++
   145  		}
   146  		x += w
   147  	}
   148  
   149  	return
   150  }
   151  
   152  func (rl *Instance) clearPrompt() {
   153  	if rl.line.RuneLen() == 0 {
   154  		return
   155  	}
   156  
   157  	output := rl.moveCursorToStartStr()
   158  
   159  	if rl.termWidth > rl.promptLen {
   160  		output += strings.Repeat(" ", rl.termWidth-rl.promptLen)
   161  	}
   162  	output += seqClearScreenBelow
   163  
   164  	output += moveCursorBackwardsStr(rl.termWidth)
   165  	output += rl.prompt
   166  
   167  	rl.line.Set(rl, []rune{})
   168  	rl.line.SetRunePos(0)
   169  
   170  	print(output)
   171  }
   172  
   173  func (rl *Instance) resetHelpers() {
   174  	rl.modeAutoFind = false
   175  	output := rl.clearPreviewStr()
   176  	output += rl.clearHelpersStr()
   177  
   178  	rl.resetHintText()
   179  	rl.resetTabCompletion()
   180  
   181  	print(output)
   182  }
   183  
   184  func (rl *Instance) clearHelpersStr() string {
   185  	posX, posY := rl.lineWrapCellPos()
   186  	_, lineY := rl.lineWrapCellLen()
   187  
   188  	y := lineY - posY
   189  
   190  	output := moveCursorDownStr(y)
   191  	output += "\r\n" + seqClearScreenBelow
   192  
   193  	output += moveCursorUpStr(y + 1)
   194  	output += moveCursorForwardsStr(posX)
   195  
   196  	return output
   197  }
   198  
   199  func (rl *Instance) renderHelpersStr() string {
   200  	output := rl.writeHintTextStr()
   201  	output += rl.writeTabCompletionStr()
   202  	output += rl.writePreviewStr()
   203  	return output
   204  }
   205  
   206  func (rl *Instance) updateHelpersStr() string {
   207  	rl.tcOffset = 0
   208  	rl.getHintText()
   209  	if rl.modeTabCompletion {
   210  		rl.getTabCompletion()
   211  	}
   212  	output := rl.clearHelpersStr()
   213  	output += rl.renderHelpersStr()
   214  	return output
   215  }