Skip to content

Commit

Permalink
Merge branch 'johan/fix-wrap-search'
Browse files Browse the repository at this point in the history
  • Loading branch information
walles committed Sep 25, 2022
2 parents 8c68a5c + 6bee135 commit f8228e4
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 12 deletions.
1 change: 0 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJh
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
91 changes: 91 additions & 0 deletions m/pager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,97 @@ func TestScrollToBottomWrapNextToLastLine(t *testing.T) {
assert.Equal(t, actual, expected)
}

func TestIsScrolledToEnd_LongFile(t *testing.T) {
// Six lines of contents
reader := NewReaderFromText("Testing", "a\nb\nc\nd\ne\nf\n")

// Three lines screen
screen := twin.NewFakeScreen(20, 3)

// Create the pager
pager := NewPager(reader)
pager.screen = screen

assert.Equal(t, false, pager.isScrolledToEnd())

pager.scrollToEnd()
assert.Equal(t, true, pager.isScrolledToEnd())
}

func TestIsScrolledToEnd_ShortFile(t *testing.T) {
// Three lines of contents
reader := NewReaderFromText("Testing", "a\nb\nc")

// Six lines screen
screen := twin.NewFakeScreen(20, 6)

// Create the pager
pager := NewPager(reader)
pager.screen = screen

assert.Equal(t, true, pager.isScrolledToEnd())

pager.scrollToEnd()
assert.Equal(t, true, pager.isScrolledToEnd())
}

func TestIsScrolledToEnd_ExactFile(t *testing.T) {
// Three lines of contents
reader := NewReaderFromText("Testing", "a\nb\nc")

// Three lines screen
screen := twin.NewFakeScreen(20, 3)

// Create the pager
pager := NewPager(reader)
pager.screen = screen
pager.ShowStatusBar = false

assert.Equal(t, true, pager.isScrolledToEnd())

pager.scrollToEnd()
assert.Equal(t, true, pager.isScrolledToEnd())
}

func TestIsScrolledToEnd_WrappedLastLine(t *testing.T) {
// Three lines of contents
reader := NewReaderFromText("Testing", "a\nb\nc d e f g h i j k l m n")

// Three lines screen
screen := twin.NewFakeScreen(5, 3)

// Create the pager
pager := NewPager(reader)
pager.screen = screen
pager.WrapLongLines = true

assert.Equal(t, false, pager.isScrolledToEnd())

pager.scrollToEnd()
assert.Equal(t, true, pager.isScrolledToEnd())

pager.onKey(twin.KeyUp)
pager.redraw("XXX")
assert.Equal(t, false, pager.isScrolledToEnd())
}

func TestIsScrolledToEnd_EmptyFile(t *testing.T) {
// No contents
reader := NewReaderFromText("Testing", "")

// Three lines screen
screen := twin.NewFakeScreen(20, 3)

// Create the pager
pager := NewPager(reader)
pager.screen = screen

assert.Equal(t, true, pager.isScrolledToEnd())

pager.scrollToEnd()
assert.Equal(t, true, pager.isScrolledToEnd())
}

