Hilbish/readline/line.go

228 lines
5.1 KiB
Go

package readline
import (
"strings"
)
// When the DelayedSyntaxWorker gives us a new line, we need to check if there
// is any processing to be made, that all lines match in terms of content.
func (rl *Instance) updateLine(line []rune) {
if len(rl.currentComp) > 0 {
} else {
rl.line = line
}
rl.renderHelpers()
}
// getLine - In many places we need the current line input. We either return the real line,
// or the one that includes the current completion candidate, if there is any.
func (rl *Instance) GetLine() []rune {
if len(rl.currentComp) > 0 {
return rl.lineComp
}
return rl.line
}
// echo - refresh the current input line, either virtually completed or not.
// also renders the current completions and hints. To be noted, the updateReferences()
// function is only ever called once, and after having moved back to prompt position
// and having printed the line: this is so that at any moment, everyone has the good
// values for moving around, synchronized with the update input line.
func (rl *Instance) echo() {
// Then we print the prompt, and the line,
switch {
case rl.PasswordMask != 0:
case rl.PasswordMask > 0:
print(strings.Repeat(string(rl.PasswordMask), len(rl.line)) + " ")
default:
// Go back to prompt position, and clear everything below
moveCursorBackwards(GetTermWidth())
moveCursorUp(rl.posY)
print(seqClearScreenBelow)
// Print the prompt
print(string(rl.realPrompt))
// Assemble the line, taking virtual completions into account
var line []rune
if len(rl.currentComp) > 0 {
line = rl.lineComp
} else {
line = rl.line
}
// Print the input line with optional syntax highlighting
if rl.SyntaxHighlighter != nil {
print(rl.SyntaxHighlighter(line))
} else {
print(string(line))
}
}
// Update references with new coordinates only now, because
// the new line may be longer/shorter than the previous one.
rl.updateReferences()
// Go back to the current cursor position, with new coordinates
moveCursorBackwards(GetTermWidth())
moveCursorUp(rl.fullY)
moveCursorDown(rl.posY)
moveCursorForwards(rl.posX)
}
func (rl *Instance) insert(r []rune) {
for {
// I don't really understand why `0` is creaping in at the end of the
// array but it only happens with unicode characters.
if len(r) > 1 && r[len(r)-1] == 0 {
r = r[:len(r)-1]
continue
}
break
}
// We can ONLY have three fondamentally different cases:
switch {
// The line is empty
case len(rl.line) == 0:
rl.line = r
// We are inserting somewhere in the middle
case rl.pos < len(rl.line):
r := append(r, rl.line[rl.pos:]...)
rl.line = append(rl.line[:rl.pos], r...)
// We are at the end of the input line
case rl.pos == len(rl.line):
rl.line = append(rl.line, r...)
}
rl.pos += len(r)
// This should also update the rl.pos
rl.updateHelpers()
}
func (rl *Instance) Insert(t string) {
rl.insert([]rune(t))
}
func (rl *Instance) deleteX() {
switch {
case len(rl.line) == 0:
return
case rl.pos == 0:
rl.line = rl.line[1:]
case rl.pos > len(rl.line):
rl.pos = len(rl.line)
case rl.pos == len(rl.line):
rl.pos--
rl.line = rl.line[:rl.pos]
default:
rl.line = append(rl.line[:rl.pos], rl.line[rl.pos+1:]...)
}
rl.updateHelpers()
}
func (rl *Instance) deleteBackspace(forward bool) {
switch {
case len(rl.line) == 0:
return
case forward:
rl.line = append(rl.line[:rl.pos], rl.line[rl.pos+1:]...)
case rl.pos > len(rl.line):
rl.backspace(forward) // There is an infite loop going on here...
case rl.pos == len(rl.line):
rl.pos--
rl.line = rl.line[:rl.pos]
default:
rl.pos--
rl.line = append(rl.line[:rl.pos], rl.line[rl.pos+1:]...)
}
rl.updateHelpers()
}
func (rl *Instance) clearLine() {
if len(rl.line) == 0 {
return
}
// We need to go back to prompt
moveCursorUp(rl.posY)
moveCursorBackwards(GetTermWidth())
moveCursorForwards(rl.promptLen)
// Clear everything after & below the cursor
print(seqClearScreenBelow)
// Real input line
rl.line = []rune{}
rl.lineComp = []rune{}
rl.pos = 0
rl.posX = 0
rl.fullX = 0
rl.posY = 0
rl.fullY = 0
// Completions are also reset
rl.clearVirtualComp()
}
func (rl *Instance) deleteToBeginning() {
rl.resetVirtualComp(false)
// Keep the line length up until the cursor
rl.line = rl.line[rl.pos:]
rl.pos = 0
}
func (rl *Instance) deleteToEnd() {
rl.resetVirtualComp(false)
// Keep everything before the cursor
rl.line = rl.line[:rl.pos]
}
// @TODO(Renzix): move to emacs sepecific file
func (rl *Instance) emacsForwardWord(tokeniser tokeniser) (adjust int) {
split, index, pos := tokeniser(rl.line, rl.pos)
if len(split) == 0 {
return
}
word := strings.TrimSpace(split[index])
switch {
case len(split) == 0:
return
case pos == len(word) && index != len(split)-1:
extrawhitespace := len(strings.TrimLeft(split[index], " ")) - len(word)
word = split[index+1]
adjust = len(word) + extrawhitespace
default:
adjust = len(word) - pos
}
return
}
func (rl *Instance) emacsBackwardWord(tokeniser tokeniser) (adjust int) {
split, index, pos := tokeniser(rl.line, rl.pos)
if len(split) == 0 {
return
}
switch {
case len(split) == 0:
return
case pos == 0 && index != 0:
adjust = len(split[index-1])
default:
adjust = pos
}
return
}