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 }