// Verify that we can page all files in ../sample-files/* without crashing
func TestPageSamples(t *testing.T) {
for _, fileName := range getTestFiles() {
Expand Down
3 changes: 3 additions & 0 deletions m/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,9 @@ func newReaderFromStream(reader io.Reader, originalFileName *string, fromFilter
//
// First parameter is the name of this Reader. This name will be displayed by
// Moar in the bottom left corner of the screen.
//
// Calling _wait() on this Reader will always return immediately, no
// asynchronous ops will be performed.
func NewReaderFromText(name string, text string) *Reader {
noExternalNewlines := strings.Trim(text, "\n")
lines := []*Line{}
Expand Down
5 changes: 3 additions & 2 deletions m/screenLines.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ func (p *Pager) renderScreenLines() (lines [][]twin.Cell, statusText string) {
// The returned lines are display ready, meaning that they come with horizontal
// scroll markers and line numbers as necessary.
//
// The maximum number of lines returned by this method will be one less than the
// screen height, leaving space for the status line.
// The maximum number of lines returned by this method is limited by the screen
// height. If the status line is visible, you'll get at most one less than the
// screen height from this method.
func (p *Pager) renderLines() ([]renderedLine, string) {
_, height := p.screen.Size()
wantedLineCount := height - 1
Expand Down
42 changes: 38 additions & 4 deletions m/scrollPosition.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,48 @@ func (sp *scrollPosition) deltaScreenLines(pager *Pager) int {
}

func (p *Pager) scrollToEnd() {
lastInputLine := p.reader.GetLineCount()
inputLine := p.reader.GetLine(lastInputLine)
screenLines := p.renderLine(inputLine, 0)
inputLineCount := p.reader.GetLineCount()
if inputLineCount == 0 {
return
}
lastInputLineNumberOneBased := inputLineCount

lastInputLine := p.reader.GetLine(lastInputLineNumberOneBased)
screenLines := p.renderLine(lastInputLine, lastInputLineNumberOneBased)

p.scrollPosition.internalDontTouch.lineNumberOneBased = lastInputLine
p.scrollPosition.internalDontTouch.lineNumberOneBased = lastInputLineNumberOneBased
p.scrollPosition.internalDontTouch.deltaScreenLines = len(screenLines) - 1
}

// Can be either because Pager.scrollToEnd() was just called or because the user
// has pressed the down arrow enough times.
func (p *Pager) isScrolledToEnd() bool {
inputLineCount := p.reader.GetLineCount()
if inputLineCount == 0 {
// No lines available, which means we can't scroll any further down
return true
}
lastInputLineNumberOneBased := inputLineCount

visibleLines, _ := p.renderLines()
lastVisibleLine := visibleLines[len(visibleLines)-1]
if lastVisibleLine.inputLineOneBased != lastInputLineNumberOneBased {
// Last input line is not on the screen
return false
}

// Last line is on screen, now we need to figure out whether we can see all
// of it
lastInputLine := p.reader.GetLine(lastInputLineNumberOneBased)
lastInputLineRendered := p.renderLine(lastInputLine, lastInputLineNumberOneBased)
lastRenderedSubLine := lastInputLineRendered[len(lastInputLineRendered)-1]

// If the last visible subline is the same as the last possible subline then
// we're at the bottom
return lastVisibleLine.wrapIndex == lastRenderedSubLine.wrapIndex
}

// Returns nil if there are no lines
func (p *Pager) getLastVisiblePosition() *scrollPosition {
renderedLines, _ := p.renderLines()
if len(renderedLines) == 0 {
Expand Down
5 changes: 5 additions & 0 deletions m/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ func (p *Pager) scrollToNextSearchHit() {
return
}

if p.mode == _Viewing && p.isScrolledToEnd() {
p.mode = _NotFound
return
}

var firstSearchPosition scrollPosition

switch p.mode {
Expand Down
26 changes: 21 additions & 5 deletions m/search_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package m

import (
"strings"
"testing"

"github.com/walles/moar/twin"
Expand All @@ -10,10 +9,7 @@ import (

// Create a pager with three screen lines reading from a six lines stream
func createThreeLinesPager(t *testing.T) *Pager {
reader := NewReaderFromStream("", strings.NewReader("a\nb\nc\nd\ne\nf\n"))
if err := reader._wait(); err != nil {
panic(err)
}
reader := NewReaderFromText("", "a\nb\nc\nd\ne\nf\n")

screen := twin.NewFakeScreen(20, 3)
pager := NewPager(reader)
Expand Down Expand Up @@ -73,3 +69,23 @@ func TestScrollToNextSearchHit_WrapAfterNotFound(t *testing.T) {
assert.Equal(t, _Viewing, pager.mode)
assert.Equal(t, 1, pager.lineNumberOneBased())
}

func TestScrollToNextSearchHit_WrapAfterFound(t *testing.T) {
// Create a pager scrolled to the last line
pager := createThreeLinesPager(t)
pager.scrollToEnd()

// Search for "f", it's on the last line (ref createThreeLinesPager())
pager.searchString = "f"
pager.searchPattern = toPattern(pager.searchString)

// Scroll to the next search hit, this should take us into _NotFound
pager.scrollToNextSearchHit()
assert.Equal(t, _NotFound, pager.mode)

// Scroll to the next search hit, this should wrap the search and take us
// back to the bottom again
pager.scrollToNextSearchHit()
assert.Equal(t, _Viewing, pager.mode)
assert.Equal(t, 5, pager.lineNumberOneBased())
}

0 comments on commit f8228e4

Please sign in to comment.