2023-10-07 19:13:39 +00:00
|
|
|
|
package atom
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"os"
|
|
|
|
|
|
|
|
|
|
"gitea.zaclys.com/bvaudour/gob/option"
|
|
|
|
|
"gitea.zaclys.com/bvaudour/gob/shell/console"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// State représente l’état d’un terminal.
|
|
|
|
|
type State struct {
|
|
|
|
|
origTerm termios
|
|
|
|
|
defaultTerm termios
|
|
|
|
|
in *input
|
|
|
|
|
buffer Buffer
|
|
|
|
|
history console.History
|
|
|
|
|
historySearch option.Option[console.History]
|
|
|
|
|
yank console.History
|
|
|
|
|
savedBuffer option.Option[*Buffer]
|
|
|
|
|
dim TerminalSize
|
|
|
|
|
prompt string
|
|
|
|
|
replace bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewState retourne un nouvel état de terminal.
|
|
|
|
|
func NewState(hist ...console.History) *State {
|
|
|
|
|
var h console.History
|
|
|
|
|
if len(hist) > 0 {
|
|
|
|
|
h = hist[0]
|
|
|
|
|
} else {
|
|
|
|
|
h = NewHistory(false)
|
|
|
|
|
}
|
|
|
|
|
st := State{
|
|
|
|
|
in: newInput(os.Stdin),
|
|
|
|
|
history: h,
|
|
|
|
|
yank: NewHistory(false),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !terminalSupported() {
|
|
|
|
|
panic("Unsupported terminal")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m := newTermios(stdin)
|
|
|
|
|
if err, ok := m.Err(); ok {
|
|
|
|
|
panic(fmt.Sprintf("Unsupported terminal: %s", err))
|
|
|
|
|
}
|
|
|
|
|
if t, ok := m.Ok(); ok {
|
|
|
|
|
st.origTerm = *t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err, ok := newTermios(stdout).Err(); ok {
|
|
|
|
|
panic(fmt.Sprintf("Unsupported terminal: %s", err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
st.origTerm.Iflag &^= icrnl | inpck | istrip | ixon
|
|
|
|
|
st.origTerm.Cflag |= cs8
|
|
|
|
|
st.origTerm.Lflag &^= echo | icanon | iexten
|
|
|
|
|
st.origTerm.Cc[vmin] = 1
|
|
|
|
|
st.origTerm.Cc[vtime] = 0
|
|
|
|
|
st.origTerm.applyMode()
|
|
|
|
|
|
|
|
|
|
var ok bool
|
|
|
|
|
|
|
|
|
|
ts := GetTerminalSize()
|
|
|
|
|
if st.dim, ok = ts.Get(); !ok {
|
|
|
|
|
panic("Unsupported terminal: unknown dimensions")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &st
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start lance l’initialisation du terminal.
|
|
|
|
|
func (st *State) Start() {
|
|
|
|
|
m, _ := newTermios(stdin).Ok()
|
|
|
|
|
st.defaultTerm = *m
|
|
|
|
|
st.defaultTerm.Lflag &^= isig
|
|
|
|
|
st.defaultTerm.applyMode()
|
|
|
|
|
st.Restart()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Restart redémarre la possibilité de saisir des caractères.
|
|
|
|
|
func (st *State) Restart() {
|
|
|
|
|
st.in.restart()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stop retourne à l’état originel du terminal.
|
|
|
|
|
func (st *State) Stop() {
|
|
|
|
|
st.defaultTerm.applyMode()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Next retourne la prochaine saisie de touche ou de combinaison de touches.
|
|
|
|
|
func (st *State) Next() option.Result[Key] {
|
|
|
|
|
return st.in.nextChar()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close retourne à l’état originel du terminal.
|
|
|
|
|
func (st *State) Close() option.Option[error] {
|
|
|
|
|
return st.origTerm.applyMode()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// InputWaiting retourne vrai si une saisie est en attente de traitement.
|
|
|
|
|
func (st *State) InputWaiting() bool {
|
|
|
|
|
return st.in.isWaiting()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetPrompt enregistre le prompt à utiiser.
|
|
|
|
|
// Si le prompt contient des caractères non imprimables, retourne une erreur.
|
|
|
|
|
func (st *State) SetPrompt(p string) (err option.Option[error]) {
|
|
|
|
|
if err := CheckUnicode(p); err.IsDefined() {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
st.prompt = p
|
|
|
|
|
SaveCursorPosition()
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Print imprime le prompt suivie de la chaîne spécifié et place
|
|
|
|
|
// le curseur à la position indiquée (commence à 0 à partir du début de la chaîne).
|
|
|
|
|
func (st *State) Print(str string, cursor int) (err option.Option[error]) {
|
|
|
|
|
if err = CheckUnicode(str); err.IsDefined() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
st.dim, _ = GetTerminalSize().Get()
|
|
|
|
|
str = st.prompt + str
|
|
|
|
|
px, py, n := CursorOffset(str, cursor+VisibleWidth(st.prompt), st.dim.Width())
|
|
|
|
|
|
|
|
|
|
RestoreCursorPosition()
|
2024-03-02 13:26:17 +00:00
|
|
|
|
ClearEndOfScreen()
|
2023-10-07 19:13:39 +00:00
|
|
|
|
|
|
|
|
|
if n > 0 {
|
|
|
|
|
NewLines(n)
|
|
|
|
|
MoveLineUp(n)
|
|
|
|
|
SaveCursorPosition()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Print(str)
|
|
|
|
|
RestoreCursorPosition()
|
|
|
|
|
|
|
|
|
|
if py > 0 {
|
|
|
|
|
MoveDown(py)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if px > 0 {
|
|
|
|
|
MoveRight(px)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return ajoute une nouvelle ligne.
|
|
|
|
|
func (st *State) Return() {
|
|
|
|
|
NewLines(1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Beep émet un bip.
|
|
|
|
|
func (st *State) Beep() {
|
|
|
|
|
Beep()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SaveBuffer enregistre le buffer actuel.
|
|
|
|
|
// Si force, force l’enregistrement du buffer même s’il y a une sauvegarde existante.
|
|
|
|
|
func (st *State) SaveBuffer(force ...bool) (ok bool) {
|
|
|
|
|
f := false
|
|
|
|
|
if len(force) > 0 {
|
|
|
|
|
f = force[0]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ok = f || !st.savedBuffer.IsDefined(); ok {
|
|
|
|
|
st.savedBuffer = option.Some(st.buffer.Clone())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RestoreBuffer restaure le buffer à partir de sa sauvegarde précédente.
|
|
|
|
|
func (st *State) RestoreBuffer() (ok bool) {
|
|
|
|
|
var sb *Buffer
|
|
|
|
|
|
|
|
|
|
if sb, ok = st.savedBuffer.Get(); ok {
|
|
|
|
|
b := sb.Clone()
|
|
|
|
|
st.buffer = *b
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RemoveSavedBuffer supprime le buffer de sauvegarde.
|
|
|
|
|
func (st *State) RemoveSavedBuffer() (ok bool) {
|
|
|
|
|
if ok = st.savedBuffer.IsDefined(); ok {
|
|
|
|
|
st.savedBuffer = option.None[*Buffer]()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RemoveHistorySearch supprime la recherche d’historique.
|
|
|
|
|
func (st *State) RemoveHistorySearch() (ok bool) {
|
|
|
|
|
if ok = st.historySearch.IsDefined(); ok {
|
|
|
|
|
st.historySearch = option.None[console.History]()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UnfocusHistory supprime le pointage vers un élément de l’historique.
|
|
|
|
|
func (st *State) UnfocusHistory() (ok bool) {
|
|
|
|
|
if ok = console.IsFocused(st.history); ok {
|
|
|
|
|
console.Unfocus(st.history)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UnfocusYank supprime le pointage vers un élément copiable.
|
|
|
|
|
func (st *State) UnfocusYank() (ok bool) {
|
|
|
|
|
if ok = console.IsFocused(st.yank); ok {
|
|
|
|
|
console.Unfocus(st.yank)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FixState s’assure que le buffer est désormais fixé.
|
|
|
|
|
func (st *State) FixState() (ok bool) {
|
|
|
|
|
ok1 := st.RemoveSavedBuffer()
|
|
|
|
|
ok2 := st.RemoveHistorySearch()
|
|
|
|
|
ok3 := st.UnfocusHistory()
|
|
|
|
|
ok4 := st.UnfocusYank()
|
|
|
|
|
|
|
|
|
|
return ok1 || ok2 || ok3 || ok4
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsHistoryMode retourne vrai si on est en mode parcours de l’historique.
|
|
|
|
|
func (st *State) IsHistoryMode() bool {
|
|
|
|
|
return console.IsFocused(st.history)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsYankMode retourne vrai si on est en mode collage.
|
|
|
|
|
func (st *State) IsYankMode() bool {
|
|
|
|
|
return console.IsFocused(st.yank)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsHistorySearchMode retourne vrai si on est en mode recherche dans l’historique.
|
|
|
|
|
func (st *State) IsHistorySearchMode() bool {
|
|
|
|
|
return st.historySearch.IsDefined()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsTmpMode retourne vrai si le buffer peut être restauré à un état précédent.
|
|
|
|
|
func (st *State) IsTmpMode() bool {
|
|
|
|
|
return st.savedBuffer.IsDefined()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsReplaceMode retourne vrai si on est en mode remplacement.
|
|
|
|
|
func (st *State) IsReplaceMode() (ok bool) {
|
|
|
|
|
return st.replace
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LoadHistory charge l’historique à partir d’un fichier.
|
|
|
|
|
func (st *State) LoadHistory(r io.Reader) option.Result[int] {
|
|
|
|
|
return st.history.Read(r)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SaveHistory persiste l’historique dans un fichier.
|
|
|
|
|
func (st *State) SaveHistory(w io.Writer) option.Result[int] {
|
|
|
|
|
return st.history.Write(w)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ClearHistory efface l’historique.
|
|
|
|
|
func (st *State) ClearHistory() {
|
|
|
|
|
st.history.Clear()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AppendHistory ajoute une entrée dans l’historique.
|
|
|
|
|
func (st *State) AppendHistory(line string) {
|
|
|
|
|
st.history.Append(line)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AppendYank ajoute une entrée dans la liste des éléments copiables.
|
|
|
|
|
func (st *State) AppendYank(yank string) {
|
|
|
|
|
st.yank.Append(yank)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Buffer retourne la représentation du buffer et la position du curseur dans le buffer.
|
|
|
|
|
func (st *State) Buffer() (string, int) {
|
|
|
|
|
return st.buffer.String(), st.buffer.Cursor()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BufferLen retourne le nombre de caractères du buffer.
|
|
|
|
|
func (st *State) BufferLen() int {
|
|
|
|
|
return st.buffer.Len()
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-02 13:26:17 +00:00
|
|
|
|
// SavedBuffer retouren la représentation du buffer sauvegardé, si celui-ci existe.
|
|
|
|
|
func (st *State) SavedBuffer() (out option.Option[string]) {
|
|
|
|
|
if buf, ok := st.savedBuffer.Get(); ok {
|
|
|
|
|
out = option.Some(buf.String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-07 19:13:39 +00:00
|
|
|
|
// Width retourne la largeur du terminal.
|
|
|
|
|
func (st *State) Width() int {
|
|
|
|
|
return st.dim.Width()
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-02 13:26:17 +00:00
|
|
|
|
// Height retourn la hauteur du terminal.
|
|
|
|
|
func (st *State) Height() int {
|
|
|
|
|
return st.dim.Height()
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-07 19:13:39 +00:00
|
|
|
|
// ToggleReplace se place en mode remplacement si on était on mode insertion
|
|
|
|
|
// et vice-versa.
|
|
|
|
|
func (st *State) ToggleReplace() {
|
|
|
|
|
st.replace = !st.replace
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Insert insert (ou remplace, suivant le mode actuel) les caractères donnés
|
|
|
|
|
// dans le buffer.
|
|
|
|
|
func (st *State) Insert(data ...Char) (ok bool) {
|
|
|
|
|
if ok = len(data) > 0; ok {
|
|
|
|
|
if st.replace {
|
|
|
|
|
st.buffer.Replace(data...)
|
|
|
|
|
} else {
|
|
|
|
|
st.buffer.Insert(data...)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Transpose transpose le caractère sous le curseur avec le caractère précédent.
|
|
|
|
|
func (st *State) Transpose() bool {
|
|
|
|
|
return st.buffer.Transpose()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Left déplace le curseur vers la gauche.
|
|
|
|
|
func (st *State) Left() bool {
|
|
|
|
|
return st.buffer.Left()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Right déplace le curseur vers la droite.
|
|
|
|
|
func (st *State) Right() bool {
|
|
|
|
|
return st.buffer.Right()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Begin déplace le curseur au début du buffer.
|
|
|
|
|
func (st *State) Begin() bool {
|
|
|
|
|
return st.buffer.Begin()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// End déplace le curseur à la fin du buffer.
|
|
|
|
|
func (st *State) End() bool {
|
|
|
|
|
return st.buffer.End()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PrevWord déplace le curseur au début du mot précédent.
|
|
|
|
|
func (st *State) PrevWord() bool {
|
|
|
|
|
return st.buffer.PrevWord()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NextWord déplace le curseur au début du mot suivant.
|
|
|
|
|
func (st *State) NextWord() bool {
|
|
|
|
|
return st.buffer.NextWord()
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-02 13:26:17 +00:00
|
|
|
|
// SetCursor déplace le curseur à la position donnée
|
|
|
|
|
func (st *State) SetCursor(cursor int) bool {
|
|
|
|
|
return st.buffer.SetCursor(cursor)
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-07 19:13:39 +00:00
|
|
|
|
func (st *State) rem(cb func() option.Option[string]) (ok bool) {
|
|
|
|
|
var yank string
|
|
|
|
|
if yank, ok = cb().Get(); ok {
|
|
|
|
|
st.AppendYank(yank)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Back supprime le caractère situé avant le curseur.
|
|
|
|
|
func (st *State) Back() bool {
|
|
|
|
|
return st.rem(st.buffer.Back)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Del supprime le caractère sous le curseur.
|
|
|
|
|
func (st *State) Del() bool {
|
|
|
|
|
return st.rem(st.buffer.Del)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BackWord supprime le mot avant le curseur.
|
|
|
|
|
func (st *State) BackWord() bool {
|
|
|
|
|
return st.rem(st.buffer.BackWord)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DelWord supprime le mot après le curseur.
|
|
|
|
|
func (st *State) DelWord() bool {
|
|
|
|
|
return st.rem(st.buffer.DelWord)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RemoveBegin supprime les caractères avant le curseur.
|
|
|
|
|
func (st *State) RemoveBegin() bool {
|
|
|
|
|
return st.rem(st.buffer.RemoveBegin)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RemoveEnd supprime les caractères après le curseur, curseur compris.
|
|
|
|
|
func (st *State) RemoveEnd() bool {
|
|
|
|
|
return st.rem(st.buffer.RemoveEnd)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear efface le buffer.
|
|
|
|
|
func (st *State) Clear() bool {
|
|
|
|
|
return st.buffer.Clear().IsDefined()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetBuffer réinitialise le buffer avec la chaîne donnée.
|
|
|
|
|
func (st *State) SetBuffer(value string) bool {
|
|
|
|
|
st.buffer.Clear()
|
|
|
|
|
st.buffer.Insert([]rune(value)...)
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// YankPrev colle dans le buffer le précédent élément copié.
|
|
|
|
|
func (st *State) YankPrev() (ok bool) {
|
|
|
|
|
if ok = st.yank.Prev() && st.RestoreBuffer(); ok {
|
|
|
|
|
st.UnfocusHistory()
|
|
|
|
|
st.RemoveHistorySearch()
|
|
|
|
|
|
|
|
|
|
var yank string
|
|
|
|
|
if yank, ok = console.FocusedElement(st.yank).Get(); ok {
|
|
|
|
|
ok = st.Insert([]rune(yank)...)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Yank colle le dernier élément copié.
|
|
|
|
|
func (st *State) Yank() (ok bool) {
|
|
|
|
|
st.yank.SetCursor(st.yank.Len())
|
|
|
|
|
|
|
|
|
|
if ok = st.yank.Prev(); ok {
|
|
|
|
|
st.UnfocusHistory()
|
|
|
|
|
st.RemoveHistorySearch()
|
|
|
|
|
st.SaveBuffer()
|
|
|
|
|
|
|
|
|
|
var yank string
|
|
|
|
|
if yank, ok = console.FocusedElement(st.yank).Get(); ok {
|
|
|
|
|
ok = st.Insert([]rune(yank)...)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HistoryPrev remonte dans l’historique.
|
|
|
|
|
func (st *State) HistoryPrev() (ok bool) {
|
|
|
|
|
if ok = st.history.Prev(); !ok {
|
|
|
|
|
ok = st.history.SetCursor(0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
|
st.UnfocusYank()
|
|
|
|
|
st.RemoveHistorySearch()
|
|
|
|
|
st.SaveBuffer()
|
|
|
|
|
|
|
|
|
|
var h string
|
|
|
|
|
if h, ok = console.FocusedElement(st.history).Get(); ok {
|
|
|
|
|
ok = st.SetBuffer(h)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HistoryNext redescend dans l’historique.
|
|
|
|
|
func (st *State) HistoryNext() (ok bool) {
|
|
|
|
|
if ok = st.history.Next(); ok {
|
|
|
|
|
st.UnfocusYank()
|
|
|
|
|
st.RemoveHistorySearch()
|
|
|
|
|
st.SaveBuffer()
|
|
|
|
|
|
|
|
|
|
var h string
|
|
|
|
|
h, ok = console.FocusedElement(st.history).Get()
|
|
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
|
ok = st.SetBuffer(h)
|
|
|
|
|
} else {
|
|
|
|
|
ok = st.RestoreBuffer()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-02 13:26:17 +00:00
|
|
|
|
// GetProposal retourne une suggestion de saisie pour la saisie en cours
|
|
|
|
|
// basée sur l’historique.
|
|
|
|
|
func (st *State) GetProposal(insensitive ...bool) (proposal string) {
|
|
|
|
|
candidates := console.HistoryFilterPrefix(st.history, st.buffer.String(), insensitive...)
|
|
|
|
|
if l := len(candidates); l > 0 {
|
|
|
|
|
proposal = candidates[l-1]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SearchMotiveNext remonte dans l’historique de recherche.
|
|
|
|
|
// L’historique de recherche est initialisée avec le motif du buffer.
|
|
|
|
|
func (st *State) SearchMotiveNext(insensitive ...bool) (ok bool) {
|
|
|
|
|
var hs console.History
|
|
|
|
|
|
|
|
|
|
if hs, ok = st.historySearch.Get(); !ok {
|
|
|
|
|
motive := st.buffer.String()
|
|
|
|
|
hs = NewHistory(false)
|
|
|
|
|
candidates := console.HistoryFilterMotive(st.history, motive, insensitive...)
|
|
|
|
|
|
|
|
|
|
for _, h := range candidates {
|
|
|
|
|
hs.Append(h)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hs.SetCursor(-1)
|
|
|
|
|
st.historySearch = option.Some(hs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ok = hs.Next(); ok {
|
|
|
|
|
st.UnfocusYank()
|
|
|
|
|
st.UnfocusHistory()
|
|
|
|
|
st.SaveBuffer()
|
|
|
|
|
|
|
|
|
|
var h string
|
|
|
|
|
if h, ok = console.FocusedElement(hs).Get(); ok {
|
|
|
|
|
st.SetBuffer(h)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SearchMotivePrev redescend dans l’historique de recherche.
|
|
|
|
|
// L’historique de recherche est initialisée avec le motif du buffer.
|
|
|
|
|
func (st *State) SearchMotivePrev(insensitive ...bool) (ok bool) {
|
|
|
|
|
var hs console.History
|
|
|
|
|
|
|
|
|
|
if hs, ok = st.historySearch.Get(); !ok {
|
|
|
|
|
prefix := st.buffer.String()
|
|
|
|
|
hs = NewHistory(false)
|
|
|
|
|
candidates := console.HistoryFilterMotive(st.history, prefix, insensitive...)
|
|
|
|
|
|
|
|
|
|
for _, h := range candidates {
|
|
|
|
|
hs.Append(h)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
st.historySearch = option.Some(hs)
|
|
|
|
|
}
|
|
|
|
|
if ok = hs.Prev(); ok {
|
|
|
|
|
st.UnfocusYank()
|
|
|
|
|
st.UnfocusHistory()
|
|
|
|
|
st.SaveBuffer()
|
|
|
|
|
|
|
|
|
|
var h string
|
|
|
|
|
if h, ok = console.FocusedElement(hs).Get(); ok {
|
|
|
|
|
st.SetBuffer(h)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-07 19:13:39 +00:00
|
|
|
|
// SearchHistoryNext remonte dans l’historique de recherche.
|
|
|
|
|
// L’historique de recherche est initialisée avec le préfixe du buffer.
|
|
|
|
|
func (st *State) SearchHistoryNext(insensitive ...bool) (ok bool) {
|
|
|
|
|
var hs console.History
|
|
|
|
|
|
|
|
|
|
if hs, ok = st.historySearch.Get(); !ok {
|
|
|
|
|
prefix := st.buffer.String()
|
|
|
|
|
hs = NewHistory(false)
|
|
|
|
|
candidates := console.HistoryFilterPrefix(st.history, prefix, insensitive...)
|
|
|
|
|
|
|
|
|
|
for _, h := range candidates {
|
|
|
|
|
hs.Append(h)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hs.SetCursor(-1)
|
|
|
|
|
st.historySearch = option.Some(hs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ok = hs.Next(); ok {
|
|
|
|
|
st.UnfocusYank()
|
|
|
|
|
st.UnfocusHistory()
|
|
|
|
|
st.SaveBuffer()
|
|
|
|
|
|
|
|
|
|
var h string
|
|
|
|
|
if h, ok = console.FocusedElement(hs).Get(); ok {
|
|
|
|
|
st.SetBuffer(h)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SearchHistoryPrev redescend dans l’historique de recherche.
|
|
|
|
|
// L’historique de recherche est initialisée avec le préfixe du buffer.
|
|
|
|
|
func (st *State) SearchHistoryPrev(insensitive ...bool) (ok bool) {
|
|
|
|
|
var hs console.History
|
|
|
|
|
|
|
|
|
|
if hs, ok = st.historySearch.Get(); !ok {
|
|
|
|
|
prefix := st.buffer.String()
|
|
|
|
|
hs = NewHistory(false)
|
|
|
|
|
candidates := console.HistoryFilterPrefix(st.history, prefix, insensitive...)
|
|
|
|
|
|
|
|
|
|
for _, h := range candidates {
|
|
|
|
|
hs.Append(h)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
st.historySearch = option.Some(hs)
|
|
|
|
|
}
|
|
|
|
|
if ok = hs.Prev(); ok {
|
|
|
|
|
st.UnfocusYank()
|
|
|
|
|
st.UnfocusHistory()
|
|
|
|
|
st.SaveBuffer()
|
|
|
|
|
|
|
|
|
|
var h string
|
|
|
|
|
if h, ok = console.FocusedElement(hs).Get(); ok {
|
|
|
|
|
st.SetBuffer(h)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cancel annule l’historique, la recherche ou la copie en cours.
|
|
|
|
|
func (st *State) Cancel() (ok bool) {
|
|
|
|
|
var sb *Buffer
|
|
|
|
|
if sb, ok = st.savedBuffer.Get(); ok {
|
|
|
|
|
b := sb.Clone()
|
|
|
|
|
st.buffer = *b
|
|
|
|
|
st.FixState()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
2024-03-02 13:26:17 +00:00
|
|
|
|
|
|
|
|
|
// GetPrompt retourne le prompt configuré.
|
|
|
|
|
func (st *State) GetPrompt() string {
|
|
|
|
|
return st.prompt
|
|
|
|
|
}
|