2022-03-13 17:48:49 +00:00
|
|
|
package readline
|
|
|
|
|
2022-10-10 22:17:58 +00:00
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"golang.org/x/text/width"
|
|
|
|
)
|
2022-04-20 12:18:34 +00:00
|
|
|
|
2022-03-13 17:48:49 +00:00
|
|
|
// updateHelpers is a key part of the whole refresh process:
|
2022-03-26 21:34:09 +00:00
|
|
|
// it should coordinate reprinting the input line, any Infos and completions
|
2022-03-13 17:48:49 +00:00
|
|
|
// and manage to get back to the current (computed) cursor coordinates
|
|
|
|
func (rl *Instance) updateHelpers() {
|
|
|
|
|
2022-03-26 21:34:09 +00:00
|
|
|
// Load all Infos & completions before anything.
|
|
|
|
// Thus overwrites anything having been dirtily added/forced/modified, like rl.SetInfoText()
|
|
|
|
rl.getInfoText()
|
2022-03-13 17:48:49 +00:00
|
|
|
rl.getHintText()
|
2022-05-31 19:16:32 +00:00
|
|
|
if rl.modeTabCompletion && !rl.completionOpen {
|
2022-03-13 17:48:49 +00:00
|
|
|
rl.getTabCompletion()
|
2022-05-31 19:16:32 +00:00
|
|
|
} else {
|
|
|
|
if rl.completionOpen { rl.completionOpen = false }
|
2022-03-13 17:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// We clear everything
|
|
|
|
rl.clearHelpers()
|
|
|
|
|
|
|
|
// We are at the prompt line (with the latter
|
|
|
|
// not printed yet), then reprint everything
|
|
|
|
rl.renderHelpers()
|
|
|
|
}
|
|
|
|
|
2022-04-20 12:18:34 +00:00
|
|
|
const tabWidth = 4
|
|
|
|
|
|
|
|
func getWidth(x []rune) int {
|
|
|
|
var w int
|
|
|
|
for _, j := range x {
|
|
|
|
k := width.LookupRune(j).Kind()
|
|
|
|
if j == '\t' {
|
|
|
|
w += tabWidth
|
|
|
|
} else if k == width.EastAsianWide || k == width.EastAsianFullwidth {
|
|
|
|
w += 2
|
|
|
|
} else {
|
|
|
|
w++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
2022-03-13 17:48:49 +00:00
|
|
|
// Update reference should be called only once in a "loop" (not Readline(), but key control loop)
|
|
|
|
func (rl *Instance) updateReferences() {
|
|
|
|
|
|
|
|
// We always need to work with clean data,
|
|
|
|
// since we will have incrementers all around
|
|
|
|
rl.posX = 0
|
|
|
|
rl.fullX = 0
|
|
|
|
rl.posY = 0
|
|
|
|
rl.fullY = 0
|
|
|
|
|
2022-10-10 22:17:58 +00:00
|
|
|
var curLine []rune
|
2022-03-13 17:48:49 +00:00
|
|
|
if len(rl.currentComp) > 0 {
|
2022-10-10 22:17:58 +00:00
|
|
|
curLine = rl.lineComp
|
2022-03-13 17:48:49 +00:00
|
|
|
} else {
|
2022-10-10 22:17:58 +00:00
|
|
|
curLine = rl.line
|
2022-03-13 17:48:49 +00:00
|
|
|
}
|
2022-10-10 22:17:58 +00:00
|
|
|
fullLine := getWidth(curLine)
|
|
|
|
cPosLine := getWidth(curLine[:rl.pos])
|
2022-03-13 17:48:49 +00:00
|
|
|
|
|
|
|
// We need the X offset of the whole line
|
|
|
|
toEndLine := rl.promptLen + fullLine
|
|
|
|
fullOffset := toEndLine / GetTermWidth()
|
2022-10-10 22:17:58 +00:00
|
|
|
rl.fullY = fullOffset + strings.Count(string(curLine), "\n")
|
2022-03-13 17:48:49 +00:00
|
|
|
fullRest := toEndLine % GetTermWidth()
|
|
|
|
rl.fullX = fullRest
|
|
|
|
|
2022-04-19 02:42:27 +00:00
|
|
|
if fullRest == 0 && fullOffset > 0 {
|
|
|
|
print("\n")
|
|
|
|
}
|
|
|
|
|
2022-03-13 17:48:49 +00:00
|
|
|
// Use rl.pos value to get the offset to go TO/FROM the CURRENT POSITION
|
|
|
|
lineToCursorPos := rl.promptLen + cPosLine
|
|
|
|
offsetToCursor := lineToCursorPos / GetTermWidth()
|
|
|
|
cPosRest := lineToCursorPos % GetTermWidth()
|
|
|
|
|
|
|
|
// If we are at the end of line
|
|
|
|
if fullLine == rl.pos {
|
|
|
|
rl.posY = fullOffset
|
|
|
|
|
|
|
|
if fullRest == 0 {
|
|
|
|
rl.posX = 0
|
|
|
|
} else if fullRest > 0 {
|
|
|
|
rl.posX = fullRest
|
|
|
|
}
|
|
|
|
} else if rl.pos < fullLine {
|
|
|
|
// If we are somewhere in the middle of the line
|
|
|
|
rl.posY = offsetToCursor
|
|
|
|
|
|
|
|
if cPosRest == 0 {
|
|
|
|
} else if cPosRest > 0 {
|
|
|
|
rl.posX = cPosRest
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rl *Instance) resetHelpers() {
|
|
|
|
rl.modeAutoFind = false
|
|
|
|
|
|
|
|
// Now reset all below-input helpers
|
2022-03-26 21:34:09 +00:00
|
|
|
rl.resetInfoText()
|
2022-03-13 17:48:49 +00:00
|
|
|
rl.resetTabCompletion()
|
|
|
|
}
|
|
|
|
|
2022-03-26 21:34:09 +00:00
|
|
|
// clearHelpers - Clears everything: prompt, input, Infos & comps,
|
2022-03-13 17:48:49 +00:00
|
|
|
// and comes back at the prompt.
|
|
|
|
func (rl *Instance) clearHelpers() {
|
|
|
|
|
|
|
|
// Now go down to the last line of input
|
|
|
|
moveCursorDown(rl.fullY - rl.posY)
|
|
|
|
moveCursorBackwards(rl.posX)
|
|
|
|
moveCursorForwards(rl.fullX)
|
|
|
|
|
|
|
|
// Clear everything below
|
|
|
|
print(seqClearScreenBelow)
|
|
|
|
|
|
|
|
// Go back to current cursor position
|
|
|
|
moveCursorBackwards(GetTermWidth())
|
|
|
|
moveCursorUp(rl.fullY - rl.posY)
|
|
|
|
moveCursorForwards(rl.posX)
|
|
|
|
}
|
|
|
|
|
2022-03-26 21:34:09 +00:00
|
|
|
// renderHelpers - pritns all components (prompt, line, Infos & comps)
|
2022-03-13 17:48:49 +00:00
|
|
|
// and replaces the cursor to its current position. This function never
|
|
|
|
// computes or refreshes any value, except from inside the echo function.
|
|
|
|
func (rl *Instance) renderHelpers() {
|
2022-04-20 12:18:34 +00:00
|
|
|
|
2022-03-26 21:34:09 +00:00
|
|
|
// when the instance is in this state we want it to be "below" the user's
|
|
|
|
// input for it to be aligned properly
|
|
|
|
if !rl.compConfirmWait {
|
|
|
|
rl.writeHintText()
|
|
|
|
}
|
2022-03-13 17:48:49 +00:00
|
|
|
rl.echo()
|
2022-03-26 21:34:09 +00:00
|
|
|
if rl.modeTabCompletion {
|
|
|
|
// in tab complete mode we want it to update
|
|
|
|
// when something has been selected
|
|
|
|
// (dynamic!!)
|
|
|
|
rl.getHintText()
|
|
|
|
rl.writeHintText()
|
|
|
|
} else if !rl.compConfirmWait {
|
2022-04-19 02:42:27 +00:00
|
|
|
// for the same reason above of wanting it below user input, do nothing here
|
2022-03-26 21:34:09 +00:00
|
|
|
} else {
|
|
|
|
rl.writeHintText()
|
|
|
|
}
|
2022-03-13 17:48:49 +00:00
|
|
|
|
2022-04-13 14:13:46 +00:00
|
|
|
rl.echoRightPrompt()
|
|
|
|
|
2022-03-13 17:48:49 +00:00
|
|
|
// Go at beginning of first line after input remainder
|
|
|
|
moveCursorDown(rl.fullY - rl.posY)
|
|
|
|
moveCursorBackwards(GetTermWidth())
|
|
|
|
|
2022-03-26 21:34:09 +00:00
|
|
|
// Print Infos, check for any confirmation Info current.
|
|
|
|
// (do not overwrite the confirmation question Info)
|
2022-03-13 17:48:49 +00:00
|
|
|
if !rl.compConfirmWait {
|
2022-03-26 21:34:09 +00:00
|
|
|
if len(rl.infoText) > 0 {
|
2022-03-13 17:48:49 +00:00
|
|
|
print("\n")
|
|
|
|
}
|
2022-03-26 21:34:09 +00:00
|
|
|
rl.writeInfoText()
|
2022-03-13 17:48:49 +00:00
|
|
|
moveCursorBackwards(GetTermWidth())
|
|
|
|
|
|
|
|
// Print completions and go back to beginning of this line
|
|
|
|
print("\n")
|
|
|
|
rl.writeTabCompletion()
|
|
|
|
moveCursorBackwards(GetTermWidth())
|
|
|
|
moveCursorUp(rl.tcUsedY)
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we are still waiting for the user to confirm too long completions
|
2022-03-26 21:34:09 +00:00
|
|
|
// Immediately refresh the Infos
|
2022-03-13 17:48:49 +00:00
|
|
|
if rl.compConfirmWait {
|
|
|
|
print("\n")
|
2022-03-26 21:34:09 +00:00
|
|
|
rl.writeInfoText()
|
|
|
|
rl.getInfoText()
|
2022-03-13 17:48:49 +00:00
|
|
|
moveCursorBackwards(GetTermWidth())
|
|
|
|
}
|
|
|
|
|
2022-03-26 21:34:09 +00:00
|
|
|
// Anyway, compensate for Info printout
|
|
|
|
if len(rl.infoText) > 0 {
|
|
|
|
moveCursorUp(rl.infoY)
|
2022-03-13 17:48:49 +00:00
|
|
|
} else if !rl.compConfirmWait {
|
|
|
|
moveCursorUp(1)
|
|
|
|
} else if rl.compConfirmWait {
|
|
|
|
moveCursorUp(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Go back to current cursor position
|
|
|
|
moveCursorUp(rl.fullY - rl.posY)
|
|
|
|
moveCursorForwards(rl.posX)
|
|
|
|
}
|