commit 6f0d19e
delthas
·
2022-06-04 10:05:11 +0000 UTC
parent 1c5d730
Reset history when flushing lines Previously, when modifying a line in the history (and then possibly sending it), the history was modified, meaning that when looking up history lines again, instead of seeing a new line, the actual original line had been modified. With this patch, history is always kepy, and modifying a previous line and sending it will add this line to the history, and restore the previous history, after flushing the message.
1 files changed,
+39,
-20
+39,
-20
1@@ -16,6 +16,11 @@ type Editor struct {
2 // text contains the written runes. An empty slice means no text is written.
3 text [][]rune
4
5+ // history contains the original content of each previously sent message.
6+ // It gets out of sync with text when history is modified à la readline,
7+ // then overwrites text when a new message is added.
8+ history [][]rune
9+
10 lineIdx int
11
12 // textWidth[i] contains the width of string(text[:i]). Therefore
13@@ -40,7 +45,10 @@ type Editor struct {
14
15 backsearch bool
16 backsearchPattern []rune // pre-lowercased
17- backsearchIdx int
18+
19+ // oldest (lowest) index in text of lines that were changed.
20+ // used as an optimization to reduce copying when flushing lines.
21+ oldestTextChange int
22 }
23
24 // NewEditor returns a new Editor.
25@@ -48,6 +56,7 @@ type Editor struct {
26 func NewEditor(autoComplete func(cursorIdx int, text []rune) []Completion) Editor {
27 return Editor{
28 text: [][]rune{{}},
29+ history: [][]rune{},
30 textWidth: []int{0},
31 autoComplete: autoComplete,
32 }
33@@ -89,11 +98,7 @@ func (e *Editor) PutRune(r rune) {
34 wasEmpty := len(e.backsearchPattern) == 0
35 e.backsearchPattern = append(e.backsearchPattern, lowerRune)
36 if wasEmpty {
37- clearLine := e.lineIdx == len(e.text)-1
38 e.backsearchUpdate(e.lineIdx - 1)
39- if clearLine && e.lineIdx < len(e.text)-1 {
40- e.text = e.text[:len(e.text)-1]
41- }
42 } else {
43 e.backsearchUpdate(e.lineIdx)
44 }
45@@ -104,6 +109,7 @@ func (e *Editor) putRune(r rune) {
46 e.text[e.lineIdx] = append(e.text[e.lineIdx], ' ')
47 copy(e.text[e.lineIdx][e.cursorIdx+1:], e.text[e.lineIdx][e.cursorIdx:])
48 e.text[e.lineIdx][e.cursorIdx] = r
49+ e.bumpOldestChange()
50
51 rw := runeWidth(r)
52 tw := e.textWidth[len(e.textWidth)-1]
53@@ -154,6 +160,8 @@ func (e *Editor) remRuneAt(idx int) {
54
55 copy(e.text[e.lineIdx][idx:], e.text[e.lineIdx][idx+1:])
56 e.text[e.lineIdx] = e.text[e.lineIdx][:len(e.text[e.lineIdx])-1]
57+
58+ e.bumpOldestChange()
59 }
60
61 func (e *Editor) RemWord() (ok bool) {
62@@ -186,20 +194,29 @@ func (e *Editor) RemWord() (ok bool) {
63 return
64 }
65
66-func (e *Editor) Flush() (content string) {
67- content = string(e.text[e.lineIdx])
68- if len(e.text[len(e.text)-1]) == 0 {
69- e.lineIdx = len(e.text) - 1
70- } else {
71- e.lineIdx = len(e.text)
72+func (e *Editor) Flush() string {
73+ content := string(e.text[e.lineIdx])
74+ if len(content) > 0 {
75+ // intended []rune -> string -> []rune conversion to make copies
76+ e.history = append(e.history, []rune(content))
77+ }
78+ for i, line := range e.history[e.oldestTextChange:] {
79+ i := i + e.oldestTextChange
80+ e.text[i] = append(e.text[i][:0], line...)
81+ }
82+ if len(content) > 0 {
83 e.text = append(e.text, []rune{})
84+ } else {
85+ e.text[len(e.text)-1] = []rune{}
86 }
87+ e.lineIdx = len(e.text) - 1
88 e.textWidth = e.textWidth[:1]
89 e.cursorIdx = 0
90 e.offsetIdx = 0
91 e.autoCache = nil
92 e.backsearchEnd()
93- return
94+ e.oldestTextChange = len(e.text) - 1
95+ return content
96 }
97
98 func (e *Editor) Clear() bool {
99@@ -207,6 +224,7 @@ func (e *Editor) Clear() bool {
100 return false
101 }
102 e.text[e.lineIdx] = []rune{}
103+ e.bumpOldestChange()
104 e.textWidth = e.textWidth[:1]
105 e.cursorIdx = 0
106 e.offsetIdx = 0
107@@ -217,6 +235,7 @@ func (e *Editor) Clear() bool {
108 func (e *Editor) Set(text string) {
109 r := []rune(text)
110 e.text[e.lineIdx] = r
111+ e.bumpOldestChange()
112 e.cursorIdx = len(r)
113 e.computeTextWidth()
114 e.offsetIdx = 0
115@@ -335,9 +354,6 @@ func (e *Editor) Up() {
116
117 func (e *Editor) Down() {
118 if e.lineIdx == len(e.text)-1 {
119- if len(e.text[e.lineIdx]) == 0 {
120- return
121- }
122 e.Flush()
123 return
124 }
125@@ -363,6 +379,7 @@ func (e *Editor) AutoComplete(offset int) (ok bool) {
126 }
127
128 e.text[e.lineIdx] = e.autoCache[e.autoCacheIdx].Text
129+ e.bumpOldestChange()
130 e.cursorIdx = e.autoCache[e.autoCacheIdx].CursorIdx
131 e.computeTextWidth()
132 if len(e.textWidth) <= e.offsetIdx {
133@@ -377,16 +394,11 @@ func (e *Editor) AutoComplete(offset int) (ok bool) {
134 }
135
136 func (e *Editor) BackSearch() {
137- clearLine := false
138 if !e.backsearch {
139 e.backsearch = true
140 e.backsearchPattern = []rune(strings.ToLower(string(e.text[e.lineIdx])))
141- clearLine = e.lineIdx == len(e.text)-1
142 }
143 e.backsearchUpdate(e.lineIdx - 1)
144- if clearLine && e.lineIdx < len(e.text)-1 {
145- e.text = e.text[:len(e.text)-1]
146- }
147 }
148
149 func (e *Editor) backsearchUpdate(start int) {
150@@ -422,6 +434,13 @@ func (e *Editor) computeTextWidth() {
151 }
152 }
153
154+// call this everytime e.text is modified
155+func (e *Editor) bumpOldestChange() {
156+ if e.oldestTextChange > e.lineIdx {
157+ e.oldestTextChange = e.lineIdx
158+ }
159+}
160+
161 func (e *Editor) Draw(screen tcell.Screen, x0, y int) {
162 st := tcell.StyleDefault
163