Hilbish/readline/comp-grid.go

154 lines
3.5 KiB
Go

package readline
import (
"fmt"
"strconv"
"strings"
"github.com/rivo/uniseg"
)
// initGrid - Grid display details. Called each time we want to be sure to have
// a working completion group either immediately, or later on. Generally defered.
func (g *CompletionGroup) initGrid(rl *Instance) {
// Compute size of each completion item box
tcMaxLength := 1
for i := range g.Suggestions {
if uniseg.GraphemeClusterCount(g.Suggestions[i]) > tcMaxLength {
tcMaxLength = uniseg.GraphemeClusterCount(g.Suggestions[i])
}
}
g.tcPosX = 0
g.tcPosY = 1
g.tcOffset = 0
// Max number of columns
g.tcMaxX = GetTermWidth() / (tcMaxLength + 2)
if g.tcMaxX < 1 {
g.tcMaxX = 1 // avoid a divide by zero error
}
// Maximum number of lines
maxY := len(g.Suggestions) / g.tcMaxX
rest := len(g.Suggestions) % g.tcMaxX
if rest != 0 {
// if rest != 0 && maxY != 1 {
maxY++
}
if maxY > g.MaxLength {
g.tcMaxY = g.MaxLength
} else {
g.tcMaxY = maxY
}
}
// moveTabGridHighlight - Moves the highlighting for currently selected completion item (grid display)
func (g *CompletionGroup) moveTabGridHighlight(rl *Instance, x, y int) (done bool, next bool) {
g.tcPosX += x
g.tcPosY += y
// Columns
if g.tcPosX < 1 {
if g.tcPosY == 1 && rl.tabCompletionReverse {
g.tcPosX = 1
g.tcPosY = 0
} else {
// This is when multiple ligns, not yet on first one.
g.tcPosX = g.tcMaxX
g.tcPosY--
}
}
if g.tcPosY > g.tcMaxY {
g.tcPosY = 1
return true, true
}
// If we must move to next line in same group
if g.tcPosX > g.tcMaxX {
g.tcPosX = 1
g.tcPosY++
}
// Real max number of suggestions.
max := g.tcMaxX * g.tcMaxY
if max > len(g.Suggestions) {
max = len(g.Suggestions)
}
// We arrived at the end of suggestions. This condition can never be triggered
// while going in the reverse order, only forward, so no further checks in it.
if (g.tcMaxX*(g.tcPosY-1))+g.tcPosX > max {
return true, true
}
// In case we are reverse cycling and currently selecting the first item,
// we adjust the coordinates to point to the last item and return
// We set g.tcPosY because the printer needs to get the a candidate nonetheless.
if rl.tabCompletionReverse && g.tcPosX == 1 && g.tcPosY == 0 {
g.tcPosY = 1
return true, false
}
// By default, come back to this group for next item.
return false, false
}
// writeGrid - A grid completion string
func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) {
// If group title, print it and adjust offset.
if g.Name != "" {
comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, fmtEscape(g.Name), RESET)
rl.tcUsedY++
}
cellWidth := strconv.Itoa((GetTermWidth() / g.tcMaxX) - 4)
x := 0
y := 1
for i := range g.Suggestions {
x++
if x > g.tcMaxX {
x = 1
y++
if y > g.tcMaxY {
y--
break
} else {
comp += "\r\n"
}
}
if (x == g.tcPosX && y == g.tcPosY) && (g.isCurrent) {
comp += seqInvert
}
sugg := g.Suggestions[i]
if len(sugg) > GetTermWidth() {
sugg = sugg[:GetTermWidth() - 4] + "..."
}
formatStr := "%-"+cellWidth+"s%s "
if g.tcMaxX == 1 {
formatStr = "%s%s"
}
comp += fmt.Sprintf(formatStr, fmtEscape(sugg), seqReset)
}
// Always add a newline to the group if the end if not punctuated with one
if !strings.HasSuffix(comp, "\n") {
comp += "\n"
}
// Add the equivalent of this group's size to final screen clearing.
// This is either the max allowed print size for this group, or its actual size if inferior.
if g.MaxLength < y {
rl.tcUsedY += g.MaxLength
} else {
rl.tcUsedY += y
}
return
}