8

Inspired by this question: Let Emacs move the cursor off-screen, I am considering writing a minor mode that will keep the cursor in a fixed position that is not affected by scrolling operations. I have some ideas of how I might implement this, but it would require a hook to let me know when the cursor is being "pushed" off of the screen.

Somewhere within the bowels of Emacs, there exists code that is called when the location of point is being moved off of the display. While it has been said that the code that controls this behavior lives deep with the C source code, I would like to know if there is any way that I can be notified when it is about to happen.

Is there any way that I can be notified when a scrolling command is about to move the cursor? Or at least, is there a way that I can be notified when the display is about to be scrolled?


Update: I am no longer planning on writing a minor mode to achieve this because a new package was just released on GNU ELPA called scroll-restore that provides exactly the features that I was thinking of implementing myself!

nispio
  • 8,175
  • 2
  • 35
  • 73

2 Answers2

6

Yes.

The functions in window-scroll-functions are called just before a redisplay that would cause scrolling. Each function returns two arguments, the window window and the new window start position new-start. You can call (window-start window) to get the current window start position, (window-end window) to get the current window end position, and (window-end window t) to get the new window end position. If the point isn't between new-start and (window-end window t), it's about to be moved. As far as I can tell, these functions are called in all cases that change a window's extent, including scrolling and resizing. They are also called when a window starts showing a buffer (because it's just been created or because it's now showing a new buffer).

To make scrolling commands appear not to move the point, you would probably want to save the current point in this hook, and do something in post-command-hook. Since the point will have to move due to the scrolling, you presumably would save it, and restore it in pre-command-hook.

  • 1
    Excellent! I must admit, I was not expecting the answer to be "yes." – nispio Oct 15 '14 at 21:09
  • Is there anything like `(window-start)` for horizontal scrolling? (note that `window-hscroll` does not do the job when the font in the buffer differs from the default one). – AlwaysLearning Sep 14 '17 at 13:03
1

@Gilles answer is the right way to go. I’ll keep this one here for the information and the hacks.

I believe the point movement is performed by the C functions window_scroll_line_based and window_scroll_pixel_based (dependinyg on your frame parameters). These functions are not exposed to the lisp machine, so you can’t advise them. You would have to advise functions that call them.

Two of the (lisp accessible) functions that can do this are scroll-up and scroll-down. Here’s how I found out about them:

  1. Go to some long buffer.
  2. Select a small region.
  3. Call, M-x (put-text-property (region-beginning) (region-end) 'point-left (lambda (&rest _) (error "Point leaving"))). This will throw an error when point leaves the region.
  4. Activate M-x toggle-debug-on-error.
  5. Make sure point is inside this region.
  6. Scroll with the mouse wheel until point is dragged out of the region.

You will be given a backtrace buffer like this one, which clearly points out the scroll-down function.

Debugger entered--Lisp error: (error "Point leaving")
  signal(error ("Point leaving"))
  error("Point leaving")
  (lambda (&rest _) (error "Point leaving"))(1508 1192)
  scroll-down(10)
  funcall(scroll-down 10)
  mwheel-scroll((double-mouse-4 (#<window 50 on *Backtrace*> 1299 (578 . 545) 77086296 nil 1299 (57 . 25) nil (398 . 20) (10 . 21)) 2))
  funcall-interactively(mwheel-scroll (double-mouse-4 (#<window 50 on *Backtrace*> 1299 (578 . 545) 77086296 nil 1299 (57 . 25) nil (398 . 20) (10 . 21)) 2))
  call-interactively(mwheel-scroll nil nil)
  command-execute(mwheel-scroll)

I followed scroll-down to its C definition, which led me to window_scroll, which lead me to those two functions above.

What you can do

You can advise scroll-down/up with an around advice (as well as any other functions that might also result in point movement). Use this advice to check if the position of point has changed and record the old position. And then do whatever you want with it.

Be warned, advices are very unreliable when it comes to C functions. Some important functions have special addresses in byte-compiled code, which means advices on them aren’t called when the code is compiled. I don’t think that will be the case here, but I figured you should no.

Malabarba
  • 22,878
  • 6
  • 78
  • 163
  • I don't think advising `scroll-down` and `scroll-up` would be useful here. There are many other ways scrolling can happen. At least all commands having the `isearch-scroll` property should be considered scrolling commands. – Gilles 'SO- stop being evil' Oct 15 '14 at 20:29
  • @Gilles that's why I said _"as well as any other functions that might also result in point movement"_. But your answer definitely looks better! – Malabarba Oct 15 '14 at 20:34
  • That is some pretty clever debugging! – nispio Oct 15 '14 at 20:37