2022-03-13 17:48:49 +00:00
|
|
|
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 {
|
2022-03-26 21:34:09 +00:00
|
|
|
print(rl.SyntaxHighlighter(line))
|
2022-03-13 17:48:49 +00:00
|
|
|
} else {
|
2022-03-26 21:34:09 +00:00
|
|
|
print(string(line))
|
2022-03-13 17:48:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
}
|
|
|
|
|
2022-05-14 00:43:40 +00:00
|
|
|
func (rl *Instance) Insert(t string) {
|
|
|
|
rl.insert([]rune(t))
|
|
|
|
}
|
|
|
|
|
2022-03-13 17:48:49 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2022-03-24 01:11:24 +00:00
|
|
|
func (rl *Instance) deleteBackspace(forward bool) {
|
2022-03-13 17:48:49 +00:00
|
|
|
switch {
|
|
|
|
case len(rl.line) == 0:
|
|
|
|
return
|
2022-03-24 01:11:24 +00:00
|
|
|
case forward:
|
|
|
|
rl.line = append(rl.line[:rl.pos], rl.line[rl.pos+1:]...)
|
2022-03-13 17:48:49 +00:00
|
|
|
case rl.pos > len(rl.line):
|
2022-03-24 01:11:24 +00:00
|
|
|
rl.backspace(forward) // There is an infite loop going on here...
|
2022-03-13 17:48:49 +00:00
|
|
|
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
|
|
|
|
}
|
2022-03-27 03:43:30 +00:00
|
|
|
|
|
|
|
func (rl *Instance) deleteToEnd() {
|
|
|
|
rl.resetVirtualComp(false)
|
|
|
|
// Keep everything before the cursor
|
|
|
|
rl.line = rl.line[:rl.pos]
|
|
|
|
}
|
2022-04-13 03:08:44 +00:00
|
|
|
|
2022-04-14 11:42:46 +00:00
|
|
|
// @TODO(Renzix): move to emacs sepecific file
|
2022-04-13 03:08:44 +00:00
|
|
|
func (rl *Instance) emacsForwardWord(tokeniser tokeniser) (adjust int) {
|
|
|
|
split, index, pos := tokeniser(rl.line, rl.pos)
|
|
|
|
if len(split) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-04-14 11:42:46 +00:00
|
|
|
word := strings.TrimSpace(split[index])
|
2022-04-13 03:08:44 +00:00
|
|
|
|
|
|
|
switch {
|
|
|
|
case len(split) == 0:
|
|
|
|
return
|
2022-04-14 11:42:46 +00:00
|
|
|
case pos == len(word) && index != len(split)-1:
|
|
|
|
extrawhitespace := len(strings.TrimLeft(split[index], " ")) - len(word)
|
|
|
|
word = split[index+1]
|
|
|
|
adjust = len(word) + extrawhitespace
|
2022-04-13 03:08:44 +00:00
|
|
|
default:
|
|
|
|
adjust = len(word) - pos
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2022-04-14 11:42:46 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|