2
2
mirror of https://github.com/Hilbis/Hilbish synced 2025-03-13 18:00:41 +00:00
Hilbish/readline/update.go
TorchedSammy 308e257872
fix(readline): take into account newlines when calculating amount of lines taken up by input
this does not really fix the issue of multiline input
being broken completely, but prevents the prompt
being reprinted on input
2022-10-10 18:17:58 -04:00

197 lines
4.8 KiB
Go

package readline
import (
"strings"
"golang.org/x/text/width"
)
// updateHelpers is a key part of the whole refresh process:
// it should coordinate reprinting the input line, any Infos and completions
// and manage to get back to the current (computed) cursor coordinates
func (rl *Instance) updateHelpers() {
// Load all Infos & completions before anything.
// Thus overwrites anything having been dirtily added/forced/modified, like rl.SetInfoText()
rl.getInfoText()
rl.getHintText()
if rl.modeTabCompletion && !rl.completionOpen {
rl.getTabCompletion()
} else {
if rl.completionOpen { rl.completionOpen = false }
}
// We clear everything
rl.clearHelpers()
// We are at the prompt line (with the latter
// not printed yet), then reprint everything
rl.renderHelpers()
}
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
}
// 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
var curLine []rune
if len(rl.currentComp) > 0 {
curLine = rl.lineComp
} else {
curLine = rl.line
}
fullLine := getWidth(curLine)
cPosLine := getWidth(curLine[:rl.pos])
// We need the X offset of the whole line
toEndLine := rl.promptLen + fullLine
fullOffset := toEndLine / GetTermWidth()
rl.fullY = fullOffset + strings.Count(string(curLine), "\n")
fullRest := toEndLine % GetTermWidth()
rl.fullX = fullRest
if fullRest == 0 && fullOffset > 0 {
print("\n")
}
// 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
rl.resetInfoText()
rl.resetTabCompletion()
}
// clearHelpers - Clears everything: prompt, input, Infos & comps,
// 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)
}
// renderHelpers - pritns all components (prompt, line, Infos & comps)
// 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() {
// 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()
}
rl.echo()
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 {
// for the same reason above of wanting it below user input, do nothing here
} else {
rl.writeHintText()
}
rl.echoRightPrompt()
// Go at beginning of first line after input remainder
moveCursorDown(rl.fullY - rl.posY)
moveCursorBackwards(GetTermWidth())
// Print Infos, check for any confirmation Info current.
// (do not overwrite the confirmation question Info)
if !rl.compConfirmWait {
if len(rl.infoText) > 0 {
print("\n")
}
rl.writeInfoText()
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
// Immediately refresh the Infos
if rl.compConfirmWait {
print("\n")
rl.writeInfoText()
rl.getInfoText()
moveCursorBackwards(GetTermWidth())
}
// Anyway, compensate for Info printout
if len(rl.infoText) > 0 {
moveCursorUp(rl.infoY)
} 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)
}