/
githubmirror
/
lazygit
Обзор
Документация
Войти
/
githubmirror
/
lazygit
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
pkg/gocui/text_area_test.go
1 088 строк
28 KB
Stefan Haller
Exclude more commit trailers from auto-wrapping
02 авг 2026, 20:22
02 авг 2026, 20:22
b6deefa
Код
Авторство
О чём код?
package gocui import ( "reflect" "strconv" "testing" "github.com/stretchr/testify/assert" ) func TestTextArea(t *testing.T) { tests := []struct { actions func(*TextArea) wrapWidth int expectedContent string expectedCursor int expectedClipboard string }{ { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.TypeCharacter("c") }, expectedContent: "abc", expectedCursor: 3, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("\n") textarea.TypeCharacter("c") }, expectedContent: "a\nc", expectedCursor: 3, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString("abcd") }, expectedContent: "abcd", expectedCursor: 4, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString("a字cd") }, expectedContent: "a字cd", expectedCursor: 6, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.BackSpaceChar() }, expectedContent: "", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.BackSpaceChar() }, expectedContent: "", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.BackSpaceChar() }, expectedContent: "a", expectedCursor: 1, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.DeleteChar() }, expectedContent: "", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.DeleteChar() }, expectedContent: "a", expectedCursor: 1, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.MoveCursorLeft() textarea.DeleteChar() }, expectedContent: "", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.TypeCharacter("c") textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.DeleteChar() }, expectedContent: "ac", expectedCursor: 1, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.MoveCursorLeft() }, expectedContent: "", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.MoveCursorLeft() }, expectedContent: "a", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.MoveCursorLeft() }, expectedContent: "ab", expectedCursor: 1, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.MoveCursorRight() }, expectedContent: "", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.MoveCursorRight() }, expectedContent: "a", expectedCursor: 1, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.MoveCursorLeft() textarea.MoveCursorRight() }, expectedContent: "ab", expectedCursor: 2, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("漢") textarea.TypeCharacter("字") textarea.MoveCursorLeft() }, expectedContent: "漢字", expectedCursor: 3, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.ToggleOverwrite() textarea.TypeCharacter("a") textarea.TypeCharacter("b") }, expectedContent: "ab", expectedCursor: 2, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.TypeCharacter("c") textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.ToggleOverwrite() textarea.TypeCharacter("d") }, expectedContent: "adc", expectedCursor: 2, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString("aaa bbb") textarea.MoveLeftWord() }, expectedContent: "aaa bbb", expectedCursor: 4, }, { actions: func(textarea *TextArea) { textarea.TypeString("aaa\nbbb") textarea.MoveLeftWord() }, expectedContent: "aaa\nbbb", expectedCursor: 4, }, { actions: func(textarea *TextArea) { textarea.TypeString("aaa bbb") textarea.GoToStartOfLine() textarea.MoveLeftWord() }, wrapWidth: 4, expectedContent: "aaa bbb", expectedCursor: 0, }, { actions: func(textarea *TextArea) { textarea.TypeString("aaa bbb\n") textarea.MoveLeftWord() }, expectedContent: "aaa bbb\n", expectedCursor: 7, }, { actions: func(textarea *TextArea) { textarea.TypeString("aaa bbb") textarea.MoveLeftWord() textarea.MoveLeftWord() }, expectedContent: "aaa bbb", expectedCursor: 0, }, { actions: func(textarea *TextArea) { textarea.TypeString("aaa") textarea.GoToStartOfLine() textarea.MoveLeftWord() }, expectedContent: "aaa", expectedCursor: 0, }, { actions: func(textarea *TextArea) { textarea.TypeString("aaa bbb") textarea.MoveRightWord() }, expectedContent: "aaa bbb", expectedCursor: 7, }, { actions: func(textarea *TextArea) { textarea.TypeString("aaa\nbbb") textarea.GoToStartOfLine() textarea.MoveCursorLeft() textarea.MoveRightWord() }, expectedContent: "aaa\nbbb", expectedCursor: 4, }, { actions: func(textarea *TextArea) { textarea.TypeString("aaa bbb") textarea.GoToStartOfLine() textarea.MoveCursorLeft() textarea.MoveRightWord() }, wrapWidth: 4, expectedContent: "aaa bbb", expectedCursor: 7, }, { actions: func(textarea *TextArea) { textarea.TypeString("aaa bbb") textarea.GoToStartOfLine() textarea.MoveRightWord() }, expectedContent: "aaa bbb", expectedCursor: 3, }, { actions: func(textarea *TextArea) { textarea.TypeString("aaa bbb\n") textarea.MoveCursorLeft() textarea.GoToStartOfLine() textarea.MoveRightWord() textarea.MoveRightWord() }, expectedContent: "aaa bbb\n", expectedCursor: 7, }, { actions: func(textarea *TextArea) { // overwrite mode acts same as normal mode when cursor is at the end textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.TypeCharacter("c") textarea.ToggleOverwrite() textarea.TypeCharacter("d") }, expectedContent: "abcd", expectedCursor: 4, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.DeleteToStartOfLine() }, expectedContent: "", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.DeleteToStartOfLine() }, expectedContent: "", expectedCursor: 0, expectedClipboard: "ab", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.DeleteToStartOfLine() }, expectedContent: "ab", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.TypeCharacter("\n") textarea.DeleteToStartOfLine() }, expectedContent: "ab", expectedCursor: 2, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.TypeCharacter("\n") textarea.TypeCharacter("c") textarea.TypeCharacter("d") textarea.DeleteToStartOfLine() }, expectedContent: "ab\n", expectedCursor: 3, expectedClipboard: "cd", }, { actions: func(textarea *TextArea) { textarea.GoToStartOfLine() }, expectedContent: "", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.MoveCursorLeft() textarea.GoToStartOfLine() }, expectedContent: "a", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.TypeCharacter("\n") textarea.TypeCharacter("c") textarea.TypeCharacter("d") textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.GoToStartOfLine() }, expectedContent: "ab\ncd", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.TypeCharacter("\n") textarea.TypeCharacter("c") textarea.TypeCharacter("d") textarea.GoToStartOfLine() }, expectedContent: "ab\ncd", expectedCursor: 3, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.TypeCharacter("\n") textarea.TypeCharacter("c") textarea.TypeCharacter("d") textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.GoToStartOfLine() }, expectedContent: "ab\ncd", expectedCursor: 3, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.GoToEndOfLine() }, expectedContent: "", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("a") textarea.TypeCharacter("b") textarea.TypeCharacter("\n") textarea.TypeCharacter("c") textarea.TypeCharacter("d") textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.GoToEndOfLine() }, expectedContent: "ab\ncd", expectedCursor: 5, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.SetCursor2D(10, 10) }, expectedContent: "", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.SetCursor2D(-1, -1) }, expectedContent: "", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString("ab\ncd") textarea.SetCursor2D(0, 0) }, expectedContent: "ab\ncd", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString("ab\ncd") textarea.SetCursor2D(2, 0) }, expectedContent: "ab\ncd", expectedCursor: 2, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString("ab\ncd\nef") textarea.SetCursor2D(2, 1) }, expectedContent: "ab\ncd\nef", expectedCursor: 5, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString("abcd\n\nijkl") textarea.MoveCursorUp() }, expectedContent: "abcd\n\nijkl", expectedCursor: 5, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString("abcdef\n老老老") textarea.MoveCursorLeft() textarea.MoveCursorUp() }, expectedContent: "abcdef\n老老老", expectedCursor: 4, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString("abcdef\n老老老") textarea.MoveCursorUp() textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.MoveCursorDown() }, expectedContent: "abcdef\n老老老", expectedCursor: 13, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString("abcd\nef") textarea.MoveCursorUp() textarea.GoToEndOfLine() textarea.MoveCursorDown() }, expectedContent: "abcd\nef", expectedCursor: 7, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString("abcd") textarea.MoveCursorUp() }, expectedContent: "abcd", expectedCursor: 4, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString(`abcdefg`) textarea.Clear() }, expectedContent: "", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString(`abcdefg`) textarea.Clear() }, expectedContent: "", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString(`abc def`) textarea.MoveCursorLeft() textarea.BackSpaceWord() }, expectedContent: "abc f", expectedCursor: 4, expectedClipboard: "de", }, { actions: func(textarea *TextArea) { textarea.TypeString(`abc def `) textarea.BackSpaceWord() }, expectedContent: "abc ", expectedCursor: 5, expectedClipboard: "def ", }, { actions: func(textarea *TextArea) { textarea.TypeString("abc def\nghi") textarea.BackSpaceWord() }, expectedContent: "abc def\n", expectedCursor: 8, expectedClipboard: "ghi", }, { actions: func(textarea *TextArea) { textarea.TypeString("abc def\nghi") textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.BackSpaceWord() }, expectedContent: "abc defghi", expectedCursor: 7, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString(`abc(def)`) textarea.BackSpaceWord() }, expectedContent: "abc(def", expectedCursor: 7, expectedClipboard: ")", }, { actions: func(textarea *TextArea) { textarea.TypeString(`abc(def`) textarea.BackSpaceWord() }, expectedContent: "abc(", expectedCursor: 4, expectedClipboard: "def", }, { actions: func(textarea *TextArea) { textarea.TypeString(`abc`) textarea.GoToStartOfLine() textarea.BackSpaceWord() }, expectedContent: "abc", expectedCursor: 0, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString(`abc`) textarea.Yank() }, expectedContent: "abc", expectedCursor: 3, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString(`abc def`) textarea.DeleteToStartOfLine() textarea.Yank() textarea.Yank() }, expectedContent: "abc defabc def", expectedCursor: 14, expectedClipboard: "abc def", }, { actions: func(textarea *TextArea) { textarea.TypeString("abc\ndef") textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.MoveCursorUp() textarea.DeleteToEndOfLine() }, expectedContent: "a\ndef", expectedCursor: 1, expectedClipboard: "bc", }, { actions: func(textarea *TextArea) { textarea.TypeString("abc\ndef") textarea.MoveCursorUp() textarea.DeleteToEndOfLine() }, expectedContent: "abcdef", expectedCursor: 3, expectedClipboard: "", }, { actions: func(textarea *TextArea) { textarea.TypeString(`abc def`) textarea.BackSpaceWord() textarea.Yank() textarea.Yank() }, expectedContent: "abc defdef", expectedCursor: 10, expectedClipboard: "def", }, { actions: func(textarea *TextArea) { textarea.TypeString(`abc def`) textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.DeleteToEndOfLine() textarea.Yank() textarea.Yank() }, expectedContent: "abc defef", expectedCursor: 9, expectedClipboard: "ef", }, } for i, test := range tests { t.Run(strconv.Itoa(i+1), func(t *testing.T) { textarea := &TextArea{} if test.wrapWidth > 0 { textarea.AutoWrap = true textarea.AutoWrapWidth = test.wrapWidth } test.actions(textarea) assert.EqualValues(t, test.expectedContent, textarea.GetUnwrappedContent()) assert.EqualValues(t, test.expectedCursor, textarea.cursor) assert.EqualValues(t, test.expectedClipboard, textarea.clipboard) }) } } func TestGetCursorXY(t *testing.T) { tests := []struct { actions func(*TextArea) wrapWidth int expectedX int expectedY int }{ { actions: func(textarea *TextArea) { // do nothing }, expectedX: 0, expectedY: 0, }, { actions: func(textarea *TextArea) { textarea.TypeString("\n") }, expectedX: 0, expectedY: 1, }, { actions: func(textarea *TextArea) { textarea.TypeString("\na") }, expectedX: 1, expectedY: 1, }, { actions: func(textarea *TextArea) { textarea.TypeString("\n") textarea.MoveCursorUp() }, expectedX: 0, expectedY: 0, }, { actions: func(textarea *TextArea) { textarea.TypeString("\n\n") textarea.MoveCursorUp() }, expectedX: 0, expectedY: 1, }, { actions: func(textarea *TextArea) { textarea.TypeString("ab\ncd") }, expectedX: 2, expectedY: 1, }, { actions: func(textarea *TextArea) { textarea.TypeString("ab\n") }, expectedX: 0, expectedY: 1, }, { actions: func(textarea *TextArea) { textarea.TypeString("ab\n") textarea.MoveCursorLeft() }, expectedX: 2, expectedY: 0, }, { actions: func(textarea *TextArea) { textarea.TypeString("ab\n\n") }, expectedX: 0, expectedY: 2, }, { actions: func(textarea *TextArea) { textarea.TypeString("ab\n\n") textarea.MoveCursorLeft() }, expectedX: 0, expectedY: 1, }, { actions: func(textarea *TextArea) { textarea.TypeCharacter("漢") textarea.TypeCharacter("字") }, expectedX: 4, expectedY: 0, }, { actions: func(textarea *TextArea) { textarea.TypeString("abc de") textarea.MoveCursorLeft() }, wrapWidth: 4, expectedX: 1, expectedY: 1, }, { actions: func(textarea *TextArea) { textarea.TypeString("abc de") textarea.MoveCursorLeft() textarea.MoveCursorLeft() }, wrapWidth: 4, expectedX: 0, expectedY: 1, }, { actions: func(textarea *TextArea) { textarea.TypeString("abc de") textarea.MoveCursorLeft() textarea.MoveCursorLeft() textarea.MoveCursorLeft() }, wrapWidth: 4, expectedX: 3, expectedY: 0, }, } for i, test := range tests { t.Run(strconv.Itoa(i+1), func(t *testing.T) { textarea := &TextArea{} if test.wrapWidth > 0 { textarea.AutoWrap = true textarea.AutoWrapWidth = test.wrapWidth } test.actions(textarea) x, y := textarea.GetCursorXY() assert.EqualValues(t, test.expectedX, x) assert.EqualValues(t, test.expectedY, y) // As a sanity check, test that setting the cursor back to (x, y) results in the same cursor position: cursor := textarea.cursor textarea.SetCursor2D(x, y) assert.EqualValues(t, cursor, textarea.cursor) }) } } func Test_AutoWrapContent(t *testing.T) { tests := []struct { name string content string autoWrapWidth int expectedWrappedContent string expectedSoftLineBreaks []int }{ { name: "empty content", content: "", autoWrapWidth: 7, expectedWrappedContent: "", expectedSoftLineBreaks: []int{}, }, { name: "no wrapping necessary", content: "abcde", autoWrapWidth: 7, expectedWrappedContent: "abcde", expectedSoftLineBreaks: []int{}, }, { name: "wrap at whitespace", content: "abcde xyz", autoWrapWidth: 7, expectedWrappedContent: "abcde \nxyz", expectedSoftLineBreaks: []int{6}, }, { name: "take wide characters into account", content: "🏴 🏴 x y", // the flag has a width of 2 autoWrapWidth: 7, expectedWrappedContent: "🏴 🏴 x \ny", expectedSoftLineBreaks: []int{60}, }, { name: "take wide characters into account at line end", content: "🏴 🏴 🏴", // the flag has a width of 2 autoWrapWidth: 7, expectedWrappedContent: "🏴 🏴 \n🏴", expectedSoftLineBreaks: []int{58}, }, { name: "lots of whitespace is preserved at end of line", content: "abcde xyz", autoWrapWidth: 7, expectedWrappedContent: "abcde \nxyz", expectedSoftLineBreaks: []int{11}, }, { name: "don't wrap inside long word when there's no whitespace", content: "abc defghijklmn opq", autoWrapWidth: 7, expectedWrappedContent: "abc \ndefghijklmn \nopq", expectedSoftLineBreaks: []int{4, 16}, }, { name: "don't break at space after footnote symbol", content: "abc\n[1]: https://long/link\ndef", autoWrapWidth: 7, expectedWrappedContent: "abc\n[1]: https://long/link\ndef", expectedSoftLineBreaks: []int{}, }, { name: "don't break at space after footnote symbol at soft line start", content: "abc def [1]: https://long/link\nghi", autoWrapWidth: 7, expectedWrappedContent: "abc def \n[1]: https://long/link\nghi", expectedSoftLineBreaks: []int{8}, }, { name: "do break at subsequent space after footnote symbol", content: "abc\n[1]: normal text follows\ndef", autoWrapWidth: 7, expectedWrappedContent: "abc\n[1]: normal \ntext \nfollows\ndef", expectedSoftLineBreaks: []int{16, 21}, }, { name: "don't break at space after trailer at beginning of message", content: "Signed-off-by: John Doe <john@doe.com>\nDepends-on: Some dependency with spaces\n", autoWrapWidth: 10, expectedWrappedContent: "Signed-off-by: John Doe <john@doe.com>\nDepends-on: Some dependency with spaces\n", expectedSoftLineBreaks: []int{}, }, { name: "don't break at space after trailer in a trailer block at the end of a message", content: "abc\n\nSigned-off-by: John Doe <john@doe.com>\nDepends-on: Some dependency with spaces\n", autoWrapWidth: 10, expectedWrappedContent: "abc\n\nSigned-off-by: John Doe <john@doe.com>\nDepends-on: Some dependency with spaces\n", expectedSoftLineBreaks: []int{}, }, { name: "don't break at space after trailer with URL value", content: "abc\n\nBug: https://example.com/a/very/long/path\nIssue: http://example.com/a/very/long/path\n", autoWrapWidth: 10, expectedWrappedContent: "abc\n\nBug: https://example.com/a/very/long/path\nIssue: http://example.com/a/very/long/path\n", expectedSoftLineBreaks: []int{}, }, { name: "do break at space if trailer is not in a trailer block at the end", content: "abc\n\nSigned-off-by: John Doe <john@doe.com>\n\nMore text here\n", autoWrapWidth: 10, expectedWrappedContent: "abc\n\nSigned-off-by: \nJohn Doe \n<john@doe.com>\n\nMore text \nhere\n", expectedSoftLineBreaks: []int{20, 29, 55}, }, { // Each line in the trailer block is judged on its own, so a line // that isn't recognized as a trailer wraps without affecting the // real trailers around it. name: "keep a trailer next to a non-trailer line in the same block", content: "abc\n\nFixes: a long description that wraps\nSigned-off-by: John Doe <john@doe.com>\n", autoWrapWidth: 10, expectedWrappedContent: "abc\n\nFixes: a \nlong \ndescription \nthat wraps\nSigned-off-by: John Doe <john@doe.com>\n", expectedSoftLineBreaks: []int{14, 19, 31}, }, { name: "don't break at space after trailer when the block ends in a blank line", content: "abc\n\nSigned-off-by: John Doe <john@doe.com>\n\n", autoWrapWidth: 10, expectedWrappedContent: "abc\n\nSigned-off-by: John Doe <john@doe.com>\n\n", expectedSoftLineBreaks: []int{}, }, { name: "do break normal text after non-hyphenated key", content: "However: in this commit blah blah blah\n", autoWrapWidth: 10, expectedWrappedContent: "However: \nin this \ncommit \nblah blah \nblah\n", expectedSoftLineBreaks: []int{9, 17, 24, 34}, }, { name: "do break at space after trailer if there is no space after the colon", content: "abc\n\nSigned-off-by:John Doe <john@doe.com>\n", autoWrapWidth: 10, expectedWrappedContent: "abc\n\nSigned-off-by:John \nDoe \n<john@doe.com>\n", expectedSoftLineBreaks: []int{24, 28}, }, { name: "hard line breaks", content: "abc\ndef\n", autoWrapWidth: 7, expectedWrappedContent: "abc\ndef\n", expectedSoftLineBreaks: []int{}, }, { name: "mixture of hard and soft line breaks", content: "abc def ghi jkl mno\npqr stu vwx yz\n", autoWrapWidth: 7, expectedWrappedContent: "abc def \nghi jkl \nmno\npqr stu \nvwx yz\n", expectedSoftLineBreaks: []int{8, 16, 28}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { textArea := &TextArea{content: tt.content, AutoWrapWidth: tt.autoWrapWidth, AutoWrap: true} cells, softLineBreakIndices := contentToCells(tt.content, tt.autoWrapWidth) textArea.cells = cells if !reflect.DeepEqual(textArea.GetContent(), tt.expectedWrappedContent) { t.Errorf("autoWrapContentImpl() wrappedContent = %v, expected %v", textArea.GetContent(), tt.expectedWrappedContent) } if !reflect.DeepEqual(softLineBreakIndices, tt.expectedSoftLineBreaks) { t.Errorf("autoWrapContentImpl() softLineBreakIndices = %v, expected %v", softLineBreakIndices, tt.expectedSoftLineBreaks) } // As a sanity check, run through all characters of the original content, // convert the cursor to the wrapped cursor, and check that the character // in the wrapped content at that position is the same: origCursor := 0 for _, chr := range stringToGraphemes(tt.content) { wrappedIndex := textArea.contentCursorToCellCursor(origCursor) if chr != textArea.cells[wrappedIndex].char { t.Errorf("Runes in orig content and wrapped content don't match at %d: expected %v, got %v", origCursor, chr, textArea.cells[wrappedIndex].char) } // Also, check that converting the wrapped position back to the // orig position yields the original value again: origIndexAgain := textArea.cellCursorToContentCursor(wrappedIndex) if origCursor != origIndexAgain { t.Errorf("wrappedCursorToOrigCursor doesn't yield original position: expected %d, got %d", origCursor, origIndexAgain) } origCursor += len(chr) } }) } } var testContent = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vehicula mi at elit pellentesque, eu pulvinar ligula molestie. In vitae orci vitae elit fermentum lobortis sed in nisi. Nam non odio nisi. Donec vitae elit enim. Pellentesque faucibus dolor at metus elementum sollicitudin. Mauris eu orci vel odio ornare feugiat eget ac nisl. Nam at dolor erat. Integer sit amet rutrum lectus, mollis pretium sapien. Maecenas ligula ipsum, congue vitae rhoncus eget, volutpat at quam. Donec ac ultricies tortor, sit amet sollicitudin urna. Integer porta ornare diam a imperdiet. Praesent vulputate mi turpis, in porttitor diam commodo a. Donec ut enim ligula. [Thïs-is-not-à-fôôtnöte]: https://example.com/footnote Sïgned-öff-by: This is not a trailer [1]: This is a footnote Signed-off-by: John Doe <john@doe.com> ` func BenchmarkTypeCharacter(b *testing.B) { textArea := &TextArea{content: testContent, AutoWrapWidth: 72, AutoWrap: true} textArea.SetCursor2D(0, 0) b.ResetTimer() for b.Loop() { textArea.TypeCharacter("a") } }