This can be done using scrolling commands written in elisp.
;; Scrolling that re-centers, keeping the cursor vertically centered.
;; Also clamps window top when scrolling down,
;; so the text doesn't scroll off-screen.
(defun my-scroll-and-clamp--forward-line (n)
"Wrap forward-line', supporting Emacs built-in goal column. Argument N the number of lines, passed to
forward-line'."
(let ((next-column
(or goal-column
(and (memq last-command
'(next-line previous-line line-move))
(if (consp temporary-goal-column)
(car temporary-goal-column)
temporary-goal-column)))))
(unless next-column
(setq temporary-goal-column (current-column))
(setq next-column temporary-goal-column))
(forward-line n)
(move-to-column next-column))
;; Needed so `temporary-goal-column' is respected in the future.
(setq this-command 'line-move))
(defmacro my-scroll-and-clamp--with-evil-visual-mode-hack (&rest body)
"Execute BODY with the point not restricted to line limits.
This is needed so the point is not forced to line bounds
even when in evil visual line mode."
`(let ((mark-found nil))
(when (and (fboundp 'evil-visual-state-p)
(funcall 'evil-visual-state-p)
(fboundp 'evil-visual-type)
(eq (funcall 'evil-visual-type) 'line)
(boundp 'evil-visual-point))
(let ((mark (symbol-value 'evil-visual-point)))
(when (markerp mark)
(setq mark-found mark))))
(unwind-protect
(progn
(when mark-found
(goto-char (marker-position mark-found)))
,@body)
(when mark-found
(set-marker mark-found (point))))))
;;;###autoload
(defun my-scroll-and-clamp-up-command ()
(interactive)
(my-scroll-and-clamp--with-evil-visual-mode-hack
(let ((height (window-height)))
;; Move point.
(my-scroll-and-clamp--forward-line height)
;; Move window.
(set-window-start
(selected-window)
(min
(save-excursion ;; new point.
(forward-line (- (/ height 2)))
(point))
(save-excursion ;; max point.
(goto-char (point-max))
(beginning-of-line)
(forward-line (- (- height (+ 1 (* 2 scroll-margin)))))
(point))))))
(redisplay))
;;;###autoload
(defun my-scroll-and-clamp-down-command ()
(interactive)
(my-scroll-and-clamp--with-evil-visual-mode-hack
(let* ((height (window-height)))
;; Move point.
(my-scroll-and-clamp--forward-line (- height))
(setq this-command 'line-move)
;; Move window.
(set-window-start
(selected-window)
(save-excursion ;; new point.
(forward-line (- (/ height 2)))
(point)))))
(redisplay))
Example shortcut bindings:
(global-set-key (kbd "<next>") 'my-scroll-and-clamp-up-command)
(global-set-key (kbd "<prior>") 'my-scroll-and-clamp-down-command)
S-down-mouse-1
but seems like it did not take any affect :-( Is it also possible to add N-line margin while scrolling on your code. smooth scrolling have this as(setq smooth-scroll-margin 5)
@ideasman42 – alper Jul 27 '20 at 13:42height
as a smaller/fixed value. Note that I wrote a package on melpa that does fast modal scrolling using the mouse, see:scroll-on-drag
- https://gitlab.com/ideasman42/emacs-scroll-on-drag – ideasman42 Jul 28 '20 at 09:28‘next-line’ is for interactive use only; use ‘forward-line’ instead.
should I changenext-line
withforward-line
? – alper Aug 17 '21 at 14:18next-line
isn't called directly, so it's only checked for comparison. Could you find what function causes this warning? – ideasman42 Aug 18 '21 at 01:16next-line
. Please see https://emacs.stackexchange.com/a/59843/18414 // I come up with a function that is originally referencing your solution to ignore emtpy lines on the bottom of the buffer. // Formy-bottom ()
function. – alper Aug 18 '21 at 11:20