1. Frank Fischer
  2. evil
  3. Issues
Issue #267 resolved

H/L (evil-window-top/bottom) don't respect scroll-margin

Michael Chen
created an issue
make emacs
(setq scroll-margin 5)
open a long file
H or L causes line scrolling instead of keeping window fixed

H/L should respect scroll-margin and not scroll the window, just moving the cursor to the correct line. I tried changing:

evil-commands.el@759: (move-to-window-line (or count 0))
(move-to-window-line (or count scroll-margin 0))

and the equivalent for evil-window-bottom, but this solution doesn't give the correct behavior at the top and bottom of the buffers (which should place the cursor on the uppermost/bottommost line once the number of lines remaining are less than the scroll margin.

Comments (8)

  1. Michael Markert

    That description makes no sense to me. Either you don't want scrolling or you want scroll-margin to be respected. Those two are mutually exclusive as a move to the top of the window with a non-zero scroll-margin will result in a scrolling.

    If you mean that the scrolling is too extreme then take a look at scroll-conservatively.

    Otherwise please describe the exact behavior you expect.

  2. Michael Chen reporter

    Under no circumstance should there be any scrolling from H/L. In the middle of a file (enough so that the top/bottom of the file aren't on screen), the scroll-margin should be respected and the cursor should move to the top/bottom offset by the scroll-margin.

    However, when the top/bottom line of the file is shown, H/L should instead go to the top/bottom line. This doesn't result in any scrolling because the window is already at the top/bottom of the file, but the scroll-margin is no longer respected in these two cases in that the selected line will no longer be offset from the top/bottom.

    As an example of the desired behavior, please load up Vim (I'm using v7.3) and use :set scrolloff=5 and try the H/L commands.

  3. Michael Markert

    You seem to think that scroll-margin and scrolloff are aequivalent: They are not.

        scroll-margin is a variable defined in `C source code'.
        Its value is 0
        Number of lines of margin at the top and bottom of a window.
        Recenter the window whenever point gets within this many lines
        of the top or bottom of the window.

    To emphasize it: scroll-margin recenters if the point/cursor enters that critical region.

    scrolloff on the other hand keeps n lines above/below the cursor.

    To repeat: scroll-margin is the wrong starting point.

    What you actually want is a port of scrolloff.

  4. Michael Chen reporter

    Ah yes, you are correct. I forgot that I had set my scroll-conservatively to a high value so that scrolling to a window boundary doesn't recenter.

    However, is it the intended behavior for a user of a non-zero scroll-margin to have H/L trigger a scroll/recenter? This will happen no matter the value of scroll-conservatively. Since H/L are intended to replicate functions in Vim, and in Vim those commands never scroll the view, I feel like the commands should still respect the scroll-margin.

    Here's a patch for evil-commands.el to replicate Vim's behavior:

    @@ -756,7 +756,8 @@ In Insert state, insert a newline."
     on the first non-blank character."
       :jump t
       :type line
    -  (move-to-window-line (or count 0))
    +  (move-to-window-line (or count
    +                          (if (eq (window-start) 1) 0 scroll-margin)))
     (evil-define-motion evil-window-middle ()
    @@ -773,7 +774,8 @@ on the first non-blank character."
     on the first non-blank character."
       :jump t
       :type line
    -  (move-to-window-line (- (or count 1)))
    +  (move-to-window-line (- (or count
    +                             (if (eq (window-end) (point-max)) 1 (+ scroll-margin 1)))))
     ;; scrolling
  5. Michael Markert

    To repeat for the third time: scroll-margin is the wrong variable. But I can absolute understand your desire to have an scrolloff equivalent in evil.

    Here is a cleaner implementation:

    (defcustom evil-scrolloff nil
      "Minimal number of screen lines to keep above and below the cursor."
      :type '(choice (integer :tag "Number of lines")
                     (const :tag "No lines" nil)))
    (evil-define-motion evil-window-bottom (count)
      "Move the cursor to line COUNT from the bottom of the window
    on the first non-blank character."
      :jump t
      :type line
      (let ((scroll-margin 0))
        (move-to-window-line (- (or count evil-scrolloff 1))))
    (evil-define-motion evil-window-top (count)
      "Move the cursor to line COUNT from the top of the window
    on the first non-blank character."
      :jump t
      :type line
      (let ((scroll-margin 0))
        (move-to-window-line (or count evil-scrolloff 0)))

    I hope we can have a proper support for scrolloff in evil in time, but it involves quite a few motions, i.e. all line-moving.

  6. Michael Chen reporter

    Thanks for the cleaned up code, I am still new to elisp so my code is likely very messy. However, the code you provided does not produce the desired behavior:

    1. Off-by-one error for evil-window-bottom means it will still trigger a scroll down if evil-scrolloff is set to the same value as scroll-margin

    2. Doesn't handle the cases where first/last lines are shown in the window.

    I understand that scroll-margin is not the exact equivalent to scrolloff, but they do serve a similar purpose: to always provide context lines above/below the point. In any case, I'm not sure why it would be desired for the H/L motions to trigger scrolling when scroll-margin is set. However, I'll leave the point alone though as I can't be sure of others' usage habits.

  7. Log in to comment