123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- /*
- * Public Domain Software
- *
- * I (Matthias Ladkau) am the author of the source code in this file.
- * I have placed the source code in this file in the public domain.
- *
- * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
- */
- package termutil
- import (
- "bufio"
- "io/ioutil"
- "os"
- "strings"
- "devt.de/krotik/common/datautil"
- "devt.de/krotik/common/fileutil"
- "devt.de/krotik/common/stringutil"
- "devt.de/krotik/common/termutil/getch"
- )
- /*
- DefaultHistoryBufferSize is the default history buffer size in lines
- */
- var DefaultHistoryBufferSize = 100
- /*
- historyLineTerminalMixin adds history functionality to a given ConsoleLineTerminal
- */
- type historyLineTerminalMixin struct {
- ConsoleLineTerminal // Terminal which is being extended
- histFile string // File containing the history
- history *datautil.RingBuffer // Buffer containing the history
- historyPointer int // Pointer into history buffer
- lastEntry string // Temporary storage for last entry
- ignoreLine func(string) bool // Ignore line function
- }
- /*
- AddHistoryMixin adds history support for a given ConsoleLineTerminal. History
- is collected with every line and persisted in a file. The user can scroll
- through the history using the cursor keys up and down. The client can optionally
- define a ignoreLine function which causes a line to be ignored if it returns true.
- */
- func AddHistoryMixin(term ConsoleLineTerminal, histFile string,
- ignoreLine func(string) bool) (ConsoleLineTerminal, error) {
- var err error
- histterm := &historyLineTerminalMixin{term, histFile,
- datautil.NewRingBuffer(DefaultHistoryBufferSize), 0, "", ignoreLine}
- // Add key handler
- histterm.AddKeyHandler(histterm.handleKeyInput)
- if histFile != "" {
- if ok, err := fileutil.PathExists(histFile); err == nil && ok {
- var file *os.File
- // Read old history
- if file, err = os.Open(histFile); err == nil {
- defer file.Close()
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- histterm.history.Add(scanner.Text())
- }
- histterm.historyPointer = histterm.history.Size()
- }
- }
- }
- return histterm, err
- }
- /*
- handleKeyInput handles the key input for the history mixin.
- */
- func (ht *historyLineTerminalMixin) handleKeyInput(e *getch.KeyEvent, buf []rune) (bool, []rune, error) {
- var ret []rune
- if e.Code == getch.KeyArrowUp && ht.historyPointer > 0 {
- // Go up in history
- if ht.historyPointer == ht.history.Size() {
- // Save the current entered text
- ht.lastEntry = stringutil.RuneSliceToString(buf)
- }
- ht.historyPointer--
- histLine := ht.history.Get(ht.historyPointer).(string)
- ret = stringutil.StringToRuneSlice(histLine)
- } else if e.Code == getch.KeyArrowDown && ht.historyPointer < ht.history.Size()-1 {
- // Go down in history
- ht.historyPointer++
- histLine := ht.history.Get(ht.historyPointer).(string)
- ret = stringutil.StringToRuneSlice(histLine)
- } else if e.Code == getch.KeyArrowDown && ht.historyPointer == ht.history.Size()-1 {
- // Restore the last entry from where we started
- ret = stringutil.StringToRuneSlice(ht.lastEntry)
- ht.historyPointer++
- }
- return ret != nil, ret, nil
- }
- /*
- NextLine lets the user produce the next line in the terminal. All entered
- characters are echoed. The line is finished if the user presses return or
- pastes in a newline character. The final newline is echoed. If single
- character input via getch is not available then the code falls back to a
- simple line input from stdin. If single character input is available then
- the entered lines are safed in a history buffer which can be accessed via the
- up and down arrow keys.
- */
- func (ht *historyLineTerminalMixin) NextLine() (string, error) {
- line, err := ht.ConsoleLineTerminal.NextLine()
- if strings.TrimSpace(line) != "" && (ht.ignoreLine == nil || !ht.ignoreLine(line)) {
- // Safe entered line
- ht.history.Add(line)
- ht.lastEntry = ""
- ht.historyPointer = ht.history.Size()
- }
- if ht.histFile != "" {
- ioutil.WriteFile(ht.histFile, []byte(ht.history.String()), 0600)
- }
- return line, err
- }
|