-
Notifications
You must be signed in to change notification settings - Fork 0
/
histterm.go
149 lines (111 loc) · 4.07 KB
/
histterm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
* 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"
"github.com/krotik/common/datautil"
"github.com/krotik/common/fileutil"
"github.com/krotik/common/stringutil"
"github.com/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
}