178 lines
3.9 KiB
Go
178 lines
3.9 KiB
Go
package prompt
|
|
|
|
import (
|
|
"log"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
shortenSuffix = "..."
|
|
leftPrefix = " "
|
|
leftSuffix = " "
|
|
rightPrefix = " "
|
|
rightSuffix = " "
|
|
leftMargin = len(leftPrefix + leftSuffix)
|
|
rightMargin = len(rightPrefix + rightSuffix)
|
|
completionMargin = leftMargin + rightMargin
|
|
)
|
|
|
|
// Suggest is printed when completing.
|
|
type Suggest struct {
|
|
Text string
|
|
Description string
|
|
}
|
|
|
|
// CompletionManager manages which suggestion is now selected.
|
|
type CompletionManager struct {
|
|
selected int // -1 means nothing one is selected.
|
|
tmp []Suggest
|
|
max uint16
|
|
completer Completer
|
|
|
|
verticalScroll int
|
|
}
|
|
|
|
// GetSelectedSuggestion returns the selected item.
|
|
func (c *CompletionManager) GetSelectedSuggestion() (s Suggest, ok bool) {
|
|
if c.selected == -1 {
|
|
return Suggest{}, false
|
|
} else if c.selected < -1 {
|
|
log.Printf("[ERROR] shoud be reached here, selected=%d", c.selected)
|
|
c.selected = -1
|
|
return Suggest{}, false
|
|
}
|
|
return c.tmp[c.selected], true
|
|
}
|
|
|
|
// GetSuggestions returns the list of suggestion.
|
|
func (c *CompletionManager) GetSuggestions() []Suggest {
|
|
return c.tmp
|
|
}
|
|
|
|
// Reset to select nothing.
|
|
func (c *CompletionManager) Reset() {
|
|
c.selected = -1
|
|
c.verticalScroll = 0
|
|
c.Update(*NewDocument())
|
|
return
|
|
}
|
|
|
|
// Update to update the suggestions.
|
|
func (c *CompletionManager) Update(in Document) {
|
|
c.tmp = c.completer(in)
|
|
return
|
|
}
|
|
|
|
// Previous to select the previous suggestion item.
|
|
func (c *CompletionManager) Previous() {
|
|
if c.verticalScroll == c.selected && c.selected > 0 {
|
|
c.verticalScroll--
|
|
}
|
|
c.selected--
|
|
c.update()
|
|
return
|
|
}
|
|
|
|
// Next to select the next suggestion item.
|
|
func (c *CompletionManager) Next() {
|
|
if c.verticalScroll+int(c.max)-1 == c.selected {
|
|
c.verticalScroll++
|
|
}
|
|
c.selected++
|
|
c.update()
|
|
return
|
|
}
|
|
|
|
// Completing returns whether the CompletionManager selects something one.
|
|
func (c *CompletionManager) Completing() bool {
|
|
return c.selected != -1
|
|
}
|
|
|
|
func (c *CompletionManager) update() {
|
|
max := int(c.max)
|
|
if len(c.tmp) < max {
|
|
max = len(c.tmp)
|
|
}
|
|
|
|
if c.selected >= len(c.tmp) {
|
|
c.Reset()
|
|
} else if c.selected < -1 {
|
|
c.selected = len(c.tmp) - 1
|
|
c.verticalScroll = len(c.tmp) - max
|
|
}
|
|
}
|
|
|
|
func formatTexts(o []string, max int, prefix, suffix string) (new []string, width int) {
|
|
l := len(o)
|
|
n := make([]string, l)
|
|
|
|
lenPrefix := len([]rune(prefix))
|
|
lenSuffix := len([]rune(suffix))
|
|
lenShorten := len(shortenSuffix)
|
|
min := lenPrefix + lenSuffix + lenShorten
|
|
for i := 0; i < l; i++ {
|
|
if width < len([]rune(o[i])) {
|
|
width = len([]rune(o[i]))
|
|
}
|
|
}
|
|
|
|
if width == 0 {
|
|
return n, 0
|
|
}
|
|
if min >= max {
|
|
log.Println("[WARN] formatTexts: max is lower than length of prefix and suffix.")
|
|
return n, 0
|
|
}
|
|
if lenPrefix+width+lenSuffix > max {
|
|
width = max - lenPrefix - lenSuffix
|
|
}
|
|
|
|
for i := 0; i < l; i++ {
|
|
r := []rune(o[i])
|
|
x := len(r)
|
|
if x <= width {
|
|
spaces := strings.Repeat(" ", width-x)
|
|
n[i] = prefix + o[i] + spaces + suffix
|
|
} else if x > width {
|
|
n[i] = prefix + string(r[:width-lenShorten]) + shortenSuffix + suffix
|
|
}
|
|
}
|
|
return n, lenPrefix + width + lenSuffix
|
|
}
|
|
|
|
func formatSuggestions(suggests []Suggest, max int) (new []Suggest, width int) {
|
|
num := len(suggests)
|
|
new = make([]Suggest, num)
|
|
|
|
left := make([]string, num)
|
|
for i := 0; i < num; i++ {
|
|
left[i] = suggests[i].Text
|
|
}
|
|
right := make([]string, num)
|
|
for i := 0; i < num; i++ {
|
|
right[i] = suggests[i].Description
|
|
}
|
|
|
|
left, leftWidth := formatTexts(left, max, leftPrefix, leftSuffix)
|
|
if leftWidth == 0 {
|
|
return []Suggest{}, 0
|
|
}
|
|
right, rightWidth := formatTexts(right, max-leftWidth, rightPrefix, rightSuffix)
|
|
|
|
for i := 0; i < num; i++ {
|
|
new[i] = Suggest{Text: left[i], Description: right[i]}
|
|
}
|
|
return new, leftWidth + rightWidth
|
|
}
|
|
|
|
// NewCompletionManager returns initialized CompletionManager object.
|
|
func NewCompletionManager(completer Completer, max uint16) *CompletionManager {
|
|
return &CompletionManager{
|
|
selected: -1,
|
|
max: max,
|
|
completer: completer,
|
|
|
|
verticalScroll: 0,
|
|
}
|
|
}
|