Corrections + ajout de la possibilité de naviguer dans la complétion avec les touches fléchées
This commit is contained in:
parent
8694cb0d18
commit
bea7084d5b
52
README.md
52
README.md
|
@ -51,28 +51,30 @@ func main() {
|
|||
|
||||
## Raccourcis clavier <u>disponibles</u>
|
||||
|
||||
| Raccourci clavier | Action |
|
||||
| -------------------------------------- | ------------------------------------------------------------ |
|
||||
| Ctrl+A, Home | Retour au début de la saisie |
|
||||
| Ctrl+E, End | Retour à la fin de la saisie |
|
||||
| Ctrl+B, Left | Déplacement d’un caractère vers la gauche |
|
||||
| Ctrl+F, Right | Déplacement d’un caractère vers la droite |
|
||||
| Alt+B, Ctrl+Left | Déplacement d’un mot vers la gauche |
|
||||
| Alt+F, Ctrl+Right | Déplacement d’un mot vers la droite |
|
||||
| Ctrl+U | Suppression de tous les caractères du début de la ligne au curseur (curseur non compris) |
|
||||
| Ctrl+K | Suppression de tous les carctères du curseur (compris) à la fin de la ligne |
|
||||
| Ctrl+D (si saisie commencée), Del | Suppression du caractère sous le curseur |
|
||||
| Ctrl+D (si ligne vide) | Termine l’application (EOF) |
|
||||
| Ctrl+H, Backspace | Suppression du caractère avant le curseur |
|
||||
| Alt+D, Alt+Del | Suppression du prochain mot |
|
||||
| Alt+Backspace | Suppression du précédent mot |
|
||||
| Ctrl+C | Termine l’application (avec erreur) |
|
||||
| Ctrl+P, Up (si ligne vide au départ) | Remonte à l’historique précédent |
|
||||
| Ctrl+N, Down (si ligne vide au départ) | Descend dans l’historique |
|
||||
| Ctrl+R, Up (si ligne non vide) | Recherche dans l’historique (par préfixe de ligne) à partir de la fin |
|
||||
| Ctrl+S, Down (si ligne non vide) | Recherche dans l’historique (par préfixe de ligne) à partir du début |
|
||||
| Ctrl+Y | Copie le dernier élément supprimé |
|
||||
| Alt+Y | Remonte dans l’historique des éléments supprimés et les copie (implique Ctrl+Y précédemment lancé) |
|
||||
| Ctrl+G, Cancel | Annule les dernières actions temporaires (historique, recherche, copie, historique de copie) |
|
||||
| Tab | Complétion suivante |
|
||||
| Shift+Tab | Complétion précédente |
|
||||
| Raccourci clavier | Action |
|
||||
| ----------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| Ctrl+A, Home | Retour au début de la saisie |
|
||||
| Ctrl+E, End | Retour à la fin de la saisie |
|
||||
| Ctrl+B, Left | Déplacement d’un caractère vers la gauche |
|
||||
| Ctrl+F, Right | Déplacement d’un caractère vers la droite |
|
||||
| Alt+B, Ctrl+Left | Déplacement d’un mot vers la gauche |
|
||||
| Alt+F, Ctrl+Right | Déplacement d’un mot vers la droite |
|
||||
| Ctrl+U | Suppression de tous les caractères du début de la ligne au curseur (curseur non compris) |
|
||||
| Ctrl+K | Suppression de tous les carctères du curseur (compris) à la fin de la ligne |
|
||||
| Ctrl+D (si saisie commencée), Del | Suppression du caractère sous le curseur |
|
||||
| Ctrl+D (si ligne vide) | Termine l’application (EOF) |
|
||||
| Ctrl+H, Backspace | Suppression du caractère avant le curseur |
|
||||
| Alt+D, Alt+Del | Suppression du prochain mot |
|
||||
| Alt+Backspace | Suppression du précédent mot |
|
||||
| Ctrl+C | Termine l’application (avec erreur) |
|
||||
| Ctrl+P, Up (si ligne vide au départ) | Remonte à l’historique précédent |
|
||||
| Ctrl+N, Down (si ligne vide au départ) | Descend dans l’historique |
|
||||
| Ctrl+R, Up (si ligne non vide) | Recherche dans l’historique (par préfixe de ligne) à partir de la fin |
|
||||
| Ctrl+S, Down (si ligne non vide) | Recherche dans l’historique (par préfixe de ligne) à partir du début |
|
||||
| Ctrl+Y | Copie le dernier élément supprimé |
|
||||
| Alt+Y | Remonte dans l’historique des éléments supprimés et les copie (implique Ctrl+Y précédemment lancé) |
|
||||
| Ctrl+G, Cancel | Annule les dernières actions temporaires (historique, recherche, copie, historique de copie) |
|
||||
| Tab, Up (si complétion commencée) | Complétion suivante |
|
||||
| Shift+Tab, Down (si complétion commencée) | Complétion précédente |
|
||||
| Left (si complétion commencée) | Complétion à gauche |
|
||||
| Right (si complétion commencée) | Complétion à droite |
|
||||
|
|
136
completer.go
136
completer.go
|
@ -11,20 +11,28 @@ import (
|
|||
)
|
||||
|
||||
func coordToIndex(x, y, c, n int) int {
|
||||
if x < 0 || x >= c || y < 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
l, r := n/c, n%c
|
||||
switch {
|
||||
case (x < r && y >= l+1) || (x >= r && y >= l):
|
||||
return -1
|
||||
case r == 0:
|
||||
return x*l + y
|
||||
case x < r:
|
||||
return x*(l+1) + y
|
||||
case y >= l:
|
||||
return -1
|
||||
default:
|
||||
return r*(l+1) + (x-r)*l + y
|
||||
}
|
||||
}
|
||||
|
||||
func indexToCoord(i, c, n int) (x, y int) {
|
||||
if i < 0 || i >= n {
|
||||
return -1, -1
|
||||
}
|
||||
|
||||
l, r := n/c, n%c
|
||||
switch {
|
||||
case r == 0:
|
||||
|
@ -32,7 +40,7 @@ func indexToCoord(i, c, n int) (x, y int) {
|
|||
case i < r*(l+1):
|
||||
x, y = i/(l+1), i%(l+1)
|
||||
default:
|
||||
j := i - r*l
|
||||
j := i - r*(l+1)
|
||||
x, y = r+j/l, j%l
|
||||
}
|
||||
|
||||
|
@ -144,50 +152,118 @@ func (c *completer) next() bool { return c.choices.Next() && c.set() }
|
|||
|
||||
func (c *completer) move(index int) bool { return c.choices.SetCursor(index) && c.set() }
|
||||
|
||||
func (c *completer) formatChoices(currentWord string, terminalWidth, terminalHeight int) string {
|
||||
var buf strings.Builder
|
||||
l, cursor := c.choices.Len(), c.choices.Cursor()
|
||||
if l == 0 {
|
||||
return ""
|
||||
func (c *completer) gridInfo(terminalWidth, terminalHeight int) (nbChoices, colWidth, nbCols, nbLines int, choices []string) {
|
||||
nbChoices = c.choices.Len()
|
||||
if nbChoices == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var maxLen int
|
||||
choices := make([]string, l)
|
||||
for i := 0; i < l; i++ {
|
||||
var wordWidth int
|
||||
choices = make([]string, nbChoices)
|
||||
for i := 0; i < nbChoices; i++ {
|
||||
word, _ := c.choices.Index(i).Get()
|
||||
maxLen, choices[i] = max(atom.VisibleWidth(word)+1, maxLen), word
|
||||
choices[i], wordWidth = word, max(atom.VisibleWidth(word)+1, wordWidth)
|
||||
}
|
||||
|
||||
maxCol := min(max(terminalWidth/maxLen, 1), 5)
|
||||
lines := l / maxCol
|
||||
if l%maxCol > 0 {
|
||||
lines++
|
||||
nbCols = min(max(terminalWidth/wordWidth, 1), 5)
|
||||
nbLines = nbChoices / nbCols
|
||||
if nbChoices%nbCols > 0 {
|
||||
nbLines++
|
||||
}
|
||||
lineMin, lineMax := 0, lines-1
|
||||
var isPartial bool
|
||||
if isPartial = terminalHeight < lines; isPartial {
|
||||
_, cursorLine := indexToCoord(cursor, maxCol, l)
|
||||
if cursorLine < terminalHeight {
|
||||
lineMax = min(terminalHeight, lines) - 1
|
||||
|
||||
colWidth = terminalWidth / nbCols
|
||||
if colWidth > wordWidth {
|
||||
colWidth = (colWidth + wordWidth) >> 1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *completer) infoMove(terminalWidth, terminalHeight int) (nbCols, nbLines, nbChoices, x, y, r int) {
|
||||
nbChoices, _, nbCols, nbLines, _ = c.gridInfo(terminalWidth, terminalHeight)
|
||||
|
||||
if nbChoices > 0 {
|
||||
i := c.choices.Cursor()
|
||||
if i < 0 || i >= nbChoices {
|
||||
x, y = -1, -1
|
||||
} else {
|
||||
lineMin, lineMax = cursorLine+1-min(terminalHeight, lines), cursorLine
|
||||
x, y = indexToCoord(i, nbCols, nbChoices)
|
||||
r = nbChoices % nbCols
|
||||
}
|
||||
}
|
||||
|
||||
colWith := terminalWidth / maxCol
|
||||
if colWith > maxLen {
|
||||
colWith = (colWith + maxLen) >> 1
|
||||
return
|
||||
}
|
||||
|
||||
func (c *completer) left(terminalWidth, terminalHeight int) bool {
|
||||
nbCols, nbLines, nbChoices, x, y, r := c.infoMove(terminalWidth, terminalHeight)
|
||||
if nbChoices == 0 || x < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch {
|
||||
case x > 0:
|
||||
x--
|
||||
case y > 0:
|
||||
x, y = nbCols-1, y-1
|
||||
case r > 0:
|
||||
x, y = r-1, nbLines-1
|
||||
default:
|
||||
x, y = nbCols-1, nbLines-1
|
||||
}
|
||||
|
||||
i := coordToIndex(x, y, nbCols, nbChoices)
|
||||
|
||||
return i >= 0 && c.move(i)
|
||||
}
|
||||
|
||||
func (c *completer) right(terminalWidth, terminalHeight int) bool {
|
||||
nbCols, nbLines, nbChoices, x, y, r := c.infoMove(terminalWidth, terminalHeight)
|
||||
if nbChoices == 0 || x < 0 {
|
||||
return false
|
||||
}
|
||||
fmt.Println("debug0:", x, y)
|
||||
|
||||
if x < r-1 || (x < nbCols-1 && (r == 0 || y < nbLines-1)) {
|
||||
x++
|
||||
} else if y < nbLines-1 {
|
||||
x, y = 0, y+1
|
||||
} else {
|
||||
x, y = 0, 0
|
||||
}
|
||||
fmt.Println("debug1:", x, y)
|
||||
|
||||
i := coordToIndex(x, y, nbCols, nbChoices)
|
||||
|
||||
return i >= 0 && c.move(i)
|
||||
}
|
||||
|
||||
func (c *completer) formatChoices(currentWord string, terminalWidth, terminalHeight int) string {
|
||||
nbChoices, colWidth, nbCols, nbLines, choices := c.gridInfo(terminalWidth, terminalHeight)
|
||||
if nbChoices == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
cursor := c.choices.Cursor()
|
||||
lineMin, lineMax := 0, nbLines-1
|
||||
var isPartial bool
|
||||
if isPartial = terminalHeight < nbLines; isPartial {
|
||||
_, cursorLine := indexToCoord(cursor, nbCols, nbChoices)
|
||||
lineMin = max(0, cursorLine-terminalHeight+1)
|
||||
lineMax = lineMin + terminalHeight - 1
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
for y := lineMin; y <= lineMax; y++ {
|
||||
if y > 0 {
|
||||
buf.WriteRune('\n')
|
||||
}
|
||||
for x := 0; x < maxCol; x++ {
|
||||
i := coordToIndex(x, y, maxCol, l)
|
||||
for x := 0; x < nbCols; x++ {
|
||||
i := coordToIndex(x, y, nbCols, nbChoices)
|
||||
if i >= 0 {
|
||||
word := choices[i]
|
||||
realSize, displaySize := len([]rune(word)), atom.VisibleWidth(word)
|
||||
size := maxLen + realSize - displaySize
|
||||
size := colWidth + realSize - displaySize
|
||||
word = strings.TrimPrefix(format.Left(word, size), currentWord)
|
||||
if i == cursor {
|
||||
buf.WriteString(format.Apply(currentWord, "bg_l_white", "black", "bold", "underline"))
|
||||
|
@ -201,7 +277,7 @@ func (c *completer) formatChoices(currentWord string, terminalWidth, terminalHei
|
|||
}
|
||||
|
||||
if isPartial {
|
||||
position := fmt.Sprintf("lignes %d à %d (%d)", lineMin+1, lineMax+1, lines)
|
||||
position := fmt.Sprintf("lignes %d à %d (%d)", lineMin+1, lineMax+1, nbLines)
|
||||
buf.WriteRune('\n')
|
||||
buf.WriteString(format.Apply(position, "bg_l_cyan", "black"))
|
||||
}
|
||||
|
|
82
fishline.go
82
fishline.go
|
@ -113,14 +113,45 @@ func (fl *Fishline) completionNext() (ok bool) {
|
|||
return
|
||||
}
|
||||
|
||||
func (fl *Fishline) fixState() bool {
|
||||
ok1 := fl.st.RemoveSavedBuffer()
|
||||
ok2 := fl.st.RemoveHistorySearch()
|
||||
ok3 := fl.st.UnfocusHistory()
|
||||
ok4 := fl.st.UnfocusYank()
|
||||
ok5 := fl.removeCompletion()
|
||||
func (fl *Fishline) getCompletionInfos(buffer ...string) (w, h int) {
|
||||
var str string
|
||||
if len(buffer) > 0 {
|
||||
str = buffer[0]
|
||||
} else {
|
||||
str, _ = fl.st.Buffer()
|
||||
}
|
||||
|
||||
return ok1 || ok2 || ok3 || ok4 || ok5
|
||||
w, h = fl.st.Width(), fl.st.Height()-1
|
||||
hb, _, _ := atom.CursorOffset(fl.st.GetPrompt()+str, 0, 0)
|
||||
|
||||
return w, h - hb
|
||||
}
|
||||
|
||||
func (fl *Fishline) completionLeft() (ok bool) {
|
||||
completion := fl.initCompletion()
|
||||
|
||||
if ok = completion.left(fl.getCompletionInfos()); ok {
|
||||
fl.setCompletion(completion)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (fl *Fishline) completionRight() (ok bool) {
|
||||
completion := fl.initCompletion()
|
||||
|
||||
if ok = completion.right(fl.getCompletionInfos()); ok {
|
||||
fl.setCompletion(completion)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (fl *Fishline) fixState() bool {
|
||||
ok1 := fl.st.FixState()
|
||||
ok2 := fl.removeCompletion()
|
||||
|
||||
return ok1 || ok2
|
||||
}
|
||||
|
||||
func (fl *Fishline) cancel() (ok bool) {
|
||||
|
@ -286,7 +317,9 @@ func (fl *Fishline) execSequence(s atom.Sequence, isPassword bool) (fix, stop bo
|
|||
if !isPassword {
|
||||
l := fl.st.BufferLen()
|
||||
changeProposal = -1
|
||||
if l == 0 || fl.st.IsHistoryMode() {
|
||||
if fl.isCompletionMode() {
|
||||
noBeep = fl.completionPrev()
|
||||
} else if l == 0 || fl.st.IsHistoryMode() {
|
||||
noBeep = fl.historyPrev()
|
||||
} else {
|
||||
noBeep = fl.searchHistoryPrev()
|
||||
|
@ -296,20 +329,30 @@ func (fl *Fishline) execSequence(s atom.Sequence, isPassword bool) (fix, stop bo
|
|||
if !isPassword {
|
||||
l := fl.st.BufferLen()
|
||||
changeProposal = -1
|
||||
if l == 0 || fl.st.IsHistoryMode() {
|
||||
if fl.isCompletionMode() {
|
||||
noBeep = fl.completionNext()
|
||||
} else if l == 0 || fl.st.IsHistoryMode() {
|
||||
noBeep = fl.historyNext()
|
||||
} else {
|
||||
noBeep = fl.searchHistoryNext()
|
||||
}
|
||||
}
|
||||
case atom.Right: // Move right
|
||||
noBeep, fix = fl.st.Right(), true
|
||||
if !noBeep && fl.proposal != "" {
|
||||
noBeep, changeProposal = true, -1
|
||||
fl.st.SetBuffer(fl.proposal)
|
||||
if fl.isCompletionMode() && !isPassword {
|
||||
changeProposal, noBeep = -1, fl.completionRight()
|
||||
} else {
|
||||
noBeep, fix = fl.st.Right(), true
|
||||
if !noBeep && fl.proposal != "" {
|
||||
noBeep, changeProposal = true, -1
|
||||
fl.st.SetBuffer(fl.proposal)
|
||||
}
|
||||
}
|
||||
case atom.Left: // Move left
|
||||
noBeep, fix = fl.st.Left(), true
|
||||
if fl.isCompletionMode() && !isPassword {
|
||||
changeProposal, noBeep = -1, fl.completionLeft()
|
||||
} else {
|
||||
noBeep, fix = fl.st.Left(), true
|
||||
}
|
||||
case atom.Ins: // Toggle insert
|
||||
fl.st.ToggleReplace()
|
||||
noBeep = true
|
||||
|
@ -387,16 +430,11 @@ func (fl *Fishline) prompt(p string, isPassword bool) (result option.Result[stri
|
|||
output.WriteString(format.Apply(suffix, "l_black"))
|
||||
}
|
||||
if c, ok := fl.completion.Get(); ok {
|
||||
w, h := fl.st.Width(), fl.st.Height()-1
|
||||
wb := atom.VisibleWidth(fl.st.GetPrompt() + buffer + suffix)
|
||||
hb := wb / w
|
||||
if wb%w > 0 {
|
||||
hb++
|
||||
}
|
||||
choices := c.formatChoices(fl.currentWord, w, h-hb)
|
||||
w, h := fl.getCompletionInfos(buffer + suffix)
|
||||
choices := c.formatChoices(fl.currentWord, w, h)
|
||||
if choices != "" {
|
||||
output.WriteString("\n")
|
||||
output.WriteString(c.formatChoices(fl.currentWord, w, h-hb))
|
||||
output.WriteString(choices)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue