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