mirror of
https://github.com/quitesimpleorg/hs9001.git
synced 2025-01-08 10:23:43 +01:00
137 lines
3.2 KiB
Go
137 lines
3.2 KiB
Go
package liner
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"sync"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
type HistoryProvider interface {
|
|
ReadHistory(r io.Reader) (num int, err error)
|
|
WriteHistory(w io.Writer) (num int, err error)
|
|
AppendHistory(item string)
|
|
ClearHistory()
|
|
GetHistoryByPrefix(prefix string) (ph []string)
|
|
GetHistoryByPattern(pattern string) (ph []string, pos []int)
|
|
RLock()
|
|
RUnlock()
|
|
}
|
|
|
|
type defaultHistoryProvider struct {
|
|
history []string
|
|
historyMutex sync.RWMutex
|
|
}
|
|
|
|
// RUnlock unlocks history for reading
|
|
func (s *defaultHistoryProvider) RUnlock() {
|
|
s.historyMutex.RUnlock()
|
|
}
|
|
|
|
// RUnlock locks history for reading
|
|
func (s *defaultHistoryProvider) RLock() {
|
|
s.historyMutex.RLock()
|
|
}
|
|
|
|
// ReadHistory reads scrollback history from r. Returns the number of lines
|
|
// read, and any read error (except io.EOF).
|
|
func (s *defaultHistoryProvider) ReadHistory(r io.Reader) (num int, err error) {
|
|
s.historyMutex.Lock()
|
|
defer s.historyMutex.Unlock()
|
|
|
|
in := bufio.NewReader(r)
|
|
num = 0
|
|
for {
|
|
line, part, err := in.ReadLine()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return num, err
|
|
}
|
|
if part {
|
|
return num, fmt.Errorf("line %d is too long", num+1)
|
|
}
|
|
if !utf8.Valid(line) {
|
|
return num, fmt.Errorf("invalid string at line %d", num+1)
|
|
}
|
|
num++
|
|
s.history = append(s.history, string(line))
|
|
if len(s.history) > HistoryLimit {
|
|
s.history = s.history[1:]
|
|
}
|
|
}
|
|
return num, nil
|
|
}
|
|
|
|
// WriteHistory writes scrollback history to w. Returns the number of lines
|
|
// successfully written, and any write error.
|
|
//
|
|
// Unlike the rest of liner's API, WriteHistory is safe to call
|
|
// from another goroutine while Prompt is in progress.
|
|
// This exception is to facilitate the saving of the history buffer
|
|
// during an unexpected exit (for example, due to Ctrl-C being invoked)
|
|
func (s *defaultHistoryProvider) WriteHistory(w io.Writer) (num int, err error) {
|
|
s.historyMutex.RLock()
|
|
defer s.historyMutex.RUnlock()
|
|
|
|
for _, item := range s.history {
|
|
_, err := fmt.Fprintln(w, item)
|
|
if err != nil {
|
|
return num, err
|
|
}
|
|
num++
|
|
}
|
|
return num, nil
|
|
}
|
|
|
|
// AppendHistory appends an entry to the scrollback history. AppendHistory
|
|
// should be called iff Prompt returns a valid command.
|
|
func (s *defaultHistoryProvider) AppendHistory(item string) {
|
|
s.historyMutex.Lock()
|
|
defer s.historyMutex.Unlock()
|
|
|
|
if len(s.history) > 0 {
|
|
if item == s.history[len(s.history)-1] {
|
|
return
|
|
}
|
|
}
|
|
s.history = append(s.history, item)
|
|
if len(s.history) > HistoryLimit {
|
|
s.history = s.history[1:]
|
|
}
|
|
}
|
|
|
|
// ClearHistory clears the scrollback history.
|
|
func (s *defaultHistoryProvider) ClearHistory() {
|
|
s.historyMutex.Lock()
|
|
defer s.historyMutex.Unlock()
|
|
s.history = nil
|
|
}
|
|
|
|
// Returns the history lines starting with prefix
|
|
func (s *defaultHistoryProvider) getHistoryByPrefix(prefix string) (ph []string) {
|
|
for _, h := range s.history {
|
|
if strings.HasPrefix(h, prefix) {
|
|
ph = append(ph, h)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Returns the history lines matching the intelligent search
|
|
func (s *defaultHistoryProvider) getHistoryByPattern(pattern string) (ph []string, pos []int) {
|
|
if pattern == "" {
|
|
return
|
|
}
|
|
for _, h := range s.history {
|
|
if i := strings.Index(h, pattern); i >= 0 {
|
|
ph = append(ph, h)
|
|
pos = append(pos, i)
|
|
}
|
|
}
|
|
return
|
|
}
|