2024-03-02 14:05:33 +00:00
|
|
|
|
package gfishline
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"os"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"gitea.zaclys.com/bvaudour/gob/format"
|
|
|
|
|
"gitea.zaclys.com/bvaudour/gob/option"
|
|
|
|
|
"gitea.zaclys.com/bvaudour/gob/shell/console/atom"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Fishline struct {
|
|
|
|
|
st *atom.State
|
|
|
|
|
complete option.Option[CompleteFunc]
|
|
|
|
|
format option.Option[FormatFunc]
|
|
|
|
|
commands []string
|
|
|
|
|
options []string
|
|
|
|
|
proposal string
|
|
|
|
|
currentWord string
|
|
|
|
|
completion option.Option[*completer]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func New() *Fishline {
|
|
|
|
|
return &Fishline{
|
|
|
|
|
st: atom.NewState(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) SetCompleter(cb CompleteFunc) {
|
|
|
|
|
if cb == nil {
|
|
|
|
|
fl.complete = option.None[CompleteFunc]()
|
|
|
|
|
} else {
|
|
|
|
|
fl.complete = option.Some(cb)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) SetFormater(cb FormatFunc) {
|
|
|
|
|
if cb == nil {
|
|
|
|
|
fl.format = option.None[FormatFunc]()
|
|
|
|
|
} else {
|
|
|
|
|
fl.format = option.Some(cb)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) SetCommands(commands ...string) {
|
|
|
|
|
fl.commands = commands
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) SetOptions(options ...string) {
|
|
|
|
|
fl.options = options
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) isCompletionMode() bool {
|
|
|
|
|
return fl.completion.IsDefined()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) removeCompletion() (ok bool) {
|
|
|
|
|
if ok = fl.completion.IsDefined(); ok {
|
|
|
|
|
fl.completion = option.None[*completer]()
|
|
|
|
|
fl.currentWord = ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) initCompletion() (completion *completer) {
|
|
|
|
|
var ok bool
|
|
|
|
|
|
|
|
|
|
if completion, ok = fl.completion.Get(); !ok {
|
|
|
|
|
buffer, cursor := fl.st.Buffer()
|
|
|
|
|
complete, exists := fl.complete.Get()
|
|
|
|
|
if !exists {
|
|
|
|
|
complete = DefaultCompletion(fl.commands, fl.options)
|
|
|
|
|
}
|
|
|
|
|
completion = newCompleter(buffer, cursor, complete)
|
|
|
|
|
fl.completion = option.Some(completion)
|
|
|
|
|
if !completion.needAdd && completion.wordIndex >= 0 && completion.wordIndex < len(completion.words) {
|
|
|
|
|
fl.currentWord = completion.words[completion.wordIndex]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return completion
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) setCompletion(completion *completer) {
|
|
|
|
|
fl.st.UnfocusYank()
|
|
|
|
|
fl.st.UnfocusHistory()
|
|
|
|
|
fl.st.SaveBuffer()
|
|
|
|
|
fl.st.RemoveHistorySearch()
|
|
|
|
|
|
|
|
|
|
buffer, cursor := completion.buffer()
|
|
|
|
|
fl.st.SetBuffer(buffer)
|
|
|
|
|
fl.st.SetCursor(cursor)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) completionPrev() (ok bool) {
|
|
|
|
|
completion := fl.initCompletion()
|
|
|
|
|
|
|
|
|
|
if ok = completion.prev(); ok {
|
|
|
|
|
fl.setCompletion(completion)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) completionNext() (ok bool) {
|
|
|
|
|
completion := fl.initCompletion()
|
|
|
|
|
|
|
|
|
|
if ok = completion.next(); ok {
|
|
|
|
|
fl.setCompletion(completion)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-02 20:19:12 +00:00
|
|
|
|
func (fl *Fishline) getCompletionInfos(buffer ...string) (w, h int) {
|
|
|
|
|
var str string
|
|
|
|
|
if len(buffer) > 0 {
|
|
|
|
|
str = buffer[0]
|
|
|
|
|
} else {
|
|
|
|
|
str, _ = fl.st.Buffer()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-02 14:05:33 +00:00
|
|
|
|
func (fl *Fishline) fixState() bool {
|
2024-03-02 20:19:12 +00:00
|
|
|
|
ok1 := fl.st.FixState()
|
|
|
|
|
ok2 := fl.removeCompletion()
|
2024-03-02 14:05:33 +00:00
|
|
|
|
|
2024-03-02 20:19:12 +00:00
|
|
|
|
return ok1 || ok2
|
2024-03-02 14:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) cancel() (ok bool) {
|
|
|
|
|
if ok = fl.st.Cancel(); ok {
|
|
|
|
|
fl.removeCompletion()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) yankPrev() (ok bool) {
|
|
|
|
|
if ok = fl.st.YankPrev(); ok {
|
|
|
|
|
fl.removeCompletion()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) yank() (ok bool) {
|
|
|
|
|
if ok = fl.st.Yank(); ok {
|
|
|
|
|
fl.removeCompletion()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) historyPrev() (ok bool) {
|
|
|
|
|
if ok = fl.st.HistoryPrev(); ok {
|
|
|
|
|
fl.removeCompletion()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) historyNext() (ok bool) {
|
|
|
|
|
if ok = fl.st.HistoryNext(); ok {
|
|
|
|
|
fl.removeCompletion()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) searchHistoryPrev() (ok bool) {
|
|
|
|
|
if ok = fl.st.SearchMotivePrev(); ok {
|
|
|
|
|
fl.removeCompletion()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) searchHistoryNext() (ok bool) {
|
|
|
|
|
if ok = fl.st.SearchMotiveNext(); ok {
|
|
|
|
|
fl.removeCompletion()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) execChar(r atom.Char, isPassword bool) (fix, stop bool) {
|
|
|
|
|
if stop = r == '\n' || r == '\r'; stop {
|
|
|
|
|
sp := newSplitter(fl.st.Buffer())
|
|
|
|
|
if stop = sp.isComplete(); stop {
|
|
|
|
|
fix, fl.proposal = true, ""
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if r != atom.Tab && (r == '\n' || atom.IsPrintable(r)) {
|
|
|
|
|
fl.st.Insert(r)
|
|
|
|
|
fix = true
|
|
|
|
|
if !isPassword {
|
|
|
|
|
fl.proposal = fl.st.GetProposal()
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var noBeep bool
|
|
|
|
|
var changeProposal int
|
|
|
|
|
switch r {
|
|
|
|
|
case atom.Bs, atom.C_H: // Back
|
|
|
|
|
noBeep, fix, changeProposal = fl.st.Back(), true, 1
|
|
|
|
|
case atom.C_W: // Back word
|
|
|
|
|
noBeep, fix, changeProposal = fl.st.BackWord(), true, 1
|
|
|
|
|
case atom.C_D: // Del or reset line
|
|
|
|
|
l := fl.st.BufferLen()
|
|
|
|
|
if l == 0 {
|
|
|
|
|
fl.st.Return()
|
|
|
|
|
os.Exit(0)
|
|
|
|
|
} else {
|
|
|
|
|
noBeep, fix, changeProposal = fl.st.Del(), true, 1
|
|
|
|
|
fl.st.Restart()
|
|
|
|
|
}
|
|
|
|
|
case atom.C_U: // Remove BOL
|
|
|
|
|
noBeep, fix, changeProposal = fl.st.RemoveBegin(), true, 1
|
|
|
|
|
case atom.C_K: // Remove EOL
|
|
|
|
|
noBeep, fix, changeProposal = fl.st.RemoveEnd(), true, 1
|
|
|
|
|
case atom.C_A: // Move to BOL
|
|
|
|
|
noBeep, fix = fl.st.Begin(), true
|
|
|
|
|
fl.proposal = fl.st.GetProposal()
|
|
|
|
|
case atom.C_E: // Move to EOL
|
|
|
|
|
noBeep, fix = fl.st.End(), true
|
|
|
|
|
if !noBeep && fl.proposal != "" {
|
|
|
|
|
noBeep, changeProposal = true, -1
|
|
|
|
|
fl.st.SetBuffer(fl.proposal)
|
|
|
|
|
}
|
|
|
|
|
case atom.C_B: // Move left
|
|
|
|
|
noBeep, fix = fl.st.Left(), true
|
|
|
|
|
case atom.C_F: // Move right
|
|
|
|
|
noBeep, fix = fl.st.Right(), true
|
|
|
|
|
if !noBeep && fl.proposal != "" {
|
|
|
|
|
noBeep, changeProposal = true, -1
|
|
|
|
|
fl.st.SetBuffer(fl.proposal)
|
|
|
|
|
}
|
|
|
|
|
case atom.C_T: // Transpose
|
|
|
|
|
noBeep, fix, changeProposal = fl.st.Transpose(), true, 1
|
|
|
|
|
case atom.C_P: // Previous history
|
|
|
|
|
if !isPassword {
|
|
|
|
|
noBeep, changeProposal = fl.historyPrev(), -1
|
|
|
|
|
}
|
|
|
|
|
case atom.C_N: // Next history
|
|
|
|
|
if !isPassword {
|
|
|
|
|
noBeep, changeProposal = fl.historyNext(), -1
|
|
|
|
|
}
|
|
|
|
|
case atom.C_S: // Search history
|
|
|
|
|
if !isPassword {
|
|
|
|
|
noBeep, changeProposal = fl.searchHistoryNext(), -1
|
|
|
|
|
}
|
|
|
|
|
case atom.C_R: // Reverse search history
|
|
|
|
|
if !isPassword {
|
|
|
|
|
noBeep, changeProposal = fl.searchHistoryPrev(), -1
|
|
|
|
|
}
|
|
|
|
|
case atom.C_G, atom.Esc: // Cancel history
|
|
|
|
|
noBeep, fix, changeProposal = fl.cancel(), true, 1
|
|
|
|
|
case atom.C_Y: // Yank
|
|
|
|
|
noBeep, changeProposal = fl.yank(), 1
|
|
|
|
|
case atom.C_C: // Emergency stop
|
|
|
|
|
fl.st.Return()
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
case atom.Tab:
|
|
|
|
|
if !isPassword {
|
|
|
|
|
noBeep, changeProposal = fl.completionNext(), 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !noBeep {
|
|
|
|
|
fl.st.Beep()
|
|
|
|
|
} else {
|
|
|
|
|
if changeProposal > 0 {
|
|
|
|
|
fl.proposal = fl.st.GetProposal()
|
|
|
|
|
} else if changeProposal < 0 {
|
|
|
|
|
fl.proposal = ""
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) execSequence(s atom.Sequence, isPassword bool) (fix, stop bool) {
|
|
|
|
|
var noBeep bool
|
|
|
|
|
var changeProposal int
|
|
|
|
|
switch s {
|
|
|
|
|
case atom.Up: // Previous history
|
|
|
|
|
if !isPassword {
|
|
|
|
|
l := fl.st.BufferLen()
|
|
|
|
|
changeProposal = -1
|
2024-03-02 20:19:12 +00:00
|
|
|
|
if fl.isCompletionMode() {
|
|
|
|
|
noBeep = fl.completionPrev()
|
|
|
|
|
} else if l == 0 || fl.st.IsHistoryMode() {
|
2024-03-02 14:05:33 +00:00
|
|
|
|
noBeep = fl.historyPrev()
|
|
|
|
|
} else {
|
|
|
|
|
noBeep = fl.searchHistoryPrev()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case atom.Down: // Next history
|
|
|
|
|
if !isPassword {
|
|
|
|
|
l := fl.st.BufferLen()
|
|
|
|
|
changeProposal = -1
|
2024-03-02 20:19:12 +00:00
|
|
|
|
if fl.isCompletionMode() {
|
|
|
|
|
noBeep = fl.completionNext()
|
|
|
|
|
} else if l == 0 || fl.st.IsHistoryMode() {
|
2024-03-02 14:05:33 +00:00
|
|
|
|
noBeep = fl.historyNext()
|
|
|
|
|
} else {
|
|
|
|
|
noBeep = fl.searchHistoryNext()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case atom.Right: // Move right
|
2024-03-02 20:19:12 +00:00
|
|
|
|
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)
|
|
|
|
|
}
|
2024-03-02 14:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
case atom.Left: // Move left
|
2024-03-02 20:19:12 +00:00
|
|
|
|
if fl.isCompletionMode() && !isPassword {
|
|
|
|
|
changeProposal, noBeep = -1, fl.completionLeft()
|
|
|
|
|
} else {
|
|
|
|
|
noBeep, fix = fl.st.Left(), true
|
|
|
|
|
}
|
2024-03-02 14:05:33 +00:00
|
|
|
|
case atom.Ins: // Toggle insert
|
|
|
|
|
fl.st.ToggleReplace()
|
|
|
|
|
noBeep = true
|
|
|
|
|
case atom.Del: // Delete under cursor
|
|
|
|
|
noBeep, fix, changeProposal = fl.st.Del(), true, 1
|
|
|
|
|
case atom.Home: // Move BOL
|
|
|
|
|
noBeep, fix = fl.st.Begin(), true
|
|
|
|
|
case atom.End: // Move EOL
|
|
|
|
|
noBeep, fix = fl.st.End(), true
|
|
|
|
|
if !noBeep && fl.proposal != "" {
|
|
|
|
|
noBeep, changeProposal = true, -1
|
|
|
|
|
fl.st.SetBuffer(fl.proposal)
|
|
|
|
|
}
|
|
|
|
|
case atom.A_Bs: // Delete begin of word
|
|
|
|
|
noBeep, fix, changeProposal = fl.st.BackWord(), true, 1
|
|
|
|
|
case atom.A_D, atom.A_Del: // Delete end of word
|
|
|
|
|
noBeep, fix, changeProposal = fl.st.DelWord(), true, 1
|
|
|
|
|
case atom.C_Right, atom.A_F: // Next word
|
|
|
|
|
noBeep, fix = fl.st.NextWord(), true
|
|
|
|
|
case atom.C_Left, atom.A_B: // Begin of word
|
|
|
|
|
noBeep, fix = fl.st.PrevWord(), true
|
|
|
|
|
case atom.A_Y: // Previous yank
|
|
|
|
|
noBeep, changeProposal = fl.yankPrev(), 1
|
|
|
|
|
case atom.S_Tab:
|
|
|
|
|
noBeep, changeProposal = fl.completionPrev(), 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !noBeep {
|
|
|
|
|
fl.st.Beep()
|
|
|
|
|
} else {
|
|
|
|
|
if changeProposal > 0 {
|
|
|
|
|
fl.proposal = fl.st.GetProposal()
|
|
|
|
|
} else if changeProposal < 0 {
|
|
|
|
|
fl.proposal = ""
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fl *Fishline) prompt(p string, isPassword bool) (result option.Result[string]) {
|
|
|
|
|
if err, ok := fl.st.SetPrompt(p).Get(); ok {
|
|
|
|
|
return option.Err[string](err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fl.st.Start()
|
|
|
|
|
defer fl.st.Stop()
|
|
|
|
|
|
|
|
|
|
var refresh func() option.Option[error]
|
|
|
|
|
if isPassword {
|
|
|
|
|
refresh = func() option.Option[error] { return fl.st.Print("", 0) }
|
|
|
|
|
} else {
|
|
|
|
|
refresh = func() option.Option[error] {
|
|
|
|
|
buffer, cursor := fl.st.Buffer()
|
|
|
|
|
var output strings.Builder
|
|
|
|
|
if fl.st.IsHistorySearchMode() {
|
|
|
|
|
savedBuffer, _ := fl.st.SavedBuffer().Get()
|
|
|
|
|
if i := strings.Index(buffer, savedBuffer); i >= 0 {
|
|
|
|
|
output.WriteString(buffer[:i])
|
|
|
|
|
output.WriteString(format.Apply(savedBuffer, "bg_l_black"))
|
|
|
|
|
output.WriteString(buffer[i+len(savedBuffer):])
|
|
|
|
|
} else {
|
|
|
|
|
output.WriteString(buffer)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cb, ok := fl.format.Get()
|
|
|
|
|
if !ok {
|
|
|
|
|
cb = DefaultFormat(fl.commands, fl.options)
|
|
|
|
|
}
|
|
|
|
|
f := newFormater(buffer, cursor, cb)
|
|
|
|
|
output.WriteString(f.join())
|
|
|
|
|
var suffix string
|
|
|
|
|
if fl.proposal != buffer && strings.HasPrefix(fl.proposal, buffer) {
|
|
|
|
|
suffix = strings.TrimPrefix(fl.proposal, buffer)
|
|
|
|
|
output.WriteString(format.Apply(suffix, "l_black"))
|
|
|
|
|
}
|
|
|
|
|
if c, ok := fl.completion.Get(); ok {
|
2024-03-02 20:19:12 +00:00
|
|
|
|
w, h := fl.getCompletionInfos(buffer + suffix)
|
|
|
|
|
choices := c.formatChoices(fl.currentWord, w, h)
|
2024-03-02 14:05:33 +00:00
|
|
|
|
if choices != "" {
|
|
|
|
|
output.WriteString("\n")
|
2024-03-02 20:19:12 +00:00
|
|
|
|
output.WriteString(choices)
|
2024-03-02 14:05:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//return option.None[error]()
|
|
|
|
|
return fl.st.Print(output.String(), cursor)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
refresh()
|
|
|
|
|
|
|
|
|
|
var fix, stop, isErr bool
|
|
|
|
|
for {
|
|
|
|
|
n := fl.st.Next()
|
|
|
|
|
if err, ok := n.Err(); ok {
|
|
|
|
|
result, isErr = option.Err[string](err), true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key, _ := n.Ok()
|
|
|
|
|
if s, ok := key.Sequence(); ok {
|
|
|
|
|
fix, stop = fl.execSequence(s, isPassword)
|
|
|
|
|
} else if c, ok := key.Char(); ok {
|
|
|
|
|
fix, stop = fl.execChar(c, isPassword)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if fix {
|
|
|
|
|
fl.fixState()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := refresh()
|
|
|
|
|
if e, ok := err.Get(); ok {
|
|
|
|
|
result, isErr = option.Err[string](e), true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if stop {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !isErr {
|
|
|
|
|
r, _ := fl.st.Buffer()
|
|
|
|
|
result = option.Ok(r)
|
|
|
|
|
if !isPassword && len(r) > 0 {
|
|
|
|
|
fl.st.AppendHistory(r)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fl.st.Clear()
|
|
|
|
|
fl.fixState()
|
|
|
|
|
fl.st.Return()
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prompt retourne la chaîne saisie.
|
|
|
|
|
func (fl *Fishline) Prompt(p string) option.Result[string] { return fl.prompt(p, false) }
|
|
|
|
|
|
|
|
|
|
// PromptPassword agit comme Prompt mais n’affiche pas à l’écran ce qui est saisi.
|
|
|
|
|
func (fl *Fishline) PromptPassword(p string) option.Result[string] { return fl.prompt(p, true) }
|
|
|
|
|
|
|
|
|
|
// Close ferme proprement le Readline.
|
|
|
|
|
func (fl *Fishline) Close() option.Option[error] { return fl.st.Close() }
|