Skip to content

Commit a80d108

Browse files
committed
textarea: import upstream fix
This imports charmbracelet/bubbles#370.
1 parent 1378db0 commit a80d108

File tree

2 files changed

+62
-45
lines changed

2 files changed

+62
-45
lines changed

editline/internal/textarea/textarea.go

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ const (
2727
defaultHeight = 6
2828
defaultWidth = 40
2929
defaultCharLimit = 400
30-
maxHeight = 99
31-
maxWidth = 500
30+
defaultMaxHeight = 99
31+
defaultMaxWidth = 500
3232
)
3333

3434
// Internal messages for clipboard operations.
@@ -182,6 +182,14 @@ type Model struct {
182182
// accept. If 0 or less, there's no limit.
183183
CharLimit int
184184

185+
// MaxHeight is the maximum height of the text area in rows. If 0 or less,
186+
// there's no limit.
187+
MaxHeight int
188+
189+
// MaxWidth is the maximum width of the text area in columns. If 0 or less,
190+
// there's no limit.
191+
MaxWidth int
192+
185193
// If promptFunc is set, it replaces Prompt as a generator for
186194
// prompt strings at the beginning of each line.
187195
promptFunc func(line int) string
@@ -239,6 +247,8 @@ func New() Model {
239247

240248
m := Model{
241249
CharLimit: defaultCharLimit,
250+
MaxHeight: defaultMaxHeight,
251+
MaxWidth: defaultMaxWidth,
242252
Prompt: lipgloss.ThickBorder().Left + " ",
243253
style: &blurredStyle,
244254
FocusedStyle: focusedStyle,
@@ -248,7 +258,7 @@ func New() Model {
248258
Cursor: cur,
249259
KeyMap: DefaultKeyMap,
250260

251-
value: make([][]rune, minHeight, maxHeight),
261+
value: make([][]rune, minHeight, defaultMaxHeight),
252262
focus: false,
253263
col: 0,
254264
row: 0,
@@ -332,6 +342,10 @@ func (m *Model) insertRunesFromUserInput(runes []rune) {
332342
lstart := 0
333343
for i := 0; i < len(runes); i++ {
334344
if runes[i] == '\n' {
345+
// Queue a line to become a new row in the text area below.
346+
// Beware to clamp the max capacity of the slice, to ensure no
347+
// data from different rows get overwritten when later edits
348+
// will modify this line.
335349
lines = append(lines, runes[lstart:i:i])
336350
lstart = i + 1
337351
}
@@ -343,8 +357,8 @@ func (m *Model) insertRunesFromUserInput(runes []rune) {
343357
}
344358

345359
// Obey the maximum height limit.
346-
if len(m.value)+len(lines)-1 > maxHeight {
347-
allowedHeight := max(0, maxHeight-len(m.value)+1)
360+
if m.MaxHeight > 0 && len(m.value)+len(lines)-1 > m.MaxHeight {
361+
allowedHeight := max(0, m.MaxHeight-len(m.value)+1)
348362
lines = lines[:allowedHeight]
349363
}
350364

@@ -353,7 +367,7 @@ func (m *Model) insertRunesFromUserInput(runes []rune) {
353367
return
354368
}
355369

356-
// Save the reminder of the original line at the current
370+
// Save the remainder of the original line at the current
357371
// cursor position.
358372
tail := make([]rune, len(m.value[m.row][m.col:]))
359373
copy(tail, m.value[m.row][m.col:])
@@ -450,7 +464,7 @@ func (m *Model) CursorDown() {
450464
m.row++
451465
m.col = 0
452466
} else {
453-
// Move the cursor to the start of the next line. So that we can get
467+
// Move the cursor to the start of the next line so that we can get
454468
// the line information. We need to add 2 columns to account for the
455469
// trailing space wrapping.
456470
m.col = min(li.StartColumn+li.Width+2, len(m.value[m.row])-1)
@@ -539,7 +553,7 @@ func (m *Model) Focus() tea.Cmd {
539553
return m.Cursor.Focus()
540554
}
541555

542-
// Blur removes the focus state on the model. When the model is blurred it can
556+
// Blur removes the focus state on the model. When the model is blurred it can
543557
// not receive keyboard input and the cursor will be hidden.
544558
func (m *Model) Blur() {
545559
m.focus = false
@@ -549,14 +563,18 @@ func (m *Model) Blur() {
549563

550564
// Reset sets the input to its default state with no input.
551565
func (m *Model) Reset() {
552-
m.value = make([][]rune, minHeight, maxHeight)
566+
startCap := m.MaxHeight
567+
if startCap <= 0 {
568+
startCap = defaultMaxHeight
569+
}
570+
m.value = make([][]rune, minHeight, startCap)
553571
m.col = 0
554572
m.row = 0
555573
m.viewport.GotoTop()
556574
m.SetCursor(0)
557575
}
558576

559-
// rsan initializes or retrieves the rune sanitizer.
577+
// san initializes or retrieves the rune sanitizer.
560578
func (m *Model) san() runeutil.Sanitizer {
561579
if m.rsan == nil {
562580
// Textinput has all its input on a single line so collapse
@@ -852,12 +870,16 @@ func (m *Model) moveToEnd() {
852870
// whether or not line numbers are being shown.
853871
//
854872
// Ensure that SetWidth is called after setting the Prompt and ShowLineNumbers,
855-
// If it important that the width of the textarea be exactly the given width
873+
// It is important that the width of the textarea be exactly the given width
856874
// and no more.
857875
func (m *Model) SetWidth(w int) {
858-
m.viewport.Width = clamp(w, minWidth, maxWidth)
876+
if m.MaxWidth > 0 {
877+
m.viewport.Width = clamp(w, minWidth, m.MaxWidth)
878+
} else {
879+
m.viewport.Width = max(w, minWidth)
880+
}
859881

860-
// Since the width of the textarea input is dependant on the width of the
882+
// Since the width of the textarea input is dependent on the width of the
861883
// prompt and line numbers, we need to calculate it by subtracting.
862884
inputWidth := w
863885
if m.ShowLineNumbers {
@@ -872,7 +894,11 @@ func (m *Model) SetWidth(w int) {
872894
}
873895

874896
inputWidth -= m.promptWidth
875-
m.width = clamp(inputWidth, minWidth, maxWidth)
897+
if m.MaxWidth > 0 {
898+
m.width = clamp(inputWidth, minWidth, m.MaxWidth)
899+
} else {
900+
m.width = max(inputWidth, minWidth)
901+
}
876902
}
877903

878904
// SetPromptFunc supersedes the Prompt field and sets a dynamic prompt
@@ -894,13 +920,18 @@ func (m Model) Height() int {
894920

895921
// SetHeight sets the height of the textarea.
896922
func (m *Model) SetHeight(h int) {
897-
m.height = clamp(h, minHeight, maxHeight)
898-
m.viewport.Height = clamp(h, minHeight, maxHeight)
923+
if m.MaxHeight > 0 {
924+
m.height = clamp(h, minHeight, m.MaxHeight)
925+
m.viewport.Height = clamp(h, minHeight, m.MaxHeight)
926+
} else {
927+
m.height = max(h, minHeight)
928+
m.viewport.Height = max(h, minHeight)
929+
}
899930
}
900931

901932
// InsertNewline inserts a newline character at the cursor.
902933
func (m *Model) InsertNewline() {
903-
if len(m.value) >= maxHeight {
934+
if m.MaxHeight > 0 && len(m.value) >= m.MaxHeight {
904935
return
905936
}
906937
m.col = clamp(m.col, 0, len(m.value[m.row]))
@@ -1223,7 +1254,7 @@ func (m Model) cursorLineNumber() int {
12231254
return line
12241255
}
12251256

1226-
// mergeLineBelow merges the current line with the line below.
1257+
// mergeLineBelow merges the current line the cursor is on with the line below.
12271258
func (m *Model) mergeLineBelow(row int) {
12281259
if row >= len(m.value)-1 {
12291260
return

editline/internal/textarea/textarea.go.diff

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
--- textarea.go.orig 2023-01-24 19:21:36.201018000 +0100
2-
+++ textarea.go 2023-02-05 12:13:30.385554000 +0100
1+
--- textarea.go.orig 2023-04-22 18:36:07.734330000 +0200
2+
+++ textarea.go 2023-04-22 18:38:02.341039000 +0200
33
@@ -1,3 +1,9 @@
44
+// The code below is imported from
55
+// https://github.com/charmbracelet/bubbles/tree/master/textarea
@@ -80,7 +80,7 @@
8080
}
8181

8282
// LineInfo is a helper for keeping track of line information regarding
83-
@@ -197,6 +205,9 @@
83+
@@ -205,6 +213,9 @@
8484
// component. When false, ignore keyboard input and hide the cursor.
8585
focus bool
8686

@@ -90,21 +90,7 @@
9090
// Cursor column.
9191
col int
9292

93-
@@ -321,11 +332,11 @@
94-
lstart := 0
95-
for i := 0; i < len(runes); i++ {
96-
if runes[i] == '\n' {
97-
- lines = append(lines, runes[lstart:i])
98-
+ lines = append(lines, runes[lstart:i:i])
99-
lstart = i + 1
100-
}
101-
}
102-
- if lstart < len(runes) {
103-
+ if lstart <= len(runes) {
104-
// The last line did not end with a newline character.
105-
// Take it now.
106-
lines = append(lines, runes[lstart:])
107-
@@ -381,6 +392,18 @@
93+
@@ -395,6 +406,18 @@
10894
m.SetCursor(m.col)
10995
}
11096

@@ -123,7 +109,7 @@
123109
// Value returns the value of the text input.
124110
func (m Model) Value() string {
125111
if m.value == nil {
126-
@@ -750,14 +773,20 @@
112+
@@ -768,14 +791,20 @@
127113
// LineInfo returns the number of characters from the start of the
128114
// (soft-wrapped) line and the (soft-wrapped) line width.
129115
func (m Model) LineInfo() LineInfo {
@@ -146,7 +132,7 @@
146132
// We wrap around to the next line if we are at the end of the
147133
// previous line so that we can be at the very beginning of the row
148134
return LineInfo{
149-
@@ -765,16 +794,16 @@
135+
@@ -783,16 +812,16 @@
150136
ColumnOffset: 0,
151137
Height: len(grid),
152138
RowOffset: i + 1,
@@ -167,13 +153,13 @@
167153
Height: len(grid),
168154
RowOffset: i,
169155
StartColumn: counter,
170-
@@ -869,6 +898,48 @@
171-
m.viewport.Height = clamp(h, minHeight, maxHeight)
156+
@@ -900,6 +929,48 @@
157+
}
172158
}
173159

174160
+// InsertNewline inserts a newline character at the cursor.
175161
+func (m *Model) InsertNewline() {
176-
+ if len(m.value) >= maxHeight {
162+
+ if m.MaxHeight > 0 && len(m.value) >= m.MaxHeight {
177163
+ return
178164
+ }
179165
+ m.col = clamp(m.col, 0, len(m.value[m.row]))
@@ -216,7 +202,7 @@
216202
// Update is the Bubble Tea update loop.
217203
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
218204
if !m.focus {
219-
@@ -903,25 +974,9 @@
205+
@@ -934,25 +1005,9 @@
220206
}
221207
m.deleteBeforeCursor()
222208
case key.Matches(msg, m.KeyMap.DeleteCharacterBackward):
@@ -244,11 +230,11 @@
244230
case key.Matches(msg, m.KeyMap.DeleteWordBackward):
245231
if m.col <= 0 {
246232
m.mergeLineAbove(m.row)
247-
@@ -936,11 +991,7 @@
233+
@@ -967,11 +1022,7 @@
248234
}
249235
m.deleteWordRight()
250236
case key.Matches(msg, m.KeyMap.InsertNewline):
251-
- if len(m.value) >= maxHeight {
237+
- if m.MaxHeight > 0 && len(m.value) >= m.MaxHeight {
252238
- return m, nil
253239
- }
254240
- m.col = clamp(m.col, 0, len(m.value[m.row]))
@@ -257,7 +243,7 @@
257243
case key.Matches(msg, m.KeyMap.LineEnd):
258244
m.CursorEnd()
259245
case key.Matches(msg, m.KeyMap.LineStart):
260-
@@ -971,9 +1022,18 @@
246+
@@ -1002,9 +1053,18 @@
261247
m.capitalizeRight()
262248
case key.Matches(msg, m.KeyMap.TransposeCharacterBackward):
263249
m.transposeLeft()

0 commit comments

Comments
 (0)