diff --git a/cmd/micro/micro_test.go b/cmd/micro/micro_test.go index 185b296e5b..f00622d2b7 100644 --- a/cmd/micro/micro_test.go +++ b/cmd/micro/micro_test.go @@ -4,12 +4,14 @@ import ( "fmt" "log" "os" + "strings" "testing" "github.com/go-errors/errors" "github.com/micro-editor/micro/v2/internal/action" "github.com/micro-editor/micro/v2/internal/buffer" "github.com/micro-editor/micro/v2/internal/config" + "github.com/micro-editor/micro/v2/internal/display" "github.com/micro-editor/micro/v2/internal/screen" "github.com/micro-editor/micro/v2/internal/util" "github.com/micro-editor/tcell/v2" @@ -329,6 +331,62 @@ func TestSearchAndReplace(t *testing.T) { assert.Equal(t, srTest3, string(data)) } +func TestSoftwrapScroll(t *testing.T) { + // lines of 1, 3, 6 and 8 rows, so rows and lines don't match up + var sb strings.Builder + for i := 0; i < 50; i++ { + fmt.Fprintf(&sb, "%d %s\n", i, strings.Repeat("word ", i%4*40)) + } + text := sb.String() + openFile(createTestFile(t, text)) + h := action.MainTab().CurPane() + h.Buf.SetOptionNative("softwrap", true) + height := h.BufView().Height + end := h.SLocFromLoc(h.Buf.End()) + lastPage := h.Scroll(end, -height+1) + + // check against Diff() for every StartLine near the end + v := h.GetView() + for s := h.Scroll(end, -3*height); s != end; s = h.Scroll(s, 1) { + v.StartLine = s + assert.Equal(t, h.Diff(s, end) < height, h.ScrollReachedEnd(), "ScrollReachedEnd at %v", s) + want := s + if h.Diff(s, end) < height-1 { + want = lastPage + } + h.ScrollAdjust() + assert.Equal(t, want, h.GetView().StartLine, "ScrollAdjust at %v", s) + } + + // PageDown stops with the last line at the bottom of the view + h.GotoLoc(buffer.Loc{X: 0, Y: 0}) + for i := 0; i < 50; i++ { + h.PageDown() + } + assert.Equal(t, lastPage, h.GetView().StartLine) + + // GotoLoc recenters only when the cursor moves by at least a view height + from := h.Scroll(end, -2*height) + for _, d := range []int{height - 1, height, 1 - height, -height} { + h.GotoLoc(h.LocFromVLoc(display.VLoc{SLoc: from})) + sloc := h.Scroll(from, d) + h.GotoLoc(h.LocFromVLoc(display.VLoc{SLoc: sloc})) + recentered := h.GetView().StartLine == h.Scroll(sloc, -height/4) + assert.Equal(t, util.Abs(d) >= height, recentered, "GotoLoc by %d rows", d) + } + + // opening a buffer recenters only when the cursor is not on the first page + for _, d := range []int{height - 1, height} { + sloc := h.Scroll(display.SLoc{}, d) + b := buffer.NewBufferFromString(text, "", buffer.BTDefault) + b.SetOptionNative("softwrap", true) + b.GetActiveCursor().GotoLoc(h.LocFromVLoc(display.VLoc{SLoc: sloc})) + h.OpenBuffer(b) + recentered := h.GetView().StartLine == h.Scroll(sloc, -height/4) + assert.Equal(t, d >= height, recentered, "open at row %d", d) + } +} + func TestMultiCursor(t *testing.T) { // TODO } diff --git a/internal/action/actions.go b/internal/action/actions.go index ce1d0e1099..f1ec01f619 100644 --- a/internal/action/actions.go +++ b/internal/action/actions.go @@ -40,8 +40,8 @@ func (h *BufPane) ScrollDown(n int) { func (h *BufPane) ScrollAdjust() { v := h.GetView() end := h.SLocFromLoc(h.Buf.End()) - if h.Diff(v.StartLine, end) < h.BufView().Height-1 { - v.StartLine = h.Scroll(end, -h.BufView().Height+1) + if last := h.Scroll(end, -h.BufView().Height+1); last.LessThan(v.StartLine) { + v.StartLine = last } h.SetView(v) } @@ -51,7 +51,7 @@ func (h *BufPane) ScrollAdjust() { func (h *BufPane) ScrollReachedEnd() bool { v := h.GetView() end := h.SLocFromLoc(h.Buf.End()) - return h.Diff(v.StartLine, end) < h.BufView().Height + return end.LessEqual(h.Scroll(v.StartLine, h.BufView().Height-1)) } // MousePress is the event that should happen when a normal click happens diff --git a/internal/action/bufpane.go b/internal/action/bufpane.go index df6a20f8d8..939eb8ed2f 100644 --- a/internal/action/bufpane.go +++ b/internal/action/bufpane.go @@ -361,14 +361,14 @@ func (h *BufPane) OpenBuffer(b *buffer.Buffer) { // Use GotoLoc when the new location may be far away from the current location. func (h *BufPane) GotoLoc(loc buffer.Loc) { sloc := h.SLocFromLoc(loc) - d := h.Diff(h.SLocFromLoc(h.Cursor.Loc), sloc) + cur := h.SLocFromLoc(h.Cursor.Loc) h.Cursor.GotoLoc(loc) // If the new location is far away from the previous one, // ensure the cursor is at 25% of the window height height := h.BufView().Height - if util.Abs(d) >= height { + if sloc.GreaterThan(h.Scroll(cur, height-1)) || sloc.LessThan(h.Scroll(cur, -height+1)) { v := h.GetView() v.StartLine = h.Scroll(sloc, -height/4) h.ScrollAdjust() @@ -384,7 +384,7 @@ func (h *BufPane) initialRelocate() { // If the initial cursor location is far away from the beginning // of the buffer, ensure the cursor is at 25% of the window height v := h.GetView() - if h.Diff(display.SLoc{0, 0}, sloc) < height { + if sloc.LessEqual(h.Scroll(display.SLoc{0, 0}, height-1)) { v.StartLine = display.SLoc{0, 0} } else { v.StartLine = h.Scroll(sloc, -height/4)