diff --git a/gui.go b/gui.go index 0ea2a73..7caa174 100644 --- a/gui.go +++ b/gui.go @@ -335,7 +335,7 @@ func (g *Gui) SetView(name string, x0, y0, x1, y1 int, overlaps byte) (*View, er g.Mutexes.ViewsMutex.Lock() - v := newView(name, x0, y0, x1, y1, g.outputMode) + v := NewView(name, x0, y0, x1, y1, g.outputMode) v.BgColor, v.FgColor = g.BgColor, g.FgColor v.SelBgColor, v.SelFgColor = g.SelBgColor, g.SelFgColor v.Overlaps = overlaps diff --git a/view.go b/view.go index 190a653..5a331b4 100644 --- a/view.go +++ b/view.go @@ -402,8 +402,8 @@ func (l lineType) String() string { return str } -// newView returns a new View object. -func newView(name string, x0, y0, x1, y1 int, mode OutputMode) *View { +// NewView returns a new View object. +func NewView(name string, x0, y0, x1, y1 int, mode OutputMode) *View { v := &View{ name: name, x0: x0, @@ -494,31 +494,15 @@ func (v *View) setRune(x, y int, ch rune, fgColor, bgColor Attribute) { bgColor = v.BgColor ch = v.Mask } else if v.Highlight { - var ry, rcy int - - _, ry, ok := v.realPosition(x, y) - if !ok { - return - } - _, rrcy, ok := v.realPosition(v.cx, v.cy) - // out of bounds is fine - if ok { - rcy = rrcy - } - - rangeSelectStart := rcy - rangeSelectEnd := rcy + rangeSelectStart := v.cy + rangeSelectEnd := v.cy if v.rangeSelectStartY != -1 { - _, realRangeSelectStart, ok := v.realPosition(0, v.rangeSelectStartY-v.oy) - if !ok { - return - } - - rangeSelectStart = min(realRangeSelectStart, rcy) - rangeSelectEnd = max(realRangeSelectStart, rcy) + relativeRangeSelectStart := v.rangeSelectStartY - v.oy + rangeSelectStart = min(relativeRangeSelectStart, v.cy) + rangeSelectEnd = max(relativeRangeSelectStart, v.cy) } - if ry >= rangeSelectStart && ry <= rangeSelectEnd { + if y >= rangeSelectStart && y <= rangeSelectEnd { // this ensures we use the bright variant of a colour upon highlight fgColorComponent := fgColor & ^AttrAll if fgColorComponent >= AttrIsValidColor && fgColorComponent < AttrIsValidColor+8 { @@ -1103,6 +1087,8 @@ func (v *View) updateSearchPositions() { if v.searcher.modelSearchResults != nil { for _, result := range v.searcher.modelSearchResults { + // This code only works when v.Wrap is false. + if result.Y >= len(v.lines) { break } @@ -1131,8 +1117,9 @@ func (v *View) updateSearchPositions() { } } } else { - for y, line := range v.lines { - v.searcher.searchPositions = append(v.searcher.searchPositions, searchPositionsForLine(line, y)...) + v.refreshViewLinesIfNeeded() + for y, line := range v.viewLines { + v.searcher.searchPositions = append(v.searcher.searchPositions, searchPositionsForLine(line.line, y)...) } } } @@ -1373,6 +1360,8 @@ func (v *View) ViewBufferLines() []string { v.writeMutex.Lock() defer v.writeMutex.Unlock() + v.refreshViewLinesIfNeeded() + lines := make([]string, len(v.viewLines)) for i, l := range v.viewLines { str := lineType(l.line).String() @@ -1512,18 +1501,20 @@ func lineWrap(line []cell, columns int) [][]cell { lines = append(lines, line[offset:i]) offset = i n = rw - } else if lastWhitespaceIndex != -1 && lastWhitespaceIndex+1 != i { + } else if lastWhitespaceIndex != -1 { // if there is a space in the line and the line is not breaking at a space/hyphen if line[lastWhitespaceIndex].chr == '-' { // if break occurs at hyphen, we'll retain the hyphen lines = append(lines, line[offset:lastWhitespaceIndex+1]) - offset = lastWhitespaceIndex + 1 - n = i - offset } else { // if break occurs at space, we'll omit the space lines = append(lines, line[offset:lastWhitespaceIndex]) - offset = lastWhitespaceIndex + 1 - n = i - offset + 1 + } + // Either way, continue *after* the break + offset = lastWhitespaceIndex + 1 + n = 0 + for _, c := range line[offset : i+1] { + n += runewidth.RuneWidth(c.chr) } } else { // in this case we're breaking mid-word diff --git a/view_test.go b/view_test.go index 1e4329a..7677aa7 100644 --- a/view_test.go +++ b/view_test.go @@ -221,7 +221,48 @@ func TestLineWrap(t *testing.T) { expected: []string{ "one-two-", "three-", - "four-five", + "four-", + "five", + }, + }, + { + name: "Several lines using all the available width", + line: "aaa bb cc ddd-ee ff", + columns: 5, + expected: []string{ + "aaa", + "bb cc", + "ddd-", + "ee ff", + }, + }, + { + name: "Multi-cell runes", + line: "ðŸĪðŸĪðŸĪ 🐝🐝 🙉 ðŸĶŠðŸĶŠðŸĶŠ-🐎🐎 ðŸĶĒðŸĶĒ", + columns: 9, + expected: []string{ + "ðŸĪðŸĪðŸĪ", + "🐝🐝 🙉", + "ðŸĶŠðŸĶŠðŸĶŠ-", + "🐎🐎 ðŸĶĒðŸĶĒ", + }, + }, + { + name: "Space in last column", + line: "hello world", + columns: 6, + expected: []string{ + "hello", + "world", + }, + }, + { + name: "Hyphen in last column", + line: "hello-world", + columns: 6, + expected: []string{ + "hello-", + "world", }, }, {